OSDN Git Service

fix
[pf3gnuchains/gcc-fork.git] / gcc / gcse.c
index 27dcc71..22f19bb 100644 (file)
@@ -1,6 +1,6 @@
 /* Global common subexpression elimination/Partial redundancy elimination
    and global constant/copy propagation for GNU compiler.
-   Copyright (C) 1997, 1998, 1999, 2000, 2001, 2002, 2003
+   Copyright (C) 1997, 1998, 1999, 2000, 2001, 2002, 2003, 2004
    Free Software Foundation, Inc.
 
 This file is part of GCC.
@@ -150,6 +150,7 @@ Software Foundation, 59 Temple Place - Suite 330, Boston, MA
 #include "toplev.h"
 
 #include "rtl.h"
+#include "tree.h"
 #include "tm_p.h"
 #include "regs.h"
 #include "hard-reg-set.h"
@@ -165,7 +166,7 @@ Software Foundation, 59 Temple Place - Suite 330, Boston, MA
 #include "ggc.h"
 #include "params.h"
 #include "cselib.h"
-
+#include "intl.h"
 #include "obstack.h"
 
 /* Propagate flow information through back edges and thus enable PRE's
@@ -191,7 +192,8 @@ Software Foundation, 59 Temple Place - Suite 330, Boston, MA
 
    3) Perform copy/constant propagation.
 
-   4) Perform global cse.
+   4) Perform global cse using lazy code motion if not optimizing
+      for size, or code hoisting if we are.
 
    5) Perform another pass of copy/constant propagation.
 
@@ -230,8 +232,8 @@ Software Foundation, 59 Temple Place - Suite 330, Boston, MA
    substitutions.
 
    PRE is quite expensive in complicated functions because the DFA can take
-   awhile to converge.  Hence we only perform one pass.  The parameter max-gcse-passes can
-   be modified if one wants to experiment.
+   a while to converge.  Hence we only perform one pass.  The parameter
+   max-gcse-passes can be modified if one wants to experiment.
 
    **********************
 
@@ -286,7 +288,6 @@ static FILE *gcse_file;
     * If we changed any jumps via cprop.
 
     * If we added any labels via edge splitting.  */
-
 static int run_jump_opt_after_gcse;
 
 /* Bitmaps are normally not included in debugging dumps.
@@ -299,14 +300,6 @@ static FILE *debug_stderr;
 /* An obstack for our working variables.  */
 static struct obstack gcse_obstack;
 
-/* Nonzero for each mode that supports (set (reg) (reg)).
-   This is trivially true for integer and floating point values.
-   It may or may not be true for condition codes.  */
-static char can_copy_p[(int) NUM_MACHINE_MODES];
-
-/* Nonzero if can_copy_p has been initialized.  */
-static int can_copy_init_p;
-
 struct reg_use {rtx reg_rtx; };
 
 /* Hash table of expressions.  */
@@ -470,12 +463,13 @@ struct ls_expr
 {
   struct expr * expr;          /* Gcse expression reference for LM.  */
   rtx pattern;                 /* Pattern of this mem.  */
+  rtx pattern_regs;            /* List of registers mentioned by the mem.  */
   rtx loads;                   /* INSN list of loads seen.  */
   rtx stores;                  /* INSN list of stores seen.  */
   struct ls_expr * next;       /* Next in the list.  */
   int invalid;                 /* Invalid for some reason.  */
   int index;                   /* If it maps to a bitmap index.  */
-  int hash_index;              /* Index when in a hash table.  */
+  unsigned int hash_index;     /* Index when in a hash table.  */
   rtx reaching_reg;            /* Register to use when re-writing.  */
 };
 
@@ -491,7 +485,7 @@ static struct ls_expr * pre_ldst_mems = NULL;
 static regset reg_set_bitmap;
 
 /* For each block, a bitmap of registers set in the block.
-   This is used by expr_killed_p and compute_transp.
+   This is used by compute_transp.
    It is computed during hash table computation and not by compute_sets
    as it includes registers added since the last pass (or between cprop and
    gcse) and it's currently not easy to realloc sbitmap vectors.  */
@@ -521,25 +515,8 @@ static int const_prop_count;
 /* Number of copys propagated.  */
 static int copy_prop_count;
 \f
-/* These variables are used by classic GCSE.
-   Normally they'd be defined a bit later, but `rd_gen' needs to
-   be declared sooner.  */
-
-/* Each block has a bitmap of each type.
-   The length of each blocks bitmap is:
-
-       max_cuid  - for reaching definitions
-       n_exprs - for available expressions
-
-   Thus we view the bitmaps as 2 dimensional arrays.  i.e.
-   rd_kill[block_num][cuid_num]
-   ae_kill[block_num][expr_num]                         */
-
-/* For reaching defs */
-static sbitmap *rd_kill, *rd_gen, *reaching_defs, *rd_out;
-
-/* for available exprs */
-static sbitmap *ae_kill, *ae_gen, *ae_in, *ae_out;
+/* For available exprs */
+static sbitmap *ae_kill, *ae_gen;
 
 /* Objects of this type are passed around by the null-pointer check
    removal routines.  */
@@ -555,169 +532,149 @@ struct null_pointer_info
   sbitmap *nonnull_killed;
 };
 \f
-static void compute_can_copy   PARAMS ((void));
-static char *gmalloc           PARAMS ((unsigned int));
-static char *grealloc          PARAMS ((char *, unsigned int));
-static char *gcse_alloc                PARAMS ((unsigned long));
-static void alloc_gcse_mem     PARAMS ((rtx));
-static void free_gcse_mem      PARAMS ((void));
-static void alloc_reg_set_mem  PARAMS ((int));
-static void free_reg_set_mem   PARAMS ((void));
-static int get_bitmap_width     PARAMS ((int, int, int));
-static void record_one_set     PARAMS ((int, rtx));
-static void record_set_info    PARAMS ((rtx, rtx, void *));
-static void compute_sets       PARAMS ((rtx));
-static void hash_scan_insn     PARAMS ((rtx, struct hash_table *, int));
-static void hash_scan_set      PARAMS ((rtx, rtx, struct hash_table *));
-static void hash_scan_clobber  PARAMS ((rtx, rtx, struct hash_table *));
-static void hash_scan_call     PARAMS ((rtx, rtx, struct hash_table *));
-static int want_to_gcse_p      PARAMS ((rtx));
-static int oprs_unchanged_p    PARAMS ((rtx, rtx, int));
-static int oprs_anticipatable_p PARAMS ((rtx, rtx));
-static int oprs_available_p    PARAMS ((rtx, rtx));
-static void insert_expr_in_table PARAMS ((rtx, enum machine_mode, rtx,
-                                         int, int, struct hash_table *));
-static void insert_set_in_table PARAMS ((rtx, rtx, struct hash_table *));
-static unsigned int hash_expr  PARAMS ((rtx, enum machine_mode, int *, int));
-static unsigned int hash_expr_1 PARAMS ((rtx, enum machine_mode, int *));
-static unsigned int hash_string_1 PARAMS ((const char *));
-static unsigned int hash_set   PARAMS ((int, int));
-static int expr_equiv_p                PARAMS ((rtx, rtx));
-static void record_last_reg_set_info PARAMS ((rtx, int));
-static void record_last_mem_set_info PARAMS ((rtx));
-static void record_last_set_info PARAMS ((rtx, rtx, void *));
-static void compute_hash_table PARAMS ((struct hash_table *));
-static void alloc_hash_table PARAMS ((int, struct hash_table *, int));
-static void free_hash_table PARAMS ((struct hash_table *));
-static void compute_hash_table_work PARAMS ((struct hash_table *));
-static void dump_hash_table    PARAMS ((FILE *, const char *,
-                                       struct hash_table *));
-static struct expr *lookup_expr        PARAMS ((rtx, struct hash_table *));
-static struct expr *lookup_set PARAMS ((unsigned int, struct hash_table *));
-static struct expr *next_set   PARAMS ((unsigned int, struct expr *));
-static void reset_opr_set_tables PARAMS ((void));
-static int oprs_not_set_p      PARAMS ((rtx, rtx));
-static void mark_call          PARAMS ((rtx));
-static void mark_set           PARAMS ((rtx, rtx));
-static void mark_clobber       PARAMS ((rtx, rtx));
-static void mark_oprs_set      PARAMS ((rtx));
-static void alloc_cprop_mem    PARAMS ((int, int));
-static void free_cprop_mem     PARAMS ((void));
-static void compute_transp     PARAMS ((rtx, int, sbitmap *, int));
-static void compute_transpout  PARAMS ((void));
-static void compute_local_properties PARAMS ((sbitmap *, sbitmap *, sbitmap *,
-                                             struct hash_table *));
-static void compute_cprop_data PARAMS ((void));
-static void find_used_regs     PARAMS ((rtx *, void *));
-static int try_replace_reg     PARAMS ((rtx, rtx, rtx));
-static struct expr *find_avail_set PARAMS ((int, rtx));
-static int cprop_jump          PARAMS ((basic_block, rtx, rtx, rtx, rtx));
-static void mems_conflict_for_gcse_p PARAMS ((rtx, rtx, void *));
-static int load_killed_in_block_p    PARAMS ((basic_block, int, rtx, int));
-static void canon_list_insert        PARAMS ((rtx, rtx, void *));
-static int cprop_insn          PARAMS ((rtx, int));
-static int cprop               PARAMS ((int));
-static rtx fis_get_condition   PARAMS ((rtx));
-static void find_implicit_sets PARAMS ((void));
-static int one_cprop_pass      PARAMS ((int, int, int));
-static bool constprop_register PARAMS ((rtx, rtx, rtx, int));
-static struct expr *find_bypass_set PARAMS ((int, int));
-static int bypass_block                    PARAMS ((basic_block, rtx, rtx));
-static int bypass_conditional_jumps PARAMS ((void));
-static void alloc_pre_mem      PARAMS ((int, int));
-static void free_pre_mem       PARAMS ((void));
-static void compute_pre_data   PARAMS ((void));
-static int pre_expr_reaches_here_p PARAMS ((basic_block, struct expr *,
-                                           basic_block));
-static void insert_insn_end_bb PARAMS ((struct expr *, basic_block, int));
-static void pre_insert_copy_insn PARAMS ((struct expr *, rtx));
-static void pre_insert_copies  PARAMS ((void));
-static int pre_delete          PARAMS ((void));
-static int pre_gcse            PARAMS ((void));
-static int one_pre_gcse_pass   PARAMS ((int));
-static void add_label_notes    PARAMS ((rtx, rtx));
-static void alloc_code_hoist_mem PARAMS ((int, int));
-static void free_code_hoist_mem        PARAMS ((void));
-static void compute_code_hoist_vbeinout        PARAMS ((void));
-static void compute_code_hoist_data PARAMS ((void));
-static int hoist_expr_reaches_here_p PARAMS ((basic_block, int, basic_block,
-                                             char *));
-static void hoist_code         PARAMS ((void));
-static int one_code_hoisting_pass PARAMS ((void));
-static void alloc_rd_mem       PARAMS ((int, int));
-static void free_rd_mem                PARAMS ((void));
-static void handle_rd_kill_set PARAMS ((rtx, int, basic_block));
-static void compute_kill_rd    PARAMS ((void));
-static void compute_rd         PARAMS ((void));
-static void alloc_avail_expr_mem PARAMS ((int, int));
-static void free_avail_expr_mem PARAMS ((void));
-static void compute_ae_gen     PARAMS ((struct hash_table *));
-static int expr_killed_p       PARAMS ((rtx, basic_block));
-static void compute_ae_kill    PARAMS ((sbitmap *, sbitmap *, struct hash_table *));
-static int expr_reaches_here_p PARAMS ((struct occr *, struct expr *,
-                                        basic_block, int));
-static rtx computing_insn      PARAMS ((struct expr *, rtx));
-static int def_reaches_here_p  PARAMS ((rtx, rtx));
-static int can_disregard_other_sets PARAMS ((struct reg_set **, rtx, int));
-static int handle_avail_expr   PARAMS ((rtx, struct expr *));
-static int classic_gcse                PARAMS ((void));
-static int one_classic_gcse_pass PARAMS ((int));
-static void invalidate_nonnull_info PARAMS ((rtx, rtx, void *));
-static int delete_null_pointer_checks_1 PARAMS ((unsigned int *,
-                                                 sbitmap *, sbitmap *,
-                                                 struct null_pointer_info *));
-static rtx process_insert_insn PARAMS ((struct expr *));
-static int pre_edge_insert     PARAMS ((struct edge_list *, struct expr **));
-static int expr_reaches_here_p_work PARAMS ((struct occr *, struct expr *,
-                                            basic_block, int, char *));
-static int pre_expr_reaches_here_p_work        PARAMS ((basic_block, struct expr *,
-                                                basic_block, char *));
-static struct ls_expr * ldst_entry     PARAMS ((rtx));
-static void free_ldst_entry            PARAMS ((struct ls_expr *));
-static void free_ldst_mems             PARAMS ((void));
-static void print_ldst_list            PARAMS ((FILE *));
-static struct ls_expr * find_rtx_in_ldst PARAMS ((rtx));
-static int enumerate_ldsts             PARAMS ((void));
-static inline struct ls_expr * first_ls_expr PARAMS ((void));
-static inline struct ls_expr * next_ls_expr  PARAMS ((struct ls_expr *));
-static int simple_mem                  PARAMS ((rtx));
-static void invalidate_any_buried_refs PARAMS ((rtx));
-static void compute_ld_motion_mems     PARAMS ((void));
-static void trim_ld_motion_mems                PARAMS ((void));
-static void update_ld_motion_stores    PARAMS ((struct expr *));
-static void reg_set_info               PARAMS ((rtx, rtx, void *));
-static int store_ops_ok                        PARAMS ((rtx, basic_block));
-static void find_moveable_store                PARAMS ((rtx));
-static int compute_store_table         PARAMS ((void));
-static int load_kills_store            PARAMS ((rtx, rtx));
-static int find_loads                  PARAMS ((rtx, rtx));
-static int store_killed_in_insn                PARAMS ((rtx, rtx));
-static int store_killed_after          PARAMS ((rtx, rtx, basic_block));
-static int store_killed_before         PARAMS ((rtx, rtx, basic_block));
-static void build_store_vectors                PARAMS ((void));
-static void insert_insn_start_bb       PARAMS ((rtx, basic_block));
-static int insert_store                        PARAMS ((struct ls_expr *, edge));
-static void replace_store_insn         PARAMS ((rtx, rtx, basic_block));
-static void delete_store               PARAMS ((struct ls_expr *,
-                                                basic_block));
-static void free_store_memory          PARAMS ((void));
-static void store_motion               PARAMS ((void));
-static void free_insn_expr_list_list   PARAMS ((rtx *));
-static void clear_modify_mem_tables    PARAMS ((void));
-static void free_modify_mem_tables     PARAMS ((void));
-static rtx gcse_emit_move_after                PARAMS ((rtx, rtx, rtx));
-static void local_cprop_find_used_regs PARAMS ((rtx *, void *));
-static bool do_local_cprop             PARAMS ((rtx, rtx, int, rtx*));
-static bool adjust_libcall_notes       PARAMS ((rtx, rtx, rtx, rtx*));
-static void local_cprop_pass           PARAMS ((int));
+static void compute_can_copy (void);
+static void *gmalloc (size_t) ATTRIBUTE_MALLOC;
+static void *gcalloc (size_t, size_t) ATTRIBUTE_MALLOC;
+static void *grealloc (void *, size_t);
+static void *gcse_alloc (unsigned long);
+static void alloc_gcse_mem (rtx);
+static void free_gcse_mem (void);
+static void alloc_reg_set_mem (int);
+static void free_reg_set_mem (void);
+static void record_one_set (int, rtx);
+static void replace_one_set (int, rtx, rtx);
+static void record_set_info (rtx, rtx, void *);
+static void compute_sets (rtx);
+static void hash_scan_insn (rtx, struct hash_table *, int);
+static void hash_scan_set (rtx, rtx, struct hash_table *);
+static void hash_scan_clobber (rtx, rtx, struct hash_table *);
+static void hash_scan_call (rtx, rtx, struct hash_table *);
+static int want_to_gcse_p (rtx);
+static bool can_assign_to_reg_p (rtx);
+static bool gcse_constant_p (rtx);
+static int oprs_unchanged_p (rtx, rtx, int);
+static int oprs_anticipatable_p (rtx, rtx);
+static int oprs_available_p (rtx, rtx);
+static void insert_expr_in_table (rtx, enum machine_mode, rtx, int, int,
+                                 struct hash_table *);
+static void insert_set_in_table (rtx, rtx, struct hash_table *);
+static unsigned int hash_expr (rtx, enum machine_mode, int *, int);
+static unsigned int hash_expr_1 (rtx, enum machine_mode, int *);
+static unsigned int hash_string_1 (const char *);
+static unsigned int hash_set (int, int);
+static int expr_equiv_p (rtx, rtx);
+static void record_last_reg_set_info (rtx, int);
+static void record_last_mem_set_info (rtx);
+static void record_last_set_info (rtx, rtx, void *);
+static void compute_hash_table (struct hash_table *);
+static void alloc_hash_table (int, struct hash_table *, int);
+static void free_hash_table (struct hash_table *);
+static void compute_hash_table_work (struct hash_table *);
+static void dump_hash_table (FILE *, const char *, struct hash_table *);
+static struct expr *lookup_expr (rtx, struct hash_table *);
+static struct expr *lookup_set (unsigned int, struct hash_table *);
+static struct expr *next_set (unsigned int, struct expr *);
+static void reset_opr_set_tables (void);
+static int oprs_not_set_p (rtx, rtx);
+static void mark_call (rtx);
+static void mark_set (rtx, rtx);
+static void mark_clobber (rtx, rtx);
+static void mark_oprs_set (rtx);
+static void alloc_cprop_mem (int, int);
+static void free_cprop_mem (void);
+static void compute_transp (rtx, int, sbitmap *, int);
+static void compute_transpout (void);
+static void compute_local_properties (sbitmap *, sbitmap *, sbitmap *,
+                                     struct hash_table *);
+static void compute_cprop_data (void);
+static void find_used_regs (rtx *, void *);
+static int try_replace_reg (rtx, rtx, rtx);
+static struct expr *find_avail_set (int, rtx);
+static int cprop_jump (basic_block, rtx, rtx, rtx, rtx);
+static void mems_conflict_for_gcse_p (rtx, rtx, void *);
+static int load_killed_in_block_p (basic_block, int, rtx, int);
+static void canon_list_insert (rtx, rtx, void *);
+static int cprop_insn (rtx, int);
+static int cprop (int);
+static void find_implicit_sets (void);
+static int one_cprop_pass (int, int, int);
+static bool constprop_register (rtx, rtx, rtx, int);
+static struct expr *find_bypass_set (int, int);
+static bool reg_killed_on_edge (rtx, edge);
+static int bypass_block (basic_block, rtx, rtx);
+static int bypass_conditional_jumps (void);
+static void alloc_pre_mem (int, int);
+static void free_pre_mem (void);
+static void compute_pre_data (void);
+static int pre_expr_reaches_here_p (basic_block, struct expr *,
+                                   basic_block);
+static void insert_insn_end_bb (struct expr *, basic_block, int);
+static void pre_insert_copy_insn (struct expr *, rtx);
+static void pre_insert_copies (void);
+static int pre_delete (void);
+static int pre_gcse (void);
+static int one_pre_gcse_pass (int);
+static void add_label_notes (rtx, rtx);
+static void alloc_code_hoist_mem (int, int);
+static void free_code_hoist_mem (void);
+static void compute_code_hoist_vbeinout (void);
+static void compute_code_hoist_data (void);
+static int hoist_expr_reaches_here_p (basic_block, int, basic_block, char *);
+static void hoist_code (void);
+static int one_code_hoisting_pass (void);
+static rtx process_insert_insn (struct expr *);
+static int pre_edge_insert (struct edge_list *, struct expr **);
+static int pre_expr_reaches_here_p_work (basic_block, struct expr *,
+                                        basic_block, char *);
+static struct ls_expr * ldst_entry (rtx);
+static void free_ldst_entry (struct ls_expr *);
+static void free_ldst_mems (void);
+static void print_ldst_list (FILE *);
+static struct ls_expr * find_rtx_in_ldst (rtx);
+static int enumerate_ldsts (void);
+static inline struct ls_expr * first_ls_expr (void);
+static inline struct ls_expr * next_ls_expr (struct ls_expr *);
+static int simple_mem (rtx);
+static void invalidate_any_buried_refs (rtx);
+static void compute_ld_motion_mems (void);
+static void trim_ld_motion_mems (void);
+static void update_ld_motion_stores (struct expr *);
+static void reg_set_info (rtx, rtx, void *);
+static void reg_clear_last_set (rtx, rtx, void *);
+static bool store_ops_ok (rtx, int *);
+static rtx extract_mentioned_regs (rtx);
+static rtx extract_mentioned_regs_helper (rtx, rtx);
+static void find_moveable_store (rtx, int *, int *);
+static int compute_store_table (void);
+static bool load_kills_store (rtx, rtx, int);
+static bool find_loads (rtx, rtx, int);
+static bool store_killed_in_insn (rtx, rtx, rtx, int);
+static bool store_killed_after (rtx, rtx, rtx, basic_block, int *, rtx *);
+static bool store_killed_before (rtx, rtx, rtx, basic_block, int *);
+static void build_store_vectors (void);
+static void insert_insn_start_bb (rtx, basic_block);
+static int insert_store (struct ls_expr *, edge);
+static void remove_reachable_equiv_notes (basic_block, struct ls_expr *);
+static void replace_store_insn (rtx, rtx, basic_block, struct ls_expr *);
+static void delete_store (struct ls_expr *, basic_block);
+static void free_store_memory (void);
+static void store_motion (void);
+static void free_insn_expr_list_list (rtx *);
+static void clear_modify_mem_tables (void);
+static void free_modify_mem_tables (void);
+static rtx gcse_emit_move_after (rtx, rtx, rtx);
+static void local_cprop_find_used_regs (rtx *, void *);
+static bool do_local_cprop (rtx, rtx, int, rtx*);
+static bool adjust_libcall_notes (rtx, rtx, rtx, rtx*);
+static void local_cprop_pass (int);
+static bool is_too_expensive (const char *);
 \f
+
 /* Entry point for global common subexpression elimination.
    F is the first instruction in the function.  */
 
 int
-gcse_main (f, file)
-     rtx f;
-     FILE *file;
+gcse_main (rtx f, FILE *file)
 {
   int changed, pass;
   /* Bytes used at start of pass.  */
@@ -746,46 +703,10 @@ gcse_main (f, file)
   if (file)
     dump_flow_info (file);
 
-  /* Return if there's nothing to do.  */
-  if (n_basic_blocks <= 1)
+  /* Return if there's nothing to do, or it is too expensive.  */
+  if (n_basic_blocks <= 1 || is_too_expensive (_("GCSE disabled")))
     return 0;
 
-  /* Trying to perform global optimizations on flow graphs which have
-     a high connectivity will take a long time and is unlikely to be
-     particularly useful.
-
-     In normal circumstances a cfg should have about twice as many edges
-     as blocks.  But we do not want to punish small functions which have
-     a couple switch statements.  So we require a relatively large number
-     of basic blocks and the ratio of edges to blocks to be high.  */
-  if (n_basic_blocks > 1000 && n_edges / n_basic_blocks >= 20)
-    {
-      if (warn_disabled_optimization)
-       warning ("GCSE disabled: %d > 1000 basic blocks and %d >= 20 edges/basic block",
-                n_basic_blocks, n_edges / n_basic_blocks);
-      return 0;
-    }
-
-  /* If allocating memory for the cprop bitmap would take up too much
-     storage it's better just to disable the optimization.  */
-  if ((n_basic_blocks
-       * SBITMAP_SET_SIZE (max_gcse_regno)
-       * sizeof (SBITMAP_ELT_TYPE)) > MAX_GCSE_MEMORY)
-    {
-      if (warn_disabled_optimization)
-       warning ("GCSE disabled: %d basic blocks and %d registers",
-                n_basic_blocks, max_gcse_regno);
-
-      return 0;
-    }
-
-  /* See what modes support reg/reg copy operations.  */
-  if (! can_copy_init_p)
-    {
-      compute_can_copy ();
-      can_copy_init_p = 1;
-    }
-
   gcc_obstack_init (&gcse_obstack);
   bytes_used = 0;
 
@@ -828,7 +749,7 @@ gcse_main (f, file)
       changed = one_cprop_pass (pass + 1, 0, 0);
 
       if (optimize_size)
-       changed |= one_classic_gcse_pass (pass + 1);
+       /* Do nothing.  */ ;
       else
        {
          changed |= one_pre_gcse_pass (pass + 1);
@@ -838,12 +759,8 @@ gcse_main (f, file)
          if (changed)
            {
              free_modify_mem_tables ();
-             modify_mem_list
-               = (rtx *) gmalloc (last_basic_block * sizeof (rtx));
-             canon_modify_mem_list
-               = (rtx *) gmalloc (last_basic_block * sizeof (rtx));
-             memset ((char *) modify_mem_list, 0, last_basic_block * sizeof (rtx));
-             memset ((char *) canon_modify_mem_list, 0, last_basic_block * sizeof (rtx));
+             modify_mem_list = gcalloc (last_basic_block, sizeof (rtx));
+             canon_modify_mem_list = gcalloc (last_basic_block, sizeof (rtx));
            }
          free_reg_set_mem ();
          alloc_reg_set_mem (max_reg_num ());
@@ -860,11 +777,10 @@ gcse_main (f, file)
         partial redundancy elimination.  */
       free_gcse_mem ();
 
-      /* It does not make sense to run code hoisting unless we optimizing
+      /* It does not make sense to run code hoisting unless we are optimizing
         for code size -- it rarely makes programs faster, and can make
         them bigger if we did partial redundancy elimination (when optimizing
-        for space, we use a classic gcse algorithm instead of partial
-        redundancy algorithms).  */
+        for space, we don't run the partial redundancy algorithms).  */
       if (optimize_size)
        {
          max_gcse_regno = max_reg_num ();
@@ -898,86 +814,113 @@ gcse_main (f, file)
   if (file)
     {
       fprintf (file, "GCSE of %s: %d basic blocks, ",
-              current_function_name, n_basic_blocks);
+              current_function_name (), n_basic_blocks);
       fprintf (file, "%d pass%s, %d bytes\n\n",
               pass, pass > 1 ? "es" : "", max_pass_bytes);
     }
 
   obstack_free (&gcse_obstack, NULL);
   free_reg_set_mem ();
+
   /* We are finished with alias.  */
   end_alias_analysis ();
   allocate_reg_info (max_reg_num (), FALSE, FALSE);
 
-  /* Store motion disabled until it is fixed.  */
-  if (0 && !optimize_size && flag_gcse_sm)
+  if (!optimize_size && flag_gcse_sm)
     store_motion ();
+
   /* Record where pseudo-registers are set.  */
   return run_jump_opt_after_gcse;
 }
 \f
 /* Misc. utilities.  */
 
+/* Nonzero for each mode that supports (set (reg) (reg)).
+   This is trivially true for integer and floating point values.
+   It may or may not be true for condition codes.  */
+static char can_copy[(int) NUM_MACHINE_MODES];
+
 /* Compute which modes support reg/reg copy operations.  */
 
 static void
-compute_can_copy ()
+compute_can_copy (void)
 {
   int i;
 #ifndef AVOID_CCMODE_COPIES
   rtx reg, insn;
 #endif
-  memset (can_copy_p, 0, NUM_MACHINE_MODES);
+  memset (can_copy, 0, NUM_MACHINE_MODES);
 
   start_sequence ();
   for (i = 0; i < NUM_MACHINE_MODES; i++)
     if (GET_MODE_CLASS (i) == MODE_CC)
       {
 #ifdef AVOID_CCMODE_COPIES
-       can_copy_p[i] = 0;
+       can_copy[i] = 0;
 #else
        reg = gen_rtx_REG ((enum machine_mode) i, LAST_VIRTUAL_REGISTER + 1);
        insn = emit_insn (gen_rtx_SET (VOIDmode, reg, reg));
        if (recog (PATTERN (insn), insn, NULL) >= 0)
-         can_copy_p[i] = 1;
+         can_copy[i] = 1;
 #endif
       }
     else
-      can_copy_p[i] = 1;
+      can_copy[i] = 1;
 
   end_sequence ();
 }
+
+/* Returns whether the mode supports reg/reg copy operations.  */
+
+bool
+can_copy_p (enum machine_mode mode)
+{
+  static bool can_copy_init_p = false;
+
+  if (! can_copy_init_p)
+    {
+      compute_can_copy ();
+      can_copy_init_p = true;
+    }
+
+  return can_copy[mode] != 0;
+}
 \f
 /* Cover function to xmalloc to record bytes allocated.  */
 
-static char *
-gmalloc (size)
-     unsigned int size;
+static void *
+gmalloc (size_t size)
 {
   bytes_used += size;
   return xmalloc (size);
 }
 
+/* Cover function to xcalloc to record bytes allocated.  */
+
+static void *
+gcalloc (size_t nelem, size_t elsize)
+{
+  bytes_used += nelem * elsize;
+  return xcalloc (nelem, elsize);
+}
+
 /* Cover function to xrealloc.
    We don't record the additional size since we don't know it.
    It won't affect memory usage stats much anyway.  */
 
-static char *
-grealloc (ptr, size)
-     char *ptr;
-     unsigned int size;
+static void *
+grealloc (void *ptr, size_t size)
 {
   return xrealloc (ptr, size);
 }
 
 /* Cover function to obstack_alloc.  */
 
-static char *
-gcse_alloc (size)
-     unsigned long size;
+static void *
+gcse_alloc (unsigned long size)
 {
   bytes_used += size;
-  return (char *) obstack_alloc (&gcse_obstack, size);
+  return obstack_alloc (&gcse_obstack, size);
 }
 
 /* Allocate memory for the cuid mapping array,
@@ -986,10 +929,9 @@ gcse_alloc (size)
    This is called at the start of each pass.  */
 
 static void
-alloc_gcse_mem (f)
-     rtx f;
+alloc_gcse_mem (rtx f)
 {
-  int i, n;
+  int i;
   rtx insn;
 
   /* Find the largest UID and create a mapping from UIDs to CUIDs.
@@ -997,9 +939,7 @@ alloc_gcse_mem (f)
      and only apply to real insns.  */
 
   max_uid = get_max_uid ();
-  n = (max_uid + 1) * sizeof (int);
-  uid_cuid = (int *) gmalloc (n);
-  memset ((char *) uid_cuid, 0, n);
+  uid_cuid = gcalloc (max_uid + 1, sizeof (int));
   for (insn = f, i = 0; insn; insn = NEXT_INSN (insn))
     {
       if (INSN_P (insn))
@@ -1011,9 +951,7 @@ alloc_gcse_mem (f)
   /* Create a table mapping cuids to insns.  */
 
   max_cuid = i;
-  n = (max_cuid + 1) * sizeof (rtx);
-  cuid_insn = (rtx *) gmalloc (n);
-  memset ((char *) cuid_insn, 0, n);
+  cuid_insn = gcalloc (max_cuid + 1, sizeof (rtx));
   for (insn = f, i = 0; insn; insn = NEXT_INSN (insn))
     if (INSN_P (insn))
       CUID_INSN (i++) = insn;
@@ -1022,14 +960,11 @@ alloc_gcse_mem (f)
   reg_set_bitmap = BITMAP_XMALLOC ();
 
   /* Allocate vars to track sets of regs, memory per block.  */
-  reg_set_in_block = (sbitmap *) sbitmap_vector_alloc (last_basic_block,
-                                                      max_gcse_regno);
+  reg_set_in_block = sbitmap_vector_alloc (last_basic_block, max_gcse_regno);
   /* Allocate array to keep a list of insns which modify memory in each
      basic block.  */
-  modify_mem_list = (rtx *) gmalloc (last_basic_block * sizeof (rtx));
-  canon_modify_mem_list = (rtx *) gmalloc (last_basic_block * sizeof (rtx));
-  memset ((char *) modify_mem_list, 0, last_basic_block * sizeof (rtx));
-  memset ((char *) canon_modify_mem_list, 0, last_basic_block * sizeof (rtx));
+  modify_mem_list = gcalloc (last_basic_block, sizeof (rtx));
+  canon_modify_mem_list = gcalloc (last_basic_block, sizeof (rtx));
   modify_mem_list_set = BITMAP_XMALLOC ();
   canon_modify_mem_list_set = BITMAP_XMALLOC ();
 }
@@ -1037,7 +972,7 @@ alloc_gcse_mem (f)
 /* Free memory allocated by alloc_gcse_mem.  */
 
 static void
-free_gcse_mem ()
+free_gcse_mem (void)
 {
   free (uid_cuid);
   free (cuid_insn);
@@ -1049,49 +984,6 @@ free_gcse_mem ()
   BITMAP_XFREE (modify_mem_list_set);
   BITMAP_XFREE (canon_modify_mem_list_set);
 }
-
-/* Many of the global optimization algorithms work by solving dataflow
-   equations for various expressions.  Initially, some local value is
-   computed for each expression in each block.  Then, the values across the
-   various blocks are combined (by following flow graph edges) to arrive at
-   global values.  Conceptually, each set of equations is independent.  We
-   may therefore solve all the equations in parallel, solve them one at a
-   time, or pick any intermediate approach.
-
-   When you're going to need N two-dimensional bitmaps, each X (say, the
-   number of blocks) by Y (say, the number of expressions), call this
-   function.  It's not important what X and Y represent; only that Y
-   correspond to the things that can be done in parallel.  This function will
-   return an appropriate chunking factor C; you should solve C sets of
-   equations in parallel.  By going through this function, we can easily
-   trade space against time; by solving fewer equations in parallel we use
-   less space.  */
-
-static int
-get_bitmap_width (n, x, y)
-     int n;
-     int x;
-     int y;
-{
-  /* It's not really worth figuring out *exactly* how much memory will
-     be used by a particular choice.  The important thing is to get
-     something approximately right.  */
-  size_t max_bitmap_memory = 10 * 1024 * 1024;
-
-  /* The number of bytes we'd use for a single column of minimum
-     width.  */
-  size_t column_size = n * x * sizeof (SBITMAP_ELT_TYPE);
-
-  /* Often, it's reasonable just to solve all the equations in
-     parallel.  */
-  if (column_size * SBITMAP_SET_SIZE (y) <= max_bitmap_memory)
-    return y;
-
-  /* Otherwise, pick the largest width we can, without going over the
-     limit.  */
-  return SBITMAP_ELT_BITS * ((max_bitmap_memory + column_size - 1)
-                            / column_size);
-}
 \f
 /* Compute the local properties of each recorded expression.
 
@@ -1121,11 +1013,8 @@ get_bitmap_width (n, x, y)
    ABSALTERED.  */
 
 static void
-compute_local_properties (transp, comp, antloc, table)
-     sbitmap *transp;
-     sbitmap *comp;
-     sbitmap *antloc;
-     struct hash_table *table;
+compute_local_properties (sbitmap *transp, sbitmap *comp, sbitmap *antloc,
+                         struct hash_table *table)
 {
   unsigned int i;
 
@@ -1197,32 +1086,43 @@ compute_local_properties (transp, comp, antloc, table)
 static struct obstack reg_set_obstack;
 
 static void
-alloc_reg_set_mem (n_regs)
-     int n_regs;
+alloc_reg_set_mem (int n_regs)
 {
-  unsigned int n;
-
   reg_set_table_size = n_regs + REG_SET_TABLE_SLOP;
-  n = reg_set_table_size * sizeof (struct reg_set *);
-  reg_set_table = (struct reg_set **) gmalloc (n);
-  memset ((char *) reg_set_table, 0, n);
+  reg_set_table = gcalloc (reg_set_table_size, sizeof (struct reg_set *));
 
   gcc_obstack_init (&reg_set_obstack);
 }
 
 static void
-free_reg_set_mem ()
+free_reg_set_mem (void)
 {
   free (reg_set_table);
   obstack_free (&reg_set_obstack, NULL);
 }
 
+/* An OLD_INSN that used to set REGNO was replaced by NEW_INSN.
+   Update the corresponding `reg_set_table' entry accordingly.
+   We assume that NEW_INSN is not already recorded in reg_set_table[regno].  */
+
+static void
+replace_one_set (int regno, rtx old_insn, rtx new_insn)
+{
+  struct reg_set *reg_info;
+  if (regno >= reg_set_table_size)
+    return;
+  for (reg_info = reg_set_table[regno]; reg_info; reg_info = reg_info->next)
+    if (reg_info->insn == old_insn)
+      {
+        reg_info->insn = new_insn;
+        break;
+      }
+}
+
 /* Record REGNO in the reg_set table.  */
 
 static void
-record_one_set (regno, insn)
-     int regno;
-     rtx insn;
+record_one_set (int regno, rtx insn)
 {
   /* Allocate a new reg_set element and link it onto the list.  */
   struct reg_set *new_reg_info;
@@ -1232,16 +1132,14 @@ record_one_set (regno, insn)
     {
       int new_size = regno + REG_SET_TABLE_SLOP;
 
-      reg_set_table
-       = (struct reg_set **) grealloc ((char *) reg_set_table,
-                                       new_size * sizeof (struct reg_set *));
-      memset ((char *) (reg_set_table + reg_set_table_size), 0,
+      reg_set_table = grealloc (reg_set_table,
+                               new_size * sizeof (struct reg_set *));
+      memset (reg_set_table + reg_set_table_size, 0,
              (new_size - reg_set_table_size) * sizeof (struct reg_set *));
       reg_set_table_size = new_size;
     }
 
-  new_reg_info = (struct reg_set *) obstack_alloc (&reg_set_obstack,
-                                                  sizeof (struct reg_set));
+  new_reg_info = obstack_alloc (&reg_set_obstack, sizeof (struct reg_set));
   bytes_used += sizeof (struct reg_set);
   new_reg_info->insn = insn;
   new_reg_info->next = reg_set_table[regno];
@@ -1253,13 +1151,11 @@ record_one_set (regno, insn)
    occurring.  */
 
 static void
-record_set_info (dest, setter, data)
-     rtx dest, setter ATTRIBUTE_UNUSED;
-     void *data;
+record_set_info (rtx dest, rtx setter ATTRIBUTE_UNUSED, void *data)
 {
   rtx record_set_insn = (rtx) data;
 
-  if (GET_CODE (dest) == REG && REGNO (dest) >= FIRST_PSEUDO_REGISTER)
+  if (REG_P (dest) && REGNO (dest) >= FIRST_PSEUDO_REGISTER)
     record_one_set (REGNO (dest), record_set_insn);
 }
 
@@ -1269,8 +1165,7 @@ record_set_info (dest, setter, data)
    `reg_set_table' for further documentation.  */
 
 static void
-compute_sets (f)
-     rtx f;
+compute_sets (rtx f)
 {
   rtx insn;
 
@@ -1295,14 +1190,9 @@ static basic_block current_bb;
 /* See whether X, the source of a set, is something we want to consider for
    GCSE.  */
 
-static GTY(()) rtx test_insn;
 static int
-want_to_gcse_p (x)
-     rtx x;
+want_to_gcse_p (rtx x)
 {
-  int num_clobbers = 0;
-  int icode;
-
   switch (GET_CODE (x))
     {
     case REG:
@@ -1311,12 +1201,24 @@ want_to_gcse_p (x)
     case CONST_DOUBLE:
     case CONST_VECTOR:
     case CALL:
-    case CONSTANT_P_RTX:
       return 0;
 
     default:
-      break;
+      return can_assign_to_reg_p (x);
     }
+}
+
+/* Used internally by can_assign_to_reg_p.  */
+
+static GTY(()) rtx test_insn;
+
+/* Return true if we can assign X to a pseudo register.  */
+
+static bool
+can_assign_to_reg_p (rtx x)
+{
+  int num_clobbers = 0;
+  int icode;
 
   /* If this is a valid operand, we are OK.  If it's VOIDmode, we aren't.  */
   if (general_operand (x, GET_MODE (x)))
@@ -1349,9 +1251,7 @@ want_to_gcse_p (x)
    or from INSN to the end of INSN's basic block (if AVAIL_P != 0).  */
 
 static int
-oprs_unchanged_p (x, insn, avail_p)
-     rtx x, insn;
-     int avail_p;
+oprs_unchanged_p (rtx x, rtx insn, int avail_p)
 {
   int i, j;
   enum rtx_code code;
@@ -1444,9 +1344,8 @@ static rtx gcse_mem_operand;
    gcse_mems_conflict_p to a nonzero value.  */
 
 static void
-mems_conflict_for_gcse_p (dest, setter, data)
-     rtx dest, setter ATTRIBUTE_UNUSED;
-     void *data ATTRIBUTE_UNUSED;
+mems_conflict_for_gcse_p (rtx dest, rtx setter ATTRIBUTE_UNUSED,
+                         void *data ATTRIBUTE_UNUSED)
 {
   while (GET_CODE (dest) == SUBREG
         || GET_CODE (dest) == ZERO_EXTRACT
@@ -1457,13 +1356,13 @@ mems_conflict_for_gcse_p (dest, setter, data)
   /* If DEST is not a MEM, then it will not conflict with the load.  Note
      that function calls are assumed to clobber memory, but are handled
      elsewhere.  */
-  if (GET_CODE (dest) != MEM)
+  if (! MEM_P (dest))
     return;
 
   /* If we are setting a MEM in our list of specially recognized MEMs,
      don't mark as killed this time.  */
 
-  if (dest == gcse_mem_operand && pre_ldst_mems != NULL)
+  if (expr_equiv_p (dest, gcse_mem_operand) && pre_ldst_mems != NULL)
     {
       if (!find_rtx_in_ldst (dest))
        gcse_mems_conflict_p = 1;
@@ -1484,11 +1383,7 @@ mems_conflict_for_gcse_p (dest, setter, data)
    AVAIL_P to 0.  */
 
 static int
-load_killed_in_block_p (bb, uid_limit, x, avail_p)
-     basic_block bb;
-     int uid_limit;
-     rtx x;
-     int avail_p;
+load_killed_in_block_p (basic_block bb, int uid_limit, rtx x, int avail_p)
 {
   rtx list_entry = modify_mem_list[bb->index];
   while (list_entry)
@@ -1509,7 +1404,7 @@ load_killed_in_block_p (bb, uid_limit, x, avail_p)
       /* If SETTER is a call everything is clobbered.  Note that calls
         to pure functions are never put on the list, so we need not
         worry about them.  */
-      if (GET_CODE (setter) == CALL_INSN)
+      if (CALL_P (setter))
        return 1;
 
       /* SETTER must be an INSN of some kind that sets memory.  Call
@@ -1531,8 +1426,7 @@ load_killed_in_block_p (bb, uid_limit, x, avail_p)
    the start of INSN's basic block up to but not including INSN.  */
 
 static int
-oprs_anticipatable_p (x, insn)
-     rtx x, insn;
+oprs_anticipatable_p (rtx x, rtx insn)
 {
   return oprs_unchanged_p (x, insn, 0);
 }
@@ -1541,8 +1435,7 @@ oprs_anticipatable_p (x, insn)
    INSN to the end of INSN's basic block.  */
 
 static int
-oprs_available_p (x, insn)
-     rtx x, insn;
+oprs_available_p (rtx x, rtx insn)
 {
   return oprs_unchanged_p (x, insn, 1);
 }
@@ -1551,16 +1444,14 @@ oprs_available_p (x, insn)
 
    MODE is only used if X is a CONST_INT.  DO_NOT_RECORD_P is a boolean
    indicating if a volatile operand is found or if the expression contains
-   something we don't want to insert in the table.
+   something we don't want to insert in the table.  HASH_TABLE_SIZE is
+   the current size of the hash table to be probed.
 
    ??? One might want to merge this with canon_hash.  Later.  */
 
 static unsigned int
-hash_expr (x, mode, do_not_record_p, hash_table_size)
-     rtx x;
-     enum machine_mode mode;
-     int *do_not_record_p;
-     int hash_table_size;
+hash_expr (rtx x, enum machine_mode mode, int *do_not_record_p,
+          int hash_table_size)
 {
   unsigned int hash;
 
@@ -1573,8 +1464,7 @@ hash_expr (x, mode, do_not_record_p, hash_table_size)
 /* Hash a string.  Just add its bytes up.  */
 
 static inline unsigned
-hash_string_1 (ps)
-     const char *ps;
+hash_string_1 (const char *ps)
 {
   unsigned hash = 0;
   const unsigned char *p = (const unsigned char *) ps;
@@ -1589,24 +1479,21 @@ hash_string_1 (ps)
 /* Subroutine of hash_expr to do the actual work.  */
 
 static unsigned int
-hash_expr_1 (x, mode, do_not_record_p)
-     rtx x;
-     enum machine_mode mode;
-     int *do_not_record_p;
+hash_expr_1 (rtx x, enum machine_mode mode, int *do_not_record_p)
 {
   int i, j;
   unsigned hash = 0;
   enum rtx_code code;
   const char *fmt;
 
-  /* 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.  */
-
   if (x == 0)
     return hash;
 
+  /* 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:
+
   code = GET_CODE (x);
   switch (code)
     {
@@ -1780,9 +1667,7 @@ hash_expr_1 (x, mode, do_not_record_p)
    ??? May need to make things more elaborate.  Later, as necessary.  */
 
 static unsigned int
-hash_set (regno, hash_table_size)
-     int regno;
-     int hash_table_size;
+hash_set (int regno, int hash_table_size)
 {
   unsigned int hash;
 
@@ -1794,8 +1679,7 @@ hash_set (regno, hash_table_size)
    ??? Borrowed from cse.c.  Might want to remerge with cse.c.  Later.  */
 
 static int
-expr_equiv_p (x, y)
-     rtx x, y;
+expr_equiv_p (rtx x, rtx y)
 {
   int i, j;
   enum rtx_code code;
@@ -1805,7 +1689,7 @@ expr_equiv_p (x, y)
     return 1;
 
   if (x == 0 || y == 0)
-    return x == y;
+    return 0;
 
   code = GET_CODE (x);
   if (code != GET_CODE (y))
@@ -1819,10 +1703,8 @@ expr_equiv_p (x, y)
     {
     case PC:
     case CC0:
-      return x == y;
-
     case CONST_INT:
-      return INTVAL (x) == INTVAL (y);
+      return 0;
 
     case LABEL_REF:
       return XEXP (x, 0) == XEXP (y, 0);
@@ -1839,6 +1721,10 @@ expr_equiv_p (x, y)
         due to it being set with the different alias set.  */
       if (MEM_ALIAS_SET (x) != MEM_ALIAS_SET (y))
        return 0;
+
+      /* A volatile mem should not be considered equivalent to any other.  */
+      if (MEM_VOLATILE_P (x) || MEM_VOLATILE_P (y))
+       return 0;
       break;
 
     /*  For commutative operations, check both orders.  */
@@ -1944,12 +1830,8 @@ expr_equiv_p (x, y)
    AVAIL_P is nonzero if X is an available expression.  */
 
 static void
-insert_expr_in_table (x, mode, insn, antic_p, avail_p, table)
-     rtx x;
-     enum machine_mode mode;
-     rtx insn;
-     int antic_p, avail_p;
-     struct hash_table *table;
+insert_expr_in_table (rtx x, enum machine_mode mode, rtx insn, int antic_p,
+                     int avail_p, struct hash_table *table)
 {
   int found, do_not_record_p;
   unsigned int hash;
@@ -1978,7 +1860,7 @@ insert_expr_in_table (x, mode, insn, antic_p, avail_p, table)
 
   if (! found)
     {
-      cur_expr = (struct expr *) gcse_alloc (sizeof (struct expr));
+      cur_expr = gcse_alloc (sizeof (struct expr));
       bytes_used += sizeof (struct expr);
       if (table->table[hash] == NULL)
        /* This is the first pattern that hashed to this index.  */
@@ -2017,7 +1899,7 @@ insert_expr_in_table (x, mode, insn, antic_p, avail_p, table)
       else
        {
          /* First occurrence of this expression in this basic block.  */
-         antic_occr = (struct occr *) gcse_alloc (sizeof (struct occr));
+         antic_occr = gcse_alloc (sizeof (struct occr));
          bytes_used += sizeof (struct occr);
          /* First occurrence of this expression in any block?  */
          if (cur_expr->antic_occr == NULL)
@@ -2027,6 +1909,7 @@ insert_expr_in_table (x, mode, insn, antic_p, avail_p, table)
 
          antic_occr->insn = insn;
          antic_occr->next = NULL;
+         antic_occr->deleted_p = 0;
        }
     }
 
@@ -2052,7 +1935,7 @@ insert_expr_in_table (x, mode, insn, antic_p, avail_p, table)
       else
        {
          /* First occurrence of this expression in this basic block.  */
-         avail_occr = (struct occr *) gcse_alloc (sizeof (struct occr));
+         avail_occr = gcse_alloc (sizeof (struct occr));
          bytes_used += sizeof (struct occr);
 
          /* First occurrence of this expression in any block?  */
@@ -2063,6 +1946,7 @@ insert_expr_in_table (x, mode, insn, antic_p, avail_p, table)
 
          avail_occr->insn = insn;
          avail_occr->next = NULL;
+         avail_occr->deleted_p = 0;
        }
     }
 }
@@ -2073,10 +1957,7 @@ insert_expr_in_table (x, mode, insn, antic_p, avail_p, table)
    basic block.  */
 
 static void
-insert_set_in_table (x, insn, table)
-     rtx x;
-     rtx insn;
-     struct hash_table *table;
+insert_set_in_table (rtx x, rtx insn, struct hash_table *table)
 {
   int found;
   unsigned int hash;
@@ -2084,7 +1965,7 @@ insert_set_in_table (x, insn, table)
   struct occr *cur_occr, *last_occr = NULL;
 
   if (GET_CODE (x) != SET
-      || GET_CODE (SET_DEST (x)) != REG)
+      || ! REG_P (SET_DEST (x)))
     abort ();
 
   hash = hash_set (REGNO (SET_DEST (x)), table->size);
@@ -2102,7 +1983,7 @@ insert_set_in_table (x, insn, table)
 
   if (! found)
     {
-      cur_expr = (struct expr *) gcse_alloc (sizeof (struct expr));
+      cur_expr = gcse_alloc (sizeof (struct expr));
       bytes_used += sizeof (struct expr);
       if (table->table[hash] == NULL)
        /* This is the first pattern that hashed to this index.  */
@@ -2141,7 +2022,7 @@ insert_set_in_table (x, insn, table)
   else
     {
       /* First occurrence of this expression in this basic block.  */
-      cur_occr = (struct occr *) gcse_alloc (sizeof (struct occr));
+      cur_occr = gcse_alloc (sizeof (struct occr));
       bytes_used += sizeof (struct occr);
 
       /* First occurrence of this expression in any block?  */
@@ -2152,16 +2033,39 @@ insert_set_in_table (x, insn, table)
 
       cur_occr->insn = insn;
       cur_occr->next = NULL;
+      cur_occr->deleted_p = 0;
     }
 }
 
+/* Determine whether the rtx X should be treated as a constant for
+   the purposes of GCSE's constant propagation.  */
+
+static bool
+gcse_constant_p (rtx x)
+{
+  /* Consider a COMPARE of two integers constant.  */
+  if (GET_CODE (x) == COMPARE
+      && GET_CODE (XEXP (x, 0)) == CONST_INT
+      && GET_CODE (XEXP (x, 1)) == CONST_INT)
+    return true;
+
+  /* Consider a COMPARE of the same registers is a constant
+     if they are not floating point registers.  */
+  if (GET_CODE(x) == COMPARE
+      && REG_P (XEXP (x, 0)) && REG_P (XEXP (x, 1))
+      && REGNO (XEXP (x, 0)) == REGNO (XEXP (x, 1))
+      && ! FLOAT_MODE_P (GET_MODE (XEXP (x, 0)))
+      && ! FLOAT_MODE_P (GET_MODE (XEXP (x, 1))))
+    return true;
+
+  return CONSTANT_P (x);
+}
+
 /* Scan pattern PAT of INSN and add an entry to the hash TABLE (set or
    expression one).  */
 
 static void
-hash_scan_set (pat, insn, table)
-     rtx pat, insn;
-     struct hash_table *table;
+hash_scan_set (rtx pat, rtx insn, struct hash_table *table)
 {
   rtx src = SET_SRC (pat);
   rtx dest = SET_DEST (pat);
@@ -2170,7 +2074,7 @@ hash_scan_set (pat, insn, table)
   if (GET_CODE (src) == CALL)
     hash_scan_call (src, insn, table);
 
-  else if (GET_CODE (dest) == REG)
+  else if (REG_P (dest))
     {
       unsigned int regno = REGNO (dest);
       rtx tmp;
@@ -2178,14 +2082,14 @@ hash_scan_set (pat, insn, table)
       /* If this is a single set and we are doing constant propagation,
         see if a REG_NOTE shows this equivalent to a constant.  */
       if (table->set_p && (note = find_reg_equal_equiv_note (insn)) != 0
-         && CONSTANT_P (XEXP (note, 0)))
+         && gcse_constant_p (XEXP (note, 0)))
        src = XEXP (note, 0), pat = gen_rtx_SET (VOIDmode, dest, src);
 
       /* Only record sets of pseudo-regs in the hash table.  */
       if (! table->set_p
          && regno >= FIRST_PSEUDO_REGISTER
          /* Don't GCSE something if we can't do a reg/reg copy.  */
-         && can_copy_p [GET_MODE (dest)]
+         && can_copy_p (GET_MODE (dest))
          /* GCSE commonly inserts instruction after the insn.  We can't
             do that easily for EH_REGION notes so disable GCSE on these
             for now.  */
@@ -2200,7 +2104,7 @@ hash_scan_set (pat, insn, table)
             explicitly, it means address of parameter has been taken,
             so we should not extend the lifetime of the pseudo.  */
          && ((note = find_reg_note (insn, REG_EQUIV, NULL_RTX)) == 0
-             || GET_CODE (XEXP (note, 0)) != MEM))
+             || ! MEM_P (XEXP (note, 0))))
        {
          /* An expression is not anticipatable if its operands are
             modified before this insn or if this is not the only SET in
@@ -2219,34 +2123,74 @@ hash_scan_set (pat, insn, table)
       /* Record sets for constant/copy propagation.  */
       else if (table->set_p
               && regno >= FIRST_PSEUDO_REGISTER
-              && ((GET_CODE (src) == REG
+              && ((REG_P (src)
                    && REGNO (src) >= FIRST_PSEUDO_REGISTER
-                   && can_copy_p [GET_MODE (dest)]
+                   && can_copy_p (GET_MODE (dest))
                    && REGNO (src) != regno)
-                  || (CONSTANT_P (src)
-                      && GET_CODE (src) != CONSTANT_P_RTX))
+                  || gcse_constant_p (src))
               /* A copy is not available if its src or dest is subsequently
                  modified.  Here we want to search from INSN+1 on, but
                  oprs_available_p searches from INSN on.  */
-              && (insn == BLOCK_END (BLOCK_NUM (insn))
+              && (insn == BB_END (BLOCK_FOR_INSN (insn))
                   || ((tmp = next_nonnote_insn (insn)) != NULL_RTX
                       && oprs_available_p (pat, tmp))))
        insert_set_in_table (pat, insn, table);
     }
+  /* In case of store we want to consider the memory value as available in
+     the REG stored in that memory. This makes it possible to remove
+     redundant loads from due to stores to the same location.  */
+  else if (flag_gcse_las && REG_P (src) && MEM_P (dest))
+      {
+        unsigned int regno = REGNO (src);
+
+        /* Do not do this for constant/copy propagation.  */
+        if (! table->set_p
+            /* Only record sets of pseudo-regs in the hash table.  */
+           && regno >= FIRST_PSEUDO_REGISTER
+          /* Don't GCSE something if we can't do a reg/reg copy.  */
+          && can_copy_p (GET_MODE (src))
+          /* GCSE commonly inserts instruction after the insn.  We can't
+             do that easily for EH_REGION notes so disable GCSE on these
+             for now.  */
+          && ! find_reg_note (insn, REG_EH_REGION, NULL_RTX)
+          /* Is SET_DEST something we want to gcse?  */
+          && want_to_gcse_p (dest)
+          /* Don't CSE a nop.  */
+          && ! set_noop_p (pat)
+          /* Don't GCSE if it has attached REG_EQUIV note.
+             At this point this only function parameters should have
+             REG_EQUIV notes and if the argument slot is used somewhere
+             explicitly, it means address of parameter has been taken,
+             so we should not extend the lifetime of the pseudo.  */
+          && ((note = find_reg_note (insn, REG_EQUIV, NULL_RTX)) == 0
+              || ! MEM_P (XEXP (note, 0))))
+             {
+               /* Stores are never anticipatable.  */
+               int antic_p = 0;
+              /* An expression is not available if its operands are
+                 subsequently modified, including this insn.  It's also not
+                 available if this is a branch, because we can't insert
+                 a set after the branch.  */
+               int avail_p = oprs_available_p (dest, insn)
+                            && ! JUMP_P (insn);
+
+              /* Record the memory expression (DEST) in the hash table.  */
+              insert_expr_in_table (dest, GET_MODE (dest), insn,
+                                    antic_p, avail_p, table);
+             }
+      }
 }
 
 static void
-hash_scan_clobber (x, insn, table)
-     rtx x ATTRIBUTE_UNUSED, insn ATTRIBUTE_UNUSED;
-     struct hash_table *table ATTRIBUTE_UNUSED;
+hash_scan_clobber (rtx x ATTRIBUTE_UNUSED, rtx insn ATTRIBUTE_UNUSED,
+                  struct hash_table *table ATTRIBUTE_UNUSED)
 {
   /* Currently nothing to do.  */
 }
 
 static void
-hash_scan_call (x, insn, table)
-     rtx x ATTRIBUTE_UNUSED, insn ATTRIBUTE_UNUSED;
-     struct hash_table *table ATTRIBUTE_UNUSED;
+hash_scan_call (rtx x ATTRIBUTE_UNUSED, rtx insn ATTRIBUTE_UNUSED,
+               struct hash_table *table ATTRIBUTE_UNUSED)
 {
   /* Currently nothing to do.  */
 }
@@ -2265,10 +2209,7 @@ hash_scan_call (x, insn, table)
    not record any expressions.  */
 
 static void
-hash_scan_insn (insn, table, in_libcall_block)
-     rtx insn;
-     struct hash_table *table;
-     int in_libcall_block;
+hash_scan_insn (rtx insn, struct hash_table *table, int in_libcall_block)
 {
   rtx pat = PATTERN (insn);
   int i;
@@ -2301,10 +2242,7 @@ hash_scan_insn (insn, table, in_libcall_block)
 }
 
 static void
-dump_hash_table (file, name, table)
-     FILE *file;
-     const char *name;
-     struct hash_table *table;
+dump_hash_table (FILE *file, const char *name, struct hash_table *table)
 {
   int i;
   /* Flattened out table, so it's printed in proper order.  */
@@ -2312,9 +2250,8 @@ dump_hash_table (file, name, table)
   unsigned int *hash_val;
   struct expr *expr;
 
-  flat_table
-    = (struct expr **) xcalloc (table->n_elems, sizeof (struct expr *));
-  hash_val = (unsigned int *) xmalloc (table->n_elems * sizeof (unsigned int));
+  flat_table = xcalloc (table->n_elems, sizeof (struct expr *));
+  hash_val = xmalloc (table->n_elems * sizeof (unsigned int));
 
   for (i = 0; i < (int) table->size; i++)
     for (expr = table->table[i]; expr != NULL; expr = expr->next_same_hash)
@@ -2357,9 +2294,7 @@ dump_hash_table (file, name, table)
    and is used to compute "transparency".  */
 
 static void
-record_last_reg_set_info (insn, regno)
-     rtx insn;
-     int regno;
+record_last_reg_set_info (rtx insn, int regno)
 {
   struct reg_avail_info *info = &reg_avail_info[regno];
   int cuid = INSN_CUID (insn);
@@ -2379,10 +2314,8 @@ record_last_reg_set_info (insn, regno)
    taken off pairwise.  */
 
 static void
-canon_list_insert (dest, unused1, v_insn)
-     rtx    dest ATTRIBUTE_UNUSED;
-     rtx    unused1 ATTRIBUTE_UNUSED;
-     void * v_insn;
+canon_list_insert (rtx dest ATTRIBUTE_UNUSED, rtx unused1 ATTRIBUTE_UNUSED,
+                  void * v_insn)
 {
   rtx dest_addr, insn;
   int bb;
@@ -2397,7 +2330,7 @@ canon_list_insert (dest, unused1, v_insn)
      that function calls are assumed to clobber memory, but are handled
      elsewhere.  */
 
-  if (GET_CODE (dest) != MEM)
+  if (! MEM_P (dest))
     return;
 
   dest_addr = get_addr (XEXP (dest, 0));
@@ -2417,8 +2350,7 @@ canon_list_insert (dest, unused1, v_insn)
    a CALL_INSN).  We merely need to record which insns modify memory.  */
 
 static void
-record_last_mem_set_info (insn)
-     rtx insn;
+record_last_mem_set_info (rtx insn)
 {
   int bb = BLOCK_NUM (insn);
 
@@ -2427,7 +2359,7 @@ record_last_mem_set_info (insn)
   modify_mem_list[bb] = alloc_INSN_LIST (insn, modify_mem_list[bb]);
   bitmap_set_bit (modify_mem_list_set, bb);
 
-  if (GET_CODE (insn) == CALL_INSN)
+  if (CALL_P (insn))
     {
       /* Note that traversals of this loop (other than for free-ing)
         will break after encountering a CALL_INSN.  So, there's no
@@ -2445,18 +2377,16 @@ record_last_mem_set_info (insn)
    the SET is taking place.  */
 
 static void
-record_last_set_info (dest, setter, data)
-     rtx dest, setter ATTRIBUTE_UNUSED;
-     void *data;
+record_last_set_info (rtx dest, rtx setter ATTRIBUTE_UNUSED, void *data)
 {
   rtx last_set_insn = (rtx) data;
 
   if (GET_CODE (dest) == SUBREG)
     dest = SUBREG_REG (dest);
 
-  if (GET_CODE (dest) == REG)
+  if (REG_P (dest))
     record_last_reg_set_info (last_set_insn, REGNO (dest));
-  else if (GET_CODE (dest) == MEM
+  else if (MEM_P (dest)
           /* Ignore pushes, they clobber nothing.  */
           && ! push_operand (dest, GET_MODE (dest)))
     record_last_mem_set_info (last_set_insn);
@@ -2479,8 +2409,7 @@ record_last_set_info (dest, setter, data)
    TABLE is the table computed.  */
 
 static void
-compute_hash_table_work (table)
-     struct hash_table *table;
+compute_hash_table_work (struct hash_table *table)
 {
   unsigned int i;
 
@@ -2493,8 +2422,7 @@ compute_hash_table_work (table)
   /* re-Cache any INSN_LIST nodes we have allocated.  */
   clear_modify_mem_tables ();
   /* Some working arrays used to track first and last set in each block.  */
-  reg_avail_info = (struct reg_avail_info*)
-    gmalloc (max_gcse_regno * sizeof (struct reg_avail_info));
+  reg_avail_info = gmalloc (max_gcse_regno * sizeof (struct reg_avail_info));
 
   for (i = 0; i < max_gcse_regno; ++i)
     reg_avail_info[i].last_bb = NULL;
@@ -2510,14 +2438,14 @@ compute_hash_table_work (table)
         ??? hard-reg reg_set_in_block computation
         could be moved to compute_sets since they currently don't change.  */
 
-      for (insn = current_bb->head;
-          insn && insn != NEXT_INSN (current_bb->end);
+      for (insn = BB_HEAD (current_bb);
+          insn && insn != NEXT_INSN (BB_END (current_bb));
           insn = NEXT_INSN (insn))
        {
          if (! INSN_P (insn))
            continue;
 
-         if (GET_CODE (insn) == CALL_INSN)
+         if (CALL_P (insn))
            {
              bool clobbers_all = false;
 #ifdef NON_SAVING_SETJMP
@@ -2541,12 +2469,12 @@ compute_hash_table_work (table)
       if (table->set_p
          && implicit_sets[current_bb->index] != NULL_RTX)
        hash_scan_set (implicit_sets[current_bb->index],
-                      current_bb->head, table);
+                      BB_HEAD (current_bb), table);
 
       /* The next pass builds the hash table.  */
 
-      for (insn = current_bb->head, in_libcall_block = 0;
-          insn && insn != NEXT_INSN (current_bb->end);
+      for (insn = BB_HEAD (current_bb), in_libcall_block = 0;
+          insn && insn != NEXT_INSN (BB_END (current_bb));
           insn = NEXT_INSN (insn))
        if (INSN_P (insn))
          {
@@ -2571,10 +2499,7 @@ compute_hash_table_work (table)
    be created.  */
 
 static void
-alloc_hash_table (n_insns, table, set_p)
-     int n_insns;
-     struct hash_table *table;
-     int set_p;
+alloc_hash_table (int n_insns, struct hash_table *table, int set_p)
 {
   int n;
 
@@ -2587,15 +2512,14 @@ alloc_hash_table (n_insns, table, set_p)
      ??? Later take some measurements.  */
   table->size |= 1;
   n = table->size * sizeof (struct expr *);
-  table->table = (struct expr **) gmalloc (n);
+  table->table = gmalloc (n);
   table->set_p = set_p;
 }
 
 /* Free things allocated by alloc_hash_table.  */
 
 static void
-free_hash_table (table)
-     struct hash_table *table;
+free_hash_table (struct hash_table *table)
 {
   free (table->table);
 }
@@ -2604,13 +2528,11 @@ free_hash_table (table)
    expression hash table.  */
 
 static void
-compute_hash_table (table)
-    struct hash_table *table;
+compute_hash_table (struct hash_table *table)
 {
   /* Initialize count of number of entries in hash table.  */
   table->n_elems = 0;
-  memset ((char *) table->table, 0,
-         table->size * sizeof (struct expr *));
+  memset (table->table, 0, table->size * sizeof (struct expr *));
 
   compute_hash_table_work (table);
 }
@@ -2621,9 +2543,7 @@ compute_hash_table (table)
    The result is a pointer to the table entry, or NULL if not found.  */
 
 static struct expr *
-lookup_expr (pat, table)
-     rtx pat;
-     struct hash_table *table;
+lookup_expr (rtx pat, struct hash_table *table)
 {
   int do_not_record_p;
   unsigned int hash = hash_expr (pat, GET_MODE (pat), &do_not_record_p,
@@ -2645,9 +2565,7 @@ lookup_expr (pat, table)
    table entry, or NULL if not found.  */
 
 static struct expr *
-lookup_set (regno, table)
-     unsigned int regno;
-     struct hash_table *table;
+lookup_set (unsigned int regno, struct hash_table *table)
 {
   unsigned int hash = hash_set (regno, table->size);
   struct expr *expr;
@@ -2663,9 +2581,7 @@ lookup_set (regno, table)
 /* Return the next entry for REGNO in list EXPR.  */
 
 static struct expr *
-next_set (regno, expr)
-     unsigned int regno;
-     struct expr *expr;
+next_set (unsigned int regno, struct expr *expr)
 {
   do
     expr = expr->next_same_hash;
@@ -2678,8 +2594,7 @@ next_set (regno, expr)
    types may be mixed.  */
 
 static void
-free_insn_expr_list_list (listp)
-     rtx *listp;
+free_insn_expr_list_list (rtx *listp)
 {
   rtx list, next;
 
@@ -2697,7 +2612,7 @@ free_insn_expr_list_list (listp)
 
 /* Clear canon_modify_mem_list and modify_mem_list tables.  */
 static void
-clear_modify_mem_tables ()
+clear_modify_mem_tables (void)
 {
   int i;
 
@@ -2714,7 +2629,7 @@ clear_modify_mem_tables ()
 /* Release memory used by modify_mem_list_set and canon_modify_mem_list_set.  */
 
 static void
-free_modify_mem_tables ()
+free_modify_mem_tables (void)
 {
   clear_modify_mem_tables ();
   free (modify_mem_list);
@@ -2727,7 +2642,7 @@ free_modify_mem_tables ()
    start of the block].  */
 
 static void
-reset_opr_set_tables ()
+reset_opr_set_tables (void)
 {
   /* Maintain a bitmap of which regs have been set since beginning of
      the block.  */
@@ -2743,8 +2658,7 @@ reset_opr_set_tables ()
    INSN's basic block.  */
 
 static int
-oprs_not_set_p (x, insn)
-     rtx x, insn;
+oprs_not_set_p (rtx x, rtx insn)
 {
   int i, j;
   enum rtx_code code;
@@ -2807,8 +2721,7 @@ oprs_not_set_p (x, insn)
 /* Mark things set by a CALL.  */
 
 static void
-mark_call (insn)
-     rtx insn;
+mark_call (rtx insn)
 {
   if (! CONST_OR_PURE_CALL_P (insn))
     record_last_mem_set_info (insn);
@@ -2817,8 +2730,7 @@ mark_call (insn)
 /* Mark things set by a SET.  */
 
 static void
-mark_set (pat, insn)
-     rtx pat, insn;
+mark_set (rtx pat, rtx insn)
 {
   rtx dest = SET_DEST (pat);
 
@@ -2828,9 +2740,9 @@ mark_set (pat, insn)
         || GET_CODE (dest) == STRICT_LOW_PART)
     dest = XEXP (dest, 0);
 
-  if (GET_CODE (dest) == REG)
+  if (REG_P (dest))
     SET_REGNO_REG_SET (reg_set_bitmap, REGNO (dest));
-  else if (GET_CODE (dest) == MEM)
+  else if (MEM_P (dest))
     record_last_mem_set_info (insn);
 
   if (GET_CODE (SET_SRC (pat)) == CALL)
@@ -2840,15 +2752,14 @@ mark_set (pat, insn)
 /* Record things set by a CLOBBER.  */
 
 static void
-mark_clobber (pat, insn)
-     rtx pat, insn;
+mark_clobber (rtx pat, rtx insn)
 {
   rtx clob = XEXP (pat, 0);
 
   while (GET_CODE (clob) == SUBREG || GET_CODE (clob) == STRICT_LOW_PART)
     clob = XEXP (clob, 0);
 
-  if (GET_CODE (clob) == REG)
+  if (REG_P (clob))
     SET_REGNO_REG_SET (reg_set_bitmap, REGNO (clob));
   else
     record_last_mem_set_info (insn);
@@ -2858,8 +2769,7 @@ mark_clobber (pat, insn)
    This data is used by oprs_not_set_p.  */
 
 static void
-mark_oprs_set (insn)
-     rtx insn;
+mark_oprs_set (rtx insn)
 {
   rtx pat = PATTERN (insn);
   int i;
@@ -2886,229 +2796,230 @@ mark_oprs_set (insn)
 }
 
 \f
-/* Classic GCSE reaching definition support.  */
-
-/* Allocate reaching def variables.  */
-
-static void
-alloc_rd_mem (n_blocks, n_insns)
-     int n_blocks, n_insns;
-{
-  rd_kill = (sbitmap *) sbitmap_vector_alloc (n_blocks, n_insns);
-  sbitmap_vector_zero (rd_kill, n_blocks);
-
-  rd_gen = (sbitmap *) sbitmap_vector_alloc (n_blocks, n_insns);
-  sbitmap_vector_zero (rd_gen, n_blocks);
+/* Compute copy/constant propagation working variables.  */
 
-  reaching_defs = (sbitmap *) sbitmap_vector_alloc (n_blocks, n_insns);
-  sbitmap_vector_zero (reaching_defs, n_blocks);
+/* Local properties of assignments.  */
+static sbitmap *cprop_pavloc;
+static sbitmap *cprop_absaltered;
 
-  rd_out = (sbitmap *) sbitmap_vector_alloc (n_blocks, n_insns);
-  sbitmap_vector_zero (rd_out, n_blocks);
-}
+/* Global properties of assignments (computed from the local properties).  */
+static sbitmap *cprop_avin;
+static sbitmap *cprop_avout;
 
-/* Free reaching def variables.  */
+/* Allocate vars used for copy/const propagation.  N_BLOCKS is the number of
+   basic blocks.  N_SETS is the number of sets.  */
 
 static void
-free_rd_mem ()
+alloc_cprop_mem (int n_blocks, int n_sets)
 {
-  sbitmap_vector_free (rd_kill);
-  sbitmap_vector_free (rd_gen);
-  sbitmap_vector_free (reaching_defs);
-  sbitmap_vector_free (rd_out);
+  cprop_pavloc = sbitmap_vector_alloc (n_blocks, n_sets);
+  cprop_absaltered = sbitmap_vector_alloc (n_blocks, n_sets);
+
+  cprop_avin = sbitmap_vector_alloc (n_blocks, n_sets);
+  cprop_avout = sbitmap_vector_alloc (n_blocks, n_sets);
 }
 
-/* Add INSN to the kills of BB.  REGNO, set in BB, is killed by INSN.  */
+/* Free vars used by copy/const propagation.  */
 
 static void
-handle_rd_kill_set (insn, regno, bb)
-     rtx insn;
-     int regno;
-     basic_block bb;
+free_cprop_mem (void)
 {
-  struct reg_set *this_reg;
-
-  for (this_reg = reg_set_table[regno]; this_reg; this_reg = this_reg ->next)
-    if (BLOCK_NUM (this_reg->insn) != BLOCK_NUM (insn))
-      SET_BIT (rd_kill[bb->index], INSN_CUID (this_reg->insn));
+  sbitmap_vector_free (cprop_pavloc);
+  sbitmap_vector_free (cprop_absaltered);
+  sbitmap_vector_free (cprop_avin);
+  sbitmap_vector_free (cprop_avout);
 }
 
-/* Compute the set of kill's for reaching definitions.  */
+/* For each block, compute whether X is transparent.  X is either an
+   expression or an assignment [though we don't care which, for this context
+   an assignment is treated as an expression].  For each block where an
+   element of X is modified, set (SET_P == 1) or reset (SET_P == 0) the INDX
+   bit in BMAP.  */
 
 static void
-compute_kill_rd ()
+compute_transp (rtx x, int indx, sbitmap *bmap, int set_p)
 {
-  int cuid;
-  unsigned int regno;
-  int i;
+  int i, j;
   basic_block bb;
+  enum rtx_code code;
+  reg_set *r;
+  const char *fmt;
 
-  /* For each block
-       For each set bit in `gen' of the block (i.e each insn which
-          generates a definition in the block)
-        Call the reg set by the insn corresponding to that bit regx
-        Look at the linked list starting at reg_set_table[regx]
-        For each setting of regx in the linked list, which is not in
-            this block
-          Set the bit in `kill' corresponding to that insn.  */
-  FOR_EACH_BB (bb)
-    for (cuid = 0; cuid < max_cuid; cuid++)
-      if (TEST_BIT (rd_gen[bb->index], cuid))
-       {
-         rtx insn = CUID_INSN (cuid);
-         rtx pat = PATTERN (insn);
+  /* repeat is used to turn tail-recursion into iteration since GCC
+     can't do it when there's no return value.  */
+ repeat:
 
-         if (GET_CODE (insn) == CALL_INSN)
+  if (x == 0)
+    return;
+
+  code = GET_CODE (x);
+  switch (code)
+    {
+    case REG:
+      if (set_p)
+       {
+         if (REGNO (x) < FIRST_PSEUDO_REGISTER)
            {
-             for (regno = 0; regno < FIRST_PSEUDO_REGISTER; regno++)
-               if (TEST_HARD_REG_BIT (regs_invalidated_by_call, regno))
-                 handle_rd_kill_set (insn, regno, bb);
+             FOR_EACH_BB (bb)
+               if (TEST_BIT (reg_set_in_block[bb->index], REGNO (x)))
+                 SET_BIT (bmap[bb->index], indx);
            }
-
-         if (GET_CODE (pat) == PARALLEL)
+         else
            {
-             for (i = XVECLEN (pat, 0) - 1; i >= 0; i--)
-               {
-                 enum rtx_code code = GET_CODE (XVECEXP (pat, 0, i));
-
-                 if ((code == SET || code == CLOBBER)
-                     && GET_CODE (XEXP (XVECEXP (pat, 0, i), 0)) == REG)
-                   handle_rd_kill_set (insn,
-                                       REGNO (XEXP (XVECEXP (pat, 0, i), 0)),
-                                       bb);
-               }
+             for (r = reg_set_table[REGNO (x)]; r != NULL; r = r->next)
+               SET_BIT (bmap[BLOCK_NUM (r->insn)], indx);
+           }
+       }
+      else
+       {
+         if (REGNO (x) < FIRST_PSEUDO_REGISTER)
+           {
+             FOR_EACH_BB (bb)
+               if (TEST_BIT (reg_set_in_block[bb->index], REGNO (x)))
+                 RESET_BIT (bmap[bb->index], indx);
+           }
+         else
+           {
+             for (r = reg_set_table[REGNO (x)]; r != NULL; r = r->next)
+               RESET_BIT (bmap[BLOCK_NUM (r->insn)], indx);
            }
-         else if (GET_CODE (pat) == SET && GET_CODE (SET_DEST (pat)) == REG)
-           /* Each setting of this register outside of this block
-              must be marked in the set of kills in this block.  */
-           handle_rd_kill_set (insn, REGNO (SET_DEST (pat)), bb);
        }
-}
-
-/* Compute the reaching definitions as in
-   Compilers Principles, Techniques, and Tools. Aho, Sethi, Ullman,
-   Chapter 10.  It is the same algorithm as used for computing available
-   expressions but applied to the gens and kills of reaching definitions.  */
-
-static void
-compute_rd ()
-{
-  int changed, passes;
-  basic_block bb;
 
-  FOR_EACH_BB (bb)
-    sbitmap_copy (rd_out[bb->index] /*dst*/, rd_gen[bb->index] /*src*/);
+      return;
 
-  passes = 0;
-  changed = 1;
-  while (changed)
-    {
-      changed = 0;
+    case MEM:
       FOR_EACH_BB (bb)
        {
-         sbitmap_union_of_preds (reaching_defs[bb->index], rd_out, bb->index);
-         changed |= sbitmap_union_of_diff_cg (rd_out[bb->index], rd_gen[bb->index],
-                                              reaching_defs[bb->index], rd_kill[bb->index]);
-       }
-      passes++;
-    }
+         rtx list_entry = canon_modify_mem_list[bb->index];
 
-  if (gcse_file)
-    fprintf (gcse_file, "reaching def computation: %d passes\n", passes);
-}
-\f
-/* Classic GCSE available expression support.  */
+         while (list_entry)
+           {
+             rtx dest, dest_addr;
 
-/* Allocate memory for available expression computation.  */
+             if (CALL_P (XEXP (list_entry, 0)))
+               {
+                 if (set_p)
+                   SET_BIT (bmap[bb->index], indx);
+                 else
+                   RESET_BIT (bmap[bb->index], indx);
+                 break;
+               }
+             /* LIST_ENTRY must be an INSN of some kind that sets memory.
+                Examine each hunk of memory that is modified.  */
 
-static void
-alloc_avail_expr_mem (n_blocks, n_exprs)
-     int n_blocks, n_exprs;
-{
-  ae_kill = (sbitmap *) sbitmap_vector_alloc (n_blocks, n_exprs);
-  sbitmap_vector_zero (ae_kill, n_blocks);
+             dest = XEXP (list_entry, 0);
+             list_entry = XEXP (list_entry, 1);
+             dest_addr = XEXP (list_entry, 0);
 
-  ae_gen = (sbitmap *) sbitmap_vector_alloc (n_blocks, n_exprs);
-  sbitmap_vector_zero (ae_gen, n_blocks);
+             if (canon_true_dependence (dest, GET_MODE (dest), dest_addr,
+                                        x, rtx_addr_varies_p))
+               {
+                 if (set_p)
+                   SET_BIT (bmap[bb->index], indx);
+                 else
+                   RESET_BIT (bmap[bb->index], indx);
+                 break;
+               }
+             list_entry = XEXP (list_entry, 1);
+           }
+       }
 
-  ae_in = (sbitmap *) sbitmap_vector_alloc (n_blocks, n_exprs);
-  sbitmap_vector_zero (ae_in, n_blocks);
+      x = XEXP (x, 0);
+      goto repeat;
 
-  ae_out = (sbitmap *) sbitmap_vector_alloc (n_blocks, n_exprs);
-  sbitmap_vector_zero (ae_out, n_blocks);
-}
+    case PC:
+    case CC0: /*FIXME*/
+    case CONST:
+    case CONST_INT:
+    case CONST_DOUBLE:
+    case CONST_VECTOR:
+    case SYMBOL_REF:
+    case LABEL_REF:
+    case ADDR_VEC:
+    case ADDR_DIFF_VEC:
+      return;
 
-static void
-free_avail_expr_mem ()
-{
-  sbitmap_vector_free (ae_kill);
-  sbitmap_vector_free (ae_gen);
-  sbitmap_vector_free (ae_in);
-  sbitmap_vector_free (ae_out);
+    default:
+      break;
+    }
+
+  for (i = GET_RTX_LENGTH (code) - 1, fmt = GET_RTX_FORMAT (code); i >= 0; i--)
+    {
+      if (fmt[i] == 'e')
+       {
+         /* If we are about to do the last recursive call
+            needed at this level, change it into iteration.
+            This function is called enough to be worth it.  */
+         if (i == 0)
+           {
+             x = XEXP (x, i);
+             goto repeat;
+           }
+
+         compute_transp (XEXP (x, i), indx, bmap, set_p);
+       }
+      else if (fmt[i] == 'E')
+       for (j = 0; j < XVECLEN (x, i); j++)
+         compute_transp (XVECEXP (x, i, j), indx, bmap, set_p);
+    }
 }
 
-/* Compute the set of available expressions generated in each basic block.  */
+/* Top level routine to do the dataflow analysis needed by copy/const
+   propagation.  */
 
 static void
-compute_ae_gen (expr_hash_table)
-     struct hash_table *expr_hash_table;
+compute_cprop_data (void)
 {
-  unsigned int i;
-  struct expr *expr;
-  struct occr *occr;
-
-  /* For each recorded occurrence of each expression, set ae_gen[bb][expr].
-     This is all we have to do because an expression is not recorded if it
-     is not available, and the only expressions we want to work with are the
-     ones that are recorded.  */
-  for (i = 0; i < expr_hash_table->size; i++)
-    for (expr = expr_hash_table->table[i]; expr != 0; expr = expr->next_same_hash)
-      for (occr = expr->avail_occr; occr != 0; occr = occr->next)
-       SET_BIT (ae_gen[BLOCK_NUM (occr->insn)], expr->bitmap_index);
+  compute_local_properties (cprop_absaltered, cprop_pavloc, NULL, &set_hash_table);
+  compute_available (cprop_pavloc, cprop_absaltered,
+                    cprop_avout, cprop_avin);
 }
+\f
+/* Copy/constant propagation.  */
 
-/* Return nonzero if expression X is killed in BB.  */
+/* Maximum number of register uses in an insn that we handle.  */
+#define MAX_USES 8
 
-static int
-expr_killed_p (x, bb)
-     rtx x;
-     basic_block bb;
+/* Table of uses found in an insn.
+   Allocated statically to avoid alloc/free complexity and overhead.  */
+static struct reg_use reg_use_table[MAX_USES];
+
+/* Index into `reg_use_table' while building it.  */
+static int reg_use_count;
+
+/* Set up a list of register numbers used in INSN.  The found uses are stored
+   in `reg_use_table'.  `reg_use_count' is initialized to zero before entry,
+   and contains the number of uses in the table upon exit.
+
+   ??? If a register appears multiple times we will record it multiple times.
+   This doesn't hurt anything but it will slow things down.  */
+
+static void
+find_used_regs (rtx *xptr, void *data ATTRIBUTE_UNUSED)
 {
   int i, j;
   enum rtx_code code;
   const char *fmt;
+  rtx x = *xptr;
 
+  /* repeat is used to turn tail-recursion into iteration since GCC
+     can't do it when there's no return value.  */
+ repeat:
   if (x == 0)
-    return 1;
+    return;
 
   code = GET_CODE (x);
-  switch (code)
+  if (REG_P (x))
     {
-    case REG:
-      return TEST_BIT (reg_set_in_block[bb->index], REGNO (x));
-
-    case MEM:
-      if (load_killed_in_block_p (bb, get_max_uid () + 1, x, 0))
-       return 1;
-      else
-       return expr_killed_p (XEXP (x, 0), bb);
-
-    case PC:
-    case CC0: /*FIXME*/
-    case CONST:
-    case CONST_INT:
-    case CONST_DOUBLE:
-    case CONST_VECTOR:
-    case SYMBOL_REF:
-    case LABEL_REF:
-    case ADDR_VEC:
-    case ADDR_DIFF_VEC:
-      return 0;
+      if (reg_use_count == MAX_USES)
+       return;
 
-    default:
-      break;
+      reg_use_table[reg_use_count].reg_rtx = x;
+      reg_use_count++;
     }
 
+  /* Recursively scan the operands of this expression.  */
+
   for (i = GET_RTX_LENGTH (code) - 1, fmt = GET_RTX_FORMAT (code); i >= 0; i--)
     {
       if (fmt[i] == 'e')
@@ -3117,440 +3028,376 @@ expr_killed_p (x, bb)
             needed at this level, change it into iteration.
             This function is called enough to be worth it.  */
          if (i == 0)
-           return expr_killed_p (XEXP (x, i), bb);
-         else if (expr_killed_p (XEXP (x, i), bb))
-           return 1;
+           {
+             x = XEXP (x, 0);
+             goto repeat;
+           }
+
+         find_used_regs (&XEXP (x, i), data);
        }
       else if (fmt[i] == 'E')
        for (j = 0; j < XVECLEN (x, i); j++)
-         if (expr_killed_p (XVECEXP (x, i, j), bb))
-           return 1;
+         find_used_regs (&XVECEXP (x, i, j), data);
     }
-
-  return 0;
 }
 
-/* Compute the set of available expressions killed in each basic block.  */
+/* Try to replace all non-SET_DEST occurrences of FROM in INSN with TO.
+   Returns nonzero is successful.  */
 
-static void
-compute_ae_kill (ae_gen, ae_kill, expr_hash_table)
-     sbitmap *ae_gen, *ae_kill;
-     struct hash_table *expr_hash_table;
+static int
+try_replace_reg (rtx from, rtx to, rtx insn)
 {
-  basic_block bb;
-  unsigned int i;
-  struct expr *expr;
+  rtx note = find_reg_equal_equiv_note (insn);
+  rtx src = 0;
+  int success = 0;
+  rtx set = single_set (insn);
 
-  FOR_EACH_BB (bb)
-    for (i = 0; i < expr_hash_table->size; i++)
-      for (expr = expr_hash_table->table[i]; expr; expr = expr->next_same_hash)
-       {
-         /* Skip EXPR if generated in this block.  */
-         if (TEST_BIT (ae_gen[bb->index], expr->bitmap_index))
-           continue;
+  validate_replace_src_group (from, to, insn);
+  if (num_changes_pending () && apply_change_group ())
+    success = 1;
 
-         if (expr_killed_p (expr->expr, bb))
-           SET_BIT (ae_kill[bb->index], expr->bitmap_index);
-       }
-}
-\f
-/* Actually perform the Classic GCSE optimizations.  */
+  /* Try to simplify SET_SRC if we have substituted a constant.  */
+  if (success && set && CONSTANT_P (to))
+    {
+      src = simplify_rtx (SET_SRC (set));
 
-/* Return nonzero if occurrence OCCR of expression EXPR reaches block BB.
+      if (src)
+       validate_change (insn, &SET_SRC (set), src, 0);
+    }
 
-   CHECK_SELF_LOOP is nonzero if we should consider a block reaching itself
-   as a positive reach.  We want to do this when there are two computations
-   of the expression in the block.
+  /* If there is already a NOTE, update the expression in it with our
+     replacement.  */
+  if (note != 0)
+    XEXP (note, 0) = simplify_replace_rtx (XEXP (note, 0), from, to);
 
-   VISITED is a pointer to a working buffer for tracking which BB's have
-   been visited.  It is NULL for the top-level call.
+  if (!success && set && reg_mentioned_p (from, SET_SRC (set)))
+    {
+      /* If above failed and this is a single set, try to simplify the source of
+        the set given our substitution.  We could perhaps try this for multiple
+        SETs, but it probably won't buy us anything.  */
+      src = simplify_replace_rtx (SET_SRC (set), from, to);
 
-   We treat reaching expressions that go through blocks containing the same
-   reaching expression as "not reaching".  E.g. if EXPR is generated in blocks
-   2 and 3, INSN is in block 4, and 2->3->4, we treat the expression in block
-   2 as not reaching.  The intent is to improve the probability of finding
-   only one reaching expression and to reduce register lifetimes by picking
-   the closest such expression.  */
+      if (!rtx_equal_p (src, SET_SRC (set))
+         && validate_change (insn, &SET_SRC (set), src, 0))
+       success = 1;
 
-static int
-expr_reaches_here_p_work (occr, expr, bb, check_self_loop, visited)
-     struct occr *occr;
-     struct expr *expr;
-     basic_block bb;
-     int check_self_loop;
-     char *visited;
+      /* If we've failed to do replacement, have a single SET, don't already
+        have a note, and have no special SET, add a REG_EQUAL note to not
+        lose information.  */
+      if (!success && note == 0 && set != 0
+         && GET_CODE (XEXP (set, 0)) != ZERO_EXTRACT
+         && GET_CODE (XEXP (set, 0)) != SIGN_EXTRACT)
+       note = set_unique_reg_note (insn, REG_EQUAL, copy_rtx (src));
+    }
+
+  /* REG_EQUAL may get simplified into register.
+     We don't allow that. Remove that note. This code ought
+     not to happen, because previous code ought to synthesize
+     reg-reg move, but be on the safe side.  */
+  if (note && REG_P (XEXP (note, 0)))
+    remove_note (insn, note);
+
+  return success;
+}
+
+/* Find a set of REGNOs that are available on entry to INSN's block.  Returns
+   NULL no such set is found.  */
+
+static struct expr *
+find_avail_set (int regno, rtx insn)
 {
-  edge pred;
+  /* SET1 contains the last set found that can be returned to the caller for
+     use in a substitution.  */
+  struct expr *set1 = 0;
 
-  for (pred = bb->pred; pred != NULL; pred = pred->pred_next)
+  /* Loops are not possible here.  To get a loop we would need two sets
+     available at the start of the block containing INSN.  ie we would
+     need two sets like this available at the start of the block:
+
+       (set (reg X) (reg Y))
+       (set (reg Y) (reg X))
+
+     This can not happen since the set of (reg Y) would have killed the
+     set of (reg X) making it unavailable at the start of this block.  */
+  while (1)
     {
-      basic_block pred_bb = pred->src;
+      rtx src;
+      struct expr *set = lookup_set (regno, &set_hash_table);
 
-      if (visited[pred_bb->index])
-       /* This predecessor has already been visited. Nothing to do.  */
-         ;
-      else if (pred_bb == bb)
+      /* Find a set that is available at the start of the block
+        which contains INSN.  */
+      while (set)
        {
-         /* BB loops on itself.  */
-         if (check_self_loop
-             && TEST_BIT (ae_gen[pred_bb->index], expr->bitmap_index)
-             && BLOCK_NUM (occr->insn) == pred_bb->index)
-           return 1;
-
-         visited[pred_bb->index] = 1;
+         if (TEST_BIT (cprop_avin[BLOCK_NUM (insn)], set->bitmap_index))
+           break;
+         set = next_set (regno, set);
        }
 
-      /* Ignore this predecessor if it kills the expression.  */
-      else if (TEST_BIT (ae_kill[pred_bb->index], expr->bitmap_index))
-       visited[pred_bb->index] = 1;
-
-      /* Does this predecessor generate this expression?  */
-      else if (TEST_BIT (ae_gen[pred_bb->index], expr->bitmap_index))
-       {
-         /* Is this the occurrence we're looking for?
-            Note that there's only one generating occurrence per block
-            so we just need to check the block number.  */
-         if (BLOCK_NUM (occr->insn) == pred_bb->index)
-           return 1;
+      /* If no available set was found we've reached the end of the
+        (possibly empty) copy chain.  */
+      if (set == 0)
+       break;
 
-         visited[pred_bb->index] = 1;
-       }
+      if (GET_CODE (set->expr) != SET)
+       abort ();
 
-      /* Neither gen nor kill.  */
-      else
-       {
-         visited[pred_bb->index] = 1;
-         if (expr_reaches_here_p_work (occr, expr, pred_bb, check_self_loop,
-             visited))
+      src = SET_SRC (set->expr);
 
-           return 1;
-       }
-    }
+      /* We know the set is available.
+        Now check that SRC is ANTLOC (i.e. none of the source operands
+        have changed since the start of the block).
 
-  /* All paths have been checked.  */
-  return 0;
-}
+         If the source operand changed, we may still use it for the next
+         iteration of this loop, but we may not use it for substitutions.  */
 
-/* This wrapper for expr_reaches_here_p_work() is to ensure that any
-   memory allocated for that function is returned.  */
+      if (gcse_constant_p (src) || oprs_not_set_p (src, insn))
+       set1 = set;
 
-static int
-expr_reaches_here_p (occr, expr, bb, check_self_loop)
-     struct occr *occr;
-     struct expr *expr;
-     basic_block bb;
-     int check_self_loop;
-{
-  int rval;
-  char *visited = (char *) xcalloc (last_basic_block, 1);
+      /* If the source of the set is anything except a register, then
+        we have reached the end of the copy chain.  */
+      if (! REG_P (src))
+       break;
 
-  rval = expr_reaches_here_p_work (occr, expr, bb, check_self_loop, visited);
+      /* Follow the copy chain, ie start another iteration of the loop
+        and see if we have an available copy into SRC.  */
+      regno = REGNO (src);
+    }
 
-  free (visited);
-  return rval;
+  /* SET1 holds the last set that was available and anticipatable at
+     INSN.  */
+  return set1;
 }
 
-/* Return the instruction that computes EXPR that reaches INSN's basic block.
-   If there is more than one such instruction, return NULL.
-
-   Called only by handle_avail_expr.  */
+/* Subroutine of cprop_insn that tries to propagate constants into
+   JUMP_INSNS.  JUMP must be a conditional jump.  If SETCC is non-NULL
+   it is the instruction that immediately precedes JUMP, and must be a
+   single SET of a register.  FROM is what we will try to replace,
+   SRC is the constant we will try to substitute for it.  Returns nonzero
+   if a change was made.  */
 
-static rtx
-computing_insn (expr, insn)
-     struct expr *expr;
-     rtx insn;
+static int
+cprop_jump (basic_block bb, rtx setcc, rtx jump, rtx from, rtx src)
 {
-  basic_block bb = BLOCK_FOR_INSN (insn);
+  rtx new, set_src, note_src;
+  rtx set = pc_set (jump);
+  rtx note = find_reg_equal_equiv_note (jump);
 
-  if (expr->avail_occr->next == NULL)
+  if (note)
     {
-      if (BLOCK_FOR_INSN (expr->avail_occr->insn) == bb)
-       /* The available expression is actually itself
-          (i.e. a loop in the flow graph) so do nothing.  */
-       return NULL;
+      note_src = XEXP (note, 0);
+      if (GET_CODE (note_src) == EXPR_LIST)
+       note_src = NULL_RTX;
+    }
+  else note_src = NULL_RTX;
+
+  /* Prefer REG_EQUAL notes except those containing EXPR_LISTs.  */
+  set_src = note_src ? note_src : SET_SRC (set);
 
-      /* (FIXME) Case that we found a pattern that was created by
-        a substitution that took place.  */
-      return expr->avail_occr->insn;
+  /* First substitute the SETCC condition into the JUMP instruction,
+     then substitute that given values into this expanded JUMP.  */
+  if (setcc != NULL_RTX
+      && !modified_between_p (from, setcc, jump)
+      && !modified_between_p (src, setcc, jump))
+    {
+      rtx setcc_src;
+      rtx setcc_set = single_set (setcc);
+      rtx setcc_note = find_reg_equal_equiv_note (setcc);
+      setcc_src = (setcc_note && GET_CODE (XEXP (setcc_note, 0)) != EXPR_LIST)
+               ? XEXP (setcc_note, 0) : SET_SRC (setcc_set);
+      set_src = simplify_replace_rtx (set_src, SET_DEST (setcc_set),
+                                     setcc_src);
     }
   else
-    {
-      /* Pattern is computed more than once.
-        Search backwards from this insn to see how many of these
-        computations actually reach this insn.  */
-      struct occr *occr;
-      rtx insn_computes_expr = NULL;
-      int can_reach = 0;
+    setcc = NULL_RTX;
 
-      for (occr = expr->avail_occr; occr != NULL; occr = occr->next)
-       {
-         if (BLOCK_FOR_INSN (occr->insn) == bb)
-           {
-             /* The expression is generated in this block.
-                The only time we care about this is when the expression
-                is generated later in the block [and thus there's a loop].
-                We let the normal cse pass handle the other cases.  */
-             if (INSN_CUID (insn) < INSN_CUID (occr->insn)
-                 && expr_reaches_here_p (occr, expr, bb, 1))
-               {
-                 can_reach++;
-                 if (can_reach > 1)
-                   return NULL;
+  new = simplify_replace_rtx (set_src, from, src);
 
-                 insn_computes_expr = occr->insn;
-               }
-           }
-         else if (expr_reaches_here_p (occr, expr, bb, 0))
-           {
-             can_reach++;
-             if (can_reach > 1)
-               return NULL;
+  /* If no simplification can be made, then try the next register.  */
+  if (rtx_equal_p (new, SET_SRC (set)))
+    return 0;
 
-             insn_computes_expr = occr->insn;
-           }
+  /* If this is now a no-op delete it, otherwise this must be a valid insn.  */
+  if (new == pc_rtx)
+    delete_insn (jump);
+  else
+    {
+      /* Ensure the value computed inside the jump insn to be equivalent
+         to one computed by setcc.  */
+      if (setcc && modified_in_p (new, setcc))
+       return 0;
+      if (! validate_change (jump, &SET_SRC (set), new, 0))
+       {
+         /* When (some) constants are not valid in a comparison, and there
+            are two registers to be replaced by constants before the entire
+            comparison can be folded into a constant, we need to keep
+            intermediate information in REG_EQUAL notes.  For targets with
+            separate compare insns, such notes are added by try_replace_reg.
+            When we have a combined compare-and-branch instruction, however,
+            we need to attach a note to the branch itself to make this
+            optimization work.  */
+
+         if (!rtx_equal_p (new, note_src))
+           set_unique_reg_note (jump, REG_EQUAL, copy_rtx (new));
+         return 0;
        }
 
-      if (insn_computes_expr == NULL)
-       abort ();
-
-      return insn_computes_expr;
-    }
-}
+      /* Remove REG_EQUAL note after simplification.  */
+      if (note_src)
+       remove_note (jump, note);
 
-/* Return nonzero if the definition in DEF_INSN can reach INSN.
-   Only called by can_disregard_other_sets.  */
+      /* If this has turned into an unconditional jump,
+        then put a barrier after it so that the unreachable
+        code will be deleted.  */
+      if (GET_CODE (SET_SRC (set)) == LABEL_REF)
+       emit_barrier_after (jump);
+     }
 
-static int
-def_reaches_here_p (insn, def_insn)
-     rtx insn, def_insn;
-{
-  rtx reg;
+#ifdef HAVE_cc0
+  /* Delete the cc0 setter.  */
+  if (setcc != NULL && CC0_P (SET_DEST (single_set (setcc))))
+    delete_insn (setcc);
+#endif
 
-  if (TEST_BIT (reaching_defs[BLOCK_NUM (insn)], INSN_CUID (def_insn)))
-    return 1;
+  run_jump_opt_after_gcse = 1;
 
-  if (BLOCK_NUM (insn) == BLOCK_NUM (def_insn))
+  const_prop_count++;
+  if (gcse_file != NULL)
     {
-      if (INSN_CUID (def_insn) < INSN_CUID (insn))
-       {
-         if (GET_CODE (PATTERN (def_insn)) == PARALLEL)
-           return 1;
-         else if (GET_CODE (PATTERN (def_insn)) == CLOBBER)
-           reg = XEXP (PATTERN (def_insn), 0);
-         else if (GET_CODE (PATTERN (def_insn)) == SET)
-           reg = SET_DEST (PATTERN (def_insn));
-         else
-           abort ();
-
-         return ! reg_set_between_p (reg, NEXT_INSN (def_insn), insn);
-       }
-      else
-       return 0;
+      fprintf (gcse_file,
+              "CONST-PROP: Replacing reg %d in jump_insn %d with constant ",
+              REGNO (from), INSN_UID (jump));
+      print_rtl (gcse_file, src);
+      fprintf (gcse_file, "\n");
     }
+  purge_dead_edges (bb);
 
-  return 0;
+  return 1;
 }
 
-/* Return nonzero if *ADDR_THIS_REG can only have one value at INSN.  The
-   value returned is the number of definitions that reach INSN.  Returning a
-   value of zero means that [maybe] more than one definition reaches INSN and
-   the caller can't perform whatever optimization it is trying.  i.e. it is
-   always safe to return zero.  */
-
-static int
-can_disregard_other_sets (addr_this_reg, insn, for_combine)
-     struct reg_set **addr_this_reg;
-     rtx insn;
-     int for_combine;
+static bool
+constprop_register (rtx insn, rtx from, rtx to, int alter_jumps)
 {
-  int number_of_reaching_defs = 0;
-  struct reg_set *this_reg;
-
-  for (this_reg = *addr_this_reg; this_reg != 0; this_reg = this_reg->next)
-    if (def_reaches_here_p (insn, this_reg->insn))
-      {
-       number_of_reaching_defs++;
-       /* Ignore parallels for now.  */
-       if (GET_CODE (PATTERN (this_reg->insn)) == PARALLEL)
-         return 0;
+  rtx sset;
 
-       if (!for_combine
-           && (GET_CODE (PATTERN (this_reg->insn)) == CLOBBER
-               || ! rtx_equal_p (SET_SRC (PATTERN (this_reg->insn)),
-                                 SET_SRC (PATTERN (insn)))))
-         /* A setting of the reg to a different value reaches INSN.  */
-         return 0;
+  /* Check for reg or cc0 setting instructions followed by
+     conditional branch instructions first.  */
+  if (alter_jumps
+      && (sset = single_set (insn)) != NULL
+      && NEXT_INSN (insn)
+      && any_condjump_p (NEXT_INSN (insn)) && onlyjump_p (NEXT_INSN (insn)))
+    {
+      rtx dest = SET_DEST (sset);
+      if ((REG_P (dest) || CC0_P (dest))
+         && cprop_jump (BLOCK_FOR_INSN (insn), insn, NEXT_INSN (insn), from, to))
+       return 1;
+    }
 
-       if (number_of_reaching_defs > 1)
-         {
-           /* If in this setting the value the register is being set to is
-              equal to the previous value the register was set to and this
-              setting reaches the insn we are trying to do the substitution
-              on then we are ok.  */
-           if (GET_CODE (PATTERN (this_reg->insn)) == CLOBBER)
-             return 0;
-           else if (! rtx_equal_p (SET_SRC (PATTERN (this_reg->insn)),
-                                   SET_SRC (PATTERN (insn))))
-             return 0;
-         }
+  /* Handle normal insns next.  */
+  if (GET_CODE (insn) == INSN
+      && try_replace_reg (from, to, insn))
+    return 1;
 
-       *addr_this_reg = this_reg;
-      }
+  /* Try to propagate a CONST_INT into a conditional jump.
+     We're pretty specific about what we will handle in this
+     code, we can extend this as necessary over time.
 
-  return number_of_reaching_defs;
+     Right now the insn in question must look like
+     (set (pc) (if_then_else ...))  */
+  else if (alter_jumps && any_condjump_p (insn) && onlyjump_p (insn))
+    return cprop_jump (BLOCK_FOR_INSN (insn), NULL, insn, from, to);
+  return 0;
 }
 
-/* Expression computed by insn is available and the substitution is legal,
-   so try to perform the substitution.
-
-   The result is nonzero if any changes were made.  */
+/* Perform constant and copy propagation on INSN.
+   The result is nonzero if a change was made.  */
 
 static int
-handle_avail_expr (insn, expr)
-     rtx insn;
-     struct expr *expr;
-{
-  rtx pat, insn_computes_expr, expr_set;
-  rtx to;
-  struct reg_set *this_reg;
-  int found_setting, use_src;
+cprop_insn (rtx insn, int alter_jumps)
+{
+  struct reg_use *reg_used;
   int changed = 0;
+  rtx note;
 
-  /* We only handle the case where one computation of the expression
-     reaches this instruction.  */
-  insn_computes_expr = computing_insn (expr, insn);
-  if (insn_computes_expr == NULL)
+  if (!INSN_P (insn))
     return 0;
-  expr_set = single_set (insn_computes_expr);
-  if (!expr_set)
-    abort ();
-
-  found_setting = 0;
-  use_src = 0;
-
-  /* At this point we know only one computation of EXPR outside of this
-     block reaches this insn.  Now try to find a register that the
-     expression is computed into.  */
-  if (GET_CODE (SET_SRC (expr_set)) == REG)
-    {
-      /* This is the case when the available expression that reaches
-        here has already been handled as an available expression.  */
-      unsigned int regnum_for_replacing
-       = REGNO (SET_SRC (expr_set));
-
-      /* If the register was created by GCSE we can't use `reg_set_table',
-        however we know it's set only once.  */
-      if (regnum_for_replacing >= max_gcse_regno
-         /* If the register the expression is computed into is set only once,
-            or only one set reaches this insn, we can use it.  */
-         || (((this_reg = reg_set_table[regnum_for_replacing]),
-              this_reg->next == NULL)
-             || can_disregard_other_sets (&this_reg, insn, 0)))
-       {
-         use_src = 1;
-         found_setting = 1;
-       }
-    }
-
-  if (!found_setting)
-    {
-      unsigned int regnum_for_replacing
-       = REGNO (SET_DEST (expr_set));
 
-      /* This shouldn't happen.  */
-      if (regnum_for_replacing >= max_gcse_regno)
-       abort ();
+  reg_use_count = 0;
+  note_uses (&PATTERN (insn), find_used_regs, NULL);
 
-      this_reg = reg_set_table[regnum_for_replacing];
+  note = find_reg_equal_equiv_note (insn);
 
-      /* If the register the expression is computed into is set only once,
-        or only one set reaches this insn, use it.  */
-      if (this_reg->next == NULL
-         || can_disregard_other_sets (&this_reg, insn, 0))
-       found_setting = 1;
-    }
+  /* We may win even when propagating constants into notes.  */
+  if (note)
+    find_used_regs (&XEXP (note, 0), NULL);
 
-  if (found_setting)
+  for (reg_used = &reg_use_table[0]; reg_use_count > 0;
+       reg_used++, reg_use_count--)
     {
-      pat = PATTERN (insn);
-      if (use_src)
-       to = SET_SRC (expr_set);
-      else
-       to = SET_DEST (expr_set);
-      changed = validate_change (insn, &SET_SRC (pat), to, 0);
+      unsigned int regno = REGNO (reg_used->reg_rtx);
+      rtx pat, src;
+      struct expr *set;
 
-      /* We should be able to ignore the return code from validate_change but
-        to play it safe we check.  */
-      if (changed)
-       {
-         gcse_subst_count++;
-         if (gcse_file != NULL)
-           {
-             fprintf (gcse_file, "GCSE: Replacing the source in insn %d with",
-                      INSN_UID (insn));
-             fprintf (gcse_file, " reg %d %s insn %d\n",
-                      REGNO (to), use_src ? "from" : "set in",
-                      INSN_UID (insn_computes_expr));
-           }
-       }
-    }
+      /* Ignore registers created by GCSE.
+        We do this because ...  */
+      if (regno >= max_gcse_regno)
+       continue;
 
-  /* The register that the expr is computed into is set more than once.  */
-  else if (1 /*expensive_op(this_pattrn->op) && do_expensive_gcse)*/)
-    {
-      /* Insert an insn after insnx that copies the reg set in insnx
-        into a new pseudo register call this new register REGN.
-        From insnb until end of basic block or until REGB is set
-        replace all uses of REGB with REGN.  */
-      rtx new_insn;
+      /* If the register has already been set in this block, there's
+        nothing we can do.  */
+      if (! oprs_not_set_p (reg_used->reg_rtx, insn))
+       continue;
 
-      to = gen_reg_rtx (GET_MODE (SET_DEST (expr_set)));
+      /* Find an assignment that sets reg_used and is available
+        at the start of the block.  */
+      set = find_avail_set (regno, insn);
+      if (! set)
+       continue;
 
-      /* Generate the new insn.  */
-      /* ??? If the change fails, we return 0, even though we created
-        an insn.  I think this is ok.  */
-      new_insn
-       = emit_insn_after (gen_rtx_SET (VOIDmode, to,
-                                       SET_DEST (expr_set)),
-                          insn_computes_expr);
+      pat = set->expr;
+      /* ??? We might be able to handle PARALLELs.  Later.  */
+      if (GET_CODE (pat) != SET)
+       abort ();
 
-      /* Keep register set table up to date.  */
-      record_one_set (REGNO (to), new_insn);
+      src = SET_SRC (pat);
 
-      gcse_create_count++;
-      if (gcse_file != NULL)
+      /* Constant propagation.  */
+      if (gcse_constant_p (src))
        {
-         fprintf (gcse_file, "GCSE: Creating insn %d to copy value of reg %d",
-                  INSN_UID (NEXT_INSN (insn_computes_expr)),
-                  REGNO (SET_SRC (PATTERN (NEXT_INSN (insn_computes_expr)))));
-         fprintf (gcse_file, ", computed in insn %d,\n",
-                  INSN_UID (insn_computes_expr));
-         fprintf (gcse_file, "      into newly allocated reg %d\n",
-                  REGNO (to));
+          if (constprop_register (insn, reg_used->reg_rtx, src, alter_jumps))
+           {
+             changed = 1;
+             const_prop_count++;
+             if (gcse_file != NULL)
+               {
+                 fprintf (gcse_file, "GLOBAL CONST-PROP: Replacing reg %d in ", regno);
+                 fprintf (gcse_file, "insn %d with constant ", INSN_UID (insn));
+                 print_rtl (gcse_file, src);
+                 fprintf (gcse_file, "\n");
+               }
+             if (INSN_DELETED_P (insn))
+               return 1;
+           }
        }
-
-      pat = PATTERN (insn);
-
-      /* Do register replacement for INSN.  */
-      changed = validate_change (insn, &SET_SRC (pat),
-                                SET_DEST (PATTERN
-                                          (NEXT_INSN (insn_computes_expr))),
-                                0);
-
-      /* We should be able to ignore the return code from validate_change but
-        to play it safe we check.  */
-      if (changed)
+      else if (REG_P (src)
+              && REGNO (src) >= FIRST_PSEUDO_REGISTER
+              && REGNO (src) != regno)
        {
-         gcse_subst_count++;
-         if (gcse_file != NULL)
+         if (try_replace_reg (reg_used->reg_rtx, src, insn))
            {
-             fprintf (gcse_file,
-                      "GCSE: Replacing the source in insn %d with reg %d ",
-                      INSN_UID (insn),
-                      REGNO (SET_DEST (PATTERN (NEXT_INSN
-                                                (insn_computes_expr)))));
-             fprintf (gcse_file, "set in insn %d\n",
-                      INSN_UID (insn_computes_expr));
+             changed = 1;
+             copy_prop_count++;
+             if (gcse_file != NULL)
+               {
+                 fprintf (gcse_file, "GLOBAL COPY-PROP: Replacing reg %d in insn %d",
+                          regno, INSN_UID (insn));
+                 fprintf (gcse_file, " with reg %d\n", REGNO (src));
+               }
+
+             /* The original insn setting reg_used may or may not now be
+                deletable.  We leave the deletion to flow.  */
+             /* FIXME: If it turns out that the insn isn't deletable,
+                then we may have unnecessarily extended register lifetimes
+                and made things worse.  */
            }
        }
     }
@@ -3558,773 +3405,62 @@ handle_avail_expr (insn, expr)
   return changed;
 }
 
-/* Perform classic GCSE.  This is called by one_classic_gcse_pass after all
-   the dataflow analysis has been done.
-
-   The result is nonzero if a change was made.  */
+/* Like find_used_regs, but avoid recording uses that appear in
+   input-output contexts such as zero_extract or pre_dec.  This
+   restricts the cases we consider to those for which local cprop
+   can legitimately make replacements.  */
 
-static int
-classic_gcse ()
+static void
+local_cprop_find_used_regs (rtx *xptr, void *data)
 {
-  int changed;
-  rtx insn;
-  basic_block bb;
-
-  /* Note we start at block 1.  */
+  rtx x = *xptr;
 
-  if (ENTRY_BLOCK_PTR->next_bb == EXIT_BLOCK_PTR)
-    return 0;
+  if (x == 0)
+    return;
 
-  changed = 0;
-  FOR_BB_BETWEEN (bb, ENTRY_BLOCK_PTR->next_bb->next_bb, EXIT_BLOCK_PTR, next_bb)
+  switch (GET_CODE (x))
     {
-      /* Reset tables used to keep track of what's still valid [since the
-        start of the block].  */
-      reset_opr_set_tables ();
+    case ZERO_EXTRACT:
+    case SIGN_EXTRACT:
+    case STRICT_LOW_PART:
+      return;
 
-      for (insn = bb->head;
-          insn != NULL && insn != NEXT_INSN (bb->end);
-          insn = NEXT_INSN (insn))
-       {
-         /* Is insn of form (set (pseudo-reg) ...)?  */
-         if (GET_CODE (insn) == INSN
-             && GET_CODE (PATTERN (insn)) == SET
-             && GET_CODE (SET_DEST (PATTERN (insn))) == REG
-             && REGNO (SET_DEST (PATTERN (insn))) >= FIRST_PSEUDO_REGISTER)
-           {
-             rtx pat = PATTERN (insn);
-             rtx src = SET_SRC (pat);
-             struct expr *expr;
+    case PRE_DEC:
+    case PRE_INC:
+    case POST_DEC:
+    case POST_INC:
+    case PRE_MODIFY:
+    case POST_MODIFY:
+      /* Can only legitimately appear this early in the context of
+        stack pushes for function arguments, but handle all of the
+        codes nonetheless.  */
+      return;
 
-             if (want_to_gcse_p (src)
-                 /* Is the expression recorded?  */
-                 && ((expr = lookup_expr (src, &expr_hash_table)) != NULL)
-                 /* Is the expression available [at the start of the
-                    block]?  */
-                 && TEST_BIT (ae_in[bb->index], expr->bitmap_index)
-                 /* Are the operands unchanged since the start of the
-                    block?  */
-                 && oprs_not_set_p (src, insn))
-               changed |= handle_avail_expr (insn, expr);
-           }
+    case SUBREG:
+      /* Setting a subreg of a register larger than word_mode leaves
+        the non-written words unchanged.  */
+      if (GET_MODE_BITSIZE (GET_MODE (SUBREG_REG (x))) > BITS_PER_WORD)
+       return;
+      break;
 
-         /* Keep track of everything modified by this insn.  */
-         /* ??? Need to be careful w.r.t. mods done to INSN.  */
-         if (INSN_P (insn))
-           mark_oprs_set (insn);
-       }
+    default:
+      break;
     }
 
-  return changed;
+  find_used_regs (xptr, data);
 }
 
-/* Top level routine to perform one classic GCSE pass.
-
-   Return nonzero if a change was made.  */
+/* LIBCALL_SP is a zero-terminated array of insns at the end of a libcall;
+   their REG_EQUAL notes need updating.  */
 
-static int
-one_classic_gcse_pass (pass)
-     int pass;
+static bool
+do_local_cprop (rtx x, rtx insn, int alter_jumps, rtx *libcall_sp)
 {
-  int changed = 0;
-
-  gcse_subst_count = 0;
-  gcse_create_count = 0;
-
-  alloc_hash_table (max_cuid, &expr_hash_table, 0);
-  alloc_rd_mem (last_basic_block, max_cuid);
-  compute_hash_table (&expr_hash_table);
-  if (gcse_file)
-    dump_hash_table (gcse_file, "Expression", &expr_hash_table);
-
-  if (expr_hash_table.n_elems > 0)
-    {
-      compute_kill_rd ();
-      compute_rd ();
-      alloc_avail_expr_mem (last_basic_block, expr_hash_table.n_elems);
-      compute_ae_gen (&expr_hash_table);
-      compute_ae_kill (ae_gen, ae_kill, &expr_hash_table);
-      compute_available (ae_gen, ae_kill, ae_out, ae_in);
-      changed = classic_gcse ();
-      free_avail_expr_mem ();
-    }
-
-  free_rd_mem ();
-  free_hash_table (&expr_hash_table);
-
-  if (gcse_file)
-    {
-      fprintf (gcse_file, "\n");
-      fprintf (gcse_file, "GCSE of %s, pass %d: %d bytes needed, %d substs,",
-              current_function_name, pass, bytes_used, gcse_subst_count);
-      fprintf (gcse_file, "%d insns created\n", gcse_create_count);
-    }
-
-  return changed;
-}
-\f
-/* Compute copy/constant propagation working variables.  */
-
-/* Local properties of assignments.  */
-static sbitmap *cprop_pavloc;
-static sbitmap *cprop_absaltered;
-
-/* Global properties of assignments (computed from the local properties).  */
-static sbitmap *cprop_avin;
-static sbitmap *cprop_avout;
-
-/* Allocate vars used for copy/const propagation.  N_BLOCKS is the number of
-   basic blocks.  N_SETS is the number of sets.  */
-
-static void
-alloc_cprop_mem (n_blocks, n_sets)
-     int n_blocks, n_sets;
-{
-  cprop_pavloc = sbitmap_vector_alloc (n_blocks, n_sets);
-  cprop_absaltered = sbitmap_vector_alloc (n_blocks, n_sets);
-
-  cprop_avin = sbitmap_vector_alloc (n_blocks, n_sets);
-  cprop_avout = sbitmap_vector_alloc (n_blocks, n_sets);
-}
-
-/* Free vars used by copy/const propagation.  */
-
-static void
-free_cprop_mem ()
-{
-  sbitmap_vector_free (cprop_pavloc);
-  sbitmap_vector_free (cprop_absaltered);
-  sbitmap_vector_free (cprop_avin);
-  sbitmap_vector_free (cprop_avout);
-}
-
-/* For each block, compute whether X is transparent.  X is either an
-   expression or an assignment [though we don't care which, for this context
-   an assignment is treated as an expression].  For each block where an
-   element of X is modified, set (SET_P == 1) or reset (SET_P == 0) the INDX
-   bit in BMAP.  */
-
-static void
-compute_transp (x, indx, bmap, set_p)
-     rtx x;
-     int indx;
-     sbitmap *bmap;
-     int set_p;
-{
-  int i, j;
-  basic_block bb;
-  enum rtx_code code;
-  reg_set *r;
-  const char *fmt;
-
-  /* repeat is used to turn tail-recursion into iteration since GCC
-     can't do it when there's no return value.  */
- repeat:
-
-  if (x == 0)
-    return;
-
-  code = GET_CODE (x);
-  switch (code)
-    {
-    case REG:
-      if (set_p)
-       {
-         if (REGNO (x) < FIRST_PSEUDO_REGISTER)
-           {
-             FOR_EACH_BB (bb)
-               if (TEST_BIT (reg_set_in_block[bb->index], REGNO (x)))
-                 SET_BIT (bmap[bb->index], indx);
-           }
-         else
-           {
-             for (r = reg_set_table[REGNO (x)]; r != NULL; r = r->next)
-               SET_BIT (bmap[BLOCK_NUM (r->insn)], indx);
-           }
-       }
-      else
-       {
-         if (REGNO (x) < FIRST_PSEUDO_REGISTER)
-           {
-             FOR_EACH_BB (bb)
-               if (TEST_BIT (reg_set_in_block[bb->index], REGNO (x)))
-                 RESET_BIT (bmap[bb->index], indx);
-           }
-         else
-           {
-             for (r = reg_set_table[REGNO (x)]; r != NULL; r = r->next)
-               RESET_BIT (bmap[BLOCK_NUM (r->insn)], indx);
-           }
-       }
-
-      return;
-
-    case MEM:
-      FOR_EACH_BB (bb)
-       {
-         rtx list_entry = canon_modify_mem_list[bb->index];
-
-         while (list_entry)
-           {
-             rtx dest, dest_addr;
-
-             if (GET_CODE (XEXP (list_entry, 0)) == CALL_INSN)
-               {
-                 if (set_p)
-                   SET_BIT (bmap[bb->index], indx);
-                 else
-                   RESET_BIT (bmap[bb->index], indx);
-                 break;
-               }
-             /* LIST_ENTRY must be an INSN of some kind that sets memory.
-                Examine each hunk of memory that is modified.  */
-
-             dest = XEXP (list_entry, 0);
-             list_entry = XEXP (list_entry, 1);
-             dest_addr = XEXP (list_entry, 0);
-
-             if (canon_true_dependence (dest, GET_MODE (dest), dest_addr,
-                                        x, rtx_addr_varies_p))
-               {
-                 if (set_p)
-                   SET_BIT (bmap[bb->index], indx);
-                 else
-                   RESET_BIT (bmap[bb->index], indx);
-                 break;
-               }
-             list_entry = XEXP (list_entry, 1);
-           }
-       }
-
-      x = XEXP (x, 0);
-      goto repeat;
-
-    case PC:
-    case CC0: /*FIXME*/
-    case CONST:
-    case CONST_INT:
-    case CONST_DOUBLE:
-    case CONST_VECTOR:
-    case SYMBOL_REF:
-    case LABEL_REF:
-    case ADDR_VEC:
-    case ADDR_DIFF_VEC:
-      return;
-
-    default:
-      break;
-    }
-
-  for (i = GET_RTX_LENGTH (code) - 1, fmt = GET_RTX_FORMAT (code); i >= 0; i--)
-    {
-      if (fmt[i] == 'e')
-       {
-         /* If we are about to do the last recursive call
-            needed at this level, change it into iteration.
-            This function is called enough to be worth it.  */
-         if (i == 0)
-           {
-             x = XEXP (x, i);
-             goto repeat;
-           }
-
-         compute_transp (XEXP (x, i), indx, bmap, set_p);
-       }
-      else if (fmt[i] == 'E')
-       for (j = 0; j < XVECLEN (x, i); j++)
-         compute_transp (XVECEXP (x, i, j), indx, bmap, set_p);
-    }
-}
-
-/* Top level routine to do the dataflow analysis needed by copy/const
-   propagation.  */
-
-static void
-compute_cprop_data ()
-{
-  compute_local_properties (cprop_absaltered, cprop_pavloc, NULL, &set_hash_table);
-  compute_available (cprop_pavloc, cprop_absaltered,
-                    cprop_avout, cprop_avin);
-}
-\f
-/* Copy/constant propagation.  */
-
-/* Maximum number of register uses in an insn that we handle.  */
-#define MAX_USES 8
-
-/* Table of uses found in an insn.
-   Allocated statically to avoid alloc/free complexity and overhead.  */
-static struct reg_use reg_use_table[MAX_USES];
-
-/* Index into `reg_use_table' while building it.  */
-static int reg_use_count;
-
-/* Set up a list of register numbers used in INSN.  The found uses are stored
-   in `reg_use_table'.  `reg_use_count' is initialized to zero before entry,
-   and contains the number of uses in the table upon exit.
-
-   ??? If a register appears multiple times we will record it multiple times.
-   This doesn't hurt anything but it will slow things down.  */
-
-static void
-find_used_regs (xptr, data)
-     rtx *xptr;
-     void *data ATTRIBUTE_UNUSED;
-{
-  int i, j;
-  enum rtx_code code;
-  const char *fmt;
-  rtx x = *xptr;
-
-  /* repeat is used to turn tail-recursion into iteration since GCC
-     can't do it when there's no return value.  */
- repeat:
-  if (x == 0)
-    return;
-
-  code = GET_CODE (x);
-  if (REG_P (x))
-    {
-      if (reg_use_count == MAX_USES)
-       return;
-
-      reg_use_table[reg_use_count].reg_rtx = x;
-      reg_use_count++;
-    }
-
-  /* Recursively scan the operands of this expression.  */
-
-  for (i = GET_RTX_LENGTH (code) - 1, fmt = GET_RTX_FORMAT (code); i >= 0; i--)
-    {
-      if (fmt[i] == 'e')
-       {
-         /* If we are about to do the last recursive call
-            needed at this level, change it into iteration.
-            This function is called enough to be worth it.  */
-         if (i == 0)
-           {
-             x = XEXP (x, 0);
-             goto repeat;
-           }
-
-         find_used_regs (&XEXP (x, i), data);
-       }
-      else if (fmt[i] == 'E')
-       for (j = 0; j < XVECLEN (x, i); j++)
-         find_used_regs (&XVECEXP (x, i, j), data);
-    }
-}
-
-/* Try to replace all non-SET_DEST occurrences of FROM in INSN with TO.
-   Returns nonzero is successful.  */
-
-static int
-try_replace_reg (from, to, insn)
-     rtx from, to, insn;
-{
-  rtx note = find_reg_equal_equiv_note (insn);
-  rtx src = 0;
-  int success = 0;
-  rtx set = single_set (insn);
-
-  validate_replace_src_group (from, to, insn);
-  if (num_changes_pending () && apply_change_group ())
-    success = 1;
-
-  if (!success && set && reg_mentioned_p (from, SET_SRC (set)))
-    {
-      /* If above failed and this is a single set, try to simplify the source of
-        the set given our substitution.  We could perhaps try this for multiple
-        SETs, but it probably won't buy us anything.  */
-      src = simplify_replace_rtx (SET_SRC (set), from, to);
-
-      if (!rtx_equal_p (src, SET_SRC (set))
-         && validate_change (insn, &SET_SRC (set), src, 0))
-       success = 1;
-
-      /* If we've failed to do replacement, have a single SET, and don't already
-        have a note, add a REG_EQUAL note to not lose information.  */
-      if (!success && note == 0 && set != 0)
-       note = set_unique_reg_note (insn, REG_EQUAL, copy_rtx (src));
-    }
-
-  /* If there is already a NOTE, update the expression in it with our
-     replacement.  */
-  else if (note != 0)
-    XEXP (note, 0) = simplify_replace_rtx (XEXP (note, 0), from, to);
-
-  /* REG_EQUAL may get simplified into register.
-     We don't allow that. Remove that note. This code ought
-     not to happen, because previous code ought to synthesize
-     reg-reg move, but be on the safe side.  */
-  if (note && REG_P (XEXP (note, 0)))
-    remove_note (insn, note);
-
-  return success;
-}
-
-/* Find a set of REGNOs that are available on entry to INSN's block.  Returns
-   NULL no such set is found.  */
-
-static struct expr *
-find_avail_set (regno, insn)
-     int regno;
-     rtx insn;
-{
-  /* SET1 contains the last set found that can be returned to the caller for
-     use in a substitution.  */
-  struct expr *set1 = 0;
-
-  /* Loops are not possible here.  To get a loop we would need two sets
-     available at the start of the block containing INSN.  ie we would
-     need two sets like this available at the start of the block:
-
-       (set (reg X) (reg Y))
-       (set (reg Y) (reg X))
-
-     This can not happen since the set of (reg Y) would have killed the
-     set of (reg X) making it unavailable at the start of this block.  */
-  while (1)
-    {
-      rtx src;
-      struct expr *set = lookup_set (regno, &set_hash_table);
-
-      /* Find a set that is available at the start of the block
-        which contains INSN.  */
-      while (set)
-       {
-         if (TEST_BIT (cprop_avin[BLOCK_NUM (insn)], set->bitmap_index))
-           break;
-         set = next_set (regno, set);
-       }
-
-      /* If no available set was found we've reached the end of the
-        (possibly empty) copy chain.  */
-      if (set == 0)
-       break;
-
-      if (GET_CODE (set->expr) != SET)
-       abort ();
-
-      src = SET_SRC (set->expr);
-
-      /* We know the set is available.
-        Now check that SRC is ANTLOC (i.e. none of the source operands
-        have changed since the start of the block).
-
-         If the source operand changed, we may still use it for the next
-         iteration of this loop, but we may not use it for substitutions.  */
-
-      if (CONSTANT_P (src) || oprs_not_set_p (src, insn))
-       set1 = set;
-
-      /* If the source of the set is anything except a register, then
-        we have reached the end of the copy chain.  */
-      if (GET_CODE (src) != REG)
-       break;
-
-      /* Follow the copy chain, ie start another iteration of the loop
-        and see if we have an available copy into SRC.  */
-      regno = REGNO (src);
-    }
-
-  /* SET1 holds the last set that was available and anticipatable at
-     INSN.  */
-  return set1;
-}
-
-/* Subroutine of cprop_insn that tries to propagate constants into
-   JUMP_INSNS.  JUMP must be a conditional jump.  If SETCC is non-NULL
-   it is the instruction that immediately precedes JUMP, and must be a
-   single SET of a register.  FROM is what we will try to replace,
-   SRC is the constant we will try to substitute for it.  Returns nonzero
-   if a change was made.  */
-
-static int
-cprop_jump (bb, setcc, jump, from, src)
-     basic_block bb;
-     rtx setcc;
-     rtx jump;
-     rtx from;
-     rtx src;
-{
-  rtx new, new_set;
-  rtx set = pc_set (jump);
-
-  /* First substitute in the INSN condition as the SET_SRC of the JUMP,
-     then substitute that given values in this expanded JUMP.  */
-  if (setcc != NULL
-      && !modified_between_p (from, setcc, jump)
-      && !modified_between_p (src, setcc, jump))
-    {
-      rtx setcc_set = single_set (setcc);
-      rtx note = find_reg_equal_equiv_note (setcc);
-      /* Use REG_EQUAL note if available.  */
-      rtx setcc_set_src = (note == 0) ? SET_SRC (setcc_set) : XEXP (note, 0);
-
-      new_set = simplify_replace_rtx (SET_SRC (set),
-                                     SET_DEST (setcc_set),
-                                     setcc_set_src);
-    }
-  else
-    new_set = set;
-
-  /* If NEW_SET is simplified down to either a label or a no-op, we
-     don't have to replace FROM with SRC, but we still have to either
-     turn JUMP to an unconditional branch or remove the no-op.  This
-     can happen if JUMP is simplified using the REG_EQUAL note in
-     SETCC.  */
-  if (GET_CODE (new_set) == LABEL_REF || new_set == pc_rtx)
-    new = new_set;
-  else
-    {
-      new = simplify_replace_rtx (new_set, from, src);
-
-      /* If no simplification can be made, then try the next
-        register.  */
-      if (rtx_equal_p (new, new_set) || rtx_equal_p (new, SET_SRC (set)))
-       return 0;
-    }
-
-  /* If this is now a no-op delete it, otherwise this must be a valid insn.  */
-  if (new == pc_rtx)
-    delete_insn (jump);
-  else
-    {
-      /* Ensure the value computed inside the jump insn to be equivalent
-         to one computed by setcc.  */
-      if (setcc 
-         && modified_in_p (new, setcc))
-       return 0;
-      if (! validate_change (jump, &SET_SRC (set), new, 0))
-       return 0;
-
-      /* If this has turned into an unconditional jump,
-        then put a barrier after it so that the unreachable
-        code will be deleted.  */
-      if (GET_CODE (SET_SRC (set)) == LABEL_REF)
-       emit_barrier_after (jump);
-     }
-
-#ifdef HAVE_cc0
-  /* Delete the cc0 setter.  */
-  if (setcc != NULL && CC0_P (SET_DEST (single_set (setcc))))
-    delete_insn (setcc);
-#endif
-
-  run_jump_opt_after_gcse = 1;
-
-  const_prop_count++;
-  if (gcse_file != NULL)
-    {
-      fprintf (gcse_file,
-              "CONST-PROP: Replacing reg %d in jump_insn %d with constant ",
-              REGNO (from), INSN_UID (jump));
-      print_rtl (gcse_file, src);
-      fprintf (gcse_file, "\n");
-    }
-  purge_dead_edges (bb);
-
-  return 1;
-}
-
-static bool
-constprop_register (insn, from, to, alter_jumps)
-     rtx insn;
-     rtx from;
-     rtx to;
-     int alter_jumps;
-{
-  rtx sset;
-
-  /* Check for reg or cc0 setting instructions followed by
-     conditional branch instructions first.  */
-  if (alter_jumps
-      && (sset = single_set (insn)) != NULL
-      && NEXT_INSN (insn)
-      && any_condjump_p (NEXT_INSN (insn)) && onlyjump_p (NEXT_INSN (insn)))
-    {
-      rtx dest = SET_DEST (sset);
-      if ((REG_P (dest) || CC0_P (dest))
-         && cprop_jump (BLOCK_FOR_INSN (insn), insn, NEXT_INSN (insn), from, to))
-       return 1;
-    }
-
-  /* Handle normal insns next.  */
-  if (GET_CODE (insn) == INSN
-      && try_replace_reg (from, to, insn))
-    return 1;
-
-  /* Try to propagate a CONST_INT into a conditional jump.
-     We're pretty specific about what we will handle in this
-     code, we can extend this as necessary over time.
-
-     Right now the insn in question must look like
-     (set (pc) (if_then_else ...))  */
-  else if (alter_jumps && any_condjump_p (insn) && onlyjump_p (insn))
-    return cprop_jump (BLOCK_FOR_INSN (insn), NULL, insn, from, to);
-  return 0;
-}
-
-/* Perform constant and copy propagation on INSN.
-   The result is nonzero if a change was made.  */
-
-static int
-cprop_insn (insn, alter_jumps)
-     rtx insn;
-     int alter_jumps;
-{
-  struct reg_use *reg_used;
-  int changed = 0;
-  rtx note;
-
-  if (!INSN_P (insn))
-    return 0;
-
-  reg_use_count = 0;
-  note_uses (&PATTERN (insn), find_used_regs, NULL);
-
-  note = find_reg_equal_equiv_note (insn);
-
-  /* We may win even when propagating constants into notes.  */
-  if (note)
-    find_used_regs (&XEXP (note, 0), NULL);
-
-  for (reg_used = &reg_use_table[0]; reg_use_count > 0;
-       reg_used++, reg_use_count--)
-    {
-      unsigned int regno = REGNO (reg_used->reg_rtx);
-      rtx pat, src;
-      struct expr *set;
-
-      /* Ignore registers created by GCSE.
-        We do this because ...  */
-      if (regno >= max_gcse_regno)
-       continue;
-
-      /* If the register has already been set in this block, there's
-        nothing we can do.  */
-      if (! oprs_not_set_p (reg_used->reg_rtx, insn))
-       continue;
-
-      /* Find an assignment that sets reg_used and is available
-        at the start of the block.  */
-      set = find_avail_set (regno, insn);
-      if (! set)
-       continue;
-
-      pat = set->expr;
-      /* ??? We might be able to handle PARALLELs.  Later.  */
-      if (GET_CODE (pat) != SET)
-       abort ();
-
-      src = SET_SRC (pat);
-
-      /* Constant propagation.  */
-      if (CONSTANT_P (src))
-       {
-          if (constprop_register (insn, reg_used->reg_rtx, src, alter_jumps))
-           {
-             changed = 1;
-             const_prop_count++;
-             if (gcse_file != NULL)
-               {
-                 fprintf (gcse_file, "GLOBAL CONST-PROP: Replacing reg %d in ", regno);
-                 fprintf (gcse_file, "insn %d with constant ", INSN_UID (insn));
-                 print_rtl (gcse_file, src);
-                 fprintf (gcse_file, "\n");
-               }
-           }
-       }
-      else if (GET_CODE (src) == REG
-              && REGNO (src) >= FIRST_PSEUDO_REGISTER
-              && REGNO (src) != regno)
-       {
-         if (try_replace_reg (reg_used->reg_rtx, src, insn))
-           {
-             changed = 1;
-             copy_prop_count++;
-             if (gcse_file != NULL)
-               {
-                 fprintf (gcse_file, "GLOBAL COPY-PROP: Replacing reg %d in insn %d",
-                          regno, INSN_UID (insn));
-                 fprintf (gcse_file, " with reg %d\n", REGNO (src));
-               }
-
-             /* The original insn setting reg_used may or may not now be
-                deletable.  We leave the deletion to flow.  */
-             /* FIXME: If it turns out that the insn isn't deletable,
-                then we may have unnecessarily extended register lifetimes
-                and made things worse.  */
-           }
-       }
-    }
-
-  return changed;
-}
-
-/* Like find_used_regs, but avoid recording uses that appear in
-   input-output contexts such as zero_extract or pre_dec.  This
-   restricts the cases we consider to those for which local cprop
-   can legitimately make replacements.  */
-
-static void
-local_cprop_find_used_regs (xptr, data)
-     rtx *xptr;
-     void *data;
-{
-  rtx x = *xptr;
-
-  if (x == 0)
-    return;
-
-  switch (GET_CODE (x))
-    {
-    case ZERO_EXTRACT:
-    case SIGN_EXTRACT:
-    case STRICT_LOW_PART:
-      return;
-
-    case PRE_DEC:
-    case PRE_INC:
-    case POST_DEC:
-    case POST_INC:
-    case PRE_MODIFY:
-    case POST_MODIFY:
-      /* Can only legitimately appear this early in the context of
-        stack pushes for function arguments, but handle all of the
-        codes nonetheless.  */
-      return;
-
-    case SUBREG:
-      /* Setting a subreg of a register larger than word_mode leaves
-        the non-written words unchanged.  */
-      if (GET_MODE_BITSIZE (GET_MODE (SUBREG_REG (x))) > BITS_PER_WORD)
-       return;
-      break;
-
-    default:
-      break;
-    }
-
-  find_used_regs (xptr, data);
-}
-  
-/* LIBCALL_SP is a zero-terminated array of insns at the end of a libcall;
-   their REG_EQUAL notes need updating.  */
-
-static bool
-do_local_cprop (x, insn, alter_jumps, libcall_sp)
-     rtx x;
-     rtx insn;
-     int alter_jumps;
-     rtx *libcall_sp;
-{
-  rtx newreg = NULL, newcnst = NULL;
+  rtx newreg = NULL, newcnst = NULL;
 
   /* Rule out USE instructions and ASM statements as we don't want to
      change the hard registers mentioned.  */
-  if (GET_CODE (x) == REG
+  if (REG_P (x)
       && (REGNO (x) >= FIRST_PSEUDO_REGISTER
           || (GET_CODE (PATTERN (insn)) != USE
              && asm_noperands (PATTERN (insn)) < 0)))
@@ -4342,8 +3478,7 @@ do_local_cprop (x, insn, alter_jumps, libcall_sp)
          if (l->in_libcall)
            continue;
 
-         if (CONSTANT_P (this_rtx)
-             && GET_CODE (this_rtx) != CONSTANT_P_RTX)
+         if (gcse_constant_p (this_rtx))
            newcnst = this_rtx;
          if (REG_P (this_rtx) && REGNO (this_rtx) >= FIRST_PSEUDO_REGISTER
              /* Don't copy propagate if it has attached REG_EQUIV note.
@@ -4352,7 +3487,7 @@ do_local_cprop (x, insn, alter_jumps, libcall_sp)
                 explicitly, it means address of parameter has been taken,
                 so we should not extend the lifetime of the pseudo.  */
              && (!(note = find_reg_note (l->setting_insn, REG_EQUIV, NULL_RTX))
-                 || GET_CODE (XEXP (note, 0)) != MEM))
+                 || ! MEM_P (XEXP (note, 0))))
            newreg = this_rtx;
        }
       if (newcnst && constprop_register (insn, x, newcnst, alter_jumps))
@@ -4398,8 +3533,7 @@ do_local_cprop (x, insn, alter_jumps, libcall_sp)
    replaced with NEWVAL in INSN.  Return true if all substitutions could
    be made.  */
 static bool
-adjust_libcall_notes (oldreg, newval, insn, libcall_sp)
-     rtx oldreg, newval, insn, *libcall_sp;
+adjust_libcall_notes (rtx oldreg, rtx newval, rtx insn, rtx *libcall_sp)
 {
   rtx end;
 
@@ -4435,15 +3569,14 @@ adjust_libcall_notes (oldreg, newval, insn, libcall_sp)
 #define MAX_NESTED_LIBCALLS 9
 
 static void
-local_cprop_pass (alter_jumps)
-     int alter_jumps;
+local_cprop_pass (int alter_jumps)
 {
   rtx insn;
   struct reg_use *reg_used;
   rtx libcall_stack[MAX_NESTED_LIBCALLS + 1], *libcall_sp;
   bool changed = false;
 
-  cselib_init ();
+  cselib_init (false);
   libcall_sp = &libcall_stack[MAX_NESTED_LIBCALLS];
   *libcall_sp = 0;
   for (insn = get_insns (); insn; insn = NEXT_INSN (insn))
@@ -4477,6 +3610,8 @@ local_cprop_pass (alter_jumps)
                    changed = true;
                    break;
                  }
+             if (INSN_DELETED_P (insn))
+               break;
            }
          while (reg_use_count);
        }
@@ -4493,3148 +3628,3967 @@ local_cprop_pass (alter_jumps)
     }
 }
 
-/* Forward propagate copies.  This includes copies and constants.  Return
-   nonzero if a change was made.  */
+/* Forward propagate copies.  This includes copies and constants.  Return
+   nonzero if a change was made.  */
+
+static int
+cprop (int alter_jumps)
+{
+  int changed;
+  basic_block bb;
+  rtx insn;
+
+  /* Note we start at block 1.  */
+  if (ENTRY_BLOCK_PTR->next_bb == EXIT_BLOCK_PTR)
+    {
+      if (gcse_file != NULL)
+       fprintf (gcse_file, "\n");
+      return 0;
+    }
+
+  changed = 0;
+  FOR_BB_BETWEEN (bb, ENTRY_BLOCK_PTR->next_bb->next_bb, EXIT_BLOCK_PTR, next_bb)
+    {
+      /* Reset tables used to keep track of what's still valid [since the
+        start of the block].  */
+      reset_opr_set_tables ();
+
+      for (insn = BB_HEAD (bb);
+          insn != NULL && insn != NEXT_INSN (BB_END (bb));
+          insn = NEXT_INSN (insn))
+       if (INSN_P (insn))
+         {
+           changed |= cprop_insn (insn, alter_jumps);
+
+           /* Keep track of everything modified by this insn.  */
+           /* ??? Need to be careful w.r.t. mods done to INSN.  Don't
+              call mark_oprs_set if we turned the insn into a NOTE.  */
+           if (! NOTE_P (insn))
+             mark_oprs_set (insn);
+         }
+    }
+
+  if (gcse_file != NULL)
+    fprintf (gcse_file, "\n");
+
+  return changed;
+}
+
+/* Similar to get_condition, only the resulting condition must be
+   valid at JUMP, instead of at EARLIEST.
+
+   This differs from noce_get_condition in ifcvt.c in that we prefer not to
+   settle for the condition variable in the jump instruction being integral.
+   We prefer to be able to record the value of a user variable, rather than
+   the value of a temporary used in a condition.  This could be solved by
+   recording the value of *every* register scaned by canonicalize_condition,
+   but this would require some code reorganization.  */
+
+rtx
+fis_get_condition (rtx jump)
+{
+  rtx cond, set, tmp, insn, earliest;
+  bool reverse;
+
+  if (! any_condjump_p (jump))
+    return NULL_RTX;
+
+  set = pc_set (jump);
+  cond = XEXP (SET_SRC (set), 0);
+
+  /* If this branches to JUMP_LABEL when the condition is false,
+     reverse the condition.  */
+  reverse = (GET_CODE (XEXP (SET_SRC (set), 2)) == LABEL_REF
+            && XEXP (XEXP (SET_SRC (set), 2), 0) == JUMP_LABEL (jump));
+
+  /* Use canonicalize_condition to do the dirty work of manipulating
+     MODE_CC values and COMPARE rtx codes.  */
+  tmp = canonicalize_condition (jump, cond, reverse, &earliest, NULL_RTX,
+                               false);
+  if (!tmp)
+    return NULL_RTX;
+
+  /* Verify that the given condition is valid at JUMP by virtue of not
+     having been modified since EARLIEST.  */
+  for (insn = earliest; insn != jump; insn = NEXT_INSN (insn))
+    if (INSN_P (insn) && modified_in_p (tmp, insn))
+      break;
+  if (insn == jump)
+    return tmp;
+
+  /* The condition was modified.  See if we can get a partial result
+     that doesn't follow all the reversals.  Perhaps combine can fold
+     them together later.  */
+  tmp = XEXP (tmp, 0);
+  if (!REG_P (tmp) || GET_MODE_CLASS (GET_MODE (tmp)) != MODE_INT)
+    return NULL_RTX;
+  tmp = canonicalize_condition (jump, cond, reverse, &earliest, tmp,
+                               false);
+  if (!tmp)
+    return NULL_RTX;
+
+  /* For sanity's sake, re-validate the new result.  */
+  for (insn = earliest; insn != jump; insn = NEXT_INSN (insn))
+    if (INSN_P (insn) && modified_in_p (tmp, insn))
+      return NULL_RTX;
+
+  return tmp;
+}
+
+/* Check the comparison COND to see if we can safely form an implicit set from
+   it.  COND is either an EQ or NE comparison.  */
+
+static bool
+implicit_set_cond_p (rtx cond)
+{
+  enum machine_mode mode = GET_MODE (XEXP (cond, 0));
+  rtx cst = XEXP (cond, 1);
+
+  /* We can't perform this optimization if either operand might be or might
+     contain a signed zero.  */
+  if (HONOR_SIGNED_ZEROS (mode))
+    {
+      /* It is sufficient to check if CST is or contains a zero.  We must
+        handle float, complex, and vector.  If any subpart is a zero, then
+        the optimization can't be performed.  */
+      /* ??? The complex and vector checks are not implemented yet.  We just
+        always return zero for them.  */
+      if (GET_CODE (cst) == CONST_DOUBLE)
+       {
+         REAL_VALUE_TYPE d;
+         REAL_VALUE_FROM_CONST_DOUBLE (d, cst);
+         if (REAL_VALUES_EQUAL (d, dconst0))
+           return 0;
+       }
+      else
+       return 0;
+    }
+
+  return gcse_constant_p (cst);
+}
+
+/* Find the implicit sets of a function.  An "implicit set" is a constraint
+   on the value of a variable, implied by a conditional jump.  For example,
+   following "if (x == 2)", the then branch may be optimized as though the
+   conditional performed an "explicit set", in this example, "x = 2".  This
+   function records the set patterns that are implicit at the start of each
+   basic block.  */
+
+static void
+find_implicit_sets (void)
+{
+  basic_block bb, dest;
+  unsigned int count;
+  rtx cond, new;
+
+  count = 0;
+  FOR_EACH_BB (bb)
+    /* Check for more than one successor.  */
+    if (bb->succ && bb->succ->succ_next)
+      {
+       cond = fis_get_condition (BB_END (bb));
+
+       if (cond
+           && (GET_CODE (cond) == EQ || GET_CODE (cond) == NE)
+           && REG_P (XEXP (cond, 0))
+           && REGNO (XEXP (cond, 0)) >= FIRST_PSEUDO_REGISTER
+           && implicit_set_cond_p (cond))
+         {
+           dest = GET_CODE (cond) == EQ ? BRANCH_EDGE (bb)->dest
+                                        : FALLTHRU_EDGE (bb)->dest;
+
+           if (dest && ! dest->pred->pred_next
+               && dest != EXIT_BLOCK_PTR)
+             {
+               new = gen_rtx_SET (VOIDmode, XEXP (cond, 0),
+                                            XEXP (cond, 1));
+               implicit_sets[dest->index] = new;
+               if (gcse_file)
+                 {
+                   fprintf(gcse_file, "Implicit set of reg %d in ",
+                           REGNO (XEXP (cond, 0)));
+                   fprintf(gcse_file, "basic block %d\n", dest->index);
+                 }
+               count++;
+             }
+         }
+      }
+
+  if (gcse_file)
+    fprintf (gcse_file, "Found %d implicit sets\n", count);
+}
+
+/* Perform one copy/constant propagation pass.
+   PASS is the pass count.  If CPROP_JUMPS is true, perform constant
+   propagation into conditional jumps.  If BYPASS_JUMPS is true,
+   perform conditional jump bypassing optimizations.  */
+
+static int
+one_cprop_pass (int pass, int cprop_jumps, int bypass_jumps)
+{
+  int changed = 0;
+
+  const_prop_count = 0;
+  copy_prop_count = 0;
+
+  local_cprop_pass (cprop_jumps);
+
+  /* Determine implicit sets.  */
+  implicit_sets = xcalloc (last_basic_block, sizeof (rtx));
+  find_implicit_sets ();
+
+  alloc_hash_table (max_cuid, &set_hash_table, 1);
+  compute_hash_table (&set_hash_table);
+
+  /* Free implicit_sets before peak usage.  */
+  free (implicit_sets);
+  implicit_sets = NULL;
+
+  if (gcse_file)
+    dump_hash_table (gcse_file, "SET", &set_hash_table);
+  if (set_hash_table.n_elems > 0)
+    {
+      alloc_cprop_mem (last_basic_block, set_hash_table.n_elems);
+      compute_cprop_data ();
+      changed = cprop (cprop_jumps);
+      if (bypass_jumps)
+       changed |= bypass_conditional_jumps ();
+      free_cprop_mem ();
+    }
+
+  free_hash_table (&set_hash_table);
+
+  if (gcse_file)
+    {
+      fprintf (gcse_file, "CPROP of %s, pass %d: %d bytes needed, ",
+              current_function_name (), pass, bytes_used);
+      fprintf (gcse_file, "%d const props, %d copy props\n\n",
+              const_prop_count, copy_prop_count);
+    }
+  /* Global analysis may get into infinite loops for unreachable blocks.  */
+  if (changed && cprop_jumps)
+    delete_unreachable_blocks ();
+
+  return changed;
+}
+\f
+/* Bypass conditional jumps.  */
+
+/* The value of last_basic_block at the beginning of the jump_bypass
+   pass.  The use of redirect_edge_and_branch_force may introduce new
+   basic blocks, but the data flow analysis is only valid for basic
+   block indices less than bypass_last_basic_block.  */
+
+static int bypass_last_basic_block;
+
+/* Find a set of REGNO to a constant that is available at the end of basic
+   block BB.  Returns NULL if no such set is found.  Based heavily upon
+   find_avail_set.  */
+
+static struct expr *
+find_bypass_set (int regno, int bb)
+{
+  struct expr *result = 0;
+
+  for (;;)
+    {
+      rtx src;
+      struct expr *set = lookup_set (regno, &set_hash_table);
+
+      while (set)
+       {
+         if (TEST_BIT (cprop_avout[bb], set->bitmap_index))
+           break;
+         set = next_set (regno, set);
+       }
+
+      if (set == 0)
+       break;
+
+      if (GET_CODE (set->expr) != SET)
+       abort ();
+
+      src = SET_SRC (set->expr);
+      if (gcse_constant_p (src))
+       result = set;
+
+      if (! REG_P (src))
+       break;
+
+      regno = REGNO (src);
+    }
+  return result;
+}
+
+
+/* Subroutine of bypass_block that checks whether a pseudo is killed by
+   any of the instructions inserted on an edge.  Jump bypassing places
+   condition code setters on CFG edges using insert_insn_on_edge.  This
+   function is required to check that our data flow analysis is still
+   valid prior to commit_edge_insertions.  */
+
+static bool
+reg_killed_on_edge (rtx reg, edge e)
+{
+  rtx insn;
+
+  for (insn = e->insns.r; insn; insn = NEXT_INSN (insn))
+    if (INSN_P (insn) && reg_set_p (reg, insn))
+      return true;
+
+  return false;
+}
+
+/* Subroutine of bypass_conditional_jumps that attempts to bypass the given
+   basic block BB which has more than one predecessor.  If not NULL, SETCC
+   is the first instruction of BB, which is immediately followed by JUMP_INSN
+   JUMP.  Otherwise, SETCC is NULL, and JUMP is the first insn of BB.
+   Returns nonzero if a change was made.
+
+   During the jump bypassing pass, we may place copies of SETCC instructions
+   on CFG edges.  The following routine must be careful to pay attention to
+   these inserted insns when performing its transformations.  */
 
 static int
-cprop (alter_jumps)
-     int alter_jumps;
+bypass_block (basic_block bb, rtx setcc, rtx jump)
 {
-  int changed;
-  basic_block bb;
-  rtx insn;
+  rtx insn, note;
+  edge e, enext, edest;
+  int i, change;
+  int may_be_loop_header;
 
-  /* Note we start at block 1.  */
-  if (ENTRY_BLOCK_PTR->next_bb == EXIT_BLOCK_PTR)
-    {
-      if (gcse_file != NULL)
-       fprintf (gcse_file, "\n");
-      return 0;
-    }
+  insn = (setcc != NULL) ? setcc : jump;
 
-  changed = 0;
-  FOR_BB_BETWEEN (bb, ENTRY_BLOCK_PTR->next_bb->next_bb, EXIT_BLOCK_PTR, next_bb)
+  /* Determine set of register uses in INSN.  */
+  reg_use_count = 0;
+  note_uses (&PATTERN (insn), find_used_regs, NULL);
+  note = find_reg_equal_equiv_note (insn);
+  if (note)
+    find_used_regs (&XEXP (note, 0), NULL);
+
+  may_be_loop_header = false;
+  for (e = bb->pred; e; e = e->pred_next)
+    if (e->flags & EDGE_DFS_BACK)
+      {
+       may_be_loop_header = true;
+       break;
+      }
+
+  change = 0;
+  for (e = bb->pred; e; e = enext)
     {
-      /* Reset tables used to keep track of what's still valid [since the
-        start of the block].  */
-      reset_opr_set_tables ();
+      enext = e->pred_next;
+      if (e->flags & EDGE_COMPLEX)
+       continue;
 
-      for (insn = bb->head;
-          insn != NULL && insn != NEXT_INSN (bb->end);
-          insn = NEXT_INSN (insn))
-       if (INSN_P (insn))
-         {
-           changed |= cprop_insn (insn, alter_jumps);
+      /* We can't redirect edges from new basic blocks.  */
+      if (e->src->index >= bypass_last_basic_block)
+       continue;
 
-           /* Keep track of everything modified by this insn.  */
-           /* ??? Need to be careful w.r.t. mods done to INSN.  Don't
-              call mark_oprs_set if we turned the insn into a NOTE.  */
-           if (GET_CODE (insn) != NOTE)
-             mark_oprs_set (insn);
-         }
-    }
+      /* The irreducible loops created by redirecting of edges entering the
+        loop from outside would decrease effectiveness of some of the following
+        optimizations, so prevent this.  */
+      if (may_be_loop_header
+         && !(e->flags & EDGE_DFS_BACK))
+       continue;
 
-  if (gcse_file != NULL)
-    fprintf (gcse_file, "\n");
+      for (i = 0; i < reg_use_count; i++)
+       {
+         struct reg_use *reg_used = &reg_use_table[i];
+         unsigned int regno = REGNO (reg_used->reg_rtx);
+         basic_block dest, old_dest;
+         struct expr *set;
+         rtx src, new;
 
-  return changed;
-}
+         if (regno >= max_gcse_regno)
+           continue;
 
-/* Similar to get_condition, only the resulting condition must be
-   valid at JUMP, instead of at EARLIEST.
+         set = find_bypass_set (regno, e->src->index);
 
-   This differs from noce_get_condition in ifcvt.c in that we prefer not to
-   settle for the condition variable in the jump instruction being integral.
-   We prefer to be able to record the value of a user variable, rather than
-   the value of a temporary used in a condition.  This could be solved by
-   recording the value of *every* register scaned by canonicalize_condition,
-   but this would require some code reorganization.  */
+         if (! set)
+           continue;
 
-static rtx
-fis_get_condition (jump)
-     rtx jump;
-{
-  rtx cond, set, tmp, insn, earliest;
-  bool reverse;
+         /* Check the data flow is valid after edge insertions.  */
+         if (e->insns.r && reg_killed_on_edge (reg_used->reg_rtx, e))
+           continue;
 
-  if (! any_condjump_p (jump))
-    return NULL_RTX;
+         src = SET_SRC (pc_set (jump));
 
-  set = pc_set (jump);
-  cond = XEXP (SET_SRC (set), 0);
+         if (setcc != NULL)
+             src = simplify_replace_rtx (src,
+                                         SET_DEST (PATTERN (setcc)),
+                                         SET_SRC (PATTERN (setcc)));
 
-  /* If this branches to JUMP_LABEL when the condition is false,
-     reverse the condition.  */
-  reverse = (GET_CODE (XEXP (SET_SRC (set), 2)) == LABEL_REF
-            && XEXP (XEXP (SET_SRC (set), 2), 0) == JUMP_LABEL (jump));
+         new = simplify_replace_rtx (src, reg_used->reg_rtx,
+                                     SET_SRC (set->expr));
 
-  /* Use canonicalize_condition to do the dirty work of manipulating
-     MODE_CC values and COMPARE rtx codes.  */
-  tmp = canonicalize_condition (jump, cond, reverse, &earliest, NULL_RTX);
-  if (!tmp)
-    return NULL_RTX;
+         /* Jump bypassing may have already placed instructions on
+            edges of the CFG.  We can't bypass an outgoing edge that
+            has instructions associated with it, as these insns won't
+            get executed if the incoming edge is redirected.  */
 
-  /* Verify that the given condition is valid at JUMP by virtue of not
-     having been modified since EARLIEST.  */
-  for (insn = earliest; insn != jump; insn = NEXT_INSN (insn))
-    if (INSN_P (insn) && modified_in_p (tmp, insn))
-      break;
-  if (insn == jump)
-    return tmp;
+         if (new == pc_rtx)
+           {
+             edest = FALLTHRU_EDGE (bb);
+             dest = edest->insns.r ? NULL : edest->dest;
+           }
+         else if (GET_CODE (new) == LABEL_REF)
+           {
+             dest = BLOCK_FOR_INSN (XEXP (new, 0));
+             /* Don't bypass edges containing instructions.  */
+             for (edest = bb->succ; edest; edest = edest->succ_next)
+               if (edest->dest == dest && edest->insns.r)
+                 {
+                   dest = NULL;
+                   break;
+                 }
+           }
+         else
+           dest = NULL;
 
-  /* The condition was modified.  See if we can get a partial result
-     that doesn't follow all the reversals.  Perhaps combine can fold
-     them together later.  */
-  tmp = XEXP (tmp, 0);
-  if (!REG_P (tmp) || GET_MODE_CLASS (GET_MODE (tmp)) != MODE_INT)
-    return NULL_RTX;
-  tmp = canonicalize_condition (jump, cond, reverse, &earliest, tmp);
-  if (!tmp)
-    return NULL_RTX;
+         /* Avoid unification of the edge with other edges from original
+            branch.  We would end up emitting the instruction on "both"
+            edges.  */
 
-  /* For sanity's sake, re-validate the new result.  */
-  for (insn = earliest; insn != jump; insn = NEXT_INSN (insn))
-    if (INSN_P (insn) && modified_in_p (tmp, insn))
-      return NULL_RTX;
+         if (dest && setcc && !CC0_P (SET_DEST (PATTERN (setcc))))
+           {
+             edge e2;
+             for (e2 = e->src->succ; e2; e2 = e2->succ_next)
+               if (e2->dest == dest)
+                 {
+                   dest = NULL;
+                   break;
+                 }
+           }
 
-  return tmp;
+         old_dest = e->dest;
+         if (dest != NULL
+             && dest != old_dest
+             && dest != EXIT_BLOCK_PTR)
+            {
+             redirect_edge_and_branch_force (e, dest);
+
+             /* Copy the register setter to the redirected edge.
+                Don't copy CC0 setters, as CC0 is dead after jump.  */
+             if (setcc)
+               {
+                 rtx pat = PATTERN (setcc);
+                 if (!CC0_P (SET_DEST (pat)))
+                   insert_insn_on_edge (copy_insn (pat), e);
+               }
+
+             if (gcse_file != NULL)
+               {
+                 fprintf (gcse_file, "JUMP-BYPASS: Proved reg %d in jump_insn %d equals constant ",
+                          regno, INSN_UID (jump));
+                 print_rtl (gcse_file, SET_SRC (set->expr));
+                 fprintf (gcse_file, "\nBypass edge from %d->%d to %d\n",
+                          e->src->index, old_dest->index, dest->index);
+               }
+             change = 1;
+             break;
+           }
+       }
+    }
+  return change;
 }
 
-/* Find the implicit sets of a function.  An "implicit set" is a constraint
-   on the value of a variable, implied by a conditional jump.  For example,
-   following "if (x == 2)", the then branch may be optimized as though the
-   conditional performed an "explicit set", in this example, "x = 2".  This
-   function records the set patterns that are implicit at the start of each
-   basic block.  */
+/* Find basic blocks with more than one predecessor that only contain a
+   single conditional jump.  If the result of the comparison is known at
+   compile-time from any incoming edge, redirect that edge to the
+   appropriate target.  Returns nonzero if a change was made.
 
-static void
-find_implicit_sets ()
+   This function is now mis-named, because we also handle indirect jumps.  */
+
+static int
+bypass_conditional_jumps (void)
 {
-  basic_block bb, dest;
-  unsigned int count;
-  rtx cond, new;
+  basic_block bb;
+  int changed;
+  rtx setcc;
+  rtx insn;
+  rtx dest;
 
-  count = 0;
-  FOR_EACH_BB (bb)
-    /* Check for more than one sucessor.  */
-    if (bb->succ && bb->succ->succ_next)
-      {
-       cond = fis_get_condition (bb->end);
+  /* Note we start at block 1.  */
+  if (ENTRY_BLOCK_PTR->next_bb == EXIT_BLOCK_PTR)
+    return 0;
 
-       if (cond
-           && (GET_CODE (cond) == EQ || GET_CODE (cond) == NE)
-           && GET_CODE (XEXP (cond, 0)) == REG
-           && REGNO (XEXP (cond, 0)) >= FIRST_PSEUDO_REGISTER
-           && CONSTANT_P (XEXP (cond, 1)))
-         {
-           dest = GET_CODE (cond) == EQ ? BRANCH_EDGE (bb)->dest
-                                        : FALLTHRU_EDGE (bb)->dest;
+  bypass_last_basic_block = last_basic_block;
+  mark_dfs_back_edges ();
 
-           if (dest && ! dest->pred->pred_next
-               && dest != EXIT_BLOCK_PTR)
+  changed = 0;
+  FOR_BB_BETWEEN (bb, ENTRY_BLOCK_PTR->next_bb->next_bb,
+                 EXIT_BLOCK_PTR, next_bb)
+    {
+      /* Check for more than one predecessor.  */
+      if (bb->pred && bb->pred->pred_next)
+       {
+         setcc = NULL_RTX;
+         for (insn = BB_HEAD (bb);
+              insn != NULL && insn != NEXT_INSN (BB_END (bb));
+              insn = NEXT_INSN (insn))
+           if (GET_CODE (insn) == INSN)
              {
-               new = gen_rtx_SET (VOIDmode, XEXP (cond, 0),
-                                            XEXP (cond, 1));
-               implicit_sets[dest->index] = new;
-               if (gcse_file)
-                 {
-                   fprintf(gcse_file, "Implicit set of reg %d in ",
-                           REGNO (XEXP (cond, 0)));
-                   fprintf(gcse_file, "basic block %d\n", dest->index);
-                 }
-               count++;
+               if (setcc)
+                 break;
+               if (GET_CODE (PATTERN (insn)) != SET)
+                 break;
+
+               dest = SET_DEST (PATTERN (insn));
+               if (REG_P (dest) || CC0_P (dest))
+                 setcc = insn;
+               else
+                 break;
              }
-         }
-      }
+           else if (JUMP_P (insn))
+             {
+               if ((any_condjump_p (insn) || computed_jump_p (insn))
+                   && onlyjump_p (insn))
+                 changed |= bypass_block (bb, setcc, insn);
+               break;
+             }
+           else if (INSN_P (insn))
+             break;
+       }
+    }
 
-  if (gcse_file)
-    fprintf (gcse_file, "Found %d implicit sets\n", count);
+  /* If we bypassed any register setting insns, we inserted a
+     copy on the redirected edge.  These need to be committed.  */
+  if (changed)
+    commit_edge_insertions();
+
+  return changed;
 }
+\f
+/* Compute PRE+LCM working variables.  */
 
-/* Perform one copy/constant propagation pass.
-   PASS is the pass count.  If CPROP_JUMPS is true, perform constant
-   propagation into conditional jumps.  If BYPASS_JUMPS is true,
-   perform conditional jump bypassing optimizations.  */
+/* Local properties of expressions.  */
+/* Nonzero for expressions that are transparent in the block.  */
+static sbitmap *transp;
 
-static int
-one_cprop_pass (pass, cprop_jumps, bypass_jumps)
-     int pass;
-     int cprop_jumps;
-     int bypass_jumps;
-{
-  int changed = 0;
+/* Nonzero for expressions that are transparent at the end of the block.
+   This is only zero for expressions killed by abnormal critical edge
+   created by a calls.  */
+static sbitmap *transpout;
 
-  const_prop_count = 0;
-  copy_prop_count = 0;
+/* Nonzero for expressions that are computed (available) in the block.  */
+static sbitmap *comp;
 
-  local_cprop_pass (cprop_jumps);
+/* Nonzero for expressions that are locally anticipatable in the block.  */
+static sbitmap *antloc;
 
-  /* Determine implicit sets.  */
-  implicit_sets = (rtx *) xcalloc (last_basic_block, sizeof (rtx));
-  find_implicit_sets ();
+/* Nonzero for expressions where this block is an optimal computation
+   point.  */
+static sbitmap *pre_optimal;
 
-  alloc_hash_table (max_cuid, &set_hash_table, 1);
-  compute_hash_table (&set_hash_table);
+/* Nonzero for expressions which are redundant in a particular block.  */
+static sbitmap *pre_redundant;
 
-  /* Free implicit_sets before peak usage.  */
-  free (implicit_sets);
-  implicit_sets = NULL;
+/* Nonzero for expressions which should be inserted on a specific edge.  */
+static sbitmap *pre_insert_map;
 
-  if (gcse_file)
-    dump_hash_table (gcse_file, "SET", &set_hash_table);
-  if (set_hash_table.n_elems > 0)
-    {
-      alloc_cprop_mem (last_basic_block, set_hash_table.n_elems);
-      compute_cprop_data ();
-      changed = cprop (cprop_jumps);
-      if (bypass_jumps)
-       changed |= bypass_conditional_jumps ();
-      free_cprop_mem ();
-    }
+/* Nonzero for expressions which should be deleted in a specific block.  */
+static sbitmap *pre_delete_map;
 
-  free_hash_table (&set_hash_table);
+/* Contains the edge_list returned by pre_edge_lcm.  */
+static struct edge_list *edge_list;
 
-  if (gcse_file)
-    {
-      fprintf (gcse_file, "CPROP of %s, pass %d: %d bytes needed, ",
-              current_function_name, pass, bytes_used);
-      fprintf (gcse_file, "%d const props, %d copy props\n\n",
-              const_prop_count, copy_prop_count);
-    }
-  /* Global analysis may get into infinite loops for unreachable blocks.  */
-  if (changed && cprop_jumps)
-    delete_unreachable_blocks ();
+/* Redundant insns.  */
+static sbitmap pre_redundant_insns;
 
-  return changed;
+/* Allocate vars used for PRE analysis.  */
+
+static void
+alloc_pre_mem (int n_blocks, int n_exprs)
+{
+  transp = sbitmap_vector_alloc (n_blocks, n_exprs);
+  comp = sbitmap_vector_alloc (n_blocks, n_exprs);
+  antloc = sbitmap_vector_alloc (n_blocks, n_exprs);
+
+  pre_optimal = NULL;
+  pre_redundant = NULL;
+  pre_insert_map = NULL;
+  pre_delete_map = NULL;
+  ae_kill = sbitmap_vector_alloc (n_blocks, n_exprs);
+
+  /* pre_insert and pre_delete are allocated later.  */
 }
-\f
-/* Bypass conditional jumps.  */
 
-/* The value of last_basic_block at the beginning of the jump_bypass
-   pass.  The use of redirect_edge_and_branch_force may introduce new
-   basic blocks, but the data flow analysis is only valid for basic
-   block indices less than bypass_last_basic_block.  */
+/* Free vars used for PRE analysis.  */
 
-static int bypass_last_basic_block;
+static void
+free_pre_mem (void)
+{
+  sbitmap_vector_free (transp);
+  sbitmap_vector_free (comp);
 
-/* Find a set of REGNO to a constant that is available at the end of basic
-   block BB.  Returns NULL if no such set is found.  Based heavily upon
-   find_avail_set.  */
+  /* ANTLOC and AE_KILL are freed just after pre_lcm finishes.  */
 
-static struct expr *
-find_bypass_set (regno, bb)
-     int regno;
-     int bb;
+  if (pre_optimal)
+    sbitmap_vector_free (pre_optimal);
+  if (pre_redundant)
+    sbitmap_vector_free (pre_redundant);
+  if (pre_insert_map)
+    sbitmap_vector_free (pre_insert_map);
+  if (pre_delete_map)
+    sbitmap_vector_free (pre_delete_map);
+
+  transp = comp = NULL;
+  pre_optimal = pre_redundant = pre_insert_map = pre_delete_map = NULL;
+}
+
+/* Top level routine to do the dataflow analysis needed by PRE.  */
+
+static void
+compute_pre_data (void)
 {
-  struct expr *result = 0;
+  sbitmap trapping_expr;
+  basic_block bb;
+  unsigned int ui;
 
-  for (;;)
-    {
-      rtx src;
-      struct expr *set = lookup_set (regno, &set_hash_table);
+  compute_local_properties (transp, comp, antloc, &expr_hash_table);
+  sbitmap_vector_zero (ae_kill, last_basic_block);
 
-      while (set)
-       {
-         if (TEST_BIT (cprop_avout[bb], set->bitmap_index))
-           break;
-         set = next_set (regno, set);
-       }
+  /* Collect expressions which might trap.  */
+  trapping_expr = sbitmap_alloc (expr_hash_table.n_elems);
+  sbitmap_zero (trapping_expr);
+  for (ui = 0; ui < expr_hash_table.size; ui++)
+    {
+      struct expr *e;
+      for (e = expr_hash_table.table[ui]; e != NULL; e = e->next_same_hash)
+       if (may_trap_p (e->expr))
+         SET_BIT (trapping_expr, e->bitmap_index);
+    }
 
-      if (set == 0)
-       break;
+  /* Compute ae_kill for each basic block using:
 
-      if (GET_CODE (set->expr) != SET)
-       abort ();
+     ~(TRANSP | COMP)
+  */
 
-      src = SET_SRC (set->expr);
-      if (CONSTANT_P (src))
-       result = set;
+  FOR_EACH_BB (bb)
+    {
+      edge e;
 
-      if (GET_CODE (src) != REG)
-       break;
+      /* If the current block is the destination of an abnormal edge, we
+        kill all trapping expressions because we won't be able to properly
+        place the instruction on the edge.  So make them neither
+        anticipatable nor transparent.  This is fairly conservative.  */
+      for (e = bb->pred; e ; e = e->pred_next)
+       if (e->flags & EDGE_ABNORMAL)
+         {
+           sbitmap_difference (antloc[bb->index], antloc[bb->index], trapping_expr);
+           sbitmap_difference (transp[bb->index], transp[bb->index], trapping_expr);
+           break;
+         }
 
-      regno = REGNO (src);
+      sbitmap_a_or_b (ae_kill[bb->index], transp[bb->index], comp[bb->index]);
+      sbitmap_not (ae_kill[bb->index], ae_kill[bb->index]);
     }
-  return result;
+
+  edge_list = pre_edge_lcm (gcse_file, expr_hash_table.n_elems, transp, comp, antloc,
+                           ae_kill, &pre_insert_map, &pre_delete_map);
+  sbitmap_vector_free (antloc);
+  antloc = NULL;
+  sbitmap_vector_free (ae_kill);
+  ae_kill = NULL;
+  sbitmap_free (trapping_expr);
 }
+\f
+/* PRE utilities */
 
+/* Return nonzero if an occurrence of expression EXPR in OCCR_BB would reach
+   block BB.
 
-/* Subroutine of bypass_conditional_jumps that attempts to bypass the given
-   basic block BB which has more than one predecessor.  If not NULL, SETCC
-   is the first instruction of BB, which is immediately followed by JUMP_INSN
-   JUMP.  Otherwise, SETCC is NULL, and JUMP is the first insn of BB.
-   Returns nonzero if a change was made.  */
+   VISITED is a pointer to a working buffer for tracking which BB's have
+   been visited.  It is NULL for the top-level call.
+
+   We treat reaching expressions that go through blocks containing the same
+   reaching expression as "not reaching".  E.g. if EXPR is generated in blocks
+   2 and 3, INSN is in block 4, and 2->3->4, we treat the expression in block
+   2 as not reaching.  The intent is to improve the probability of finding
+   only one reaching expression and to reduce register lifetimes by picking
+   the closest such expression.  */
 
 static int
-bypass_block (bb, setcc, jump)
-     basic_block bb;
-     rtx setcc, jump;
+pre_expr_reaches_here_p_work (basic_block occr_bb, struct expr *expr, basic_block bb, char *visited)
 {
-  rtx insn, note;
-  edge e, enext;
-  int i, change;
+  edge pred;
 
-  insn = (setcc != NULL) ? setcc : jump;
+  for (pred = bb->pred; pred != NULL; pred = pred->pred_next)
+    {
+      basic_block pred_bb = pred->src;
 
-  /* Determine set of register uses in INSN.  */
-  reg_use_count = 0;
-  note_uses (&PATTERN (insn), find_used_regs, NULL);
-  note = find_reg_equal_equiv_note (insn);
-  if (note)
-    find_used_regs (&XEXP (note, 0), NULL);
+      if (pred->src == ENTRY_BLOCK_PTR
+         /* Has predecessor has already been visited?  */
+         || visited[pred_bb->index])
+       ;/* Nothing to do.  */
 
-  change = 0;
-  for (e = bb->pred; e; e = enext)
-    {
-      enext = e->pred_next;
-      if (e->flags & EDGE_COMPLEX)
-       continue;
+      /* Does this predecessor generate this expression?  */
+      else if (TEST_BIT (comp[pred_bb->index], expr->bitmap_index))
+       {
+         /* Is this the occurrence we're looking for?
+            Note that there's only one generating occurrence per block
+            so we just need to check the block number.  */
+         if (occr_bb == pred_bb)
+           return 1;
 
-      /* We can't redirect edges from new basic blocks.  */
-      if (e->src->index >= bypass_last_basic_block)
-       continue;
+         visited[pred_bb->index] = 1;
+       }
+      /* Ignore this predecessor if it kills the expression.  */
+      else if (! TEST_BIT (transp[pred_bb->index], expr->bitmap_index))
+       visited[pred_bb->index] = 1;
 
-      for (i = 0; i < reg_use_count; i++)
+      /* Neither gen nor kill.  */
+      else
        {
-         struct reg_use *reg_used = &reg_use_table[i];
-         unsigned int regno = REGNO (reg_used->reg_rtx);
-         basic_block dest, old_dest;
-         struct expr *set;
-         rtx src, new;
+         visited[pred_bb->index] = 1;
+         if (pre_expr_reaches_here_p_work (occr_bb, expr, pred_bb, visited))
+           return 1;
+       }
+    }
+
+  /* All paths have been checked.  */
+  return 0;
+}
 
-         if (regno >= max_gcse_regno)
-           continue;
+/* The wrapper for pre_expr_reaches_here_work that ensures that any
+   memory allocated for that function is returned.  */
 
-         set = find_bypass_set (regno, e->src->index);
+static int
+pre_expr_reaches_here_p (basic_block occr_bb, struct expr *expr, basic_block bb)
+{
+  int rval;
+  char *visited = xcalloc (last_basic_block, 1);
 
-         if (! set)
-           continue;
+  rval = pre_expr_reaches_here_p_work (occr_bb, expr, bb, visited);
 
-         src = SET_SRC (pc_set (jump));
+  free (visited);
+  return rval;
+}
+\f
 
-         if (setcc != NULL)
-             src = simplify_replace_rtx (src,
-                                         SET_DEST (PATTERN (setcc)),
-                                         SET_SRC (PATTERN (setcc)));
+/* Given an expr, generate RTL which we can insert at the end of a BB,
+   or on an edge.  Set the block number of any insns generated to
+   the value of BB.  */
 
-         new = simplify_replace_rtx (src, reg_used->reg_rtx,
-                                     SET_SRC (set->expr));
+static rtx
+process_insert_insn (struct expr *expr)
+{
+  rtx reg = expr->reaching_reg;
+  rtx exp = copy_rtx (expr->expr);
+  rtx pat;
 
-         if (new == pc_rtx)
-           dest = FALLTHRU_EDGE (bb)->dest;
-         else if (GET_CODE (new) == LABEL_REF)
-           dest = BLOCK_FOR_INSN (XEXP (new, 0));
-         else
-           dest = NULL;
+  start_sequence ();
 
-         old_dest = e->dest;
-         if (dest != NULL
-             && dest != old_dest
-             && dest != EXIT_BLOCK_PTR)
-            {
-             redirect_edge_and_branch_force (e, dest);
+  /* If the expression is something that's an operand, like a constant,
+     just copy it to a register.  */
+  if (general_operand (exp, GET_MODE (reg)))
+    emit_move_insn (reg, exp);
 
-             /* Copy the register setter to the redirected edge.
-                Don't copy CC0 setters, as CC0 is dead after jump.  */
-             if (setcc)
-               {
-                 rtx pat = PATTERN (setcc);
-                 if (!CC0_P (SET_DEST (pat)))
-                   insert_insn_on_edge (copy_insn (pat), e);
-               }
+  /* Otherwise, make a new insn to compute this expression and make sure the
+     insn will be recognized (this also adds any needed CLOBBERs).  Copy the
+     expression to make sure we don't have any sharing issues.  */
+  else if (insn_invalid_p (emit_insn (gen_rtx_SET (VOIDmode, reg, exp))))
+    abort ();
 
-             if (gcse_file != NULL)
-               {
-                 fprintf (gcse_file, "JUMP-BYPASS: Proved reg %d in jump_insn %d equals constant ",
-                          regno, INSN_UID (jump));
-                 print_rtl (gcse_file, SET_SRC (set->expr));
-                 fprintf (gcse_file, "\nBypass edge from %d->%d to %d\n",
-                          e->src->index, old_dest->index, dest->index);
-               }
-             change = 1;
-             break;
-           }
-       }
-    }
-  return change;
+  pat = get_insns ();
+  end_sequence ();
+
+  return pat;
 }
 
-/* Find basic blocks with more than one predecessor that only contain a
-   single conditional jump.  If the result of the comparison is known at
-   compile-time from any incoming edge, redirect that edge to the
-   appropriate target.  Returns nonzero if a change was made.
+/* Add EXPR to the end of basic block BB.
 
-   This function is now mis-named, because we also handle indirect jumps.  */
+   This is used by both the PRE and code hoisting.
 
-static int
-bypass_conditional_jumps ()
+   For PRE, we want to verify that the expr is either transparent
+   or locally anticipatable in the target block.  This check makes
+   no sense for code hoisting.  */
+
+static void
+insert_insn_end_bb (struct expr *expr, basic_block bb, int pre)
 {
-  basic_block bb;
-  int changed;
-  rtx setcc;
-  rtx insn;
-  rtx dest;
+  rtx insn = BB_END (bb);
+  rtx new_insn;
+  rtx reg = expr->reaching_reg;
+  int regno = REGNO (reg);
+  rtx pat, pat_end;
 
-  /* Note we start at block 1.  */
-  if (ENTRY_BLOCK_PTR->next_bb == EXIT_BLOCK_PTR)
-    return 0;
+  pat = process_insert_insn (expr);
+  if (pat == NULL_RTX || ! INSN_P (pat))
+    abort ();
 
-  bypass_last_basic_block = last_basic_block;
+  pat_end = pat;
+  while (NEXT_INSN (pat_end) != NULL_RTX)
+    pat_end = NEXT_INSN (pat_end);
 
-  changed = 0;
-  FOR_BB_BETWEEN (bb, ENTRY_BLOCK_PTR->next_bb->next_bb,
-                 EXIT_BLOCK_PTR, next_bb)
+  /* If the last insn is a jump, insert EXPR in front [taking care to
+     handle cc0, etc. properly].  Similarly we need to care trapping
+     instructions in presence of non-call exceptions.  */
+
+  if (JUMP_P (insn)
+      || (GET_CODE (insn) == INSN
+         && (bb->succ->succ_next || (bb->succ->flags & EDGE_ABNORMAL))))
     {
-      /* Check for more than one predecessor.  */
-      if (bb->pred && bb->pred->pred_next)
-       {
-         setcc = NULL_RTX;
-         for (insn = bb->head;
-              insn != NULL && insn != NEXT_INSN (bb->end);
-              insn = NEXT_INSN (insn))
-           if (GET_CODE (insn) == INSN)
-             {
-               if (setcc)
-                 break;
-               if (GET_CODE (PATTERN (insn)) != SET)
-                 break;
+#ifdef HAVE_cc0
+      rtx note;
+#endif
+      /* It should always be the case that we can put these instructions
+        anywhere in the basic block with performing PRE optimizations.
+        Check this.  */
+      if (GET_CODE (insn) == INSN && pre
+         && !TEST_BIT (antloc[bb->index], expr->bitmap_index)
+         && !TEST_BIT (transp[bb->index], expr->bitmap_index))
+       abort ();
 
-               dest = SET_DEST (PATTERN (insn));
-               if (REG_P (dest) || CC0_P (dest))
-                 setcc = insn;
-               else
-                 break;
-             }
-           else if (GET_CODE (insn) == JUMP_INSN)
-             {
-               if ((any_condjump_p (insn) || computed_jump_p (insn))
-                   && onlyjump_p (insn))
-                 changed |= bypass_block (bb, setcc, insn);
-               break;
-             }
-           else if (INSN_P (insn))
-             break;
+      /* If this is a jump table, then we can't insert stuff here.  Since
+        we know the previous real insn must be the tablejump, we insert
+        the new instruction just before the tablejump.  */
+      if (GET_CODE (PATTERN (insn)) == ADDR_VEC
+         || GET_CODE (PATTERN (insn)) == ADDR_DIFF_VEC)
+       insn = prev_real_insn (insn);
+
+#ifdef HAVE_cc0
+      /* FIXME: 'twould be nice to call prev_cc0_setter here but it aborts
+        if cc0 isn't set.  */
+      note = find_reg_note (insn, REG_CC_SETTER, NULL_RTX);
+      if (note)
+       insn = XEXP (note, 0);
+      else
+       {
+         rtx maybe_cc0_setter = prev_nonnote_insn (insn);
+         if (maybe_cc0_setter
+             && INSN_P (maybe_cc0_setter)
+             && sets_cc0_p (PATTERN (maybe_cc0_setter)))
+           insn = maybe_cc0_setter;
        }
+#endif
+      /* FIXME: What if something in cc0/jump uses value set in new insn?  */
+      new_insn = emit_insn_before (pat, insn);
     }
 
-  /* If we bypassed any register setting insns, we inserted a
-     copy on the redirected edge.  These need to be committed.  */
-  if (changed)
-    commit_edge_insertions();
+  /* Likewise if the last insn is a call, as will happen in the presence
+     of exception handling.  */
+  else if (CALL_P (insn)
+          && (bb->succ->succ_next || (bb->succ->flags & EDGE_ABNORMAL)))
+    {
+      /* Keeping in mind SMALL_REGISTER_CLASSES and parameters in registers,
+        we search backward and place the instructions before the first
+        parameter is loaded.  Do this for everyone for consistency and a
+        presumption that we'll get better code elsewhere as well.
 
-  return changed;
-}
-\f
-/* Compute PRE+LCM working variables.  */
+        It should always be the case that we can put these instructions
+        anywhere in the basic block with performing PRE optimizations.
+        Check this.  */
 
-/* Local properties of expressions.  */
-/* Nonzero for expressions that are transparent in the block.  */
-static sbitmap *transp;
+      if (pre
+         && !TEST_BIT (antloc[bb->index], expr->bitmap_index)
+         && !TEST_BIT (transp[bb->index], expr->bitmap_index))
+       abort ();
 
-/* Nonzero for expressions that are transparent at the end of the block.
-   This is only zero for expressions killed by abnormal critical edge
-   created by a calls.  */
-static sbitmap *transpout;
+      /* Since different machines initialize their parameter registers
+        in different orders, assume nothing.  Collect the set of all
+        parameter registers.  */
+      insn = find_first_parameter_load (insn, BB_HEAD (bb));
 
-/* Nonzero for expressions that are computed (available) in the block.  */
-static sbitmap *comp;
+      /* If we found all the parameter loads, then we want to insert
+        before the first parameter load.
 
-/* Nonzero for expressions that are locally anticipatable in the block.  */
-static sbitmap *antloc;
+        If we did not find all the parameter loads, then we might have
+        stopped on the head of the block, which could be a CODE_LABEL.
+        If we inserted before the CODE_LABEL, then we would be putting
+        the insn in the wrong basic block.  In that case, put the insn
+        after the CODE_LABEL.  Also, respect NOTE_INSN_BASIC_BLOCK.  */
+      while (LABEL_P (insn)
+            || NOTE_INSN_BASIC_BLOCK_P (insn))
+       insn = NEXT_INSN (insn);
 
-/* Nonzero for expressions where this block is an optimal computation
-   point.  */
-static sbitmap *pre_optimal;
+      new_insn = emit_insn_before (pat, insn);
+    }
+  else
+    new_insn = emit_insn_after (pat, insn);
 
-/* Nonzero for expressions which are redundant in a particular block.  */
-static sbitmap *pre_redundant;
+  while (1)
+    {
+      if (INSN_P (pat))
+       {
+         add_label_notes (PATTERN (pat), new_insn);
+         note_stores (PATTERN (pat), record_set_info, pat);
+       }
+      if (pat == pat_end)
+       break;
+      pat = NEXT_INSN (pat);
+    }
 
-/* Nonzero for expressions which should be inserted on a specific edge.  */
-static sbitmap *pre_insert_map;
+  gcse_create_count++;
+
+  if (gcse_file)
+    {
+      fprintf (gcse_file, "PRE/HOIST: end of bb %d, insn %d, ",
+              bb->index, INSN_UID (new_insn));
+      fprintf (gcse_file, "copying expression %d to reg %d\n",
+              expr->bitmap_index, regno);
+    }
+}
+
+/* Insert partially redundant expressions on edges in the CFG to make
+   the expressions fully redundant.  */
+
+static int
+pre_edge_insert (struct edge_list *edge_list, struct expr **index_map)
+{
+  int e, i, j, num_edges, set_size, did_insert = 0;
+  sbitmap *inserted;
 
-/* Nonzero for expressions which should be deleted in a specific block.  */
-static sbitmap *pre_delete_map;
+  /* Where PRE_INSERT_MAP is nonzero, we add the expression on that edge
+     if it reaches any of the deleted expressions.  */
 
-/* Contains the edge_list returned by pre_edge_lcm.  */
-static struct edge_list *edge_list;
+  set_size = pre_insert_map[0]->size;
+  num_edges = NUM_EDGES (edge_list);
+  inserted = sbitmap_vector_alloc (num_edges, expr_hash_table.n_elems);
+  sbitmap_vector_zero (inserted, num_edges);
 
-/* Redundant insns.  */
-static sbitmap pre_redundant_insns;
+  for (e = 0; e < num_edges; e++)
+    {
+      int indx;
+      basic_block bb = INDEX_EDGE_PRED_BB (edge_list, e);
 
-/* Allocate vars used for PRE analysis.  */
+      for (i = indx = 0; i < set_size; i++, indx += SBITMAP_ELT_BITS)
+       {
+         SBITMAP_ELT_TYPE insert = pre_insert_map[e]->elms[i];
 
-static void
-alloc_pre_mem (n_blocks, n_exprs)
-     int n_blocks, n_exprs;
-{
-  transp = sbitmap_vector_alloc (n_blocks, n_exprs);
-  comp = sbitmap_vector_alloc (n_blocks, n_exprs);
-  antloc = sbitmap_vector_alloc (n_blocks, n_exprs);
+         for (j = indx; insert && j < (int) expr_hash_table.n_elems; j++, insert >>= 1)
+           if ((insert & 1) != 0 && index_map[j]->reaching_reg != NULL_RTX)
+             {
+               struct expr *expr = index_map[j];
+               struct occr *occr;
 
-  pre_optimal = NULL;
-  pre_redundant = NULL;
-  pre_insert_map = NULL;
-  pre_delete_map = NULL;
-  ae_in = NULL;
-  ae_out = NULL;
-  ae_kill = sbitmap_vector_alloc (n_blocks, n_exprs);
+               /* Now look at each deleted occurrence of this expression.  */
+               for (occr = expr->antic_occr; occr != NULL; occr = occr->next)
+                 {
+                   if (! occr->deleted_p)
+                     continue;
 
-  /* pre_insert and pre_delete are allocated later.  */
-}
+                   /* Insert this expression on this edge if if it would
+                      reach the deleted occurrence in BB.  */
+                   if (!TEST_BIT (inserted[e], j))
+                     {
+                       rtx insn;
+                       edge eg = INDEX_EDGE (edge_list, e);
 
-/* Free vars used for PRE analysis.  */
+                       /* We can't insert anything on an abnormal and
+                          critical edge, so we insert the insn at the end of
+                          the previous block. There are several alternatives
+                          detailed in Morgans book P277 (sec 10.5) for
+                          handling this situation.  This one is easiest for
+                          now.  */
 
-static void
-free_pre_mem ()
-{
-  sbitmap_vector_free (transp);
-  sbitmap_vector_free (comp);
+                       if ((eg->flags & EDGE_ABNORMAL) == EDGE_ABNORMAL)
+                         insert_insn_end_bb (index_map[j], bb, 0);
+                       else
+                         {
+                           insn = process_insert_insn (index_map[j]);
+                           insert_insn_on_edge (insn, eg);
+                         }
 
-  /* ANTLOC and AE_KILL are freed just after pre_lcm finishes.  */
+                       if (gcse_file)
+                         {
+                           fprintf (gcse_file, "PRE/HOIST: edge (%d,%d), ",
+                                    bb->index,
+                                    INDEX_EDGE_SUCC_BB (edge_list, e)->index);
+                           fprintf (gcse_file, "copy expression %d\n",
+                                    expr->bitmap_index);
+                         }
 
-  if (pre_optimal)
-    sbitmap_vector_free (pre_optimal);
-  if (pre_redundant)
-    sbitmap_vector_free (pre_redundant);
-  if (pre_insert_map)
-    sbitmap_vector_free (pre_insert_map);
-  if (pre_delete_map)
-    sbitmap_vector_free (pre_delete_map);
-  if (ae_in)
-    sbitmap_vector_free (ae_in);
-  if (ae_out)
-    sbitmap_vector_free (ae_out);
+                       update_ld_motion_stores (expr);
+                       SET_BIT (inserted[e], j);
+                       did_insert = 1;
+                       gcse_create_count++;
+                     }
+                 }
+             }
+       }
+    }
 
-  transp = comp = NULL;
-  pre_optimal = pre_redundant = pre_insert_map = pre_delete_map = NULL;
-  ae_in = ae_out = NULL;
+  sbitmap_vector_free (inserted);
+  return did_insert;
 }
 
-/* Top level routine to do the dataflow analysis needed by PRE.  */
+/* Copy the result of EXPR->EXPR generated by INSN to EXPR->REACHING_REG.
+   Given "old_reg <- expr" (INSN), instead of adding after it
+     reaching_reg <- old_reg
+   it's better to do the following:
+     reaching_reg <- expr
+     old_reg      <- reaching_reg
+   because this way copy propagation can discover additional PRE
+   opportunities.  But if this fails, we try the old way.
+   When "expr" is a store, i.e.
+   given "MEM <- old_reg", instead of adding after it
+     reaching_reg <- old_reg
+   it's better to add it before as follows:
+     reaching_reg <- old_reg
+     MEM          <- reaching_reg.  */
 
 static void
-compute_pre_data ()
+pre_insert_copy_insn (struct expr *expr, rtx insn)
 {
-  sbitmap trapping_expr;
-  basic_block bb;
-  unsigned int ui;
-
-  compute_local_properties (transp, comp, antloc, &expr_hash_table);
-  sbitmap_vector_zero (ae_kill, last_basic_block);
+  rtx reg = expr->reaching_reg;
+  int regno = REGNO (reg);
+  int indx = expr->bitmap_index;
+  rtx pat = PATTERN (insn);
+  rtx set, new_insn;
+  rtx old_reg;
+  int i;
 
-  /* Collect expressions which might trap.  */
-  trapping_expr = sbitmap_alloc (expr_hash_table.n_elems);
-  sbitmap_zero (trapping_expr);
-  for (ui = 0; ui < expr_hash_table.size; ui++)
+  /* This block matches the logic in hash_scan_insn.  */
+  if (GET_CODE (pat) == SET)
+    set = pat;
+  else if (GET_CODE (pat) == PARALLEL)
     {
-      struct expr *e;
-      for (e = expr_hash_table.table[ui]; e != NULL; e = e->next_same_hash)
-       if (may_trap_p (e->expr))
-         SET_BIT (trapping_expr, e->bitmap_index);
+      /* Search through the parallel looking for the set whose
+        source was the expression that we're interested in.  */
+      set = NULL_RTX;
+      for (i = 0; i < XVECLEN (pat, 0); i++)
+       {
+         rtx x = XVECEXP (pat, 0, i);
+         if (GET_CODE (x) == SET
+             && expr_equiv_p (SET_SRC (x), expr->expr))
+           {
+             set = x;
+             break;
+           }
+       }
     }
+  else
+    abort ();
 
-  /* Compute ae_kill for each basic block using:
-
-     ~(TRANSP | COMP)
+  if (REG_P (SET_DEST (set)))
+    {
+      old_reg = SET_DEST (set);
+      /* Check if we can modify the set destination in the original insn.  */
+      if (validate_change (insn, &SET_DEST (set), reg, 0))
+        {
+          new_insn = gen_move_insn (old_reg, reg);
+          new_insn = emit_insn_after (new_insn, insn);
 
-     This is significantly faster than compute_ae_kill.  */
+          /* Keep register set table up to date.  */
+          replace_one_set (REGNO (old_reg), insn, new_insn);
+          record_one_set (regno, insn);
+        }
+      else
+        {
+          new_insn = gen_move_insn (reg, old_reg);
+          new_insn = emit_insn_after (new_insn, insn);
 
-  FOR_EACH_BB (bb)
+          /* Keep register set table up to date.  */
+          record_one_set (regno, new_insn);
+        }
+    }
+  else /* This is possible only in case of a store to memory.  */
     {
-      edge e;
+      old_reg = SET_SRC (set);
+      new_insn = gen_move_insn (reg, old_reg);
 
-      /* If the current block is the destination of an abnormal edge, we
-        kill all trapping expressions because we won't be able to properly
-        place the instruction on the edge.  So make them neither
-        anticipatable nor transparent.  This is fairly conservative.  */
-      for (e = bb->pred; e ; e = e->pred_next)
-       if (e->flags & EDGE_ABNORMAL)
-         {
-           sbitmap_difference (antloc[bb->index], antloc[bb->index], trapping_expr);
-           sbitmap_difference (transp[bb->index], transp[bb->index], trapping_expr);
-           break;
-         }
+      /* Check if we can modify the set source in the original insn.  */
+      if (validate_change (insn, &SET_SRC (set), reg, 0))
+        new_insn = emit_insn_before (new_insn, insn);
+      else
+        new_insn = emit_insn_after (new_insn, insn);
 
-      sbitmap_a_or_b (ae_kill[bb->index], transp[bb->index], comp[bb->index]);
-      sbitmap_not (ae_kill[bb->index], ae_kill[bb->index]);
+      /* Keep register set table up to date.  */
+      record_one_set (regno, new_insn);
     }
 
-  edge_list = pre_edge_lcm (gcse_file, expr_hash_table.n_elems, transp, comp, antloc,
-                           ae_kill, &pre_insert_map, &pre_delete_map);
-  sbitmap_vector_free (antloc);
-  antloc = NULL;
-  sbitmap_vector_free (ae_kill);
-  ae_kill = NULL;
-  sbitmap_free (trapping_expr);
+  gcse_create_count++;
+
+  if (gcse_file)
+    fprintf (gcse_file,
+            "PRE: bb %d, insn %d, copy expression %d in insn %d to reg %d\n",
+             BLOCK_NUM (insn), INSN_UID (new_insn), indx,
+             INSN_UID (insn), regno);
 }
-\f
-/* PRE utilities */
 
-/* Return nonzero if an occurrence of expression EXPR in OCCR_BB would reach
-   block BB.
+/* Copy available expressions that reach the redundant expression
+   to `reaching_reg'.  */
 
-   VISITED is a pointer to a working buffer for tracking which BB's have
-   been visited.  It is NULL for the top-level call.
+static void
+pre_insert_copies (void)
+{
+  unsigned int i, added_copy;
+  struct expr *expr;
+  struct occr *occr;
+  struct occr *avail;
 
-   We treat reaching expressions that go through blocks containing the same
-   reaching expression as "not reaching".  E.g. if EXPR is generated in blocks
-   2 and 3, INSN is in block 4, and 2->3->4, we treat the expression in block
-   2 as not reaching.  The intent is to improve the probability of finding
-   only one reaching expression and to reduce register lifetimes by picking
-   the closest such expression.  */
+  /* For each available expression in the table, copy the result to
+     `reaching_reg' if the expression reaches a deleted one.
 
-static int
-pre_expr_reaches_here_p_work (occr_bb, expr, bb, visited)
-     basic_block occr_bb;
-     struct expr *expr;
-     basic_block bb;
-     char *visited;
-{
-  edge pred;
+     ??? The current algorithm is rather brute force.
+     Need to do some profiling.  */
 
-  for (pred = bb->pred; pred != NULL; pred = pred->pred_next)
-    {
-      basic_block pred_bb = pred->src;
+  for (i = 0; i < expr_hash_table.size; i++)
+    for (expr = expr_hash_table.table[i]; expr != NULL; expr = expr->next_same_hash)
+      {
+       /* If the basic block isn't reachable, PPOUT will be TRUE.  However,
+          we don't want to insert a copy here because the expression may not
+          really be redundant.  So only insert an insn if the expression was
+          deleted.  This test also avoids further processing if the
+          expression wasn't deleted anywhere.  */
+       if (expr->reaching_reg == NULL)
+         continue;
 
-      if (pred->src == ENTRY_BLOCK_PTR
-         /* Has predecessor has already been visited?  */
-         || visited[pred_bb->index])
-       ;/* Nothing to do.  */
+       /* Set when we add a copy for that expression.  */
+       added_copy = 0;
 
-      /* Does this predecessor generate this expression?  */
-      else if (TEST_BIT (comp[pred_bb->index], expr->bitmap_index))
-       {
-         /* Is this the occurrence we're looking for?
-            Note that there's only one generating occurrence per block
-            so we just need to check the block number.  */
-         if (occr_bb == pred_bb)
-           return 1;
+       for (occr = expr->antic_occr; occr != NULL; occr = occr->next)
+         {
+           if (! occr->deleted_p)
+             continue;
 
-         visited[pred_bb->index] = 1;
-       }
-      /* Ignore this predecessor if it kills the expression.  */
-      else if (! TEST_BIT (transp[pred_bb->index], expr->bitmap_index))
-       visited[pred_bb->index] = 1;
+           for (avail = expr->avail_occr; avail != NULL; avail = avail->next)
+             {
+               rtx insn = avail->insn;
 
-      /* Neither gen nor kill.  */
-      else
-       {
-         visited[pred_bb->index] = 1;
-         if (pre_expr_reaches_here_p_work (occr_bb, expr, pred_bb, visited))
-           return 1;
-       }
-    }
+               /* No need to handle this one if handled already.  */
+               if (avail->copied_p)
+                 continue;
+
+               /* Don't handle this one if it's a redundant one.  */
+               if (TEST_BIT (pre_redundant_insns, INSN_CUID (insn)))
+                 continue;
+
+               /* Or if the expression doesn't reach the deleted one.  */
+               if (! pre_expr_reaches_here_p (BLOCK_FOR_INSN (avail->insn),
+                                              expr,
+                                              BLOCK_FOR_INSN (occr->insn)))
+                 continue;
+
+                added_copy = 1;
+
+               /* Copy the result of avail to reaching_reg.  */
+               pre_insert_copy_insn (expr, insn);
+               avail->copied_p = 1;
+             }
+         }
 
-  /* All paths have been checked.  */
-  return 0;
+         if (added_copy)
+            update_ld_motion_stores (expr);
+      }
 }
 
-/* The wrapper for pre_expr_reaches_here_work that ensures that any
-   memory allocated for that function is returned.  */
-
-static int
-pre_expr_reaches_here_p (occr_bb, expr, bb)
-     basic_block occr_bb;
-     struct expr *expr;
-     basic_block bb;
+/* Emit move from SRC to DEST noting the equivalence with expression computed
+   in INSN.  */
+static rtx
+gcse_emit_move_after (rtx src, rtx dest, rtx insn)
 {
-  int rval;
-  char *visited = (char *) xcalloc (last_basic_block, 1);
+  rtx new;
+  rtx set = single_set (insn), set2;
+  rtx note;
+  rtx eqv;
 
-  rval = pre_expr_reaches_here_p_work (occr_bb, expr, bb, visited);
+  /* This should never fail since we're creating a reg->reg copy
+     we've verified to be valid.  */
 
-  free (visited);
-  return rval;
+  new = emit_insn_after (gen_move_insn (dest, src), insn);
+
+  /* Note the equivalence for local CSE pass.  */
+  set2 = single_set (new);
+  if (!set2 || !rtx_equal_p (SET_DEST (set2), dest))
+    return new;
+  if ((note = find_reg_equal_equiv_note (insn)))
+    eqv = XEXP (note, 0);
+  else
+    eqv = SET_SRC (set);
+
+  set_unique_reg_note (new, REG_EQUAL, copy_insn_1 (eqv));
+
+  return new;
 }
-\f
 
-/* Given an expr, generate RTL which we can insert at the end of a BB,
-   or on an edge.  Set the block number of any insns generated to
-   the value of BB.  */
+/* Delete redundant computations.
+   Deletion is done by changing the insn to copy the `reaching_reg' of
+   the expression into the result of the SET.  It is left to later passes
+   (cprop, cse2, flow, combine, regmove) to propagate the copy or eliminate it.
 
-static rtx
-process_insert_insn (expr)
-     struct expr *expr;
+   Returns nonzero if a change is made.  */
+
+static int
+pre_delete (void)
 {
-  rtx reg = expr->reaching_reg;
-  rtx exp = copy_rtx (expr->expr);
-  rtx pat;
+  unsigned int i;
+  int changed;
+  struct expr *expr;
+  struct occr *occr;
 
-  start_sequence ();
+  changed = 0;
+  for (i = 0; i < expr_hash_table.size; i++)
+    for (expr = expr_hash_table.table[i];
+        expr != NULL;
+        expr = expr->next_same_hash)
+      {
+       int indx = expr->bitmap_index;
 
-  /* If the expression is something that's an operand, like a constant,
-     just copy it to a register.  */
-  if (general_operand (exp, GET_MODE (reg)))
-    emit_move_insn (reg, exp);
+       /* We only need to search antic_occr since we require
+          ANTLOC != 0.  */
 
-  /* Otherwise, make a new insn to compute this expression and make sure the
-     insn will be recognized (this also adds any needed CLOBBERs).  Copy the
-     expression to make sure we don't have any sharing issues.  */
-  else if (insn_invalid_p (emit_insn (gen_rtx_SET (VOIDmode, reg, exp))))
-    abort ();
+       for (occr = expr->antic_occr; occr != NULL; occr = occr->next)
+         {
+           rtx insn = occr->insn;
+           rtx set;
+           basic_block bb = BLOCK_FOR_INSN (insn);
 
-  pat = get_insns ();
-  end_sequence ();
+           /* We only delete insns that have a single_set.  */
+           if (TEST_BIT (pre_delete_map[bb->index], indx)
+               && (set = single_set (insn)) != 0)
+             {
+               /* Create a pseudo-reg to store the result of reaching
+                  expressions into.  Get the mode for the new pseudo from
+                  the mode of the original destination pseudo.  */
+               if (expr->reaching_reg == NULL)
+                 expr->reaching_reg
+                   = gen_reg_rtx (GET_MODE (SET_DEST (set)));
 
-  return pat;
+               gcse_emit_move_after (expr->reaching_reg, SET_DEST (set), insn);
+               delete_insn (insn);
+               occr->deleted_p = 1;
+               SET_BIT (pre_redundant_insns, INSN_CUID (insn));
+               changed = 1;
+               gcse_subst_count++;
+
+               if (gcse_file)
+                 {
+                   fprintf (gcse_file,
+                            "PRE: redundant insn %d (expression %d) in ",
+                              INSN_UID (insn), indx);
+                   fprintf (gcse_file, "bb %d, reaching reg is %d\n",
+                            bb->index, REGNO (expr->reaching_reg));
+                 }
+             }
+         }
+      }
+
+  return changed;
 }
 
-/* Add EXPR to the end of basic block BB.
+/* Perform GCSE optimizations using PRE.
+   This is called by one_pre_gcse_pass after all the dataflow analysis
+   has been done.
 
-   This is used by both the PRE and code hoisting.
+   This is based on the original Morel-Renvoise paper Fred Chow's thesis, and
+   lazy code motion from Knoop, Ruthing and Steffen as described in Advanced
+   Compiler Design and Implementation.
 
-   For PRE, we want to verify that the expr is either transparent
-   or locally anticipatable in the target block.  This check makes
-   no sense for code hoisting.  */
+   ??? A new pseudo reg is created to hold the reaching expression.  The nice
+   thing about the classical approach is that it would try to use an existing
+   reg.  If the register can't be adequately optimized [i.e. we introduce
+   reload problems], one could add a pass here to propagate the new register
+   through the block.
 
-static void
-insert_insn_end_bb (expr, bb, pre)
-     struct expr *expr;
-     basic_block bb;
-     int pre;
+   ??? We don't handle single sets in PARALLELs because we're [currently] not
+   able to copy the rest of the parallel when we insert copies to create full
+   redundancies from partial redundancies.  However, there's no reason why we
+   can't handle PARALLELs in the cases where there are no partial
+   redundancies.  */
+
+static int
+pre_gcse (void)
 {
-  rtx insn = bb->end;
-  rtx new_insn;
-  rtx reg = expr->reaching_reg;
-  int regno = REGNO (reg);
-  rtx pat, pat_end;
+  unsigned int i;
+  int did_insert, changed;
+  struct expr **index_map;
+  struct expr *expr;
 
-  pat = process_insert_insn (expr);
-  if (pat == NULL_RTX || ! INSN_P (pat))
-    abort ();
+  /* Compute a mapping from expression number (`bitmap_index') to
+     hash table entry.  */
 
-  pat_end = pat;
-  while (NEXT_INSN (pat_end) != NULL_RTX)
-    pat_end = NEXT_INSN (pat_end);
+  index_map = xcalloc (expr_hash_table.n_elems, sizeof (struct expr *));
+  for (i = 0; i < expr_hash_table.size; i++)
+    for (expr = expr_hash_table.table[i]; expr != NULL; expr = expr->next_same_hash)
+      index_map[expr->bitmap_index] = expr;
 
-  /* If the last insn is a jump, insert EXPR in front [taking care to
-     handle cc0, etc. properly].  Similary we need to care trapping
-     instructions in presence of non-call exceptions.  */
+  /* Reset bitmap used to track which insns are redundant.  */
+  pre_redundant_insns = sbitmap_alloc (max_cuid);
+  sbitmap_zero (pre_redundant_insns);
 
-  if (GET_CODE (insn) == JUMP_INSN
-      || (GET_CODE (insn) == INSN
-         && (bb->succ->succ_next || (bb->succ->flags & EDGE_ABNORMAL))))
-    {
-#ifdef HAVE_cc0
-      rtx note;
-#endif
-      /* It should always be the case that we can put these instructions
-        anywhere in the basic block with performing PRE optimizations.
-        Check this.  */
-      if (GET_CODE (insn) == INSN && pre
-         && !TEST_BIT (antloc[bb->index], expr->bitmap_index)
-         && !TEST_BIT (transp[bb->index], expr->bitmap_index))
-       abort ();
+  /* Delete the redundant insns first so that
+     - we know what register to use for the new insns and for the other
+       ones with reaching expressions
+     - we know which insns are redundant when we go to create copies  */
 
-      /* If this is a jump table, then we can't insert stuff here.  Since
-        we know the previous real insn must be the tablejump, we insert
-        the new instruction just before the tablejump.  */
-      if (GET_CODE (PATTERN (insn)) == ADDR_VEC
-         || GET_CODE (PATTERN (insn)) == ADDR_DIFF_VEC)
-       insn = prev_real_insn (insn);
+  changed = pre_delete ();
 
-#ifdef HAVE_cc0
-      /* FIXME: 'twould be nice to call prev_cc0_setter here but it aborts
-        if cc0 isn't set.  */
-      note = find_reg_note (insn, REG_CC_SETTER, NULL_RTX);
-      if (note)
-       insn = XEXP (note, 0);
-      else
-       {
-         rtx maybe_cc0_setter = prev_nonnote_insn (insn);
-         if (maybe_cc0_setter
-             && INSN_P (maybe_cc0_setter)
-             && sets_cc0_p (PATTERN (maybe_cc0_setter)))
-           insn = maybe_cc0_setter;
-       }
-#endif
-      /* FIXME: What if something in cc0/jump uses value set in new insn?  */
-      new_insn = emit_insn_before (pat, insn);
-    }
+  did_insert = pre_edge_insert (edge_list, index_map);
 
-  /* Likewise if the last insn is a call, as will happen in the presence
-     of exception handling.  */
-  else if (GET_CODE (insn) == CALL_INSN
-          && (bb->succ->succ_next || (bb->succ->flags & EDGE_ABNORMAL)))
+  /* In other places with reaching expressions, copy the expression to the
+     specially allocated pseudo-reg that reaches the redundant expr.  */
+  pre_insert_copies ();
+  if (did_insert)
     {
-      /* Keeping in mind SMALL_REGISTER_CLASSES and parameters in registers,
-        we search backward and place the instructions before the first
-        parameter is loaded.  Do this for everyone for consistency and a
-        presumption that we'll get better code elsewhere as well.
+      commit_edge_insertions ();
+      changed = 1;
+    }
 
-        It should always be the case that we can put these instructions
-        anywhere in the basic block with performing PRE optimizations.
-        Check this.  */
+  free (index_map);
+  sbitmap_free (pre_redundant_insns);
+  return changed;
+}
 
-      if (pre
-         && !TEST_BIT (antloc[bb->index], expr->bitmap_index)
-         && !TEST_BIT (transp[bb->index], expr->bitmap_index))
-       abort ();
+/* Top level routine to perform one PRE GCSE pass.
 
-      /* Since different machines initialize their parameter registers
-        in different orders, assume nothing.  Collect the set of all
-        parameter registers.  */
-      insn = find_first_parameter_load (insn, bb->head);
+   Return nonzero if a change was made.  */
 
-      /* If we found all the parameter loads, then we want to insert
-        before the first parameter load.
+static int
+one_pre_gcse_pass (int pass)
+{
+  int changed = 0;
 
-        If we did not find all the parameter loads, then we might have
-        stopped on the head of the block, which could be a CODE_LABEL.
-        If we inserted before the CODE_LABEL, then we would be putting
-        the insn in the wrong basic block.  In that case, put the insn
-        after the CODE_LABEL.  Also, respect NOTE_INSN_BASIC_BLOCK.  */
-      while (GET_CODE (insn) == CODE_LABEL
-            || NOTE_INSN_BASIC_BLOCK_P (insn))
-       insn = NEXT_INSN (insn);
+  gcse_subst_count = 0;
+  gcse_create_count = 0;
 
-      new_insn = emit_insn_before (pat, insn);
-    }
-  else
-    new_insn = emit_insn_after (pat, insn);
+  alloc_hash_table (max_cuid, &expr_hash_table, 0);
+  add_noreturn_fake_exit_edges ();
+  if (flag_gcse_lm)
+    compute_ld_motion_mems ();
 
-  while (1)
+  compute_hash_table (&expr_hash_table);
+  trim_ld_motion_mems ();
+  if (gcse_file)
+    dump_hash_table (gcse_file, "Expression", &expr_hash_table);
+
+  if (expr_hash_table.n_elems > 0)
     {
-      if (INSN_P (pat))
-       {
-         add_label_notes (PATTERN (pat), new_insn);
-         note_stores (PATTERN (pat), record_set_info, pat);
-       }
-      if (pat == pat_end)
-       break;
-      pat = NEXT_INSN (pat);
+      alloc_pre_mem (last_basic_block, expr_hash_table.n_elems);
+      compute_pre_data ();
+      changed |= pre_gcse ();
+      free_edge_list (edge_list);
+      free_pre_mem ();
     }
 
-  gcse_create_count++;
+  free_ldst_mems ();
+  remove_fake_edges ();
+  free_hash_table (&expr_hash_table);
 
   if (gcse_file)
     {
-      fprintf (gcse_file, "PRE/HOIST: end of bb %d, insn %d, ",
-              bb->index, INSN_UID (new_insn));
-      fprintf (gcse_file, "copying expression %d to reg %d\n",
-              expr->bitmap_index, regno);
+      fprintf (gcse_file, "\nPRE GCSE of %s, pass %d: %d bytes needed, ",
+              current_function_name (), pass, bytes_used);
+      fprintf (gcse_file, "%d substs, %d insns created\n",
+              gcse_subst_count, gcse_create_count);
     }
+
+  return changed;
 }
+\f
+/* If X contains any LABEL_REF's, add REG_LABEL notes for them to INSN.
+   If notes are added to an insn which references a CODE_LABEL, the
+   LABEL_NUSES count is incremented.  We have to add REG_LABEL notes,
+   because the following loop optimization pass requires them.  */
 
-/* Insert partially redundant expressions on edges in the CFG to make
-   the expressions fully redundant.  */
+/* ??? This is very similar to the loop.c add_label_notes function.  We
+   could probably share code here.  */
 
-static int
-pre_edge_insert (edge_list, index_map)
-     struct edge_list *edge_list;
-     struct expr **index_map;
+/* ??? If there was a jump optimization pass after gcse and before loop,
+   then we would not need to do this here, because jump would add the
+   necessary REG_LABEL notes.  */
+
+static void
+add_label_notes (rtx x, rtx insn)
 {
-  int e, i, j, num_edges, set_size, did_insert = 0;
-  sbitmap *inserted;
+  enum rtx_code code = GET_CODE (x);
+  int i, j;
+  const char *fmt;
 
-  /* Where PRE_INSERT_MAP is nonzero, we add the expression on that edge
-     if it reaches any of the deleted expressions.  */
+  if (code == LABEL_REF && !LABEL_REF_NONLOCAL_P (x))
+    {
+      /* This code used to ignore labels that referred to dispatch tables to
+        avoid flow generating (slightly) worse code.
 
-  set_size = pre_insert_map[0]->size;
-  num_edges = NUM_EDGES (edge_list);
-  inserted = sbitmap_vector_alloc (num_edges, expr_hash_table.n_elems);
-  sbitmap_vector_zero (inserted, num_edges);
+        We no longer ignore such label references (see LABEL_REF handling in
+        mark_jump_label for additional information).  */
 
-  for (e = 0; e < num_edges; e++)
+      REG_NOTES (insn) = gen_rtx_INSN_LIST (REG_LABEL, XEXP (x, 0),
+                                           REG_NOTES (insn));
+      if (LABEL_P (XEXP (x, 0)))
+       LABEL_NUSES (XEXP (x, 0))++;
+      return;
+    }
+
+  for (i = GET_RTX_LENGTH (code) - 1, fmt = GET_RTX_FORMAT (code); i >= 0; i--)
     {
-      int indx;
-      basic_block bb = INDEX_EDGE_PRED_BB (edge_list, e);
+      if (fmt[i] == 'e')
+       add_label_notes (XEXP (x, i), insn);
+      else if (fmt[i] == 'E')
+       for (j = XVECLEN (x, i) - 1; j >= 0; j--)
+         add_label_notes (XVECEXP (x, i, j), insn);
+    }
+}
 
-      for (i = indx = 0; i < set_size; i++, indx += SBITMAP_ELT_BITS)
-       {
-         SBITMAP_ELT_TYPE insert = pre_insert_map[e]->elms[i];
+/* Compute transparent outgoing information for each block.
 
-         for (j = indx; insert && j < (int) expr_hash_table.n_elems; j++, insert >>= 1)
-           if ((insert & 1) != 0 && index_map[j]->reaching_reg != NULL_RTX)
-             {
-               struct expr *expr = index_map[j];
-               struct occr *occr;
+   An expression is transparent to an edge unless it is killed by
+   the edge itself.  This can only happen with abnormal control flow,
+   when the edge is traversed through a call.  This happens with
+   non-local labels and exceptions.
 
-               /* Now look at each deleted occurrence of this expression.  */
-               for (occr = expr->antic_occr; occr != NULL; occr = occr->next)
-                 {
-                   if (! occr->deleted_p)
-                     continue;
+   This would not be necessary if we split the edge.  While this is
+   normally impossible for abnormal critical edges, with some effort
+   it should be possible with exception handling, since we still have
+   control over which handler should be invoked.  But due to increased
+   EH table sizes, this may not be worthwhile.  */
 
-                   /* Insert this expression on this edge if if it would
-                      reach the deleted occurrence in BB.  */
-                   if (!TEST_BIT (inserted[e], j))
-                     {
-                       rtx insn;
-                       edge eg = INDEX_EDGE (edge_list, e);
+static void
+compute_transpout (void)
+{
+  basic_block bb;
+  unsigned int i;
+  struct expr *expr;
 
-                       /* We can't insert anything on an abnormal and
-                          critical edge, so we insert the insn at the end of
-                          the previous block. There are several alternatives
-                          detailed in Morgans book P277 (sec 10.5) for
-                          handling this situation.  This one is easiest for
-                          now.  */
+  sbitmap_vector_ones (transpout, last_basic_block);
 
-                       if ((eg->flags & EDGE_ABNORMAL) == EDGE_ABNORMAL)
-                         insert_insn_end_bb (index_map[j], bb, 0);
-                       else
-                         {
-                           insn = process_insert_insn (index_map[j]);
-                           insert_insn_on_edge (insn, eg);
-                         }
+  FOR_EACH_BB (bb)
+    {
+      /* Note that flow inserted a nop a the end of basic blocks that
+        end in call instructions for reasons other than abnormal
+        control flow.  */
+      if (! CALL_P (BB_END (bb)))
+       continue;
 
-                       if (gcse_file)
-                         {
-                           fprintf (gcse_file, "PRE/HOIST: edge (%d,%d), ",
-                                    bb->index,
-                                    INDEX_EDGE_SUCC_BB (edge_list, e)->index);
-                           fprintf (gcse_file, "copy expression %d\n",
-                                    expr->bitmap_index);
-                         }
+      for (i = 0; i < expr_hash_table.size; i++)
+       for (expr = expr_hash_table.table[i]; expr ; expr = expr->next_same_hash)
+         if (MEM_P (expr->expr))
+           {
+             if (GET_CODE (XEXP (expr->expr, 0)) == SYMBOL_REF
+                 && CONSTANT_POOL_ADDRESS_P (XEXP (expr->expr, 0)))
+               continue;
 
-                       update_ld_motion_stores (expr);
-                       SET_BIT (inserted[e], j);
-                       did_insert = 1;
-                       gcse_create_count++;
-                     }
-                 }
-             }
-       }
+             /* ??? Optimally, we would use interprocedural alias
+                analysis to determine if this mem is actually killed
+                by this call.  */
+             RESET_BIT (transpout[bb->index], expr->bitmap_index);
+           }
     }
-
-  sbitmap_vector_free (inserted);
-  return did_insert;
 }
 
-/* Copy the result of INSN to REG.  INDX is the expression number.  */
+/* Code Hoisting variables and subroutines.  */
+
+/* Very busy expressions.  */
+static sbitmap *hoist_vbein;
+static sbitmap *hoist_vbeout;
+
+/* Hoistable expressions.  */
+static sbitmap *hoist_exprs;
+
+/* ??? We could compute post dominators and run this algorithm in
+   reverse to perform tail merging, doing so would probably be
+   more effective than the tail merging code in jump.c.
+
+   It's unclear if tail merging could be run in parallel with
+   code hoisting.  It would be nice.  */
+
+/* Allocate vars used for code hoisting analysis.  */
 
 static void
-pre_insert_copy_insn (expr, insn)
-     struct expr *expr;
-     rtx insn;
+alloc_code_hoist_mem (int n_blocks, int n_exprs)
 {
-  rtx reg = expr->reaching_reg;
-  int regno = REGNO (reg);
-  int indx = expr->bitmap_index;
-  rtx set = single_set (insn);
-  rtx new_insn;
+  antloc = sbitmap_vector_alloc (n_blocks, n_exprs);
+  transp = sbitmap_vector_alloc (n_blocks, n_exprs);
+  comp = sbitmap_vector_alloc (n_blocks, n_exprs);
 
-  if (!set)
-    abort ();
+  hoist_vbein = sbitmap_vector_alloc (n_blocks, n_exprs);
+  hoist_vbeout = sbitmap_vector_alloc (n_blocks, n_exprs);
+  hoist_exprs = sbitmap_vector_alloc (n_blocks, n_exprs);
+  transpout = sbitmap_vector_alloc (n_blocks, n_exprs);
+}
 
-  new_insn = emit_insn_after (gen_move_insn (reg, SET_DEST (set)), insn);
+/* Free vars used for code hoisting analysis.  */
 
-  /* Keep register set table up to date.  */
-  record_one_set (regno, new_insn);
+static void
+free_code_hoist_mem (void)
+{
+  sbitmap_vector_free (antloc);
+  sbitmap_vector_free (transp);
+  sbitmap_vector_free (comp);
 
-  gcse_create_count++;
+  sbitmap_vector_free (hoist_vbein);
+  sbitmap_vector_free (hoist_vbeout);
+  sbitmap_vector_free (hoist_exprs);
+  sbitmap_vector_free (transpout);
 
-  if (gcse_file)
-    fprintf (gcse_file,
-            "PRE: bb %d, insn %d, copy expression %d in insn %d to reg %d\n",
-             BLOCK_NUM (insn), INSN_UID (new_insn), indx,
-             INSN_UID (insn), regno);
-  update_ld_motion_stores (expr);
+  free_dominance_info (CDI_DOMINATORS);
 }
 
-/* Copy available expressions that reach the redundant expression
-   to `reaching_reg'.  */
+/* Compute the very busy expressions at entry/exit from each block.
+
+   An expression is very busy if all paths from a given point
+   compute the expression.  */
 
 static void
-pre_insert_copies ()
+compute_code_hoist_vbeinout (void)
 {
-  unsigned int i;
-  struct expr *expr;
-  struct occr *occr;
-  struct occr *avail;
-
-  /* For each available expression in the table, copy the result to
-     `reaching_reg' if the expression reaches a deleted one.
+  int changed, passes;
+  basic_block bb;
 
-     ??? The current algorithm is rather brute force.
-     Need to do some profiling.  */
+  sbitmap_vector_zero (hoist_vbeout, last_basic_block);
+  sbitmap_vector_zero (hoist_vbein, last_basic_block);
 
-  for (i = 0; i < expr_hash_table.size; i++)
-    for (expr = expr_hash_table.table[i]; expr != NULL; expr = expr->next_same_hash)
-      {
-       /* If the basic block isn't reachable, PPOUT will be TRUE.  However,
-          we don't want to insert a copy here because the expression may not
-          really be redundant.  So only insert an insn if the expression was
-          deleted.  This test also avoids further processing if the
-          expression wasn't deleted anywhere.  */
-       if (expr->reaching_reg == NULL)
-         continue;
+  passes = 0;
+  changed = 1;
 
-       for (occr = expr->antic_occr; occr != NULL; occr = occr->next)
-         {
-           if (! occr->deleted_p)
-             continue;
+  while (changed)
+    {
+      changed = 0;
 
-           for (avail = expr->avail_occr; avail != NULL; avail = avail->next)
-             {
-               rtx insn = avail->insn;
+      /* We scan the blocks in the reverse order to speed up
+        the convergence.  */
+      FOR_EACH_BB_REVERSE (bb)
+       {
+         changed |= sbitmap_a_or_b_and_c_cg (hoist_vbein[bb->index], antloc[bb->index],
+                                             hoist_vbeout[bb->index], transp[bb->index]);
+         if (bb->next_bb != EXIT_BLOCK_PTR)
+           sbitmap_intersection_of_succs (hoist_vbeout[bb->index], hoist_vbein, bb->index);
+       }
 
-               /* No need to handle this one if handled already.  */
-               if (avail->copied_p)
-                 continue;
+      passes++;
+    }
 
-               /* Don't handle this one if it's a redundant one.  */
-               if (TEST_BIT (pre_redundant_insns, INSN_CUID (insn)))
-                 continue;
+  if (gcse_file)
+    fprintf (gcse_file, "hoisting vbeinout computation: %d passes\n", passes);
+}
 
-               /* Or if the expression doesn't reach the deleted one.  */
-               if (! pre_expr_reaches_here_p (BLOCK_FOR_INSN (avail->insn),
-                                              expr,
-                                              BLOCK_FOR_INSN (occr->insn)))
-                 continue;
+/* Top level routine to do the dataflow analysis needed by code hoisting.  */
 
-               /* Copy the result of avail to reaching_reg.  */
-               pre_insert_copy_insn (expr, insn);
-               avail->copied_p = 1;
-             }
-         }
-      }
+static void
+compute_code_hoist_data (void)
+{
+  compute_local_properties (transp, comp, antloc, &expr_hash_table);
+  compute_transpout ();
+  compute_code_hoist_vbeinout ();
+  calculate_dominance_info (CDI_DOMINATORS);
+  if (gcse_file)
+    fprintf (gcse_file, "\n");
 }
 
-/* Emit move from SRC to DEST noting the equivalence with expression computed
-   in INSN.  */
-static rtx
-gcse_emit_move_after (src, dest, insn)
-     rtx src, dest, insn;
+/* Determine if the expression identified by EXPR_INDEX would
+   reach BB unimpared if it was placed at the end of EXPR_BB.
+
+   It's unclear exactly what Muchnick meant by "unimpared".  It seems
+   to me that the expression must either be computed or transparent in
+   *every* block in the path(s) from EXPR_BB to BB.  Any other definition
+   would allow the expression to be hoisted out of loops, even if
+   the expression wasn't a loop invariant.
+
+   Contrast this to reachability for PRE where an expression is
+   considered reachable if *any* path reaches instead of *all*
+   paths.  */
+
+static int
+hoist_expr_reaches_here_p (basic_block expr_bb, int expr_index, basic_block bb, char *visited)
 {
-  rtx new;
-  rtx set = single_set (insn), set2;
-  rtx note;
-  rtx eqv;
+  edge pred;
+  int visited_allocated_locally = 0;
 
-  /* This should never fail since we're creating a reg->reg copy
-     we've verified to be valid.  */
 
-  new = emit_insn_after (gen_move_insn (dest, src), insn);
+  if (visited == NULL)
+    {
+      visited_allocated_locally = 1;
+      visited = xcalloc (last_basic_block, 1);
+    }
 
-  /* Note the equivalence for local CSE pass.  */
-  set2 = single_set (new);
-  if (!set2 || !rtx_equal_p (SET_DEST (set2), dest))
-    return new;
-  if ((note = find_reg_equal_equiv_note (insn)))
-    eqv = XEXP (note, 0);
-  else
-    eqv = SET_SRC (set);
+  for (pred = bb->pred; pred != NULL; pred = pred->pred_next)
+    {
+      basic_block pred_bb = pred->src;
 
-  set_unique_reg_note (new, REG_EQUAL, copy_insn_1 (eqv));
+      if (pred->src == ENTRY_BLOCK_PTR)
+       break;
+      else if (pred_bb == expr_bb)
+       continue;
+      else if (visited[pred_bb->index])
+       continue;
 
-  return new;
-}
+      /* Does this predecessor generate this expression?  */
+      else if (TEST_BIT (comp[pred_bb->index], expr_index))
+       break;
+      else if (! TEST_BIT (transp[pred_bb->index], expr_index))
+       break;
 
-/* Delete redundant computations.
-   Deletion is done by changing the insn to copy the `reaching_reg' of
-   the expression into the result of the SET.  It is left to later passes
-   (cprop, cse2, flow, combine, regmove) to propagate the copy or eliminate it.
+      /* Not killed.  */
+      else
+       {
+         visited[pred_bb->index] = 1;
+         if (! hoist_expr_reaches_here_p (expr_bb, expr_index,
+                                          pred_bb, visited))
+           break;
+       }
+    }
+  if (visited_allocated_locally)
+    free (visited);
 
-   Returns nonzero if a change is made.  */
+  return (pred == NULL);
+}
+\f
+/* Actually perform code hoisting.  */
 
-static int
-pre_delete ()
+static void
+hoist_code (void)
 {
-  unsigned int i;
-  int changed;
+  basic_block bb, dominated;
+  basic_block *domby;
+  unsigned int domby_len;
+  unsigned int i,j;
+  struct expr **index_map;
   struct expr *expr;
-  struct occr *occr;
-
-  changed = 0;
-  for (i = 0; i < expr_hash_table.size; i++)
-    for (expr = expr_hash_table.table[i]; expr != NULL; expr = expr->next_same_hash)
-      {
-       int indx = expr->bitmap_index;
 
-       /* We only need to search antic_occr since we require
-          ANTLOC != 0.  */
-
-       for (occr = expr->antic_occr; occr != NULL; occr = occr->next)
-         {
-           rtx insn = occr->insn;
-           rtx set;
-           basic_block bb = BLOCK_FOR_INSN (insn);
+  sbitmap_vector_zero (hoist_exprs, last_basic_block);
 
-           if (TEST_BIT (pre_delete_map[bb->index], indx))
-             {
-               set = single_set (insn);
-               if (! set)
-                 abort ();
+  /* Compute a mapping from expression number (`bitmap_index') to
+     hash table entry.  */
 
-               /* Create a pseudo-reg to store the result of reaching
-                  expressions into.  Get the mode for the new pseudo from
-                  the mode of the original destination pseudo.  */
-               if (expr->reaching_reg == NULL)
-                 expr->reaching_reg
-                   = gen_reg_rtx (GET_MODE (SET_DEST (set)));
+  index_map = xcalloc (expr_hash_table.n_elems, sizeof (struct expr *));
+  for (i = 0; i < expr_hash_table.size; i++)
+    for (expr = expr_hash_table.table[i]; expr != NULL; expr = expr->next_same_hash)
+      index_map[expr->bitmap_index] = expr;
 
-               gcse_emit_move_after (expr->reaching_reg, SET_DEST (set), insn);
-               delete_insn (insn);
-               occr->deleted_p = 1;
-               SET_BIT (pre_redundant_insns, INSN_CUID (insn));
-               changed = 1;
-               gcse_subst_count++;
+  /* Walk over each basic block looking for potentially hoistable
+     expressions, nothing gets hoisted from the entry block.  */
+  FOR_EACH_BB (bb)
+    {
+      int found = 0;
+      int insn_inserted_p;
 
-               if (gcse_file)
-                 {
-                   fprintf (gcse_file,
-                            "PRE: redundant insn %d (expression %d) in ",
-                              INSN_UID (insn), indx);
-                   fprintf (gcse_file, "bb %d, reaching reg is %d\n",
-                            bb->index, REGNO (expr->reaching_reg));
-                 }
-             }
-         }
-      }
+      domby_len = get_dominated_by (CDI_DOMINATORS, bb, &domby);
+      /* Examine each expression that is very busy at the exit of this
+        block.  These are the potentially hoistable expressions.  */
+      for (i = 0; i < hoist_vbeout[bb->index]->n_bits; i++)
+       {
+         int hoistable = 0;
 
-  return changed;
-}
+         if (TEST_BIT (hoist_vbeout[bb->index], i)
+             && TEST_BIT (transpout[bb->index], i))
+           {
+             /* We've found a potentially hoistable expression, now
+                we look at every block BB dominates to see if it
+                computes the expression.  */
+             for (j = 0; j < domby_len; j++)
+               {
+                 dominated = domby[j];
+                 /* Ignore self dominance.  */
+                 if (bb == dominated)
+                   continue;
+                 /* We've found a dominated block, now see if it computes
+                    the busy expression and whether or not moving that
+                    expression to the "beginning" of that block is safe.  */
+                 if (!TEST_BIT (antloc[dominated->index], i))
+                   continue;
 
-/* Perform GCSE optimizations using PRE.
-   This is called by one_pre_gcse_pass after all the dataflow analysis
-   has been done.
+                 /* Note if the expression would reach the dominated block
+                    unimpared if it was placed at the end of BB.
 
-   This is based on the original Morel-Renvoise paper Fred Chow's thesis, and
-   lazy code motion from Knoop, Ruthing and Steffen as described in Advanced
-   Compiler Design and Implementation.
+                    Keep track of how many times this expression is hoistable
+                    from a dominated block into BB.  */
+                 if (hoist_expr_reaches_here_p (bb, i, dominated, NULL))
+                   hoistable++;
+               }
 
-   ??? A new pseudo reg is created to hold the reaching expression.  The nice
-   thing about the classical approach is that it would try to use an existing
-   reg.  If the register can't be adequately optimized [i.e. we introduce
-   reload problems], one could add a pass here to propagate the new register
-   through the block.
+             /* If we found more than one hoistable occurrence of this
+                expression, then note it in the bitmap of expressions to
+                hoist.  It makes no sense to hoist things which are computed
+                in only one BB, and doing so tends to pessimize register
+                allocation.  One could increase this value to try harder
+                to avoid any possible code expansion due to register
+                allocation issues; however experiments have shown that
+                the vast majority of hoistable expressions are only movable
+                from two successors, so raising this threshold is likely
+                to nullify any benefit we get from code hoisting.  */
+             if (hoistable > 1)
+               {
+                 SET_BIT (hoist_exprs[bb->index], i);
+                 found = 1;
+               }
+           }
+       }
+      /* If we found nothing to hoist, then quit now.  */
+      if (! found)
+        {
+         free (domby);
+       continue;
+       }
 
-   ??? We don't handle single sets in PARALLELs because we're [currently] not
-   able to copy the rest of the parallel when we insert copies to create full
-   redundancies from partial redundancies.  However, there's no reason why we
-   can't handle PARALLELs in the cases where there are no partial
-   redundancies.  */
+      /* Loop over all the hoistable expressions.  */
+      for (i = 0; i < hoist_exprs[bb->index]->n_bits; i++)
+       {
+         /* We want to insert the expression into BB only once, so
+            note when we've inserted it.  */
+         insn_inserted_p = 0;
 
-static int
-pre_gcse ()
-{
-  unsigned int i;
-  int did_insert, changed;
-  struct expr **index_map;
-  struct expr *expr;
+         /* These tests should be the same as the tests above.  */
+         if (TEST_BIT (hoist_vbeout[bb->index], i))
+           {
+             /* We've found a potentially hoistable expression, now
+                we look at every block BB dominates to see if it
+                computes the expression.  */
+             for (j = 0; j < domby_len; j++)
+               {
+                 dominated = domby[j];
+                 /* Ignore self dominance.  */
+                 if (bb == dominated)
+                   continue;
 
-  /* Compute a mapping from expression number (`bitmap_index') to
-     hash table entry.  */
+                 /* We've found a dominated block, now see if it computes
+                    the busy expression and whether or not moving that
+                    expression to the "beginning" of that block is safe.  */
+                 if (!TEST_BIT (antloc[dominated->index], i))
+                   continue;
 
-  index_map = (struct expr **) xcalloc (expr_hash_table.n_elems, sizeof (struct expr *));
-  for (i = 0; i < expr_hash_table.size; i++)
-    for (expr = expr_hash_table.table[i]; expr != NULL; expr = expr->next_same_hash)
-      index_map[expr->bitmap_index] = expr;
+                 /* The expression is computed in the dominated block and
+                    it would be safe to compute it at the start of the
+                    dominated block.  Now we have to determine if the
+                    expression would reach the dominated block if it was
+                    placed at the end of BB.  */
+                 if (hoist_expr_reaches_here_p (bb, i, dominated, NULL))
+                   {
+                     struct expr *expr = index_map[i];
+                     struct occr *occr = expr->antic_occr;
+                     rtx insn;
+                     rtx set;
 
-  /* Reset bitmap used to track which insns are redundant.  */
-  pre_redundant_insns = sbitmap_alloc (max_cuid);
-  sbitmap_zero (pre_redundant_insns);
+                     /* Find the right occurrence of this expression.  */
+                     while (BLOCK_FOR_INSN (occr->insn) != dominated && occr)
+                       occr = occr->next;
 
-  /* Delete the redundant insns first so that
-     - we know what register to use for the new insns and for the other
-       ones with reaching expressions
-     - we know which insns are redundant when we go to create copies  */
+                     /* Should never happen.  */
+                     if (!occr)
+                       abort ();
 
-  changed = pre_delete ();
+                     insn = occr->insn;
 
-  did_insert = pre_edge_insert (edge_list, index_map);
+                     set = single_set (insn);
+                     if (! set)
+                       abort ();
 
-  /* In other places with reaching expressions, copy the expression to the
-     specially allocated pseudo-reg that reaches the redundant expr.  */
-  pre_insert_copies ();
-  if (did_insert)
-    {
-      commit_edge_insertions ();
-      changed = 1;
+                     /* Create a pseudo-reg to store the result of reaching
+                        expressions into.  Get the mode for the new pseudo
+                        from the mode of the original destination pseudo.  */
+                     if (expr->reaching_reg == NULL)
+                       expr->reaching_reg
+                         = gen_reg_rtx (GET_MODE (SET_DEST (set)));
+
+                     gcse_emit_move_after (expr->reaching_reg, SET_DEST (set), insn);
+                     delete_insn (insn);
+                     occr->deleted_p = 1;
+                     if (!insn_inserted_p)
+                       {
+                         insert_insn_end_bb (index_map[i], bb, 0);
+                         insn_inserted_p = 1;
+                       }
+                   }
+               }
+           }
+       }
+      free (domby);
     }
 
   free (index_map);
-  sbitmap_free (pre_redundant_insns);
-  return changed;
 }
 
-/* Top level routine to perform one PRE GCSE pass.
+/* Top level routine to perform one code hoisting (aka unification) pass
 
    Return nonzero if a change was made.  */
 
 static int
-one_pre_gcse_pass (pass)
-     int pass;
+one_code_hoisting_pass (void)
 {
   int changed = 0;
 
-  gcse_subst_count = 0;
-  gcse_create_count = 0;
-
   alloc_hash_table (max_cuid, &expr_hash_table, 0);
-  add_noreturn_fake_exit_edges ();
-  if (flag_gcse_lm)
-    compute_ld_motion_mems ();
-
   compute_hash_table (&expr_hash_table);
-  trim_ld_motion_mems ();
   if (gcse_file)
-    dump_hash_table (gcse_file, "Expression", &expr_hash_table);
+    dump_hash_table (gcse_file, "Code Hosting Expressions", &expr_hash_table);
 
   if (expr_hash_table.n_elems > 0)
     {
-      alloc_pre_mem (last_basic_block, expr_hash_table.n_elems);
-      compute_pre_data ();
-      changed |= pre_gcse ();
-      free_edge_list (edge_list);
-      free_pre_mem ();
+      alloc_code_hoist_mem (last_basic_block, expr_hash_table.n_elems);
+      compute_code_hoist_data ();
+      hoist_code ();
+      free_code_hoist_mem ();
     }
 
-  free_ldst_mems ();
-  remove_fake_edges ();
   free_hash_table (&expr_hash_table);
 
-  if (gcse_file)
-    {
-      fprintf (gcse_file, "\nPRE GCSE of %s, pass %d: %d bytes needed, ",
-              current_function_name, pass, bytes_used);
-      fprintf (gcse_file, "%d substs, %d insns created\n",
-              gcse_subst_count, gcse_create_count);
-    }
-
   return changed;
 }
 \f
-/* If X contains any LABEL_REF's, add REG_LABEL notes for them to INSN.
-   If notes are added to an insn which references a CODE_LABEL, the
-   LABEL_NUSES count is incremented.  We have to add REG_LABEL notes,
-   because the following loop optimization pass requires them.  */
-
-/* ??? This is very similar to the loop.c add_label_notes function.  We
-   could probably share code here.  */
-
-/* ??? If there was a jump optimization pass after gcse and before loop,
-   then we would not need to do this here, because jump would add the
-   necessary REG_LABEL notes.  */
-
-static void
-add_label_notes (x, insn)
-     rtx x;
-     rtx insn;
-{
-  enum rtx_code code = GET_CODE (x);
-  int i, j;
-  const char *fmt;
-
-  if (code == LABEL_REF && !LABEL_REF_NONLOCAL_P (x))
-    {
-      /* This code used to ignore labels that referred to dispatch tables to
-        avoid flow generating (slighly) worse code.
+/*  Here we provide the things required to do store motion towards
+    the exit. In order for this to be effective, gcse also needed to
+    be taught how to move a load when it is kill only by a store to itself.
 
-        We no longer ignore such label references (see LABEL_REF handling in
-        mark_jump_label for additional information).  */
+           int i;
+           float a[10];
 
-      REG_NOTES (insn) = gen_rtx_INSN_LIST (REG_LABEL, XEXP (x, 0),
-                                           REG_NOTES (insn));
-      if (LABEL_P (XEXP (x, 0)))
-       LABEL_NUSES (XEXP (x, 0))++;
-      return;
-    }
+           void foo(float scale)
+           {
+             for (i=0; i<10; i++)
+               a[i] *= scale;
+           }
 
-  for (i = GET_RTX_LENGTH (code) - 1, fmt = GET_RTX_FORMAT (code); i >= 0; i--)
-    {
-      if (fmt[i] == 'e')
-       add_label_notes (XEXP (x, i), insn);
-      else if (fmt[i] == 'E')
-       for (j = XVECLEN (x, i) - 1; j >= 0; j--)
-         add_label_notes (XVECEXP (x, i, j), insn);
-    }
-}
+    'i' is both loaded and stored to in the loop. Normally, gcse cannot move
+    the load out since its live around the loop, and stored at the bottom
+    of the loop.
 
-/* Compute transparent outgoing information for each block.
+      The 'Load Motion' referred to and implemented in this file is
+    an enhancement to gcse which when using edge based lcm, recognizes
+    this situation and allows gcse to move the load out of the loop.
 
-   An expression is transparent to an edge unless it is killed by
-   the edge itself.  This can only happen with abnormal control flow,
-   when the edge is traversed through a call.  This happens with
-   non-local labels and exceptions.
+      Once gcse has hoisted the load, store motion can then push this
+    load towards the exit, and we end up with no loads or stores of 'i'
+    in the loop.  */
 
-   This would not be necessary if we split the edge.  While this is
-   normally impossible for abnormal critical edges, with some effort
-   it should be possible with exception handling, since we still have
-   control over which handler should be invoked.  But due to increased
-   EH table sizes, this may not be worthwhile.  */
+/* This will search the ldst list for a matching expression. If it
+   doesn't find one, we create one and initialize it.  */
 
-static void
-compute_transpout ()
+static struct ls_expr *
+ldst_entry (rtx x)
 {
-  basic_block bb;
-  unsigned int i;
-  struct expr *expr;
-
-  sbitmap_vector_ones (transpout, last_basic_block);
+  int do_not_record_p = 0;
+  struct ls_expr * ptr;
+  unsigned int hash;
 
-  FOR_EACH_BB (bb)
-    {
-      /* Note that flow inserted a nop a the end of basic blocks that
-        end in call instructions for reasons other than abnormal
-        control flow.  */
-      if (GET_CODE (bb->end) != CALL_INSN)
-       continue;
+  hash = hash_expr_1 (x, GET_MODE (x), & do_not_record_p);
 
-      for (i = 0; i < expr_hash_table.size; i++)
-       for (expr = expr_hash_table.table[i]; expr ; expr = expr->next_same_hash)
-         if (GET_CODE (expr->expr) == MEM)
-           {
-             if (GET_CODE (XEXP (expr->expr, 0)) == SYMBOL_REF
-                 && CONSTANT_POOL_ADDRESS_P (XEXP (expr->expr, 0)))
-               continue;
+  for (ptr = pre_ldst_mems; ptr != NULL; ptr = ptr->next)
+    if (ptr->hash_index == hash && expr_equiv_p (ptr->pattern, x))
+      return ptr;
 
-             /* ??? Optimally, we would use interprocedural alias
-                analysis to determine if this mem is actually killed
-                by this call.  */
-             RESET_BIT (transpout[bb->index], expr->bitmap_index);
-           }
-    }
-}
+  ptr = xmalloc (sizeof (struct ls_expr));
 
-/* Removal of useless null pointer checks */
+  ptr->next         = pre_ldst_mems;
+  ptr->expr         = NULL;
+  ptr->pattern      = x;
+  ptr->pattern_regs = NULL_RTX;
+  ptr->loads        = NULL_RTX;
+  ptr->stores       = NULL_RTX;
+  ptr->reaching_reg = NULL_RTX;
+  ptr->invalid      = 0;
+  ptr->index        = 0;
+  ptr->hash_index   = hash;
+  pre_ldst_mems     = ptr;
 
-/* Called via note_stores.  X is set by SETTER.  If X is a register we must
-   invalidate nonnull_local and set nonnull_killed.  DATA is really a
-   `null_pointer_info *'.
+  return ptr;
+}
 
-   We ignore hard registers.  */
+/* Free up an individual ldst entry.  */
 
 static void
-invalidate_nonnull_info (x, setter, data)
-     rtx x;
-     rtx setter ATTRIBUTE_UNUSED;
-     void *data;
+free_ldst_entry (struct ls_expr * ptr)
 {
-  unsigned int regno;
-  struct null_pointer_info *npi = (struct null_pointer_info *) data;
-
-  while (GET_CODE (x) == SUBREG)
-    x = SUBREG_REG (x);
-
-  /* Ignore anything that is not a register or is a hard register.  */
-  if (GET_CODE (x) != REG
-      || REGNO (x) < npi->min_reg
-      || REGNO (x) >= npi->max_reg)
-    return;
-
-  regno = REGNO (x) - npi->min_reg;
+  free_INSN_LIST_list (& ptr->loads);
+  free_INSN_LIST_list (& ptr->stores);
 
-  RESET_BIT (npi->nonnull_local[npi->current_block->index], regno);
-  SET_BIT (npi->nonnull_killed[npi->current_block->index], regno);
+  free (ptr);
 }
 
-/* Do null-pointer check elimination for the registers indicated in
-   NPI.  NONNULL_AVIN and NONNULL_AVOUT are pre-allocated sbitmaps;
-   they are not our responsibility to free.  */
-
-static int
-delete_null_pointer_checks_1 (block_reg, nonnull_avin,
-                             nonnull_avout, npi)
-     unsigned int *block_reg;
-     sbitmap *nonnull_avin;
-     sbitmap *nonnull_avout;
-     struct null_pointer_info *npi;
-{
-  basic_block bb, current_block;
-  sbitmap *nonnull_local = npi->nonnull_local;
-  sbitmap *nonnull_killed = npi->nonnull_killed;
-  int something_changed = 0;
-
-  /* Compute local properties, nonnull and killed.  A register will have
-     the nonnull property if at the end of the current block its value is
-     known to be nonnull.  The killed property indicates that somewhere in
-     the block any information we had about the register is killed.
-
-     Note that a register can have both properties in a single block.  That
-     indicates that it's killed, then later in the block a new value is
-     computed.  */
-  sbitmap_vector_zero (nonnull_local, last_basic_block);
-  sbitmap_vector_zero (nonnull_killed, last_basic_block);
-
-  FOR_EACH_BB (current_block)
-    {
-      rtx insn, stop_insn;
-
-      /* Set the current block for invalidate_nonnull_info.  */
-      npi->current_block = current_block;
-
-      /* Scan each insn in the basic block looking for memory references and
-        register sets.  */
-      stop_insn = NEXT_INSN (current_block->end);
-      for (insn = current_block->head;
-          insn != stop_insn;
-          insn = NEXT_INSN (insn))
-       {
-         rtx set;
-         rtx reg;
-
-         /* Ignore anything that is not a normal insn.  */
-         if (! INSN_P (insn))
-           continue;
-
-         /* Basically ignore anything that is not a simple SET.  We do have
-            to make sure to invalidate nonnull_local and set nonnull_killed
-            for such insns though.  */
-         set = single_set (insn);
-         if (!set)
-           {
-             note_stores (PATTERN (insn), invalidate_nonnull_info, npi);
-             continue;
-           }
-
-         /* See if we've got a usable memory load.  We handle it first
-            in case it uses its address register as a dest (which kills
-            the nonnull property).  */
-         if (GET_CODE (SET_SRC (set)) == MEM
-             && GET_CODE ((reg = XEXP (SET_SRC (set), 0))) == REG
-             && REGNO (reg) >= npi->min_reg
-             && REGNO (reg) < npi->max_reg)
-           SET_BIT (nonnull_local[current_block->index],
-                    REGNO (reg) - npi->min_reg);
-
-         /* Now invalidate stuff clobbered by this insn.  */
-         note_stores (PATTERN (insn), invalidate_nonnull_info, npi);
-
-         /* And handle stores, we do these last since any sets in INSN can
-            not kill the nonnull property if it is derived from a MEM
-            appearing in a SET_DEST.  */
-         if (GET_CODE (SET_DEST (set)) == MEM
-             && GET_CODE ((reg = XEXP (SET_DEST (set), 0))) == REG
-             && REGNO (reg) >= npi->min_reg
-             && REGNO (reg) < npi->max_reg)
-           SET_BIT (nonnull_local[current_block->index],
-                    REGNO (reg) - npi->min_reg);
-       }
-    }
-
-  /* Now compute global properties based on the local properties.   This
-     is a classic global availability algorithm.  */
-  compute_available (nonnull_local, nonnull_killed,
-                    nonnull_avout, nonnull_avin);
+/* Free up all memory associated with the ldst list.  */
 
-  /* Now look at each bb and see if it ends with a compare of a value
-     against zero.  */
-  FOR_EACH_BB (bb)
+static void
+free_ldst_mems (void)
+{
+  while (pre_ldst_mems)
     {
-      rtx last_insn = bb->end;
-      rtx condition, earliest;
-      int compare_and_branch;
-
-      /* Since MIN_REG is always at least FIRST_PSEUDO_REGISTER, and
-        since BLOCK_REG[BB] is zero if this block did not end with a
-        comparison against zero, this condition works.  */
-      if (block_reg[bb->index] < npi->min_reg
-         || block_reg[bb->index] >= npi->max_reg)
-       continue;
-
-      /* LAST_INSN is a conditional jump.  Get its condition.  */
-      condition = get_condition (last_insn, &earliest);
-
-      /* If we can't determine the condition then skip.  */
-      if (! condition)
-       continue;
-
-      /* Is the register known to have a nonzero value?  */
-      if (!TEST_BIT (nonnull_avout[bb->index], block_reg[bb->index] - npi->min_reg))
-       continue;
+      struct ls_expr * tmp = pre_ldst_mems;
 
-      /* Try to compute whether the compare/branch at the loop end is one or
-        two instructions.  */
-      if (earliest == last_insn)
-       compare_and_branch = 1;
-      else if (earliest == prev_nonnote_insn (last_insn))
-       compare_and_branch = 2;
-      else
-       continue;
+      pre_ldst_mems = pre_ldst_mems->next;
 
-      /* We know the register in this comparison is nonnull at exit from
-        this block.  We can optimize this comparison.  */
-      if (GET_CODE (condition) == NE)
-       {
-         rtx new_jump;
+      free_ldst_entry (tmp);
+    }
 
-         new_jump = emit_jump_insn_after (gen_jump (JUMP_LABEL (last_insn)),
-                                          last_insn);
-         JUMP_LABEL (new_jump) = JUMP_LABEL (last_insn);
-         LABEL_NUSES (JUMP_LABEL (new_jump))++;
-         emit_barrier_after (new_jump);
-       }
+  pre_ldst_mems = NULL;
+}
 
-      something_changed = 1;
-      delete_insn (last_insn);
-      if (compare_and_branch == 2)
-       delete_insn (earliest);
-      purge_dead_edges (bb);
+/* Dump debugging info about the ldst list.  */
 
-      /* Don't check this block again.  (Note that BLOCK_END is
-        invalid here; we deleted the last instruction in the
-        block.)  */
-      block_reg[bb->index] = 0;
-    }
+static void
+print_ldst_list (FILE * file)
+{
+  struct ls_expr * ptr;
 
-  return something_changed;
-}
+  fprintf (file, "LDST list: \n");
 
-/* Find EQ/NE comparisons against zero which can be (indirectly) evaluated
-   at compile time.
+  for (ptr = first_ls_expr(); ptr != NULL; ptr = next_ls_expr (ptr))
+    {
+      fprintf (file, "  Pattern (%3d): ", ptr->index);
 
-   This is conceptually similar to global constant/copy propagation and
-   classic global CSE (it even uses the same dataflow equations as cprop).
+      print_rtl (file, ptr->pattern);
 
-   If a register is used as memory address with the form (mem (reg)), then we
-   know that REG can not be zero at that point in the program.  Any instruction
-   which sets REG "kills" this property.
+      fprintf (file, "\n        Loads : ");
 
-   So, if every path leading to a conditional branch has an available memory
-   reference of that form, then we know the register can not have the value
-   zero at the conditional branch.
+      if (ptr->loads)
+       print_rtl (file, ptr->loads);
+      else
+       fprintf (file, "(nil)");
 
-   So we merely need to compute the local properties and propagate that data
-   around the cfg, then optimize where possible.
+      fprintf (file, "\n       Stores : ");
 
-   We run this pass two times.  Once before CSE, then again after CSE.  This
-   has proven to be the most profitable approach.  It is rare for new
-   optimization opportunities of this nature to appear after the first CSE
-   pass.
+      if (ptr->stores)
+       print_rtl (file, ptr->stores);
+      else
+       fprintf (file, "(nil)");
 
-   This could probably be integrated with global cprop with a little work.  */
+      fprintf (file, "\n\n");
+    }
 
-int
-delete_null_pointer_checks (f)
-     rtx f ATTRIBUTE_UNUSED;
-{
-  sbitmap *nonnull_avin, *nonnull_avout;
-  unsigned int *block_reg;
-  basic_block bb;
-  int reg;
-  int regs_per_pass;
-  int max_reg;
-  struct null_pointer_info npi;
-  int something_changed = 0;
-
-  /* If we have only a single block, then there's nothing to do.  */
-  if (n_basic_blocks <= 1)
-    return 0;
+  fprintf (file, "\n");
+}
 
-  /* Trying to perform global optimizations on flow graphs which have
-     a high connectivity will take a long time and is unlikely to be
-     particularly useful.
+/* Returns 1 if X is in the list of ldst only expressions.  */
 
-     In normal circumstances a cfg should have about twice as many edges
-     as blocks.  But we do not want to punish small functions which have
-     a couple switch statements.  So we require a relatively large number
-     of basic blocks and the ratio of edges to blocks to be high.  */
-  if (n_basic_blocks > 1000 && n_edges / n_basic_blocks >= 20)
-    return 0;
+static struct ls_expr *
+find_rtx_in_ldst (rtx x)
+{
+  struct ls_expr * ptr;
 
-  /* We need four bitmaps, each with a bit for each register in each
-     basic block.  */
-  max_reg = max_reg_num ();
-  regs_per_pass = get_bitmap_width (4, last_basic_block, max_reg);
-
-  /* Allocate bitmaps to hold local and global properties.  */
-  npi.nonnull_local = sbitmap_vector_alloc (last_basic_block, regs_per_pass);
-  npi.nonnull_killed = sbitmap_vector_alloc (last_basic_block, regs_per_pass);
-  nonnull_avin = sbitmap_vector_alloc (last_basic_block, regs_per_pass);
-  nonnull_avout = sbitmap_vector_alloc (last_basic_block, regs_per_pass);
-
-  /* Go through the basic blocks, seeing whether or not each block
-     ends with a conditional branch whose condition is a comparison
-     against zero.  Record the register compared in BLOCK_REG.  */
-  block_reg = (unsigned int *) xcalloc (last_basic_block, sizeof (int));
-  FOR_EACH_BB (bb)
-    {
-      rtx last_insn = bb->end;
-      rtx condition, earliest, reg;
+  for (ptr = pre_ldst_mems; ptr != NULL; ptr = ptr->next)
+    if (expr_equiv_p (ptr->pattern, x) && ! ptr->invalid)
+      return ptr;
 
-      /* We only want conditional branches.  */
-      if (GET_CODE (last_insn) != JUMP_INSN
-         || !any_condjump_p (last_insn)
-         || !onlyjump_p (last_insn))
-       continue;
+  return NULL;
+}
 
-      /* LAST_INSN is a conditional jump.  Get its condition.  */
-      condition = get_condition (last_insn, &earliest);
+/* Assign each element of the list of mems a monotonically increasing value.  */
 
-      /* If we were unable to get the condition, or it is not an equality
-        comparison against zero then there's nothing we can do.  */
-      if (!condition
-         || (GET_CODE (condition) != NE && GET_CODE (condition) != EQ)
-         || GET_CODE (XEXP (condition, 1)) != CONST_INT
-         || (XEXP (condition, 1)
-             != CONST0_RTX (GET_MODE (XEXP (condition, 0)))))
-       continue;
+static int
+enumerate_ldsts (void)
+{
+  struct ls_expr * ptr;
+  int n = 0;
 
-      /* We must be checking a register against zero.  */
-      reg = XEXP (condition, 0);
-      if (GET_CODE (reg) != REG)
-       continue;
+  for (ptr = pre_ldst_mems; ptr != NULL; ptr = ptr->next)
+    ptr->index = n++;
 
-      block_reg[bb->index] = REGNO (reg);
-    }
+  return n;
+}
 
-  /* Go through the algorithm for each block of registers.  */
-  for (reg = FIRST_PSEUDO_REGISTER; reg < max_reg; reg += regs_per_pass)
-    {
-      npi.min_reg = reg;
-      npi.max_reg = MIN (reg + regs_per_pass, max_reg);
-      something_changed |= delete_null_pointer_checks_1 (block_reg,
-                                                        nonnull_avin,
-                                                        nonnull_avout,
-                                                        &npi);
-    }
+/* Return first item in the list.  */
 
-  /* Free the table of registers compared at the end of every block.  */
-  free (block_reg);
+static inline struct ls_expr *
+first_ls_expr (void)
+{
+  return pre_ldst_mems;
+}
 
-  /* Free bitmaps.  */
-  sbitmap_vector_free (npi.nonnull_local);
-  sbitmap_vector_free (npi.nonnull_killed);
-  sbitmap_vector_free (nonnull_avin);
-  sbitmap_vector_free (nonnull_avout);
+/* Return the next item in the list after the specified one.  */
 
-  return something_changed;
+static inline struct ls_expr *
+next_ls_expr (struct ls_expr * ptr)
+{
+  return ptr->next;
 }
+\f
+/* Load Motion for loads which only kill themselves.  */
 
-/* Code Hoisting variables and subroutines.  */
+/* Return true if x is a simple MEM operation, with no registers or
+   side effects. These are the types of loads we consider for the
+   ld_motion list, otherwise we let the usual aliasing take care of it.  */
 
-/* Very busy expressions.  */
-static sbitmap *hoist_vbein;
-static sbitmap *hoist_vbeout;
+static int
+simple_mem (rtx x)
+{
+  if (! MEM_P (x))
+    return 0;
 
-/* Hoistable expressions.  */
-static sbitmap *hoist_exprs;
+  if (MEM_VOLATILE_P (x))
+    return 0;
 
-/* Dominator bitmaps.  */
-dominance_info dominators;
+  if (GET_MODE (x) == BLKmode)
+    return 0;
 
-/* ??? We could compute post dominators and run this algorithm in
-   reverse to perform tail merging, doing so would probably be
-   more effective than the tail merging code in jump.c.
+  /* If we are handling exceptions, we must be careful with memory references
+     that may trap. If we are not, the behavior is undefined, so we may just
+     continue.  */
+  if (flag_non_call_exceptions && may_trap_p (x))
+    return 0;
 
-   It's unclear if tail merging could be run in parallel with
-   code hoisting.  It would be nice.  */
+  if (side_effects_p (x))
+    return 0;
 
-/* Allocate vars used for code hoisting analysis.  */
+  /* Do not consider function arguments passed on stack.  */
+  if (reg_mentioned_p (stack_pointer_rtx, x))
+    return 0;
 
-static void
-alloc_code_hoist_mem (n_blocks, n_exprs)
-     int n_blocks, n_exprs;
-{
-  antloc = sbitmap_vector_alloc (n_blocks, n_exprs);
-  transp = sbitmap_vector_alloc (n_blocks, n_exprs);
-  comp = sbitmap_vector_alloc (n_blocks, n_exprs);
+  if (flag_float_store && FLOAT_MODE_P (GET_MODE (x)))
+    return 0;
 
-  hoist_vbein = sbitmap_vector_alloc (n_blocks, n_exprs);
-  hoist_vbeout = sbitmap_vector_alloc (n_blocks, n_exprs);
-  hoist_exprs = sbitmap_vector_alloc (n_blocks, n_exprs);
-  transpout = sbitmap_vector_alloc (n_blocks, n_exprs);
+  return 1;
 }
 
-/* Free vars used for code hoisting analysis.  */
+/* Make sure there isn't a buried reference in this pattern anywhere.
+   If there is, invalidate the entry for it since we're not capable
+   of fixing it up just yet.. We have to be sure we know about ALL
+   loads since the aliasing code will allow all entries in the
+   ld_motion list to not-alias itself.  If we miss a load, we will get
+   the wrong value since gcse might common it and we won't know to
+   fix it up.  */
 
 static void
-free_code_hoist_mem ()
+invalidate_any_buried_refs (rtx x)
 {
-  sbitmap_vector_free (antloc);
-  sbitmap_vector_free (transp);
-  sbitmap_vector_free (comp);
+  const char * fmt;
+  int i, j;
+  struct ls_expr * ptr;
 
-  sbitmap_vector_free (hoist_vbein);
-  sbitmap_vector_free (hoist_vbeout);
-  sbitmap_vector_free (hoist_exprs);
-  sbitmap_vector_free (transpout);
+  /* Invalidate it in the list.  */
+  if (MEM_P (x) && simple_mem (x))
+    {
+      ptr = ldst_entry (x);
+      ptr->invalid = 1;
+    }
 
-  free_dominance_info (dominators);
-}
+  /* Recursively process the insn.  */
+  fmt = GET_RTX_FORMAT (GET_CODE (x));
 
-/* Compute the very busy expressions at entry/exit from each block.
+  for (i = GET_RTX_LENGTH (GET_CODE (x)) - 1; i >= 0; i--)
+    {
+      if (fmt[i] == 'e')
+       invalidate_any_buried_refs (XEXP (x, i));
+      else if (fmt[i] == 'E')
+       for (j = XVECLEN (x, i) - 1; j >= 0; j--)
+         invalidate_any_buried_refs (XVECEXP (x, i, j));
+    }
+}
 
-   An expression is very busy if all paths from a given point
-   compute the expression.  */
+/* Find all the 'simple' MEMs which are used in LOADs and STORES.  Simple
+   being defined as MEM loads and stores to symbols, with no side effects
+   and no registers in the expression.  For a MEM destination, we also
+   check that the insn is still valid if we replace the destination with a
+   REG, as is done in update_ld_motion_stores.  If there are any uses/defs
+   which don't match this criteria, they are invalidated and trimmed out
+   later.  */
 
 static void
-compute_code_hoist_vbeinout ()
+compute_ld_motion_mems (void)
 {
-  int changed, passes;
+  struct ls_expr * ptr;
   basic_block bb;
+  rtx insn;
 
-  sbitmap_vector_zero (hoist_vbeout, last_basic_block);
-  sbitmap_vector_zero (hoist_vbein, last_basic_block);
-
-  passes = 0;
-  changed = 1;
+  pre_ldst_mems = NULL;
 
-  while (changed)
+  FOR_EACH_BB (bb)
     {
-      changed = 0;
-
-      /* We scan the blocks in the reverse order to speed up
-        the convergence.  */
-      FOR_EACH_BB_REVERSE (bb)
+      for (insn = BB_HEAD (bb);
+          insn && insn != NEXT_INSN (BB_END (bb));
+          insn = NEXT_INSN (insn))
        {
-         changed |= sbitmap_a_or_b_and_c_cg (hoist_vbein[bb->index], antloc[bb->index],
-                                             hoist_vbeout[bb->index], transp[bb->index]);
-         if (bb->next_bb != EXIT_BLOCK_PTR)
-           sbitmap_intersection_of_succs (hoist_vbeout[bb->index], hoist_vbein, bb->index);
-       }
-
-      passes++;
-    }
+         if (INSN_P (insn))
+           {
+             if (GET_CODE (PATTERN (insn)) == SET)
+               {
+                 rtx src = SET_SRC (PATTERN (insn));
+                 rtx dest = SET_DEST (PATTERN (insn));
 
-  if (gcse_file)
-    fprintf (gcse_file, "hoisting vbeinout computation: %d passes\n", passes);
-}
+                 /* Check for a simple LOAD...  */
+                 if (MEM_P (src) && simple_mem (src))
+                   {
+                     ptr = ldst_entry (src);
+                     if (REG_P (dest))
+                       ptr->loads = alloc_INSN_LIST (insn, ptr->loads);
+                     else
+                       ptr->invalid = 1;
+                   }
+                 else
+                   {
+                     /* Make sure there isn't a buried load somewhere.  */
+                     invalidate_any_buried_refs (src);
+                   }
 
-/* Top level routine to do the dataflow analysis needed by code hoisting.  */
+                 /* Check for stores. Don't worry about aliased ones, they
+                    will block any movement we might do later. We only care
+                    about this exact pattern since those are the only
+                    circumstance that we will ignore the aliasing info.  */
+                 if (MEM_P (dest) && simple_mem (dest))
+                   {
+                     ptr = ldst_entry (dest);
 
-static void
-compute_code_hoist_data ()
-{
-  compute_local_properties (transp, comp, antloc, &expr_hash_table);
-  compute_transpout ();
-  compute_code_hoist_vbeinout ();
-  dominators = calculate_dominance_info (CDI_DOMINATORS);
-  if (gcse_file)
-    fprintf (gcse_file, "\n");
+                     if (! MEM_P (src)
+                         && GET_CODE (src) != ASM_OPERANDS
+                         /* Check for REG manually since want_to_gcse_p
+                            returns 0 for all REGs.  */
+                         && can_assign_to_reg_p (src))
+                       ptr->stores = alloc_INSN_LIST (insn, ptr->stores);
+                     else
+                       ptr->invalid = 1;
+                   }
+               }
+             else
+               invalidate_any_buried_refs (PATTERN (insn));
+           }
+       }
+    }
 }
 
-/* Determine if the expression identified by EXPR_INDEX would
-   reach BB unimpared if it was placed at the end of EXPR_BB.
-
-   It's unclear exactly what Muchnick meant by "unimpared".  It seems
-   to me that the expression must either be computed or transparent in
-   *every* block in the path(s) from EXPR_BB to BB.  Any other definition
-   would allow the expression to be hoisted out of loops, even if
-   the expression wasn't a loop invariant.
-
-   Contrast this to reachability for PRE where an expression is
-   considered reachable if *any* path reaches instead of *all*
-   paths.  */
+/* Remove any references that have been either invalidated or are not in the
+   expression list for pre gcse.  */
 
-static int
-hoist_expr_reaches_here_p (expr_bb, expr_index, bb, visited)
-     basic_block expr_bb;
-     int expr_index;
-     basic_block bb;
-     char *visited;
+static void
+trim_ld_motion_mems (void)
 {
-  edge pred;
-  int visited_allocated_locally = 0;
-
-
-  if (visited == NULL)
-    {
-      visited_allocated_locally = 1;
-      visited = xcalloc (last_basic_block, 1);
-    }
+  struct ls_expr * * last = & pre_ldst_mems;
+  struct ls_expr * ptr = pre_ldst_mems;
 
-  for (pred = bb->pred; pred != NULL; pred = pred->pred_next)
+  while (ptr != NULL)
     {
-      basic_block pred_bb = pred->src;
+      struct expr * expr;
 
-      if (pred->src == ENTRY_BLOCK_PTR)
-       break;
-      else if (pred_bb == expr_bb)
-       continue;
-      else if (visited[pred_bb->index])
-       continue;
+      /* Delete if entry has been made invalid.  */
+      if (! ptr->invalid)
+       {
+         /* Delete if we cannot find this mem in the expression list.  */
+         unsigned int hash = ptr->hash_index % expr_hash_table.size;
 
-      /* Does this predecessor generate this expression?  */
-      else if (TEST_BIT (comp[pred_bb->index], expr_index))
-       break;
-      else if (! TEST_BIT (transp[pred_bb->index], expr_index))
-       break;
+         for (expr = expr_hash_table.table[hash];
+              expr != NULL;
+              expr = expr->next_same_hash)
+           if (expr_equiv_p (expr->expr, ptr->pattern))
+             break;
+       }
+      else
+       expr = (struct expr *) 0;
 
-      /* Not killed.  */
+      if (expr)
+       {
+         /* Set the expression field if we are keeping it.  */
+         ptr->expr = expr;
+         last = & ptr->next;
+         ptr = ptr->next;
+       }
       else
        {
-         visited[pred_bb->index] = 1;
-         if (! hoist_expr_reaches_here_p (expr_bb, expr_index,
-                                          pred_bb, visited))
-           break;
+         *last = ptr->next;
+         free_ldst_entry (ptr);
+         ptr = * last;
        }
     }
-  if (visited_allocated_locally)
-    free (visited);
 
-  return (pred == NULL);
+  /* Show the world what we've found.  */
+  if (gcse_file && pre_ldst_mems != NULL)
+    print_ldst_list (gcse_file);
 }
-\f
-/* Actually perform code hoisting.  */
+
+/* This routine will take an expression which we are replacing with
+   a reaching register, and update any stores that are needed if
+   that expression is in the ld_motion list.  Stores are updated by
+   copying their SRC to the reaching register, and then storing
+   the reaching register into the store location. These keeps the
+   correct value in the reaching register for the loads.  */
 
 static void
-hoist_code ()
+update_ld_motion_stores (struct expr * expr)
 {
-  basic_block bb, dominated;
-  basic_block *domby;
-  unsigned int domby_len;
-  unsigned int i,j;
-  struct expr **index_map;
-  struct expr *expr;
-
-  sbitmap_vector_zero (hoist_exprs, last_basic_block);
-
-  /* Compute a mapping from expression number (`bitmap_index') to
-     hash table entry.  */
-
-  index_map = (struct expr **) xcalloc (expr_hash_table.n_elems, sizeof (struct expr *));
-  for (i = 0; i < expr_hash_table.size; i++)
-    for (expr = expr_hash_table.table[i]; expr != NULL; expr = expr->next_same_hash)
-      index_map[expr->bitmap_index] = expr;
+  struct ls_expr * mem_ptr;
 
-  /* Walk over each basic block looking for potentially hoistable
-     expressions, nothing gets hoisted from the entry block.  */
-  FOR_EACH_BB (bb)
+  if ((mem_ptr = find_rtx_in_ldst (expr->expr)))
     {
-      int found = 0;
-      int insn_inserted_p;
+      /* We can try to find just the REACHED stores, but is shouldn't
+        matter to set the reaching reg everywhere...  some might be
+        dead and should be eliminated later.  */
 
-      domby_len = get_dominated_by (dominators, bb, &domby);
-      /* Examine each expression that is very busy at the exit of this
-        block.  These are the potentially hoistable expressions.  */
-      for (i = 0; i < hoist_vbeout[bb->index]->n_bits; i++)
+      /* We replace (set mem expr) with (set reg expr) (set mem reg)
+        where reg is the reaching reg used in the load.  We checked in
+        compute_ld_motion_mems that we can replace (set mem expr) with
+        (set reg expr) in that insn.  */
+      rtx list = mem_ptr->stores;
+
+      for ( ; list != NULL_RTX; list = XEXP (list, 1))
        {
-         int hoistable = 0;
+         rtx insn = XEXP (list, 0);
+         rtx pat = PATTERN (insn);
+         rtx src = SET_SRC (pat);
+         rtx reg = expr->reaching_reg;
+         rtx copy, new;
 
-         if (TEST_BIT (hoist_vbeout[bb->index], i)
-             && TEST_BIT (transpout[bb->index], i))
-           {
-             /* We've found a potentially hoistable expression, now
-                we look at every block BB dominates to see if it
-                computes the expression.  */
-             for (j = 0; j < domby_len; j++)
-               {
-                 dominated = domby[j];
-                 /* Ignore self dominance.  */
-                 if (bb == dominated)
-                   continue;
-                 /* We've found a dominated block, now see if it computes
-                    the busy expression and whether or not moving that
-                    expression to the "beginning" of that block is safe.  */
-                 if (!TEST_BIT (antloc[dominated->index], i))
-                   continue;
+         /* If we've already copied it, continue.  */
+         if (expr->reaching_reg == src)
+           continue;
 
-                 /* Note if the expression would reach the dominated block
-                    unimpared if it was placed at the end of BB.
+         if (gcse_file)
+           {
+             fprintf (gcse_file, "PRE:  store updated with reaching reg ");
+             print_rtl (gcse_file, expr->reaching_reg);
+             fprintf (gcse_file, ":\n  ");
+             print_inline_rtx (gcse_file, insn, 8);
+             fprintf (gcse_file, "\n");
+           }
 
-                    Keep track of how many times this expression is hoistable
-                    from a dominated block into BB.  */
-                 if (hoist_expr_reaches_here_p (bb, i, dominated, NULL))
-                   hoistable++;
-               }
+         copy = gen_move_insn ( reg, copy_rtx (SET_SRC (pat)));
+         new = emit_insn_before (copy, insn);
+         record_one_set (REGNO (reg), new);
+         SET_SRC (pat) = reg;
 
-             /* If we found more than one hoistable occurrence of this
-                expression, then note it in the bitmap of expressions to
-                hoist.  It makes no sense to hoist things which are computed
-                in only one BB, and doing so tends to pessimize register
-                allocation.  One could increase this value to try harder
-                to avoid any possible code expansion due to register
-                allocation issues; however experiments have shown that
-                the vast majority of hoistable expressions are only movable
-                from two successors, so raising this threshhold is likely
-                to nullify any benefit we get from code hoisting.  */
-             if (hoistable > 1)
-               {
-                 SET_BIT (hoist_exprs[bb->index], i);
-                 found = 1;
-               }
-           }
-       }
-      /* If we found nothing to hoist, then quit now.  */
-      if (! found)
-        {
-         free (domby);
-       continue;
+         /* un-recognize this pattern since it's probably different now.  */
+         INSN_CODE (insn) = -1;
+         gcse_create_count++;
        }
+    }
+}
+\f
+/* Store motion code.  */
 
-      /* Loop over all the hoistable expressions.  */
-      for (i = 0; i < hoist_exprs[bb->index]->n_bits; i++)
-       {
-         /* We want to insert the expression into BB only once, so
-            note when we've inserted it.  */
-         insn_inserted_p = 0;
-
-         /* These tests should be the same as the tests above.  */
-         if (TEST_BIT (hoist_vbeout[bb->index], i))
-           {
-             /* We've found a potentially hoistable expression, now
-                we look at every block BB dominates to see if it
-                computes the expression.  */
-             for (j = 0; j < domby_len; j++)
-               {
-                 dominated = domby[j];
-                 /* Ignore self dominance.  */
-                 if (bb == dominated)
-                   continue;
+#define ANTIC_STORE_LIST(x)            ((x)->loads)
+#define AVAIL_STORE_LIST(x)            ((x)->stores)
+#define LAST_AVAIL_CHECK_FAILURE(x)    ((x)->reaching_reg)
 
-                 /* We've found a dominated block, now see if it computes
-                    the busy expression and whether or not moving that
-                    expression to the "beginning" of that block is safe.  */
-                 if (!TEST_BIT (antloc[dominated->index], i))
-                   continue;
+/* This is used to communicate the target bitvector we want to use in the
+   reg_set_info routine when called via the note_stores mechanism.  */
+static int * regvec;
 
-                 /* The expression is computed in the dominated block and
-                    it would be safe to compute it at the start of the
-                    dominated block.  Now we have to determine if the
-                    expression would reach the dominated block if it was
-                    placed at the end of BB.  */
-                 if (hoist_expr_reaches_here_p (bb, i, dominated, NULL))
-                   {
-                     struct expr *expr = index_map[i];
-                     struct occr *occr = expr->antic_occr;
-                     rtx insn;
-                     rtx set;
+/* And current insn, for the same routine.  */
+static rtx compute_store_table_current_insn;
 
-                     /* Find the right occurrence of this expression.  */
-                     while (BLOCK_FOR_INSN (occr->insn) != dominated && occr)
-                       occr = occr->next;
+/* Used in computing the reverse edge graph bit vectors.  */
+static sbitmap * st_antloc;
 
-                     /* Should never happen.  */
-                     if (!occr)
-                       abort ();
+/* Global holding the number of store expressions we are dealing with.  */
+static int num_stores;
 
-                     insn = occr->insn;
+/* Checks to set if we need to mark a register set.  Called from
+   note_stores.  */
 
-                     set = single_set (insn);
-                     if (! set)
-                       abort ();
+static void
+reg_set_info (rtx dest, rtx setter ATTRIBUTE_UNUSED,
+             void *data)
+{
+  sbitmap bb_reg = data;
 
-                     /* Create a pseudo-reg to store the result of reaching
-                        expressions into.  Get the mode for the new pseudo
-                        from the mode of the original destination pseudo.  */
-                     if (expr->reaching_reg == NULL)
-                       expr->reaching_reg
-                         = gen_reg_rtx (GET_MODE (SET_DEST (set)));
+  if (GET_CODE (dest) == SUBREG)
+    dest = SUBREG_REG (dest);
 
-                     gcse_emit_move_after (expr->reaching_reg, SET_DEST (set), insn);
-                     delete_insn (insn);
-                     occr->deleted_p = 1;
-                     if (!insn_inserted_p)
-                       {
-                         insert_insn_end_bb (index_map[i], bb, 0);
-                         insn_inserted_p = 1;
-                       }
-                   }
-               }
-           }
-       }
-      free (domby);
+  if (REG_P (dest))
+    {
+      regvec[REGNO (dest)] = INSN_UID (compute_store_table_current_insn);
+      if (bb_reg)
+       SET_BIT (bb_reg, REGNO (dest));
     }
-
-  free (index_map);
 }
 
-/* Top level routine to perform one code hoisting (aka unification) pass
-
-   Return nonzero if a change was made.  */
+/* Clear any mark that says that this insn sets dest.  Called from
+   note_stores.  */
 
-static int
-one_code_hoisting_pass ()
+static void
+reg_clear_last_set (rtx dest, rtx setter ATTRIBUTE_UNUSED,
+             void *data)
 {
-  int changed = 0;
-
-  alloc_hash_table (max_cuid, &expr_hash_table, 0);
-  compute_hash_table (&expr_hash_table);
-  if (gcse_file)
-    dump_hash_table (gcse_file, "Code Hosting Expressions", &expr_hash_table);
-
-  if (expr_hash_table.n_elems > 0)
-    {
-      alloc_code_hoist_mem (last_basic_block, expr_hash_table.n_elems);
-      compute_code_hoist_data ();
-      hoist_code ();
-      free_code_hoist_mem ();
-    }
+  int *dead_vec = data;
 
-  free_hash_table (&expr_hash_table);
+  if (GET_CODE (dest) == SUBREG)
+    dest = SUBREG_REG (dest);
 
-  return changed;
+  if (REG_P (dest) &&
+      dead_vec[REGNO (dest)] == INSN_UID (compute_store_table_current_insn))
+    dead_vec[REGNO (dest)] = 0;
 }
-\f
-/*  Here we provide the things required to do store motion towards
-    the exit. In order for this to be effective, gcse also needed to
-    be taught how to move a load when it is kill only by a store to itself.
-
-           int i;
-           float a[10];
 
-           void foo(float scale)
-           {
-             for (i=0; i<10; i++)
-               a[i] *= scale;
-           }
+/* Return zero if some of the registers in list X are killed
+   due to set of registers in bitmap REGS_SET.  */
 
-    'i' is both loaded and stored to in the loop. Normally, gcse cannot move
-    the load out since its live around the loop, and stored at the bottom
-    of the loop.
+static bool
+store_ops_ok (rtx x, int *regs_set)
+{
+  rtx reg;
 
-      The 'Load Motion' referred to and implemented in this file is
-    an enhancement to gcse which when using edge based lcm, recognizes
-    this situation and allows gcse to move the load out of the loop.
+  for (; x; x = XEXP (x, 1))
+    {
+      reg = XEXP (x, 0);
+      if (regs_set[REGNO(reg)])
+       return false;
+    }
 
-      Once gcse has hoisted the load, store motion can then push this
-    load towards the exit, and we end up with no loads or stores of 'i'
-    in the loop.  */
+  return true;
+}
 
-/* This will search the ldst list for a matching expression. If it
-   doesn't find one, we create one and initialize it.  */
+/* Returns a list of registers mentioned in X.  */
+static rtx
+extract_mentioned_regs (rtx x)
+{
+  return extract_mentioned_regs_helper (x, NULL_RTX);
+}
 
-static struct ls_expr *
-ldst_entry (x)
-     rtx x;
+/* Helper for extract_mentioned_regs; ACCUM is used to accumulate used
+   registers.  */
+static rtx
+extract_mentioned_regs_helper (rtx x, rtx accum)
 {
-  struct ls_expr * ptr;
+  int i;
+  enum rtx_code code;
+  const char * fmt;
 
-  for (ptr = first_ls_expr(); ptr != NULL; ptr = next_ls_expr (ptr))
-    if (expr_equiv_p (ptr->pattern, x))
-      break;
+  /* Repeat is used to turn tail-recursion into iteration.  */
+ repeat:
 
-  if (!ptr)
-    {
-      ptr = (struct ls_expr *) xmalloc (sizeof (struct ls_expr));
+  if (x == 0)
+    return accum;
 
-      ptr->next         = pre_ldst_mems;
-      ptr->expr         = NULL;
-      ptr->pattern      = x;
-      ptr->loads        = NULL_RTX;
-      ptr->stores       = NULL_RTX;
-      ptr->reaching_reg = NULL_RTX;
-      ptr->invalid      = 0;
-      ptr->index        = 0;
-      ptr->hash_index   = 0;
-      pre_ldst_mems     = ptr;
-    }
+  code = GET_CODE (x);
+  switch (code)
+    {
+    case REG:
+      return alloc_EXPR_LIST (0, x, accum);
 
-  return ptr;
-}
+    case MEM:
+      x = XEXP (x, 0);
+      goto repeat;
 
-/* Free up an individual ldst entry.  */
+    case PRE_DEC:
+    case PRE_INC:
+    case POST_DEC:
+    case POST_INC:
+      /* We do not run this function with arguments having side effects.  */
+      abort ();
 
-static void
-free_ldst_entry (ptr)
-     struct ls_expr * ptr;
-{
-  free_INSN_LIST_list (& ptr->loads);
-  free_INSN_LIST_list (& ptr->stores);
+    case PC:
+    case CC0: /*FIXME*/
+    case CONST:
+    case CONST_INT:
+    case CONST_DOUBLE:
+    case CONST_VECTOR:
+    case SYMBOL_REF:
+    case LABEL_REF:
+    case ADDR_VEC:
+    case ADDR_DIFF_VEC:
+      return accum;
 
-  free (ptr);
-}
+    default:
+      break;
+    }
 
-/* Free up all memory associated with the ldst list.  */
+  i = GET_RTX_LENGTH (code) - 1;
+  fmt = GET_RTX_FORMAT (code);
 
-static void
-free_ldst_mems ()
-{
-  while (pre_ldst_mems)
+  for (; i >= 0; i--)
     {
-      struct ls_expr * tmp = pre_ldst_mems;
+      if (fmt[i] == 'e')
+       {
+         rtx tem = XEXP (x, i);
 
-      pre_ldst_mems = pre_ldst_mems->next;
+         /* If we are about to do the last recursive call
+            needed at this level, change it into iteration.  */
+         if (i == 0)
+           {
+             x = tem;
+             goto repeat;
+           }
 
-      free_ldst_entry (tmp);
+         accum = extract_mentioned_regs_helper (tem, accum);
+       }
+      else if (fmt[i] == 'E')
+       {
+         int j;
+
+         for (j = 0; j < XVECLEN (x, i); j++)
+           accum = extract_mentioned_regs_helper (XVECEXP (x, i, j), accum);
+       }
     }
 
-  pre_ldst_mems = NULL;
+  return accum;
 }
 
-/* Dump debugging info about the ldst list.  */
+/* Determine whether INSN is MEM store pattern that we will consider moving.
+   REGS_SET_BEFORE is bitmap of registers set before (and including) the
+   current insn, REGS_SET_AFTER is bitmap of registers set after (and
+   including) the insn in this basic block.  We must be passing through BB from
+   head to end, as we are using this fact to speed things up.
+
+   The results are stored this way:
+
+   -- the first anticipatable expression is added into ANTIC_STORE_LIST
+   -- if the processed expression is not anticipatable, NULL_RTX is added
+      there instead, so that we can use it as indicator that no further
+      expression of this type may be anticipatable
+   -- if the expression is available, it is added as head of AVAIL_STORE_LIST;
+      consequently, all of them but this head are dead and may be deleted.
+   -- if the expression is not available, the insn due to that it fails to be
+      available is stored in reaching_reg.
+
+   The things are complicated a bit by fact that there already may be stores
+   to the same MEM from other blocks; also caller must take care of the
+   necessary cleanup of the temporary markers after end of the basic block.
+   */
 
 static void
-print_ldst_list (file)
-     FILE * file;
+find_moveable_store (rtx insn, int *regs_set_before, int *regs_set_after)
 {
   struct ls_expr * ptr;
+  rtx dest, set, tmp;
+  int check_anticipatable, check_available;
+  basic_block bb = BLOCK_FOR_INSN (insn);
 
-  fprintf (file, "LDST list: \n");
+  set = single_set (insn);
+  if (!set)
+    return;
 
-  for (ptr = first_ls_expr(); ptr != NULL; ptr = next_ls_expr (ptr))
-    {
-      fprintf (file, "  Pattern (%3d): ", ptr->index);
+  dest = SET_DEST (set);
 
-      print_rtl (file, ptr->pattern);
+  if (! MEM_P (dest) || MEM_VOLATILE_P (dest)
+      || GET_MODE (dest) == BLKmode)
+    return;
 
-      fprintf (file, "\n        Loads : ");
+  if (side_effects_p (dest))
+    return;
 
-      if (ptr->loads)
-       print_rtl (file, ptr->loads);
-      else
-       fprintf (file, "(nil)");
+  /* If we are handling exceptions, we must be careful with memory references
+     that may trap. If we are not, the behavior is undefined, so we may just
+     continue.  */
+  if (flag_non_call_exceptions && may_trap_p (dest))
+    return;
 
-      fprintf (file, "\n       Stores : ");
+  /* Even if the destination cannot trap, the source may.  In this case we'd
+     need to handle updating the REG_EH_REGION note.  */
+  if (find_reg_note (insn, REG_EH_REGION, NULL_RTX))
+    return;
 
-      if (ptr->stores)
-       print_rtl (file, ptr->stores);
+  ptr = ldst_entry (dest);
+  if (!ptr->pattern_regs)
+    ptr->pattern_regs = extract_mentioned_regs (dest);
+
+  /* Do not check for anticipatability if we either found one anticipatable
+     store already, or tested for one and found out that it was killed.  */
+  check_anticipatable = 0;
+  if (!ANTIC_STORE_LIST (ptr))
+    check_anticipatable = 1;
+  else
+    {
+      tmp = XEXP (ANTIC_STORE_LIST (ptr), 0);
+      if (tmp != NULL_RTX
+         && BLOCK_FOR_INSN (tmp) != bb)
+       check_anticipatable = 1;
+    }
+  if (check_anticipatable)
+    {
+      if (store_killed_before (dest, ptr->pattern_regs, insn, bb, regs_set_before))
+       tmp = NULL_RTX;
       else
-       fprintf (file, "(nil)");
-
-      fprintf (file, "\n\n");
+       tmp = insn;
+      ANTIC_STORE_LIST (ptr) = alloc_INSN_LIST (tmp,
+                                               ANTIC_STORE_LIST (ptr));
     }
 
-  fprintf (file, "\n");
+  /* It is not necessary to check whether store is available if we did
+     it successfully before; if we failed before, do not bother to check
+     until we reach the insn that caused us to fail.  */
+  check_available = 0;
+  if (!AVAIL_STORE_LIST (ptr))
+    check_available = 1;
+  else
+    {
+      tmp = XEXP (AVAIL_STORE_LIST (ptr), 0);
+      if (BLOCK_FOR_INSN (tmp) != bb)
+       check_available = 1;
+    }
+  if (check_available)
+    {
+      /* Check that we have already reached the insn at that the check
+        failed last time.  */
+      if (LAST_AVAIL_CHECK_FAILURE (ptr))
+       {
+         for (tmp = BB_END (bb);
+              tmp != insn && tmp != LAST_AVAIL_CHECK_FAILURE (ptr);
+              tmp = PREV_INSN (tmp))
+           continue;
+         if (tmp == insn)
+           check_available = 0;
+       }
+      else
+       check_available = store_killed_after (dest, ptr->pattern_regs, insn,
+                                             bb, regs_set_after,
+                                             &LAST_AVAIL_CHECK_FAILURE (ptr));
+    }
+  if (!check_available)
+    AVAIL_STORE_LIST (ptr) = alloc_INSN_LIST (insn, AVAIL_STORE_LIST (ptr));
 }
 
-/* Returns 1 if X is in the list of ldst only expressions.  */
+/* Find available and anticipatable stores.  */
 
-static struct ls_expr *
-find_rtx_in_ldst (x)
-     rtx x;
+static int
+compute_store_table (void)
 {
-  struct ls_expr * ptr;
+  int ret;
+  basic_block bb;
+  unsigned regno;
+  rtx insn, pat, tmp;
+  int *last_set_in, *already_set;
+  struct ls_expr * ptr, **prev_next_ptr_ptr;
 
-  for (ptr = pre_ldst_mems; ptr != NULL; ptr = ptr->next)
-    if (expr_equiv_p (ptr->pattern, x) && ! ptr->invalid)
-      return ptr;
+  max_gcse_regno = max_reg_num ();
 
-  return NULL;
-}
+  reg_set_in_block = sbitmap_vector_alloc (last_basic_block,
+                                                      max_gcse_regno);
+  sbitmap_vector_zero (reg_set_in_block, last_basic_block);
+  pre_ldst_mems = 0;
+  last_set_in = xcalloc (max_gcse_regno, sizeof (int));
+  already_set = xmalloc (sizeof (int) * max_gcse_regno);
 
-/* Assign each element of the list of mems a monotonically increasing value.  */
+  /* Find all the stores we care about.  */
+  FOR_EACH_BB (bb)
+    {
+      /* First compute the registers set in this block.  */
+      regvec = last_set_in;
 
-static int
-enumerate_ldsts ()
-{
-  struct ls_expr * ptr;
-  int n = 0;
+      for (insn = BB_HEAD (bb);
+          insn != NEXT_INSN (BB_END (bb));
+          insn = NEXT_INSN (insn))
+       {
+         if (! INSN_P (insn))
+           continue;
 
-  for (ptr = pre_ldst_mems; ptr != NULL; ptr = ptr->next)
-    ptr->index = n++;
+         if (CALL_P (insn))
+           {
+             bool clobbers_all = false;
+#ifdef NON_SAVING_SETJMP
+             if (NON_SAVING_SETJMP
+                 && find_reg_note (insn, REG_SETJMP, NULL_RTX))
+               clobbers_all = true;
+#endif
 
-  return n;
-}
+             for (regno = 0; regno < FIRST_PSEUDO_REGISTER; regno++)
+               if (clobbers_all
+                   || TEST_HARD_REG_BIT (regs_invalidated_by_call, regno))
+                 {
+                   last_set_in[regno] = INSN_UID (insn);
+                   SET_BIT (reg_set_in_block[bb->index], regno);
+                 }
+           }
 
-/* Return first item in the list.  */
+         pat = PATTERN (insn);
+         compute_store_table_current_insn = insn;
+         note_stores (pat, reg_set_info, reg_set_in_block[bb->index]);
+       }
 
-static inline struct ls_expr *
-first_ls_expr ()
-{
-  return pre_ldst_mems;
-}
+      /* Now find the stores.  */
+      memset (already_set, 0, sizeof (int) * max_gcse_regno);
+      regvec = already_set;
+      for (insn = BB_HEAD (bb);
+          insn != NEXT_INSN (BB_END (bb));
+          insn = NEXT_INSN (insn))
+       {
+         if (! INSN_P (insn))
+           continue;
 
-/* Return the next item in ther list after the specified one.  */
+         if (CALL_P (insn))
+           {
+             bool clobbers_all = false;
+#ifdef NON_SAVING_SETJMP
+             if (NON_SAVING_SETJMP
+                 && find_reg_note (insn, REG_SETJMP, NULL_RTX))
+               clobbers_all = true;
+#endif
 
-static inline struct ls_expr *
-next_ls_expr (ptr)
-     struct ls_expr * ptr;
-{
-  return ptr->next;
-}
-\f
-/* Load Motion for loads which only kill themselves.  */
+             for (regno = 0; regno < FIRST_PSEUDO_REGISTER; regno++)
+               if (clobbers_all
+                   || TEST_HARD_REG_BIT (regs_invalidated_by_call, regno))
+                 already_set[regno] = 1;
+           }
 
-/* Return true if x is a simple MEM operation, with no registers or
-   side effects. These are the types of loads we consider for the
-   ld_motion list, otherwise we let the usual aliasing take care of it.  */
+         pat = PATTERN (insn);
+         note_stores (pat, reg_set_info, NULL);
 
-static int
-simple_mem (x)
-     rtx x;
-{
-  if (GET_CODE (x) != MEM)
-    return 0;
+         /* Now that we've marked regs, look for stores.  */
+         find_moveable_store (insn, already_set, last_set_in);
 
-  if (MEM_VOLATILE_P (x))
-    return 0;
+         /* Unmark regs that are no longer set.  */
+         compute_store_table_current_insn = insn;
+         note_stores (pat, reg_clear_last_set, last_set_in);
+         if (CALL_P (insn))
+           {
+             bool clobbers_all = false;
+#ifdef NON_SAVING_SETJMP
+             if (NON_SAVING_SETJMP
+                 && find_reg_note (insn, REG_SETJMP, NULL_RTX))
+               clobbers_all = true;
+#endif
 
-  if (GET_MODE (x) == BLKmode)
-    return 0;
+             for (regno = 0; regno < FIRST_PSEUDO_REGISTER; regno++)
+               if ((clobbers_all
+                    || TEST_HARD_REG_BIT (regs_invalidated_by_call, regno))
+                   && last_set_in[regno] == INSN_UID (insn))
+                 last_set_in[regno] = 0;
+           }
+       }
 
-  if (!rtx_varies_p (XEXP (x, 0), 0))
-    return 1;
+#ifdef ENABLE_CHECKING
+      /* last_set_in should now be all-zero.  */
+      for (regno = 0; regno < max_gcse_regno; regno++)
+       if (last_set_in[regno] != 0)
+         abort ();
+#endif
 
-  return 0;
+      /* Clear temporary marks.  */
+      for (ptr = first_ls_expr (); ptr != NULL; ptr = next_ls_expr (ptr))
+       {
+         LAST_AVAIL_CHECK_FAILURE(ptr) = NULL_RTX;
+         if (ANTIC_STORE_LIST (ptr)
+             && (tmp = XEXP (ANTIC_STORE_LIST (ptr), 0)) == NULL_RTX)
+           ANTIC_STORE_LIST (ptr) = XEXP (ANTIC_STORE_LIST (ptr), 1);
+       }
+    }
+
+  /* Remove the stores that are not available anywhere, as there will
+     be no opportunity to optimize them.  */
+  for (ptr = pre_ldst_mems, prev_next_ptr_ptr = &pre_ldst_mems;
+       ptr != NULL;
+       ptr = *prev_next_ptr_ptr)
+    {
+      if (!AVAIL_STORE_LIST (ptr))
+       {
+         *prev_next_ptr_ptr = ptr->next;
+         free_ldst_entry (ptr);
+       }
+      else
+       prev_next_ptr_ptr = &ptr->next;
+    }
+
+  ret = enumerate_ldsts ();
+
+  if (gcse_file)
+    {
+      fprintf (gcse_file, "ST_avail and ST_antic (shown under loads..)\n");
+      print_ldst_list (gcse_file);
+    }
+
+  free (last_set_in);
+  free (already_set);
+  return ret;
 }
 
-/* Make sure there isn't a buried reference in this pattern anywhere.
-   If there is, invalidate the entry for it since we're not capable
-   of fixing it up just yet.. We have to be sure we know about ALL
-   loads since the aliasing code will allow all entries in the
-   ld_motion list to not-alias itself.  If we miss a load, we will get
-   the wrong value since gcse might common it and we won't know to
-   fix it up.  */
+/* Check to see if the load X is aliased with STORE_PATTERN.
+   AFTER is true if we are checking the case when STORE_PATTERN occurs
+   after the X.  */
 
-static void
-invalidate_any_buried_refs (x)
-     rtx x;
+static bool
+load_kills_store (rtx x, rtx store_pattern, int after)
+{
+  if (after)
+    return anti_dependence (x, store_pattern);
+  else
+    return true_dependence (store_pattern, GET_MODE (store_pattern), x,
+                           rtx_addr_varies_p);
+}
+
+/* Go through the entire insn X, looking for any loads which might alias
+   STORE_PATTERN.  Return true if found.
+   AFTER is true if we are checking the case when STORE_PATTERN occurs
+   after the insn X.  */
+
+static bool
+find_loads (rtx x, rtx store_pattern, int after)
 {
   const char * fmt;
   int i, j;
-  struct ls_expr * ptr;
+  int ret = false;
+
+  if (!x)
+    return false;
+
+  if (GET_CODE (x) == SET)
+    x = SET_SRC (x);
 
-  /* Invalidate it in the list.  */
-  if (GET_CODE (x) == MEM && simple_mem (x))
+  if (MEM_P (x))
     {
-      ptr = ldst_entry (x);
-      ptr->invalid = 1;
+      if (load_kills_store (x, store_pattern, after))
+       return true;
     }
 
   /* Recursively process the insn.  */
   fmt = GET_RTX_FORMAT (GET_CODE (x));
 
-  for (i = GET_RTX_LENGTH (GET_CODE (x)) - 1; i >= 0; i--)
+  for (i = GET_RTX_LENGTH (GET_CODE (x)) - 1; i >= 0 && !ret; i--)
     {
       if (fmt[i] == 'e')
-       invalidate_any_buried_refs (XEXP (x, i));
+       ret |= find_loads (XEXP (x, i), store_pattern, after);
       else if (fmt[i] == 'E')
        for (j = XVECLEN (x, i) - 1; j >= 0; j--)
-         invalidate_any_buried_refs (XVECEXP (x, i, j));
+         ret |= find_loads (XVECEXP (x, i, j), store_pattern, after);
     }
+  return ret;
 }
 
-/* Find all the 'simple' MEMs which are used in LOADs and STORES. Simple
-   being defined as MEM loads and stores to symbols, with no
-   side effects and no registers in the expression. If there are any
-   uses/defs which don't match this criteria, it is invalidated and
-   trimmed out later.  */
+/* Check if INSN kills the store pattern X (is aliased with it).
+   AFTER is true if we are checking the case when store X occurs
+   after the insn.  Return true if it it does.  */
 
-static void
-compute_ld_motion_mems ()
+static bool
+store_killed_in_insn (rtx x, rtx x_regs, rtx insn, int after)
 {
-  struct ls_expr * ptr;
-  basic_block bb;
-  rtx insn;
+  rtx reg, base, note;
 
-  pre_ldst_mems = NULL;
+  if (!INSN_P (insn))
+    return false;
 
-  FOR_EACH_BB (bb)
+  if (CALL_P (insn))
     {
-      for (insn = bb->head;
-          insn && insn != NEXT_INSN (bb->end);
-          insn = NEXT_INSN (insn))
+      /* A normal or pure call might read from pattern,
+        but a const call will not.  */
+      if (! CONST_OR_PURE_CALL_P (insn) || pure_call_p (insn))
+       return true;
+
+      /* But even a const call reads its parameters.  Check whether the
+        base of some of registers used in mem is stack pointer.  */
+      for (reg = x_regs; reg; reg = XEXP (reg, 1))
        {
-         if (GET_RTX_CLASS (GET_CODE (insn)) == 'i')
-           {
-             if (GET_CODE (PATTERN (insn)) == SET)
-               {
-                 rtx src = SET_SRC (PATTERN (insn));
-                 rtx dest = SET_DEST (PATTERN (insn));
+         base = find_base_term (XEXP (reg, 0));
+         if (!base
+             || (GET_CODE (base) == ADDRESS
+                 && GET_MODE (base) == Pmode
+                 && XEXP (base, 0) == stack_pointer_rtx))
+           return true;
+       }
 
-                 /* Check for a simple LOAD...  */
-                 if (GET_CODE (src) == MEM && simple_mem (src))
-                   {
-                     ptr = ldst_entry (src);
-                     if (GET_CODE (dest) == REG)
-                       ptr->loads = alloc_INSN_LIST (insn, ptr->loads);
-                     else
-                       ptr->invalid = 1;
-                   }
-                 else
-                   {
-                     /* Make sure there isn't a buried load somewhere.  */
-                     invalidate_any_buried_refs (src);
-                   }
+      return false;
+    }
 
-                 /* Check for stores. Don't worry about aliased ones, they
-                    will block any movement we might do later. We only care
-                    about this exact pattern since those are the only
-                    circumstance that we will ignore the aliasing info.  */
-                 if (GET_CODE (dest) == MEM && simple_mem (dest))
-                   {
-                     ptr = ldst_entry (dest);
+  if (GET_CODE (PATTERN (insn)) == SET)
+    {
+      rtx pat = PATTERN (insn);
+      rtx dest = SET_DEST (pat);
 
-                     if (GET_CODE (src) != MEM
-                         && GET_CODE (src) != ASM_OPERANDS)
-                       ptr->stores = alloc_INSN_LIST (insn, ptr->stores);
-                     else
-                       ptr->invalid = 1;
-                   }
-               }
-             else
-               invalidate_any_buried_refs (PATTERN (insn));
+      if (GET_CODE (dest) == SIGN_EXTRACT
+         || GET_CODE (dest) == ZERO_EXTRACT)
+       dest = XEXP (dest, 0);
+
+      /* Check for memory stores to aliased objects.  */
+      if (MEM_P (dest)
+         && !expr_equiv_p (dest, x))
+       {
+         if (after)
+           {
+             if (output_dependence (dest, x))
+               return true;
+           }
+         else
+           {
+             if (output_dependence (x, dest))
+               return true;
            }
        }
+      if (find_loads (SET_SRC (pat), x, after))
+       return true;
     }
+  else if (find_loads (PATTERN (insn), x, after))
+    return true;
+
+  /* If this insn has a REG_EQUAL or REG_EQUIV note referencing a memory
+     location aliased with X, then this insn kills X.  */
+  note = find_reg_equal_equiv_note (insn);
+  if (! note)
+    return false;
+  note = XEXP (note, 0);
+
+  /* However, if the note represents a must alias rather than a may
+     alias relationship, then it does not kill X.  */
+  if (expr_equiv_p (note, x))
+    return false;
+
+  /* See if there are any aliased loads in the note.  */
+  return find_loads (note, x, after);
 }
 
-/* Remove any references that have been either invalidated or are not in the
-   expression list for pre gcse.  */
+/* Returns true if the expression X is loaded or clobbered on or after INSN
+   within basic block BB.  REGS_SET_AFTER is bitmap of registers set in
+   or after the insn.  X_REGS is list of registers mentioned in X. If the store
+   is killed, return the last insn in that it occurs in FAIL_INSN.  */
 
-static void
-trim_ld_motion_mems ()
+static bool
+store_killed_after (rtx x, rtx x_regs, rtx insn, basic_block bb,
+                   int *regs_set_after, rtx *fail_insn)
 {
-  struct ls_expr * last = NULL;
-  struct ls_expr * ptr = first_ls_expr ();
+  rtx last = BB_END (bb), act;
 
-  while (ptr != NULL)
+  if (!store_ops_ok (x_regs, regs_set_after))
     {
-      int del = ptr->invalid;
-      struct expr * expr = NULL;
+      /* We do not know where it will happen.  */
+      if (fail_insn)
+       *fail_insn = NULL_RTX;
+      return true;
+    }
 
-      /* Delete if entry has been made invalid.  */
-      if (!del)
+  /* Scan from the end, so that fail_insn is determined correctly.  */
+  for (act = last; act != PREV_INSN (insn); act = PREV_INSN (act))
+    if (store_killed_in_insn (x, x_regs, act, false))
+      {
+       if (fail_insn)
+         *fail_insn = act;
+       return true;
+      }
+
+  return false;
+}
+
+/* Returns true if the expression X is loaded or clobbered on or before INSN
+   within basic block BB. X_REGS is list of registers mentioned in X.
+   REGS_SET_BEFORE is bitmap of registers set before or in this insn.  */
+static bool
+store_killed_before (rtx x, rtx x_regs, rtx insn, basic_block bb,
+                    int *regs_set_before)
+{
+  rtx first = BB_HEAD (bb);
+
+  if (!store_ops_ok (x_regs, regs_set_before))
+    return true;
+
+  for ( ; insn != PREV_INSN (first); insn = PREV_INSN (insn))
+    if (store_killed_in_insn (x, x_regs, insn, true))
+      return true;
+
+  return false;
+}
+
+/* Fill in available, anticipatable, transparent and kill vectors in
+   STORE_DATA, based on lists of available and anticipatable stores.  */
+static void
+build_store_vectors (void)
+{
+  basic_block bb;
+  int *regs_set_in_block;
+  rtx insn, st;
+  struct ls_expr * ptr;
+  unsigned regno;
+
+  /* Build the gen_vector. This is any store in the table which is not killed
+     by aliasing later in its block.  */
+  ae_gen = sbitmap_vector_alloc (last_basic_block, num_stores);
+  sbitmap_vector_zero (ae_gen, last_basic_block);
+
+  st_antloc = sbitmap_vector_alloc (last_basic_block, num_stores);
+  sbitmap_vector_zero (st_antloc, last_basic_block);
+
+  for (ptr = first_ls_expr (); ptr != NULL; ptr = next_ls_expr (ptr))
+    {
+      for (st = AVAIL_STORE_LIST (ptr); st != NULL; st = XEXP (st, 1))
        {
-         unsigned int i;
+         insn = XEXP (st, 0);
+         bb = BLOCK_FOR_INSN (insn);
 
-         del = 1;
-         /* Delete if we cannot find this mem in the expression list.  */
-         for (i = 0; i < expr_hash_table.size && del; i++)
+         /* If we've already seen an available expression in this block,
+            we can delete this one (It occurs earlier in the block). We'll
+            copy the SRC expression to an unused register in case there
+            are any side effects.  */
+         if (TEST_BIT (ae_gen[bb->index], ptr->index))
            {
-             for (expr = expr_hash_table.table[i];
-                  expr != NULL;
-                  expr = expr->next_same_hash)
-               if (expr_equiv_p (expr->expr, ptr->pattern))
-                 {
-                   del = 0;
-                   break;
-                 }
+             rtx r = gen_reg_rtx (GET_MODE (ptr->pattern));
+             if (gcse_file)
+               fprintf (gcse_file, "Removing redundant store:\n");
+             replace_store_insn (r, XEXP (st, 0), bb, ptr);
+             continue;
            }
+         SET_BIT (ae_gen[bb->index], ptr->index);
        }
 
-      if (del)
+      for (st = ANTIC_STORE_LIST (ptr); st != NULL; st = XEXP (st, 1))
        {
-         if (last != NULL)
+         insn = XEXP (st, 0);
+         bb = BLOCK_FOR_INSN (insn);
+         SET_BIT (st_antloc[bb->index], ptr->index);
+       }
+    }
+
+  ae_kill = sbitmap_vector_alloc (last_basic_block, num_stores);
+  sbitmap_vector_zero (ae_kill, last_basic_block);
+
+  transp = sbitmap_vector_alloc (last_basic_block, num_stores);
+  sbitmap_vector_zero (transp, last_basic_block);
+  regs_set_in_block = xmalloc (sizeof (int) * max_gcse_regno);
+
+  FOR_EACH_BB (bb)
+    {
+      for (regno = 0; regno < max_gcse_regno; regno++)
+       regs_set_in_block[regno] = TEST_BIT (reg_set_in_block[bb->index], regno);
+
+      for (ptr = first_ls_expr (); ptr != NULL; ptr = next_ls_expr (ptr))
+       {
+         if (store_killed_after (ptr->pattern, ptr->pattern_regs, BB_HEAD (bb),
+                                 bb, regs_set_in_block, NULL))
            {
-             last->next = ptr->next;
-             free_ldst_entry (ptr);
-             ptr = last->next;
+             /* It should not be necessary to consider the expression
+                killed if it is both anticipatable and available.  */
+             if (!TEST_BIT (st_antloc[bb->index], ptr->index)
+                 || !TEST_BIT (ae_gen[bb->index], ptr->index))
+               SET_BIT (ae_kill[bb->index], ptr->index);
            }
          else
-           {
-             pre_ldst_mems = pre_ldst_mems->next;
-             free_ldst_entry (ptr);
-             ptr = pre_ldst_mems;
-           }
-       }
-      else
-       {
-         /* Set the expression field if we are keeping it.  */
-         last = ptr;
-         ptr->expr = expr;
-         ptr = ptr->next;
+           SET_BIT (transp[bb->index], ptr->index);
        }
     }
 
-  /* Show the world what we've found.  */
-  if (gcse_file && pre_ldst_mems != NULL)
-    print_ldst_list (gcse_file);
+  free (regs_set_in_block);
+
+  if (gcse_file)
+    {
+      dump_sbitmap_vector (gcse_file, "st_antloc", "", st_antloc, last_basic_block);
+      dump_sbitmap_vector (gcse_file, "st_kill", "", ae_kill, last_basic_block);
+      dump_sbitmap_vector (gcse_file, "Transpt", "", transp, last_basic_block);
+      dump_sbitmap_vector (gcse_file, "st_avloc", "", ae_gen, last_basic_block);
+    }
 }
 
-/* This routine will take an expression which we are replacing with
-   a reaching register, and update any stores that are needed if
-   that expression is in the ld_motion list.  Stores are updated by
-   copying their SRC to the reaching register, and then storeing
-   the reaching register into the store location. These keeps the
-   correct value in the reaching register for the loads.  */
+/* Insert an instruction at the beginning of a basic block, and update
+   the BB_HEAD if needed.  */
 
 static void
-update_ld_motion_stores (expr)
-     struct expr * expr;
+insert_insn_start_bb (rtx insn, basic_block bb)
 {
-  struct ls_expr * mem_ptr;
+  /* Insert at start of successor block.  */
+  rtx prev = PREV_INSN (BB_HEAD (bb));
+  rtx before = BB_HEAD (bb);
+  while (before != 0)
+    {
+      if (! LABEL_P (before)
+         && (! NOTE_P (before)
+             || NOTE_LINE_NUMBER (before) != NOTE_INSN_BASIC_BLOCK))
+       break;
+      prev = before;
+      if (prev == BB_END (bb))
+       break;
+      before = NEXT_INSN (before);
+    }
 
-  if ((mem_ptr = find_rtx_in_ldst (expr->expr)))
+  insn = emit_insn_after (insn, prev);
+
+  if (gcse_file)
     {
-      /* We can try to find just the REACHED stores, but is shouldn't
-        matter to set the reaching reg everywhere...  some might be
-        dead and should be eliminated later.  */
+      fprintf (gcse_file, "STORE_MOTION  insert store at start of BB %d:\n",
+              bb->index);
+      print_inline_rtx (gcse_file, insn, 6);
+      fprintf (gcse_file, "\n");
+    }
+}
 
-      /* We replace  SET mem = expr   with
-          SET reg = expr
-          SET mem = reg , where reg is the
-          reaching reg used in the load.  */
-      rtx list = mem_ptr->stores;
+/* This routine will insert a store on an edge. EXPR is the ldst entry for
+   the memory reference, and E is the edge to insert it on.  Returns nonzero
+   if an edge insertion was performed.  */
 
-      for ( ; list != NULL_RTX; list = XEXP (list, 1))
+static int
+insert_store (struct ls_expr * expr, edge e)
+{
+  rtx reg, insn;
+  basic_block bb;
+  edge tmp;
+
+  /* We did all the deleted before this insert, so if we didn't delete a
+     store, then we haven't set the reaching reg yet either.  */
+  if (expr->reaching_reg == NULL_RTX)
+    return 0;
+
+  if (e->flags & EDGE_FAKE)
+    return 0;
+
+  reg = expr->reaching_reg;
+  insn = gen_move_insn (copy_rtx (expr->pattern), reg);
+
+  /* If we are inserting this expression on ALL predecessor edges of a BB,
+     insert it at the start of the BB, and reset the insert bits on the other
+     edges so we don't try to insert it on the other edges.  */
+  bb = e->dest;
+  for (tmp = e->dest->pred; tmp ; tmp = tmp->pred_next)
+    if (!(tmp->flags & EDGE_FAKE))
+      {
+       int index = EDGE_INDEX (edge_list, tmp->src, tmp->dest);
+       if (index == EDGE_INDEX_NO_EDGE)
+         abort ();
+       if (! TEST_BIT (pre_insert_map[index], expr->index))
+         break;
+      }
+
+  /* If tmp is NULL, we found an insertion on every edge, blank the
+     insertion vector for these edges, and insert at the start of the BB.  */
+  if (!tmp && bb != EXIT_BLOCK_PTR)
+    {
+      for (tmp = e->dest->pred; tmp ; tmp = tmp->pred_next)
        {
-         rtx insn = XEXP (list, 0);
-         rtx pat = PATTERN (insn);
-         rtx src = SET_SRC (pat);
-         rtx reg = expr->reaching_reg;
-         rtx copy, new;
+         int index = EDGE_INDEX (edge_list, tmp->src, tmp->dest);
+         RESET_BIT (pre_insert_map[index], expr->index);
+       }
+      insert_insn_start_bb (insn, bb);
+      return 0;
+    }
 
-         /* If we've already copied it, continue.  */
-         if (expr->reaching_reg == src)
-           continue;
+  /* We can't insert on this edge, so we'll insert at the head of the
+     successors block.  See Morgan, sec 10.5.  */
+  if ((e->flags & EDGE_ABNORMAL) == EDGE_ABNORMAL)
+    {
+      insert_insn_start_bb (insn, bb);
+      return 0;
+    }
 
-         if (gcse_file)
-           {
-             fprintf (gcse_file, "PRE:  store updated with reaching reg ");
-             print_rtl (gcse_file, expr->reaching_reg);
-             fprintf (gcse_file, ":\n  ");
-             print_inline_rtx (gcse_file, insn, 8);
-             fprintf (gcse_file, "\n");
+  insert_insn_on_edge (insn, e);
+
+  if (gcse_file)
+    {
+      fprintf (gcse_file, "STORE_MOTION  insert insn on edge (%d, %d):\n",
+              e->src->index, e->dest->index);
+      print_inline_rtx (gcse_file, insn, 6);
+      fprintf (gcse_file, "\n");
+    }
+
+  return 1;
+}
+
+/* Remove any REG_EQUAL or REG_EQUIV notes containing a reference to the
+   memory location in SMEXPR set in basic block BB.
+
+   This could be rather expensive.  */
+
+static void
+remove_reachable_equiv_notes (basic_block bb, struct ls_expr *smexpr)
+{
+  edge *stack = xmalloc (sizeof (edge) * n_basic_blocks), act;
+  sbitmap visited = sbitmap_alloc (last_basic_block);
+  int stack_top = 0;
+  rtx last, insn, note;
+  rtx mem = smexpr->pattern;
+
+  sbitmap_zero (visited);
+  act = bb->succ;
+
+  while (1)
+    {
+      if (!act)
+       {
+         if (!stack_top)
+           {
+             free (stack);
+             sbitmap_free (visited);
+             return;
            }
+         act = stack[--stack_top];
+       }
+      bb = act->dest;
 
-         copy = gen_move_insn ( reg, SET_SRC (pat));
-         new = emit_insn_before (copy, insn);
-         record_one_set (REGNO (reg), new);
-         SET_SRC (pat) = reg;
-
-         /* un-recognize this pattern since it's probably different now.  */
-         INSN_CODE (insn) = -1;
-         gcse_create_count++;
+      if (bb == EXIT_BLOCK_PTR
+         || TEST_BIT (visited, bb->index))
+       {
+         act = act->succ_next;
+         continue;
        }
-    }
-}
-\f
-/* Store motion code.  */
+      SET_BIT (visited, bb->index);
 
-/* This is used to communicate the target bitvector we want to use in the
-   reg_set_info routine when called via the note_stores mechanism.  */
-static sbitmap * regvec;
+      if (TEST_BIT (st_antloc[bb->index], smexpr->index))
+       {
+         for (last = ANTIC_STORE_LIST (smexpr);
+              BLOCK_FOR_INSN (XEXP (last, 0)) != bb;
+              last = XEXP (last, 1))
+           continue;
+         last = XEXP (last, 0);
+       }
+      else
+       last = NEXT_INSN (BB_END (bb));
 
-/* Used in computing the reverse edge graph bit vectors.  */
-static sbitmap * st_antloc;
+      for (insn = BB_HEAD (bb); insn != last; insn = NEXT_INSN (insn))
+       if (INSN_P (insn))
+         {
+           note = find_reg_equal_equiv_note (insn);
+           if (!note || !expr_equiv_p (XEXP (note, 0), mem))
+             continue;
 
-/* Global holding the number of store expressions we are dealing with.  */
-static int num_stores;
+           if (gcse_file)
+             fprintf (gcse_file, "STORE_MOTION  drop REG_EQUAL note at insn %d:\n",
+                      INSN_UID (insn));
+           remove_note (insn, note);
+         }
+      act = act->succ_next;
+      if (bb->succ)
+       {
+         if (act)
+           stack[stack_top++] = act;
+         act = bb->succ;
+       }
+    }
+}
 
-/* Checks to set if we need to mark a register set. Called from note_stores.  */
+/* This routine will replace a store with a SET to a specified register.  */
 
 static void
-reg_set_info (dest, setter, data)
-     rtx dest, setter ATTRIBUTE_UNUSED;
-     void * data ATTRIBUTE_UNUSED;
+replace_store_insn (rtx reg, rtx del, basic_block bb, struct ls_expr *smexpr)
 {
-  if (GET_CODE (dest) == SUBREG)
-    dest = SUBREG_REG (dest);
-
-  if (GET_CODE (dest) == REG)
-    SET_BIT (*regvec, REGNO (dest));
-}
+  rtx insn, mem, note, set, ptr;
 
-/* Return nonzero if the register operands of expression X are killed
-   anywhere in basic block BB.  */
+  mem = smexpr->pattern;
+  insn = gen_move_insn (reg, SET_SRC (single_set (del)));
+  insn = emit_insn_after (insn, del);
 
-static int
-store_ops_ok (x, bb)
-     rtx x;
-     basic_block bb;
-{
-  int i;
-  enum rtx_code code;
-  const char * fmt;
+  if (gcse_file)
+    {
+      fprintf (gcse_file,
+              "STORE_MOTION  delete insn in BB %d:\n      ", bb->index);
+      print_inline_rtx (gcse_file, del, 6);
+      fprintf (gcse_file, "\nSTORE MOTION  replaced with insn:\n      ");
+      print_inline_rtx (gcse_file, insn, 6);
+      fprintf (gcse_file, "\n");
+    }
 
-  /* Repeat is used to turn tail-recursion into iteration.  */
- repeat:
+  for (ptr = ANTIC_STORE_LIST (smexpr); ptr; ptr = XEXP (ptr, 1))
+    if (XEXP (ptr, 0) == del)
+      {
+       XEXP (ptr, 0) = insn;
+       break;
+      }
+  delete_insn (del);
 
-  if (x == 0)
-    return 1;
+  /* Now we must handle REG_EQUAL notes whose contents is equal to the mem;
+     they are no longer accurate provided that they are reached by this
+     definition, so drop them.  */
+  for (; insn != NEXT_INSN (BB_END (bb)); insn = NEXT_INSN (insn))
+    if (INSN_P (insn))
+      {
+       set = single_set (insn);
+       if (!set)
+         continue;
+       if (expr_equiv_p (SET_DEST (set), mem))
+         return;
+       note = find_reg_equal_equiv_note (insn);
+       if (!note || !expr_equiv_p (XEXP (note, 0), mem))
+         continue;
 
-  code = GET_CODE (x);
-  switch (code)
-    {
-    case REG:
-       /* If a reg has changed after us in this
-          block, the operand has been killed.  */
-       return TEST_BIT (reg_set_in_block[bb->index], REGNO (x));
+       if (gcse_file)
+         fprintf (gcse_file, "STORE_MOTION  drop REG_EQUAL note at insn %d:\n",
+                  INSN_UID (insn));
+       remove_note (insn, note);
+      }
+  remove_reachable_equiv_notes (bb, smexpr);
+}
 
-    case MEM:
-      x = XEXP (x, 0);
-      goto repeat;
 
-    case PRE_DEC:
-    case PRE_INC:
-    case POST_DEC:
-    case POST_INC:
-      return 0;
+/* Delete a store, but copy the value that would have been stored into
+   the reaching_reg for later storing.  */
 
-    case PC:
-    case CC0: /*FIXME*/
-    case CONST:
-    case CONST_INT:
-    case CONST_DOUBLE:
-    case CONST_VECTOR:
-    case SYMBOL_REF:
-    case LABEL_REF:
-    case ADDR_VEC:
-    case ADDR_DIFF_VEC:
-      return 1;
+static void
+delete_store (struct ls_expr * expr, basic_block bb)
+{
+  rtx reg, i, del;
 
-    default:
-      break;
-    }
+  if (expr->reaching_reg == NULL_RTX)
+    expr->reaching_reg = gen_reg_rtx (GET_MODE (expr->pattern));
 
-  i = GET_RTX_LENGTH (code) - 1;
-  fmt = GET_RTX_FORMAT (code);
+  reg = expr->reaching_reg;
 
-  for (; i >= 0; i--)
+  for (i = AVAIL_STORE_LIST (expr); i; i = XEXP (i, 1))
     {
-      if (fmt[i] == 'e')
+      del = XEXP (i, 0);
+      if (BLOCK_FOR_INSN (del) == bb)
        {
-         rtx tem = XEXP (x, i);
+         /* We know there is only one since we deleted redundant
+            ones during the available computation.  */
+         replace_store_insn (reg, del, bb, expr);
+         break;
+       }
+    }
+}
 
-         /* 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;
-             goto repeat;
-           }
+/* Free memory used by store motion.  */
 
-         if (! store_ops_ok (tem, bb))
-           return 0;
-       }
-      else if (fmt[i] == 'E')
-       {
-         int j;
+static void
+free_store_memory (void)
+{
+  free_ldst_mems ();
 
-         for (j = 0; j < XVECLEN (x, i); j++)
-           {
-             if (! store_ops_ok (XVECEXP (x, i, j), bb))
-               return 0;
-           }
-       }
-    }
+  if (ae_gen)
+    sbitmap_vector_free (ae_gen);
+  if (ae_kill)
+    sbitmap_vector_free (ae_kill);
+  if (transp)
+    sbitmap_vector_free (transp);
+  if (st_antloc)
+    sbitmap_vector_free (st_antloc);
+  if (pre_insert_map)
+    sbitmap_vector_free (pre_insert_map);
+  if (pre_delete_map)
+    sbitmap_vector_free (pre_delete_map);
+  if (reg_set_in_block)
+    sbitmap_vector_free (reg_set_in_block);
 
-  return 1;
+  ae_gen = ae_kill = transp = st_antloc = NULL;
+  pre_insert_map = pre_delete_map = reg_set_in_block = NULL;
 }
 
-/* Determine whether insn is MEM store pattern that we will consider moving.  */
+/* Perform store motion. Much like gcse, except we move expressions the
+   other way by looking at the flowgraph in reverse.  */
 
 static void
-find_moveable_store (insn)
-     rtx insn;
+store_motion (void)
 {
+  basic_block bb;
+  int x;
   struct ls_expr * ptr;
-  rtx dest = PATTERN (insn);
-
-  if (GET_CODE (dest) != SET
-      || GET_CODE (SET_SRC (dest)) == ASM_OPERANDS)
-    return;
+  int update_flow = 0;
 
-  dest = SET_DEST (dest);
+  if (gcse_file)
+    {
+      fprintf (gcse_file, "before store motion\n");
+      print_rtl (gcse_file, get_insns ());
+    }
 
-  if (GET_CODE (dest) != MEM || MEM_VOLATILE_P (dest)
-      || GET_MODE (dest) == BLKmode)
-    return;
+  init_alias_analysis ();
 
-  if (GET_CODE (XEXP (dest, 0)) != SYMBOL_REF)
+  /* Find all the available and anticipatable stores.  */
+  num_stores = compute_store_table ();
+  if (num_stores == 0)
+    {
+      sbitmap_vector_free (reg_set_in_block);
+      end_alias_analysis ();
       return;
+    }
 
-  if (rtx_varies_p (XEXP (dest, 0), 0))
-    return;
+  /* Now compute kill & transp vectors.  */
+  build_store_vectors ();
+  add_noreturn_fake_exit_edges ();
+  connect_infinite_loops_to_exit ();
 
-  ptr = ldst_entry (dest);
-  ptr->stores = alloc_INSN_LIST (insn, ptr->stores);
+  edge_list = pre_edge_rev_lcm (gcse_file, num_stores, transp, ae_gen,
+                               st_antloc, ae_kill, &pre_insert_map,
+                               &pre_delete_map);
+
+  /* Now we want to insert the new stores which are going to be needed.  */
+  for (ptr = first_ls_expr (); ptr != NULL; ptr = next_ls_expr (ptr))
+    {
+      FOR_EACH_BB (bb)
+       if (TEST_BIT (pre_delete_map[bb->index], ptr->index))
+         delete_store (ptr, bb);
+
+      for (x = 0; x < NUM_EDGES (edge_list); x++)
+       if (TEST_BIT (pre_insert_map[x], ptr->index))
+         update_flow |= insert_store (ptr, INDEX_EDGE (edge_list, x));
+    }
+
+  if (update_flow)
+    commit_edge_insertions ();
+
+  free_store_memory ();
+  free_edge_list (edge_list);
+  remove_fake_edges ();
+  end_alias_analysis ();
 }
 
-/* Perform store motion. Much like gcse, except we move expressions the
-   other way by looking at the flowgraph in reverse.  */
+\f
+/* Entry point for jump bypassing optimization pass.  */
 
-static int
-compute_store_table ()
+int
+bypass_jumps (FILE *file)
 {
-  int ret;
-  basic_block bb;
-  unsigned regno;
-  rtx insn, pat;
+  int changed;
+
+  /* We do not construct an accurate cfg in functions which call
+     setjmp, so just punt to be safe.  */
+  if (current_function_calls_setjmp)
+    return 0;
+
+  /* For calling dump_foo fns from gdb.  */
+  debug_stderr = stderr;
+  gcse_file = file;
 
+  /* Identify the basic block information for this function, including
+     successors and predecessors.  */
   max_gcse_regno = max_reg_num ();
 
-  reg_set_in_block = (sbitmap *) sbitmap_vector_alloc (last_basic_block,
-                                                      max_gcse_regno);
-  sbitmap_vector_zero (reg_set_in_block, last_basic_block);
-  pre_ldst_mems = 0;
+  if (file)
+    dump_flow_info (file);
 
-  /* Find all the stores we care about.  */
-  FOR_EACH_BB (bb)
-    {
-      regvec = & (reg_set_in_block[bb->index]);
-      for (insn = bb->end;
-          insn && insn != PREV_INSN (bb->end);
-          insn = PREV_INSN (insn))
-       {
-         /* Ignore anything that is not a normal insn.  */
-         if (! INSN_P (insn))
-           continue;
+  /* Return if there's nothing to do, or it is too expensive.  */
+  if (n_basic_blocks <= 1 || is_too_expensive (_ ("jump bypassing disabled")))
+    return 0;
 
-         if (GET_CODE (insn) == CALL_INSN)
-           {
-             bool clobbers_all = false;
-#ifdef NON_SAVING_SETJMP
-             if (NON_SAVING_SETJMP
-                 && find_reg_note (insn, REG_SETJMP, NULL_RTX))
-               clobbers_all = true;
-#endif
+  gcc_obstack_init (&gcse_obstack);
+  bytes_used = 0;
 
-             for (regno = 0; regno < FIRST_PSEUDO_REGISTER; regno++)
-               if (clobbers_all
-                   || TEST_HARD_REG_BIT (regs_invalidated_by_call, regno))
-                 SET_BIT (reg_set_in_block[bb->index], regno);
-           }
+  /* We need alias.  */
+  init_alias_analysis ();
+
+  /* Record where pseudo-registers are set.  This data is kept accurate
+     during each pass.  ??? We could also record hard-reg information here
+     [since it's unchanging], however it is currently done during hash table
+     computation.
 
-         pat = PATTERN (insn);
-         note_stores (pat, reg_set_info, NULL);
+     It may be tempting to compute MEM set information here too, but MEM sets
+     will be subject to code motion one day and thus we need to compute
+     information about memory sets when we build the hash tables.  */
 
-         /* Now that we've marked regs, look for stores.  */
-         if (GET_CODE (pat) == SET)
-           find_moveable_store (insn);
-       }
-    }
+  alloc_reg_set_mem (max_gcse_regno);
+  compute_sets (get_insns ());
 
-  ret = enumerate_ldsts ();
+  max_gcse_regno = max_reg_num ();
+  alloc_gcse_mem (get_insns ());
+  changed = one_cprop_pass (1, 1, 1);
+  free_gcse_mem ();
 
-  if (gcse_file)
+  if (file)
     {
-      fprintf (gcse_file, "Store Motion Expressions.\n");
-      print_ldst_list (gcse_file);
+      fprintf (file, "BYPASS of %s: %d basic blocks, ",
+              current_function_name (), n_basic_blocks);
+      fprintf (file, "%d bytes\n\n", bytes_used);
     }
 
-  return ret;
-}
+  obstack_free (&gcse_obstack, NULL);
+  free_reg_set_mem ();
 
-/* Check to see if the load X is aliased with STORE_PATTERN.  */
+  /* We are finished with alias.  */
+  end_alias_analysis ();
+  allocate_reg_info (max_reg_num (), FALSE, FALSE);
 
-static int
-load_kills_store (x, store_pattern)
-     rtx x, store_pattern;
-{
-  if (true_dependence (x, GET_MODE (x), store_pattern, rtx_addr_varies_p))
-    return 1;
-  return 0;
+  return changed;
 }
 
-/* Go through the entire insn X, looking for any loads which might alias
-   STORE_PATTERN.  Return 1 if found.  */
+/* Return true if the graph is too expensive to optimize. PASS is the
+   optimization about to be performed.  */
 
-static int
-find_loads (x, store_pattern)
-     rtx x, store_pattern;
+static bool
+is_too_expensive (const char *pass)
 {
-  const char * fmt;
-  int i, j;
-  int ret = 0;
-
-  if (!x)
-    return 0;
-
-  if (GET_CODE (x) == SET)
-    x = SET_SRC (x);
+  /* Trying to perform global optimizations on flow graphs which have
+     a high connectivity will take a long time and is unlikely to be
+     particularly useful.
 
-  if (GET_CODE (x) == MEM)
+     In normal circumstances a cfg should have about twice as many
+     edges as blocks.  But we do not want to punish small functions
+     which have a couple switch statements.  Rather than simply
+     threshold the number of blocks, uses something with a more
+     graceful degradation.  */
+  if (n_edges > 20000 + n_basic_blocks * 4)
     {
-      if (load_kills_store (x, store_pattern))
-       return 1;
-    }
+      if (warn_disabled_optimization)
+       warning ("%s: %d basic blocks and %d edges/basic block",
+                pass, n_basic_blocks, n_edges / n_basic_blocks);
 
-  /* Recursively process the insn.  */
-  fmt = GET_RTX_FORMAT (GET_CODE (x));
+      return true;
+    }
 
-  for (i = GET_RTX_LENGTH (GET_CODE (x)) - 1; i >= 0 && !ret; i--)
+  /* If allocating memory for the cprop bitmap would take up too much
+     storage it's better just to disable the optimization.  */
+  if ((n_basic_blocks
+       * SBITMAP_SET_SIZE (max_reg_num ())
+       * sizeof (SBITMAP_ELT_TYPE)) > MAX_GCSE_MEMORY)
     {
-      if (fmt[i] == 'e')
-       ret |= find_loads (XEXP (x, i), store_pattern);
-      else if (fmt[i] == 'E')
-       for (j = XVECLEN (x, i) - 1; j >= 0; j--)
-         ret |= find_loads (XVECEXP (x, i, j), store_pattern);
+      if (warn_disabled_optimization)
+       warning ("%s: %d basic blocks and %d registers",
+                pass, n_basic_blocks, max_reg_num ());
+
+      return true;
     }
-  return ret;
+
+  return false;
 }
 
-/* Check if INSN kills the store pattern X (is aliased with it).
-   Return 1 if it it does.  */
+/* The following code implements gcse after reload, the purpose of this
+   pass is to cleanup redundant loads generated by reload and other
+   optimizations that come after gcse. It searches for simple inter-block
+   redundancies and tries to eliminate them by adding moves and loads
+   in cold places.  */
 
-static int
-store_killed_in_insn (x, insn)
-     rtx x, insn;
+/* The following structure holds the information about the occurrences of
+   the redundant instructions.  */
+struct unoccr
 {
-  if (GET_RTX_CLASS (GET_CODE (insn)) != 'i')
-    return 0;
+  struct unoccr *next;
+  edge pred;
+  rtx insn;
+};
 
-  if (GET_CODE (insn) == CALL_INSN)
-    {
-      /* A normal or pure call might read from pattern,
-        but a const call will not.  */
-      return ! CONST_OR_PURE_CALL_P (insn) || pure_call_p (insn);
-    }
+static bool reg_used_on_edge (rtx, edge);
+static rtx reg_set_between_after_reload_p (rtx, rtx, rtx);
+static rtx reg_used_between_after_reload_p (rtx, rtx, rtx);
+static rtx get_avail_load_store_reg (rtx);
+static bool is_jump_table_basic_block (basic_block);
+static bool bb_has_well_behaved_predecessors (basic_block);
+static struct occr* get_bb_avail_insn (basic_block, struct occr *);
+static void hash_scan_set_after_reload (rtx, rtx, struct hash_table *);
+static void compute_hash_table_after_reload (struct hash_table *);
+static void eliminate_partially_redundant_loads (basic_block,
+                                               rtx,
+                                               struct expr *);
+static void gcse_after_reload (void);
+static struct occr* get_bb_avail_insn (basic_block, struct occr *);
+void gcse_after_reload_main (rtx, FILE *);
+
+
+/* Check if register REG is used in any insn waiting to be inserted on E.
+   Assumes no such insn can be a CALL_INSN; if so call reg_used_between_p
+   with PREV(insn),NEXT(insn) instead of calling
+   reg_overlap_mentioned_p.  */
 
-  if (GET_CODE (PATTERN (insn)) == SET)
-    {
-      rtx pat = PATTERN (insn);
-      /* Check for memory stores to aliased objects.  */
-      if (GET_CODE (SET_DEST (pat)) == MEM && !expr_equiv_p (SET_DEST (pat), x))
-       /* pretend its a load and check for aliasing.  */
-       if (find_loads (SET_DEST (pat), x))
-         return 1;
-      return find_loads (SET_SRC (pat), x);
-    }
-  else
-    return find_loads (PATTERN (insn), x);
+static bool
+reg_used_on_edge (rtx reg, edge e)
+{
+  rtx insn;
+
+  for (insn = e->insns.r; insn; insn = NEXT_INSN (insn))
+    if (INSN_P (insn) && reg_overlap_mentioned_p (reg, PATTERN (insn)))
+      return true;
+
+  return false;
 }
 
-/* Returns 1 if the expression X is loaded or clobbered on or after INSN
-   within basic block BB.  */
+/* Return the insn that sets register REG or clobbers it in between
+   FROM_INSN and TO_INSN (exclusive of those two).
+   Just like reg_set_between but for hard registers and not pseudos.  */
 
-static int
-store_killed_after (x, insn, bb)
-     rtx x, insn;
-     basic_block bb;
+static rtx
+reg_set_between_after_reload_p (rtx reg, rtx from_insn, rtx to_insn)
 {
-  rtx last = bb->end;
+  rtx insn;
+  int regno;
 
-  if (insn == last)
-    return 0;
+  if (! REG_P (reg))
+    abort ();
+  regno = REGNO (reg);
 
-  /* Check if the register operands of the store are OK in this block.
-     Note that if registers are changed ANYWHERE in the block, we'll
-     decide we can't move it, regardless of whether it changed above
-     or below the store. This could be improved by checking the register
-     operands while looking for aliasing in each insn.  */
-  if (!store_ops_ok (XEXP (x, 0), bb))
-    return 1;
+  /* We are called after register allocation.  */
+  if (regno >= FIRST_PSEUDO_REGISTER)
+    abort ();
 
-  for ( ; insn && insn != NEXT_INSN (last); insn = NEXT_INSN (insn))
-    if (store_killed_in_insn (x, insn))
-      return 1;
+  if (from_insn == to_insn)
+    return NULL_RTX;
 
-  return 0;
+  for (insn = NEXT_INSN (from_insn);
+       insn != to_insn;
+       insn = NEXT_INSN (insn))
+    {
+      if (INSN_P (insn))
+       {
+         if (FIND_REG_INC_NOTE (insn, reg)
+             || (CALL_P (insn)
+                 && call_used_regs[regno])
+             || find_reg_fusage (insn, CLOBBER, reg))
+           return insn;
+       }
+      if (set_of (reg, insn) != NULL_RTX)
+       return insn;
+    }
+  return NULL_RTX;
 }
 
-/* Returns 1 if the expression X is loaded or clobbered on or before INSN
-   within basic block BB.  */
-static int
-store_killed_before (x, insn, bb)
-     rtx x, insn;
-     basic_block bb;
+/* Return the insn that uses register REG in between FROM_INSN and TO_INSN
+   (exclusive of those two). Similar to reg_used_between but for hard
+   registers and not pseudos.  */
+
+static rtx
+reg_used_between_after_reload_p (rtx reg, rtx from_insn, rtx to_insn)
 {
-  rtx first = bb->head;
+  rtx insn;
+  int regno;
 
-  if (insn == first)
-    return store_killed_in_insn (x, insn);
+  if (! REG_P (reg))
+    return to_insn;
+  regno = REGNO (reg);
 
-  /* Check if the register operands of the store are OK in this block.
-     Note that if registers are changed ANYWHERE in the block, we'll
-     decide we can't move it, regardless of whether it changed above
-     or below the store. This could be improved by checking the register
-     operands while looking for aliasing in each insn.  */
-  if (!store_ops_ok (XEXP (x, 0), bb))
-    return 1;
+  /* We are called after register allocation.  */
+  if (regno >= FIRST_PSEUDO_REGISTER)
+    abort ();
+  if (from_insn == to_insn)
+    return NULL_RTX;
 
-  for ( ; insn && insn != PREV_INSN (first); insn = PREV_INSN (insn))
-    if (store_killed_in_insn (x, insn))
-      return 1;
+  for (insn = NEXT_INSN (from_insn);
+       insn != to_insn;
+       insn = NEXT_INSN (insn))
+    if (INSN_P (insn)
+       && (reg_overlap_mentioned_p (reg, PATTERN (insn))
+           || (CALL_P (insn)
+               && call_used_regs[regno])
+           || find_reg_fusage (insn, USE, reg)
+           || find_reg_fusage (insn, CLOBBER, reg)))
+      return insn;
+  return NULL_RTX;
+}
 
-  return 0;
+/* Return the loaded/stored register of a load/store instruction.  */
+
+static rtx
+get_avail_load_store_reg (rtx insn)
+{
+  if (REG_P (SET_DEST (PATTERN (insn))))  /* A load.  */
+    return SET_DEST(PATTERN(insn));
+  if (REG_P (SET_SRC (PATTERN (insn))))  /* A store.  */
+    return SET_SRC (PATTERN (insn));
+  abort ();
 }
 
-#define ANTIC_STORE_LIST(x)    ((x)->loads)
-#define AVAIL_STORE_LIST(x)    ((x)->stores)
+/* Don't handle ABNORMAL edges or jump tables.  */
 
-/* Given the table of available store insns at the end of blocks,
-   determine which ones are not killed by aliasing, and generate
-   the appropriate vectors for gen and killed.  */
-static void
-build_store_vectors ()
+static bool
+is_jump_table_basic_block (basic_block bb)
 {
-  basic_block bb, b;
-  rtx insn, st;
-  struct ls_expr * ptr;
+  rtx insn = BB_END (bb);
 
-  /* Build the gen_vector. This is any store in the table which is not killed
-     by aliasing later in its block.  */
-  ae_gen = (sbitmap *) sbitmap_vector_alloc (last_basic_block, num_stores);
-  sbitmap_vector_zero (ae_gen, last_basic_block);
+  if (JUMP_TABLE_DATA_P (insn))
+    return true;
+  return false;
+}
 
-  st_antloc = (sbitmap *) sbitmap_vector_alloc (last_basic_block, num_stores);
-  sbitmap_vector_zero (st_antloc, last_basic_block);
+/* Return nonzero if the predecessors of BB are "well behaved".  */
 
-  for (ptr = first_ls_expr (); ptr != NULL; ptr = next_ls_expr (ptr))
-    {
-      /* Put all the stores into either the antic list, or the avail list,
-        or both.  */
-      rtx store_list = ptr->stores;
-      ptr->stores = NULL_RTX;
+static bool
+bb_has_well_behaved_predecessors (basic_block bb)
+{
+  edge pred;
 
-      for (st = store_list; st != NULL; st = XEXP (st, 1))
-       {
-         insn = XEXP (st, 0);
-         bb = BLOCK_FOR_INSN (insn);
+  if (! bb->pred)
+    return false;
+  for (pred = bb->pred; pred != NULL; pred = pred->pred_next)
+    if (((pred->flags & EDGE_ABNORMAL) && EDGE_CRITICAL_P (pred))
+       || is_jump_table_basic_block (pred->src))
+      return false;
+  return true;
+}
 
-         if (!store_killed_after (ptr->pattern, insn, bb))
-           {
-             /* If we've already seen an available expression in this block,
-                we can delete the one we saw already (It occurs earlier in
-                the block), and replace it with this one). We'll copy the
-                old SRC expression to an unused register in case there
-                are any side effects.  */
-             if (TEST_BIT (ae_gen[bb->index], ptr->index))
-               {
-                 /* Find previous store.  */
-                 rtx st;
-                 for (st = AVAIL_STORE_LIST (ptr); st ; st = XEXP (st, 1))
-                   if (BLOCK_FOR_INSN (XEXP (st, 0)) == bb)
-                     break;
-                 if (st)
-                   {
-                     rtx r = gen_reg_rtx (GET_MODE (ptr->pattern));
-                     if (gcse_file)
-                       fprintf (gcse_file, "Removing redundant store:\n");
-                     replace_store_insn (r, XEXP (st, 0), bb);
-                     XEXP (st, 0) = insn;
-                     continue;
-                   }
-               }
-             SET_BIT (ae_gen[bb->index], ptr->index);
-             AVAIL_STORE_LIST (ptr) = alloc_INSN_LIST (insn,
-                                                       AVAIL_STORE_LIST (ptr));
-           }
 
-         if (!store_killed_before (ptr->pattern, insn, bb))
-           {
-             SET_BIT (st_antloc[BLOCK_NUM (insn)], ptr->index);
-             ANTIC_STORE_LIST (ptr) = alloc_INSN_LIST (insn,
-                                                       ANTIC_STORE_LIST (ptr));
-           }
-       }
+/* Search for the occurrences of expression in BB.  */
 
-      /* Free the original list of store insns.  */
-      free_INSN_LIST_list (&store_list);
-    }
+static struct occr*
+get_bb_avail_insn (basic_block bb, struct occr *occr)
+{
+  for (; occr != NULL; occr = occr->next)
+    if (BLOCK_FOR_INSN (occr->insn)->index == bb->index)
+      return occr;
+  return NULL;
+}
 
-  ae_kill = (sbitmap *) sbitmap_vector_alloc (last_basic_block, num_stores);
-  sbitmap_vector_zero (ae_kill, last_basic_block);
+/* Perform partial GCSE pass after reload, try to eliminate redundant loads
+   created by the reload pass. We try to look for a full or partial
+   redundant loads fed by one or more loads/stores in predecessor BBs,
+   and try adding loads to make them fully redundant. We also check if
+   it's worth adding loads to be able to delete the redundant load.
 
-  transp = (sbitmap *) sbitmap_vector_alloc (last_basic_block, num_stores);
-  sbitmap_vector_zero (transp, last_basic_block);
+   Algorithm:
+   1. Build available expressions hash table:
+       For each load/store instruction, if the loaded/stored memory didn't
+       change until the end of the basic block add this memory expression to
+       the hash table.
+   2. Perform Redundancy elimination:
+      For each load instruction do the following:
+        perform partial redundancy elimination, check if it's worth adding
+        loads to make the load fully redundant. If so add loads and
+        register copies and delete the load.
 
-  for (ptr = first_ls_expr (); ptr != NULL; ptr = next_ls_expr (ptr))
-    FOR_EACH_BB (b)
-      {
-       if (store_killed_after (ptr->pattern, b->head, b))
-         {
-           /* The anticipatable expression is not killed if it's gen'd.  */
-           /*
-             We leave this check out for now. If we have a code sequence
-             in a block which looks like:
-                       ST MEMa = x
-                       L     y = MEMa
-                       ST MEMa = z
-             We should flag this as having an ANTIC expression, NOT
-             transparent, NOT killed, and AVAIL.
-             Unfortunately, since we haven't re-written all loads to
-             use the reaching reg, we'll end up doing an incorrect
-             Load in the middle here if we push the store down. It happens in
-                   gcc.c-torture/execute/960311-1.c with -O3
-             If we always kill it in this case, we'll sometimes do
-             unnecessary work, but it shouldn't actually hurt anything.
-           if (!TEST_BIT (ae_gen[b], ptr->index)).  */
-           SET_BIT (ae_kill[b->index], ptr->index);
-         }
-       else
-         SET_BIT (transp[b->index], ptr->index);
-      }
+   Future enhancement:
+     if loaded register is used/defined between load and some store,
+     look for some other free register between load and all its stores,
+     and replace load with a copy from this register to the loaded
+     register.  */
 
-  /* Any block with no exits calls some non-returning function, so
-     we better mark the store killed here, or we might not store to
-     it at all.  If we knew it was abort, we wouldn't have to store,
-     but we don't know that for sure.  */
-  if (gcse_file)
-    {
-      fprintf (gcse_file, "ST_avail and ST_antic (shown under loads..)\n");
-      print_ldst_list (gcse_file);
-      dump_sbitmap_vector (gcse_file, "st_antloc", "", st_antloc, last_basic_block);
-      dump_sbitmap_vector (gcse_file, "st_kill", "", ae_kill, last_basic_block);
-      dump_sbitmap_vector (gcse_file, "Transpt", "", transp, last_basic_block);
-      dump_sbitmap_vector (gcse_file, "st_avloc", "", ae_gen, last_basic_block);
-    }
-}
 
-/* Insert an instruction at the beginning of a basic block, and update
-   the BLOCK_HEAD if needed.  */
+/* This handles the case where several stores feed a partially redundant
+   load. It checks if the redundancy elimination is possible and if it's
+   worth it.  */
 
 static void
-insert_insn_start_bb (insn, bb)
-     rtx insn;
-     basic_block bb;
+eliminate_partially_redundant_loads (basic_block bb, rtx insn,
+                                    struct expr *expr)
 {
-  /* Insert at start of successor block.  */
-  rtx prev = PREV_INSN (bb->head);
-  rtx before = bb->head;
-  while (before != 0)
-    {
-      if (GET_CODE (before) != CODE_LABEL
-         && (GET_CODE (before) != NOTE
-             || NOTE_LINE_NUMBER (before) != NOTE_INSN_BASIC_BLOCK))
-       break;
-      prev = before;
-      if (prev == bb->end)
-       break;
-      before = NEXT_INSN (before);
-    }
-
-  insn = emit_insn_after (insn, prev);
+  edge pred;
+  rtx avail_insn = NULL_RTX;
+  rtx avail_reg;
+  rtx dest, pat;
+  struct occr *a_occr;
+  struct unoccr *occr, *avail_occrs = NULL;
+  struct unoccr *unoccr, *unavail_occrs = NULL;
+  int npred_ok = 0;
+  gcov_type ok_count = 0; /* Redundant load execution count.  */
+  gcov_type critical_count = 0; /* Execution count of critical edges.  */
+
+  /* The execution count of the loads to be added to make the
+     load fully redundant.  */
+  gcov_type not_ok_count = 0;
+  basic_block pred_bb;
+
+  pat = PATTERN (insn);
+  dest = SET_DEST (pat);
+  /* Check that the loaded register is not used, set, or killed from the
+     beginning of the block.  */
+  if (reg_used_between_after_reload_p (dest,
+                                       PREV_INSN (BB_HEAD (bb)), insn)
+      || reg_set_between_after_reload_p (dest,
+                                         PREV_INSN (BB_HEAD (bb)), insn))
+    return;
 
-  if (gcse_file)
+  /* Check potential for replacing load with copy for predecessors.  */
+  for (pred = bb->pred; pred; pred = pred->pred_next)
     {
-      fprintf (gcse_file, "STORE_MOTION  insert store at start of BB %d:\n",
-              bb->index);
-      print_inline_rtx (gcse_file, insn, 6);
-      fprintf (gcse_file, "\n");
-    }
-}
+      rtx next_pred_bb_end;
 
-/* This routine will insert a store on an edge. EXPR is the ldst entry for
-   the memory reference, and E is the edge to insert it on.  Returns nonzero
-   if an edge insertion was performed.  */
+      avail_insn = NULL_RTX;
+      pred_bb = pred->src;
+      next_pred_bb_end = NEXT_INSN (BB_END (pred_bb));
+      for (a_occr = get_bb_avail_insn (pred_bb, expr->avail_occr); a_occr;
+          a_occr = get_bb_avail_insn (pred_bb, a_occr->next))
+       {
+         /* Check if the loaded register is not used.  */
+         avail_insn = a_occr->insn;
+         if (! (avail_reg = get_avail_load_store_reg (avail_insn)))
+           abort ();
+         /* Make sure we can generate a move from register avail_reg to
+            dest.  */
+         extract_insn (gen_move_insn (copy_rtx (dest),
+                                      copy_rtx (avail_reg)));
+         if (! constrain_operands (1)
+             || reg_killed_on_edge (avail_reg, pred)
+             || reg_used_on_edge (dest, pred))
+           {
+             avail_insn = NULL;
+             continue;
+           }
+         if (! reg_set_between_after_reload_p (avail_reg, avail_insn,
+                                               next_pred_bb_end))
+           /* AVAIL_INSN remains non-null.  */
+           break;
+         else
+           avail_insn = NULL;
+       }
+      if (avail_insn != NULL_RTX)
+       {
+         npred_ok++;
+         ok_count += pred->count;
+          if (EDGE_CRITICAL_P (pred))
+            critical_count += pred->count;
+         occr = gmalloc (sizeof (struct unoccr));
+         occr->insn = avail_insn;
+         occr->pred = pred;
+         occr->next = avail_occrs;
+         avail_occrs = occr;
+       }
+      else
+       {
+         not_ok_count += pred->count;
+          if (EDGE_CRITICAL_P (pred))
+            critical_count += pred->count;
+         unoccr = gmalloc (sizeof (struct unoccr));
+         unoccr->insn = NULL_RTX;
+         unoccr->pred = pred;
+         unoccr->next = unavail_occrs;
+         unavail_occrs = unoccr;
+       }
+    }
 
-static int
-insert_store (expr, e)
-     struct ls_expr * expr;
-     edge e;
-{
-  rtx reg, insn;
-  basic_block bb;
-  edge tmp;
+  if (npred_ok == 0    /* No load can be replaced by copy.  */
+      || (optimize_size && npred_ok > 1)) /* Prevent exploding the code.  */
+    goto cleanup;
 
-  /* We did all the deleted before this insert, so if we didn't delete a
-     store, then we haven't set the reaching reg yet either.  */
-  if (expr->reaching_reg == NULL_RTX)
-    return 0;
+  /* Check if it's worth applying the partial redundancy elimination.  */
+  if (ok_count < GCSE_AFTER_RELOAD_PARTIAL_FRACTION * not_ok_count)
+    goto cleanup;
 
-  reg = expr->reaching_reg;
-  insn = gen_move_insn (expr->pattern, reg);
+  if (ok_count < GCSE_AFTER_RELOAD_CRITICAL_FRACTION * critical_count)
+    goto cleanup;
 
-  /* If we are inserting this expression on ALL predecessor edges of a BB,
-     insert it at the start of the BB, and reset the insert bits on the other
-     edges so we don't try to insert it on the other edges.  */
-  bb = e->dest;
-  for (tmp = e->dest->pred; tmp ; tmp = tmp->pred_next)
+  /* Generate moves to the loaded register from where
+     the memory is available.  */
+  for (occr = avail_occrs; occr; occr = occr->next)
     {
-      int index = EDGE_INDEX (edge_list, tmp->src, tmp->dest);
-      if (index == EDGE_INDEX_NO_EDGE)
+      avail_insn = occr->insn;
+      pred = occr->pred;
+      /* Set avail_reg to be the register having the value of the
+        memory.  */
+      avail_reg = get_avail_load_store_reg (avail_insn);
+      if (! avail_reg)
        abort ();
-      if (! TEST_BIT (pre_insert_map[index], expr->index))
-       break;
-    }
 
-  /* If tmp is NULL, we found an insertion on every edge, blank the
-     insertion vector for these edges, and insert at the start of the BB.  */
-  if (!tmp && bb != EXIT_BLOCK_PTR)
-    {
-      for (tmp = e->dest->pred; tmp ; tmp = tmp->pred_next)
-       {
-         int index = EDGE_INDEX (edge_list, tmp->src, tmp->dest);
-         RESET_BIT (pre_insert_map[index], expr->index);
-       }
-      insert_insn_start_bb (insn, bb);
-      return 0;
+      insert_insn_on_edge (gen_move_insn (copy_rtx (dest),
+                                         copy_rtx (avail_reg)),
+                          pred);
+
+      if (gcse_file)
+       fprintf (gcse_file,
+                "GCSE AFTER reload generating move from %d to %d on \
+                edge from %d to %d\n",
+                REGNO (avail_reg),
+                REGNO (dest),
+                pred->src->index,
+                pred->dest->index);
     }
 
-  /* We can't insert on this edge, so we'll insert at the head of the
-     successors block.  See Morgan, sec 10.5.  */
-  if ((e->flags & EDGE_ABNORMAL) == EDGE_ABNORMAL)
+  /* Regenerate loads where the memory is unavailable.  */
+  for (unoccr = unavail_occrs; unoccr; unoccr = unoccr->next)
     {
-      insert_insn_start_bb (insn, bb);
-      return 0;
+      pred = unoccr->pred;
+      insert_insn_on_edge (copy_insn (PATTERN (insn)), pred);
+
+      if (gcse_file)
+       fprintf (gcse_file,
+                "GCSE AFTER reload: generating on edge from %d to %d\
+                 a copy of load:\n",
+                pred->src->index,
+                pred->dest->index);
     }
 
-  insert_insn_on_edge (insn, e);
+  /* Delete the insn if it is not available in this block and mark it
+     for deletion if it is available. If insn is available it may help
+     discover additional redundancies, so mark it for later deletion.*/
+  for (a_occr = get_bb_avail_insn (bb, expr->avail_occr);
+       a_occr && (a_occr->insn != insn);
+       a_occr = get_bb_avail_insn (bb, a_occr->next));
 
-  if (gcse_file)
+  if (!a_occr)
+    delete_insn (insn);
+  else
+    a_occr->deleted_p = 1;
+
+cleanup:
+
+  while (unavail_occrs)
     {
-      fprintf (gcse_file, "STORE_MOTION  insert insn on edge (%d, %d):\n",
-              e->src->index, e->dest->index);
-      print_inline_rtx (gcse_file, insn, 6);
-      fprintf (gcse_file, "\n");
+      struct unoccr *temp = unavail_occrs->next;
+      free (unavail_occrs);
+      unavail_occrs = temp;
     }
 
-  return 1;
+  while (avail_occrs)
+    {
+      struct unoccr *temp = avail_occrs->next;
+      free (avail_occrs);
+      avail_occrs = temp;
+    }
 }
 
-/* This routine will replace a store with a SET to a specified register.  */
+/* Performing the redundancy elimination as described before.  */
 
 static void
-replace_store_insn (reg, del, bb)
-     rtx reg, del;
-     basic_block bb;
+gcse_after_reload (void)
 {
+  unsigned int i;
   rtx insn;
+  basic_block bb;
+  struct expr *expr;
+  struct occr *occr;
 
-  insn = gen_move_insn (reg, SET_SRC (PATTERN (del)));
-  insn = emit_insn_after (insn, del);
+  /* Note we start at block 1.  */
 
-  if (gcse_file)
-    {
-      fprintf (gcse_file,
-              "STORE_MOTION  delete insn in BB %d:\n      ", bb->index);
-      print_inline_rtx (gcse_file, del, 6);
-      fprintf (gcse_file, "\nSTORE MOTION  replaced with insn:\n      ");
-      print_inline_rtx (gcse_file, insn, 6);
-      fprintf (gcse_file, "\n");
-    }
+  if (ENTRY_BLOCK_PTR->next_bb == EXIT_BLOCK_PTR)
+    return;
 
-  delete_insn (del);
-}
+  FOR_BB_BETWEEN (bb,
+                 ENTRY_BLOCK_PTR->next_bb->next_bb,
+                 EXIT_BLOCK_PTR,
+                 next_bb)
+    {
+      if (! bb_has_well_behaved_predecessors (bb))
+       continue;
 
+      /* Do not try this optimization on cold basic blocks.  */
+      if (probably_cold_bb_p (bb))
+       continue;
 
-/* Delete a store, but copy the value that would have been stored into
-   the reaching_reg for later storing.  */
+      reset_opr_set_tables ();
 
-static void
-delete_store (expr, bb)
-     struct ls_expr * expr;
-     basic_block bb;
-{
-  rtx reg, i, del;
+      for (insn = BB_HEAD (bb);
+          insn != NULL
+          && insn != NEXT_INSN (BB_END (bb));
+          insn = NEXT_INSN (insn))
+       {
+         /* Is it a load - of the form (set (reg) (mem))?  */
+         if (GET_CODE (insn) == INSN
+              && GET_CODE (PATTERN (insn)) == SET
+             && REG_P (SET_DEST (PATTERN (insn)))
+             && MEM_P (SET_SRC (PATTERN (insn))))
+           {
+             rtx pat = PATTERN (insn);
+             rtx src = SET_SRC (pat);
+             struct expr *expr;
 
-  if (expr->reaching_reg == NULL_RTX)
-    expr->reaching_reg = gen_reg_rtx (GET_MODE (expr->pattern));
+             if (general_operand (src, GET_MODE (src))
+                 /* Is the expression recorded?  */
+                 && (expr = lookup_expr (src, &expr_hash_table)) != NULL
+                 /* Are the operands unchanged since the start of the
+                    block?  */
+                 && oprs_not_set_p (src, insn)
+                 && ! MEM_VOLATILE_P (src)
+                 && GET_MODE (src) != BLKmode
+                 && !(flag_non_call_exceptions && may_trap_p (src))
+                 && !side_effects_p (src))
+               {
+                 /* We now have a load (insn) and an available memory at
+                    its BB start (expr). Try to remove the loads if it is
+                    redundant.  */
+                 eliminate_partially_redundant_loads (bb, insn, expr);
+               }
+           }
 
+           /* Keep track of everything modified by this insn.  */
+           if (INSN_P (insn))
+             mark_oprs_set (insn);
+       }
+    }
 
-  /* If there is more than 1 store, the earlier ones will be dead,
-     but it doesn't hurt to replace them here.  */
-  reg = expr->reaching_reg;
+  commit_edge_insertions ();
 
-  for (i = AVAIL_STORE_LIST (expr); i; i = XEXP (i, 1))
+  /* Go over the expression hash table and delete insns that were
+     marked for later deletion.  */
+  for (i = 0; i < expr_hash_table.size; i++)
     {
-      del = XEXP (i, 0);
-      if (BLOCK_FOR_INSN (del) == bb)
-       {
-         /* We know there is only one since we deleted redundant
-            ones during the available computation.  */
-         replace_store_insn (reg, del, bb);
-         break;
-       }
+      for (expr = expr_hash_table.table[i];
+          expr != NULL;
+          expr = expr->next_same_hash)
+       for (occr = expr->avail_occr; occr; occr = occr->next)
+         if (occr->deleted_p)
+           delete_insn (occr->insn);
     }
 }
 
-/* Free memory used by store motion.  */
+/* Scan pattern PAT of INSN and add an entry to the hash TABLE.
+   After reload we are interested in loads/stores only.  */
 
 static void
-free_store_memory ()
+hash_scan_set_after_reload (rtx pat, rtx insn, struct hash_table *table)
 {
-  free_ldst_mems ();
+  rtx src = SET_SRC (pat);
+  rtx dest = SET_DEST (pat);
 
-  if (ae_gen)
-    sbitmap_vector_free (ae_gen);
-  if (ae_kill)
-    sbitmap_vector_free (ae_kill);
-  if (transp)
-    sbitmap_vector_free (transp);
-  if (st_antloc)
-    sbitmap_vector_free (st_antloc);
-  if (pre_insert_map)
-    sbitmap_vector_free (pre_insert_map);
-  if (pre_delete_map)
-    sbitmap_vector_free (pre_delete_map);
-  if (reg_set_in_block)
-    sbitmap_vector_free (reg_set_in_block);
+  if (! MEM_P (src) && ! MEM_P (dest))
+    return;
 
-  ae_gen = ae_kill = transp = st_antloc = NULL;
-  pre_insert_map = pre_delete_map = reg_set_in_block = NULL;
+  if (REG_P (dest))
+    {
+      if (/* Don't GCSE something if we can't do a reg/reg copy.  */
+         can_copy_p (GET_MODE (dest))
+         /* GCSE commonly inserts instruction after the insn.  We can't
+            do that easily for EH_REGION notes so disable GCSE on these
+            for now.  */
+         && ! find_reg_note (insn, REG_EH_REGION, NULL_RTX)
+         /* Is SET_SRC something we want to gcse?  */
+         && general_operand (src, GET_MODE (src))
+         /* Don't CSE a nop.  */
+         && ! set_noop_p (pat)
+         && ! JUMP_P (insn))
+       {
+         /* An expression is not available if its operands are
+            subsequently modified, including this insn.  */
+         if (oprs_available_p (src, insn))
+           insert_expr_in_table (src, GET_MODE (dest), insn, 0, 1, table);
+       }
+    }
+  else if (REG_P (src))
+    {
+      /* Only record sets of pseudo-regs in the hash table.  */
+      if (/* Don't GCSE something if we can't do a reg/reg copy.  */
+         can_copy_p (GET_MODE (src))
+         /* GCSE commonly inserts instruction after the insn.  We can't
+            do that easily for EH_REGION notes so disable GCSE on these
+            for now.  */
+         && ! find_reg_note (insn, REG_EH_REGION, NULL_RTX)
+         /* Is SET_DEST something we want to gcse?  */
+         && general_operand (dest, GET_MODE (dest))
+         /* Don't CSE a nop.  */
+         && ! set_noop_p (pat)
+         &&! JUMP_P (insn)
+         && ! (flag_float_store && FLOAT_MODE_P (GET_MODE (dest)))
+         /* Check if the memory expression is killed after insn.  */
+         && ! load_killed_in_block_p (BLOCK_FOR_INSN (insn),
+                                      INSN_CUID (insn) + 1,
+                                      dest,
+                                      1)
+         && oprs_unchanged_p (XEXP (dest, 0), insn, 1))
+       {
+         insert_expr_in_table (dest, GET_MODE (dest), insn, 0, 1, table);
+       }
+    }
 }
 
-/* Perform store motion. Much like gcse, except we move expressions the
-   other way by looking at the flowgraph in reverse.  */
+
+/* Create hash table of memory expressions available at end of basic
+   blocks.  */
 
 static void
-store_motion ()
+compute_hash_table_after_reload (struct hash_table *table)
 {
-  basic_block bb;
-  int x;
-  struct ls_expr * ptr;
-  int update_flow = 0;
+  unsigned int i;
 
-  if (gcse_file)
-    {
-      fprintf (gcse_file, "before store motion\n");
-      print_rtl (gcse_file, get_insns ());
-    }
+  table->set_p = 0;
 
+  /* Initialize count of number of entries in hash table.  */
+  table->n_elems = 0;
+  memset ((char *) table->table, 0,
+         table->size * sizeof (struct expr *));
 
-  init_alias_analysis ();
+  /* While we compute the hash table we also compute a bit array of which
+     registers are set in which blocks.  */
+  sbitmap_vector_zero (reg_set_in_block, last_basic_block);
 
-  /* Find all the stores that are live to the end of their block.  */
-  num_stores = compute_store_table ();
-  if (num_stores == 0)
-    {
-      sbitmap_vector_free (reg_set_in_block);
-      end_alias_analysis ();
-      return;
-    }
+  /* Re-cache any INSN_LIST nodes we have allocated.  */
+  clear_modify_mem_tables ();
 
-  /* Now compute whats actually available to move.  */
-  add_noreturn_fake_exit_edges ();
-  build_store_vectors ();
+  /* Some working arrays used to track first and last set in each block.  */
+  reg_avail_info = gmalloc (max_gcse_regno * sizeof (struct reg_avail_info));
 
-  edge_list = pre_edge_rev_lcm (gcse_file, num_stores, transp, ae_gen,
-                               st_antloc, ae_kill, &pre_insert_map,
-                               &pre_delete_map);
+  for (i = 0; i < max_gcse_regno; ++i)
+    reg_avail_info[i].last_bb = NULL;
 
-  /* Now we want to insert the new stores which are going to be needed.  */
-  for (ptr = first_ls_expr (); ptr != NULL; ptr = next_ls_expr (ptr))
+  FOR_EACH_BB (current_bb)
     {
-      FOR_EACH_BB (bb)
-       if (TEST_BIT (pre_delete_map[bb->index], ptr->index))
-         delete_store (ptr, bb);
-
-      for (x = 0; x < NUM_EDGES (edge_list); x++)
-       if (TEST_BIT (pre_insert_map[x], ptr->index))
-         update_flow |= insert_store (ptr, INDEX_EDGE (edge_list, x));
-    }
+      rtx insn;
+      unsigned int regno;
 
-  if (update_flow)
-    commit_edge_insertions ();
+      /* First pass over the instructions records information used to
+        determine when registers and memory are first and last set.  */
+      for (insn = BB_HEAD (current_bb);
+          insn && insn != NEXT_INSN (BB_END (current_bb));
+          insn = NEXT_INSN (insn))
+       {
+         if (! INSN_P (insn))
+           continue;
 
-  free_store_memory ();
-  free_edge_list (edge_list);
-  remove_fake_edges ();
-  end_alias_analysis ();
-}
+         if (CALL_P (insn))
+           {
+             bool clobbers_all = false;
 
-\f
-/* Entry point for jump bypassing optimization pass.  */
+#ifdef NON_SAVING_SETJMP
+             if (NON_SAVING_SETJMP
+                 && find_reg_note (insn, REG_SETJMP, NULL_RTX))
+               clobbers_all = true;
+#endif
 
-int
-bypass_jumps (file)
-     FILE *file;
-{
-  int changed;
+             for (regno = 0; regno < FIRST_PSEUDO_REGISTER; regno++)
+               if (clobbers_all
+                   || TEST_HARD_REG_BIT (regs_invalidated_by_call,
+                                         regno))
+                 record_last_reg_set_info (insn, regno);
 
-  /* We do not construct an accurate cfg in functions which call
-     setjmp, so just punt to be safe.  */
-  if (current_function_calls_setjmp)
-    return 0;
+             mark_call (insn);
+           }
 
-  /* For calling dump_foo fns from gdb.  */
-  debug_stderr = stderr;
-  gcse_file = file;
+           note_stores (PATTERN (insn), record_last_set_info, insn);
 
-  /* Identify the basic block information for this function, including
-     successors and predecessors.  */
-  max_gcse_regno = max_reg_num ();
+           if (GET_CODE (PATTERN (insn)) == SET)
+             {
+               rtx src, dest;
 
-  if (file)
-    dump_flow_info (file);
+               src = SET_SRC (PATTERN (insn));
+               dest = SET_DEST (PATTERN (insn));
+               if (MEM_P (src) && auto_inc_p (XEXP (src, 0)))
+                 {
+                   regno = REGNO (XEXP (XEXP (src, 0), 0));
+                   record_last_reg_set_info (insn, regno);
+                 }
+               if (MEM_P (dest) && auto_inc_p (XEXP (dest, 0)))
+                 {
+                   regno = REGNO (XEXP (XEXP (dest, 0), 0));
+                   record_last_reg_set_info (insn, regno);
+                 }
+               }
+         }
 
-  /* Return if there's nothing to do.  */
-  if (n_basic_blocks <= 1)
-    return 0;
+       /* The next pass builds the hash table.  */
+       for (insn = BB_HEAD (current_bb);
+            insn && insn != NEXT_INSN (BB_END (current_bb));
+            insn = NEXT_INSN (insn))
+         if (INSN_P (insn) && GET_CODE (PATTERN (insn)) == SET)
+           if (! find_reg_note (insn, REG_LIBCALL, NULL_RTX))
+             hash_scan_set_after_reload (PATTERN (insn), insn, table);
+    }
 
-  /* Trying to perform global optimizations on flow graphs which have
-     a high connectivity will take a long time and is unlikely to be
-     particularly useful.
+  free (reg_avail_info);
+  reg_avail_info = NULL;
+}
 
-     In normal circumstances a cfg should have about twice as many edges
-     as blocks.  But we do not want to punish small functions which have
-     a couple switch statements.  So we require a relatively large number
-     of basic blocks and the ratio of edges to blocks to be high.  */
-  if (n_basic_blocks > 1000 && n_edges / n_basic_blocks >= 20)
-    {
-      if (warn_disabled_optimization)
-        warning ("BYPASS disabled: %d > 1000 basic blocks and %d >= 20 edges/basic block",
-                 n_basic_blocks, n_edges / n_basic_blocks);
-      return 0;
-    }
 
-  /* If allocating memory for the cprop bitmap would take up too much
-     storage it's better just to disable the optimization.  */
-  if ((n_basic_blocks
-       * SBITMAP_SET_SIZE (max_gcse_regno)
-       * sizeof (SBITMAP_ELT_TYPE)) > MAX_GCSE_MEMORY)
-    {
-      if (warn_disabled_optimization)
-        warning ("GCSE disabled: %d basic blocks and %d registers",
-                 n_basic_blocks, max_gcse_regno);
+/* Main entry point of the GCSE after reload - clean some redundant loads
+   due to spilling.  */
 
-      return 0;
-    }
+void
+gcse_after_reload_main (rtx f, FILE* file)
+{
+  gcse_subst_count = 0;
+  gcse_create_count = 0;
 
-  /* See what modes support reg/reg copy operations.  */
-  if (! can_copy_init_p)
-    {
-      compute_can_copy ();
-      can_copy_init_p = 1;
-    }
+  gcse_file = file;
 
   gcc_obstack_init (&gcse_obstack);
   bytes_used = 0;
@@ -7642,38 +7596,28 @@ bypass_jumps (file)
   /* We need alias.  */
   init_alias_analysis ();
 
-  /* Record where pseudo-registers are set.  This data is kept accurate
-     during each pass.  ??? We could also record hard-reg information here
-     [since it's unchanging], however it is currently done during hash table
-     computation.
-
-     It may be tempting to compute MEM set information here too, but MEM sets
-     will be subject to code motion one day and thus we need to compute
-     information about memory sets when we build the hash tables.  */
+  max_gcse_regno = max_reg_num ();
 
   alloc_reg_set_mem (max_gcse_regno);
-  compute_sets (get_insns ());
+  alloc_gcse_mem (f);
+  alloc_hash_table (max_cuid, &expr_hash_table, 0);
+  compute_hash_table_after_reload (&expr_hash_table);
 
-  max_gcse_regno = max_reg_num ();
-  alloc_gcse_mem (get_insns ());
-  changed = one_cprop_pass (1, 1, 1);
-  free_gcse_mem ();
+  if (gcse_file)
+    dump_hash_table (gcse_file, "Expression", &expr_hash_table);
 
-  if (file)
-    {
-      fprintf (file, "BYPASS of %s: %d basic blocks, ",
-              current_function_name, n_basic_blocks);
-      fprintf (file, "%d bytes\n\n", bytes_used);
-    }
+  if (expr_hash_table.n_elems > 0)
+    gcse_after_reload ();
 
-  obstack_free (&gcse_obstack, NULL);
+  free_hash_table (&expr_hash_table);
+
+  free_gcse_mem ();
   free_reg_set_mem ();
 
   /* We are finished with alias.  */
   end_alias_analysis ();
-  allocate_reg_info (max_reg_num (), FALSE, FALSE);
 
-  return changed;
+  obstack_free (&gcse_obstack, NULL);
 }
 
 #include "gt-gcse.h"