Patchwork [1/n] Cleanup tree forwprop

login
register
mail settings
Submitter Richard Guenther
Date May 30, 2011, 3:35 p.m.
Message ID <alpine.LNX.2.00.1105301731270.810@zhemvz.fhfr.qr>
Download mbox | patch
Permalink /patch/97932/
State New
Headers show

Comments

Richard Guenther - May 30, 2011, 3:35 p.m.
As I've been there a few times recently I saw the main structure
of the pass has become quite messy given we now use forwprop as
container for all sorts of fold/combine like optimizations.

Thus this patch starts to clean up things (a bit) by separating
the stmt walks for both and for combining make sure to re-visit
changed (and newly inserted) statements.

A followup will probably add trivial DCE capabilities to that
2nd walk (so we can eventually stop doing some of the nasty
stuff we do in the first one), similar to substitute_and_fold.

Another followup might do some code re-shuffling and renaming
(and eventually try to look at what forward propagations we
still do).

Bootstrapped on x86_64-unknown-linux-gnu, testing in progress.

Richard.

2011-05-30  Richard Guenther  <rguenther@suse.de>

	* tree-ssa-forwprop.c (forward_propagate_into_comparison): Rename
	to ...
	(forward_propagate_into_comparison_1): ... this.
	(forward_propagate_comparison): Rename to ...
	(forward_propagate_into_comparison): ... this.  Split out
	real forward propagation code to ...
	(forward_propagate_comparison): ... this.
	(forward_propagate_into_gimple_cond): Remove looping.
	(forward_propagate_into_cond): Likewise.
	(simplify_not_neg_expr): Return whether we have done something.
	(simplify_gimple_switch): Likewise.
	(tree_ssa_forward_propagate_single_use_vars): Rename to ...
	(ssa_forward_propagate_and_combine): ... this.  Re-structure
	to do a forward forward-propagation walk on BBs and a backward
	stmt combining walk on BBs.  Consistently re-scan changed
	statements.
	(pass_forwprop): Adjust.

Patch

Index: gcc/tree-ssa-forwprop.c
===================================================================
--- gcc/tree-ssa-forwprop.c	(revision 174434)
+++ gcc/tree-ssa-forwprop.c	(working copy)
@@ -392,9 +392,9 @@  combine_cond_expr_cond (location_t loc,
    were no simplifying combines.  */
 
 static tree
-forward_propagate_into_comparison (location_t loc,
-				   enum tree_code code, tree type,
-				   tree op0, tree op1)
+forward_propagate_into_comparison_1 (location_t loc,
+				     enum tree_code code, tree type,
+				     tree op0, tree op1)
 {
   tree tmp = NULL_TREE;
   tree rhs0 = NULL_TREE, rhs1 = NULL_TREE;
@@ -439,141 +439,31 @@  forward_propagate_into_comparison (locat
   return tmp;
 }
 
-/* Forward propagate the comparison defined in STMT like
-   cond_1 = x CMP y to uses of the form
-     a_1 = (T')cond_1
-     a_1 = !cond_1
-     a_1 = cond_1 != 0
-   Returns 1 if a transformation was done and 2 if the CFG should
-   be cleaned up.  Else returns 0.  */
+/* Propagate from the ssa name definition statements of the assignment
+   from a comparison at *GSI into the conditional if that simplifies it.
+   Returns true if the stmt was modified, false if not.  */
 
-static int 
-forward_propagate_comparison (gimple stmt)
+static bool
+forward_propagate_into_comparison (gimple_stmt_iterator *gsi)
 {
-  tree name = gimple_assign_lhs (stmt);
-  gimple use_stmt;
-  tree tmp = NULL_TREE;
-  int did_something = 0;
+  gimple stmt = gsi_stmt (*gsi);
+  tree tmp;
 
   /* Combine the comparison with defining statements.  */
-  do
-    {
-      tmp = forward_propagate_into_comparison (gimple_location (stmt),
-					       gimple_assign_rhs_code (stmt),
-					       TREE_TYPE (name),
-					       gimple_assign_rhs1 (stmt),
-					       gimple_assign_rhs2 (stmt));
-      if (tmp)
-	{
-	  gimple_stmt_iterator gsi = gsi_for_stmt (stmt);
-	  bool inv = is_gimple_min_invariant (tmp);
-	  gimple_assign_set_rhs_from_tree (&gsi, tmp);
-	  gcc_assert (gsi_stmt (gsi) == stmt);
-	  update_stmt (stmt);
-	  did_something = MAX (did_something, inv ? 2 : 1);
-	  if (TREE_CODE_CLASS (gimple_assign_rhs_code (stmt)) != tcc_comparison)
-	    return did_something;
-	}
-    }
-  while (tmp);
-
-  /* Don't propagate ssa names that occur in abnormal phis.  */
-  if ((TREE_CODE (gimple_assign_rhs1 (stmt)) == SSA_NAME
-       && SSA_NAME_OCCURS_IN_ABNORMAL_PHI (gimple_assign_rhs1 (stmt)))
-      || (TREE_CODE (gimple_assign_rhs2 (stmt)) == SSA_NAME
-        && SSA_NAME_OCCURS_IN_ABNORMAL_PHI (gimple_assign_rhs2 (stmt))))
-    return did_something;
-
-  /* Do not un-cse comparisons.  But propagate through copies.  */
-  use_stmt = get_prop_dest_stmt (name, &name);
-  if (!use_stmt)
-    return did_something;
-
-  /* Conversion of the condition result to another integral type.  */
-  if (is_gimple_assign (use_stmt)
-      && (CONVERT_EXPR_CODE_P (gimple_assign_rhs_code (use_stmt))
-	  || TREE_CODE_CLASS (gimple_assign_rhs_code (use_stmt))
-	     == tcc_comparison
-          || gimple_assign_rhs_code (use_stmt) == TRUTH_NOT_EXPR)
-      && INTEGRAL_TYPE_P (TREE_TYPE (gimple_assign_lhs (use_stmt))))
+  tmp = forward_propagate_into_comparison_1 (gimple_location (stmt),
+					     gimple_assign_rhs_code (stmt),
+					     TREE_TYPE
+					       (gimple_assign_lhs (stmt)),
+					     gimple_assign_rhs1 (stmt),
+					     gimple_assign_rhs2 (stmt));
+  if (tmp)
     {
-      tree lhs = gimple_assign_lhs (use_stmt);
-
-      /* We can propagate the condition into a conversion.  */
-      if (CONVERT_EXPR_CODE_P (gimple_assign_rhs_code (use_stmt)))
-	{
-	  /* Avoid using fold here as that may create a COND_EXPR with
-	     non-boolean condition as canonical form.  */
-	  tmp = build2 (gimple_assign_rhs_code (stmt), TREE_TYPE (lhs),
-                        gimple_assign_rhs1 (stmt), gimple_assign_rhs2 (stmt));
-	}
-      /* We can propagate the condition into X op CST where op
-	 is EQ_EXPR or NE_EXPR and CST is either one or zero.  */
-      else if (TREE_CODE_CLASS (gimple_assign_rhs_code (use_stmt))
-              == tcc_comparison
-             && TREE_CODE (gimple_assign_rhs1 (use_stmt)) == SSA_NAME
-             && TREE_CODE (gimple_assign_rhs2 (use_stmt)) == INTEGER_CST)
-      {
-        enum tree_code code = gimple_assign_rhs_code (use_stmt);
-        tree cst = gimple_assign_rhs2 (use_stmt);
-	tree cond;
-
-	cond = build2 (gimple_assign_rhs_code (stmt),
-		       TREE_TYPE (cst),
-		       gimple_assign_rhs1 (stmt),
-		       gimple_assign_rhs2 (stmt));
-
-        tmp = combine_cond_expr_cond (gimple_location (use_stmt),
-				      code, TREE_TYPE (lhs),
-				      cond, cst, false);
-        if (tmp == NULL_TREE)
-          return did_something;
-      }
-      /* We can propagate the condition into a statement that
-	 computes the logical negation of the comparison result.  */
-      else if (gimple_assign_rhs_code (use_stmt) == TRUTH_NOT_EXPR)
-	{
-	  tree type = TREE_TYPE (gimple_assign_rhs1 (stmt));
-	  bool nans = HONOR_NANS (TYPE_MODE (type));
-	  enum tree_code code;
-	  code = invert_tree_comparison (gimple_assign_rhs_code (stmt), nans);
-	  if (code == ERROR_MARK)
-	    return did_something;
-
-	  tmp = build2 (code, TREE_TYPE (lhs), gimple_assign_rhs1 (stmt),
-                        gimple_assign_rhs2 (stmt));
-	}
-      else
-	return did_something;
-
-      {
-	gimple_stmt_iterator gsi = gsi_for_stmt (use_stmt);
-	bool inv = is_gimple_min_invariant (tmp);
-	gimple_assign_set_rhs_from_tree (&gsi, unshare_expr (tmp));
-	did_something = MAX (did_something, inv ? 2 : 1);
-	use_stmt = gsi_stmt (gsi);
-	update_stmt (use_stmt);
-      }
-
-      if (dump_file && (dump_flags & TDF_DETAILS))
-	{
-	  tree old_rhs = rhs_to_tree (TREE_TYPE (gimple_assign_lhs (stmt)),
-                                      stmt);
-	  fprintf (dump_file, "  Replaced '");
-	  print_generic_expr (dump_file, old_rhs, dump_flags);
-	  fprintf (dump_file, "' with '");
-	  print_generic_expr (dump_file, tmp, dump_flags);
-	  fprintf (dump_file, "'\n");
-	}
-
-      /* Remove defining statements.  */
-      if (remove_prop_source_from_use (name))
-	did_something = 2;
-      else
-	did_something = MAX (did_something, 1);
+      gimple_assign_set_rhs_from_tree (gsi, tmp);
+      update_stmt (stmt);
+      return true;
     }
 
-  return did_something;
+  return false;
 }
 
 /* Propagate from the ssa name definition statements of COND_EXPR
@@ -588,48 +478,41 @@  forward_propagate_into_gimple_cond (gimp
 {
   int did_something = 0;
   location_t loc = gimple_location (stmt);
+  tree tmp;
+  enum tree_code code = gimple_cond_code (stmt);
 
-  do {
-    tree tmp = NULL_TREE;
-    enum tree_code code = gimple_cond_code (stmt);
-
-    /* We can do tree combining on SSA_NAME and comparison expressions.  */
-    if (TREE_CODE_CLASS (gimple_cond_code (stmt)) == tcc_comparison)
-      tmp = forward_propagate_into_comparison (loc, code,
-					       boolean_type_node,
-					       gimple_cond_lhs (stmt),
-					       gimple_cond_rhs (stmt));
-
-    if (tmp)
-      {
-	if (dump_file && tmp)
-	  {
-            tree cond = build2 (gimple_cond_code (stmt),
-				boolean_type_node,
-				gimple_cond_lhs (stmt),
-				gimple_cond_rhs (stmt));
-	    fprintf (dump_file, "  Replaced '");
-	    print_generic_expr (dump_file, cond, 0);
-	    fprintf (dump_file, "' with '");
-	    print_generic_expr (dump_file, tmp, 0);
-	    fprintf (dump_file, "'\n");
-	  }
-
-        gimple_cond_set_condition_from_tree (stmt, unshare_expr (tmp));
-	update_stmt (stmt);
-
-	/* Remove defining statements.  */
-	if (is_gimple_min_invariant (tmp))
-	  did_something = 2;
-	else if (did_something == 0)
-	  did_something = 1;
+  /* We can do tree combining on SSA_NAME and comparison expressions.  */
+  if (TREE_CODE_CLASS (gimple_cond_code (stmt)) != tcc_comparison)
+    return 0;
+
+  tmp = forward_propagate_into_comparison_1 (loc, code,
+					     boolean_type_node,
+					     gimple_cond_lhs (stmt),
+					     gimple_cond_rhs (stmt));
+  if (tmp)
+    {
+      if (dump_file && tmp)
+	{
+	  tree cond = build2 (gimple_cond_code (stmt),
+			      boolean_type_node,
+			      gimple_cond_lhs (stmt),
+			      gimple_cond_rhs (stmt));
+	  fprintf (dump_file, "  Replaced '");
+	  print_generic_expr (dump_file, cond, 0);
+	  fprintf (dump_file, "' with '");
+	  print_generic_expr (dump_file, tmp, 0);
+	  fprintf (dump_file, "'\n");
+	}
 
-	/* Continue combining.  */
-	continue;
-      }
+      gimple_cond_set_condition_from_tree (stmt, unshare_expr (tmp));
+      update_stmt (stmt);
 
-    break;
-  } while (1);
+      /* Remove defining statements.  */
+      if (is_gimple_min_invariant (tmp))
+	did_something = 2;
+      else if (did_something == 0)
+	did_something = 1;
+    }
 
   return did_something;
 }
@@ -648,57 +531,49 @@  forward_propagate_into_cond (gimple_stmt
   gimple stmt = gsi_stmt (*gsi_p);
   location_t loc = gimple_location (stmt);
   int did_something = 0;
+  tree tmp = NULL_TREE;
+  tree cond = gimple_assign_rhs1 (stmt);
 
-  do {
-    tree tmp = NULL_TREE;
-    tree cond = gimple_assign_rhs1 (stmt);
-
-    /* We can do tree combining on SSA_NAME and comparison expressions.  */
-    if (COMPARISON_CLASS_P (cond))
-      tmp = forward_propagate_into_comparison (loc, TREE_CODE (cond),
+  /* We can do tree combining on SSA_NAME and comparison expressions.  */
+  if (COMPARISON_CLASS_P (cond))
+    tmp = forward_propagate_into_comparison_1 (loc, TREE_CODE (cond),
 					       boolean_type_node,
 					       TREE_OPERAND (cond, 0),
 					       TREE_OPERAND (cond, 1));
-    else if (TREE_CODE (cond) == SSA_NAME)
-      {
-	tree name = cond, rhs0;
-	gimple def_stmt = get_prop_source_stmt (name, true, NULL);
-	if (!def_stmt || !can_propagate_from (def_stmt))
-	  return did_something;
-
-	rhs0 = gimple_assign_rhs1 (def_stmt);
-	tmp = combine_cond_expr_cond (loc, NE_EXPR, boolean_type_node, rhs0,
-				      build_int_cst (TREE_TYPE (rhs0), 0),
-				      false);
-      }
+  else if (TREE_CODE (cond) == SSA_NAME)
+    {
+      tree name = cond, rhs0;
+      gimple def_stmt = get_prop_source_stmt (name, true, NULL);
+      if (!def_stmt || !can_propagate_from (def_stmt))
+	return did_something;
 
-    if (tmp)
-      {
-	if (dump_file && tmp)
-	  {
-	    fprintf (dump_file, "  Replaced '");
-	    print_generic_expr (dump_file, cond, 0);
-	    fprintf (dump_file, "' with '");
-	    print_generic_expr (dump_file, tmp, 0);
-	    fprintf (dump_file, "'\n");
-	  }
+      rhs0 = gimple_assign_rhs1 (def_stmt);
+      tmp = combine_cond_expr_cond (loc, NE_EXPR, boolean_type_node, rhs0,
+				    build_int_cst (TREE_TYPE (rhs0), 0),
+				    false);
+    }
 
-	gimple_assign_set_rhs_from_tree (gsi_p, unshare_expr (tmp));
-	stmt = gsi_stmt (*gsi_p);
-	update_stmt (stmt);
-
-	/* Remove defining statements.  */
-	if (is_gimple_min_invariant (tmp))
-	  did_something = 2;
-	else if (did_something == 0)
-	  did_something = 1;
+  if (tmp)
+    {
+      if (dump_file && tmp)
+	{
+	  fprintf (dump_file, "  Replaced '");
+	  print_generic_expr (dump_file, cond, 0);
+	  fprintf (dump_file, "' with '");
+	  print_generic_expr (dump_file, tmp, 0);
+	  fprintf (dump_file, "'\n");
+	}
 
-	/* Continue combining.  */
-	continue;
-      }
+      gimple_assign_set_rhs_from_tree (gsi_p, unshare_expr (tmp));
+      stmt = gsi_stmt (*gsi_p);
+      update_stmt (stmt);
 
-    break;
-  } while (1);
+      /* Remove defining statements.  */
+      if (is_gimple_min_invariant (tmp))
+	did_something = 2;
+      else if (did_something == 0)
+	did_something = 1;
+    }
 
   return did_something;
 }
@@ -1224,6 +1099,116 @@  forward_propagate_addr_expr (tree name,
   return all && has_zero_uses (name);
 }
 
+
+/* Forward propagate the comparison defined in STMT like
+   cond_1 = x CMP y to uses of the form
+     a_1 = (T')cond_1
+     a_1 = !cond_1
+     a_1 = cond_1 != 0
+   Returns true if stmt is now unused.  */
+
+static bool
+forward_propagate_comparison (gimple stmt)
+{
+  tree name = gimple_assign_lhs (stmt);
+  gimple use_stmt;
+  tree tmp = NULL_TREE;
+
+  /* Don't propagate ssa names that occur in abnormal phis.  */
+  if ((TREE_CODE (gimple_assign_rhs1 (stmt)) == SSA_NAME
+       && SSA_NAME_OCCURS_IN_ABNORMAL_PHI (gimple_assign_rhs1 (stmt)))
+      || (TREE_CODE (gimple_assign_rhs2 (stmt)) == SSA_NAME
+        && SSA_NAME_OCCURS_IN_ABNORMAL_PHI (gimple_assign_rhs2 (stmt))))
+    return false;
+
+  /* Do not un-cse comparisons.  But propagate through copies.  */
+  use_stmt = get_prop_dest_stmt (name, &name);
+  if (!use_stmt)
+    return false;
+
+  /* Conversion of the condition result to another integral type.  */
+  if (is_gimple_assign (use_stmt)
+      && (CONVERT_EXPR_CODE_P (gimple_assign_rhs_code (use_stmt))
+	  || TREE_CODE_CLASS (gimple_assign_rhs_code (use_stmt))
+	     == tcc_comparison
+          || gimple_assign_rhs_code (use_stmt) == TRUTH_NOT_EXPR)
+      && INTEGRAL_TYPE_P (TREE_TYPE (gimple_assign_lhs (use_stmt))))
+    {
+      tree lhs = gimple_assign_lhs (use_stmt);
+
+      /* We can propagate the condition into a conversion.  */
+      if (CONVERT_EXPR_CODE_P (gimple_assign_rhs_code (use_stmt)))
+	{
+	  /* Avoid using fold here as that may create a COND_EXPR with
+	     non-boolean condition as canonical form.  */
+	  tmp = build2 (gimple_assign_rhs_code (stmt), TREE_TYPE (lhs),
+                        gimple_assign_rhs1 (stmt), gimple_assign_rhs2 (stmt));
+	}
+      /* We can propagate the condition into X op CST where op
+	 is EQ_EXPR or NE_EXPR and CST is either one or zero.  */
+      else if (TREE_CODE_CLASS (gimple_assign_rhs_code (use_stmt))
+              == tcc_comparison
+             && TREE_CODE (gimple_assign_rhs1 (use_stmt)) == SSA_NAME
+             && TREE_CODE (gimple_assign_rhs2 (use_stmt)) == INTEGER_CST)
+      {
+        enum tree_code code = gimple_assign_rhs_code (use_stmt);
+        tree cst = gimple_assign_rhs2 (use_stmt);
+	tree cond;
+
+	cond = build2 (gimple_assign_rhs_code (stmt),
+		       TREE_TYPE (cst),
+		       gimple_assign_rhs1 (stmt),
+		       gimple_assign_rhs2 (stmt));
+
+        tmp = combine_cond_expr_cond (gimple_location (use_stmt),
+				      code, TREE_TYPE (lhs),
+				      cond, cst, false);
+        if (tmp == NULL_TREE)
+          return false;
+      }
+      /* We can propagate the condition into a statement that
+	 computes the logical negation of the comparison result.  */
+      else if (gimple_assign_rhs_code (use_stmt) == TRUTH_NOT_EXPR)
+	{
+	  tree type = TREE_TYPE (gimple_assign_rhs1 (stmt));
+	  bool nans = HONOR_NANS (TYPE_MODE (type));
+	  enum tree_code code;
+	  code = invert_tree_comparison (gimple_assign_rhs_code (stmt), nans);
+	  if (code == ERROR_MARK)
+	    return false;
+
+	  tmp = build2 (code, TREE_TYPE (lhs), gimple_assign_rhs1 (stmt),
+                        gimple_assign_rhs2 (stmt));
+	}
+      else
+	return false;
+
+      {
+	gimple_stmt_iterator gsi = gsi_for_stmt (use_stmt);
+	gimple_assign_set_rhs_from_tree (&gsi, unshare_expr (tmp));
+	use_stmt = gsi_stmt (gsi);
+	update_stmt (use_stmt);
+      }
+
+      if (dump_file && (dump_flags & TDF_DETAILS))
+	{
+	  tree old_rhs = rhs_to_tree (TREE_TYPE (gimple_assign_lhs (stmt)),
+                                      stmt);
+	  fprintf (dump_file, "  Replaced '");
+	  print_generic_expr (dump_file, old_rhs, dump_flags);
+	  fprintf (dump_file, "' with '");
+	  print_generic_expr (dump_file, tmp, dump_flags);
+	  fprintf (dump_file, "'\n");
+	}
+
+      /* Remove defining statements.  */
+      return remove_prop_source_from_use (name);
+    }
+
+  return false;
+}
+
+
 /* If we have lhs = ~x (STMT), look and see if earlier we had x = ~y.
    If so, we can change STMT into lhs = y which can later be copy
    propagated.  Similarly for negation.
@@ -1236,9 +1221,11 @@  forward_propagate_addr_expr (tree name,
    there's less work to do for each NOT/NEG expression we find.
    Backwards propagation needs to look at the statement in a single
    backlink.  Forward propagation needs to look at potentially more
-   than one forward link.  */
+   than one forward link.
 
-static void
+   Returns true when the statement was changed.  */
+
+static bool 
 simplify_not_neg_expr (gimple_stmt_iterator *gsi_p)
 {
   gimple stmt = gsi_stmt (*gsi_p);
@@ -1258,14 +1245,17 @@  simplify_not_neg_expr (gimple_stmt_itera
 	  gimple_assign_set_rhs_from_tree (gsi_p, rhs_def_operand);
 	  stmt = gsi_stmt (*gsi_p);
 	  update_stmt (stmt);
+	  return true;
 	}
     }
+
+  return false;
 }
 
 /* STMT is a SWITCH_EXPR for which we attempt to find equivalent forms of
    the condition which we may be able to optimize better.  */
 
-static void
+static bool
 simplify_gimple_switch (gimple stmt)
 {
   tree cond = gimple_switch_index (stmt);
@@ -1311,10 +1301,13 @@  simplify_gimple_switch (gimple stmt)
 		{
 		  gimple_switch_set_index (stmt, def);
 		  update_stmt (stmt);
+		  return true;
 		}
 	    }
 	}
     }
+
+  return false;
 }
 
 /* For pointers p2 and p1 return p2 - p1 if the
@@ -2206,10 +2199,11 @@  combine_conversions (gimple_stmt_iterato
   return false;
 }
 
-/* Main entry point for the forward propagation optimizer.  */
+/* Main entry point for the forward propagation and statement combine
+   optimizer.  */
 
 static unsigned int
-tree_ssa_forward_propagate_single_use_vars (void)
+ssa_forward_propagate_and_combine (void)
 {
   basic_block bb;
   unsigned int todoflags = 0;
@@ -2220,166 +2214,187 @@  tree_ssa_forward_propagate_single_use_va
     {
       gimple_stmt_iterator gsi;
 
-      /* Note we update GSI within the loop as necessary.  */
+      /* Apply forward propagation to all stmts in the basic-block.
+	 Note we update GSI within the loop as necessary.  */
       for (gsi = gsi_start_bb (bb); !gsi_end_p (gsi); )
 	{
 	  gimple stmt = gsi_stmt (gsi);
+	  tree lhs, rhs;
+	  enum tree_code code;
 
-	  /* If this statement sets an SSA_NAME to an address,
-	     try to propagate the address into the uses of the SSA_NAME.  */
-	  if (is_gimple_assign (stmt))
+	  if (!is_gimple_assign (stmt))
 	    {
-	      tree lhs = gimple_assign_lhs (stmt);
-	      tree rhs = gimple_assign_rhs1 (stmt);
-	      enum tree_code code = gimple_assign_rhs_code (stmt);
+	      gsi_next (&gsi);
+	      continue;
+	    }
 
-	      if (TREE_CODE (lhs) != SSA_NAME
-		  || has_zero_uses (lhs))
-		{
-		  gsi_next (&gsi);
-		  continue;
-		}
+	  lhs = gimple_assign_lhs (stmt);
+	  rhs = gimple_assign_rhs1 (stmt);
+	  code = gimple_assign_rhs_code (stmt);
+	  if (TREE_CODE (lhs) != SSA_NAME
+	      || has_zero_uses (lhs))
+	    {
+	      gsi_next (&gsi);
+	      continue;
+	    }
 
-	      if (code == ADDR_EXPR
-		  /* Handle pointer conversions on invariant addresses
-		     as well, as this is valid gimple.  */
-		  || (CONVERT_EXPR_CODE_P (code)
-		      && TREE_CODE (rhs) == ADDR_EXPR
-		      && POINTER_TYPE_P (TREE_TYPE (lhs))))
-		{
-		  tree base = get_base_address (TREE_OPERAND (rhs, 0));
-		  if ((!base
-		       || !DECL_P (base)
-		       || decl_address_invariant_p (base))
-		      && !stmt_references_abnormal_ssa_name (stmt)
-		      && forward_propagate_addr_expr (lhs, rhs))
-		    {
-		      release_defs (stmt);
-		      todoflags |= TODO_remove_unused_locals;
-		      gsi_remove (&gsi, true);
-		    }
-		  else
-		    gsi_next (&gsi);
-		}
-	      else if (code == POINTER_PLUS_EXPR
-		       && can_propagate_from (stmt))
-		{
-		  if (TREE_CODE (gimple_assign_rhs2 (stmt)) == INTEGER_CST
-		      /* ???  Better adjust the interface to that function
-			 instead of building new trees here.  */
-		      && forward_propagate_addr_expr
-		           (lhs,
-			    build1 (ADDR_EXPR,
-				    TREE_TYPE (rhs),
-				    fold_build2 (MEM_REF,
-						 TREE_TYPE (TREE_TYPE (rhs)),
-						 rhs,
-						 fold_convert
-						   (ptr_type_node,
-						    gimple_assign_rhs2 (stmt))))))
-		    {
-		      release_defs (stmt);
-		      todoflags |= TODO_remove_unused_locals;
-		      gsi_remove (&gsi, true);
-		    }
-		  else if (is_gimple_min_invariant (rhs))
-		    {
-		      /* Make sure to fold &a[0] + off_1 here.  */
-		      fold_stmt_inplace (stmt);
-		      update_stmt (stmt);
-		      if (gimple_assign_rhs_code (stmt) == POINTER_PLUS_EXPR)
-			gsi_next (&gsi);
-		    }
-		  else
-		    gsi_next (&gsi);
-		}
-	      else if ((code == BIT_NOT_EXPR
-		        || code == NEGATE_EXPR)
-		       && TREE_CODE (rhs) == SSA_NAME)
-		{
-		  simplify_not_neg_expr (&gsi);
-		  gsi_next (&gsi);
-		}
-	      else if (code == COND_EXPR)
-                {
-                  /* In this case the entire COND_EXPR is in rhs1. */
-		  int did_something;
-		  fold_defer_overflow_warnings ();
-                  did_something = forward_propagate_into_cond (&gsi);
-		  stmt = gsi_stmt (gsi);
-		  if (did_something == 2)
-		    cfg_changed = true;
-		  fold_undefer_overflow_warnings (!TREE_NO_WARNING (rhs)
-		    && did_something, stmt, WARN_STRICT_OVERFLOW_CONDITIONAL);
-		  gsi_next (&gsi);
-                }
-	      else if (TREE_CODE_CLASS (code) == tcc_comparison)
-		{
-		  bool no_warning = gimple_no_warning_p (stmt);
-		  int did_something;
-		  fold_defer_overflow_warnings ();
-		  did_something = forward_propagate_comparison (stmt);
-		  if (did_something == 2)
-		    cfg_changed = true;
-		  fold_undefer_overflow_warnings
-		    (!no_warning && did_something,
-		     stmt, WARN_STRICT_OVERFLOW_CONDITIONAL);
-		  gsi_next (&gsi);
-		}
-	      else if (code == BIT_AND_EXPR
-		       || code == BIT_IOR_EXPR
-		       || code == BIT_XOR_EXPR)
-		{
-		  if (!simplify_bitwise_binary (&gsi))
-		    gsi_next (&gsi);
+	  /* If this statement sets an SSA_NAME to an address,
+	     try to propagate the address into the uses of the SSA_NAME.  */
+	  if (code == ADDR_EXPR
+	      /* Handle pointer conversions on invariant addresses
+		 as well, as this is valid gimple.  */
+	      || (CONVERT_EXPR_CODE_P (code)
+		  && TREE_CODE (rhs) == ADDR_EXPR
+		  && POINTER_TYPE_P (TREE_TYPE (lhs))))
+	    {
+	      tree base = get_base_address (TREE_OPERAND (rhs, 0));
+	      if ((!base
+		   || !DECL_P (base)
+		   || decl_address_invariant_p (base))
+		  && !stmt_references_abnormal_ssa_name (stmt)
+		  && forward_propagate_addr_expr (lhs, rhs))
+		{
+		  release_defs (stmt);
+		  todoflags |= TODO_remove_unused_locals;
+		  gsi_remove (&gsi, true);
 		}
-	      else if (code == PLUS_EXPR
-		       || code == MINUS_EXPR)
-		{
-		  cfg_changed |= associate_plusminus (stmt);
-		  gsi_next (&gsi);
+	      else
+		gsi_next (&gsi);
+	    }
+	  else if (code == POINTER_PLUS_EXPR
+		   && can_propagate_from (stmt))
+	    {
+	      if (TREE_CODE (gimple_assign_rhs2 (stmt)) == INTEGER_CST
+		  /* ???  Better adjust the interface to that function
+		     instead of building new trees here.  */
+		  && forward_propagate_addr_expr
+		  (lhs,
+		   build1 (ADDR_EXPR,
+			   TREE_TYPE (rhs),
+			   fold_build2 (MEM_REF,
+					TREE_TYPE (TREE_TYPE (rhs)),
+					rhs,
+					fold_convert
+					(ptr_type_node,
+					 gimple_assign_rhs2 (stmt))))))
+		{
+		  release_defs (stmt);
+		  todoflags |= TODO_remove_unused_locals;
+		  gsi_remove (&gsi, true);
 		}
-	      else if (CONVERT_EXPR_CODE_P (code)
-		       || code == FLOAT_EXPR
-		       || code == FIX_TRUNC_EXPR)
+	      else if (is_gimple_min_invariant (rhs))
 		{
-		  if (!combine_conversions (&gsi))
+		  /* Make sure to fold &a[0] + off_1 here.  */
+		  fold_stmt_inplace (stmt);
+		  update_stmt (stmt);
+		  if (gimple_assign_rhs_code (stmt) == POINTER_PLUS_EXPR)
 		    gsi_next (&gsi);
 		}
 	      else
 		gsi_next (&gsi);
 	    }
-	  else if (gimple_code (stmt) == GIMPLE_SWITCH)
+	  else if (TREE_CODE_CLASS (code) == tcc_comparison)
 	    {
-	      simplify_gimple_switch (stmt);
+	      forward_propagate_comparison (stmt);
 	      gsi_next (&gsi);
 	    }
-	  else if (gimple_code (stmt) == GIMPLE_COND)
-	    {
-	      int did_something;
-	      fold_defer_overflow_warnings ();
-	      did_something = forward_propagate_into_gimple_cond (stmt);
-	      if (did_something == 2)
-		cfg_changed = true;
-	      fold_undefer_overflow_warnings (did_something, stmt,
-					      WARN_STRICT_OVERFLOW_CONDITIONAL);
-	      gsi_next (&gsi);
-	    }
-	  else if (is_gimple_call (stmt))
-	    {
-	      tree callee = gimple_call_fndecl (stmt);
-	      if (callee == NULL_TREE
-		  || DECL_BUILT_IN_CLASS (callee) != BUILT_IN_NORMAL
-		  || !simplify_builtin_call (&gsi, callee))
-		gsi_next (&gsi);
-	    }
 	  else
 	    gsi_next (&gsi);
 	}
+
+      /* Combine stmts with the stmts defining their operands.
+	 Note we update GSI within the loop as necessary.  */
+      for (gsi = gsi_last_bb (bb); !gsi_end_p (gsi);)
+	{
+	  gimple stmt = gsi_stmt (gsi);
+	  bool changed = false;
+
+	  switch (gimple_code (stmt))
+	    {
+	    case GIMPLE_ASSIGN:
+	      {
+		tree rhs1 = gimple_assign_rhs1 (stmt);
+		enum tree_code code = gimple_assign_rhs_code (stmt);
+
+		if ((code == BIT_NOT_EXPR
+		     || code == NEGATE_EXPR)
+		    && TREE_CODE (rhs1) == SSA_NAME)
+		  changed = simplify_not_neg_expr (&gsi);
+		else if (code == COND_EXPR)
+		  {
+		    /* In this case the entire COND_EXPR is in rhs1. */
+		    int did_something;
+		    fold_defer_overflow_warnings ();
+		    did_something = forward_propagate_into_cond (&gsi);
+		    stmt = gsi_stmt (gsi);
+		    if (did_something == 2)
+		      cfg_changed = true;
+		    fold_undefer_overflow_warnings
+		      (!TREE_NO_WARNING (rhs1) && did_something, stmt,
+		       WARN_STRICT_OVERFLOW_CONDITIONAL);
+		    changed = did_something != 0;
+		  }
+		else if (TREE_CODE_CLASS (code) == tcc_comparison)
+		  {
+		    bool no_warning = gimple_no_warning_p (stmt);
+		    fold_defer_overflow_warnings ();
+		    changed = forward_propagate_into_comparison (&gsi);
+		    fold_undefer_overflow_warnings
+			(!no_warning && changed,
+			 stmt, WARN_STRICT_OVERFLOW_CONDITIONAL);
+		  }
+		else if (code == BIT_AND_EXPR
+			 || code == BIT_IOR_EXPR
+			 || code == BIT_XOR_EXPR)
+		  changed = simplify_bitwise_binary (&gsi);
+		else if (code == PLUS_EXPR
+			 || code == MINUS_EXPR)
+		  changed = associate_plusminus (stmt);
+		else if (CONVERT_EXPR_CODE_P (code)
+			 || code == FLOAT_EXPR
+			 || code == FIX_TRUNC_EXPR)
+		  changed = combine_conversions (&gsi);
+		break;
+	      }
+
+	    case GIMPLE_SWITCH:
+	      changed = simplify_gimple_switch (stmt);
+	      break;
+
+	    case GIMPLE_COND:
+	      {
+		int did_something;
+		fold_defer_overflow_warnings ();
+		did_something = forward_propagate_into_gimple_cond (stmt);
+		if (did_something == 2)
+		  cfg_changed = true;
+		fold_undefer_overflow_warnings
+		  (did_something, stmt, WARN_STRICT_OVERFLOW_CONDITIONAL);
+		changed = did_something != 0;
+		break;
+	      }
+
+	    case GIMPLE_CALL:
+	      {
+		tree callee = gimple_call_fndecl (stmt);
+		if (callee != NULL_TREE
+		    && DECL_BUILT_IN_CLASS (callee) == BUILT_IN_NORMAL)
+		  changed = simplify_builtin_call (&gsi, callee);
+		break;
+	      }
+
+	    default:;
+	    }
+
+	  /* If the stmt changed try combining it again.  */
+	  if (!changed)
+	    gsi_prev (&gsi);
+	}
     }
 
   if (cfg_changed)
     todoflags |= TODO_cleanup_cfg;
+
   return todoflags;
 }
 
@@ -2396,7 +2411,7 @@  struct gimple_opt_pass pass_forwprop =
   GIMPLE_PASS,
   "forwprop",			/* name */
   gate_forwprop,		/* gate */
-  tree_ssa_forward_propagate_single_use_vars,	/* execute */
+  ssa_forward_propagate_and_combine,	/* execute */
   NULL,				/* sub */
   NULL,				/* next */
   0,				/* static_pass_number */