vec: add exact argument for various grow functions.
[gcc.git] / gcc / fwprop.c
index c59d307614f803195fa0367573e3a06cb7fb36cb..756c1d6b4055aa97028aec9efa9d14a96ee81fa3 100644 (file)
@@ -1,5 +1,5 @@
 /* RTL-based forward propagation pass for GNU compiler.
-   Copyright (C) 2005-2014 Free Software Foundation, Inc.
+   Copyright (C) 2005-2020 Free Software Foundation, Inc.
    Contributed by Paolo Bonzini and Steven Bosscher.
 
 This file is part of GCC.
@@ -21,35 +21,23 @@ along with GCC; see the file COPYING3.  If not see
 #include "config.h"
 #include "system.h"
 #include "coretypes.h"
-#include "tm.h"
-#include "diagnostic-core.h"
-
-#include "sparseset.h"
+#include "backend.h"
+#include "target.h"
 #include "rtl.h"
+#include "predict.h"
+#include "df.h"
+#include "memmodel.h"
 #include "tm_p.h"
 #include "insn-config.h"
+#include "emit-rtl.h"
 #include "recog.h"
-#include "flags.h"
-#include "obstack.h"
-#include "predict.h"
-#include "vec.h"
-#include "hashtab.h"
-#include "hash-set.h"
-#include "machmode.h"
-#include "hard-reg-set.h"
-#include "input.h"
-#include "function.h"
-#include "dominance.h"
-#include "cfg.h"
+
+#include "sparseset.h"
 #include "cfgrtl.h"
 #include "cfgcleanup.h"
-#include "basic-block.h"
-#include "df.h"
-#include "target.h"
 #include "cfgloop.h"
 #include "tree-pass.h"
 #include "domwalk.h"
-#include "emit-rtl.h"
 #include "rtl-iter.h"
 
 
@@ -132,6 +120,13 @@ static vec<df_ref> use_def_ref;
 static vec<df_ref> reg_defs;
 static vec<df_ref> reg_defs_stack;
 
+/* The maximum number of propagations that are still allowed.  If we do
+   more propagations than originally we had uses, we must have ended up
+   in a propagation loop, as in PR79405.  Until the algorithm fwprop
+   uses can obviously not get into such loops we need a workaround like
+   this.  */
+static int propagations_left;
+
 /* The MD bitmaps are trimmed to include only live registers to cut
    memory usage on testcases like insn-recog.c.  Track live registers
    in the basic block and do not perform forward propagation if the
@@ -221,16 +216,16 @@ class single_def_use_dom_walker : public dom_walker
 public:
   single_def_use_dom_walker (cdi_direction direction)
     : dom_walker (direction) {}
-  virtual void before_dom_children (basic_block);
+  virtual edge before_dom_children (basic_block);
   virtual void after_dom_children (basic_block);
 };
 
-void
+edge
 single_def_use_dom_walker::before_dom_children (basic_block bb)
 {
   int bb_index = bb->index;
-  struct df_md_bb_info *md_bb_info = df_md_get_bb_info (bb_index);
-  struct df_lr_bb_info *lr_bb_info = df_lr_get_bb_info (bb_index);
+  class df_md_bb_info *md_bb_info = df_md_get_bb_info (bb_index);
+  class df_lr_bb_info *lr_bb_info = df_lr_get_bb_info (bb_index);
   rtx_insn *insn;
 
   bitmap_copy (local_md, &md_bb_info->in);
@@ -258,6 +253,8 @@ single_def_use_dom_walker::before_dom_children (basic_block bb)
 
   process_uses (df_get_artificial_uses (bb_index), 0);
   process_defs (df_get_artificial_defs (bb_index), 0);
+
+  return NULL;
 }
 
 /* Pop the definitions created in this basic block when leaving its
@@ -295,10 +292,10 @@ build_single_def_use_links (void)
   df_maybe_reorganize_use_refs (DF_REF_ORDER_BY_INSN_WITH_NOTES);
 
   use_def_ref.create (DF_USES_TABLE_SIZE ());
-  use_def_ref.safe_grow_cleared (DF_USES_TABLE_SIZE ());
+  use_def_ref.safe_grow_cleared (DF_USES_TABLE_SIZE (), true);
 
   reg_defs.create (max_reg_num ());
-  reg_defs.safe_grow_cleared (max_reg_num ());
+  reg_defs.safe_grow_cleared (max_reg_num (), true);
 
   reg_defs_stack.create (n_basic_blocks_for_fn (cfun) * 10);
   local_md = BITMAP_ALLOC (NULL);
@@ -360,12 +357,12 @@ canonicalize_address (rtx x)
       {
       case ASHIFT:
         if (CONST_INT_P (XEXP (x, 1))
-            && INTVAL (XEXP (x, 1)) < GET_MODE_BITSIZE (GET_MODE (x))
-            && INTVAL (XEXP (x, 1)) >= 0)
+           && INTVAL (XEXP (x, 1)) < GET_MODE_UNIT_BITSIZE (GET_MODE (x))
+           && INTVAL (XEXP (x, 1)) >= 0)
          {
            HOST_WIDE_INT shift = INTVAL (XEXP (x, 1));
            PUT_CODE (x, MULT);
-           XEXP (x, 1) = gen_int_mode ((HOST_WIDE_INT) 1 << shift,
+           XEXP (x, 1) = gen_int_mode (HOST_WIDE_INT_1 << shift,
                                        GET_MODE (x));
          }
 
@@ -394,7 +391,7 @@ canonicalize_address (rtx x)
    for a memory access in the given MODE.  */
 
 static bool
-should_replace_address (rtx old_rtx, rtx new_rtx, enum machine_mode mode,
+should_replace_address (rtx old_rtx, rtx new_rtx, machine_mode mode,
                        addr_space_t as, bool speed)
 {
   int gain;
@@ -416,7 +413,8 @@ should_replace_address (rtx old_rtx, rtx new_rtx, enum machine_mode mode,
      eliminating the most insns without additional costs, and it
      is the same that cse.c used to do.  */
   if (gain == 0)
-    gain = set_src_cost (new_rtx, speed) - set_src_cost (old_rtx, speed);
+    gain = (set_src_cost (new_rtx, VOIDmode, speed)
+           - set_src_cost (old_rtx, VOIDmode, speed));
 
   return (gain > 0);
 }
@@ -450,6 +448,18 @@ enum {
   PR_OPTIMIZE_FOR_SPEED = 4
 };
 
+/* Check that X has a single def.  */
+
+static bool
+reg_single_def_p (rtx x)
+{
+  if (!REG_P (x))
+    return false;
+
+  int regno = REGNO (x);
+  return (DF_REG_DEF_COUNT (regno) == 1
+         && !bitmap_bit_p (DF_LR_OUT (ENTRY_BLOCK_PTR_FOR_FN (cfun)), regno));
+}
 
 /* Replace all occurrences of OLD in *PX with NEW and try to simplify the
    resulting expression.  Replace *PX with a new RTL expression if an
@@ -464,8 +474,8 @@ propagate_rtx_1 (rtx *px, rtx old_rtx, rtx new_rtx, int flags)
 {
   rtx x = *px, tem = NULL_RTX, op0, op1, op2;
   enum rtx_code code = GET_CODE (x);
-  enum machine_mode mode = GET_MODE (x);
-  enum machine_mode op_mode;
+  machine_mode mode = GET_MODE (x);
+  machine_mode op_mode;
   bool can_appear = (flags & PR_CAN_APPEAR) != 0;
   bool valid_ops = true;
 
@@ -549,6 +559,54 @@ propagate_rtx_1 (rtx *px, rtx old_rtx, rtx new_rtx, int flags)
          tem = simplify_gen_subreg (mode, op0, GET_MODE (SUBREG_REG (x)),
                                     SUBREG_BYTE (x));
        }
+
+      else
+       {
+         rtvec vec;
+         rtvec newvec;
+         const char *fmt = GET_RTX_FORMAT (code);
+         rtx op;
+
+         for (int i = 0; fmt[i]; i++)
+           switch (fmt[i])
+             {
+             case 'E':
+               vec = XVEC (x, i);
+               newvec = vec;
+               for (int j = 0; j < GET_NUM_ELEM (vec); j++)
+                 {
+                   op = RTVEC_ELT (vec, j);
+                   valid_ops &= propagate_rtx_1 (&op, old_rtx, new_rtx, flags);
+                   if (op != RTVEC_ELT (vec, j))
+                     {
+                       if (newvec == vec)
+                         {
+                           newvec = shallow_copy_rtvec (vec);
+                           if (!tem)
+                             tem = shallow_copy_rtx (x);
+                           XVEC (tem, i) = newvec;
+                         }
+                       RTVEC_ELT (newvec, j) = op;
+                     }
+                 }
+               break;
+
+             case 'e':
+               if (XEXP (x, i))
+                 {
+                   op = XEXP (x, i);
+                   valid_ops &= propagate_rtx_1 (&op, old_rtx, new_rtx, flags);
+                   if (op != XEXP (x, i))
+                     {
+                       if (!tem)
+                         tem = shallow_copy_rtx (x);
+                       XEXP (tem, i) = op;
+                     }
+                 }
+               break;
+             }
+       }
+
       break;
 
     case RTX_OBJ:
@@ -629,6 +687,15 @@ propagate_rtx_1 (rtx *px, rtx old_rtx, rtx new_rtx, int flags)
 
   *px = tem;
 
+  /* Allow replacements that simplify operations on a vector or complex
+     value to a component.  The most prominent case is
+     (subreg ([vec_]concat ...)).   */
+  if (REG_P (tem) && !HARD_REGISTER_P (tem)
+      && (VECTOR_MODE_P (GET_MODE (new_rtx))
+         || COMPLEX_MODE_P (GET_MODE (new_rtx)))
+      && GET_MODE (tem) == GET_MODE_INNER (GET_MODE (new_rtx)))
+    return true;
+
   /* The replacement we made so far is valid, if all of the recursive
      replacements were valid, or we could simplify everything to
      a constant.  */
@@ -658,7 +725,7 @@ varying_mem_p (const_rtx x)
    Otherwise, we accept simplifications that have a lower or equal cost.  */
 
 static rtx
-propagate_rtx (rtx x, enum machine_mode mode, rtx old_rtx, rtx new_rtx,
+propagate_rtx (rtx x, machine_mode mode, rtx old_rtx, rtx new_rtx,
               bool speed)
 {
   rtx tem;
@@ -673,8 +740,7 @@ propagate_rtx (rtx x, enum machine_mode mode, rtx old_rtx, rtx new_rtx,
       || CONSTANT_P (new_rtx)
       || (GET_CODE (new_rtx) == SUBREG
          && REG_P (SUBREG_REG (new_rtx))
-         && (GET_MODE_SIZE (mode)
-             <= GET_MODE_SIZE (GET_MODE (SUBREG_REG (new_rtx))))))
+         && !paradoxical_subreg_p (new_rtx)))
     flags |= PR_CAN_APPEAR;
   if (!varying_mem_p (new_rtx))
     flags |= PR_HANDLE_MEM;
@@ -725,14 +791,15 @@ local_ref_killed_between_p (df_ref ref, rtx_insn *from, rtx_insn *to)
 }
 
 
-/* Check if the given DEF is available in INSN.  This would require full
-   computation of available expressions; we check only restricted conditions:
-   - if DEF is the sole definition of its register, go ahead;
-   - in the same basic block, we check for no definitions killing the
-     definition of DEF_INSN;
-   - if USE's basic block has DEF's basic block as the sole predecessor,
-     we check if the definition is killed after DEF_INSN or before
+/* Check if USE is killed between DEF_INSN and TARGET_INSN.  This would
+   require full computation of available expressions; we check only a few
+   restricted conditions:
+   - if the reg in USE has only one definition, go ahead;
+   - in the same basic block, we check for no definitions killing the use;
+   - if TARGET_INSN's basic block has DEF_INSN's basic block as its sole
+     predecessor, we check if the use is killed after DEF_INSN or before
      TARGET_INSN insn, in their respective basic blocks.  */
+
 static bool
 use_killed_between (df_ref use, rtx_insn *def_insn, rtx_insn *target_insn)
 {
@@ -756,12 +823,17 @@ use_killed_between (df_ref use, rtx_insn *def_insn, rtx_insn *target_insn)
      know that this definition reaches use, or we wouldn't be here.
      However, this is invalid for hard registers because if they are
      live at the beginning of the function it does not mean that we
-     have an uninitialized access.  */
+     have an uninitialized access.  And we have to check for the case
+     where a register may be used uninitialized in a loop as above.  */
   regno = DF_REF_REGNO (use);
   def = DF_REG_DEF_CHAIN (regno);
   if (def
       && DF_REF_NEXT_REG (def) == NULL
-      && regno >= FIRST_PSEUDO_REGISTER)
+      && regno >= FIRST_PSEUDO_REGISTER
+      && (BLOCK_FOR_INSN (DF_REF_INSN (def)) == def_bb
+         ? DF_INSN_LUID (DF_REF_INSN (def)) < DF_INSN_LUID (def_insn)
+         : dominated_by_p (CDI_DOMINATORS,
+                           def_bb, BLOCK_FOR_INSN (DF_REF_INSN (def)))))
     return false;
 
   /* Check locally if we are in the same basic block.  */
@@ -853,9 +925,7 @@ all_uses_available_at (rtx_insn *def_insn, rtx_insn *target_insn)
 
 \f
 static df_ref *active_defs;
-#ifdef ENABLE_CHECKING
 static sparseset active_defs_check;
-#endif
 
 /* Fill the ACTIVE_DEFS array with the use->def link for the registers
    mentioned in USE_REC.  Register the valid entries in ACTIVE_DEFS_CHECK
@@ -869,9 +939,8 @@ register_active_defs (df_ref use)
       df_ref def = get_def_for_use (use);
       int regno = DF_REF_REGNO (use);
 
-#ifdef ENABLE_CHECKING
-      sparseset_set_bit (active_defs_check, regno);
-#endif
+      if (flag_checking)
+       sparseset_set_bit (active_defs_check, regno);
       active_defs[regno] = def;
     }
 }
@@ -886,9 +955,8 @@ register_active_defs (df_ref use)
 static void
 update_df_init (rtx_insn *def_insn, rtx_insn *insn)
 {
-#ifdef ENABLE_CHECKING
-  sparseset_clear (active_defs_check);
-#endif
+  if (flag_checking)
+    sparseset_clear (active_defs_check);
   register_active_defs (DF_INSN_USES (def_insn));
   register_active_defs (DF_INSN_USES (insn));
   register_active_defs (DF_INSN_EQ_USES (insn));
@@ -907,11 +975,10 @@ update_uses (df_ref use)
 
       /* Set up the use-def chain.  */
       if (DF_REF_ID (use) >= (int) use_def_ref.length ())
-        use_def_ref.safe_grow_cleared (DF_REF_ID (use) + 1);
+       use_def_ref.safe_grow_cleared (DF_REF_ID (use) + 1, true);
 
-#ifdef ENABLE_CHECKING
-      gcc_assert (sparseset_bit_p (active_defs_check, regno));
-#endif
+      if (flag_checking)
+       gcc_assert (sparseset_bit_p (active_defs_check, regno));
       use_def_ref[DF_REF_ID (use)] = active_defs[regno];
     }
 }
@@ -964,7 +1031,7 @@ try_fwprop_subst (df_ref use, rtx *loc, rtx new_rtx, rtx_insn *def_insn,
      multiple sets.  If so, assume the cost of the new instruction is
      not greater than the old one.  */
   if (set)
-    old_cost = set_src_cost (SET_SRC (set), speed);
+    old_cost = set_src_cost (SET_SRC (set), GET_MODE (SET_DEST (set)), speed);
   if (dump_file)
     {
       fprintf (dump_file, "\nIn insn %d, replacing\n ", INSN_UID (insn));
@@ -985,7 +1052,8 @@ try_fwprop_subst (df_ref use, rtx *loc, rtx new_rtx, rtx_insn *def_insn,
 
   else if (DF_REF_TYPE (use) == DF_REF_REG_USE
           && set
-          && set_src_cost (SET_SRC (set), speed) > old_cost)
+          && (set_src_cost (SET_SRC (set), GET_MODE (SET_DEST (set)), speed)
+              > old_cost))
     {
       if (dump_file)
        fprintf (dump_file, "Changes to insn %d not profitable\n",
@@ -1013,10 +1081,27 @@ try_fwprop_subst (df_ref use, rtx *loc, rtx new_rtx, rtx_insn *def_insn,
         making a new one if one does not already exist.  */
       if (set_reg_equal)
        {
-         if (dump_file)
-           fprintf (dump_file, " Setting REG_EQUAL note\n");
+         /* If there are any paradoxical SUBREGs, don't add REG_EQUAL note,
+            because the bits in there can be anything and so might not
+            match the REG_EQUAL note content.  See PR70574.  */
+         subrtx_var_iterator::array_type array;
+         FOR_EACH_SUBRTX_VAR (iter, array, *loc, NONCONST)
+           {
+             rtx x = *iter;
+             if (SUBREG_P (x) && paradoxical_subreg_p (x))
+               {
+                 set_reg_equal = false;
+                 break;
+               }
+           }
 
-         note = set_unique_reg_note (insn, REG_EQUAL, copy_rtx (new_rtx));
+         if (set_reg_equal)
+           {
+             if (dump_file)
+               fprintf (dump_file, " Setting REG_EQUAL note\n");
+
+             note = set_unique_reg_note (insn, REG_EQUAL, copy_rtx (new_rtx));
+           }
        }
     }
 
@@ -1038,9 +1123,7 @@ free_load_extend (rtx src, rtx_insn *insn)
   df_ref def, use;
 
   reg = XEXP (src, 0);
-#ifdef LOAD_EXTEND_OP
-  if (LOAD_EXTEND_OP (GET_MODE (reg)) != GET_CODE (src))
-#endif
+  if (load_extend_op (GET_MODE (reg)) != GET_CODE (src))
     return false;
 
   FOR_EACH_INSN_USE (use, insn)
@@ -1078,16 +1161,15 @@ forward_propagate_subreg (df_ref use, rtx_insn *def_insn, rtx def_set)
   rtx use_reg = DF_REF_REG (use);
   rtx_insn *use_insn;
   rtx src;
+  scalar_int_mode int_use_mode, src_mode;
 
   /* Only consider subregs... */
-  enum machine_mode use_mode = GET_MODE (use_reg);
+  machine_mode use_mode = GET_MODE (use_reg);
   if (GET_CODE (use_reg) != SUBREG
       || !REG_P (SET_DEST (def_set)))
     return false;
 
-  /* If this is a paradoxical SUBREG...  */
-  if (GET_MODE_SIZE (use_mode)
-      > GET_MODE_SIZE (GET_MODE (SUBREG_REG (use_reg))))
+  if (paradoxical_subreg_p (use_reg))
     {
       /* If this is a paradoxical SUBREG, we have no idea what value the
         extra bits would have.  However, if the operand is equivalent to
@@ -1121,17 +1203,19 @@ forward_propagate_subreg (df_ref use, rtx_insn *def_insn, rtx def_set)
      definition of Y or, failing that, allow A to be deleted after
      reload through register tying.  Introducing more uses of Y
      prevents both optimisations.  */
-  else if (subreg_lowpart_p (use_reg))
+  else if (is_a <scalar_int_mode> (use_mode, &int_use_mode)
+          && subreg_lowpart_p (use_reg))
     {
       use_insn = DF_REF_INSN (use);
       src = SET_SRC (def_set);
       if ((GET_CODE (src) == ZERO_EXTEND
           || GET_CODE (src) == SIGN_EXTEND)
+         && is_a <scalar_int_mode> (GET_MODE (src), &src_mode)
          && REG_P (XEXP (src, 0))
          && REGNO (XEXP (src, 0)) >= FIRST_PSEUDO_REGISTER
          && GET_MODE (XEXP (src, 0)) == use_mode
          && !free_load_extend (src, def_insn)
-         && (targetm.mode_rep_extended (use_mode, GET_MODE (src))
+         && (targetm.mode_rep_extended (int_use_mode, src_mode)
              != (int) GET_CODE (src))
          && all_uses_available_at (def_insn, use_insn))
        return try_fwprop_subst (use, DF_REF_LOC (use), XEXP (src, 0),
@@ -1228,7 +1312,7 @@ forward_propagate_and_simplify (df_ref use, rtx_insn *def_insn, rtx def_set)
   rtx use_set = single_set (use_insn);
   rtx src, reg, new_rtx, *loc;
   bool set_reg_equal;
-  enum machine_mode mode;
+  machine_mode mode;
   int asm_use = -1;
 
   if (INSN_CODE (use_insn) < 0)
@@ -1245,7 +1329,7 @@ forward_propagate_and_simplify (df_ref use, rtx_insn *def_insn, rtx def_set)
   reg = DF_REF_REG (use);
   if (GET_CODE (reg) == SUBREG && GET_CODE (SET_DEST (def_set)) == SUBREG)
     {
-      if (SUBREG_BYTE (SET_DEST (def_set)) != SUBREG_BYTE (reg))
+      if (maybe_ne (SUBREG_BYTE (SET_DEST (def_set)), SUBREG_BYTE (reg)))
        return false;
     }
   /* Check if the def had a subreg, but the use has the whole reg.  */
@@ -1314,14 +1398,19 @@ forward_propagate_and_simplify (df_ref use, rtx_insn *def_insn, rtx def_set)
         that isn't mentioned in USE_SET, as the note would be invalid
         otherwise.  We also don't want to install a note if we are merely
         propagating a pseudo since verifying that this pseudo isn't dead
-        is a pain; moreover such a note won't help anything.  */
+        is a pain; moreover such a note won't help anything.
+        If the use is a paradoxical subreg, make sure we don't add a
+        REG_EQUAL note for it, because it is not equivalent, it is one
+        possible value for it, but we can't rely on it holding that value.
+        See PR70574.  */
       set_reg_equal = (note == NULL_RTX
                       && REG_P (SET_DEST (use_set))
                       && !REG_P (src)
                       && !(GET_CODE (src) == SUBREG
                            && REG_P (SUBREG_REG (src)))
                       && !reg_mentioned_p (SET_DEST (use_set),
-                                           SET_SRC (use_set)));
+                                           SET_SRC (use_set))
+                      && !paradoxical_subreg_p (DF_REF_REG (use)));
     }
 
   if (GET_MODE (*loc) == VOIDmode)
@@ -1341,10 +1430,11 @@ forward_propagate_and_simplify (df_ref use, rtx_insn *def_insn, rtx def_set)
 
 /* Given a use USE of an insn, if it has a single reaching
    definition, try to forward propagate it into that insn.
-   Return true if cfg cleanup will be needed.  */
+   Return true if cfg cleanup will be needed.
+   REG_PROP_ONLY is true if we should only propagate register copies.  */
 
 static bool
-forward_propagate_into (df_ref use)
+forward_propagate_into (df_ref use, bool reg_prop_only = false)
 {
   df_ref def;
   rtx_insn *def_insn, *use_insn;
@@ -1365,10 +1455,6 @@ forward_propagate_into (df_ref use)
   if (DF_REF_IS_ARTIFICIAL (def))
     return false;
 
-  /* Do not propagate loop invariant definitions inside the loop.  */
-  if (DF_REF_BB (def)->loop_father != DF_REF_BB (use)->loop_father)
-    return false;
-
   /* Check if the use is still present in the insn!  */
   use_insn = DF_REF_INSN (use);
   if (DF_REF_FLAGS (use) & DF_REF_IN_NOTE)
@@ -1386,11 +1472,26 @@ forward_propagate_into (df_ref use)
   if (!def_set)
     return false;
 
+  if (reg_prop_only
+      && (!reg_single_def_p (SET_SRC (def_set))
+         || !reg_single_def_p (SET_DEST (def_set))))
+    return false;
+
+  /* Allow propagations into a loop only for reg-to-reg copies, since
+     replacing one register by another shouldn't increase the cost.  */
+
+  if (DF_REF_BB (def)->loop_father != DF_REF_BB (use)->loop_father
+      && (!reg_single_def_p (SET_SRC (def_set))
+         || !reg_single_def_p (SET_DEST (def_set))))
+    return false;
+
   /* Only try one kind of propagation.  If two are possible, we'll
      do it on the following iterations.  */
   if (forward_propagate_and_simplify (use, def_insn, def_set)
       || forward_propagate_subreg (use, def_insn, def_set))
     {
+      propagations_left--;
+
       if (cfun->can_throw_non_call_exceptions
          && find_reg_note (use_insn, REG_EH_REGION, NULL_RTX)
          && purge_dead_edges (DF_REF_BB (use)))
@@ -1416,9 +1517,10 @@ fwprop_init (void)
   df_set_flags (DF_DEFER_INSN_RESCAN);
 
   active_defs = XNEWVEC (df_ref, max_reg_num ());
-#ifdef ENABLE_CHECKING
-  active_defs_check = sparseset_alloc (max_reg_num ());
-#endif
+  if (flag_checking)
+    active_defs_check = sparseset_alloc (max_reg_num ());
+
+  propagations_left = DF_USES_TABLE_SIZE ();
 }
 
 static void
@@ -1428,9 +1530,8 @@ fwprop_done (void)
 
   use_def_ref.release ();
   free (active_defs);
-#ifdef ENABLE_CHECKING
-  sparseset_free (active_defs_check);
-#endif
+  if (flag_checking)
+    sparseset_free (active_defs_check);
 
   free_dominance_info (CDI_DOMINATORS);
   cleanup_cfg (0);
@@ -1452,10 +1553,9 @@ gate_fwprop (void)
 }
 
 static unsigned int
-fwprop (void)
+fwprop (bool fwprop_addr_p)
 {
   unsigned i;
-  bool need_cleanup = false;
 
   fwprop_init ();
 
@@ -1467,18 +1567,24 @@ fwprop (void)
 
   for (i = 0; i < DF_USES_TABLE_SIZE (); i++)
     {
+      if (!propagations_left)
+       break;
+
       df_ref use = DF_USES_GET (i);
       if (use)
-       if (DF_REF_TYPE (use) == DF_REF_REG_USE
-           || DF_REF_BB (use)->loop_father == NULL
-           /* The outer most loop is not really a loop.  */
-           || loop_outer (DF_REF_BB (use)->loop_father) == NULL)
-         need_cleanup |= forward_propagate_into (use);
+       {
+         if (DF_REF_TYPE (use) == DF_REF_REG_USE
+             || DF_REF_BB (use)->loop_father == NULL
+             /* The outer most loop is not really a loop.  */
+             || loop_outer (DF_REF_BB (use)->loop_father) == NULL)
+           forward_propagate_into (use, fwprop_addr_p);
+
+         else if (fwprop_addr_p)
+           forward_propagate_into (use, false);
+       }
     }
 
   fwprop_done ();
-  if (need_cleanup)
-    cleanup_cfg (0);
   return 0;
 }
 
@@ -1506,7 +1612,7 @@ public:
 
   /* opt_pass methods: */
   virtual bool gate (function *) { return gate_fwprop (); }
-  virtual unsigned int execute (function *) { return fwprop (); }
+  virtual unsigned int execute (function *) { return fwprop (false); }
 
 }; // class pass_rtl_fwprop
 
@@ -1518,34 +1624,6 @@ make_pass_rtl_fwprop (gcc::context *ctxt)
   return new pass_rtl_fwprop (ctxt);
 }
 
-static unsigned int
-fwprop_addr (void)
-{
-  unsigned i;
-  bool need_cleanup = false;
-
-  fwprop_init ();
-
-  /* Go through all the uses.  df_uses_create will create new ones at the
-     end, and we'll go through them as well.  */
-  for (i = 0; i < DF_USES_TABLE_SIZE (); i++)
-    {
-      df_ref use = DF_USES_GET (i);
-      if (use)
-       if (DF_REF_TYPE (use) != DF_REF_REG_USE
-           && DF_REF_BB (use)->loop_father != NULL
-           /* The outer most loop is not really a loop.  */
-           && loop_outer (DF_REF_BB (use)->loop_father) != NULL)
-         need_cleanup |= forward_propagate_into (use);
-    }
-
-  fwprop_done ();
-
-  if (need_cleanup)
-    cleanup_cfg (0);
-  return 0;
-}
-
 namespace {
 
 const pass_data pass_data_rtl_fwprop_addr =
@@ -1570,7 +1648,7 @@ public:
 
   /* opt_pass methods: */
   virtual bool gate (function *) { return gate_fwprop (); }
-  virtual unsigned int execute (function *) { return fwprop_addr (); }
+  virtual unsigned int execute (function *) { return fwprop (true); }
 
 }; // class pass_rtl_fwprop_addr