OSDN Git Service

* config/i386/i386.md (*cmpfp_<mode>): Enable for optimize_size.
[pf3gnuchains/gcc-fork.git] / gcc / tree-ssa-propagate.c
index 35b845b..b037180 100644 (file)
@@ -1,12 +1,12 @@
 /* Generic SSA value propagation engine.
-   Copyright (C) 2004, 2005 Free Software Foundation, Inc.
+   Copyright (C) 2004, 2005, 2006, 2007 Free Software Foundation, Inc.
    Contributed by Diego Novillo <dnovillo@redhat.com>
 
    This file is part of GCC.
 
    GCC is free software; you can redistribute it and/or modify it
    under the terms of the GNU General Public License as published by the
-   Free Software Foundation; either version 2, or (at your option) any
+   Free Software Foundation; either version 3, or (at your option) any
    later version.
 
    GCC is distributed in the hope that it will be useful, but WITHOUT
@@ -15,9 +15,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.  */
+   along with GCC; see the file COPYING3.  If not see
+   <http://www.gnu.org/licenses/>.  */
 
 #include "config.h"
 #include "system.h"
@@ -30,7 +29,6 @@
 #include "ggc.h"
 #include "basic-block.h"
 #include "output.h"
-#include "errors.h"
 #include "expr.h"
 #include "function.h"
 #include "diagnostic.h"
@@ -42,6 +40,7 @@
 #include "langhooks.h"
 #include "varray.h"
 #include "vec.h"
+#include "value-prof.h"
 
 /* This file implements a generic value propagation engine based on
    the same propagation used by the SSA-CCP algorithm [1].
 static ssa_prop_visit_stmt_fn ssa_prop_visit_stmt;
 static ssa_prop_visit_phi_fn ssa_prop_visit_phi;
 
-/* Use the TREE_DEPRECATED bitflag to mark statements that have been
+/* Use the deprecated flag to mark statements that have been
    added to one of the SSA edges worklists.  This flag is used to
    avoid visiting statements unnecessarily when draining an SSA edge
    worklist.  If while simulating a basic block, we find a statement with
    STMT_IN_SSA_EDGE_WORKLIST set, we clear it to prevent SSA edge
    processing from visiting it again.  */
-#define STMT_IN_SSA_EDGE_WORKLIST(T)   TREE_DEPRECATED (T)
+#define STMT_IN_SSA_EDGE_WORKLIST(T) ((T)->base.deprecated_flag)
 
 /* A bitmap to keep track of executable blocks in the CFG.  */
 static sbitmap executable_blocks;
 
 /* Array of control flow edges on the worklist.  */
-static GTY(()) varray_type cfg_blocks = NULL;
+static VEC(basic_block,heap) *cfg_blocks;
 
 static unsigned int cfg_blocks_num = 0;
 static int cfg_blocks_tail;
@@ -177,6 +176,8 @@ cfg_blocks_empty_p (void)
 static void 
 cfg_blocks_add (basic_block bb)
 {
+  bool head = false;
+
   gcc_assert (bb != ENTRY_BLOCK_PTR && bb != EXIT_BLOCK_PTR);
   gcc_assert (!TEST_BIT (bb_in_list, bb->index));
 
@@ -188,19 +189,37 @@ cfg_blocks_add (basic_block bb)
   else
     {
       cfg_blocks_num++;
-      if (cfg_blocks_num > VARRAY_SIZE (cfg_blocks))
+      if (cfg_blocks_num > VEC_length (basic_block, cfg_blocks))
        {
-         /* We have to grow the array now.  Adjust to queue to occupy the
-            full space of the original array.  */
-         cfg_blocks_tail = VARRAY_SIZE (cfg_blocks);
+         /* We have to grow the array now.  Adjust to queue to occupy
+            the full space of the original array.  We do not need to
+            initialize the newly allocated portion of the array
+            because we keep track of CFG_BLOCKS_HEAD and
+            CFG_BLOCKS_HEAD.  */
+         cfg_blocks_tail = VEC_length (basic_block, cfg_blocks);
          cfg_blocks_head = 0;
-         VARRAY_GROW (cfg_blocks, 2 * VARRAY_SIZE (cfg_blocks));
+         VEC_safe_grow (basic_block, heap, cfg_blocks, 2 * cfg_blocks_tail);
        }
+      /* Minor optimization: we prefer to see blocks with more
+        predecessors later, because there is more of a chance that
+        the incoming edges will be executable.  */
+      else if (EDGE_COUNT (bb->preds)
+              >= EDGE_COUNT (VEC_index (basic_block, cfg_blocks,
+                                        cfg_blocks_head)->preds))
+       cfg_blocks_tail = ((cfg_blocks_tail + 1)
+                          % VEC_length (basic_block, cfg_blocks));
       else
-       cfg_blocks_tail = (cfg_blocks_tail + 1) % VARRAY_SIZE (cfg_blocks);
+       {
+         if (cfg_blocks_head == 0)
+           cfg_blocks_head = VEC_length (basic_block, cfg_blocks);
+         --cfg_blocks_head;
+         head = true;
+       }
     }
 
-  VARRAY_BB (cfg_blocks, cfg_blocks_tail) = bb;
+  VEC_replace (basic_block, cfg_blocks,
+              head ? cfg_blocks_head : cfg_blocks_tail,
+              bb);
   SET_BIT (bb_in_list, bb->index);
 }
 
@@ -212,12 +231,13 @@ cfg_blocks_get (void)
 {
   basic_block bb;
 
-  bb = VARRAY_BB (cfg_blocks, cfg_blocks_head);
+  bb = VEC_index (basic_block, cfg_blocks, cfg_blocks_head);
 
   gcc_assert (!cfg_blocks_empty_p ());
   gcc_assert (bb);
 
-  cfg_blocks_head = (cfg_blocks_head + 1) % VARRAY_SIZE (cfg_blocks);
+  cfg_blocks_head = ((cfg_blocks_head + 1)
+                    % VEC_length (basic_block, cfg_blocks));
   --cfg_blocks_num;
   RESET_BIT (bb_in_list, bb->index);
 
@@ -474,7 +494,8 @@ ssa_prop_init (void)
   if (dump_file && (dump_flags & TDF_DETAILS))
     dump_immediate_uses (dump_file);
 
-  VARRAY_BB_INIT (cfg_blocks, 20, "cfg_blocks");
+  cfg_blocks = VEC_alloc (basic_block, heap, 20);
+  VEC_safe_grow (basic_block, heap, cfg_blocks, 20);
 
   /* Initialize the values for every SSA_NAME.  */
   for (i = 1; i < num_ssa_names; i++)
@@ -508,6 +529,7 @@ ssa_prop_fini (void)
 {
   VEC_free (tree, gc, interesting_ssa_edges);
   VEC_free (tree, gc, varying_ssa_edges);
+  VEC_free (basic_block, heap, cfg_blocks);
   cfg_blocks = NULL;
   sbitmap_free (bb_in_list);
   sbitmap_free (executable_blocks);
@@ -525,12 +547,12 @@ get_rhs (tree stmt)
     {
     case RETURN_EXPR:
       stmt = TREE_OPERAND (stmt, 0);
-      if (!stmt || TREE_CODE (stmt) != MODIFY_EXPR)
+      if (!stmt || TREE_CODE (stmt) != GIMPLE_MODIFY_STMT)
        return stmt;
       /* FALLTHRU */
 
-    case MODIFY_EXPR:
-      stmt = TREE_OPERAND (stmt, 1);
+    case GIMPLE_MODIFY_STMT:
+      stmt = GENERIC_TREE_OPERAND (stmt, 1);
       if (TREE_CODE (stmt) == WITH_SIZE_EXPR)
        return TREE_OPERAND (stmt, 0);
       else
@@ -551,55 +573,153 @@ get_rhs (tree stmt)
 }
 
 
-/* Set the main expression of *STMT_P to EXPR.  If EXPR is not a valid
-   GIMPLE expression no changes are done and the function returns
-   false.  */
+/* Return true if EXPR is a valid GIMPLE expression.  */
 
 bool
-set_rhs (tree *stmt_p, tree expr)
+valid_gimple_expression_p (tree expr)
 {
-  tree stmt = *stmt_p, op;
   enum tree_code code = TREE_CODE (expr);
-  stmt_ann_t ann;
-  tree var;
-  ssa_op_iter iter;
 
-  /* Verify the constant folded result is valid gimple.  */
-  if (TREE_CODE_CLASS (code) == tcc_binary)
+  switch (TREE_CODE_CLASS (code))
     {
+    case tcc_declaration:
+      if (!is_gimple_variable (expr))
+       return false;
+      break;
+
+    case tcc_constant:
+      break;
+
+    case tcc_binary:
+    case tcc_comparison:
       if (!is_gimple_val (TREE_OPERAND (expr, 0))
          || !is_gimple_val (TREE_OPERAND (expr, 1)))
        return false;
-    }
-  else if (TREE_CODE_CLASS (code) == tcc_unary
-          || code == ADDR_EXPR)
-    {
+      break;
+
+    case tcc_unary:
       if (!is_gimple_val (TREE_OPERAND (expr, 0)))
        return false;
+      break;
+
+    case tcc_expression:
+      switch (code)
+       {
+       case ADDR_EXPR:
+         {
+           tree t = TREE_OPERAND (expr, 0);
+           while (handled_component_p (t))
+             {
+               /* ??? More checks needed, see the GIMPLE verifier.  */
+               if ((TREE_CODE (t) == ARRAY_REF
+                    || TREE_CODE (t) == ARRAY_RANGE_REF)
+                   && !is_gimple_val (TREE_OPERAND (t, 1)))
+                 return false;
+               t = TREE_OPERAND (t, 0);
+             }
+           if (!is_gimple_id (t))
+             return false;
+           break;
+         }
+
+       case TRUTH_NOT_EXPR:
+         if (!is_gimple_val (TREE_OPERAND (expr, 0)))
+           return false;
+         break;
+
+       case TRUTH_AND_EXPR:
+       case TRUTH_XOR_EXPR:
+       case TRUTH_OR_EXPR:
+         if (!is_gimple_val (TREE_OPERAND (expr, 0))
+             || !is_gimple_val (TREE_OPERAND (expr, 1)))
+           return false;
+         break;
+
+       case EXC_PTR_EXPR:
+       case FILTER_EXPR:
+         break;
+
+       default:
+         return false;
+       }
+      break;
+
+    case tcc_vl_exp:
+      switch (code)
+       {
+       case CALL_EXPR:
+         break;
+       default:
+         return false;
+       }
+      break;
+
+    case tcc_exceptional:
+      switch (code)
+       {
+       case SSA_NAME:
+         break;
+
+       default:
+         return false;
+       }
+      break;
+
+    default:
+      return false;
     }
-  else if (code == COMPOUND_EXPR)
+
+  return true;
+}
+
+
+/* Set the main expression of *STMT_P to EXPR.  If EXPR is not a valid
+   GIMPLE expression no changes are done and the function returns
+   false.  */
+
+bool
+set_rhs (tree *stmt_p, tree expr)
+{
+  tree stmt = *stmt_p, op;
+  tree new_stmt;
+  tree var;
+  ssa_op_iter iter;
+  int eh_region;
+
+  if (!valid_gimple_expression_p (expr))
     return false;
 
+  if (EXPR_HAS_LOCATION (stmt)
+      && (EXPR_P (expr)
+         || GIMPLE_STMT_P (expr))
+      && ! EXPR_HAS_LOCATION (expr)
+      && TREE_SIDE_EFFECTS (expr)
+      && TREE_CODE (expr) != LABEL_EXPR)
+    SET_EXPR_LOCATION (expr, EXPR_LOCATION (stmt));
+
   switch (TREE_CODE (stmt))
     {
     case RETURN_EXPR:
       op = TREE_OPERAND (stmt, 0);
-      if (TREE_CODE (op) != MODIFY_EXPR)
+      if (TREE_CODE (op) != GIMPLE_MODIFY_STMT)
        {
-         TREE_OPERAND (stmt, 0) = expr;
+         GIMPLE_STMT_OPERAND (stmt, 0) = expr;
          break;
        }
       stmt = op;
       /* FALLTHRU */
 
-    case MODIFY_EXPR:
-      op = TREE_OPERAND (stmt, 1);
+    case GIMPLE_MODIFY_STMT:
+      op = GIMPLE_STMT_OPERAND (stmt, 1);
       if (TREE_CODE (op) == WITH_SIZE_EXPR)
-       stmt = op;
-      TREE_OPERAND (stmt, 1) = expr;
+       TREE_OPERAND (op, 0) = expr;
+      else
+       GIMPLE_STMT_OPERAND (stmt, 1) = expr;
       break;
 
     case COND_EXPR:
+      if (!is_gimple_condexpr (expr))
+        return false;
       COND_EXPR_COND (stmt) = expr;
       break;
     case SWITCH_EXPR:
@@ -615,11 +735,25 @@ set_rhs (tree *stmt_p, tree expr)
     default:
       /* Replace the whole statement with EXPR.  If EXPR has no side
         effects, then replace *STMT_P with an empty statement.  */
-      ann = stmt_ann (stmt);
-      *stmt_p = TREE_SIDE_EFFECTS (expr) ? expr : build_empty_stmt ();
-      (*stmt_p)->common.ann = (tree_ann_t) ann;
+      new_stmt = TREE_SIDE_EFFECTS (expr) ? expr : build_empty_stmt ();
+      *stmt_p = new_stmt;
+
+      /* Preserve the annotation, the histograms and the EH region information
+         associated with the original statement. The EH information
+        needs to be preserved only if the new statement still can throw.  */
+      new_stmt->base.ann = (tree_ann_t) stmt_ann (stmt);
+      gimple_move_stmt_histograms (cfun, new_stmt, stmt);
+      if (tree_could_throw_p (new_stmt))
+       {
+         eh_region = lookup_stmt_eh_region (stmt);
+         /* We couldn't possibly turn a nothrow into a throw statement.  */
+         gcc_assert (eh_region >= 0);
+         remove_stmt_from_eh_region (stmt);
+         add_stmt_to_eh_region (new_stmt, eh_region);
+       }
 
-      if (TREE_SIDE_EFFECTS (expr))
+      if (gimple_in_ssa_p (cfun)
+         && TREE_SIDE_EFFECTS (expr))
        {
          /* Fix all the SSA_NAMEs created by *STMT_P to point to its new
             replacement.  */
@@ -629,6 +763,7 @@ set_rhs (tree *stmt_p, tree expr)
                SSA_NAME_DEF_STMT (var) = *stmt_p;
            }
        }
+      stmt->base.ann = NULL;
       break;
     }
 
@@ -674,17 +809,19 @@ ssa_propagate (ssa_prop_visit_stmt_fn visit_stmt,
 }
 
 
-/* Return the first V_MAY_DEF or V_MUST_DEF operand for STMT.  */
+/* Return the first VDEF operand for STMT.  */
 
 tree
 first_vdef (tree stmt)
 {
-  if (NUM_V_MAY_DEFS (STMT_V_MAY_DEF_OPS (stmt)) > 0)
-    return V_MAY_DEF_RESULT (STMT_V_MAY_DEF_OPS (stmt), 0);
-  else if (NUM_V_MUST_DEFS (STMT_V_MUST_DEF_OPS (stmt)) > 0)
-    return V_MUST_DEF_RESULT (STMT_V_MUST_DEF_OPS (stmt), 0);
-  else
-    gcc_unreachable ();
+  ssa_op_iter iter;
+  tree op;
+
+  /* Simply return the first operand we arrive at.  */
+  FOR_EACH_SSA_TREE_OPERAND (op, stmt, iter, SSA_OP_VIRTUAL_DEFS)
+    return (op);
+
+  gcc_unreachable ();
 }
 
 
@@ -698,14 +835,13 @@ stmt_makes_single_load (tree stmt)
 {
   tree rhs;
 
-  if (TREE_CODE (stmt) != MODIFY_EXPR)
+  if (TREE_CODE (stmt) != GIMPLE_MODIFY_STMT)
     return false;
 
-  if (NUM_V_MAY_DEFS (STMT_V_MAY_DEF_OPS (stmt)) == 0
-      && NUM_VUSES (STMT_VUSE_OPS (stmt)) == 0)
+  if (ZERO_SSA_OPERANDS (stmt, SSA_OP_VDEF|SSA_OP_VUSE))
     return false;
 
-  rhs = TREE_OPERAND (stmt, 1);
+  rhs = GIMPLE_STMT_OPERAND (stmt, 1);
   STRIP_NOPS (rhs);
 
   return (!TREE_THIS_VOLATILE (rhs)
@@ -724,14 +860,13 @@ stmt_makes_single_store (tree stmt)
 {
   tree lhs;
 
-  if (TREE_CODE (stmt) != MODIFY_EXPR)
+  if (TREE_CODE (stmt) != GIMPLE_MODIFY_STMT)
     return false;
 
-  if (NUM_V_MAY_DEFS (STMT_V_MAY_DEF_OPS (stmt)) == 0
-      && NUM_V_MUST_DEFS (STMT_V_MUST_DEF_OPS (stmt)) == 0)
+  if (ZERO_SSA_OPERANDS (stmt, SSA_OP_VDEF))
     return false;
 
-  lhs = TREE_OPERAND (stmt, 0);
+  lhs = GIMPLE_STMT_OPERAND (stmt, 0);
   STRIP_NOPS (lhs);
 
   return (!TREE_THIS_VOLATILE (lhs)
@@ -769,6 +904,8 @@ struct prop_stats_d
 {
   long num_const_prop;
   long num_copy_prop;
+  long num_pred_folded;
+  long num_dce;
 };
 
 static struct prop_stats_d prop_stats;
@@ -830,7 +967,7 @@ replace_uses_in (tree stmt, bool *replaced_addresses_p,
       GIMPLE register, then we are making a copy/constant propagation
       from a memory store.  For instance,
 
-       # a_3 = V_MAY_DEF <a_2>
+       # a_3 = VDEF <a_2>
        a.b = x_1;
        ...
        # VUSE <a_3>
@@ -841,8 +978,8 @@ replace_uses_in (tree stmt, bool *replaced_addresses_p,
       the VUSE(s) that we are replacing.  Otherwise, we may do the
       wrong replacement:
 
-       # a_3 = V_MAY_DEF <a_2>
-       # b_5 = V_MAY_DEF <b_4>
+       # a_3 = VDEF <a_2>
+       # b_5 = VDEF <b_4>
        *p = 10;
        ...
        # VUSE <b_5>
@@ -862,10 +999,10 @@ replace_uses_in (tree stmt, bool *replaced_addresses_p,
       stored in different locations:
 
                if (...)
-                 # a_3 = V_MAY_DEF <a_2>
+                 # a_3 = VDEF <a_2>
                  a.b = 3;
                else
-                 # a_4 = V_MAY_DEF <a_2>
+                 # a_4 = VDEF <a_2>
                  a.c = 3;
                # a_5 = PHI <a_3, a_4>
 
@@ -892,7 +1029,7 @@ replace_vuses_in (tree stmt, bool *replaced_addresses_p,
         see if we are trying to propagate a constant or a GIMPLE
         register (case #1 above).  */
       prop_value_t *val = get_value_loaded_by (stmt, prop_value);
-      tree rhs = TREE_OPERAND (stmt, 1);
+      tree rhs = GIMPLE_STMT_OPERAND (stmt, 1);
 
       if (val
          && val->value
@@ -904,7 +1041,7 @@ replace_vuses_in (tree stmt, bool *replaced_addresses_p,
          /* If we are replacing a constant address, inform our
             caller.  */
          if (TREE_CODE (val->value) != SSA_NAME
-             && POINTER_TYPE_P (TREE_TYPE (TREE_OPERAND (stmt, 1)))
+             && POINTER_TYPE_P (TREE_TYPE (GIMPLE_STMT_OPERAND (stmt, 1)))
              && replaced_addresses_p)
            *replaced_addresses_p = true;
 
@@ -914,7 +1051,7 @@ replace_vuses_in (tree stmt, bool *replaced_addresses_p,
             stores between DEF_STMT and STMT, we only need to check
             that the RHS of STMT is the same as the memory reference
             propagated together with the value.  */
-         TREE_OPERAND (stmt, 1) = val->value;
+         GIMPLE_STMT_OPERAND (stmt, 1) = val->value;
 
          if (TREE_CODE (val->value) != SSA_NAME)
            prop_stats.num_const_prop++;
@@ -960,6 +1097,11 @@ static void
 replace_phi_args_in (tree phi, prop_value_t *prop_value)
 {
   int i;
+  bool replaced = false;
+  tree prev_phi = NULL;
+
+  if (dump_file && (dump_flags & TDF_DETAILS))
+    prev_phi = unshare_expr (phi);
 
   for (i = 0; i < PHI_NUM_ARGS (phi); i++)
     {
@@ -977,6 +1119,7 @@ replace_phi_args_in (tree phi, prop_value_t *prop_value)
                prop_stats.num_copy_prop++;
 
              propagate_value (PHI_ARG_DEF_PTR (phi, i), val);
+             replaced = true;
 
              /* If we propagated a copy and this argument flows
                 through an abnormal edge, update the replacement
@@ -987,19 +1130,99 @@ replace_phi_args_in (tree phi, prop_value_t *prop_value)
            }
        }
     }
+  
+  if (replaced && dump_file && (dump_flags & TDF_DETAILS))
+    {
+      fprintf (dump_file, "Folded PHI node: ");
+      print_generic_stmt (dump_file, prev_phi, TDF_SLIM);
+      fprintf (dump_file, "           into: ");
+      print_generic_stmt (dump_file, phi, TDF_SLIM);
+      fprintf (dump_file, "\n");
+    }
 }
 
 
-/* Perform final substitution and folding of propagated values.  */
+/* If STMT has a predicate whose value can be computed using the value
+   range information computed by VRP, compute its value and return true.
+   Otherwise, return false.  */
 
-void
-substitute_and_fold (prop_value_t *prop_value)
+static bool
+fold_predicate_in (tree stmt)
+{
+  tree *pred_p = NULL;
+  bool modify_stmt_p = false;
+  tree val;
+
+  if (TREE_CODE (stmt) == GIMPLE_MODIFY_STMT
+      && COMPARISON_CLASS_P (GIMPLE_STMT_OPERAND (stmt, 1)))
+    {
+      modify_stmt_p = true;
+      pred_p = &GIMPLE_STMT_OPERAND (stmt, 1);
+    }
+  else if (TREE_CODE (stmt) == COND_EXPR)
+    pred_p = &COND_EXPR_COND (stmt);
+  else
+    return false;
+
+  if (TREE_CODE (*pred_p) == SSA_NAME)
+    val = vrp_evaluate_conditional (EQ_EXPR,
+                                   *pred_p,
+                                   boolean_true_node,
+                                   stmt);
+  else
+    val = vrp_evaluate_conditional (TREE_CODE (*pred_p),
+                                   TREE_OPERAND (*pred_p, 0),
+                                   TREE_OPERAND (*pred_p, 1),
+                                   stmt);
+
+  if (val)
+    {
+      if (modify_stmt_p)
+        val = fold_convert (TREE_TYPE (*pred_p), val);
+      
+      if (dump_file)
+       {
+         fprintf (dump_file, "Folding predicate ");
+         print_generic_expr (dump_file, *pred_p, 0);
+         fprintf (dump_file, " to ");
+         print_generic_expr (dump_file, val, 0);
+         fprintf (dump_file, "\n");
+       }
+
+      prop_stats.num_pred_folded++;
+      *pred_p = val;
+      return true;
+    }
+
+  return false;
+}
+
+
+/* Perform final substitution and folding of propagated values.
+
+   PROP_VALUE[I] contains the single value that should be substituted
+   at every use of SSA name N_I.  If PROP_VALUE is NULL, no values are
+   substituted.
+
+   If USE_RANGES_P is true, statements that contain predicate
+   expressions are evaluated with a call to vrp_evaluate_conditional.
+   This will only give meaningful results when called from tree-vrp.c
+   (the information used by vrp_evaluate_conditional is built by the
+   VRP pass).  
+
+   Return TRUE when something changed.  */
+
+bool
+substitute_and_fold (prop_value_t *prop_value, bool use_ranges_p)
 {
   basic_block bb;
+  bool something_changed = false;
+
+  if (prop_value == NULL && !use_ranges_p)
+    return false;
 
   if (dump_file && (dump_flags & TDF_DETAILS))
-    fprintf (dump_file,
-            "\nSubstituing values and folding statements\n\n");
+    fprintf (dump_file, "\nSubstituing values and folding statements\n\n");
 
   memset (&prop_stats, 0, sizeof (prop_stats));
 
@@ -1009,71 +1232,142 @@ substitute_and_fold (prop_value_t *prop_value)
       block_stmt_iterator i;
       tree phi;
 
-      /* Propagate our known values into PHI nodes.  */
-      for (phi = phi_nodes (bb); phi; phi = PHI_CHAIN (phi))
+      /* Propagate known values into PHI nodes.  */
+      if (prop_value)
+       for (phi = phi_nodes (bb); phi; phi = PHI_CHAIN (phi))
+         replace_phi_args_in (phi, prop_value);
+
+      /* Propagate known values into stmts.  Do a backward walk to expose
+        more trivially deletable stmts.  */
+      for (i = bsi_last (bb); !bsi_end_p (i);)
        {
-         if (dump_file && (dump_flags & TDF_DETAILS))
+          bool replaced_address, did_replace;
+         tree call, prev_stmt = NULL;
+         tree stmt = bsi_stmt (i);
+
+         /* Ignore ASSERT_EXPRs.  They are used by VRP to generate
+            range information for names and they are discarded
+            afterwards.  */
+         if (TREE_CODE (stmt) == GIMPLE_MODIFY_STMT
+             && TREE_CODE (GIMPLE_STMT_OPERAND (stmt, 1)) == ASSERT_EXPR)
            {
-             fprintf (dump_file, "Replaced ");
-             print_generic_stmt (dump_file, phi, TDF_SLIM);
+             bsi_prev (&i);
+             continue;
            }
 
-         replace_phi_args_in (phi, prop_value);
-
-         if (dump_file && (dump_flags & TDF_DETAILS))
+         /* No point propagating into a stmt whose result is not used,
+            but instead we might be able to remove a trivially dead stmt.  */
+         if (TREE_CODE (stmt) == GIMPLE_MODIFY_STMT
+             && TREE_CODE (GIMPLE_STMT_OPERAND (stmt, 0)) == SSA_NAME
+             && !stmt_ann (stmt)->has_volatile_ops
+             && has_zero_uses (GIMPLE_STMT_OPERAND (stmt, 0))
+             && !tree_could_throw_p (stmt)
+             && (!(call = get_call_expr_in (stmt))
+                 || !TREE_SIDE_EFFECTS (call)))
            {
-             fprintf (dump_file, " with ");
-             print_generic_stmt (dump_file, phi, TDF_SLIM);
-             fprintf (dump_file, "\n");
+             block_stmt_iterator i2;
+             if (dump_file && dump_flags & TDF_DETAILS)
+               {
+                 fprintf (dump_file, "Removing dead stmt ");
+                 print_generic_expr (dump_file, stmt, 0);
+                 fprintf (dump_file, "\n");
+               }
+             prop_stats.num_dce++;
+             bsi_prev (&i);
+             i2 = bsi_for_stmt (stmt);
+             bsi_remove (&i2, true);
+             release_defs (stmt);
+             continue;
            }
-       }
 
-      for (i = bsi_start (bb); !bsi_end_p (i); bsi_next (&i))
-       {
-          bool replaced_address, did_replace;
-         tree stmt = bsi_stmt (i);
+         /* Record the state of the statement before replacements.  */
+         push_stmt_changes (bsi_stmt_ptr (i));
 
          /* Replace the statement with its folded version and mark it
             folded.  */
+         did_replace = false;
+         replaced_address = false;
          if (dump_file && (dump_flags & TDF_DETAILS))
+           prev_stmt = unshare_expr (stmt);
+
+         /* If we have range information, see if we can fold
+            predicate expressions.  */
+         if (use_ranges_p)
+           did_replace = fold_predicate_in (stmt);
+
+         if (prop_value)
            {
-             fprintf (dump_file, "Replaced ");
-             print_generic_stmt (dump_file, stmt, TDF_SLIM);
+             /* Only replace real uses if we couldn't fold the
+                statement using value range information (value range
+                information is not collected on virtuals, so we only
+                need to check this for real uses).  */
+             if (!did_replace)
+               did_replace |= replace_uses_in (stmt, &replaced_address,
+                                               prop_value);
+
+             did_replace |= replace_vuses_in (stmt, &replaced_address,
+                                              prop_value);
            }
 
-         replaced_address = false;
-         did_replace = replace_uses_in (stmt, &replaced_address, prop_value);
-         did_replace |= replace_vuses_in (stmt, &replaced_address, prop_value);
+         /* If we made a replacement, fold and cleanup the statement.  */
          if (did_replace)
            {
-             fold_stmt (bsi_stmt_ptr (i));
-             stmt = bsi_stmt(i);
+             tree old_stmt = stmt;
+             tree rhs;
 
-             /* If we folded a builtin function, we'll likely
-                need to rename VDEFs.  */
-             mark_new_vars_to_rename (stmt);
+             fold_stmt (bsi_stmt_ptr (i));
+             stmt = bsi_stmt (i);
 
               /* If we cleaned up EH information from the statement,
                  remove EH edges.  */
-             if (maybe_clean_eh_stmt (stmt))
+             if (maybe_clean_or_replace_eh_stmt (old_stmt, stmt))
                tree_purge_dead_eh_edges (bb);
-           }
 
-         if (dump_file && (dump_flags & TDF_DETAILS))
+             rhs = get_rhs (stmt);
+             if (TREE_CODE (rhs) == ADDR_EXPR)
+               recompute_tree_invariant_for_addr_expr (rhs);
+
+             if (dump_file && (dump_flags & TDF_DETAILS))
+               {
+                 fprintf (dump_file, "Folded statement: ");
+                 print_generic_stmt (dump_file, prev_stmt, TDF_SLIM);
+                 fprintf (dump_file, "            into: ");
+                 print_generic_stmt (dump_file, stmt, TDF_SLIM);
+                 fprintf (dump_file, "\n");
+               }
+
+             /* Determine what needs to be done to update the SSA form.  */
+             pop_stmt_changes (bsi_stmt_ptr (i));
+             something_changed = true;
+           }
+         else
            {
-             fprintf (dump_file, " with ");
-             print_generic_stmt (dump_file, stmt, TDF_SLIM);
-             fprintf (dump_file, "\n");
+             /* The statement was not modified, discard the change buffer.  */
+             discard_stmt_changes (bsi_stmt_ptr (i));
            }
+
+         /* Some statements may be simplified using ranges.  For
+            example, division may be replaced by shifts, modulo
+            replaced with bitwise and, etc.   Do this after 
+            substituting constants, folding, etc so that we're
+            presented with a fully propagated, canonicalized
+            statement.  */
+         if (use_ranges_p)
+           simplify_stmt_using_ranges (stmt);
+
+         bsi_prev (&i);
        }
     }
 
-  if (dump_file && (dump_flags & TDF_STATS))
-    {
-      fprintf (dump_file, "Constants propagated: %6ld\n",
-              prop_stats.num_const_prop);
-      fprintf (dump_file, "Copies propagated:    %6ld\n",
-              prop_stats.num_copy_prop);
-    }
+  statistics_counter_event (cfun, "Constants propagated",
+                           prop_stats.num_const_prop);
+  statistics_counter_event (cfun, "Copies propagated",
+                           prop_stats.num_copy_prop);
+  statistics_counter_event (cfun, "Predicates folded",
+                           prop_stats.num_pred_folded);
+  statistics_counter_event (cfun, "Statements deleted",
+                           prop_stats.num_dce);
+  return something_changed;
 }
+
 #include "gt-tree-ssa-propagate.h"