]> oss.titaniummirror.com Git - msp430-gcc.git/blobdiff - gcc/cse.c
Imported gcc-4.4.3
[msp430-gcc.git] / gcc / cse.c
index 65f4542f02d0b530fb28a91bfc078d54913806ea..c16181e376d59afb00cbdd3d19d2d2bbf6605277 100644 (file)
--- a/gcc/cse.c
+++ b/gcc/cse.c
@@ -1,12 +1,13 @@
 /* Common subexpression elimination for GNU compiler.
    Copyright (C) 1987, 1988, 1989, 1992, 1993, 1994, 1995, 1996, 1997, 1998
-   1999, 2000, 2001, 2002 Free Software Foundation, Inc.
+   1999, 2000, 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009
+   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 2, or (at your option) any later
+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
@@ -15,18 +16,18 @@ 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 COPYING.  If not, write to the Free
-Software Foundation, 59 Temple Place - Suite 330, Boston, MA
-02111-1307, USA.  */
+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 "regs.h"
 #include "hard-reg-set.h"
+#include "regs.h"
 #include "basic-block.h"
 #include "flags.h"
 #include "real.h"
@@ -37,6 +38,14 @@ Software Foundation, 59 Temple Place - Suite 330, Boston, MA
 #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
@@ -74,14 +83,15 @@ Registers and "quantity numbers":
    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' records what quantity a register is currently thought
+   `REG_QTY (N)' records what quantity register N is currently thought
    of as containing.
 
-   All real quantity numbers are greater than or equal to `max_reg'.
-   If register N has not been assigned a quantity, reg_qty[N] will equal N.
+   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 `max_reg' do not exist and none of the `qty_table'
-   entries should be referenced with an index below `max_reg'.
+   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',
@@ -165,18 +175,20 @@ Other expressions:
    the register's new value.  This sequence of circumstances is rare
    within any one basic block.
 
-   The vectors `reg_tick' and `reg_in_table' 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.
+   `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, the vectors `reg_tick'
-   and `reg_in_table' do not apply to expressions which are simple
+   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
@@ -195,15 +207,6 @@ Related expressions:
    so that it is possible to find out if there exists any
    register equivalent to an expression related to a given expression.  */
 
-/* One plus largest register number used in this function.  */
-
-static int max_reg;
-
-/* One plus largest instruction UID used in this function at time of
-   cse_main call.  */
-
-static int max_insn_uid;
-
 /* Length of qty_table vector.  We know in advance we will not need
    a quantity number this big.  */
 
@@ -246,34 +249,41 @@ struct qty_table_elem
   rtx comparison_const;
   int comparison_qty;
   unsigned int first_reg, last_reg;
-  enum machine_mode mode;
-  enum rtx_code comparison_code;
+  /* 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 value last 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.  */
+   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 prev_insn_cc0;
-static enum machine_mode prev_insn_cc0_mode;
+static rtx this_insn_cc0, prev_insn_cc0;
+static enum machine_mode this_insn_cc0_mode, prev_insn_cc0_mode;
 #endif
 
-/* Previous actual insn.  0 if at first insn of basic block.  */
-
-static rtx prev_insn;
-
 /* 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
@@ -281,7 +291,7 @@ static rtx this_insn;
 
    Or -1 if this register is at the end of the chain.
 
-   If reg_qty[N] == N, reg_eqv_table[N].next is undefined.  */
+   If REG_QTY (N) == -N - 1, reg_eqv_table[N].next is undefined.  */
 
 /* Per-register equivalence chain.  */
 struct reg_eqv_elem
@@ -294,14 +304,8 @@ static struct reg_eqv_elem *reg_eqv_table;
 
 struct cse_reg_info
 {
-  /* Next in hash chain.  */
-  struct cse_reg_info *hash_next;
-
-  /* The next cse_reg_info structure in the free or used list.  */
-  struct cse_reg_info *next;
-
-  /* Search key */
-  unsigned int regno;
+  /* The timestamp at which this register is initialized.  */
+  unsigned int timestamp;
 
   /* The quantity number of the register's current contents.  */
   int reg_qty;
@@ -315,28 +319,28 @@ struct cse_reg_info
      reg_tick value, such expressions existing in the hash table are
      invalid.  */
   int reg_in_table;
-};
 
-/* A free list of cse_reg_info entries.  */
-static struct cse_reg_info *cse_reg_info_free_list;
+  /* 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 used list of cse_reg_info entries.  */
-static struct cse_reg_info *cse_reg_info_used_list;
-static struct cse_reg_info *cse_reg_info_used_list_end;
+/* A table of cse_reg_info indexed by register numbers.  */
+static struct cse_reg_info *cse_reg_info_table;
 
-/* A mapping from registers to cse_reg_info data structures.  */
-#define REGHASH_SHIFT  7
-#define REGHASH_SIZE   (1 << REGHASH_SHIFT)
-#define REGHASH_MASK   (REGHASH_SIZE - 1)
-static struct cse_reg_info *reg_hash[REGHASH_SIZE];
+/* The size of the above table.  */
+static unsigned int cse_reg_info_table_size;
 
-#define REGHASH_FN(REGNO)      \
-       (((REGNO) ^ ((REGNO) >> REGHASH_SHIFT)) & REGHASH_MASK)
+/* The index of the first entry that has not been initialized.  */
+static unsigned int cse_reg_info_table_first_uninitialized;
 
-/* The last lookup we did into the cse_reg_info_tree.  This allows us
-   to cache repeated lookups.  */
-static unsigned int cached_regno;
-static struct cse_reg_info *cached_cse_reg_info;
+/* 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
@@ -345,40 +349,17 @@ static struct cse_reg_info *cached_cse_reg_info;
 
 static HARD_REG_SET hard_regs_in_table;
 
-/* CUID of insn that starts the basic block currently being cse-processed.  */
-
-static int cse_basic_block_start;
-
-/* CUID of insn that ends the basic block currently being cse-processed.  */
-
-static int cse_basic_block_end;
-
-/* Vector mapping INSN_UIDs to cuids.
-   The cuids are like uids but increase monotonically always.
-   We use them to see whether a reg is used outside a given basic block.  */
-
-static int *uid_cuid;
-
-/* Highest UID in UID_CUID.  */
-static int max_uid;
+/* True if CSE has altered the CFG.  */
+static bool cse_cfg_altered;
 
-/* Get the cuid of an insn.  */
+/* True if CSE has altered conditional jump insns in such a way
+   that jump optimization should be redone.  */
+static bool cse_jumps_altered;
 
-#define INSN_CUID(INSN) (uid_cuid[INSN_UID (INSN)])
-
-/* Nonzero if this pass has made changes, and therefore it's
-   worthwhile to run the garbage collector.  */
-
-static int cse_altered;
-
-/* Nonzero if cse has altered conditional jump insns
-   in such a way that jump optimization should be redone.  */
-
-static int cse_jumps_altered;
-
-/* Nonzero if we put a LABEL_REF into the hash table for an INSN without a
-   REG_LABEL, we have to rerun jump after CSE to put in the note.  */
-static int recorded_label_ref;
+/* 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
@@ -386,12 +367,6 @@ static int recorded_label_ref;
 
 static int do_not_record;
 
-#ifdef LOAD_EXTEND_OP
-
-/* Scratch rtl used when looking for load-extended copy of a MEM.  */
-static rtx memory_extend_rtx;
-#endif
-
 /* canon_hash stores 1 in hash_arg_in_memory
    if it notices a reference to memory within the expression being hashed.  */
 
@@ -453,7 +428,9 @@ struct table_elt
   struct table_elt *related_value;
   int cost;
   int regcost;
-  enum machine_mode mode;
+  /* 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;
@@ -470,10 +447,16 @@ struct table_elt
    register (hard registers may require `do_not_record' to be set).  */
 
 #define HASH(X, M)     \
- ((GET_CODE (X) == REG && REGNO (X) >= FIRST_PSEUDO_REGISTER   \
+ ((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
@@ -488,39 +471,36 @@ struct table_elt
    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) \
-  ((N) == FRAME_POINTER_REGNUM || (N) == HARD_FRAME_POINTER_REGNUM     \
-   || (N) == STACK_POINTER_REGNUM || (N) == ARG_POINTER_REGNUM         \
-   || ((N) >= FIRST_VIRTUAL_REGISTER && (N) <= LAST_VIRTUAL_REGISTER)  \
-   || ((N) < FIRST_PSEUDO_REGISTER                                     \
+#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) (GET_CODE (X) == REG ? 0 : notreg_cost (X, SET))
-#define COST_IN(X,OUTER) (GET_CODE (X) == REG ? 0 : notreg_cost (X, OUTER))
-
-/* Get the info associated with register N.  */
-
-#define GET_CSE_REG_INFO(N)                    \
-  (((N) == cached_regno && cached_cse_reg_info)        \
-   ? cached_cse_reg_info : get_cse_reg_info ((N)))
+#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)
+#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)
+#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)
+#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) != (int) (N))
+#define REGNO_QTY_VALID_P(N) (REG_QTY (N) >= 0)
 
 static struct table_elt *table[HASH_SIZE];
 
@@ -529,174 +509,142 @@ static struct table_elt *table[HASH_SIZE];
 
 static struct table_elt *free_element_chain;
 
-/* Number of `struct table_elt' structures made so far for this function.  */
-
-static int n_elements_made;
-
-/* Maximum value `n_elements_made' has had so far in this compilation
-   for functions previously processed.  */
-
-static int max_elements_made;
-
-/* Surviving equivalence class when two equivalence classes are merged
-   by recording the effects of a jump in the last insn.  Zero if the
-   last insn was not a conditional jump.  */
-
-static struct table_elt *last_jump_equiv_class;
-
 /* 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;
 
-/* Define maximum length of a branch path.  */
-
-#define PATHLENGTH     10
-
-/* This data describes a block that will be processed by cse_basic_block.  */
+/* This data describes a block that will be processed by
+   cse_extended_basic_block.  */
 
 struct cse_basic_block_data
 {
-  /* Lowest CUID value of insns in block.  */
-  int low_cuid;
-  /* Highest CUID value of insns in block.  */
-  int high_cuid;
   /* Total number of SETs in block.  */
   int nsets;
-  /* Last insn in the block.  */
-  rtx last;
   /* Size of current branch path, if any.  */
   int path_size;
-  /* Current branch path, indicating which branches will be taken.  */
+  /* Current path, indicating which basic_blocks will be processed.  */
   struct branch_path
     {
-      /* The branch insn.  */
-      rtx branch;
-      /* Whether it should be taken or not.  AROUND is the same as taken
-        except that it is used when the destination label is not preceded
-       by a BARRIER.  */
-      enum taken {TAKEN, NOT_TAKEN, AROUND} status;
-    } path[PATHLENGTH];
+      /* 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);
+\f
+
+#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;
+\f
 /* 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.
-
-   ?!? FIXED_BASE_PLUS_P and NONZERO_BASE_PLUS_P need to move into
-   a header file so that their definitions can be shared with the
-   simplification routines in simplify-rtx.c.  Until then, do not
-   change these macros without also changing the copy in simplify-rtx.c.  */
-
-#define FIXED_BASE_PLUS_P(X)                                   \
-  ((X) == frame_pointer_rtx || (X) == hard_frame_pointer_rtx   \
-   || ((X) == arg_pointer_rtx && fixed_regs[ARG_POINTER_REGNUM])\
-   || (X) == virtual_stack_vars_rtx                            \
-   || (X) == virtual_incoming_args_rtx                         \
-   || (GET_CODE (X) == PLUS && GET_CODE (XEXP (X, 1)) == CONST_INT \
-       && (XEXP (X, 0) == frame_pointer_rtx                    \
-          || XEXP (X, 0) == hard_frame_pointer_rtx             \
-          || ((X) == arg_pointer_rtx                           \
-              && fixed_regs[ARG_POINTER_REGNUM])               \
-          || XEXP (X, 0) == virtual_stack_vars_rtx             \
-          || XEXP (X, 0) == virtual_incoming_args_rtx))        \
-   || GET_CODE (X) == ADDRESSOF)
-
-/* Similar, but also allows reference to the stack pointer.
-
-   This used to include FIXED_BASE_PLUS_P, however, we can't assume that
-   arg_pointer_rtx by itself is nonzero, because on at least one machine,
-   the i960, the arg pointer is zero when it is unused.  */
-
-#define NONZERO_BASE_PLUS_P(X)                                 \
-  ((X) == frame_pointer_rtx || (X) == hard_frame_pointer_rtx   \
-   || (X) == virtual_stack_vars_rtx                            \
-   || (X) == virtual_incoming_args_rtx                         \
-   || (GET_CODE (X) == PLUS && GET_CODE (XEXP (X, 1)) == CONST_INT \
-       && (XEXP (X, 0) == frame_pointer_rtx                    \
-          || XEXP (X, 0) == hard_frame_pointer_rtx             \
-          || ((X) == arg_pointer_rtx                           \
-              && fixed_regs[ARG_POINTER_REGNUM])               \
-          || XEXP (X, 0) == virtual_stack_vars_rtx             \
-          || XEXP (X, 0) == virtual_incoming_args_rtx))        \
-   || (X) == stack_pointer_rtx                                 \
-   || (X) == virtual_stack_dynamic_rtx                         \
-   || (X) == virtual_outgoing_args_rtx                         \
-   || (GET_CODE (X) == PLUS && GET_CODE (XEXP (X, 1)) == CONST_INT \
-       && (XEXP (X, 0) == stack_pointer_rtx                    \
-          || XEXP (X, 0) == virtual_stack_dynamic_rtx          \
-          || XEXP (X, 0) == virtual_outgoing_args_rtx))        \
-   || GET_CODE (X) == ADDRESSOF)
-
-static int notreg_cost         PARAMS ((rtx, enum rtx_code));
-static int approx_reg_cost_1   PARAMS ((rtx *, void *));
-static int approx_reg_cost     PARAMS ((rtx));
-static int preferrable         PARAMS ((int, int, int, int));
-static void new_basic_block    PARAMS ((void));
-static void make_new_qty       PARAMS ((unsigned int, enum machine_mode));
-static void make_regs_eqv      PARAMS ((unsigned int, unsigned int));
-static void delete_reg_equiv   PARAMS ((unsigned int));
-static int mention_regs                PARAMS ((rtx));
-static int insert_regs         PARAMS ((rtx, struct table_elt *, int));
-static void remove_from_table  PARAMS ((struct table_elt *, unsigned));
-static struct table_elt *lookup        PARAMS ((rtx, unsigned, enum machine_mode)),
-       *lookup_for_remove PARAMS ((rtx, unsigned, enum machine_mode));
-static rtx lookup_as_function  PARAMS ((rtx, enum rtx_code));
-static struct table_elt *insert PARAMS ((rtx, struct table_elt *, unsigned,
-                                        enum machine_mode));
-static void merge_equiv_classes PARAMS ((struct table_elt *,
-                                        struct table_elt *));
-static void invalidate         PARAMS ((rtx, enum machine_mode));
-static int cse_rtx_varies_p    PARAMS ((rtx, int));
-static void remove_invalid_refs        PARAMS ((unsigned int));
-static void remove_invalid_subreg_refs PARAMS ((unsigned int, unsigned int,
-                                                enum machine_mode));
-static void rehash_using_reg   PARAMS ((rtx));
-static void invalidate_memory  PARAMS ((void));
-static void invalidate_for_call        PARAMS ((void));
-static rtx use_related_value   PARAMS ((rtx, struct table_elt *));
-static unsigned canon_hash     PARAMS ((rtx, enum machine_mode));
-static unsigned canon_hash_string PARAMS ((const char *));
-static unsigned safe_hash      PARAMS ((rtx, enum machine_mode));
-static int exp_equiv_p         PARAMS ((rtx, rtx, int, int));
-static rtx canon_reg           PARAMS ((rtx, rtx));
-static void find_best_addr     PARAMS ((rtx, rtx *, enum machine_mode));
-static enum rtx_code find_comparison_args PARAMS ((enum rtx_code, rtx *, rtx *,
-                                                  enum machine_mode *,
-                                                  enum machine_mode *));
-static rtx fold_rtx            PARAMS ((rtx, rtx));
-static rtx equiv_constant      PARAMS ((rtx));
-static void record_jump_equiv  PARAMS ((rtx, int));
-static void record_jump_cond   PARAMS ((enum rtx_code, enum machine_mode,
-                                        rtx, rtx, int));
-static void cse_insn           PARAMS ((rtx, rtx));
-static int addr_affects_sp_p   PARAMS ((rtx));
-static void invalidate_from_clobbers PARAMS ((rtx));
-static rtx cse_process_notes   PARAMS ((rtx, rtx));
-static void cse_around_loop    PARAMS ((rtx));
-static void invalidate_skipped_set PARAMS ((rtx, rtx, void *));
-static void invalidate_skipped_block PARAMS ((rtx));
-static void cse_check_loop_start PARAMS ((rtx, rtx, void *));
-static void cse_set_around_loop        PARAMS ((rtx, rtx, rtx));
-static rtx cse_basic_block     PARAMS ((rtx, rtx, struct branch_path *, int));
-static void count_reg_usage    PARAMS ((rtx, int *, rtx, int));
-static int check_for_label_ref PARAMS ((rtx *, void *));
-extern void dump_class          PARAMS ((struct table_elt*));
-static struct cse_reg_info * get_cse_reg_info PARAMS ((unsigned int));
-static int check_dependence    PARAMS ((rtx *, void *));
-
-static void flush_hash_table   PARAMS ((void));
-static bool insn_live_p                PARAMS ((rtx, int *));
-static bool set_live_p         PARAMS ((rtx, rtx, int *));
-static bool dead_libcall_p     PARAMS ((rtx));
-\f
+   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 (classp)
-     struct table_elt *classp;
+dump_class (struct table_elt *classp)
 {
   struct table_elt *elt;
 
@@ -714,15 +662,28 @@ dump_class (classp)
 /* Subroutine of approx_reg_cost; called through for_each_rtx.  */
 
 static int
-approx_reg_cost_1 (xp, data)
-     rtx *xp;
-     void *data;
+approx_reg_cost_1 (rtx *xp, void *data)
 {
   rtx x = *xp;
-  regset set = (regset) data;
+  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;
+       }
+    }
 
-  if (x && GET_CODE (x) == REG)
-    SET_REGNO_REG_SET (set, REGNO (x));
   return 0;
 }
 
@@ -732,31 +693,14 @@ approx_reg_cost_1 (xp, data)
    0.  If any other hard register reference occurs, return MAX_COST.  */
 
 static int
-approx_reg_cost (x)
-     rtx x;
+approx_reg_cost (rtx x)
 {
-  regset_head set;
-  int i;
   int cost = 0;
-  int hardregs = 0;
 
-  INIT_REG_SET (&set);
-  for_each_rtx (&x, approx_reg_cost_1, (void *)&set);
+  if (for_each_rtx (&x, approx_reg_cost_1, (void *) &cost))
+    return MAX_COST;
 
-  EXECUTE_IF_SET_IN_REG_SET
-    (&set, 0, i,
-     {
-       if (! CHEAP_REGNO (i))
-        {
-          if (i < FIRST_PSEUDO_REGISTER)
-            hardregs++;
-
-          cost += i < FIRST_PSEUDO_REGISTER ? 2 : 1;
-        }
-     });
-
-  CLEAR_REG_SET (&set);
-  return hardregs && SMALL_REGISTER_CLASSES ? MAX_COST : cost;
+  return cost;
 }
 
 /* Return a negative value if an rtx A, whose costs are given by COST_A
@@ -764,10 +708,9 @@ approx_reg_cost (x)
    Return a positive value if A is less desirable, or 0 if the two are
    equally good.  */
 static int
-preferrable (cost_a, regcost_a, cost_b, regcost_b)
-     int cost_a, regcost_a, cost_b, regcost_b;
+preferable (int cost_a, int regcost_a, int cost_b, int regcost_b)
 {
-  /* First, get rid of cases involving expressions that are entirely
+  /* First, get rid of cases involving expressions that are entirely
      unwanted.  */
   if (cost_a != cost_b)
     {
@@ -799,12 +742,10 @@ preferrable (cost_a, regcost_a, cost_b, regcost_b)
    from COST macro to keep it simple.  */
 
 static int
-notreg_cost (x, outer)
-     rtx x;
-     enum rtx_code outer;
+notreg_cost (rtx x, enum rtx_code outer)
 {
   return ((GET_CODE (x) == SUBREG
-          && GET_CODE (SUBREG_REG (x)) == REG
+          && 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))
@@ -813,168 +754,89 @@ notreg_cost (x, outer)
           && TRULY_NOOP_TRUNCATION (GET_MODE_BITSIZE (GET_MODE (x)),
                                     GET_MODE_BITSIZE (GET_MODE (SUBREG_REG (x)))))
          ? 0
-         : rtx_cost (x, outer) * 2);
+         : rtx_cost (x, outer, optimize_this_for_speed_p) * 2);
 }
 
-/* Return an estimate of the cost of computing rtx X.
-   One use is in cse, to decide which expression to keep in the hash table.
-   Another is in rtl generation, to pick the cheapest way to multiply.
-   Other uses like the latter are expected in the future.  */
+\f
+/* Initialize CSE_REG_INFO_TABLE.  */
 
-int
-rtx_cost (x, outer_code)
-     rtx x;
-     enum rtx_code outer_code ATTRIBUTE_UNUSED;
+static void
+init_cse_reg_info (unsigned int nregs)
 {
-  int i, j;
-  enum rtx_code code;
-  const char *fmt;
-  int total;
-
-  if (x == 0)
-    return 0;
+  /* Do we need to grow the table?  */
+  if (nregs > cse_reg_info_table_size)
+    {
+      unsigned int new_size;
 
-  /* Compute the default costs of certain things.
-     Note that RTX_COSTS can override the defaults.  */
+      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);
 
-  code = GET_CODE (x);
-  switch (code)
-    {
-    case MULT:
-      /* Count multiplication by 2**n as a shift,
-        because if we are considering it, we would output it as a shift.  */
-      if (GET_CODE (XEXP (x, 1)) == CONST_INT
-         && exact_log2 (INTVAL (XEXP (x, 1))) >= 0)
-       total = 2;
+         while (new_size < nregs)
+           new_size *= 2;
+       }
       else
-       total = COSTS_N_INSNS (5);
-      break;
-    case DIV:
-    case UDIV:
-    case MOD:
-    case UMOD:
-      total = COSTS_N_INSNS (7);
-      break;
-    case USE:
-      /* Used in loop.c and combine.c as a marker.  */
-      total = 0;
-      break;
-    default:
-      total = COSTS_N_INSNS (1);
+       {
+         /* 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;
     }
 
-  switch (code)
+  /* Do we have all of the first NREGS entries initialized?  */
+  if (cse_reg_info_table_first_uninitialized < nregs)
     {
-    case REG:
-      return 0;
-
-    case SUBREG:
-      /* If we can't tie these modes, make this expensive.  The larger
-        the mode, the more expensive it is.  */
-      if (! MODES_TIEABLE_P (GET_MODE (x), GET_MODE (SUBREG_REG (x))))
-       return COSTS_N_INSNS (2
-                             + GET_MODE_SIZE (GET_MODE (x)) / UNITS_PER_WORD);
-      break;
+      unsigned int old_timestamp = cse_reg_info_timestamp - 1;
+      unsigned int i;
 
-#ifdef RTX_COSTS
-      RTX_COSTS (x, code, outer_code);
-#endif
-#ifdef CONST_COSTS
-      CONST_COSTS (x, code, outer_code);
-#endif
+      /* 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;
 
-    default:
-#ifdef DEFAULT_RTX_COSTS
-      DEFAULT_RTX_COSTS (x, code, outer_code);
-#endif
-      break;
+      cse_reg_info_table_first_uninitialized = nregs;
     }
-
-  /* Sum the costs of the sub-rtx's, plus cost of this operation,
-     which is already in total.  */
-
-  fmt = GET_RTX_FORMAT (code);
-  for (i = GET_RTX_LENGTH (code) - 1; i >= 0; i--)
-    if (fmt[i] == 'e')
-      total += rtx_cost (XEXP (x, i), code);
-    else if (fmt[i] == 'E')
-      for (j = 0; j < XVECLEN (x, i); j++)
-       total += rtx_cost (XVECEXP (x, i, j), code);
-
-  return total;
 }
-\f
-/* Return cost of address expression X.
-   Expect that X is properly formed address reference.  */
 
-int
-address_cost (x, mode)
-     rtx x;
-     enum machine_mode mode;
-{
-  /* The ADDRESS_COST macro does not deal with ADDRESSOF nodes.  But,
-     during CSE, such nodes are present.  Using an ADDRESSOF node which
-     refers to the address of a REG is a good thing because we can then
-     turn (MEM (ADDRESSSOF (REG))) into just plain REG.  */
-
-  if (GET_CODE (x) == ADDRESSOF && REG_P (XEXP ((x), 0)))
-    return -1;
-
-  /* We may be asked for cost of various unusual addresses, such as operands
-     of push instruction.  It is not worthwhile to complicate writing
-     of ADDRESS_COST macro by such cases.  */
-
-  if (!memory_address_p (mode, x))
-    return 1000;
-#ifdef ADDRESS_COST
-  return ADDRESS_COST (x);
-#else
-  return rtx_cost (x, MEM);
-#endif
-}
+/* Given REGNO, initialize the cse_reg_info entry for REGNO.  */
 
-\f
-static struct cse_reg_info *
-get_cse_reg_info (regno)
-     unsigned int regno;
+static void
+get_cse_reg_info_1 (unsigned int regno)
 {
-  struct cse_reg_info **hash_head = &reg_hash[REGHASH_FN (regno)];
-  struct cse_reg_info *p;
+  /* 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;
+}
 
-  for (p = *hash_head; p != NULL; p = p->hash_next)
-    if (p->regno == regno)
-      break;
+/* Find a cse_reg_info entry for REGNO.  */
 
-  if (p == NULL)
-    {
-      /* Get a new cse_reg_info structure.  */
-      if (cse_reg_info_free_list)
-       {
-         p = cse_reg_info_free_list;
-         cse_reg_info_free_list = p->next;
-       }
-      else
-       p = (struct cse_reg_info *) xmalloc (sizeof (struct cse_reg_info));
-
-      /* Insert into hash table.  */
-      p->hash_next = *hash_head;
-      *hash_head = p;
-
-      /* Initialize it.  */
-      p->reg_tick = 1;
-      p->reg_in_table = -1;
-      p->reg_qty = regno;
-      p->regno = regno;
-      p->next = cse_reg_info_used_list;
-      cse_reg_info_used_list = p;
-      if (!cse_reg_info_used_list_end)
-       cse_reg_info_used_list_end = p;
-    }
+static inline struct cse_reg_info *
+get_cse_reg_info (unsigned int regno)
+{
+  struct cse_reg_info *p = &cse_reg_info_table[regno];
 
-  /* Cache this lookup; we tend to be looking up information about the
-     same register several times in a row.  */
-  cached_regno = regno;
-  cached_cse_reg_info = p;
+  /* 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;
 }
@@ -983,24 +845,16 @@ get_cse_reg_info (regno)
    for a new basic block.  */
 
 static void
-new_basic_block ()
+new_basic_block (void)
 {
   int i;
 
-  next_qty = max_reg;
-
-  /* Clear out hash table state for this pass.  */
-
-  memset ((char *) reg_hash, 0, sizeof reg_hash);
+  next_qty = 0;
 
-  if (cse_reg_info_used_list)
-    {
-      cse_reg_info_used_list_end->next = cse_reg_info_free_list;
-      cse_reg_info_free_list = cse_reg_info_used_list;
-      cse_reg_info_used_list = cse_reg_info_used_list_end = 0;
-    }
-  cached_cse_reg_info = 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
@@ -1028,8 +882,6 @@ new_basic_block ()
        }
     }
 
-  prev_insn = 0;
-
 #ifdef HAVE_cc0
   prev_insn_cc0 = 0;
 #endif
@@ -1039,16 +891,13 @@ new_basic_block ()
    register before and initialize that quantity.  */
 
 static void
-make_new_qty (reg, mode)
-     unsigned int reg;
-     enum machine_mode mode;
+make_new_qty (unsigned int reg, enum machine_mode mode)
 {
   int q;
   struct qty_table_elem *ent;
   struct reg_eqv_elem *eqv;
 
-  if (next_qty >= max_qty)
-    abort ();
+  gcc_assert (next_qty < max_qty);
 
   q = REG_QTY (reg) = next_qty++;
   ent = &qty_table[q];
@@ -1066,20 +915,18 @@ make_new_qty (reg, mode)
    OLD is not changing; NEW is.  */
 
 static void
-make_regs_eqv (new, old)
-     unsigned int new, old;
+make_regs_eqv (unsigned int new_reg, unsigned int old_reg)
 {
   unsigned int lastr, firstr;
-  int q = REG_QTY (old);
+  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.  */
-  if (! REGNO_QTY_VALID_P (old))
-    abort ();
+  gcc_assert (REGNO_QTY_VALID_P (old_reg));
 
-  REG_QTY (new) = q;
+  REG_QTY (new_reg) = q;
   firstr = ent->first_reg;
   lastr = ent->last_reg;
 
@@ -1092,20 +939,19 @@ make_regs_eqv (new, old)
         that not only can they not be allocated by the compiler, but
         they cannot be used in substitutions or canonicalizations
         either.  */
-      && (new >= FIRST_PSEUDO_REGISTER || REGNO_REG_CLASS (new) != NO_REGS)
-      && ((new < FIRST_PSEUDO_REGISTER && FIXED_REGNO_P (new))
-         || (new >= FIRST_PSEUDO_REGISTER
+      && (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
-                 || ((uid_cuid[REGNO_LAST_UID (new)] > cse_basic_block_end
-                      || (uid_cuid[REGNO_FIRST_UID (new)]
-                          < cse_basic_block_start))
-                     && (uid_cuid[REGNO_LAST_UID (new)]
-                         > uid_cuid[REGNO_LAST_UID (firstr)]))))))
+                 || (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_eqv_table[new].next = firstr;
-      reg_eqv_table[new].prev = -1;
-      ent->first_reg = new;
+      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
     {
@@ -1115,30 +961,29 @@ make_regs_eqv (new, old)
         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 >= FIRST_PSEUDO_REGISTER)
+            && new_reg >= FIRST_PSEUDO_REGISTER)
        lastr = reg_eqv_table[lastr].prev;
-      reg_eqv_table[new].next = reg_eqv_table[lastr].next;
+      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_eqv_table[reg_eqv_table[lastr].next].prev = new_reg;
       else
-       qty_table[q].last_reg = new;
-      reg_eqv_table[lastr].next = new;
-      reg_eqv_table[new].prev = lastr;
+       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 (reg)
-     unsigned int reg;
+delete_reg_equiv (unsigned int reg)
 {
   struct qty_table_elem *ent;
   int q = REG_QTY (reg);
   int p, n;
 
   /* If invalid, do nothing.  */
-  if (q == (int) reg)
+  if (! REGNO_QTY_VALID_P (reg))
     return;
 
   ent = &qty_table[q];
@@ -1155,7 +1000,7 @@ delete_reg_equiv (reg)
   else
     ent->first_reg = n;
 
-  REG_QTY (reg) = reg;
+  REG_QTY (reg) = -reg - 1;
 }
 
 /* Remove any invalid expressions from the hash table
@@ -1171,8 +1016,7 @@ delete_reg_equiv (reg)
    of X.  */
 
 static int
-mention_regs (x)
-     rtx x;
+mention_regs (rtx x)
 {
   enum rtx_code code;
   int i, j;
@@ -1186,9 +1030,7 @@ mention_regs (x)
   if (code == REG)
     {
       unsigned int regno = REGNO (x);
-      unsigned int endregno
-       = regno + (regno >= FIRST_PSEUDO_REGISTER ? 1
-                  : HARD_REGNO_NREGS (regno, GET_MODE (x)));
+      unsigned int endregno = END_REGNO (x);
       unsigned int i;
 
       for (i = regno; i < endregno; i++)
@@ -1197,6 +1039,7 @@ mention_regs (x)
            remove_invalid_refs (i);
 
          REG_IN_TABLE (i) = REG_TICK (i);
+         SUBREG_TICKED (i) = -1;
        }
 
       return 0;
@@ -1205,24 +1048,27 @@ mention_regs (x)
   /* 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 && GET_CODE (SUBREG_REG (x)) == REG
+  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_tick has been incremented more than once since
-            reg_in_table was last set, that means that the entire
-            register has been set before, so discard anything memorized
-            for the entire register, including all SUBREG expressions.  */
-         if (REG_IN_TABLE (i) != REG_TICK (i) - 1)
+         /* 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;
     }
 
@@ -1236,9 +1082,9 @@ mention_regs (x)
      call that expensive function in the most common case where the only
      use of the register is in the comparison.  */
 
-  if (code == COMPARE || GET_RTX_CLASS (code) == '<')
+  if (code == COMPARE || COMPARISON_P (x))
     {
-      if (GET_CODE (XEXP (x, 0)) == REG
+      if (REG_P (XEXP (x, 0))
          && ! REGNO_QTY_VALID_P (REGNO (XEXP (x, 0))))
        if (insert_regs (XEXP (x, 0), NULL, 0))
          {
@@ -1246,7 +1092,7 @@ mention_regs (x)
            changed = 1;
          }
 
-      if (GET_CODE (XEXP (x, 1)) == REG
+      if (REG_P (XEXP (x, 1))
          && ! REGNO_QTY_VALID_P (REGNO (XEXP (x, 1))))
        if (insert_regs (XEXP (x, 1), NULL, 0))
          {
@@ -1277,12 +1123,9 @@ mention_regs (x)
    so X's hash code may be different.  */
 
 static int
-insert_regs (x, classp, modified)
-     rtx x;
-     struct table_elt *classp;
-     int modified;
+insert_regs (rtx x, struct table_elt *classp, int modified)
 {
-  if (GET_CODE (x) == REG)
+  if (REG_P (x))
     {
       unsigned int regno = REGNO (x);
       int qty_valid;
@@ -1305,10 +1148,27 @@ insert_regs (x, classp, modified)
            for (classp = classp->first_same_value;
                 classp != 0;
                 classp = classp->next_same_value)
-             if (GET_CODE (classp->exp) == REG
+             if (REG_P (classp->exp)
                  && GET_MODE (classp->exp) == GET_MODE (x))
                {
-                 make_regs_eqv (regno, REGNO (classp->exp));
+                 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;
                }
 
@@ -1338,7 +1198,7 @@ insert_regs (x, classp, modified)
      not be accessible because its hash code will have changed.  So assign
      a quantity number now.  */
 
-  else if (GET_CODE (x) == SUBREG && GET_CODE (SUBREG_REG (x)) == REG
+  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);
@@ -1357,9 +1217,7 @@ insert_regs (x, classp, modified)
    and we save much time not recomputing it.  */
 
 static void
-remove_from_table (elt, hash)
-     struct table_elt *elt;
-     unsigned hash;
+remove_from_table (struct table_elt *elt, unsigned int hash)
 {
   if (elt == 0)
     return;
@@ -1432,6 +1290,19 @@ remove_from_table (elt, hash)
   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.
 
@@ -1442,16 +1313,13 @@ remove_from_table (elt, hash)
    looks like X.  */
 
 static struct table_elt *
-lookup (x, hash, mode)
-     rtx x;
-     unsigned hash;
-     enum machine_mode mode;
+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 && GET_CODE (x) == REG)
-                           || exp_equiv_p (x, p->exp, GET_CODE (x) != REG, 0)))
+    if (mode == p->mode && ((x == p->exp && REG_P (x))
+                           || exp_equiv_p (x, p->exp, !REG_P (x), false)))
       return p;
 
   return 0;
@@ -1461,28 +1329,26 @@ lookup (x, hash, mode)
    Also ignore discrepancies in the machine mode of a register.  */
 
 static struct table_elt *
-lookup_for_remove (x, hash, mode)
-     rtx x;
-     unsigned hash;
-     enum machine_mode mode;
+lookup_for_remove (rtx x, unsigned int hash, enum machine_mode mode)
 {
   struct table_elt *p;
 
-  if (GET_CODE (x) == REG)
+  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 (GET_CODE (p->exp) == REG
+       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, 0)))
+       if (mode == p->mode
+           && (x == p->exp || exp_equiv_p (x, p->exp, 0, false)))
          return p;
     }
 
@@ -1493,23 +1359,10 @@ lookup_for_remove (x, hash, mode)
    If one is found, return that expression.  */
 
 static rtx
-lookup_as_function (x, code)
-     rtx x;
-     enum rtx_code code;
+lookup_as_function (rtx x, enum rtx_code code)
 {
   struct table_elt *p
-    = lookup (x, safe_hash (x, VOIDmode) & HASH_MASK, GET_MODE (x));
-
-  /* If we are looking for a CONST_INT, the mode doesn't really matter, as
-     long as we are narrowing.  So if we looked in vain for a mode narrower
-     than word_mode before, look for word_mode now.  */
-  if (p == 0 && code == CONST_INT
-      && GET_MODE_SIZE (GET_MODE (x)) < GET_MODE_SIZE (word_mode))
-    {
-      x = copy_rtx (x);
-      PUT_MODE (x, word_mode);
-      p = lookup (x, safe_hash (x, VOIDmode) & HASH_MASK, word_mode);
-    }
+    = lookup (x, SAFE_HASH (x, VOIDmode), GET_MODE (x));
 
   if (p == 0)
     return 0;
@@ -1517,7 +1370,7 @@ lookup_as_function (x, code)
   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, 0))
+       && exp_equiv_p (p->exp, p->exp, 1, false))
       return p->exp;
 
   return 0;
@@ -1548,32 +1401,20 @@ lookup_as_function (x, code)
    If necessary, update table showing constant values of quantities.  */
 
 #define CHEAPER(X, Y) \
- (preferrable ((X)->cost, (X)->regcost, (Y)->cost, (Y)->regcost) < 0)
+ (preferable ((X)->cost, (X)->regcost, (Y)->cost, (Y)->regcost) < 0)
 
 static struct table_elt *
-insert (x, classp, hash, mode)
-     rtx x;
-     struct table_elt *classp;
-     unsigned hash;
-     enum machine_mode mode;
+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.  */
-  if (GET_CODE (x) == REG && ! REGNO_QTY_VALID_P (REGNO (x)))
-    abort ();
+  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 (GET_CODE (x) == REG && REGNO (x) < FIRST_PSEUDO_REGISTER)
-    {
-      unsigned int regno = REGNO (x);
-      unsigned int endregno = regno + HARD_REGNO_NREGS (regno, GET_MODE (x));
-      unsigned int i;
-
-      for (i = regno; i < endregno; i++)
-       SET_HARD_REG_BIT (hard_regs_in_table, i);
-    }
+  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.  */
 
@@ -1581,10 +1422,7 @@ insert (x, classp, hash, mode)
   if (elt)
     free_element_chain = elt->next_same_hash;
   else
-    {
-      n_elements_made++;
-      elt = (struct table_elt *) xmalloc (sizeof (struct table_elt));
-    }
+    elt = XNEW (struct table_elt);
 
   elt->exp = x;
   elt->canon_exp = NULL_RTX;
@@ -1597,13 +1435,7 @@ insert (x, classp, hash, mode)
   elt->related_value = 0;
   elt->in_memory = 0;
   elt->mode = mode;
-  elt->is_const = (CONSTANT_P (x)
-                  /* GNU C++ takes advantage of this for `this'
-                     (and other const values).  */
-                  || (RTX_UNCHANGING_P (x)
-                      && GET_CODE (x) == REG
-                      && REGNO (x) >= FIRST_PSEUDO_REGISTER)
-                  || FIXED_BASE_PLUS_P (x));
+  elt->is_const = (CONSTANT_P (x) || fixed_base_plus_p (x));
 
   if (table[hash])
     table[hash]->prev_same_hash = elt;
@@ -1614,7 +1446,7 @@ insert (x, classp, hash, mode)
     {
       classp = classp->first_same_value;
       if (CHEAPER (elt, classp))
-       /* Insert at the head of the class */
+       /* Insert at the head of the class */
        {
          struct table_elt *p;
          elt->next_same_value = classp;
@@ -1661,17 +1493,17 @@ insert (x, classp, hash, mode)
      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 && GET_CODE (classp->exp) == REG
-      && GET_CODE (x) != REG)
+  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_if_possible (exp_ent->mode, x);
+      exp_ent->const_rtx = gen_lowpart (exp_ent->mode, x);
       exp_ent->const_insn = this_insn;
     }
 
-  else if (GET_CODE (x) == REG
+  else if (REG_P (x)
           && classp
           && ! qty_table[REG_QTY (REGNO (x))].const_rtx
           && ! elt->is_const)
@@ -1680,20 +1512,20 @@ insert (x, classp, hash, mode)
 
       for (p = classp; p != 0; p = p->next_same_value)
        {
-         if (p->is_const && GET_CODE (p->exp) != REG)
+         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_if_possible (GET_MODE (x), p->exp);
+               = gen_lowpart (GET_MODE (x), p->exp);
              x_ent->const_insn = this_insn;
              break;
            }
        }
     }
 
-  else if (GET_CODE (x) == REG
+  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;
@@ -1710,7 +1542,7 @@ insert (x, classp, hash, mode)
       if (subexp != 0)
        {
          /* Get the integer-free subexpression in the hash table.  */
-         subhash = safe_hash (subexp, mode) & HASH_MASK;
+         subhash = SAFE_HASH (subexp, mode);
          subelt = lookup (subexp, subhash, mode);
          if (subelt == 0)
            subelt = insert (subexp, NULL, subhash, mode);
@@ -1741,10 +1573,9 @@ insert (x, classp, hash, mode)
    Any invalid entries in CLASS2 will not be copied.  */
 
 static void
-merge_equiv_classes (class1, class2)
-     struct table_elt *class1, *class2;
+merge_equiv_classes (struct table_elt *class1, struct table_elt *class2)
 {
-  struct table_elt *elt, *next, *new;
+  struct table_elt *elt, *next, *new_elt;
 
   /* Ensure we start with the head of the classes.  */
   class1 = class1->first_same_value;
@@ -1765,23 +1596,31 @@ merge_equiv_classes (class1, class2)
       /* 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 (GET_CODE (exp) == REG || exp_equiv_p (exp, exp, 1, 0))
+      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 (GET_CODE (exp) == REG)
-           delete_reg_equiv (REGNO (exp));
+         if (REG_P (exp))
+           {
+             need_rehash = REGNO_QTY_VALID_P (REGNO (exp));
+             delete_reg_equiv (REGNO (exp));
+           }
 
-         remove_from_table (elt, hash);
+         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))
+         if (insert_regs (exp, class1, 0) || need_rehash)
            {
              rehash_using_reg (exp);
              hash = HASH (exp, mode);
            }
-         new = insert (exp, class1, hash, mode);
-         new->in_memory = hash_arg_in_memory;
+         new_elt = insert (exp, class1, hash, mode);
+         new_elt->in_memory = hash_arg_in_memory;
        }
     }
 }
@@ -1789,7 +1628,7 @@ merge_equiv_classes (class1, class2)
 /* Flush the entire hash table.  */
 
 static void
-flush_hash_table ()
+flush_hash_table (void)
 {
   int i;
   struct table_elt *p;
@@ -1799,8 +1638,8 @@ flush_hash_table ()
       {
        /* Note that invalidate can remove elements
           after P in the current hash chain.  */
-       if (GET_CODE (p->exp) == REG)
-         invalidate (p->exp, p->mode);
+       if (REG_P (p->exp))
+         invalidate (p->exp, VOIDmode);
        else
          remove_from_table (p, i);
       }
@@ -1811,16 +1650,16 @@ struct check_dependence_data
 {
   enum machine_mode mode;
   rtx exp;
+  rtx addr;
 };
 
 static int
-check_dependence (x, data)
-     rtx *x;
-     void *data;
+check_dependence (rtx *x, void *data)
 {
   struct check_dependence_data *d = (struct check_dependence_data *) data;
-  if (*x && GET_CODE (*x) == MEM)
-    return true_dependence (d->exp, d->mode, *x, cse_rtx_varies_p);
+  if (*x && MEM_P (*x))
+    return canon_true_dependence (d->exp, d->mode, d->addr, *x, NULL_RTX,
+                                 cse_rtx_varies_p);
   else
     return 0;
 }
@@ -1838,12 +1677,11 @@ check_dependence (x, data)
    or it may be either of those plus a numeric offset.  */
 
 static void
-invalidate (x, full_mode)
-     rtx x;
-     enum machine_mode full_mode;
+invalidate (rtx x, enum machine_mode full_mode)
 {
   int i;
   struct table_elt *p;
+  rtx addr;
 
   switch (GET_CODE (x))
     {
@@ -1867,22 +1705,15 @@ invalidate (x, full_mode)
 
        delete_reg_equiv (regno);
        REG_TICK (regno)++;
+       SUBREG_TICKED (regno) = -1;
 
        if (regno >= FIRST_PSEUDO_REGISTER)
-         {
-           /* Because a register can be referenced in more than one mode,
-              we might have to remove more than one table entry.  */
-           struct table_elt *elt;
-
-           while ((elt = lookup_for_remove (x, hash, GET_MODE (x))))
-             remove_from_table (elt, hash);
-         }
+         remove_pseudo_from_table (x, hash);
        else
          {
            HOST_WIDE_INT in_table
              = TEST_HARD_REG_BIT (hard_regs_in_table, regno);
-           unsigned int endregno
-             = regno + HARD_REGNO_NREGS (regno, GET_MODE (x));
+           unsigned int endregno = END_HARD_REGNO (x);
            unsigned int tregno, tendregno, rn;
            struct table_elt *p, *next;
 
@@ -1894,6 +1725,7 @@ invalidate (x, full_mode)
                CLEAR_HARD_REG_BIT (hard_regs_in_table, rn);
                delete_reg_equiv (rn);
                REG_TICK (rn)++;
+               SUBREG_TICKED (rn) = -1;
              }
 
            if (in_table)
@@ -1902,13 +1734,12 @@ invalidate (x, full_mode)
                  {
                    next = p->next_same_hash;
 
-                   if (GET_CODE (p->exp) != REG
+                   if (!REG_P (p->exp)
                        || REGNO (p->exp) >= FIRST_PSEUDO_REGISTER)
                      continue;
 
                    tregno = REGNO (p->exp);
-                   tendregno
-                     = tregno + HARD_REGNO_NREGS (tregno, GET_MODE (p->exp));
+                   tendregno = END_HARD_REGNO (p->exp);
                    if (tendregno > regno && tregno < endregno)
                      remove_from_table (p, hash);
                  }
@@ -1932,6 +1763,7 @@ invalidate (x, full_mode)
       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);
@@ -1959,6 +1791,7 @@ invalidate (x, full_mode)
                  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);
@@ -1968,7 +1801,7 @@ invalidate (x, full_mode)
       return;
 
     default:
-      abort ();
+      gcc_unreachable ();
     }
 }
 \f
@@ -1978,8 +1811,7 @@ invalidate (x, full_mode)
    expressions to reappear as valid.  */
 
 static void
-remove_invalid_refs (regno)
-     unsigned int regno;
+remove_invalid_refs (unsigned int regno)
 {
   unsigned int i;
   struct table_elt *p, *next;
@@ -1988,8 +1820,8 @@ remove_invalid_refs (regno)
     for (p = table[i]; p; p = next)
       {
        next = p->next_same_hash;
-       if (GET_CODE (p->exp) != REG
-           && refers_to_regno_p (regno, regno + 1, p->exp, (rtx*) 0))
+       if (!REG_P (p->exp)
+           && refers_to_regno_p (regno, regno + 1, p->exp, (rtx *) 0))
          remove_from_table (p, i);
       }
 }
@@ -1997,10 +1829,8 @@ remove_invalid_refs (regno)
 /* Likewise for a subreg with subreg_reg REGNO, subreg_byte OFFSET,
    and mode MODE.  */
 static void
-remove_invalid_subreg_refs (regno, offset, mode)
-     unsigned int regno;
-     unsigned int offset;
-     enum machine_mode mode;
+remove_invalid_subreg_refs (unsigned int regno, unsigned int offset,
+                           enum machine_mode mode)
 {
   unsigned int i;
   struct table_elt *p, *next;
@@ -2012,14 +1842,14 @@ remove_invalid_subreg_refs (regno, offset, mode)
        rtx exp = p->exp;
        next = p->next_same_hash;
 
-       if (GET_CODE (exp) != REG
+       if (!REG_P (exp)
            && (GET_CODE (exp) != SUBREG
-               || GET_CODE (SUBREG_REG (exp)) != REG
+               || !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))
+           && refers_to_regno_p (regno, regno + 1, p->exp, (rtx *) 0))
          remove_from_table (p, i);
       }
 }
@@ -2030,8 +1860,7 @@ remove_invalid_subreg_refs (regno, offset, mode)
    This is called when we make a jump equivalence.  */
 
 static void
-rehash_using_reg (x)
-     rtx x;
+rehash_using_reg (rtx x)
 {
   unsigned int i;
   struct table_elt *p, *next;
@@ -2043,22 +1872,21 @@ rehash_using_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 (GET_CODE (x) != REG
+  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.  We can skip
-     objects that are registers, since they are handled specially.  */
+     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 (GET_CODE (p->exp) != REG && reg_mentioned_p (x, p->exp)
-           && exp_equiv_p (p->exp, p->exp, 1, 0)
-           && i != (hash = safe_hash (p->exp, p->mode) & HASH_MASK))
+       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;
@@ -2081,7 +1909,7 @@ rehash_using_reg (x)
    register.  Also update their TICK values.  */
 
 static void
-invalidate_for_call ()
+invalidate_for_call (void)
 {
   unsigned int regno, endregno;
   unsigned int i;
@@ -2099,7 +1927,10 @@ invalidate_for_call ()
       {
        delete_reg_equiv (regno);
        if (REG_TICK (regno) >= 0)
-         REG_TICK (regno)++;
+         {
+           REG_TICK (regno)++;
+           SUBREG_TICKED (regno) = -1;
+         }
 
        in_table |= (TEST_HARD_REG_BIT (hard_regs_in_table, regno) != 0);
       }
@@ -2114,12 +1945,12 @@ invalidate_for_call ()
        {
          next = p->next_same_hash;
 
-         if (GET_CODE (p->exp) != REG
+         if (!REG_P (p->exp)
              || REGNO (p->exp) >= FIRST_PSEUDO_REGISTER)
            continue;
 
          regno = REGNO (p->exp);
-         endregno = regno + HARD_REGNO_NREGS (regno, GET_MODE (p->exp));
+         endregno = END_HARD_REGNO (p->exp);
 
          for (i = regno; i < endregno; i++)
            if (TEST_HARD_REG_BIT (regs_invalidated_by_call, i))
@@ -2137,9 +1968,7 @@ invalidate_for_call ()
    If none can be found, return 0.  */
 
 static rtx
-use_related_value (x, elt)
-     rtx x;
-     struct table_elt *elt;
+use_related_value (rtx x, struct table_elt *elt)
 {
   struct table_elt *relt = 0;
   struct table_elt *p, *q;
@@ -2156,7 +1985,7 @@ use_related_value (x, elt)
       rtx subexp = get_related_value (x);
       if (subexp != 0)
        relt = lookup (subexp,
-                      safe_hash (subexp, GET_MODE (subexp)) & HASH_MASK,
+                      SAFE_HASH (subexp, GET_MODE (subexp)),
                       GET_MODE (subexp));
     }
 
@@ -2182,7 +2011,7 @@ use_related_value (x, elt)
        q = 0;
       else
        for (q = p->first_same_value; q; q = q->next_same_value)
-         if (GET_CODE (q->exp) == REG)
+         if (REG_P (q->exp))
            break;
 
       if (q)
@@ -2205,14 +2034,14 @@ use_related_value (x, elt)
   return plus_constant (q->exp, offset);
 }
 \f
+
 /* Hash a string.  Just add its bytes up.  */
 static inline unsigned
-canon_hash_string (ps)
-     const char *ps;
+hash_rtx_string (const char *ps)
 {
   unsigned hash = 0;
-  const unsigned char *p = (const unsigned char *)ps;
-  
+  const unsigned char *p = (const unsigned char *) ps;
+
   if (p)
     while (*p)
       hash += *p++;
@@ -2220,82 +2049,89 @@ canon_hash_string (ps)
   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 if any subexpression is volatile.
+/* 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.  */
 
-   Store 1 in hash_arg_in_memory 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.  */
-
-static unsigned
-canon_hash (x, mode)
-     rtx x;
-     enum machine_mode mode;
+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;
 
-  /* repeat is used to turn tail-recursion into iteration.  */
+  /* 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);
-       bool record;
 
-       /* 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.  */
-
-       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)
+       if (do_not_record_p && !reload_completed)
          {
-           do_not_record = 1;
-           return 0;
+           /* 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) REG << 7) + (unsigned) REG_QTY (regno);
+       hash += ((unsigned int) REG << 7);
+        hash += (have_reg_qty ? (unsigned) REG_QTY (regno) : regno);
        return hash;
       }
 
@@ -2304,9 +2140,9 @@ canon_hash (x, mode)
        want to have to forget unrelated subregs when one subreg changes.  */
     case SUBREG:
       {
-       if (GET_CODE (SUBREG_REG (x)) == REG)
+       if (REG_P (SUBREG_REG (x)))
          {
-           hash += (((unsigned) SUBREG << 7)
+           hash += (((unsigned int) SUBREG << 7)
                     + REGNO (SUBREG_REG (x))
                     + (SUBREG_BYTE (x) / UNITS_PER_WORD));
            return hash;
@@ -2315,25 +2151,24 @@ canon_hash (x, mode)
       }
 
     case CONST_INT:
-      {
-       unsigned HOST_WIDE_INT tem = INTVAL (x);
-       hash += ((unsigned) CONST_INT << 7) + (unsigned) mode + tem;
-       return hash;
-      }
+      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) code + (unsigned) GET_MODE (x);
+      hash += (unsigned int) code + (unsigned int) GET_MODE (x);
       if (GET_MODE (x) != VOIDmode)
-       for (i = 2; i < GET_RTX_LENGTH (CONST_DOUBLE); i++)
-         {
-           unsigned HOST_WIDE_INT tem = XWINT (x, i);
-           hash += tem;
-         }
+       hash += real_hash (CONST_DOUBLE_REAL_VALUE (x));
       else
-       hash += ((unsigned) CONST_DOUBLE_LOW (x)
-                + (unsigned) CONST_DOUBLE_HIGH (x));
+       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:
@@ -2346,7 +2181,9 @@ canon_hash (x, mode)
        for (i = 0; i < units; ++i)
          {
            elt = CONST_VECTOR_ELT (x, i);
-           hash += canon_hash (elt, GET_MODE (elt));
+           hash += hash_rtx_cb (elt, GET_MODE (elt),
+                                 do_not_record_p, hash_arg_in_memory_p, 
+                                 have_reg_qty, cb);
          }
 
        return hash;
@@ -2354,25 +2191,40 @@ canon_hash (x, mode)
 
       /* Assume there is only one rtx object for any given label.  */
     case LABEL_REF:
-      hash += ((unsigned) LABEL_REF << 7) + (unsigned long) XEXP (x, 0);
+      /* 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:
-      hash += ((unsigned) SYMBOL_REF << 7) + (unsigned long) XSTR (x, 0);
-      return hash;
+      {
+       /* 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 (MEM_VOLATILE_P (x) || GET_MODE (x) == BLKmode)
+      if (do_not_record_p && (MEM_VOLATILE_P (x) || GET_MODE (x) == BLKmode))
        {
-         do_not_record = 1;
+         *do_not_record_p = 1;
          return 0;
        }
-      if (! RTX_UNCHANGING_P (x) || FIXED_BASE_PLUS_P (XEXP (x, 0)))
-       {
-         hash_arg_in_memory = 1;
-       }
+      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;
@@ -2383,15 +2235,16 @@ canon_hash (x, mode)
       /* 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.  */
-      if (GET_CODE (XEXP (x, 0)) == MEM
+        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;
+         hash += (unsigned) USE;
          x = XEXP (x, 0);
 
-         if (! RTX_UNCHANGING_P (x) || FIXED_BASE_PLUS_P (XEXP (x, 0)))
-           hash_arg_in_memory = 1;
+         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.  */
@@ -2411,34 +2264,41 @@ canon_hash (x, mode)
     case CC0:
     case CALL:
     case UNSPEC_VOLATILE:
-      do_not_record = 1;
-      return 0;
+      if (do_not_record_p) {
+        *do_not_record_p = 1;
+        return 0;
+      }
+      else
+        return hash;
+      break;
 
     case ASM_OPERANDS:
-      if (MEM_VOLATILE_P (x))
+      if (do_not_record_p && MEM_VOLATILE_P (x))
        {
-         do_not_record = 1;
+         *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)
-           + canon_hash_string (ASM_OPERANDS_TEMPLATE (x))
-           + canon_hash_string (ASM_OPERANDS_OUTPUT_CONSTRAINT (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 += (canon_hash (ASM_OPERANDS_INPUT (x, i),
-                                      GET_MODE (ASM_OPERANDS_INPUT (x, i)))
-                          + canon_hash_string (ASM_OPERANDS_INPUT_CONSTRAINT
-                                               (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 += canon_hash_string (ASM_OPERANDS_INPUT_CONSTRAINT (x, 0));
+             hash += hash_rtx_string (ASM_OPERANDS_INPUT_CONSTRAINT (x, 0));
              x = ASM_OPERANDS_INPUT (x, 0);
              mode = GET_MODE (x);
              goto repeat;
@@ -2457,52 +2317,90 @@ canon_hash (x, mode)
   fmt = GET_RTX_FORMAT (code);
   for (; i >= 0; i--)
     {
-      if (fmt[i] == 'e')
+      switch (fmt[i])
        {
-         rtx tem = XEXP (x, 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 = tem;
+             x = XEXP (x, i);
              goto repeat;
            }
-         hash += canon_hash (tem, 0);
-       }
-      else if (fmt[i] == 'E')
-       for (j = 0; j < XVECLEN (x, i); j++)
-         hash += canon_hash (XVECEXP (x, i, j), 0);
-      else if (fmt[i] == 's')
-       hash += canon_hash_string (XSTR (x, i));
-      else if (fmt[i] == 'i')
-       {
-         unsigned tem = XINT (x, i);
-         hash += tem;
+          
+         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 ();
        }
-      else if (fmt[i] == '0' || fmt[i] == 't')
-       /* Unused.  */
-       ;
-      else
-       abort ();
     }
+
   return hash;
 }
 
-/* Like canon_hash but with no side effects.  */
+/* 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.  */
 
-static unsigned
-safe_hash (x, mode)
-     rtx x;
-     enum machine_mode mode;
+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)
 {
-  int save_do_not_record = do_not_record;
-  int save_hash_arg_in_memory = hash_arg_in_memory;
-  unsigned hash = canon_hash (x, mode);
-  hash_arg_in_memory = save_hash_arg_in_memory;
-  do_not_record = save_do_not_record;
-  return hash;
+  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);
 }
 \f
 /* Return 1 iff X and Y would canonicalize into the same thing,
@@ -2512,19 +2410,10 @@ safe_hash (x, mode)
    and Y was found in the hash table.  We check register refs
    in Y for being marked as valid.
 
-   If EQUAL_VALUES is nonzero, we allow a register to match a constant value
-   that is known to be in the register.  Ordinarily, we don't allow them
-   to match, because letting them match would cause unpredictable results
-   in all the places that search a hash table chain for an equivalent
-   for a given value.  A possible equivalent that has different structure
-   has its hash code computed from different data.  Whether the hash code
-   is the same as that of the given value is pure luck.  */
+   If FOR_GCSE is true, we compare X and Y for equivalence for GCSE.  */
 
-static int
-exp_equiv_p (x, y, validate, equal_values)
-     rtx x, y;
-     int validate;
-     int equal_values;
+int
+exp_equiv_p (const_rtx x, const_rtx y, int validate, bool for_gcse)
 {
   int i, j;
   enum rtx_code code;
@@ -2534,42 +2423,13 @@ exp_equiv_p (x, y, validate, equal_values)
      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))
-    {
-      if (!equal_values)
-       return 0;
-
-      /* If X is a constant and Y is a register or vice versa, they may be
-        equivalent.  We only have to validate if Y is a register.  */
-      if (CONSTANT_P (x) && GET_CODE (y) == REG
-         && REGNO_QTY_VALID_P (REGNO (y)))
-       {
-         int y_q = REG_QTY (REGNO (y));
-         struct qty_table_elem *y_ent = &qty_table[y_q];
-
-         if (GET_MODE (y) == y_ent->mode
-             && rtx_equal_p (x, y_ent->const_rtx)
-             && (! validate || REG_IN_TABLE (REGNO (y)) == REG_TICK (REGNO (y))))
-           return 1;
-       }
-
-      if (CONSTANT_P (y) && code == REG
-         && 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
-             && rtx_equal_p (y, x_ent->const_rtx))
-           return 1;
-       }
-
-      return 0;
-    }
+    return 0;
 
   /* (MULT:SI x y) and (MULT:HI x y) are NOT equivalent.  */
   if (GET_MODE (x) != GET_MODE (y))
@@ -2580,6 +2440,8 @@ exp_equiv_p (x, y, validate, equal_values)
     case PC:
     case CC0:
     case CONST_INT:
+    case CONST_DOUBLE:
+    case CONST_FIXED:
       return x == y;
 
     case LABEL_REF:
@@ -2589,29 +2451,56 @@ exp_equiv_p (x, y, validate, equal_values)
       return XSTR (x, 0) == XSTR (y, 0);
 
     case REG:
-      {
-       unsigned int regno = REGNO (y);
-       unsigned int endregno
-         = regno + (regno >= FIRST_PSEUDO_REGISTER ? 1
-                    : HARD_REGNO_NREGS (regno, GET_MODE (y)));
-       unsigned int i;
+      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 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 (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;
 
-       if (! validate)
          return 1;
+       }
 
-       for (i = regno; i < endregno; i++)
-         if (REG_IN_TABLE (i) != REG_TICK (i))
+    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;
 
-       return 1;
-      }
+         /* 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:
@@ -2621,13 +2510,14 @@ exp_equiv_p (x, y, validate, equal_values)
     case XOR:
     case NE:
     case EQ:
-      return ((exp_equiv_p (XEXP (x, 0), XEXP (y, 0), validate, equal_values)
+      return ((exp_equiv_p (XEXP (x, 0), XEXP (y, 0),
+                            validate, for_gcse)
               && exp_equiv_p (XEXP (x, 1), XEXP (y, 1),
-                              validate, equal_values))
+                               validate, for_gcse))
              || (exp_equiv_p (XEXP (x, 0), XEXP (y, 1),
-                              validate, equal_values)
+                               validate, for_gcse)
                  && exp_equiv_p (XEXP (x, 1), XEXP (y, 0),
-                                 validate, equal_values)));
+                                  validate, for_gcse)));
 
     case ASM_OPERANDS:
       /* We don't use the generic code below because we want to
@@ -2650,7 +2540,7 @@ exp_equiv_p (x, y, validate, equal_values)
          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, equal_values)
+                              validate, for_gcse)
                || strcmp (ASM_OPERANDS_INPUT_CONSTRAINT (x, i),
                           ASM_OPERANDS_INPUT_CONSTRAINT (y, i)))
              return 0;
@@ -2663,7 +2553,7 @@ exp_equiv_p (x, y, validate, equal_values)
     }
 
   /* Compare the elements.  If any pair of corresponding elements
-     fail to match, return 0 for the whole things.  */
+     fail to match, return 0 for the whole thing.  */
 
   fmt = GET_RTX_FORMAT (code);
   for (i = GET_RTX_LENGTH (code) - 1; i >= 0; i--)
@@ -2671,7 +2561,8 @@ exp_equiv_p (x, y, validate, equal_values)
       switch (fmt[i])
        {
        case 'e':
-         if (! exp_equiv_p (XEXP (x, i), XEXP (y, i), validate, equal_values))
+         if (! exp_equiv_p (XEXP (x, i), XEXP (y, i),
+                             validate, for_gcse))
            return 0;
          break;
 
@@ -2680,7 +2571,7 @@ exp_equiv_p (x, y, validate, equal_values)
            return 0;
          for (j = 0; j < XVECLEN (x, i); j++)
            if (! exp_equiv_p (XVECEXP (x, i, j), XVECEXP (y, i, j),
-                              validate, equal_values))
+                               validate, for_gcse))
              return 0;
          break;
 
@@ -2704,7 +2595,7 @@ exp_equiv_p (x, y, validate, equal_values)
          break;
 
        default:
-         abort ();
+         gcc_unreachable ();
        }
     }
 
@@ -2715,16 +2606,14 @@ exp_equiv_p (x, y, validate, equal_values)
    executions of the program.  0 means X can be compared reliably
    against certain constants or near-constants.  */
 
-static int
-cse_rtx_varies_p (x, from_alias)
-     rtx x;
-     int from_alias;
+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 (GET_CODE (x) == REG
+  if (REG_P (x)
       && REGNO_QTY_VALID_P (REGNO (x)))
     {
       int x_q = REG_QTY (REGNO (x));
@@ -2737,7 +2626,7 @@ cse_rtx_varies_p (x, from_alias)
 
   if (GET_CODE (x) == PLUS
       && GET_CODE (XEXP (x, 1)) == CONST_INT
-      && GET_CODE (XEXP (x, 0)) == REG
+      && REG_P (XEXP (x, 0))
       && REGNO_QTY_VALID_P (REGNO (XEXP (x, 0))))
     {
       int x0_q = REG_QTY (REGNO (XEXP (x, 0)));
@@ -2754,8 +2643,8 @@ cse_rtx_varies_p (x, from_alias)
      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
-      && GET_CODE (XEXP (x, 0)) == REG
-      && GET_CODE (XEXP (x, 1)) == REG
+      && 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))))
     {
@@ -2774,21 +2663,35 @@ cse_rtx_varies_p (x, from_alias)
   return rtx_varies_p (x, from_alias);
 }
 \f
+/* 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 non-zero and we are replacing a pseudo with a hard register
-   or vice versa, validate_change is used to ensure that INSN remains valid
-   after we make our substitution.  The calls are made with IN_GROUP non-zero
+   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 (x, insn)
-     rtx x;
-     rtx insn;
+canon_reg (rtx x, rtx insn)
 {
   int i;
   enum rtx_code code;
@@ -2805,6 +2708,7 @@ canon_reg (x, insn)
     case CONST:
     case CONST_INT:
     case CONST_DOUBLE:
+    case CONST_FIXED:
     case CONST_VECTOR:
     case SYMBOL_REF:
     case LABEL_REF:
@@ -2845,270 +2749,15 @@ canon_reg (x, insn)
       int j;
 
       if (fmt[i] == 'e')
-       {
-         rtx new = canon_reg (XEXP (x, i), insn);
-         int insn_code;
-
-         /* If replacing pseudo with hard reg or vice versa, ensure the
-            insn remains valid.  Likewise if the insn has MATCH_DUPs.  */
-         if (insn != 0 && new != 0
-             && GET_CODE (new) == REG && GET_CODE (XEXP (x, i)) == REG
-             && (((REGNO (new) < FIRST_PSEUDO_REGISTER)
-                  != (REGNO (XEXP (x, i)) < FIRST_PSEUDO_REGISTER))
-                 || (insn_code = recog_memoized (insn)) < 0
-                 || insn_data[insn_code].n_dups > 0))
-           validate_change (insn, &XEXP (x, i), new, 1);
-         else
-           XEXP (x, i) = new;
-       }
+       validate_canon_reg (&XEXP (x, i), insn);
       else if (fmt[i] == 'E')
        for (j = 0; j < XVECLEN (x, i); j++)
-         XVECEXP (x, i, j) = canon_reg (XVECEXP (x, i, j), insn);
+         validate_canon_reg (&XVECEXP (x, i, j), insn);
     }
 
   return x;
 }
 \f
-/* LOC is a location within INSN that is an operand address (the contents of
-   a MEM).  Find the best equivalent address to use that is valid for this
-   insn.
-
-   On most CISC machines, complicated address modes are costly, and rtx_cost
-   is a good approximation for that cost.  However, most RISC machines have
-   only a few (usually only one) memory reference formats.  If an address is
-   valid at all, it is often just as cheap as any other address.  Hence, for
-   RISC machines, we use the configuration macro `ADDRESS_COST' to compare the
-   costs of various addresses.  For two addresses of equal cost, choose the one
-   with the highest `rtx_cost' value as that has the potential of eliminating
-   the most insns.  For equal costs, we choose the first in the equivalence
-   class.  Note that we ignore the fact that pseudo registers are cheaper
-   than hard registers here because we would also prefer the pseudo registers.
-  */
-
-static void
-find_best_addr (insn, loc, mode)
-     rtx insn;
-     rtx *loc;
-     enum machine_mode mode;
-{
-  struct table_elt *elt;
-  rtx addr = *loc;
-#ifdef ADDRESS_COST
-  struct table_elt *p;
-  int found_better = 1;
-#endif
-  int save_do_not_record = do_not_record;
-  int save_hash_arg_in_memory = hash_arg_in_memory;
-  int addr_volatile;
-  int regno;
-  unsigned hash;
-
-  /* Do not try to replace constant addresses or addresses of local and
-     argument slots.  These MEM expressions are made only once and inserted
-     in many instructions, as well as being used to control symbol table
-     output.  It is not safe to clobber them.
-
-     There are some uncommon cases where the address is already in a register
-     for some reason, but we cannot take advantage of that because we have
-     no easy way to unshare the MEM.  In addition, looking up all stack
-     addresses is costly.  */
-  if ((GET_CODE (addr) == PLUS
-       && GET_CODE (XEXP (addr, 0)) == REG
-       && GET_CODE (XEXP (addr, 1)) == CONST_INT
-       && (regno = REGNO (XEXP (addr, 0)),
-          regno == FRAME_POINTER_REGNUM || regno == HARD_FRAME_POINTER_REGNUM
-          || regno == ARG_POINTER_REGNUM))
-      || (GET_CODE (addr) == REG
-         && (regno = REGNO (addr), regno == FRAME_POINTER_REGNUM
-             || regno == HARD_FRAME_POINTER_REGNUM
-             || regno == ARG_POINTER_REGNUM))
-      || GET_CODE (addr) == ADDRESSOF
-      || CONSTANT_ADDRESS_P (addr))
-    return;
-
-  /* If this address is not simply a register, try to fold it.  This will
-     sometimes simplify the expression.  Many simplifications
-     will not be valid, but some, usually applying the associative rule, will
-     be valid and produce better code.  */
-  if (GET_CODE (addr) != REG)
-    {
-      rtx folded = fold_rtx (copy_rtx (addr), NULL_RTX);
-      int addr_folded_cost = address_cost (folded, mode);
-      int addr_cost = address_cost (addr, mode);
-
-      if ((addr_folded_cost < addr_cost
-          || (addr_folded_cost == addr_cost
-              /* ??? The rtx_cost comparison is left over from an older
-                 version of this code.  It is probably no longer helpful.  */
-              && (rtx_cost (folded, MEM) > rtx_cost (addr, MEM)
-                  || approx_reg_cost (folded) < approx_reg_cost (addr))))
-         && validate_change (insn, loc, folded, 0))
-       addr = folded;
-    }
-
-  /* If this address is not in the hash table, we can't look for equivalences
-     of the whole address.  Also, ignore if volatile.  */
-
-  do_not_record = 0;
-  hash = HASH (addr, Pmode);
-  addr_volatile = do_not_record;
-  do_not_record = save_do_not_record;
-  hash_arg_in_memory = save_hash_arg_in_memory;
-
-  if (addr_volatile)
-    return;
-
-  elt = lookup (addr, hash, Pmode);
-
-#ifndef ADDRESS_COST
-  if (elt)
-    {
-      int our_cost = elt->cost;
-
-      /* Find the lowest cost below ours that works.  */
-      for (elt = elt->first_same_value; elt; elt = elt->next_same_value)
-       if (elt->cost < our_cost
-           && (GET_CODE (elt->exp) == REG
-               || exp_equiv_p (elt->exp, elt->exp, 1, 0))
-           && validate_change (insn, loc,
-                               canon_reg (copy_rtx (elt->exp), NULL_RTX), 0))
-         return;
-    }
-#else
-
-  if (elt)
-    {
-      /* We need to find the best (under the criteria documented above) entry
-        in the class that is valid.  We use the `flag' field to indicate
-        choices that were invalid and iterate until we can't find a better
-        one that hasn't already been tried.  */
-
-      for (p = elt->first_same_value; p; p = p->next_same_value)
-       p->flag = 0;
-
-      while (found_better)
-       {
-         int best_addr_cost = address_cost (*loc, mode);
-         int best_rtx_cost = (elt->cost + 1) >> 1;
-         int exp_cost;
-         struct table_elt *best_elt = elt;
-
-         found_better = 0;
-         for (p = elt->first_same_value; p; p = p->next_same_value)
-           if (! p->flag)
-             {
-               if ((GET_CODE (p->exp) == REG
-                    || exp_equiv_p (p->exp, p->exp, 1, 0))
-                   && ((exp_cost = address_cost (p->exp, mode)) < best_addr_cost
-                       || (exp_cost == best_addr_cost
-                           && ((p->cost + 1) >> 1) > best_rtx_cost)))
-                 {
-                   found_better = 1;
-                   best_addr_cost = exp_cost;
-                   best_rtx_cost = (p->cost + 1) >> 1;
-                   best_elt = p;
-                 }
-             }
-
-         if (found_better)
-           {
-             if (validate_change (insn, loc,
-                                  canon_reg (copy_rtx (best_elt->exp),
-                                             NULL_RTX), 0))
-               return;
-             else
-               best_elt->flag = 1;
-           }
-       }
-    }
-
-  /* If the address is a binary operation with the first operand a register
-     and the second a constant, do the same as above, but looking for
-     equivalences of the register.  Then try to simplify before checking for
-     the best address to use.  This catches a few cases:  First is when we
-     have REG+const and the register is another REG+const.  We can often merge
-     the constants and eliminate one insn and one register.  It may also be
-     that a machine has a cheap REG+REG+const.  Finally, this improves the
-     code on the Alpha for unaligned byte stores.  */
-
-  if (flag_expensive_optimizations
-      && (GET_RTX_CLASS (GET_CODE (*loc)) == '2'
-         || GET_RTX_CLASS (GET_CODE (*loc)) == 'c')
-      && GET_CODE (XEXP (*loc, 0)) == REG
-      && GET_CODE (XEXP (*loc, 1)) == CONST_INT)
-    {
-      rtx c = XEXP (*loc, 1);
-
-      do_not_record = 0;
-      hash = HASH (XEXP (*loc, 0), Pmode);
-      do_not_record = save_do_not_record;
-      hash_arg_in_memory = save_hash_arg_in_memory;
-
-      elt = lookup (XEXP (*loc, 0), hash, Pmode);
-      if (elt == 0)
-       return;
-
-      /* We need to find the best (under the criteria documented above) entry
-        in the class that is valid.  We use the `flag' field to indicate
-        choices that were invalid and iterate until we can't find a better
-        one that hasn't already been tried.  */
-
-      for (p = elt->first_same_value; p; p = p->next_same_value)
-       p->flag = 0;
-
-      while (found_better)
-       {
-         int best_addr_cost = address_cost (*loc, mode);
-         int best_rtx_cost = (COST (*loc) + 1) >> 1;
-         struct table_elt *best_elt = elt;
-         rtx best_rtx = *loc;
-         int count;
-
-         /* This is at worst case an O(n^2) algorithm, so limit our search
-            to the first 32 elements on the list.  This avoids trouble
-            compiling code with very long basic blocks that can easily
-            call simplify_gen_binary so many times that we run out of
-            memory.  */
-
-         found_better = 0;
-         for (p = elt->first_same_value, count = 0;
-              p && count < 32;
-              p = p->next_same_value, count++)
-           if (! p->flag
-               && (GET_CODE (p->exp) == REG
-                   || exp_equiv_p (p->exp, p->exp, 1, 0)))
-             {
-               rtx new = simplify_gen_binary (GET_CODE (*loc), Pmode,
-                                              p->exp, c);
-               int new_cost;
-               new_cost = address_cost (new, mode);
-
-               if (new_cost < best_addr_cost
-                   || (new_cost == best_addr_cost
-                       && (COST (new) + 1) >> 1 > best_rtx_cost))
-                 {
-                   found_better = 1;
-                   best_addr_cost = new_cost;
-                   best_rtx_cost = (COST (new) + 1) >> 1;
-                   best_elt = p;
-                   best_rtx = new;
-                 }
-             }
-
-         if (found_better)
-           {
-             if (validate_change (insn, loc,
-                                  canon_reg (copy_rtx (best_rtx),
-                                             NULL_RTX), 0))
-               return;
-             else
-               best_elt->flag = 1;
-           }
-       }
-    }
-#endif
-}
-\f
 /* 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.
@@ -3122,10 +2771,8 @@ find_best_addr (insn, loc, mode)
    A or the code corresponding to the inverse of the comparison.  */
 
 static enum rtx_code
-find_comparison_args (code, parg1, parg2, pmode1, pmode2)
-     enum rtx_code code;
-     rtx *parg1, *parg2;
-     enum machine_mode *pmode1, *pmode2;
+find_comparison_args (enum rtx_code code, rtx *parg1, rtx *parg2,
+                     enum machine_mode *pmode1, enum machine_mode *pmode2)
 {
   rtx arg1, arg2;
 
@@ -3135,7 +2782,7 @@ find_comparison_args (code, parg1, parg2, pmode1, pmode2)
 
   while (arg2 == CONST0_RTX (GET_MODE (arg1)))
     {
-      /* Set non-zero when we find something of interest.  */
+      /* Set nonzero when we find something of interest.  */
       rtx x = 0;
       int reverse_code = 0;
       struct table_elt *p = 0;
@@ -3151,15 +2798,19 @@ find_comparison_args (code, parg1, parg2, pmode1, pmode2)
       /* If ARG1 is a comparison operator and CODE is testing for
         STORE_FLAG_VALUE, get the inner arguments.  */
 
-      else if (GET_RTX_CLASS (GET_CODE (arg1)) == '<')
+      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
-             || (GET_MODE_CLASS (GET_MODE (arg1)) == MODE_FLOAT
-                 && (REAL_VALUE_NEGATIVE
-                     (FLOAT_STORE_FLAG_VALUE (GET_MODE (arg1)))))
+             || (SCALAR_FLOAT_MODE_P (GET_MODE (arg1))
+                 && (fsfv = FLOAT_STORE_FLAG_VALUE (GET_MODE (arg1)),
+                     REAL_VALUE_NEGATIVE (fsfv)))
 #endif
              )
            x = arg1;
@@ -3167,9 +2818,9 @@ find_comparison_args (code, parg1, parg2, pmode1, pmode2)
                   || (GET_MODE_CLASS (GET_MODE (arg1)) == MODE_INT
                       && code == GE && STORE_FLAG_VALUE == -1)
 #ifdef FLOAT_STORE_FLAG_VALUE
-                  || (GET_MODE_CLASS (GET_MODE (arg1)) == MODE_FLOAT
-                      && (REAL_VALUE_NEGATIVE
-                          (FLOAT_STORE_FLAG_VALUE (GET_MODE (arg1)))))
+                  || (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;
@@ -3184,8 +2835,7 @@ find_comparison_args (code, parg1, parg2, pmode1, pmode2)
       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)) & HASH_MASK,
-                   GET_MODE (arg1));
+       p = lookup (arg1, SAFE_HASH (arg1, GET_MODE (arg1)), GET_MODE (arg1));
       if (p)
        {
          p = p->first_same_value;
@@ -3205,9 +2855,12 @@ find_comparison_args (code, parg1, parg2, pmode1, pmode2)
       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, 0))
+         if (! exp_equiv_p (p->exp, p->exp, 1, false))
            continue;
 
          if (GET_CODE (p->exp) == COMPARE
@@ -3228,12 +2881,12 @@ find_comparison_args (code, parg1, parg2, pmode1, pmode2)
                              << (GET_MODE_BITSIZE (inner_mode) - 1))))
 #ifdef FLOAT_STORE_FLAG_VALUE
                   || (code == LT
-                      && GET_MODE_CLASS (inner_mode) == MODE_FLOAT
-                      && (REAL_VALUE_NEGATIVE
-                          (FLOAT_STORE_FLAG_VALUE (GET_MODE (arg1)))))
+                      && SCALAR_FLOAT_MODE_P (inner_mode)
+                      && (fsfv = FLOAT_STORE_FLAG_VALUE (GET_MODE (arg1)),
+                          REAL_VALUE_NEGATIVE (fsfv)))
 #endif
                   )
-                 && GET_RTX_CLASS (GET_CODE (p->exp)) == '<'))
+                 && COMPARISON_P (p->exp)))
            {
              x = p->exp;
              break;
@@ -3248,21 +2901,22 @@ find_comparison_args (code, parg1, parg2, pmode1, pmode2)
                               << (GET_MODE_BITSIZE (inner_mode) - 1))))
 #ifdef FLOAT_STORE_FLAG_VALUE
                    || (code == GE
-                       && GET_MODE_CLASS (inner_mode) == MODE_FLOAT
-                       && (REAL_VALUE_NEGATIVE
-                           (FLOAT_STORE_FLAG_VALUE (GET_MODE (arg1)))))
+                       && SCALAR_FLOAT_MODE_P (inner_mode)
+                       && (fsfv = FLOAT_STORE_FLAG_VALUE (GET_MODE (arg1)),
+                           REAL_VALUE_NEGATIVE (fsfv)))
 #endif
                    )
-                  && GET_RTX_CLASS (GET_CODE (p->exp)) == '<')
+                  && COMPARISON_P (p->exp))
            {
              reverse_code = 1;
              x = p->exp;
              break;
            }
 
-         /* If this is fp + constant, the equivalent is a better operand since
-            it may let us predict the value of the comparison.  */
-         else if (NONZERO_BASE_PLUS_P (p->exp))
+         /* 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;
@@ -3282,9 +2936,10 @@ find_comparison_args (code, parg1, parg2, pmode1, pmode2)
          enum rtx_code reversed = reversed_comparison_code (x, NULL_RTX);
          if (reversed == UNKNOWN)
            break;
-         else code = reversed;
+         else
+           code = reversed;
        }
-      else if (GET_RTX_CLASS (GET_CODE (x)) == '<')
+      else if (COMPARISON_P (x))
        code = GET_CODE (x);
       arg1 = XEXP (x, 0), arg2 = XEXP (x, 1);
     }
@@ -3297,33 +2952,29 @@ find_comparison_args (code, parg1, parg2, pmode1, pmode2)
   return code;
 }
 \f
-/* 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
-   modified by recursive calls to this function.
+/* 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.
+   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 (x, insn)
-     rtx x;
-     rtx insn;
+fold_rtx (rtx x, rtx insn)
 {
   enum rtx_code code;
   enum machine_mode mode;
   const char *fmt;
   int i;
-  rtx new = 0;
-  int copied = 0;
-  int must_swap = 0;
+  rtx new_rtx = 0;
+  int changed = 0;
 
-  /* Folded equivalents of first two operands of X.  */
+  /* Operands of X.  */
   rtx folded_arg0;
   rtx folded_arg1;
 
@@ -3340,25 +2991,29 @@ fold_rtx (x, insn)
   if (x == 0)
     return x;
 
-  mode = GET_MODE (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:
-      /* Changing anything inside an ADDRESSOF is incorrect; we don't
-        want to (e.g.,) make (addressof (const_int 0)) just because
-        the location is known to be zero.  */
-    case ADDRESSOF:
       return x;
 
 #ifdef HAVE_cc0
@@ -3366,339 +3021,15 @@ fold_rtx (x, insn)
       return prev_insn_cc0;
 #endif
 
-    case PC:
-      /* If the next insn is a CODE_LABEL followed by a jump table,
-        PC's value is a LABEL_REF pointing to that label.  That
-        lets us fold switch statements on the VAX.  */
-      if (insn && GET_CODE (insn) == JUMP_INSN)
-       {
-         rtx next = next_nonnote_insn (insn);
-
-         if (next && GET_CODE (next) == CODE_LABEL
-             && NEXT_INSN (next) != 0
-             && GET_CODE (NEXT_INSN (next)) == JUMP_INSN
-             && (GET_CODE (PATTERN (NEXT_INSN (next))) == ADDR_VEC
-                 || GET_CODE (PATTERN (NEXT_INSN (next))) == ADDR_DIFF_VEC))
-           return gen_rtx_LABEL_REF (Pmode, next);
-       }
-      break;
-
-    case SUBREG:
-      /* See if we previously assigned a constant value to this SUBREG.  */
-      if ((new = lookup_as_function (x, CONST_INT)) != 0
-         || (new = lookup_as_function (x, CONST_DOUBLE)) != 0)
-       return new;
-
-      /* If this is a paradoxical SUBREG, we have no idea what value the
-        extra bits would have.  However, if the operand is equivalent
-        to a SUBREG whose operand is the same as our mode, and all the
-        modes are within a word, we can just use the inner operand
-        because these SUBREGs just say how to treat the register.
-
-        Similarly if we find an integer constant.  */
-
-      if (GET_MODE_SIZE (mode) > GET_MODE_SIZE (GET_MODE (SUBREG_REG (x))))
-       {
-         enum machine_mode imode = GET_MODE (SUBREG_REG (x));
-         struct table_elt *elt;
-
-         if (GET_MODE_SIZE (mode) <= UNITS_PER_WORD
-             && GET_MODE_SIZE (imode) <= UNITS_PER_WORD
-             && (elt = lookup (SUBREG_REG (x), HASH (SUBREG_REG (x), imode),
-                               imode)) != 0)
-           for (elt = elt->first_same_value; elt; elt = elt->next_same_value)
-             {
-               if (CONSTANT_P (elt->exp)
-                   && GET_MODE (elt->exp) == VOIDmode)
-                 return elt->exp;
-
-               if (GET_CODE (elt->exp) == SUBREG
-                   && GET_MODE (SUBREG_REG (elt->exp)) == mode
-                   && exp_equiv_p (elt->exp, elt->exp, 1, 0))
-                 return copy_rtx (SUBREG_REG (elt->exp));
-             }
-
-         return x;
-       }
-
-      /* Fold SUBREG_REG.  If it changed, see if we can simplify the SUBREG.
-        We might be able to if the SUBREG is extracting a single word in an
-        integral mode or extracting the low part.  */
-
-      folded_arg0 = fold_rtx (SUBREG_REG (x), insn);
-      const_arg0 = equiv_constant (folded_arg0);
-      if (const_arg0)
-       folded_arg0 = const_arg0;
-
-      if (folded_arg0 != SUBREG_REG (x))
-       {
-         new = simplify_subreg (mode, folded_arg0,
-                                GET_MODE (SUBREG_REG (x)), SUBREG_BYTE (x));
-         if (new)
-           return new;
-       }
-
-      /* If this is a narrowing SUBREG and our operand is a REG, see if
-        we can find an equivalence for REG that is an arithmetic operation
-        in a wider mode where both operands are paradoxical SUBREGs
-        from objects of our result mode.  In that case, we couldn't report
-        an equivalent value for that operation, since we don't know what the
-        extra bits will be.  But we can find an equivalence for this SUBREG
-        by folding that operation is the narrow mode.  This allows us to
-        fold arithmetic in narrow modes when the machine only supports
-        word-sized arithmetic.
-
-        Also look for a case where we have a SUBREG whose operand is the
-        same as our result.  If both modes are smaller than a word, we
-        are simply interpreting a register in different modes and we
-        can use the inner value.  */
-
-      if (GET_CODE (folded_arg0) == REG
-         && GET_MODE_SIZE (mode) < GET_MODE_SIZE (GET_MODE (folded_arg0))
-         && subreg_lowpart_p (x))
+    case ASM_OPERANDS:
+      if (insn)
        {
-         struct table_elt *elt;
-
-         /* We can use HASH here since we know that canon_hash won't be
-            called.  */
-         elt = lookup (folded_arg0,
-                       HASH (folded_arg0, GET_MODE (folded_arg0)),
-                       GET_MODE (folded_arg0));
-
-         if (elt)
-           elt = elt->first_same_value;
-
-         for (; elt; elt = elt->next_same_value)
-           {
-             enum rtx_code eltcode = GET_CODE (elt->exp);
-
-             /* Just check for unary and binary operations.  */
-             if (GET_RTX_CLASS (GET_CODE (elt->exp)) == '1'
-                 && GET_CODE (elt->exp) != SIGN_EXTEND
-                 && GET_CODE (elt->exp) != ZERO_EXTEND
-                 && GET_CODE (XEXP (elt->exp, 0)) == SUBREG
-                 && GET_MODE (SUBREG_REG (XEXP (elt->exp, 0))) == mode
-                 && (GET_MODE_CLASS (mode)
-                     == GET_MODE_CLASS (GET_MODE (XEXP (elt->exp, 0)))))
-               {
-                 rtx op0 = SUBREG_REG (XEXP (elt->exp, 0));
-
-                 if (GET_CODE (op0) != REG && ! CONSTANT_P (op0))
-                   op0 = fold_rtx (op0, NULL_RTX);
-
-                 op0 = equiv_constant (op0);
-                 if (op0)
-                   new = simplify_unary_operation (GET_CODE (elt->exp), mode,
-                                                   op0, mode);
-               }
-             else if ((GET_RTX_CLASS (GET_CODE (elt->exp)) == '2'
-                       || GET_RTX_CLASS (GET_CODE (elt->exp)) == 'c')
-                      && eltcode != DIV && eltcode != MOD
-                      && eltcode != UDIV && eltcode != UMOD
-                      && eltcode != ASHIFTRT && eltcode != LSHIFTRT
-                      && eltcode != ROTATE && eltcode != ROTATERT
-                      && ((GET_CODE (XEXP (elt->exp, 0)) == SUBREG
-                           && (GET_MODE (SUBREG_REG (XEXP (elt->exp, 0)))
-                               == mode))
-                          || CONSTANT_P (XEXP (elt->exp, 0)))
-                      && ((GET_CODE (XEXP (elt->exp, 1)) == SUBREG
-                           && (GET_MODE (SUBREG_REG (XEXP (elt->exp, 1)))
-                               == mode))
-                          || CONSTANT_P (XEXP (elt->exp, 1))))
-               {
-                 rtx op0 = gen_lowpart_common (mode, XEXP (elt->exp, 0));
-                 rtx op1 = gen_lowpart_common (mode, XEXP (elt->exp, 1));
-
-                 if (op0 && GET_CODE (op0) != REG && ! CONSTANT_P (op0))
-                   op0 = fold_rtx (op0, NULL_RTX);
-
-                 if (op0)
-                   op0 = equiv_constant (op0);
-
-                 if (op1 && GET_CODE (op1) != REG && ! CONSTANT_P (op1))
-                   op1 = fold_rtx (op1, NULL_RTX);
-
-                 if (op1)
-                   op1 = equiv_constant (op1);
-
-                 /* If we are looking for the low SImode part of
-                    (ashift:DI c (const_int 32)), it doesn't work
-                    to compute that in SImode, because a 32-bit shift
-                    in SImode is unpredictable.  We know the value is 0.  */
-                 if (op0 && op1
-                     && GET_CODE (elt->exp) == ASHIFT
-                     && GET_CODE (op1) == CONST_INT
-                     && INTVAL (op1) >= GET_MODE_BITSIZE (mode))
-                   {
-                     if (INTVAL (op1) < GET_MODE_BITSIZE (GET_MODE (elt->exp)))
-
-                       /* If the count fits in the inner mode's width,
-                          but exceeds the outer mode's width,
-                          the value will get truncated to 0
-                          by the subreg.  */
-                       new = const0_rtx;
-                     else
-                       /* If the count exceeds even the inner mode's width,
-                          don't fold this expression.  */
-                       new = 0;
-                   }
-                 else if (op0 && op1)
-                   new = simplify_binary_operation (GET_CODE (elt->exp), mode,
-                                                    op0, op1);
-               }
-
-             else if (GET_CODE (elt->exp) == SUBREG
-                      && GET_MODE (SUBREG_REG (elt->exp)) == mode
-                      && (GET_MODE_SIZE (GET_MODE (folded_arg0))
-                          <= UNITS_PER_WORD)
-                      && exp_equiv_p (elt->exp, elt->exp, 1, 0))
-               new = copy_rtx (SUBREG_REG (elt->exp));
-
-             if (new)
-               return new;
-           }
+         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;
 
-    case NOT:
-    case NEG:
-      /* If we have (NOT Y), see if Y is known to be (NOT Z).
-        If so, (NOT Y) simplifies to Z.  Similarly for NEG.  */
-      new = lookup_as_function (XEXP (x, 0), code);
-      if (new)
-       return fold_rtx (copy_rtx (XEXP (new, 0)), insn);
-      break;
-
-    case MEM:
-      /* If we are not actually processing an insn, don't try to find the
-        best address.  Not only don't we care, but we could modify the
-        MEM in an invalid way since we have no insn to validate against.  */
-      if (insn != 0)
-       find_best_addr (insn, &XEXP (x, 0), GET_MODE (x));
-
-      {
-       /* Even if we don't fold in the insn itself,
-          we can safely do so here, in hopes of getting a constant.  */
-       rtx addr = fold_rtx (XEXP (x, 0), NULL_RTX);
-       rtx base = 0;
-       HOST_WIDE_INT offset = 0;
-
-       if (GET_CODE (addr) == REG
-           && REGNO_QTY_VALID_P (REGNO (addr)))
-         {
-           int addr_q = REG_QTY (REGNO (addr));
-           struct qty_table_elem *addr_ent = &qty_table[addr_q];
-
-           if (GET_MODE (addr) == addr_ent->mode
-               && addr_ent->const_rtx != NULL_RTX)
-             addr = addr_ent->const_rtx;
-         }
-
-       /* If address is constant, split it into a base and integer offset.  */
-       if (GET_CODE (addr) == SYMBOL_REF || GET_CODE (addr) == LABEL_REF)
-         base = addr;
-       else if (GET_CODE (addr) == CONST && GET_CODE (XEXP (addr, 0)) == PLUS
-                && GET_CODE (XEXP (XEXP (addr, 0), 1)) == CONST_INT)
-         {
-           base = XEXP (XEXP (addr, 0), 0);
-           offset = INTVAL (XEXP (XEXP (addr, 0), 1));
-         }
-       else if (GET_CODE (addr) == LO_SUM
-                && GET_CODE (XEXP (addr, 1)) == SYMBOL_REF)
-         base = XEXP (addr, 1);
-       else if (GET_CODE (addr) == ADDRESSOF)
-         return change_address (x, VOIDmode, addr);
-
-       /* If this is a constant pool reference, we can fold it into its
-          constant to allow better value tracking.  */
-       if (base && GET_CODE (base) == SYMBOL_REF
-           && CONSTANT_POOL_ADDRESS_P (base))
-         {
-           rtx constant = get_pool_constant (base);
-           enum machine_mode const_mode = get_pool_mode (base);
-           rtx new;
-
-           if (CONSTANT_P (constant) && GET_CODE (constant) != CONST_INT)
-             constant_pool_entries_cost = COST (constant);
-
-           /* If we are loading the full constant, we have an equivalence.  */
-           if (offset == 0 && mode == const_mode)
-             return constant;
-
-           /* If this actually isn't a constant (weird!), we can't do
-              anything.  Otherwise, handle the two most common cases:
-              extracting a word from a multi-word constant, and extracting
-              the low-order bits.  Other cases don't seem common enough to
-              worry about.  */
-           if (! CONSTANT_P (constant))
-             return x;
-
-           if (GET_MODE_CLASS (mode) == MODE_INT
-               && GET_MODE_SIZE (mode) == UNITS_PER_WORD
-               && offset % UNITS_PER_WORD == 0
-               && (new = operand_subword (constant,
-                                          offset / UNITS_PER_WORD,
-                                          0, const_mode)) != 0)
-             return new;
-
-           if (((BYTES_BIG_ENDIAN
-                 && offset == GET_MODE_SIZE (GET_MODE (constant)) - 1)
-                || (! BYTES_BIG_ENDIAN && offset == 0))
-               && (new = gen_lowpart_if_possible (mode, constant)) != 0)
-             return new;
-         }
-
-       /* If this is a reference to a label at a known position in a jump
-          table, we also know its value.  */
-       if (base && GET_CODE (base) == LABEL_REF)
-         {
-           rtx label = XEXP (base, 0);
-           rtx table_insn = NEXT_INSN (label);
-
-           if (table_insn && GET_CODE (table_insn) == JUMP_INSN
-               && GET_CODE (PATTERN (table_insn)) == ADDR_VEC)
-             {
-               rtx table = PATTERN (table_insn);
-
-               if (offset >= 0
-                   && (offset / GET_MODE_SIZE (GET_MODE (table))
-                       < XVECLEN (table, 0)))
-                 return XVECEXP (table, 0,
-                                 offset / GET_MODE_SIZE (GET_MODE (table)));
-             }
-           if (table_insn && GET_CODE (table_insn) == JUMP_INSN
-               && GET_CODE (PATTERN (table_insn)) == ADDR_DIFF_VEC)
-             {
-               rtx table = PATTERN (table_insn);
-
-               if (offset >= 0
-                   && (offset / GET_MODE_SIZE (GET_MODE (table))
-                       < XVECLEN (table, 1)))
-                 {
-                   offset /= GET_MODE_SIZE (GET_MODE (table));
-                   new = gen_rtx_MINUS (Pmode, XVECEXP (table, 1, offset),
-                                        XEXP (table, 0));
-
-                   if (GET_MODE (table) != Pmode)
-                     new = gen_rtx_TRUNCATE (GET_MODE (table), new);
-
-                   /* Indicate this is a constant.  This isn't a
-                      valid form of CONST, but it will only be used
-                      to fold the next insns and then discarded, so
-                      it should be safe.
-
-                      Note this expression must be explicitly discarded,
-                      by cse_insn, else it may end up in a REG_EQUAL note
-                      and "escape" to cause problems elsewhere.  */
-                   return gen_rtx_CONST (GET_MODE (new), new);
-                 }
-             }
-         }
-
-       return x;
-      }
-
 #ifdef NO_FUNCTION_CSE
     case CALL:
       if (CONSTANT_P (XEXP (XEXP (x, 0), 0)))
@@ -3706,16 +3037,12 @@ fold_rtx (x, insn)
       break;
 #endif
 
-    case ASM_OPERANDS:
-      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);
-      break;
-
+    /* Anything else goes through the loop below.  */
     default:
       break;
     }
 
+  mode = GET_MODE (x);
   const_arg0 = 0;
   const_arg1 = 0;
   const_arg2 = 0;
@@ -3728,31 +3055,15 @@ fold_rtx (x, insn)
   for (i = GET_RTX_LENGTH (code) - 1; i >= 0; i--)
     if (fmt[i] == 'e')
       {
-       rtx arg = XEXP (x, i);
-       rtx folded_arg = arg, const_arg = 0;
-       enum machine_mode mode_arg = GET_MODE (arg);
-       rtx cheap_arg, expensive_arg;
-       rtx replacements[2];
-       int j;
-
-       /* Most arguments are cheap, so handle them specially.  */
-       switch (GET_CODE (arg))
+       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:
-           /* This is the same as calling equiv_constant; it is duplicated
-              here for speed.  */
-           if (REGNO_QTY_VALID_P (REGNO (arg)))
-             {
-               int arg_q = REG_QTY (REGNO (arg));
-               struct qty_table_elem *arg_ent = &qty_table[arg_q];
-
-               if (arg_ent->const_rtx != NULL_RTX
-                   && GET_CODE (arg_ent->const_rtx) != REG
-                   && GET_CODE (arg_ent->const_rtx) != PLUS)
-                 const_arg
-                   = gen_lowpart_if_possible (GET_MODE (arg),
-                                              arg_ent->const_rtx);
-             }
+         case SUBREG:
+           const_arg = equiv_constant (folded_arg);
            break;
 
          case CONST:
@@ -3760,8 +3071,9 @@ fold_rtx (x, insn)
          case SYMBOL_REF:
          case LABEL_REF:
          case CONST_DOUBLE:
+         case CONST_FIXED:
          case CONST_VECTOR:
-           const_arg = arg;
+           const_arg = folded_arg;
            break;
 
 #ifdef HAVE_cc0
@@ -3773,8 +3085,9 @@ fold_rtx (x, insn)
 #endif
 
          default:
-           folded_arg = fold_rtx (arg, insn);
+           folded_arg = fold_rtx (folded_arg, insn);
            const_arg = equiv_constant (folded_arg);
+           break;
          }
 
        /* For the first three operands, see if the operand
@@ -3795,160 +3108,105 @@ fold_rtx (x, insn)
            break;
          }
 
-       /* Pick the least expensive of the folded argument and an
-          equivalent constant argument.  */
-       if (const_arg == 0 || const_arg == folded_arg
-           || COST_IN (const_arg, code) > COST_IN (folded_arg, code))
-         cheap_arg = folded_arg, expensive_arg = const_arg;
-       else
-         cheap_arg = const_arg, expensive_arg = folded_arg;
-
-       /* Try to replace the operand with the cheapest of the two
-          possibilities.  If it doesn't work and this is either of the first
-          two operands of a commutative operation, try swapping them.
-          If THAT fails, try the more expensive, provided it is cheaper
-          than what is already there.  */
-
-       if (cheap_arg == XEXP (x, i))
+       /* 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 == 0 && ! copied)
-         {
-           x = copy_rtx (x);
-           copied = 1;
-         }
-
-       /* Order the replacements from cheapest to most expensive.  */
-       replacements[0] = cheap_arg;
-       replacements[1] = expensive_arg;
-
-       for (j = 0; j < 2 && replacements[j];  j++)
-         {
-           int old_cost = COST_IN (XEXP (x, i), code);
-           int new_cost = COST_IN (replacements[j], code);
+       if (insn == NULL_RTX && !changed)
+         x = copy_rtx (x);
+       changed = 1;
+       validate_unshare_change (insn, &XEXP (x, i), folded_arg, 1);
+      }
 
-           /* Stop if what existed before was cheaper.  Prefer constants
-              in the case of a tie.  */
-           if (new_cost > old_cost
-               || (new_cost == old_cost && CONSTANT_P (XEXP (x, i))))
-             break;
+  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;
+       }
 
-           if (validate_change (insn, &XEXP (x, i), replacements[j], 0))
-             break;
+      apply_change_group ();
+    }
 
-           if (code == NE || code == EQ || GET_RTX_CLASS (code) == 'c'
-               || code == LTGT || code == UNEQ || code == ORDERED
-               || code == UNORDERED)
-             {
-               validate_change (insn, &XEXP (x, i), XEXP (x, 1 - i), 1);
-               validate_change (insn, &XEXP (x, 1 - i), replacements[j], 1);
-
-               if (apply_change_group ())
-                 {
-                   /* Swap them back to be invalid so that this loop can
-                      continue and flag them to be swapped back later.  */
-                   rtx tem;
-
-                   tem = XEXP (x, 0); XEXP (x, 0) = XEXP (x, 1);
-                                      XEXP (x, 1) = tem;
-                   must_swap = 1;
-                   break;
-                 }
-             }
-         }
-      }
-
-    else
-      {
-       if (fmt[i] == 'E')
-         /* Don't try to fold inside of a vector of expressions.
-            Doing nothing is harmless.  */
-         {;}
-      }
-
-  /* If a commutative operation, place a constant integer as the second
-     operand unless the first operand is also a constant integer.  Otherwise,
-     place any constant second unless the first operand is also a constant.  */
-
-  if (code == EQ || code == NE || GET_RTX_CLASS (code) == 'c'
-      || code == LTGT || code == UNEQ || code == ORDERED
-      || code == UNORDERED)
-    {
-      if (must_swap || (const_arg0
-                       && (const_arg1 == 0
-                           || (GET_CODE (const_arg0) == CONST_INT
-                               && GET_CODE (const_arg1) != CONST_INT))))
-       {
-         rtx tem = XEXP (x, 0);
-
-         if (insn == 0 && ! copied)
-           {
-             x = copy_rtx (x);
-             copied = 1;
-           }
-
-         validate_change (insn, &XEXP (x, 0), XEXP (x, 1), 1);
-         validate_change (insn, &XEXP (x, 1), tem, 1);
-         if (apply_change_group ())
-           {
-             tem = const_arg0, const_arg0 = const_arg1, const_arg1 = tem;
-             tem = folded_arg0, folded_arg0 = folded_arg1, folded_arg1 = tem;
-           }
-       }
-    }
-
-  /* If X is an arithmetic operation, see if we can simplify it.  */
+  /* If X is an arithmetic operation, see if we can simplify it.  */
 
   switch (GET_RTX_CLASS (code))
     {
-    case '1':
+    case RTX_UNARY:
       {
-       int is_const = 0;
-
        /* We can't simplify extension ops unless we know the
           original mode.  */
        if ((code == ZERO_EXTEND || code == SIGN_EXTEND)
            && mode_arg0 == VOIDmode)
          break;
 
-       /* If we had a CONST, strip it off and put it back later if we
-          fold.  */
-       if (const_arg0 != 0 && GET_CODE (const_arg0) == CONST)
-         is_const = 1, const_arg0 = XEXP (const_arg0, 0);
-
-       new = simplify_unary_operation (code, mode,
+       new_rtx = simplify_unary_operation (code, mode,
                                        const_arg0 ? const_arg0 : folded_arg0,
                                        mode_arg0);
-       if (new != 0 && is_const)
-         new = gen_rtx_CONST (mode, new);
       }
       break;
 
-    case '<':
+    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 = const_true_rtx, false_rtx = const0_rtx;
+         rtx true_rtx, false_rtx;
          enum machine_mode mode_arg1;
 
-#ifdef FLOAT_STORE_FLAG_VALUE
-         if (GET_MODE_CLASS (mode) == MODE_FLOAT)
+         if (SCALAR_FLOAT_MODE_P (mode))
            {
+#ifdef FLOAT_STORE_FLAG_VALUE
              true_rtx = (CONST_DOUBLE_FROM_REAL_VALUE
-                     (FLOAT_STORE_FLAG_VALUE (mode), mode));
+                         (FLOAT_STORE_FLAG_VALUE (mode), mode));
+#else
+             true_rtx = NULL_RTX;
+#endif
              false_rtx = CONST0_RTX (mode);
            }
-#endif
+         else
+           {
+             true_rtx = const_true_rtx;
+             false_rtx = const0_rtx;
+           }
 
          code = find_comparison_args (code, &folded_arg0, &folded_arg1,
                                       &mode_arg0, &mode_arg1);
-         const_arg0 = equiv_constant (folded_arg0);
-         const_arg1 = equiv_constant (folded_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
@@ -3957,63 +3215,85 @@ fold_rtx (x, insn)
          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)
            {
-             /* Is FOLDED_ARG0 frame-pointer plus a constant?  Or
-                non-explicit constant?  These aren't zero, but we
-                don't know their sign.  */
-             if (const_arg1 == const0_rtx
-                 && (NONZERO_BASE_PLUS_P (folded_arg0)
-#if 0  /* Sad to say, on sysvr4, #pragma weak can make a symbol address
-         come out as 0.  */
-                     || GET_CODE (folded_arg0) == SYMBOL_REF
-#endif
-                     || GET_CODE (folded_arg0) == LABEL_REF
-                     || GET_CODE (folded_arg0) == CONST))
+             if (const_arg1 != NULL)
                {
-                 if (code == EQ)
-                   return false_rtx;
-                 else if (code == NE)
-                   return true_rtx;
+                 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 (folded_arg0 == folded_arg1
-                 || (GET_CODE (folded_arg0) == REG
-                     && GET_CODE (folded_arg1) == REG
-                     && (REG_QTY (REGNO (folded_arg0))
-                         == REG_QTY (REGNO (folded_arg1))))
+             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)
-                                    & HASH_MASK), mode_arg0))
+                                   SAFE_HASH (folded_arg0, mode_arg0),
+                                   mode_arg0))
                      && (p1 = lookup (folded_arg1,
-                                      (safe_hash (folded_arg1, mode_arg0)
-                                       & HASH_MASK), mode_arg0))
+                                      SAFE_HASH (folded_arg1, mode_arg0),
+                                      mode_arg0))
                      && p0->first_same_value == p1->first_same_value))
-               {
-                  /* Sadly two equal NaNs are not equivalent.  */
-                  if (TARGET_FLOAT_FORMAT != IEEE_FLOAT_FORMAT
-                      || ! FLOAT_MODE_P (mode_arg0) 
-                      || flag_unsafe_math_optimizations)
-                     return ((code == EQ || code == LE || code == GE
-                              || code == LEU || code == GEU || code == UNEQ
-                              || code == UNLE || code == UNGE || code == ORDERED)
-                             ? true_rtx : false_rtx);
-                  /* Take care for the FP compares we can resolve.  */
-                  if (code == UNEQ || code == UNLE || code == UNGE)
-                    return true_rtx;
-                  if (code == LTGT || code == LT || code == GT)
-                    return false_rtx;
-               }
+               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 (GET_CODE (folded_arg0) == REG)
+             else if (REG_P (folded_arg0))
                {
                  int qty = REG_QTY (REGNO (folded_arg0));
 
@@ -4029,10 +3309,19 @@ fold_rtx (x, insn)
                              || (const_arg1
                                  && rtx_equal_p (ent->comparison_const,
                                                  const_arg1))
-                             || (GET_CODE (folded_arg1) == REG
+                             || (REG_P (folded_arg1)
                                  && (REG_QTY (REGNO (folded_arg1)) == ent->comparison_qty))))
-                       return (comparison_dominates_p (ent->comparison_code, code)
-                               ? true_rtx : false_rtx);
+                       {
+                         if (comparison_dominates_p (ent->comparison_code, code))
+                           {
+                             if (true_rtx)
+                               return true_rtx;
+                             else
+                               break;
+                           }
+                         else
+                           return false_rtx;
+                       }
                    }
                }
            }
@@ -4041,8 +3330,7 @@ fold_rtx (x, insn)
       /* 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)
+      if (const_arg1 == const0_rtx && !const_arg0)
        {
          rtx y = lookup_as_function (folded_arg0, IOR);
          rtx inner_const;
@@ -4051,71 +3339,18 @@ fold_rtx (x, insn)
              && (inner_const = equiv_constant (XEXP (y, 1))) != 0
              && GET_CODE (inner_const) == CONST_INT
              && INTVAL (inner_const) != 0)
-           {
-             int sign_bitnum = GET_MODE_BITSIZE (mode_arg0) - 1;
-             int has_sign = (HOST_BITS_PER_WIDE_INT >= sign_bitnum
-                             && (INTVAL (inner_const)
-                                 & ((HOST_WIDE_INT) 1 << sign_bitnum)));
-             rtx true_rtx = const_true_rtx, false_rtx = const0_rtx;
-
-#ifdef FLOAT_STORE_FLAG_VALUE
-             if (GET_MODE_CLASS (mode) == MODE_FLOAT)
-               {
-                 true_rtx = (CONST_DOUBLE_FROM_REAL_VALUE
-                         (FLOAT_STORE_FLAG_VALUE (mode), mode));
-                 false_rtx = CONST0_RTX (mode);
-               }
-#endif
-
-             switch (code)
-               {
-               case EQ:
-                 return false_rtx;
-               case NE:
-                 return true_rtx;
-               case LT:  case LE:
-                 if (has_sign)
-                   return true_rtx;
-                 break;
-               case GT:  case GE:
-                 if (has_sign)
-                   return false_rtx;
-                 break;
-               default:
-                 break;
-               }
-           }
+           folded_arg0 = gen_rtx_IOR (mode_arg0, XEXP (y, 0), inner_const);
        }
 
-      new = simplify_relational_operation (code,
-                                          (mode_arg0 != VOIDmode
-                                           ? mode_arg0
-                                           : (GET_MODE (const_arg0
-                                                        ? const_arg0
-                                                        : folded_arg0)
-                                              != VOIDmode)
-                                           ? GET_MODE (const_arg0
-                                                       ? const_arg0
-                                                       : folded_arg0)
-                                           : GET_MODE (const_arg1
-                                                       ? const_arg1
-                                                       : folded_arg1)),
-                                          const_arg0 ? const_arg0 : folded_arg0,
-                                          const_arg1 ? const_arg1 : folded_arg1);
-#ifdef FLOAT_STORE_FLAG_VALUE
-      if (new != 0 && GET_MODE_CLASS (mode) == MODE_FLOAT)
-       {
-         if (new == const0_rtx)
-           new = CONST0_RTX (mode);
-         else
-           new = (CONST_DOUBLE_FROM_REAL_VALUE
-                  (FLOAT_STORE_FLAG_VALUE (mode), mode));
-       }
-#endif
+      {
+       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 '2':
-    case 'c':
+    case RTX_BIN_ARITH:
+    case RTX_COMM_ARITH:
       switch (code)
        {
        case PLUS:
@@ -4183,16 +3418,15 @@ fold_rtx (x, insn)
                 manner and hope the Sun compilers get it correct.  */
              && INTVAL (const_arg1) !=
                ((HOST_WIDE_INT) 1 << (HOST_BITS_PER_WIDE_INT - 1))
-             && GET_CODE (folded_arg1) == REG)
+             && REG_P (folded_arg1))
            {
              rtx new_const = GEN_INT (-INTVAL (const_arg1));
              struct table_elt *p
-               = lookup (new_const, safe_hash (new_const, mode) & HASH_MASK,
-                         mode);
+               = lookup (new_const, SAFE_HASH (new_const, mode), mode);
 
              if (p)
                for (p = p->first_same_value; p; p = p->next_same_value)
-                 if (GET_CODE (p->exp) == REG)
+                 if (REG_P (p->exp))
                    return simplify_gen_binary (MINUS, mode, folded_arg0,
                                                canon_reg (p->exp, NULL_RTX));
            }
@@ -4215,7 +3449,7 @@ fold_rtx (x, insn)
        from_plus:
        case SMIN:    case SMAX:      case UMIN:    case UMAX:
        case IOR:     case AND:       case XOR:
-       case MULT:    case DIV:       case UDIV:
+       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
@@ -4224,26 +3458,41 @@ fold_rtx (x, insn)
             Note that the similar optimization done by combine.c only works
             if the intermediate operation's result has only one reference.  */
 
-         if (GET_CODE (folded_arg0) == REG
+         if (REG_P (folded_arg0)
              && const_arg1 && GET_CODE (const_arg1) == CONST_INT)
            {
              int is_shift
                = (code == ASHIFT || code == ASHIFTRT || code == LSHIFTRT);
-             rtx y = lookup_as_function (folded_arg0, code);
-             rtx inner_const;
+             rtx y, inner_const, new_const;
+             rtx canon_const_arg1 = const_arg1;
              enum rtx_code associate_code;
-             rtx new_const;
-
-             if (y == 0
-                 || 0 == (inner_const
-                          = equiv_constant (fold_rtx (XEXP (y, 1), 0)))
-                 || GET_CODE (inner_const) != CONST_INT
-                 /* 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.  */
-                 || XEXP (y, 0) == folded_arg0)
+
+             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
@@ -4251,7 +3500,7 @@ fold_rtx (x, insn)
                 with a pre- or post-increment.  Similarly for two subtracts of
                 identical powers of two with post decrement.  */
 
-             if (code == PLUS && INTVAL (const_arg1) == INTVAL (inner_const)
+             if (code == PLUS && const_arg1 == inner_const
                  && ((HAVE_PRE_INCREMENT
                          && exact_log2 (INTVAL (const_arg1)) >= 0)
                      || (HAVE_POST_INCREMENT
@@ -4262,15 +3511,30 @@ fold_rtx (x, insn)
                          && 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 == DIV, we want MULT.  */
+                A-C1-C2 is A-(C1 + C2), so if CODE == MINUS, we want PLUS.  */
 
-             associate_code
-               = (code == MULT || code == DIV || code == UDIV ? MULT
-                  : is_shift || code == PLUS || code == MINUS ? PLUS : code);
+             associate_code = (is_shift || code == MINUS ? PLUS : code);
 
              new_const = simplify_binary_operation (associate_code, mode,
-                                                    const_arg1, inner_const);
+                                                    canon_const_arg1,
+                                                    inner_const);
 
              if (new_const == 0)
                break;
@@ -4281,13 +3545,16 @@ fold_rtx (x, insn)
                 shift on a machine that does a sign-extend as a pair
                 of shifts.  */
 
-             if (is_shift && GET_CODE (new_const) == CONST_INT
+             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;
                }
@@ -4305,16 +3572,24 @@ fold_rtx (x, insn)
            }
          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 = simplify_binary_operation (code, mode,
+      new_rtx = simplify_binary_operation (code, mode,
                                       const_arg0 ? const_arg0 : folded_arg0,
                                       const_arg1 ? const_arg1 : folded_arg1);
       break;
 
-    case 'o':
+    case RTX_OBJ:
       /* (lo_sum (high X) X) is simply X.  */
       if (code == LO_SUM && const_arg0 != 0
          && GET_CODE (const_arg0) == HIGH
@@ -4322,58 +3597,87 @@ fold_rtx (x, insn)
        return const_arg1;
       break;
 
-    case '3':
-    case 'b':
-      new = simplify_ternary_operation (code, mode, mode_arg0,
+    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;
 
-    case 'x':
-      /* Always eliminate CONSTANT_P_RTX at this stage.  */
-      if (code == CONSTANT_P_RTX)
-       return (const_arg0 ? const1_rtx : const0_rtx);
+    default:
       break;
     }
 
-  return new ? new : x;
+  return new_rtx ? new_rtx : x;
 }
 \f
 /* Return a constant value currently equivalent to X.
    Return 0 if we don't know one.  */
 
 static rtx
-equiv_constant (x)
-     rtx x;
+equiv_constant (rtx x)
 {
-  if (GET_CODE (x) == REG
+  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_if_possible (GET_MODE (x), x_ent->const_rtx);
+       x = gen_lowpart (GET_MODE (x), x_ent->const_rtx);
     }
 
   if (x == 0 || CONSTANT_P (x))
     return x;
 
-  /* If X is a MEM, try to fold it outside the context of any insn to see if
-     it might be equivalent to a constant.  That handles the case where it
-     is a constant-pool reference.  Then try to look it up in the hash table
-     in case it is something whose value we have seen before.  */
+  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 (GET_CODE (x) == MEM)
+  if (MEM_P (x))
     {
       struct table_elt *elt;
 
-      x = fold_rtx (x, NULL_RTX);
+      x = avoid_constant_pool_reference (x);
       if (CONSTANT_P (x))
        return x;
 
-      elt = lookup (x, safe_hash (x, GET_MODE (x)) & HASH_MASK, GET_MODE (x));
+      elt = lookup (x, SAFE_HASH (x, GET_MODE (x)), GET_MODE (x));
       if (elt == 0)
        return 0;
 
@@ -4385,64 +3689,19 @@ equiv_constant (x)
   return 0;
 }
 \f
-/* Assuming that X is an rtx (e.g., MEM, REG or SUBREG) for a fixed-point
-   number, return an rtx (MEM, SUBREG, or CONST_INT) that refers to the
-   least-significant part of X.
-   MODE specifies how big a part of X to return.
-
-   If the requested operation cannot be done, 0 is returned.
-
-   This is similar to gen_lowpart in emit-rtl.c.  */
-
-rtx
-gen_lowpart_if_possible (mode, x)
-     enum machine_mode mode;
-     rtx x;
-{
-  rtx result = gen_lowpart_common (mode, x);
-
-  if (result)
-    return result;
-  else if (GET_CODE (x) == MEM)
-    {
-      /* This is the only other case we handle.  */
-      int offset = 0;
-      rtx new;
-
-      if (WORDS_BIG_ENDIAN)
-       offset = (MAX (GET_MODE_SIZE (GET_MODE (x)), UNITS_PER_WORD)
-                 - MAX (GET_MODE_SIZE (mode), UNITS_PER_WORD));
-      if (BYTES_BIG_ENDIAN)
-       /* Adjust the address so that the address-after-the-data is
-          unchanged.  */
-       offset -= (MIN (UNITS_PER_WORD, GET_MODE_SIZE (mode))
-                  - MIN (UNITS_PER_WORD, GET_MODE_SIZE (GET_MODE (x))));
-
-      new = adjust_address_nv (x, mode, offset);
-      if (! memory_address_p (mode, XEXP (new, 0)))
-       return 0;
-
-      return new;
-    }
-  else
-    return 0;
-}
-\f
-/* Given INSN, a jump insn, TAKEN indicates if we are following the "taken"
-   branch.  It will be zero if not.
+/* 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)
+       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 (insn, taken)
-     rtx insn;
-     int taken;
+record_jump_equiv (rtx insn, bool taken)
 {
   int cond_known_true;
   rtx op0, op1;
@@ -4452,8 +3711,8 @@ record_jump_equiv (insn, taken)
   enum rtx_code code;
 
   /* Ensure this is the right kind of insn.  */
-  if (! any_condjump_p (insn))
-    return;
+  gcc_assert (any_condjump_p (insn));
+
   set = pc_set (insn);
 
   /* See if this jump condition is known true or false.  */
@@ -4487,17 +3746,26 @@ record_jump_equiv (insn, taken)
   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 (code, mode, op0, op1, reversed_nonequality)
-     enum rtx_code code;
-     enum machine_mode mode;
-     rtx op0, op1;
-     int reversed_nonequality;
+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;
@@ -4514,11 +3782,10 @@ record_jump_cond (code, mode, op0, op1, reversed_nonequality)
          > GET_MODE_SIZE (GET_MODE (SUBREG_REG (op0)))))
     {
       enum machine_mode inner_mode = GET_MODE (SUBREG_REG (op0));
-      rtx tem = gen_lowpart_if_possible (inner_mode, op1);
-
-      record_jump_cond (code, mode, SUBREG_REG (op0),
-                       tem ? tem : gen_rtx_SUBREG (inner_mode, op1, 0),
-                       reversed_nonequality);
+      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
@@ -4526,11 +3793,10 @@ record_jump_cond (code, mode, op0, op1, reversed_nonequality)
          > GET_MODE_SIZE (GET_MODE (SUBREG_REG (op1)))))
     {
       enum machine_mode inner_mode = GET_MODE (SUBREG_REG (op1));
-      rtx tem = gen_lowpart_if_possible (inner_mode, op0);
-
-      record_jump_cond (code, mode, SUBREG_REG (op1),
-                       tem ? tem : gen_rtx_SUBREG (inner_mode, op0, 0),
-                       reversed_nonequality);
+      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
@@ -4546,11 +3812,10 @@ record_jump_cond (code, mode, op0, op1, reversed_nonequality)
          < GET_MODE_SIZE (GET_MODE (SUBREG_REG (op0)))))
     {
       enum machine_mode inner_mode = GET_MODE (SUBREG_REG (op0));
-      rtx tem = gen_lowpart_if_possible (inner_mode, op1);
-
-      record_jump_cond (code, mode, SUBREG_REG (op0),
-                       tem ? tem : gen_rtx_SUBREG (inner_mode, op1, 0),
-                       reversed_nonequality);
+      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
@@ -4559,11 +3824,10 @@ record_jump_cond (code, mode, op0, op1, reversed_nonequality)
          < GET_MODE_SIZE (GET_MODE (SUBREG_REG (op1)))))
     {
       enum machine_mode inner_mode = GET_MODE (SUBREG_REG (op1));
-      rtx tem = gen_lowpart_if_possible (inner_mode, op0);
-
-      record_jump_cond (code, mode, SUBREG_REG (op1),
-                       tem ? tem : gen_rtx_SUBREG (inner_mode, op0, 0),
-                       reversed_nonequality);
+      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.  */
@@ -4610,11 +3874,11 @@ record_jump_cond (code, mode, op0, op1, reversed_nonequality)
         register, or if OP1 is neither a register or constant, we can't
         do anything.  */
 
-      if (GET_CODE (op1) != REG)
+      if (!REG_P (op1))
        op1 = equiv_constant (op1);
 
       if ((reversed_nonequality && FLOAT_MODE_P (mode))
-         || GET_CODE (op0) != REG || op1 == 0)
+         || !REG_P (op0) || op1 == 0)
        return;
 
       /* Put OP0 in the hash table if it isn't already.  This gives it a
@@ -4641,7 +3905,7 @@ record_jump_cond (code, mode, op0, op1, reversed_nonequality)
       ent = &qty_table[qty];
 
       ent->comparison_code = code;
-      if (GET_CODE (op1) == REG)
+      if (REG_P (op1))
        {
          /* Look it up again--in case op0 and op1 are the same.  */
          op1_elt = lookup (op1, op1_hash, mode);
@@ -4699,18 +3963,13 @@ record_jump_cond (code, mode, op0, op1, reversed_nonequality)
     }
 
   merge_equiv_classes (op0_elt, op1_elt);
-  last_jump_equiv_class = op0_elt;
 }
 \f
 /* 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.
-
-   If LIBCALL_INSN is nonzero, don't record any equivalence made in
-   the insn.  It means that INSN is inside libcall block.  In this
-   case LIBCALL_INSN is the corresponding insn with REG_LIBCALL.  */
+   of available values.  */
 
 /* Data on one SET contained in the instruction.  */
 
@@ -4733,34 +3992,28 @@ struct set
   /* 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.  */
-  enum machine_mode mode;
+  /* 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;
-  /* Original SET_SRC value used for libcall notes.  */
-  rtx orig_src;
   /* 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 (insn, libcall_insn)
-     rtx insn;
-     rtx libcall_insn;
+cse_insn (rtx insn)
 {
   rtx x = PATTERN (insn);
   int i;
   rtx tem;
   int n_sets = 0;
 
-#ifdef HAVE_cc0
-  /* Records what this insn does to set CC0.  */
-  rtx this_insn_cc0 = 0;
-  enum machine_mode this_insn_cc0_mode = VOIDmode;
-#endif
-
   rtx src_eqv = 0;
   struct table_elt *src_eqv_elt = 0;
   int src_eqv_volatile = 0;
@@ -4770,13 +4023,18 @@ cse_insn (insn, libcall_insn)
   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 (GET_CODE (insn) == CALL_INSN)
+  if (CALL_P (insn))
     {
       for (tem = CALL_INSN_FUNCTION_USAGE (insn); tem; tem = XEXP (tem, 1))
        {
@@ -4788,7 +4046,7 @@ cse_insn (insn, libcall_insn)
 
   if (GET_CODE (x) == SET)
     {
-      sets = (struct set *) alloca (sizeof (struct set));
+      sets = XALLOCA (struct set);
       sets[0].rtl = x;
 
       /* Ignore SETs that are unconditional jumps.
@@ -4823,7 +4081,7 @@ cse_insn (insn, libcall_insn)
     {
       int lim = XVECLEN (x, 0);
 
-      sets = (struct set *) alloca (lim * sizeof (struct set));
+      sets = XALLOCAVEC (struct set, lim);
 
       /* Find all regs explicitly clobbered in this insn,
         and ensure they are not replaced with any other regs
@@ -4843,7 +4101,7 @@ cse_insn (insn, libcall_insn)
            {
              rtx clobbered = XEXP (y, 0);
 
-             if (GET_CODE (clobbered) == REG
+             if (REG_P (clobbered)
                  || GET_CODE (clobbered) == SUBREG)
                invalidate (clobbered, VOIDmode);
              else if (GET_CODE (clobbered) == STRICT_LOW_PART
@@ -4877,13 +4135,13 @@ cse_insn (insn, libcall_insn)
              /* If we clobber memory, canon the address.
                 This does nothing when a register is clobbered
                 because we have already invalidated the reg.  */
-             if (GET_CODE (XEXP (y, 0)) == MEM)
-               canon_reg (XEXP (y, 0), NULL_RTX);
+             if (MEM_P (XEXP (y, 0)))
+               canon_reg (XEXP (y, 0), insn);
            }
          else if (GET_CODE (y) == USE
-                  && ! (GET_CODE (XEXP (y, 0)) == REG
+                  && ! (REG_P (XEXP (y, 0))
                         && REGNO (XEXP (y, 0)) < FIRST_PSEUDO_REGISTER))
-           canon_reg (y, NULL_RTX);
+           canon_reg (y, insn);
          else if (GET_CODE (y) == CALL)
            {
              /* The result of apply_change_group can be ignored; see
@@ -4896,15 +4154,15 @@ cse_insn (insn, libcall_insn)
     }
   else if (GET_CODE (x) == CLOBBER)
     {
-      if (GET_CODE (XEXP (x, 0)) == MEM)
-       canon_reg (XEXP (x, 0), NULL_RTX);
+      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
-          && ! (GET_CODE (XEXP (x, 0)) == REG
+          && ! (REG_P (XEXP (x, 0))
                 && REGNO (XEXP (x, 0)) < FIRST_PSEUDO_REGISTER))
-    canon_reg (XEXP (x, 0), NULL_RTX);
+    canon_reg (XEXP (x, 0), insn);
   else if (GET_CODE (x) == CALL)
     {
       /* The result of apply_change_group can be ignored; see canon_reg.  */
@@ -4922,8 +4180,12 @@ cse_insn (insn, libcall_insn)
       && (! rtx_equal_p (XEXP (tem, 0), SET_SRC (sets[0].rtl))
          || GET_CODE (SET_DEST (sets[0].rtl)) == STRICT_LOW_PART))
     {
-      src_eqv = fold_rtx (canon_reg (XEXP (tem, 0), NULL_RTX), insn);
-      XEXP (tem, 0) = src_eqv;
+      /* 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.
@@ -4940,20 +4202,11 @@ cse_insn (insn, libcall_insn)
     {
       rtx dest = SET_DEST (sets[i].rtl);
       rtx src = SET_SRC (sets[i].rtl);
-      rtx new = canon_reg (src, insn);
-      int insn_code;
-
-      sets[i].orig_src = src;
-      if ((GET_CODE (new) == REG && GET_CODE (src) == REG
-          && ((REGNO (new) < FIRST_PSEUDO_REGISTER)
-              != (REGNO (src) < FIRST_PSEUDO_REGISTER)))
-         || (insn_code = recog_memoized (insn)) < 0
-         || insn_data[insn_code].n_dups > 0)
-       validate_change (insn, &SET_SRC (sets[i].rtl), new, 1);
-      else
-       SET_SRC (sets[i].rtl) = new;
+      rtx new_rtx = canon_reg (src, insn);
 
-      if (GET_CODE (dest) == ZERO_EXTRACT || GET_CODE (dest) == SIGN_EXTRACT)
+      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);
@@ -4961,12 +4214,12 @@ cse_insn (insn, libcall_insn)
                           canon_reg (XEXP (dest, 2), insn), 1);
        }
 
-      while (GET_CODE (dest) == SUBREG || GET_CODE (dest) == STRICT_LOW_PART
+      while (GET_CODE (dest) == SUBREG
             || GET_CODE (dest) == ZERO_EXTRACT
-            || GET_CODE (dest) == SIGN_EXTRACT)
+            || GET_CODE (dest) == STRICT_LOW_PART)
        dest = XEXP (dest, 0);
 
-      if (GET_CODE (dest) == MEM)
+      if (MEM_P (dest))
        canon_reg (dest, insn);
     }
 
@@ -5007,7 +4260,7 @@ cse_insn (insn, libcall_insn)
       int src_folded_regcost = MAX_COST;
       int src_related_regcost = MAX_COST;
       int src_elt_regcost = MAX_COST;
-      /* Set non-zero if we need to call force_const_mem on with the
+      /* 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;
 
@@ -5060,8 +4313,7 @@ cse_insn (insn, libcall_insn)
         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
-         || GET_CODE (SET_DEST (sets[i].rtl)) == SIGN_EXTRACT)
+      if (GET_CODE (SET_DEST (sets[i].rtl)) == ZERO_EXTRACT)
        {
          rtx width = XEXP (SET_DEST (sets[i].rtl), 1);
 
@@ -5093,9 +4345,9 @@ cse_insn (insn, libcall_insn)
         RTL would be referring to SRC, so we don't lose any optimization
         opportunities by not having SRC in the hash table.  */
 
-      if (GET_CODE (src) == MEM
+      if (MEM_P (src)
          && find_reg_note (insn, REG_EQUIV, NULL_RTX) != 0
-         && GET_CODE (dest) == REG
+         && REG_P (dest)
          && REGNO (dest) >= FIRST_PSEUDO_REGISTER)
        sets[i].src_volatile = 1;
 
@@ -5103,7 +4355,7 @@ cse_insn (insn, libcall_insn)
       /* 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 perverse subreg (such as QI treated as an SI),
+      /* 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.  */
@@ -5234,7 +4486,8 @@ cse_insn (insn, libcall_insn)
          enum machine_mode wider_mode;
 
          for (wider_mode = GET_MODE_WIDER_MODE (mode);
-              GET_MODE_BITSIZE (wider_mode) <= BITS_PER_WORD
+              wider_mode != VOIDmode
+              && GET_MODE_BITSIZE (wider_mode) <= BITS_PER_WORD
               && src_related == 0;
               wider_mode = GET_MODE_WIDER_MODE (wider_mode))
            {
@@ -5246,10 +4499,9 @@ cse_insn (insn, libcall_insn)
 
              for (const_elt = const_elt->first_same_value;
                   const_elt; const_elt = const_elt->next_same_value)
-               if (GET_CODE (const_elt->exp) == REG)
+               if (REG_P (const_elt->exp))
                  {
-                   src_related = gen_lowpart_if_possible (mode,
-                                                          const_elt->exp);
+                   src_related = gen_lowpart (mode, const_elt->exp);
                    break;
                  }
            }
@@ -5272,7 +4524,7 @@ cse_insn (insn, libcall_insn)
               GET_MODE_SIZE (tmode) <= UNITS_PER_WORD;
               tmode = GET_MODE_WIDER_MODE (tmode))
            {
-             rtx inner = gen_lowpart_if_possible (tmode, XEXP (src, 0));
+             rtx inner = gen_lowpart (tmode, XEXP (src, 0));
              struct table_elt *larger_elt;
 
              if (inner)
@@ -5285,10 +4537,10 @@ cse_insn (insn, libcall_insn)
 
                  for (larger_elt = larger_elt->first_same_value;
                       larger_elt; larger_elt = larger_elt->next_same_value)
-                   if (GET_CODE (larger_elt->exp) == REG)
+                   if (REG_P (larger_elt->exp))
                      {
                        src_related
-                         = gen_lowpart_if_possible (mode, larger_elt->exp);
+                         = gen_lowpart (mode, larger_elt->exp);
                        break;
                      }
 
@@ -5302,18 +4554,21 @@ cse_insn (insn, libcall_insn)
       /* 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 these machines.  */
+        beneficial on these machines.  */
 
       if (flag_expensive_optimizations && src_related == 0
          && (GET_MODE_SIZE (mode) < UNITS_PER_WORD)
          && GET_MODE_CLASS (mode) == MODE_INT
-         && GET_CODE (src) == MEM && ! do_not_record
-         && LOAD_EXTEND_OP (mode) != NIL)
+         && 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;
 
@@ -5331,10 +4586,9 @@ cse_insn (insn, libcall_insn)
 
              for (larger_elt = larger_elt->first_same_value;
                   larger_elt; larger_elt = larger_elt->next_same_value)
-               if (GET_CODE (larger_elt->exp) == REG)
+               if (REG_P (larger_elt->exp))
                  {
-                   src_related = gen_lowpart_if_possible (mode,
-                                                          larger_elt->exp);
+                   src_related = gen_lowpart (mode, larger_elt->exp);
                    break;
                  }
 
@@ -5347,9 +4601,9 @@ cse_insn (insn, libcall_insn)
       if (src == src_folded)
        src_folded = 0;
 
-      /* At this point, ELT, if non-zero, points to a class of expressions
+      /* 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 non-zero, each contain additional equivalent
+        and SRC_RELATED, if nonzero, each contain additional equivalent
         expressions.  Prune these latter expressions by deleting expressions
         already in the equivalence class.
 
@@ -5367,7 +4621,7 @@ cse_insn (insn, libcall_insn)
          /* 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, 0))
+         if (code != REG && ! exp_equiv_p (p->exp, p->exp, 1, false))
            continue;
 
          /* Also skip paradoxical subregs, unless that's what we're
@@ -5463,8 +4717,8 @@ cse_insn (insn, libcall_insn)
          rtx trial;
 
          /* Skip invalid entries.  */
-         while (elt && GET_CODE (elt->exp) != REG
-                && ! exp_equiv_p (elt->exp, elt->exp, 1, 0))
+         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
@@ -5486,54 +4740,75 @@ cse_insn (insn, libcall_insn)
              continue;
            }
 
-          if (elt)
+         if (elt)
            {
              src_elt_cost = elt->cost;
              src_elt_regcost = elt->regcost;
            }
 
-          /* Find cheapest and skip it for the next time.   For items
+         /* 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
-             && preferrable (src_folded_cost, src_folded_regcost,
-                             src_cost, src_regcost) <= 0
-             && preferrable (src_folded_cost, src_folded_regcost,
-                             src_eqv_cost, src_eqv_regcost) <= 0
-             && preferrable (src_folded_cost, src_folded_regcost,
-                             src_related_cost, src_related_regcost) <= 0
-             && preferrable (src_folded_cost, src_folded_regcost,
-                             src_elt_cost, src_elt_regcost) <= 0)
+             && 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)
-               trial = force_const_mem (mode, trial);
+               {
+                 rtx forced = force_const_mem (mode, trial);
+                 if (forced)
+                   trial = forced;
+               }
            }
          else if (src
-                  && preferrable (src_cost, src_regcost,
-                                  src_eqv_cost, src_eqv_regcost) <= 0
-                  && preferrable (src_cost, src_regcost,
-                                  src_related_cost, src_related_regcost) <= 0
-                  && preferrable (src_cost, src_regcost,
-                                  src_elt_cost, src_elt_regcost) <= 0)
+                  && 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
-                  && preferrable (src_eqv_cost, src_eqv_regcost,
-                                  src_related_cost, src_related_regcost) <= 0
-                  && preferrable (src_eqv_cost, src_eqv_regcost,
-                                  src_elt_cost, src_elt_regcost) <= 0)
-           trial = copy_rtx (src_eqv_here), src_eqv_cost = MAX_COST;
+                  && 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
-                  && preferrable (src_related_cost, src_related_regcost,
-                                  src_elt_cost, src_elt_regcost) <= 0)
-           trial = copy_rtx (src_related), src_related_cost = MAX_COST;
+                  && preferable (src_related_cost, src_related_regcost,
+                                 src_elt_cost, src_elt_regcost) <= 0)
+           trial = src_related, src_related_cost = MAX_COST;
          else
            {
-             trial = copy_rtx (elt->exp);
+             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.
@@ -5548,31 +4823,44 @@ cse_insn (insn, libcall_insn)
                  || (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 = 1;
+             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_change (insn, &SET_SRC (sets[i].rtl), trial, 0))
+         else if (validate_unshare_change
+                    (insn, &SET_SRC (sets[i].rtl), trial, 0))
            {
-             /* If we just made a substitution inside a libcall, then we
-                need to make the same substitution in any notes attached
-                to the RETVAL insn.  */
-             if (libcall_insn
-                 && (GET_CODE (sets[i].orig_src) == REG
-                     || GET_CODE (sets[i].orig_src) == SUBREG
-                     || GET_CODE (sets[i].orig_src) == MEM))
-               replace_rtx (REG_NOTES (libcall_insn), sets[i].orig_src,
-                            canon_reg (SET_SRC (sets[i].rtl), insn));
+             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),
-                              canon_reg (SET_SRC (sets[i].rtl), insn),
-                              1);
+             validate_change (insn, &SET_SRC (sets[i].rtl), new_rtx, 1);
              apply_change_group ();
+
              break;
            }
 
@@ -5583,18 +4871,8 @@ cse_insn (insn, libcall_insn)
 
          else if (constant_pool_entries_cost
                   && CONSTANT_P (trial)
-                  /* Reject cases that will abort in decode_rtx_const.
-                     On the alpha when simplifying a switch, we get
-                     (const (truncate (minus (label_ref) (label_ref)))).  */
-                  && ! (GET_CODE (trial) == CONST
-                        && GET_CODE (XEXP (trial, 0)) == TRUNCATE)
-                  /* Likewise on IA-64, except without the truncate.  */
-                  && ! (GET_CODE (trial) == CONST
-                        && GET_CODE (XEXP (trial, 0)) == MINUS
-                        && GET_CODE (XEXP (XEXP (trial, 0), 0)) == LABEL_REF
-                        && GET_CODE (XEXP (XEXP (trial, 0), 1)) == LABEL_REF)
                   && (src_folded == 0
-                      || (GET_CODE (src_folded) != MEM
+                      || (!MEM_P (src_folded)
                           && ! src_folded_force_flag))
                   && GET_MODE_CLASS (mode) != MODE_CC
                   && mode != VOIDmode)
@@ -5602,6 +4880,7 @@ cse_insn (insn, libcall_insn)
              src_folded_force_flag = 1;
              src_folded = trial;
              src_folded_cost = constant_pool_entries_cost;
+             src_folded_regcost = constant_pool_entries_regcost;
            }
        }
 
@@ -5613,7 +4892,7 @@ cse_insn (insn, libcall_insn)
         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 (GET_CODE (dest) == REG
+      if (REG_P (dest)
          && REGNO_QTY_VALID_P (REGNO (dest)))
        {
          int dest_q = REG_QTY (REGNO (dest));
@@ -5621,12 +4900,12 @@ cse_insn (insn, libcall_insn)
 
          if (dest_ent->mode == GET_MODE (dest)
              && dest_ent->first_reg != REGNO (dest)
-             && GET_CODE (src) == REG && REGNO (src) == 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.  */
-             && (GET_CODE (sets[i].src) != REG
+             && (!REG_P (sets[i].src)
                  || REGNO (sets[i].src) >= FIRST_PSEUDO_REGISTER)
-             && (GET_CODE (dest) != REG || REGNO (dest) >= 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.  */
            {
@@ -5657,7 +4936,6 @@ cse_insn (insn, libcall_insn)
       /* If we made a change, recompute SRC values.  */
       if (src != sets[i].src)
        {
-         cse_altered = 1;
          do_not_record = 0;
          hash_arg_in_memory = 0;
          sets[i].src = src;
@@ -5677,77 +4955,45 @@ cse_insn (insn, libcall_insn)
         which can be created for a reference to a compile time computable
         entry in a jump table.  */
 
-      if (n_sets == 1 && src_const && GET_CODE (dest) == REG
-         && GET_CODE (src_const) != REG
+      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))
        {
-         /* Make sure that the rtx is not shared with any other insn.  */
-         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);
-
-          /* If storing a constant value in a register that
-            previously held the constant value 0,
-            record this fact with a REG_WAS_0 note on this insn.
-
-            Note that the *register* is required to have previously held 0,
-            not just any register in the quantity and we must point to the
-            insn that set that register to zero.
-
-            Rather than track each register individually, we just see if
-            the last set for this quantity was for this register.  */
-
-         if (REGNO_QTY_VALID_P (REGNO (dest)))
+         /* We only want a REG_EQUAL note if src_const != src.  */
+         if (! rtx_equal_p (src, src_const))
            {
-             int dest_q = REG_QTY (REGNO (dest));
-             struct qty_table_elem *dest_ent = &qty_table[dest_q];
-
-             if (dest_ent->const_rtx == const0_rtx)
-               {
-                 /* See if we previously had a REG_WAS_0 note.  */
-                 rtx note = find_reg_note (insn, REG_WAS_0, NULL_RTX);
-                 rtx const_insn = dest_ent->const_insn;
+             /* Make sure that the rtx is not shared.  */
+             src_const = copy_rtx (src_const);
 
-                 if ((tem = single_set (const_insn)) != 0
-                     && rtx_equal_p (SET_DEST (tem), dest))
-                   {
-                     if (note)
-                       XEXP (note, 0) = const_insn;
-                     else
-                       REG_NOTES (insn)
-                         = gen_rtx_INSN_LIST (REG_WAS_0, const_insn,
-                                              REG_NOTES (insn));
-                   }
-               }
+             /* 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 SIGN_EXTRACT or ZERO_EXTRACT
-        to the MEM or REG within it.  */
-      while (GET_CODE (dest) == SIGN_EXTRACT
+      /* 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) == SUBREG
             || GET_CODE (dest) == STRICT_LOW_PART)
        dest = XEXP (dest, 0);
 
       sets[i].inner_dest = dest;
 
-      if (GET_CODE (dest) == MEM)
+      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)) == 'a'
+         if (GET_RTX_CLASS (GET_CODE (addr)) == RTX_AUTOINC
              && XEXP (addr, 0) == stack_pointer_rtx)
-           invalidate (stack_pointer_rtx, Pmode);
+           invalidate (stack_pointer_rtx, VOIDmode);
 #endif
          dest = fold_rtx (dest, insn);
        }
@@ -5762,8 +5008,7 @@ cse_insn (insn, libcall_insn)
         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
-         || GET_CODE (SET_DEST (sets[i].rtl)) == SIGN_EXTRACT)
+      if (GET_CODE (SET_DEST (sets[i].rtl)) == ZERO_EXTRACT)
        {
          rtx width = XEXP (SET_DEST (sets[i].rtl), 1);
 
@@ -5793,21 +5038,17 @@ cse_insn (insn, libcall_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 (insn);
-         cse_jumps_altered = 1;
+         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)
+      else if (dest == pc_rtx && GET_CODE (src) == LABEL_REF
+              && !LABEL_REF_NONLOCAL_P (src))
        {
-         /* Now emit a BARRIER after the unconditional jump.  */
-         if (NEXT_INSN (insn) == 0
-             || GET_CODE (NEXT_INSN (insn)) != BARRIER)
-           emit_barrier_after (insn);
-
          /* 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.
@@ -5817,26 +5058,28 @@ cse_insn (insn, libcall_insn)
             and hope for the best.  */
          if (n_sets == 1)
            {
-             rtx new = emit_jump_insn_before (gen_jump (XEXP (src, 0)), insn);
+             rtx new_rtx, note;
 
-             JUMP_LABEL (new) = XEXP (src, 0);
+             new_rtx = emit_jump_insn_before (gen_jump (XEXP (src, 0)), insn);
+             JUMP_LABEL (new_rtx) = XEXP (src, 0);
              LABEL_NUSES (XEXP (src, 0))++;
-             insn = new;
 
-             /* Now emit a BARRIER after the unconditional jump.  */
-             if (NEXT_INSN (insn) == 0
-                 || GET_CODE (NEXT_INSN (insn)) != BARRIER)
-               emit_barrier_after (insn);
+             /* 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;
 
-         never_reached_warning (insn, NULL);
-
-         /* Do not bother deleting any unreachable code,
-            let jump/flow do that.  */
-
-         cse_jumps_altered = 1;
+         /* Do not bother deleting any unreachable code, let jump do it.  */
+         cse_jumps_altered = true;
          sets[i].rtl = 0;
        }
 
@@ -5845,15 +5088,10 @@ cse_insn (insn, libcall_insn)
 
       else if (do_not_record)
        {
-         if (GET_CODE (dest) == REG || GET_CODE (dest) == SUBREG)
+         if (REG_P (dest) || GET_CODE (dest) == SUBREG)
+           invalidate (dest, VOIDmode);
+         else if (MEM_P (dest))
            invalidate (dest, VOIDmode);
-         else if (GET_CODE (dest) == MEM)
-           {
-             /* Outgoing arguments for a libcall don't
-                affect any recorded expressions.  */
-             if (! libcall_insn || insn == libcall_insn)
-               invalidate (dest, VOIDmode);
-           }
          else if (GET_CODE (dest) == STRICT_LOW_PART
                   || GET_CODE (dest) == ZERO_EXTRACT)
            invalidate (XEXP (dest, 0), GET_MODE (dest));
@@ -5943,29 +5181,31 @@ cse_insn (insn, libcall_insn)
            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)
              {
-               /* Don't put a hard register source into the table if this is
-                  the last insn of a libcall.  In this case, we only need
-                  to put src_eqv_elt in src_elt.  */
-               if (! find_reg_note (insn, REG_RETVAL, NULL_RTX))
-                 {
-                   struct table_elt *elt;
+               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;
+               /* 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);
                  }
-               else
-                 sets[i].src_elt = classp;
+               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
@@ -5980,14 +5220,51 @@ cse_insn (insn, libcall_insn)
         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 (GET_CODE (insn) == CALL_INSN)
+  if (CALL_P (insn))
     {
-      if (! CONST_OR_PURE_CALL_P (insn))
+      if (!(RTL_CONST_OR_PURE_CALL_P (insn)))
        invalidate_memory ();
       invalidate_for_call ();
     }
@@ -6008,26 +5285,30 @@ cse_insn (insn, libcall_insn)
           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 (GET_CODE (dest) == REG || GET_CODE (dest) == SUBREG)
+       if (REG_P (dest) || GET_CODE (dest) == SUBREG)
+         invalidate (dest, VOIDmode);
+       else if (MEM_P (dest))
          invalidate (dest, VOIDmode);
-       else if (GET_CODE (dest) == MEM)
-         {
-           /* Outgoing arguments for a libcall don't
-              affect any recorded expressions.  */
-           if (! libcall_insn || insn == libcall_insn)
-             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 (GET_CODE (insn) == INSN
+  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
@@ -6042,7 +5323,7 @@ cse_insn (insn, libcall_insn)
        {
          rtx x = SET_DEST (sets[i].rtl);
 
-         if (GET_CODE (x) != REG)
+         if (!REG_P (x))
            mention_regs (x);
          else
            {
@@ -6059,9 +5340,7 @@ cse_insn (insn, libcall_insn)
                 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
-               = regno + (regno >= FIRST_PSEUDO_REGISTER ? 1
-                          : HARD_REGNO_NREGS (regno, GET_MODE (x)));
+             unsigned int endregno = END_REGNO (x);
              unsigned int i;
 
              for (i = regno; i < endregno; i++)
@@ -6077,12 +5356,20 @@ cse_insn (insn, libcall_insn)
     }
 
   /* 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.  */
+     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].src_elt && sets[i].src_elt->first_same_value == 0)
+       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.  */
          {
@@ -6103,24 +5390,18 @@ cse_insn (insn, libcall_insn)
     if (sets[i].rtl)
       {
        rtx dest = SET_DEST (sets[i].rtl);
-       rtx inner_dest = sets[i].inner_dest;
        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
-            && GET_CODE (dest) == MEM
+            && 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
-           /* Don't record values of destinations set inside a libcall block
-              since we might delete the libcall.  Things should have been set
-              up so we won't want to reuse such a value, but we play it safe
-              here.  */
-           || libcall_insn
            /* 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
@@ -6142,7 +5423,7 @@ cse_insn (insn, libcall_insn)
        if (GET_CODE (dest) == STRICT_LOW_PART)
          dest = SUBREG_REG (XEXP (dest, 0));
 
-       if (GET_CODE (dest) == REG || GET_CODE (dest) == SUBREG)
+       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))
            {
@@ -6152,30 +5433,19 @@ cse_insn (insn, libcall_insn)
              sets[i].dest_hash = HASH (dest, GET_MODE (dest));
            }
 
-       if (GET_CODE (inner_dest) == MEM
-           && GET_CODE (XEXP (inner_dest, 0)) == ADDRESSOF)
-         /* Given (SET (MEM (ADDRESSOF (X))) Y) we don't want to say
-            that (MEM (ADDRESSOF (X))) is equivalent to Y.
-            Consider the case in which the address of the MEM is
-            passed to a function, which alters the MEM.  Then, if we
-            later use Y instead of the MEM we'll miss the update.  */
-         elt = insert (dest, 0, sets[i].dest_hash, GET_MODE (dest));
-       else
-         elt = insert (dest, sets[i].src_elt,
-                       sets[i].dest_hash, GET_MODE (dest));
+       elt = insert (dest, sets[i].src_elt,
+                     sets[i].dest_hash, GET_MODE (dest));
 
-       elt->in_memory = (GET_CODE (sets[i].inner_dest) == MEM
-                         && (! RTX_UNCHANGING_P (sets[i].inner_dest)
-                             || FIXED_BASE_PLUS_P (XEXP (sets[i].inner_dest,
-                                                         0))));
+       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_if_possible
-          will produce a simpler value than gen_lowpart_if_possible applied to
+          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
@@ -6201,15 +5471,38 @@ cse_insn (insn, libcall_insn)
                rtx new_src = 0;
                unsigned src_hash;
                struct table_elt *src_elt;
+               int byte = 0;
 
                /* Ignore invalid entries.  */
-               if (GET_CODE (elt->exp) != REG
-                   && ! exp_equiv_p (elt->exp, elt->exp, 1, 0))
+               if (!REG_P (elt->exp)
+                   && ! exp_equiv_p (elt->exp, elt->exp, 1, false))
                  continue;
 
-               new_src = gen_lowpart_if_possible (new_mode, elt->exp);
-               if (new_src == 0)
-                 new_src = gen_rtx_SUBREG (new_mode, elt->exp, 0);
+               /* 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);
@@ -6234,8 +5527,8 @@ cse_insn (insn, libcall_insn)
                classp = src_elt->first_same_value;
                /* Ignore invalid entries.  */
                while (classp
-                      && GET_CODE (classp->exp) != REG
-                      && ! exp_equiv_p (classp->exp, classp->exp, 1, 0))
+                      && !REG_P (classp->exp)
+                      && ! exp_equiv_p (classp->exp, classp->exp, 1, false))
                  classp = classp->next_same_value;
              }
          }
@@ -6252,25 +5545,28 @@ cse_insn (insn, libcall_insn)
      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.
-
-     Also don't do this if INSN ends a libcall; this would cause an unrelated
-     register to be set in the middle of a libcall, and we then get bad code
-     if the libcall is deleted.  */
+     Also do not do this if we are operating on a copy of INSN.  */
 
-  if (n_sets == 1 && sets[0].rtl && GET_CODE (SET_DEST (sets[0].rtl)) == REG
+  if (n_sets == 1 && sets[0].rtl && REG_P (SET_DEST (sets[0].rtl))
       && NEXT_INSN (PREV_INSN (insn)) == insn
-      && GET_CODE (SET_SRC (sets[0].rtl)) == REG
+      && 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)))
-         && ! find_reg_note (insn, REG_RETVAL, NULL_RTX))
+      if (src_ent->first_reg == REGNO (SET_DEST (sets[0].rtl)))
        {
-         rtx prev = prev_nonnote_insn (insn);
+         /* 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.
@@ -6282,9 +5578,8 @@ cse_insn (insn, libcall_insn)
 
             This section previously turned the REG_EQUIV into a REG_EQUAL
             note.  We cannot do that because REG_EQUIV may provide an
-            uninitialised stack slot when REG_PARM_STACK_SPACE is used.  */
-
-         if (prev != 0 && GET_CODE (prev) == INSN
+            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))
@@ -6298,20 +5593,6 @@ cse_insn (insn, libcall_insn)
              validate_change (insn, &SET_SRC (sets[0].rtl), dest, 1);
              apply_change_group ();
 
-             /* If there was a REG_WAS_0 note on PREV, remove it.  Move
-                any REG_WAS_0 note on INSN to PREV.  */
-             note = find_reg_note (prev, REG_WAS_0, NULL_RTX);
-             if (note)
-               remove_note (prev, note);
-
-             note = find_reg_note (insn, REG_WAS_0, NULL_RTX);
-             if (note)
-               {
-                 remove_note (insn, note);
-                 XEXP (note, 1) = REG_NOTES (prev);
-                 REG_NOTES (prev) = note;
-               }
-
              /* 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
@@ -6325,36 +5606,13 @@ cse_insn (insn, libcall_insn)
        }
     }
 
-  /* If this is a conditional jump insn, record any known equivalences due to
-     the condition being tested.  */
-
-  last_jump_equiv_class = 0;
-  if (GET_CODE (insn) == JUMP_INSN
-      && n_sets == 1 && GET_CODE (x) == SET
-      && GET_CODE (SET_SRC (x)) == IF_THEN_ELSE)
-    record_jump_equiv (insn, 0);
-
-#ifdef HAVE_cc0
-  /* If the previous insn set CC0 and this insn no longer references CC0,
-     delete the previous insn.  Here we use the fact that nothing expects CC0
-     to be valid over an insn, which is true until the final pass.  */
-  if (prev_insn && GET_CODE (prev_insn) == INSN
-      && (tem = single_set (prev_insn)) != 0
-      && SET_DEST (tem) == cc0_rtx
-      && ! reg_mentioned_p (cc0_rtx, x))
-    delete_insn (prev_insn);
-
-  prev_insn_cc0 = this_insn_cc0;
-  prev_insn_cc0_mode = this_insn_cc0_mode;
-#endif
-
-  prev_insn = insn;
+done:;
 }
 \f
 /* Remove from the hash table all expressions that reference memory.  */
 
 static void
-invalidate_memory ()
+invalidate_memory (void)
 {
   int i;
   struct table_elt *p, *next;
@@ -6368,30 +5626,6 @@ invalidate_memory ()
       }
 }
 
-/* If ADDR is an address that implicitly affects the stack pointer, return
-   1 and update the register tables to show the effect.  Else, return 0.  */
-
-static int
-addr_affects_sp_p (addr)
-     rtx addr;
-{
-  if (GET_RTX_CLASS (GET_CODE (addr)) == 'a'
-      && GET_CODE (XEXP (addr, 0)) == REG
-      && REGNO (XEXP (addr, 0)) == STACK_POINTER_REGNUM)
-    {
-      if (REG_TICK (STACK_POINTER_REGNUM) >= 0)
-       REG_TICK (STACK_POINTER_REGNUM)++;
-
-      /* This should be *very* rare.  */
-      if (TEST_HARD_REG_BIT (hard_regs_in_table, STACK_POINTER_REGNUM))
-       invalidate (stack_pointer_rtx, VOIDmode);
-
-      return 1;
-    }
-
-  return 0;
-}
-
 /* 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
@@ -6400,16 +5634,15 @@ addr_affects_sp_p (addr)
    X is the pattern of the insn.  */
 
 static void
-invalidate_from_clobbers (x)
-     rtx x;
+invalidate_from_clobbers (rtx x)
 {
   if (GET_CODE (x) == CLOBBER)
     {
       rtx ref = XEXP (x, 0);
       if (ref)
        {
-         if (GET_CODE (ref) == REG || GET_CODE (ref) == SUBREG
-             || GET_CODE (ref) == MEM)
+         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)
@@ -6425,8 +5658,8 @@ invalidate_from_clobbers (x)
          if (GET_CODE (y) == CLOBBER)
            {
              rtx ref = XEXP (y, 0);
-             if (GET_CODE (ref) == REG || GET_CODE (ref) == SUBREG
-                 || GET_CODE (ref) == MEM)
+             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)
@@ -6446,9 +5679,7 @@ invalidate_from_clobbers (x)
    Return the replacement for X.  */
 
 static rtx
-cse_process_notes (x, object)
-     rtx x;
-     rtx object;
+cse_process_notes_1 (rtx x, rtx object, bool *changed)
 {
   enum rtx_code code = GET_CODE (x);
   const char *fmt = GET_RTX_FORMAT (code);
@@ -6461,6 +5692,7 @@ cse_process_notes (x, object)
     case SYMBOL_REF:
     case LABEL_REF:
     case CONST_DOUBLE:
+    case CONST_FIXED:
     case CONST_VECTOR:
     case PC:
     case CC0:
@@ -6469,26 +5701,26 @@ cse_process_notes (x, object)
 
     case MEM:
       validate_change (x, &XEXP (x, 0),
-                      cse_process_notes (XEXP (x, 0), 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);
+       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);
+       XEXP (x, 1) = cse_process_notes (XEXP (x, 1), NULL_RTX, changed);
       return x;
 
     case SIGN_EXTEND:
     case ZERO_EXTEND:
     case SUBREG:
       {
-       rtx new = cse_process_notes (XEXP (x, 0), object);
+       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) != VOIDmode)
-         validate_change (object, &XEXP (x, 0), new, 0);
+       if (GET_MODE (new_rtx) != VOIDmode)
+         validate_change (object, &XEXP (x, 0), new_rtx, 0);
        return x;
       }
 
@@ -6502,11 +5734,11 @@ cse_process_notes (x, object)
 
          if (ent->const_rtx != NULL_RTX
              && (CONSTANT_P (ent->const_rtx)
-                 || GET_CODE (ent->const_rtx) == REG))
+                 || REG_P (ent->const_rtx)))
            {
-             rtx new = gen_lowpart_if_possible (GET_MODE (x), ent->const_rtx);
-             if (new)
-               return new;
+             rtx new_rtx = gen_lowpart (GET_MODE (x), ent->const_rtx);
+             if (new_rtx)
+               return copy_rtx (new_rtx);
            }
        }
 
@@ -6520,903 +5752,511 @@ cse_process_notes (x, object)
   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), 0);
+                      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;
+}
+
 \f
-/* Find common subexpressions between the end test of a loop and the beginning
-   of the loop.  LOOP_START is the CODE_LABEL at the start of a loop.
+/* Find a path in the CFG, starting with FIRST_BB to perform CSE on.
 
-   Often we have a loop where an expression in the exit test is used
-   in the body of the loop.  For example "while (*p) *q++ = *p++;".
-   Because of the way we duplicate the loop exit test in front of the loop,
-   however, we don't detect that common subexpression.  This will be caught
-   when global cse is implemented, but this is a quite common case.
+   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.
 
-   This function handles the most common cases of these common expressions.
-   It is called after we have processed the basic block ending with the
-   NOTE_INSN_LOOP_END note that ends a loop and the previous JUMP_INSN
-   jumps to a label used only once.  */
+   If FOLLOW_JUMPS is false, the maximum path length is 1 and the only
+   block in the path will be FIRST_BB.  */
 
-static void
-cse_around_loop (loop_start)
-     rtx loop_start;
+static bool
+cse_find_path (basic_block first_bb, struct cse_basic_block_data *data,
+              int follow_jumps)
 {
-  rtx insn;
-  int i;
-  struct table_elt *p;
+  basic_block bb;
+  edge e;
+  int path_size;
+  SET_BIT (cse_visited_basic_blocks, first_bb->index);
 
-  /* If the jump at the end of the loop doesn't go to the start, we don't
-     do anything.  */
-  for (insn = PREV_INSN (loop_start);
-       insn && (GET_CODE (insn) == NOTE && NOTE_LINE_NUMBER (insn) >= 0);
-       insn = PREV_INSN (insn))
-    ;
+  /* See if there is a previous path.  */
+  path_size = data->path_size;
 
-  if (insn == 0
-      || GET_CODE (insn) != NOTE
-      || NOTE_LINE_NUMBER (insn) != NOTE_INSN_LOOP_BEG)
-    return;
-
-  /* If the last insn of the loop (the end test) was an NE comparison,
-     we will interpret it as an EQ comparison, since we fell through
-     the loop.  Any equivalences resulting from that comparison are
-     therefore not valid and must be invalidated.  */
-  if (last_jump_equiv_class)
-    for (p = last_jump_equiv_class->first_same_value; p;
-        p = p->next_same_value)
-      {
-       if (GET_CODE (p->exp) == MEM || GET_CODE (p->exp) == REG
-           || (GET_CODE (p->exp) == SUBREG
-               && GET_CODE (SUBREG_REG (p->exp)) == REG))
-         invalidate (p->exp, VOIDmode);
-       else if (GET_CODE (p->exp) == STRICT_LOW_PART
-                || GET_CODE (p->exp) == ZERO_EXTRACT)
-         invalidate (XEXP (p->exp, 0), GET_MODE (p->exp));
-      }
-
-  /* Process insns starting after LOOP_START until we hit a CALL_INSN or
-     a CODE_LABEL (we could handle a CALL_INSN, but it isn't worth it).
-
-     The only thing we do with SET_DEST is invalidate entries, so we
-     can safely process each SET in order.  It is slightly less efficient
-     to do so, but we only want to handle the most common cases.
+  /* There is a previous path.  Make sure it started with FIRST_BB.  */
+  if (path_size)
+    gcc_assert (data->path[0].bb == first_bb);
 
-     The gen_move_insn call in cse_set_around_loop may create new pseudos.
-     These pseudos won't have valid entries in any of the tables indexed
-     by register number, such as reg_qty.  We avoid out-of-range array
-     accesses by not processing any instructions created after cse started.  */
-
-  for (insn = NEXT_INSN (loop_start);
-       GET_CODE (insn) != CALL_INSN && GET_CODE (insn) != CODE_LABEL
-       && INSN_UID (insn) < max_insn_uid
-       && ! (GET_CODE (insn) == NOTE
-            && NOTE_LINE_NUMBER (insn) == NOTE_INSN_LOOP_END);
-       insn = NEXT_INSN (insn))
+  /* 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)
     {
-      if (INSN_P (insn)
-         && (GET_CODE (PATTERN (insn)) == SET
-             || GET_CODE (PATTERN (insn)) == CLOBBER))
-       cse_set_around_loop (PATTERN (insn), insn, loop_start);
-      else if (INSN_P (insn) && GET_CODE (PATTERN (insn)) == PARALLEL)
-       for (i = XVECLEN (PATTERN (insn), 0) - 1; i >= 0; i--)
-         if (GET_CODE (XVECEXP (PATTERN (insn), 0, i)) == SET
-             || GET_CODE (XVECEXP (PATTERN (insn), 0, i)) == CLOBBER)
-           cse_set_around_loop (XVECEXP (PATTERN (insn), 0, i), insn,
-                                loop_start);
+      path_size = 0;
+      goto done;
     }
-}
-\f
-/* Process one SET of an insn that was skipped.  We ignore CLOBBERs
-   since they are done elsewhere.  This function is called via note_stores.  */
 
-static void
-invalidate_skipped_set (dest, set, data)
-     rtx set;
-     rtx dest;
-     void *data ATTRIBUTE_UNUSED;
-{
-  enum rtx_code code = GET_CODE (dest);
-
-  if (code == MEM
-      && ! addr_affects_sp_p (dest)    /* If this is not a stack push ...  */
-      /* There are times when an address can appear varying and be a PLUS
-        during this scan when it would be a fixed address were we to know
-        the proper equivalences.  So invalidate all memory if there is
-        a BLKmode or nonscalar memory reference or a reference to a
-        variable address.  */
-      && (MEM_IN_STRUCT_P (dest) || GET_MODE (dest) == BLKmode
-         || cse_rtx_varies_p (XEXP (dest, 0), 0)))
+  /* If the path was empty from the beginning, construct a new path.  */
+  if (path_size == 0)
+    data->path[path_size++].bb = first_bb;
+  else
     {
-      invalidate_memory ();
-      return;
-    }
+      /* Otherwise, path_size must be equal to or greater than 2, because
+        a previous path exists that is at least two basic blocks long.
 
-  if (GET_CODE (set) == CLOBBER
-#ifdef HAVE_cc0
-      || dest == cc0_rtx
-#endif
-      || dest == pc_rtx)
-    return;
-
-  if (code == STRICT_LOW_PART || code == ZERO_EXTRACT)
-    invalidate (XEXP (dest, 0), GET_MODE (dest));
-  else if (code == REG || code == SUBREG || code == MEM)
-    invalidate (dest, VOIDmode);
-}
+        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;
+               }
+           }
 
-/* Invalidate all insns from START up to the end of the function or the
-   next label.  This called when we wish to CSE around a block that is
-   conditionally executed.  */
+         data->path[path_size].bb = NULL;
+       }
 
-static void
-invalidate_skipped_block (start)
-     rtx start;
-{
-  rtx insn;
+      /* If only one block remains in the path, bail.  */
+      if (path_size == 1)
+       {
+         path_size = 0;
+         goto done;
+       }
+    }
 
-  for (insn = start; insn && GET_CODE (insn) != CODE_LABEL;
-       insn = NEXT_INSN (insn))
+  /* Extend the path if possible.  */
+  if (follow_jumps)
     {
-      if (! INSN_P (insn))
-       continue;
-
-      if (GET_CODE (insn) == CALL_INSN)
+      bb = data->path[path_size - 1].bb;
+      while (bb && path_size < PARAM_VALUE (PARAM_MAX_CSE_PATH_LENGTH))
        {
-         if (! CONST_OR_PURE_CALL_P (insn))
-           invalidate_memory ();
-         invalidate_for_call ();
-       }
+         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;
 
-      invalidate_from_clobbers (PATTERN (insn));
-      note_stores (PATTERN (insn), invalidate_skipped_set, 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;
 }
 \f
-/* If modifying X will modify the value in *DATA (which is really an
-   `rtx *'), indicate that fact by setting the pointed to value to
-   NULL_RTX.  */
+/* Dump the path in DATA to file F.  NSETS is the number of sets
+   in the path.  */
 
 static void
-cse_check_loop_start (x, set, data)
-     rtx x;
-     rtx set ATTRIBUTE_UNUSED;
-     void *data;
+cse_dump_path (struct cse_basic_block_data *data, int nsets, FILE *f)
 {
-  rtx *cse_check_loop_start_value = (rtx *) data;
+  int path_entry;
 
-  if (*cse_check_loop_start_value == NULL_RTX
-      || GET_CODE (x) == CC0 || GET_CODE (x) == PC)
-    return;
-
-  if ((GET_CODE (x) == MEM && GET_CODE (*cse_check_loop_start_value) == MEM)
-      || reg_overlap_mentioned_p (x, *cse_check_loop_start_value))
-    *cse_check_loop_start_value = NULL_RTX;
+  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);
 }
 
-/* X is a SET or CLOBBER contained in INSN that was found near the start of
-   a loop that starts with the label at LOOP_START.
-
-   If X is a SET, we see if its SET_SRC is currently in our hash table.
-   If so, we see if it has a value equal to some register used only in the
-   loop exit code (as marked by jump.c).
-
-   If those two conditions are true, we search backwards from the start of
-   the loop to see if that same value was loaded into a register that still
-   retains its value at the start of the loop.
-
-   If so, we insert an insn after the load to copy the destination of that
-   load into the equivalent register and (try to) replace our SET_SRC with that
-   register.
-
-   In any event, we invalidate whatever this SET or CLOBBER modifies.  */
+\f
+/* Return true if BB has exception handling successor edges.  */
 
-static void
-cse_set_around_loop (x, insn, loop_start)
-     rtx x;
-     rtx insn;
-     rtx loop_start;
+static bool
+have_eh_succ_edges (basic_block bb)
 {
-  struct table_elt *src_elt;
+  edge e;
+  edge_iterator ei;
 
-  /* If this is a SET, see if we can replace SET_SRC, but ignore SETs that
-     are setting PC or CC0 or whose SET_SRC is already a register.  */
-  if (GET_CODE (x) == SET
-      && GET_CODE (SET_DEST (x)) != PC && GET_CODE (SET_DEST (x)) != CC0
-      && GET_CODE (SET_SRC (x)) != REG)
-    {
-      src_elt = lookup (SET_SRC (x),
-                       HASH (SET_SRC (x), GET_MODE (SET_DEST (x))),
-                       GET_MODE (SET_DEST (x)));
-
-      if (src_elt)
-       for (src_elt = src_elt->first_same_value; src_elt;
-            src_elt = src_elt->next_same_value)
-         if (GET_CODE (src_elt->exp) == REG && REG_LOOP_TEST_P (src_elt->exp)
-             && COST (src_elt->exp) < COST (SET_SRC (x)))
-           {
-             rtx p, set;
-
-             /* Look for an insn in front of LOOP_START that sets
-                something in the desired mode to SET_SRC (x) before we hit
-                a label or CALL_INSN.  */
-
-             for (p = prev_nonnote_insn (loop_start);
-                  p && GET_CODE (p) != CALL_INSN
-                  && GET_CODE (p) != CODE_LABEL;
-                  p = prev_nonnote_insn  (p))
-               if ((set = single_set (p)) != 0
-                   && GET_CODE (SET_DEST (set)) == REG
-                   && GET_MODE (SET_DEST (set)) == src_elt->mode
-                   && rtx_equal_p (SET_SRC (set), SET_SRC (x)))
-                 {
-                   /* We now have to ensure that nothing between P
-                      and LOOP_START modified anything referenced in
-                      SET_SRC (x).  We know that nothing within the loop
-                      can modify it, or we would have invalidated it in
-                      the hash table.  */
-                   rtx q;
-                   rtx cse_check_loop_start_value = SET_SRC (x);
-                   for (q = p; q != loop_start; q = NEXT_INSN (q))
-                     if (INSN_P (q))
-                       note_stores (PATTERN (q),
-                                    cse_check_loop_start,
-                                    &cse_check_loop_start_value);
-
-                   /* If nothing was changed and we can replace our
-                      SET_SRC, add an insn after P to copy its destination
-                      to what we will be replacing SET_SRC with.  */
-                   if (cse_check_loop_start_value
-                       && validate_change (insn, &SET_SRC (x),
-                                           src_elt->exp, 0))
-                     {
-                       /* If this creates new pseudos, this is unsafe,
-                          because the regno of new pseudo is unsuitable
-                          to index into reg_qty when cse_insn processes
-                          the new insn.  Therefore, if a new pseudo was
-                          created, discard this optimization.  */
-                       int nregs = max_reg_num ();
-                       rtx move
-                         = gen_move_insn (src_elt->exp, SET_DEST (set));
-                       if (nregs != max_reg_num ())
-                         {
-                           if (! validate_change (insn, &SET_SRC (x),
-                                                  SET_SRC (set), 0))
-                             abort ();
-                         }
-                       else
-                         emit_insn_after (move, p);
-                     }
-                   break;
-                 }
-           }
-    }
+  FOR_EACH_EDGE (e, ei, bb->succs)
+    if (e->flags & EDGE_EH)
+      return true;
 
-  /* Deal with the destination of X affecting the stack pointer.  */
-  addr_affects_sp_p (SET_DEST (x));
-
-  /* See comment on similar code in cse_insn for explanation of these
-     tests.  */
-  if (GET_CODE (SET_DEST (x)) == REG || GET_CODE (SET_DEST (x)) == SUBREG
-      || GET_CODE (SET_DEST (x)) == MEM)
-    invalidate (SET_DEST (x), VOIDmode);
-  else if (GET_CODE (SET_DEST (x)) == STRICT_LOW_PART
-          || GET_CODE (SET_DEST (x)) == ZERO_EXTRACT)
-    invalidate (XEXP (SET_DEST (x), 0), GET_MODE (SET_DEST (x)));
+  return false;
 }
-\f
-/* Find the end of INSN's basic block and return its range,
-   the total number of SETs in all the insns of the block, the last insn of the
-   block, and the branch path.
-
-   The branch path indicates which branches should be followed.  If a non-zero
-   path size is specified, the block should be rescanned and a different set
-   of branches will be taken.  The branch path is only used if
-   FLAG_CSE_FOLLOW_JUMPS or FLAG_CSE_SKIP_BLOCKS is non-zero.
 
-   DATA is a pointer to a struct cse_basic_block_data, defined below, that is
-   used to describe the block.  It is filled in with the information about
-   the current block.  The incoming structure's branch path, if any, is used
-   to construct the output branch path.  */
+\f
+/* 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.  */
 
-void
-cse_end_of_basic_block (insn, data, follow_jumps, after_loop, skip_blocks)
-     rtx insn;
-     struct cse_basic_block_data *data;
-     int follow_jumps;
-     int after_loop;
-     int skip_blocks;
+static void
+cse_prescan_path (struct cse_basic_block_data *data)
 {
-  rtx p = insn, q;
   int nsets = 0;
-  int low_cuid = INSN_CUID (insn), high_cuid = INSN_CUID (insn);
-  rtx next = INSN_P (insn) ? insn : next_real_insn (insn);
   int path_size = data->path_size;
-  int path_entry = 0;
-  int i;
+  int path_entry;
 
-  /* Update the previous branch path, if any.  If the last branch was
-     previously TAKEN, mark it NOT_TAKEN.  If it was previously NOT_TAKEN,
-     shorten the path by one and look at the previous branch.  We know that
-     at least one branch must have been taken if PATH_SIZE is non-zero.  */
-  while (path_size > 0)
+  /* Scan to end of each basic block in the path.  */
+  for (path_entry = 0; path_entry < path_size; path_entry++) 
     {
-      if (data->path[path_size - 1].status != NOT_TAKEN)
+      basic_block bb;
+      rtx insn;
+
+      bb = data->path[path_entry].bb;
+
+      FOR_BB_INSNS (bb, insn)
        {
-         data->path[path_size - 1].status = NOT_TAKEN;
-         break;
+         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;
        }
-      else
-       path_size--;
     }
 
-  /* If the first instruction is marked with QImode, that means we've
-     already processed this block.  Our caller will look at DATA->LAST
-     to figure out where to go next.  We want to return the next block
-     in the instruction stream, not some branched-to block somewhere
-     else.  We accomplish this by pretending our called forbid us to
-     follow jumps, or skip blocks.  */
-  if (GET_MODE (insn) == QImode)
-    follow_jumps = skip_blocks = 0;
-
-  /* Scan to end of this basic block.  */
-  while (p && GET_CODE (p) != CODE_LABEL)
-    {
-      /* Don't cse out the end of a loop.  This makes a difference
-        only for the unusual loops that always execute at least once;
-        all other loops have labels there so we will stop in any case.
-        Cse'ing out the end of the loop is dangerous because it
-        might cause an invariant expression inside the loop
-        to be reused after the end of the loop.  This would make it
-        hard to move the expression out of the loop in loop.c,
-        especially if it is one of several equivalent expressions
-        and loop.c would like to eliminate it.
-
-        If we are running after loop.c has finished, we can ignore
-        the NOTE_INSN_LOOP_END.  */
-
-      if (! after_loop && GET_CODE (p) == NOTE
-         && NOTE_LINE_NUMBER (p) == NOTE_INSN_LOOP_END)
-       break;
+  data->nsets = nsets;
+}
+\f
+/* Process a single extended basic block described by EBB_DATA.  */
 
-      /* 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 (PREV_INSN (p) && GET_CODE (PREV_INSN (p)) == CALL_INSN
-         && find_reg_note (PREV_INSN (p), REG_SETJMP, NULL))
-       break;
+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;
 
-      /* A PARALLEL can have lots of SETs in it,
-        especially if it is really an ASM_OPERANDS.  */
-      if (INSN_P (p) && GET_CODE (PATTERN (p)) == PARALLEL)
-       nsets += XVECLEN (PATTERN (p), 0);
-      else if (GET_CODE (p) != NOTE)
-       nsets += 1;
+  /* Allocate the space needed by qty_table.  */
+  qty_table = XNEWVEC (struct qty_table_elem, max_qty);
 
-      /* Ignore insns made by CSE; they cannot affect the boundaries of
-        the basic block.  */
+  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;
 
-      if (INSN_UID (p) <= max_uid && INSN_CUID (p) > high_cuid)
-       high_cuid = INSN_CUID (p);
-      if (INSN_UID (p) <= max_uid && INSN_CUID (p) < low_cuid)
-       low_cuid = INSN_CUID (p);
+      bb = ebb_data->path[path_entry].bb;
 
-      /* See if this insn is in our branch path.  If it is and we are to
-        take it, do so.  */
-      if (path_entry < path_size && data->path[path_entry].branch == p)
+      /* Invalidate recorded information for eh regs if there is an EH
+        edge pointing to that bb.  */
+      if (bb_has_eh_pred (bb))
        {
-         if (data->path[path_entry].status != NOT_TAKEN)
-           p = JUMP_LABEL (p);
+         df_ref *def_rec;
 
-         /* Point to next entry in path, if any.  */
-         path_entry++;
+         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)));
+           }
        }
 
-      /* If this is a conditional jump, we can follow it if -fcse-follow-jumps
-        was specified, we haven't reached our maximum path length, there are
-        insns following the target of the jump, this is the only use of the
-        jump label, and the target label is preceded by a BARRIER.
-
-        Alternatively, we can follow the jump if it branches around a
-        block of code and there are no other branches into the block.
-        In this case invalidate_skipped_block will be called to invalidate any
-        registers set in the block when following the jump.  */
-
-      else if ((follow_jumps || skip_blocks) && path_size < PATHLENGTH - 1
-              && GET_CODE (p) == JUMP_INSN
-              && GET_CODE (PATTERN (p)) == SET
-              && GET_CODE (SET_SRC (PATTERN (p))) == IF_THEN_ELSE
-              && JUMP_LABEL (p) != 0
-              && LABEL_NUSES (JUMP_LABEL (p)) == 1
-              && NEXT_INSN (JUMP_LABEL (p)) != 0)
+      FOR_BB_INSNS (bb, insn)
        {
-         for (q = PREV_INSN (JUMP_LABEL (p)); q; q = PREV_INSN (q))
-           if ((GET_CODE (q) != NOTE
-                || NOTE_LINE_NUMBER (q) == NOTE_INSN_LOOP_END
-                || (PREV_INSN (q) && GET_CODE (PREV_INSN (q)) == CALL_INSN
-                    && find_reg_note (PREV_INSN (q), REG_SETJMP, NULL)))
-               && (GET_CODE (q) != CODE_LABEL || LABEL_NUSES (q) != 0))
-             break;
+         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 we ran into a BARRIER, this code is an extension of the
-            basic block when the branch is taken.  */
-         if (follow_jumps && q != 0 && GET_CODE (q) == BARRIER)
+         if (INSN_P (insn))
            {
-             /* Don't allow ourself to keep walking around an
-                always-executed loop.  */
-             if (next_real_insn (q) == next)
+             /* Process notes first so we have all notes in canonical forms
+                when looking for duplicate operations.  */
+             if (REG_NOTES (insn))
                {
-                 p = NEXT_INSN (p);
-                 continue;
+                 bool changed = false;
+                 REG_NOTES (insn) = cse_process_notes (REG_NOTES (insn),
+                                                       NULL_RTX, &changed);
+                 if (changed)
+                   df_notes_rescan (insn);
                }
 
-             /* Similarly, don't put a branch in our path more than once.  */
-             for (i = 0; i < path_entry; i++)
-               if (data->path[i].branch == p)
-                 break;
-
-             if (i != path_entry)
-               break;
-
-             data->path[path_entry].branch = p;
-             data->path[path_entry++].status = TAKEN;
+             cse_insn (insn);
 
-             /* This branch now ends our path.  It was possible that we
-                didn't see this branch the last time around (when the
-                insn in front of the target was a JUMP_INSN that was
-                turned into a no-op).  */
-             path_size = path_entry;
+             /* 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;
 
-             p = JUMP_LABEL (p);
-             /* Mark block so we won't scan it again later.  */
-             PUT_MODE (NEXT_INSN (p), QImode);
-           }
-         /* Detect a branch around a block of code.  */
-         else if (skip_blocks && q != 0 && GET_CODE (q) != CODE_LABEL)
-           {
-             rtx tmp;
+#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 (next_real_insn (q) == next)
+             /* 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))
                {
-                 p = NEXT_INSN (p);
-                 continue;
+                 prev_insn_cc0 = this_insn_cc0;
+                 prev_insn_cc0_mode = this_insn_cc0_mode;
                }
+#endif
+           }
+       }
 
-             for (i = 0; i < path_entry; i++)
-               if (data->path[i].branch == p)
-                 break;
-
-             if (i != path_entry)
-               break;
-
-             /* This is no_labels_between_p (p, q) with an added check for
-                reaching the end of a function (in case Q precedes P).  */
-             for (tmp = NEXT_INSN (p); tmp && tmp != q; tmp = NEXT_INSN (tmp))
-               if (GET_CODE (tmp) == CODE_LABEL)
-                 break;
-
-             if (tmp == q)
+      /* 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
                {
-                 data->path[path_entry].branch = p;
-                 data->path[path_entry++].status = AROUND;
-
-                 path_size = path_entry;
-
-                 p = JUMP_LABEL (p);
-                 /* Mark block so we won't scan it again later.  */
-                 PUT_MODE (NEXT_INSN (p), QImode);
+                 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;
            }
        }
-      p = NEXT_INSN (p);
-    }
 
-  data->low_cuid = low_cuid;
-  data->high_cuid = high_cuid;
-  data->nsets = nsets;
-  data->last = p;
+      /* 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);
+       }
 
-  /* If all jumps in the path are not taken, set our path length to zero
-     so a rescan won't be done.  */
-  for (i = path_size - 1; i >= 0; i--)
-    if (data->path[i].status != NOT_TAKEN)
-      break;
+#ifdef HAVE_cc0
+      /* Clear the CC0-tracking related insns, they can't provide
+        useful information across basic block boundaries.  */
+      prev_insn_cc0 = 0;
+#endif
+    }
 
-  if (i == -1)
-    data->path_size = 0;
-  else
-    data->path_size = path_size;
+  gcc_assert (next_qty <= max_qty);
 
-  /* End the current branch path.  */
-  data->path[path_size].branch = 0;
+  free (qty_table);
 }
+
 \f
 /* 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.
 
-   AFTER_LOOP is 1 if this is the cse call done after loop optimization
-   (only if -frerun-cse-after-loop).
-
-   Returns 1 if jump_optimize should be redone due to simplifications
-   in conditional jump instructions.  */
+   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 (f, nregs, after_loop, file)
-     rtx f;
-     int nregs;
-     int after_loop;
-     FILE *file;
+cse_main (rtx f ATTRIBUTE_UNUSED, int nregs)
 {
-  struct cse_basic_block_data val;
-  rtx insn = f;
-  int i;
-
-  cse_jumps_altered = 0;
-  recorded_label_ref = 0;
-  constant_pool_entries_cost = 0;
-  val.path_size = 0;
-
-  init_recog ();
-  init_alias_analysis ();
-
-  max_reg = nregs;
-
-  max_insn_uid = get_max_uid ();
-
-  reg_eqv_table = (struct reg_eqv_elem *)
-    xmalloc (nregs * sizeof (struct reg_eqv_elem));
-
-#ifdef LOAD_EXTEND_OP
+  struct cse_basic_block_data ebb_data;
+  basic_block bb;
+  int *rc_order = XNEWVEC (int, last_basic_block);
+  int i, n_blocks;
 
-  /* Allocate scratch rtl here.  cse_insn will fill in the memory reference
-     and change the code and mode as appropriate.  */
-  memory_extend_rtx = gen_rtx_ZERO_EXTEND (VOIDmode, NULL_RTX);
-#endif
+  df_set_flags (DF_LR_RUN_DCE);
+  df_analyze ();
+  df_set_flags (DF_DEFER_INSN_RESCAN);
 
-  /* Reset the counter indicating how many elements have been made
-     thus far.  */
-  n_elements_made = 0;
+  reg_scan (get_insns (), max_reg_num ());
+  init_cse_reg_info (nregs);
 
-  /* Find the largest uid.  */
+  ebb_data.path = XNEWVEC (struct branch_path,
+                          PARAM_VALUE (PARAM_MAX_CSE_PATH_LENGTH));
 
-  max_uid = get_max_uid ();
-  uid_cuid = (int *) xcalloc (max_uid + 1, sizeof (int));
+  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;
 
-  /* Compute the mapping from uids to cuids.
-     CUIDs are numbers assigned to insns, like uids,
-     except that cuids increase monotonically through the code.
-     Don't assign cuids to line-number NOTEs, so that the distance in cuids
-     between two insns is not affected by -g.  */
+  init_recog ();
+  init_alias_analysis ();
 
-  for (insn = f, i = 0; insn; insn = NEXT_INSN (insn))
-    {
-      if (GET_CODE (insn) != NOTE
-         || NOTE_LINE_NUMBER (insn) < 0)
-       INSN_CUID (insn) = ++i;
-      else
-       /* Give a line number note the same cuid as preceding insn.  */
-       INSN_CUID (insn) = i;
-    }
+  reg_eqv_table = XNEWVEC (struct reg_eqv_elem, nregs);
 
-  ggc_push_context ();
+  /* 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.
-     Compute the maximum number of qty's needed for each basic block
-     (which is 2 for each SET).  */
-  insn = f;
-  while (insn)
+  /* 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)
     {
-      cse_altered = 0;
-      cse_end_of_basic_block (insn, &val, flag_cse_follow_jumps, after_loop,
-                             flag_cse_skip_blocks);
-
-      /* If this basic block was already processed or has no sets, skip it.  */
-      if (val.nsets == 0 || GET_MODE (insn) == QImode)
+      /* Find the first block in the RPO queue that we have not yet
+        processed before.  */
+      do
        {
-         PUT_MODE (insn, VOIDmode);
-         insn = (val.last ? NEXT_INSN (val.last) : 0);
-         val.path_size = 0;
-         continue;
+         bb = BASIC_BLOCK (rc_order[i++]);
        }
+      while (TEST_BIT (cse_visited_basic_blocks, bb->index)
+            && i < n_blocks);
 
-      cse_basic_block_start = val.low_cuid;
-      cse_basic_block_end = val.high_cuid;
-      max_qty = val.nsets * 2;
+      /* 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 (file)
-       fnotice (file, ";; Processing block from %d to %d, %d sets.\n",
-                INSN_UID (insn), val.last ? INSN_UID (val.last) : 0,
-                val.nsets);
+         /* If this basic block has no sets, skip it.  */
+         if (ebb_data.nsets == 0)
+           continue;
 
-      /* Make MAX_QTY bigger to give us room to optimize
-        past the end of this basic block, if that should prove useful.  */
-      if (max_qty < 500)
-       max_qty = 500;
+         /* 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;
 
-      max_qty += max_reg;
+         /* Dump the path we're about to process.  */
+         if (dump_file)
+           cse_dump_path (&ebb_data, ebb_data.nsets, dump_file);
 
-      /* If this basic block is being extended by following certain jumps,
-         (see `cse_end_of_basic_block'), we reprocess the code from the start.
-         Otherwise, we start after this basic block.  */
-      if (val.path_size > 0)
-       cse_basic_block (insn, val.last, val.path, 0);
-      else
-       {
-         int old_cse_jumps_altered = cse_jumps_altered;
-         rtx temp;
-
-         /* When cse changes a conditional jump to an unconditional
-            jump, we want to reprocess the block, since it will give
-            us a new branch path to investigate.  */
-         cse_jumps_altered = 0;
-         temp = cse_basic_block (insn, val.last, val.path, ! after_loop);
-         if (cse_jumps_altered == 0
-             || (flag_cse_follow_jumps == 0 && flag_cse_skip_blocks == 0))
-           insn = temp;
-
-         cse_jumps_altered |= old_cse_jumps_altered;
+         cse_extended_basic_block (&ebb_data);
        }
-
-      if (cse_altered)
-       ggc_collect ();
-
-#ifdef USE_C_ALLOCA
-      alloca (0);
-#endif
     }
 
-  ggc_pop_context ();
-
-  if (max_elements_made < n_elements_made)
-    max_elements_made = n_elements_made;
-
   /* Clean up.  */
   end_alias_analysis ();
-  free (uid_cuid);
   free (reg_eqv_table);
-
-  return cse_jumps_altered || recorded_label_ref;
-}
-
-/* Process a single basic block.  FROM and TO and the limits of the basic
-   block.  NEXT_BRANCH points to the branch path when following jumps or
-   a null path when not following jumps.
-
-   AROUND_LOOP is non-zero if we are to try to cse around to the start of a
-   loop.  This is true when we are being called for the last time on a
-   block and this CSE pass is before loop.c.  */
-
-static rtx
-cse_basic_block (from, to, next_branch, around_loop)
-     rtx from, to;
-     struct branch_path *next_branch;
-     int around_loop;
-{
-  rtx insn;
-  int to_usage = 0;
-  rtx libcall_insn = NULL_RTX;
-  int num_insns = 0;
-
-  /* This array is undefined before max_reg, so only allocate
-     the space actually needed and adjust the start.  */
-
-  qty_table
-    = (struct qty_table_elem *) xmalloc ((max_qty - max_reg)
-                                        * sizeof (struct qty_table_elem));
-  qty_table -= max_reg;
-
-  new_basic_block ();
-
-  /* TO might be a label.  If so, protect it from being deleted.  */
-  if (to != 0 && GET_CODE (to) == CODE_LABEL)
-    ++LABEL_NUSES (to);
-
-  for (insn = from; insn != to; insn = NEXT_INSN (insn))
-    {
-      enum rtx_code code = GET_CODE (insn);
-
-      /* 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.
-
-        ??? This is a real kludge and needs to be done some other way.
-        Perhaps for 2.9.  */
-      if (code != NOTE && num_insns++ > 1000)
-       {
-         flush_hash_table ();
-         num_insns = 0;
-       }
-
-      /* See if this is a branch that is part of the path.  If so, and it is
-        to be taken, do so.  */
-      if (next_branch->branch == insn)
-       {
-         enum taken status = next_branch++->status;
-         if (status != NOT_TAKEN)
-           {
-             if (status == TAKEN)
-               record_jump_equiv (insn, 1);
-             else
-               invalidate_skipped_block (NEXT_INSN (insn));
-
-             /* Set the last insn as the jump insn; it doesn't affect cc0.
-                Then follow this branch.  */
-#ifdef HAVE_cc0
-             prev_insn_cc0 = 0;
-#endif
-             prev_insn = insn;
-             insn = JUMP_LABEL (insn);
-             continue;
-           }
-       }
-
-      if (GET_MODE (insn) == QImode)
-       PUT_MODE (insn, VOIDmode);
-
-      if (GET_RTX_CLASS (code) == 'i')
-       {
-         rtx p;
-
-         /* Process notes first so we have all notes in canonical forms when
-            looking for duplicate operations.  */
-
-         if (REG_NOTES (insn))
-           REG_NOTES (insn) = cse_process_notes (REG_NOTES (insn), NULL_RTX);
-
-         /* Track when we are inside in LIBCALL block.  Inside such a block,
-            we do not want to record destinations.  The last insn of a
-            LIBCALL block is not considered to be part of the block, since
-            its destination is the result of the block and hence should be
-            recorded.  */
-
-         if (REG_NOTES (insn) != 0)
-           {
-             if ((p = find_reg_note (insn, REG_LIBCALL, NULL_RTX)))
-               libcall_insn = XEXP (p, 0);
-             else if (find_reg_note (insn, REG_RETVAL, NULL_RTX))
-               libcall_insn = 0;
-           }
-
-         cse_insn (insn, libcall_insn);
-
-         /* If we haven't already found an insn where we added a LABEL_REF,
-            check this one.  */
-         if (GET_CODE (insn) == INSN && ! recorded_label_ref
-             && for_each_rtx (&PATTERN (insn), check_for_label_ref,
-                              (void *) insn))
-           recorded_label_ref = 1;
-       }
-
-      /* If INSN is now an unconditional jump, skip to the end of our
-        basic block by pretending that we just did the last insn in the
-        basic block.  If we are jumping to the end of our block, show
-        that we can have one usage of TO.  */
-
-      if (any_uncondjump_p (insn))
-       {
-         if (to == 0)
-           {
-             free (qty_table + max_reg);
-             return 0;
-           }
-
-         if (JUMP_LABEL (insn) == to)
-           to_usage = 1;
-
-         /* Maybe TO was deleted because the jump is unconditional.
-            If so, there is nothing left in this basic block.  */
-         /* ??? Perhaps it would be smarter to set TO
-            to whatever follows this insn,
-            and pretend the basic block had always ended here.  */
-         if (INSN_DELETED_P (to))
-           break;
-
-         insn = PREV_INSN (to);
-       }
-
-      /* See if it is ok to keep on going past the label
-        which used to end our basic block.  Remember that we incremented
-        the count of that label, so we decrement it here.  If we made
-        a jump unconditional, TO_USAGE will be one; in that case, we don't
-        want to count the use in that jump.  */
-
-      if (to != 0 && NEXT_INSN (insn) == to
-         && GET_CODE (to) == CODE_LABEL && --LABEL_NUSES (to) == to_usage)
-       {
-         struct cse_basic_block_data val;
-         rtx prev;
-
-         insn = NEXT_INSN (to);
-
-         /* If TO was the last insn in the function, we are done.  */
-         if (insn == 0)
-           {
-             free (qty_table + max_reg);
-             return 0;
-           }
-
-         /* If TO was preceded by a BARRIER we are done with this block
-            because it has no continuation.  */
-         prev = prev_nonnote_insn (to);
-         if (prev && GET_CODE (prev) == BARRIER)
-           {
-             free (qty_table + max_reg);
-             return insn;
-           }
-
-         /* Find the end of the following block.  Note that we won't be
-            following branches in this case.  */
-         to_usage = 0;
-         val.path_size = 0;
-         cse_end_of_basic_block (insn, &val, 0, 0, 0);
-
-         /* If the tables we allocated have enough space left
-            to handle all the SETs in the next basic block,
-            continue through it.  Otherwise, return,
-            and that block will be scanned individually.  */
-         if (val.nsets * 2 + next_qty > max_qty)
-           break;
-
-         cse_basic_block_start = val.low_cuid;
-         cse_basic_block_end = val.high_cuid;
-         to = val.last;
-
-         /* Prevent TO from being deleted if it is a label.  */
-         if (to != 0 && GET_CODE (to) == CODE_LABEL)
-           ++LABEL_NUSES (to);
-
-         /* Back up so we process the first insn in the extension.  */
-         insn = PREV_INSN (insn);
-       }
-    }
-
-  if (next_qty > max_qty)
-    abort ();
-
-  /* If we are running before loop.c, we stopped on a NOTE_INSN_LOOP_END, and
-     the previous insn is the only insn that branches to the head of a loop,
-     we can cse into the loop.  Don't do this if we changed the jump
-     structure of a loop unless we aren't going to be following jumps.  */
-
-  insn = prev_nonnote_insn(to);
-  if ((cse_jumps_altered == 0
-       || (flag_cse_follow_jumps == 0 && flag_cse_skip_blocks == 0))
-      && around_loop && to != 0
-      && GET_CODE (to) == NOTE && NOTE_LINE_NUMBER (to) == NOTE_INSN_LOOP_END
-      && GET_CODE (insn) == JUMP_INSN
-      && JUMP_LABEL (insn) != 0
-      && LABEL_NUSES (JUMP_LABEL (insn)) == 1)
-    cse_around_loop (JUMP_LABEL (insn));
-
-  free (qty_table + max_reg);
-
-  return to ? NEXT_INSN (to) : 0;
+  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;
 }
 \f
-/* Called via for_each_rtx to see if an insn is using a LABEL_REF for which
-   there isn't a REG_LABEL note.  Return one if so.  DATA is the insn.  */
+/* 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 (rtl, data)
-     rtx *rtl;
-     void *data;
+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 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 will be added.  */
+  /* 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, XEXP (*rtl, 0)));
+         && ! find_reg_note (insn, REG_LABEL_OPERAND, XEXP (*rtl, 0)));
 }
 \f
 /* Count the number of times registers are used (not set) in X.
@@ -7425,16 +6265,15 @@ check_for_label_ref (rtl, data)
 
    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.  */
+   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 (x, counts, dest, incr)
-     rtx x;
-     int *counts;
-     rtx dest;
-     int incr;
+count_reg_usage (rtx x, int *counts, rtx dest, int incr)
 {
   enum rtx_code code;
+  rtx note;
   const char *fmt;
   int i, j;
 
@@ -7453,6 +6292,7 @@ count_reg_usage (x, counts, dest, incr)
     case CONST:
     case CONST_INT:
     case CONST_DOUBLE:
+    case CONST_FIXED:
     case CONST_VECTOR:
     case SYMBOL_REF:
     case LABEL_REF:
@@ -7461,48 +6301,76 @@ count_reg_usage (x, counts, dest, incr)
     case CLOBBER:
       /* If we are clobbering a MEM, mark any registers inside the address
          as being used.  */
-      if (GET_CODE (XEXP (x, 0)) == MEM)
+      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 (GET_CODE (SET_DEST (x)) != REG)
+      if (!REG_P (SET_DEST (x)))
        count_reg_usage (SET_DEST (x), counts, NULL_RTX, incr);
-
-      /* If SRC has side-effects, then we can't delete this insn, so the
-        usage of SET_DEST inside SRC counts.
-
-        ??? Strictly-speaking, we might be preserving this insn
-        because some other SET has side-effects, but that's hard
-        to do and can't happen now.  */
       count_reg_usage (SET_SRC (x), counts,
-                      side_effects_p (SET_SRC (x)) ? NULL_RTX : SET_DEST (x),
+                      dest ? dest : SET_DEST (x),
                       incr);
       return;
 
     case CALL_INSN:
-      count_reg_usage (CALL_INSN_FUNCTION_USAGE (x), counts, NULL_RTX, incr);
-      /* Fall through.  */
-
     case INSN:
     case JUMP_INSN:
-      count_reg_usage (PATTERN (x), counts, NULL_RTX, incr);
+    /* 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.  */
 
-      count_reg_usage (REG_NOTES (x), counts, NULL_RTX, incr);
+      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:
-    case INSN_LIST:
       if (REG_NOTE_KIND (x) == REG_EQUAL
-         || (REG_NOTE_KIND (x) != REG_NONNEG && GET_CODE (XEXP (x,0)) == USE))
+         || (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;
     }
@@ -7520,10 +6388,8 @@ count_reg_usage (x, counts, dest, incr)
 \f
 /* Return true if set is live.  */
 static bool
-set_live_p (set, insn, counts)
-     rtx set;
-     rtx insn ATTRIBUTE_UNUSED;        /* Only used with HAVE_cc0.  */
-     int *counts;
+set_live_p (rtx set, rtx insn ATTRIBUTE_UNUSED, /* Only used with HAVE_cc0.  */
+           int *counts)
 {
 #ifdef HAVE_cc0
   rtx tem;
@@ -7540,15 +6406,10 @@ set_live_p (set, insn, counts)
               || !reg_referenced_p (cc0_rtx, PATTERN (tem))))
     return false;
 #endif
-  else if (GET_CODE (SET_DEST (set)) != REG
+  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))
-          /* An ADDRESSOF expression can turn into a use of the
-             internal arg pointer, so always consider the
-             internal arg pointer live.  If it is truly dead,
-             flow will delete the initializing insn.  */
-          || (SET_DEST (set) == current_function_internal_arg_pointer))
+          || side_effects_p (SET_SRC (set)))
     return true;
   return false;
 }
@@ -7556,12 +6417,12 @@ set_live_p (set, insn, counts)
 /* Return true if insn is live.  */
 
 static bool
-insn_live_p (insn, counts)
-     rtx insn;
-     int *counts;
+insn_live_p (rtx insn, int *counts)
 {
   int i;
-  if (GET_CODE (PATTERN (insn)) == SET)
+  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)
     {
@@ -7583,36 +6444,6 @@ insn_live_p (insn, counts)
     return true;
 }
 
-/* Return true if libcall is dead as a whole.  */
-
-static bool
-dead_libcall_p (insn)
-     rtx insn;
-{
-  rtx note;
-  /* See if there's a REG_EQUAL note on this insn and try to
-     replace the source with the REG_EQUAL expression.
-
-     We assume that insns with REG_RETVALs can only be reg->reg
-     copies at this point.  */
-  note = find_reg_note (insn, REG_EQUAL, NULL_RTX);
-  if (note)
-    {
-      rtx set = single_set (insn);
-      rtx new = simplify_rtx (XEXP (note, 0));
-
-      if (!new)
-       new = XEXP (note, 0);
-
-      if (set && validate_change (insn, &SET_SRC (set), new, 0))
-       {
-         remove_note (insn, find_reg_note (insn, REG_RETVAL, NULL_RTX));
-         return true;
-       }
-    }
-  return false;
-}
-
 /* 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.
 
@@ -7621,22 +6452,19 @@ dead_libcall_p (insn)
    move dead invariants out of loops or make givs for dead quantities.  The
    remaining passes of the compilation are also sped up.  */
 
-void
-delete_trivially_dead_insns (insns, nreg, preserve_basic_blocks)
-     rtx insns;
-     int nreg;
-     int preserve_basic_blocks;
+int
+delete_trivially_dead_insns (rtx insns, int nreg)
 {
   int *counts;
   rtx insn, prev;
-  int i;
-  int in_libcall = 0, dead_libcall = 0;
-  basic_block bb;
+  int ndead = 0;
 
+  timevar_push (TV_DELETE_TRIVIALLY_DEAD);
   /* First count the number of times each register is used.  */
-  counts = (int *) xcalloc (nreg, sizeof (int));
-  for (insn = next_real_insn (insns); insn; insn = next_real_insn (insn))
-    count_reg_usage (insn, counts, NULL_RTX, 1);
+  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
@@ -7645,90 +6473,528 @@ delete_trivially_dead_insns (insns, nreg, preserve_basic_blocks)
      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.  */
-  insn = get_last_insn ();
-  if (! INSN_P (insn))
-    insn = prev_real_insn (insn);
+  for (insn = get_last_insn (); insn; insn = prev)
+    {
+      int live_insn = 0;
 
-  if (!preserve_basic_blocks)
-    for (; insn; insn = prev)
-      {
-       int live_insn = 0;
+      prev = PREV_INSN (insn);
+      if (!INSN_P (insn))
+       continue;
 
-       prev = prev_real_insn (insn);
+      live_insn = insn_live_p (insn, counts);
 
-       /* Don't delete any insns that are part of a libcall block unless
-          we can delete the whole libcall block.
+      /* If this is a dead insn, delete it and show registers in it aren't
+        being used.  */
 
-          Flow or loop might get confused if we did that.  Remember
-          that we are scanning backwards.  */
-       if (find_reg_note (insn, REG_RETVAL, NULL_RTX))
-         {
-           in_libcall = 1;
-           live_insn = 1;
-           dead_libcall = dead_libcall_p (insn);
-         }
-       else if (in_libcall)
-         live_insn = ! dead_libcall;
-       else
-         live_insn = insn_live_p (insn, counts);
+      if (! live_insn && dbg_cnt (delete_trivial_dead))
+       {
+         count_reg_usage (insn, counts, NULL_RTX, -1);
+         delete_insn_and_edges (insn);
+         ndead++;
+       }
+    }
 
-       /* If this is a dead insn, delete it and show registers in it aren't
-          being used.  */
+  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;
+}
 
-       if (! live_insn)
-         {
-           count_reg_usage (insn, counts, NULL_RTX, -1);
-           delete_related_insns (insn);
-         }
+/* 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.  */
 
-       if (find_reg_note (insn, REG_LIBCALL, NULL_RTX))
-         {
-           in_libcall = 0;
-           dead_libcall = 0;
-         }
-      }
-  else
-    for (i = 0; i < n_basic_blocks; i++)
-      for (bb = BASIC_BLOCK (i), insn = bb->end; insn != bb->head; insn = prev)
+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))
        {
-         int live_insn = 0;
+         rtx set;
 
-         prev = PREV_INSN (insn);
-         if (!INSN_P (insn))
+         if (! INSN_P (insn))
            continue;
 
-         /* Don't delete any insns that are part of a libcall block unless
-            we can delete the whole libcall block.
+         /* If CC_SRC is modified, we have to stop looking for
+            something which uses it.  */
+         if (modified_in_p (cc_src, insn))
+           break;
 
-            Flow or loop might get confused if we did that.  Remember
-            that we are scanning backwards.  */
-         if (find_reg_note (insn, REG_RETVAL, NULL_RTX))
+         /* 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))
            {
-             in_libcall = 1;
-             live_insn = 1;
-             dead_libcall = dead_libcall_p (insn);
+             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;
            }
-         else if (in_libcall)
-           live_insn = ! dead_libcall;
-         else
-           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 INSN sets CC_REG in some other way, don't look any
+            farther.  */
+         if (reg_set_p (cc_reg, insn))
+           break;
+       }
 
-         if (! live_insn)
+      /* 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)
            {
-             count_reg_usage (insn, counts, NULL_RTX, -1);
-             delete_insn (insn);
+             gcc_assert (submode == mode);
+             found_equiv = true;
+             can_change_mode = false;
            }
+       }
+    }
+
+  if (! found_equiv)
+    return VOIDmode;
 
-         if (find_reg_note (insn, REG_LIBCALL, NULL_RTX))
+  /* 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)
            {
-             in_libcall = 0;
-             dead_libcall = 0;
+             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);
        }
 
-  /* Clean up.  */
-  free (counts);
+      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);
+           }
+       }
+    }
+}
+\f
+
+/* 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 */
+ }
+};
+