OSDN Git Service

* obj-c++.dg/stubify-1.mm: Only run on powerpc.
[pf3gnuchains/gcc-fork.git] / gcc / postreload-gcse.c
index 93d81c4..76b7b8c 100644 (file)
@@ -1,5 +1,5 @@
 /* Post reload partially redundant load elimination
-   Copyright (C) 2004
+   Copyright (C) 2004, 2005
    Free Software Foundation, Inc.
 
 This file is part of GCC.
@@ -16,8 +16,8 @@ for more details.
 
 You should have received a copy of the GNU General Public License
 along with GCC; see the file COPYING.  If not, write to the Free
-Software Foundation, 59 Temple Place - Suite 330, Boston, MA
-02111-1307, USA.  */
+Software Foundation, 51 Franklin Street, Fifth Floor, Boston, MA
+02110-1301, USA.  */
 
 #include "config.h"
 #include "system.h"
@@ -43,6 +43,9 @@ Software Foundation, 59 Temple Place - Suite 330, Boston, MA
 #include "obstack.h"
 #include "hashtab.h"
 #include "params.h"
+#include "target.h"
+#include "timevar.h"
+#include "tree-pass.h"
 
 /* The following code implements gcse after reload, the purpose of this
    pass is to cleanup redundant loads generated by reload and other
@@ -135,7 +138,13 @@ static struct obstack unoccr_obstack;
 
 /* Array where each element is the CUID if the insn that last set the hard
    register with the number of the element, since the start of the current
-   basic block.  */
+   basic block.
+
+   This array is used during the building of the hash table (step 1) to
+   determine if a reg is killed before the end of a basic block.
+
+   It is also used when eliminating partial redundancies (step 2) to see
+   if a reg was modified since the start of a basic block.  */
 static int *reg_avail_info;
 
 /* A list of insns that may modify memory within the current basic block.  */
@@ -169,10 +178,7 @@ static bool oprs_unchanged_p (rtx, rtx, bool);
 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 mark_call (rtx);
-static void mark_set (rtx, rtx);
-static void mark_clobber (rtx, rtx);
-static void mark_oprs_set (rtx);
+static void record_opr_changes (rtx);
 
 static void find_mem_conflicts (rtx, rtx, void *);
 static int load_killed_in_block_p (int, rtx, bool);
@@ -218,8 +224,8 @@ alloc_mem (void)
   rtx insn;
 
   /* Find the largest UID and create a mapping from UIDs to CUIDs.  */
-  uid_cuid = xcalloc (get_max_uid () + 1, sizeof (int));
-  i = 0;
+  uid_cuid = XCNEWVEC (int, get_max_uid () + 1);
+  i = 1;
   FOR_EACH_BB (bb)
     FOR_BB_INSNS (bb, insn)
       {
@@ -296,7 +302,7 @@ hash_expr_for_htab (const void *expp)
   return exp->hash;
 }
 
-/* Callbach for hashtab.
+/* Callback for hashtab.
    Return nonzero if exp1 is equivalent to exp2.  */
 
 static int
@@ -305,9 +311,8 @@ expr_equiv_p (const void *exp1p, const void *exp2p)
   struct expr *exp1 = (struct expr *) exp1p;
   struct expr *exp2 = (struct expr *) exp2p;
   int equiv_p = exp_equiv_p (exp1->expr, exp2->expr, 0, true);
-  if (equiv_p
-      && exp1->hash != exp2->hash)
-    abort ();
+  
+  gcc_assert (!equiv_p || exp1->hash == exp2->hash);
   return equiv_p;
 }
 \f
@@ -436,7 +441,7 @@ dump_hash_table_entry (void **slot, void *filep)
   fprintf (file, "expr: ");
   print_rtl (file, expr->expr);
   fprintf (file,"\nhashcode: %u\n", expr->hash);
-  fprintf (file,"list of occurences:\n");
+  fprintf (file,"list of occurrences:\n");
   occr = expr->avail_occr;
   while (occr)
     {
@@ -466,10 +471,10 @@ dump_hash_table (FILE *file)
 }
 \f
 
-/* Return nonzero if the operands of expression X are unchanged from the
-   start of INSN's basic block up to but not including INSN if AFTER_INSN
-   is false, or from INSN to the end of INSN's basic block if AFTER_INSN
-   is true.  */
+/* Return nonzero if the operands of expression X are unchanged
+   1) from the start of INSN's basic block up to but not including INSN
+      if AFTER_INSN is false, or
+   2) from INSN to the end of INSN's basic block if AFTER_INSN is true.  */
 
 static bool
 oprs_unchanged_p (rtx x, rtx insn, bool after_insn)
@@ -485,11 +490,8 @@ oprs_unchanged_p (rtx x, rtx insn, bool after_insn)
   switch (code)
     {
     case REG:
-#ifdef ENABLE_CHECKING
       /* We are called after register allocation.  */
-      if (REGNO (x) >= FIRST_PSEUDO_REGISTER)
-       abort ();
-#endif
+      gcc_assert (REGNO (x) < FIRST_PSEUDO_REGISTER);
       if (after_insn)
        /* If the last CUID setting the insn is less than the CUID of
           INSN, then reg X is not changed in or after INSN.  */
@@ -567,7 +569,6 @@ find_mem_conflicts (rtx dest, rtx setter ATTRIBUTE_UNUSED,
 
   while (GET_CODE (dest) == SUBREG
         || GET_CODE (dest) == ZERO_EXTRACT
-        || GET_CODE (dest) == SIGN_EXTRACT
         || GET_CODE (dest) == STRICT_LOW_PART)
     dest = XEXP (dest, 0);
 
@@ -584,8 +585,12 @@ find_mem_conflicts (rtx dest, rtx setter ATTRIBUTE_UNUSED,
 \f
 
 /* Return nonzero if the expression in X (a memory reference) is killed
-   in block BB before if (AFTER_INSN is false) or after (if AFTER_INSN
-   is true) the insn with the CUID in UID_LIMIT.  */
+   in the current basic block before (if AFTER_INSN is false) or after
+   (if AFTER_INSN is true) the insn with the CUID in UID_LIMIT.
+
+   This function assumes that the modifies_mem table is flushed when
+   the hash table construction or redundancy elimination phases start
+   processing a new basic block.  */
 
 static int
 load_killed_in_block_p (int uid_limit, rtx x, bool after_insn)
@@ -629,7 +634,7 @@ load_killed_in_block_p (int uid_limit, rtx x, bool after_insn)
 
 /* Record register first/last/block set information for REGNO in INSN.  */
 
-static void
+static inline void
 record_last_reg_set_info (rtx insn, int regno)
 {
   reg_avail_info[regno] = INSN_CUID (insn);
@@ -666,12 +671,20 @@ record_last_set_info (rtx dest, rtx setter ATTRIBUTE_UNUSED, void *data)
 
   if (REG_P (dest))
     record_last_reg_set_info (last_set_insn, REGNO (dest));
-  else if (MEM_P (dest)
-          /* Ignore pushes, they clobber nothing.  */
-          && ! push_operand (dest, GET_MODE (dest)))
-    record_last_mem_set_info (last_set_insn);
+  else if (MEM_P (dest))
+    {
+      /* Ignore pushes, they don't clobber memory.  They may still
+        clobber the stack pointer though.  Some targets do argument
+        pushes without adding REG_INC notes.  See e.g. PR25196,
+        where a pushsi2 on i386 doesn't have REG_INC notes.  Note
+        such changes here too.  */
+      if (! push_operand (dest, GET_MODE (dest)))
+       record_last_mem_set_info (last_set_insn);
+      else
+       record_last_reg_set_info (last_set_insn, STACK_POINTER_REGNUM);
+    }
 }
-\f
+
 
 /* Reset tables used to keep track of what's still available since the
    start of the block.  */
@@ -683,85 +696,36 @@ reset_opr_set_tables (void)
   obstack_free (&modifies_mem_obstack, modifies_mem_obstack_bottom);
   modifies_mem_list = NULL;
 }
-
-/* Mark things set by a CALL.  */
-
-static void
-mark_call (rtx insn)
-{
-  if (! CONST_OR_PURE_CALL_P (insn))
-    record_last_mem_set_info (insn);
-}
-
-/* Mark things set by a SET.  */
-
-static void
-mark_set (rtx pat, rtx insn)
-{
-  rtx dest = SET_DEST (pat);
-
-  while (GET_CODE (dest) == SUBREG
-        || GET_CODE (dest) == ZERO_EXTRACT
-        || GET_CODE (dest) == SIGN_EXTRACT
-        || GET_CODE (dest) == STRICT_LOW_PART)
-    dest = XEXP (dest, 0);
-
-  if (REG_P (dest))
-    record_last_reg_set_info (insn, REGNO (dest));
-  else if (MEM_P (dest))
-    record_last_mem_set_info (insn);
-
-  if (GET_CODE (SET_SRC (pat)) == CALL)
-    mark_call (insn);
-}
-
-/* Record things set by a CLOBBER.  */
-
-static void
-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 (REG_P (clob))
-    record_last_reg_set_info (insn, REGNO (clob));
-  else
-    record_last_mem_set_info (insn);
-}
+\f
 
 /* Record things set by INSN.
    This data is used by oprs_unchanged_p.  */
 
 static void
-mark_oprs_set (rtx insn)
+record_opr_changes (rtx insn)
 {
-  rtx pat = PATTERN (insn);
-  int i;
+  rtx note;
 
-  if (GET_CODE (pat) == SET)
-    mark_set (pat, insn);
+  /* Find all stores and record them.  */
+  note_stores (PATTERN (insn), record_last_set_info, insn);
 
-  else if (GET_CODE (pat) == PARALLEL)
-    for (i = 0; i < XVECLEN (pat, 0); i++)
-      {
-       rtx x = XVECEXP (pat, 0, i);
-
-       if (GET_CODE (x) == SET)
-         mark_set (x, insn);
-       else if (GET_CODE (x) == CLOBBER)
-         mark_clobber (x, insn);
-       else if (GET_CODE (x) == CALL)
-         mark_call (insn);
-      }
+  /* Also record autoincremented REGs for this insn as changed.  */
+  for (note = REG_NOTES (insn); note; note = XEXP (note, 1))
+    if (REG_NOTE_KIND (note) == REG_INC)
+      record_last_reg_set_info (insn, REGNO (XEXP (note, 0)));
+
+  /* Finally, if this is a call, record all call clobbers.  */
+  if (CALL_P (insn))
+    {
+      unsigned int regno;
 
-  else if (GET_CODE (pat) == CLOBBER)
-    mark_clobber (pat, insn);
+      for (regno = 0; regno < FIRST_PSEUDO_REGISTER; regno++)
+       if (TEST_HARD_REG_BIT (regs_invalidated_by_call, regno))
+         record_last_reg_set_info (insn, regno);
 
-  else if (GET_CODE (pat) == CALL)
-    mark_call (insn);
+      if (! CONST_OR_PURE_CALL_P (insn))
+       record_last_mem_set_info (insn);
+    }
 }
 \f
 
@@ -783,18 +747,18 @@ hash_scan_set (rtx insn)
   if (JUMP_P (insn) || set_noop_p (pat))
     return;
 
-#ifdef ENABLE_CHEKCING
-  /* We shouldn't have any EH_REGION notes post reload.  */
-  if (find_reg_note (insn, REG_EH_REGION, NULL_RTX))
-    abort ();
-#endif
-
   if (REG_P (dest))
     {
-      if (/* Don't GCSE something if we can't do a reg/reg copy.  */
+      if (/* Don't CSE something if we can't do a reg/reg copy.  */
          can_copy_p (GET_MODE (dest))
          /* Is SET_SRC something we want to gcse?  */
          && general_operand (src, GET_MODE (src))
+#ifdef STACK_REGS
+         /* Never consider insns touching the register stack.  It may
+            create situations that reg-stack cannot handle (e.g. a stack
+            register live across an abnormal edge).  */
+         && (REGNO (dest) < FIRST_STACK_REG || REGNO (dest) > LAST_STACK_REG)
+#endif
          /* An expression is not available if its operands are
             subsequently modified, including this insn.  */
          && oprs_unchanged_p (src, insn, true))
@@ -805,10 +769,14 @@ hash_scan_set (rtx insn)
   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.  */
+      if (/* Don't CSE something if we can't do a reg/reg copy.  */
          can_copy_p (GET_MODE (src))
          /* Is SET_DEST something we want to gcse?  */
          && general_operand (dest, GET_MODE (dest))
+#ifdef STACK_REGS
+         /* As above for STACK_REGS.  */
+         && (REGNO (src) < FIRST_STACK_REG || REGNO (src) > LAST_STACK_REG)
+#endif
          && ! (flag_float_store && FLOAT_MODE_P (GET_MODE (dest)))
          /* Check if the memory expression is killed after insn.  */
          && ! load_killed_in_block_p (INSN_CUID (insn) + 1, dest, true)
@@ -819,8 +787,12 @@ hash_scan_set (rtx insn)
     }
 }
 \f
+
 /* Create hash table of memory expressions available at end of basic
-   blocks.  */
+   blocks.  Basically you should think of this hash table as the
+   representation of AVAIL_OUT.  This is the set of expressions that
+   is generated in a basic block and not killed before the end of the
+   same basic block.  Notice that this is really a local computation.  */
 
 static void
 compute_hash_table (void)
@@ -830,59 +802,20 @@ compute_hash_table (void)
   FOR_EACH_BB (bb)
     {
       rtx insn;
-      unsigned int regno;
-
-      reset_opr_set_tables ();
 
       /* First pass over the instructions records information used to
-        determine when registers and memory are first and last set.  */
+        determine when registers and memory are last set.
+        Since we compute a "local" AVAIL_OUT, reset the tables that
+        help us keep track of what has been modified since the start
+        of the block.  */
+      reset_opr_set_tables ();
       FOR_BB_INSNS (bb, insn)
        {
-         if (! INSN_P (insn))
-           continue;
-
-         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
-
-             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);
-
-             if (! CONST_OR_PURE_CALL_P (insn))
-               record_last_mem_set_info (insn);
-           }
-
-         note_stores (PATTERN (insn), record_last_set_info, insn);
-
-         if (GET_CODE (PATTERN (insn)) == SET)
-           {
-             rtx src, dest;
-
-             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);
-               }
-            }
-         }
+         if (INSN_P (insn))
+            record_opr_changes (insn);
+       }
 
-      /* The next pass builds the hash table.  */
+      /* The next pass actually builds the hash table.  */
       FOR_BB_INSNS (bb, insn)
        if (INSN_P (insn) && GET_CODE (PATTERN (insn)) == SET)
          hash_scan_set (insn);
@@ -932,33 +865,28 @@ static rtx
 reg_set_between_after_reload_p (rtx reg, rtx from_insn, rtx to_insn)
 {
   rtx insn;
-  int regno;
 
-#ifdef ENABLE_CHECKING
   /* We are called after register allocation.  */
-  if (!REG_P (reg) || REGNO (reg) >= FIRST_PSEUDO_REGISTER)
-    abort ();
-#endif
+  gcc_assert (REG_P (reg) && REGNO (reg) < FIRST_PSEUDO_REGISTER);
 
   if (from_insn == to_insn)
     return NULL_RTX;
 
-  regno = REGNO (reg);
   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;
-    }
+    if (INSN_P (insn))
+      {
+       if (set_of (reg, insn) != NULL_RTX)
+         return insn;
+       if ((CALL_P (insn)
+             && call_used_regs[REGNO (reg)])
+           || find_reg_fusage (insn, CLOBBER, reg))
+         return insn;
+
+       if (FIND_REG_INC_NOTE (insn, reg))
+         return insn;
+      }
 
   return NULL_RTX;
 }
@@ -971,28 +899,28 @@ static rtx
 reg_used_between_after_reload_p (rtx reg, rtx from_insn, rtx to_insn)
 {
   rtx insn;
-  int regno;
 
-#ifdef ENABLE_CHECKING
   /* We are called after register allocation.  */
-  if (!REG_P (reg) || REGNO (reg) >= FIRST_PSEUDO_REGISTER)
-    abort ();
-#endif
+  gcc_assert (REG_P (reg) && REGNO (reg) < FIRST_PSEUDO_REGISTER);
 
   if (from_insn == to_insn)
     return NULL_RTX;
 
-  regno = REGNO (reg);
   for (insn = NEXT_INSN (from_insn);
        insn != to_insn;
        insn = NEXT_INSN (insn))
-    if (INSN_P (insn)
-       && (reg_overlap_mentioned_p (reg, PATTERN (insn))
+    if (INSN_P (insn))
+      {
+       if (reg_overlap_mentioned_p (reg, PATTERN (insn))
            || (CALL_P (insn)
-               && call_used_regs[regno])
+               && call_used_regs[REGNO (reg)])
            || find_reg_fusage (insn, USE, reg)
-           || find_reg_fusage (insn, CLOBBER, reg)))
-      return insn;
+           || find_reg_fusage (insn, CLOBBER, reg))
+         return insn;
+
+       if (FIND_REG_INC_NOTE (insn, reg))
+         return insn;
+      }
 
   return NULL_RTX;
 }
@@ -1023,11 +951,15 @@ reg_set_or_used_since_bb_start (rtx reg, basic_block bb, rtx up_to_insn)
 static rtx
 get_avail_load_store_reg (rtx insn)
 {
-  if (REG_P (SET_DEST (PATTERN (insn))))  /* A load.  */
+  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 ();
+  else
+    {
+      /* A store.  */
+      gcc_assert (REG_P (SET_SRC (PATTERN (insn))));
+      return SET_SRC (PATTERN (insn));
+    }
 }
 
 /* Return nonzero if the predecessors of BB are "well behaved".  */
@@ -1036,11 +968,12 @@ static bool
 bb_has_well_behaved_predecessors (basic_block bb)
 {
   edge pred;
+  edge_iterator ei;
 
-  if (! bb->pred)
+  if (EDGE_COUNT (bb->preds) == 0)
     return false;
 
-  for (pred = bb->pred; pred != NULL; pred = pred->pred_next)
+  FOR_EACH_EDGE (pred, ei, bb->preds)
     {
       if ((pred->flags & EDGE_ABNORMAL) && EDGE_CRITICAL_P (pred))
        return false;
@@ -1066,7 +999,16 @@ get_bb_avail_insn (basic_block bb, struct occr *occr)
 
 /* 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.  */
+   worth it.
+
+   Redundancy elimination is possible if,
+   1) None of the operands of an insn have been modified since the start
+      of the current basic block.
+   2) In any predecessor of the current basic block, the same expression
+      is generated.
+
+   See the function body for the heuristics that determine if eliminating
+   a redundancy is also worth doing, assuming it is possible.  */
 
 static void
 eliminate_partially_redundant_load (basic_block bb, rtx insn,
@@ -1082,6 +1024,8 @@ eliminate_partially_redundant_load (basic_block bb, rtx insn,
   int npred_ok = 0;
   gcov_type ok_count = 0; /* Redundant load execution count.  */
   gcov_type critical_count = 0; /* Execution count of critical edges.  */
+  edge_iterator ei;
+  bool critical_edge_split = false;
 
   /* The execution count of the loads to be added to make the
      load fully redundant.  */
@@ -1097,11 +1041,12 @@ eliminate_partially_redundant_load (basic_block bb, rtx insn,
     return;
 
   /* Check potential for replacing load with copy for predecessors.  */
-  for (pred = bb->pred; pred; pred = pred->pred_next)
+  FOR_EACH_EDGE (pred, ei, bb->preds)
     {
       rtx next_pred_bb_end;
 
       avail_insn = NULL_RTX;
+      avail_reg = 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;
@@ -1109,8 +1054,9 @@ eliminate_partially_redundant_load (basic_block bb, rtx insn,
        {
          /* Check if the loaded register is not used.  */
          avail_insn = a_occr->insn;
-         if (! (avail_reg = get_avail_load_store_reg (avail_insn)))
-           abort ();
+         avail_reg = get_avail_load_store_reg (avail_insn);
+         gcc_assert (avail_reg);
+         
          /* Make sure we can generate a move from register avail_reg to
             dest.  */
          extract_insn (gen_move_insn (copy_rtx (dest),
@@ -1137,8 +1083,17 @@ eliminate_partially_redundant_load (basic_block bb, rtx insn,
        {
          npred_ok++;
          ok_count += pred->count;
+         if (! set_noop_p (PATTERN (gen_move_insn (copy_rtx (dest),
+                                                   copy_rtx (avail_reg)))))
+           {
+             /* Check if there is going to be a split.  */
+             if (EDGE_CRITICAL_P (pred))
+               critical_edge_split = true;
+           }
+         else /* Its a dead move no need to generate.  */
+           continue;
          occr = (struct unoccr *) obstack_alloc (&unoccr_obstack,
-                                                 sizeof (struct occr));
+                                                 sizeof (struct unoccr));
          occr->insn = avail_insn;
          occr->pred = pred;
          occr->next = avail_occrs;
@@ -1148,6 +1103,9 @@ eliminate_partially_redundant_load (basic_block bb, rtx insn,
        }
       else
        {
+         /* Adding a load on a critical edge will cause a split.  */
+         if (EDGE_CRITICAL_P (pred))
+           critical_edge_split = true;
          not_ok_count += pred->count;
          unoccr = (struct unoccr *) obstack_alloc (&unoccr_obstack,
                                                    sizeof (struct unoccr));
@@ -1163,7 +1121,12 @@ eliminate_partially_redundant_load (basic_block bb, rtx insn,
   if (/* No load can be replaced by copy.  */
       npred_ok == 0
       /* Prevent exploding the code.  */ 
-      || (optimize_size && npred_ok > 1))
+      || (optimize_size && npred_ok > 1)
+      /* If we don't have profile information we cannot tell if splitting 
+         a critical edge is profitable or not so don't do it.  */
+      || ((! profile_info || ! flag_branch_probabilities
+          || targetm.cannot_modify_jumps_p ())
+         && critical_edge_split))
     goto cleanup;
 
   /* Check if it's worth applying the partial redundancy elimination.  */
@@ -1181,8 +1144,7 @@ eliminate_partially_redundant_load (basic_block bb, rtx insn,
       /* 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 ();
+      gcc_assert (avail_reg);
 
       insert_insn_on_edge (gen_move_insn (copy_rtx (dest),
                                          copy_rtx (avail_reg)),
@@ -1224,7 +1186,17 @@ eliminate_partially_redundant_load (basic_block bb, rtx insn,
        a_occr = get_bb_avail_insn (bb, a_occr->next));
 
   if (!a_occr)
-    delete_insn (insn);
+    {
+      stats.insns_deleted++;
+
+      if (dump_file)
+       {
+         fprintf (dump_file, "deleting insn:\n");
+          print_rtl_single (dump_file, insn);
+          fprintf (dump_file, "\n");
+       }
+      delete_insn (insn);
+    }
   else
     a_occr->deleted_p = 1;
 
@@ -1251,15 +1223,20 @@ eliminate_partially_redundant_loads (void)
                  EXIT_BLOCK_PTR,
                  next_bb)
     {
+      /* Don't try anything on basic blocks with strange predecessors.  */
       if (! bb_has_well_behaved_predecessors (bb))
        continue;
 
-      /* Do not try this optimization on cold basic blocks.  */
+      /* Do not try anything on cold basic blocks.  */
       if (probably_cold_bb_p (bb))
        continue;
 
+      /* Reset the table of things changed since the start of the current
+        basic block.  */
       reset_opr_set_tables ();
 
+      /* Look at all insns in the current basic block and see if there are
+        any loads in it that we can record.  */
       FOR_BB_INSNS (bb, insn)
        {
          /* Is it a load - of the form (set (reg) (mem))?  */
@@ -1290,9 +1267,11 @@ eliminate_partially_redundant_loads (void)
                }
            }
 
-         /* Keep track of everything modified by this insn.  */
+         /* Keep track of everything modified by this insn, so that we
+            know what has been modified since the start of the current
+            basic block.  */
          if (INSN_P (insn))
-           mark_oprs_set (insn);
+           record_opr_changes (insn);
        }
     }
 
@@ -1339,9 +1318,10 @@ delete_redundant_insns (void)
 /* Main entry point of the GCSE after reload - clean some redundant loads
    due to spilling.  */
 
-void
+static void
 gcse_after_reload_main (rtx f ATTRIBUTE_UNUSED)
 {
+
   memset (&stats, 0, sizeof (stats));
 
   /* Allocate ememory for this pass.
@@ -1377,3 +1357,38 @@ gcse_after_reload_main (rtx f ATTRIBUTE_UNUSED)
   free_mem ();
 }
 
+\f
+static bool
+gate_handle_gcse2 (void)
+{
+  return (optimize > 0 && flag_gcse_after_reload);
+}
+
+
+static unsigned int
+rest_of_handle_gcse2 (void)
+{
+  gcse_after_reload_main (get_insns ());
+  rebuild_jump_labels (get_insns ());
+  delete_trivially_dead_insns (get_insns (), max_reg_num ());
+  return 0;
+}
+
+struct tree_opt_pass pass_gcse2 =
+{
+  "gcse2",                              /* name */
+  gate_handle_gcse2,                    /* gate */
+  rest_of_handle_gcse2,                 /* execute */
+  NULL,                                 /* sub */
+  NULL,                                 /* next */
+  0,                                    /* static_pass_number */
+  TV_GCSE_AFTER_RELOAD,                 /* tv_id */
+  0,                                    /* properties_required */
+  0,                                    /* properties_provided */
+  0,                                    /* properties_destroyed */
+  0,                                    /* todo_flags_start */
+  TODO_dump_func |
+  TODO_verify_flow | TODO_ggc_collect,  /* todo_flags_finish */
+  'J'                                   /* letter */
+};
+