re PR tree-optimization/30965 (Fails to tree-combine conditions in COND_EXPRs)
authorRichard Guenther <rguenther@suse.de>
Fri, 27 Apr 2007 13:43:42 +0000 (13:43 +0000)
committerRichard Biener <rguenth@gcc.gnu.org>
Fri, 27 Apr 2007 13:43:42 +0000 (13:43 +0000)
2007-04-27  Richard Guenther  <rguenther@suse.de>

PR tree-optimization/30965
PR tree-optimization/30978
* Makefile.in (tree-ssa-forwprop.o): Depend on $(FLAGS_H).
        * tree-ssa-forwprop.c (forward_propagate_into_cond_1): Remove.
        (find_equivalent_equality_comparison): Likewise.
        (simplify_cond): Likewise.
        (get_prop_source_stmt): New helper.
        (get_prop_dest_stmt): Likewise.
(can_propagate_from): Likewise.
(remove_prop_source_from_use): Likewise.
        (combine_cond_expr_cond): Likewise.
        (forward_propagate_comparison): New function.
        (forward_propagate_into_cond): Rewrite to use fold for
        tree combining.
(tree_ssa_forward_propagate_single_use_vars): Call
forward_propagate_comparison to propagate comparisons.

* gcc.dg/tree-ssa/pr30978.c: New testcase.
* gcc.dg/tree-ssa/bool-3.c: Remove XFAIL, explain why.
* gcc.dg/tree-ssa/ssa-fre-4.c: Use char instead of bool
* gcc.dg/strict-overflow-5.c: Adjust tree dump scanning.

From-SVN: r124217

gcc/ChangeLog
gcc/Makefile.in
gcc/testsuite/ChangeLog
gcc/testsuite/gcc.dg/strict-overflow-5.c
gcc/testsuite/gcc.dg/tree-ssa/bool-3.c
gcc/testsuite/gcc.dg/tree-ssa/pr30978.c [new file with mode: 0644]
gcc/testsuite/gcc.dg/tree-ssa/ssa-fre-4.c
gcc/tree-ssa-forwprop.c

index 0e4f987319076c767004e80f1fa31deb858696f9..34b05fefa0a618c00153a156932fe3dccab2af20 100644 (file)
@@ -1,3 +1,22 @@
+2007-04-27  Richard Guenther  <rguenther@suse.de>
+
+       PR tree-optimization/30965
+       PR tree-optimization/30978
+       * Makefile.in (tree-ssa-forwprop.o): Depend on $(FLAGS_H).
+        * tree-ssa-forwprop.c (forward_propagate_into_cond_1): Remove.
+        (find_equivalent_equality_comparison): Likewise.
+        (simplify_cond): Likewise.
+        (get_prop_source_stmt): New helper.
+        (get_prop_dest_stmt): Likewise.
+       (can_propagate_from): Likewise.
+       (remove_prop_source_from_use): Likewise.
+        (combine_cond_expr_cond): Likewise.
+        (forward_propagate_comparison): New function.
+        (forward_propagate_into_cond): Rewrite to use fold for
+        tree combining.
+       (tree_ssa_forward_propagate_single_use_vars): Call
+       forward_propagate_comparison to propagate comparisons.
+
 2007-04-27  Richard Guenther  <rguenther@suse.de>
 
        PR tree-optimization/31715
index 13d93b046dbfb7c316d685a5bd293461c10fd0ad..8856c6415793dce4c4ab753af41257ed7394ef6f 100644 (file)
@@ -1934,7 +1934,7 @@ tree-ssa-dse.o : tree-ssa-dse.c $(CONFIG_H) $(SYSTEM_H) coretypes.h \
 tree-ssa-forwprop.o : tree-ssa-forwprop.c $(CONFIG_H) $(SYSTEM_H) coretypes.h \
    $(TM_H) $(GGC_H) $(TREE_H) $(RTL_H) $(TM_P_H) $(BASIC_BLOCK_H) \
    $(TREE_FLOW_H) tree-pass.h $(TREE_DUMP_H) $(DIAGNOSTIC_H) $(TIMEVAR_H) \
-   langhooks.h
+   langhooks.h $(FLAGS_H)
 tree-ssa-phiopt.o : tree-ssa-phiopt.c $(CONFIG_H) $(SYSTEM_H) coretypes.h \
    $(TM_H) $(GGC_H) $(TREE_H) $(RTL_H) $(TM_P_H) $(BASIC_BLOCK_H) \
    $(TREE_FLOW_H) tree-pass.h $(TREE_DUMP_H) langhooks.h $(FLAGS_H) \
index c9e601eb57d9bcd0522f96c930e1f29bae47de76..49ed2ba7f837e760e43f1b92b50bc4d8b54ba5b8 100644 (file)
@@ -1,3 +1,12 @@
+2007-04-27  Richard Guenther  <rguenther@suse.de>
+
+       PR tree-optimization/30965
+       PR tree-optimization/30978
+       * gcc.dg/tree-ssa/pr30978.c: New testcase.
+       * gcc.dg/tree-ssa/bool-3.c: Remove XFAIL, explain why.
+       * gcc.dg/tree-ssa/ssa-fre-4.c: Use char instead of bool
+       * gcc.dg/strict-overflow-5.c: Adjust tree dump scanning.
+
 2007-04-27  Richard Guenther  <rguenther@suse.de>
 
        PR tree-optimization/31715
index 26e0174ba6a1622291c8a43eff1b3f8e5e3c66a4..816bfe440f0cc7bdd1eef91baa058f36d81fc628 100644 (file)
@@ -1,5 +1,5 @@
 /* { dg-do compile } */
-/* { dg-options "-fstrict-overflow -O2 -fdump-tree-final_cleanup" } */
+/* { dg-options "-fstrict-overflow -O2 -fdump-tree-optimized" } */
 
 /* We can only unroll when using strict overflow semantics.  */
 
@@ -14,6 +14,6 @@ int foo (int i)
   return r;
 }
 
-/* { dg-final { scan-tree-dump-times "r = 3" 1 "final_cleanup" } } */
-/* { dg-final { cleanup-tree-dump "final_cleanup" } } */
+/* { dg-final { scan-tree-dump "return 3" "optimized" } } */
+/* { dg-final { cleanup-tree-dump "optimized" } } */
 
index f2ee9b864dd84c685e71411e1d5f1ee3d12e344c..8fa2a57c4128b2f37e9b46811762396826da35c7 100644 (file)
@@ -11,8 +11,8 @@ int f(_Bool x)
   return y;
 }
 
-/* There should be no == 0. Though PHI-OPT or invert_truth does not
-   fold its tree.  */
-/* { dg-final { scan-tree-dump-times "== 0" 0 "optimized" { xfail *-*-* } } } */
+/* There should be no == 0.  Though PHI-OPT or invert_truth does not
+   fold its tree forwprop is able to clean up the mess.  */
+/* { dg-final { scan-tree-dump-times "== 0" 0 "optimized" } } */
 
 /* { dg-final { cleanup-tree-dump "optimized" } } */
diff --git a/gcc/testsuite/gcc.dg/tree-ssa/pr30978.c b/gcc/testsuite/gcc.dg/tree-ssa/pr30978.c
new file mode 100644 (file)
index 0000000..5444e2d
--- /dev/null
@@ -0,0 +1,14 @@
+/* { dg-do compile } */
+/* { dg-options "-O -fdump-tree-optimized" } */
+
+int foo(int a)
+{
+  unsigned int b = a > 0;
+  char c = b;
+  _Bool d = c == 0;
+  int e = !d;
+  return e;
+}
+
+/* { dg-final { scan-tree-dump "return a > 0;" "optimized" } } */
+/* { dg-final { cleanup-tree-dump "optimized" } } */
index 3966e6a8c43efa988d373961cbf2407b151a8d93..bc62255d6666908c891ac8b0d8e8f7f8e3015373 100644 (file)
@@ -3,13 +3,12 @@
 
 /* From PR21608.  */
 
-#define bool _Bool
-static inline bool wrap(bool f) { return f; }
-bool bar(bool f)
+static inline char wrap(char f) { return f; }
+char bar(char f)
 {
         return wrap(f);
 }
 
-/* { dg-final { scan-tree-dump "Replaced \\\(_Bool\\\) .*with " "fre" } } */
+/* { dg-final { scan-tree-dump "Replaced \\\(char\\\) .*with " "fre" } } */
 /* { dg-final { scan-tree-dump "Replaced \\\(int\\\) .*with " "fre" } } */
 /* { dg-final { cleanup-tree-dump "fre" } } */
index 4e7f19f62c8dc70eddc98e3cd026a16a691b2b89..08bc5ff31c33a9b176ae36331a6fa44803f20ef6 100644 (file)
@@ -33,6 +33,7 @@ Boston, MA 02110-1301, USA.  */
 #include "tree-pass.h"
 #include "tree-dump.h"
 #include "langhooks.h"
+#include "flags.h"
 
 /* This pass propagates the RHS of assignment statements into use
    sites of the LHS of the assignment.  It's basically a specialized
@@ -155,424 +156,303 @@ static bool forward_propagate_addr_expr (tree name, tree rhs);
 static bool cfg_changed;
 
 
-/* Given an SSA_NAME VAR, return true if and only if VAR is defined by
-   a comparison.  */
-
-static bool
-ssa_name_defined_by_comparison_p (tree var)
-{
-  tree def = SSA_NAME_DEF_STMT (var);
-
-  if (TREE_CODE (def) == GIMPLE_MODIFY_STMT)
-    {
-      tree rhs = GIMPLE_STMT_OPERAND (def, 1);
-      return COMPARISON_CLASS_P (rhs);
-    }
-
-  return 0;
-}
-
-/* Forward propagate a single-use variable into COND once.  Return a
-   new condition if successful.  Return NULL_TREE otherwise.  */
+/* Get the next statement we can propagate NAMEs value into skipping
+   trivial copies.  Returns the statement that is suitable as a
+   propagation destination or NULL_TREE if there is no such one.
+   This only returns destinations in a single-use chain.  FINAL_NAME_P
+   if non-NULL is written to the ssa name that represents the use.  */
 
 static tree
-forward_propagate_into_cond_1 (tree cond, tree *test_var_p)
+get_prop_dest_stmt (tree name, tree *final_name_p)
 {
-  tree new_cond = NULL_TREE;
-  enum tree_code cond_code = TREE_CODE (cond);
-  tree test_var = NULL_TREE;
-  tree def;
-  tree def_rhs;
-
-  /* If the condition is not a lone variable or an equality test of an
-     SSA_NAME against an integral constant, then we do not have an
-     optimizable case.
-
-     Note these conditions also ensure the COND_EXPR has no
-     virtual operands or other side effects.  */
-  if (cond_code != SSA_NAME
-      && !((cond_code == EQ_EXPR || cond_code == NE_EXPR)
-          && TREE_CODE (TREE_OPERAND (cond, 0)) == SSA_NAME
-          && CONSTANT_CLASS_P (TREE_OPERAND (cond, 1))
-          && INTEGRAL_TYPE_P (TREE_TYPE (TREE_OPERAND (cond, 1)))))
-    return NULL_TREE;
+  use_operand_p use;
+  tree use_stmt;
 
-  /* Extract the single variable used in the test into TEST_VAR.  */
-  if (cond_code == SSA_NAME)
-    test_var = cond;
-  else
-    test_var = TREE_OPERAND (cond, 0);
+  do {
+    /* If name has multiple uses, bail out.  */
+    if (!single_imm_use (name, &use, &use_stmt))
+      return NULL_TREE;
 
-  /* Now get the defining statement for TEST_VAR.  Skip this case if
-     it's not defined by some GIMPLE_MODIFY_STMT.  */
-  def = SSA_NAME_DEF_STMT (test_var);
-  if (TREE_CODE (def) != GIMPLE_MODIFY_STMT)
-    return NULL_TREE;
+    /* If this is not a trivial copy, we found it.  */
+    if (TREE_CODE (use_stmt) != GIMPLE_MODIFY_STMT
+       || TREE_CODE (GIMPLE_STMT_OPERAND (use_stmt, 0)) != SSA_NAME
+       || GIMPLE_STMT_OPERAND (use_stmt, 1) != name)
+      break;
 
-  def_rhs = GIMPLE_STMT_OPERAND (def, 1);
+    /* Continue searching uses of the copy destination.  */
+    name = GIMPLE_STMT_OPERAND (use_stmt, 0);
+  } while (1);
 
-  /* If TEST_VAR is set by adding or subtracting a constant
-     from an SSA_NAME, then it is interesting to us as we
-     can adjust the constant in the conditional and thus
-     eliminate the arithmetic operation.  */
-  if (TREE_CODE (def_rhs) == PLUS_EXPR
-      || TREE_CODE (def_rhs) == MINUS_EXPR)
-    {
-      tree op0 = TREE_OPERAND (def_rhs, 0);
-      tree op1 = TREE_OPERAND (def_rhs, 1);
-
-      /* The first operand must be an SSA_NAME and the second
-        operand must be a constant.  */
-      if (TREE_CODE (op0) != SSA_NAME
-         || !CONSTANT_CLASS_P (op1)
-         || !INTEGRAL_TYPE_P (TREE_TYPE (op1)))
-       return NULL_TREE;
-
-      /* Don't propagate if the first operand occurs in
-        an abnormal PHI.  */
-      if (SSA_NAME_OCCURS_IN_ABNORMAL_PHI (op0))
-       return NULL_TREE;
-
-      if (has_single_use (test_var))
-       {
-         enum tree_code new_code;
-         tree t;
-
-         /* If the variable was defined via X + C, then we must
-            subtract C from the constant in the conditional.
-            Otherwise we add C to the constant in the
-            conditional.  The result must fold into a valid
-            gimple operand to be optimizable.  */
-         new_code = (TREE_CODE (def_rhs) == PLUS_EXPR
-                     ? MINUS_EXPR : PLUS_EXPR);
-         t = int_const_binop (new_code, TREE_OPERAND (cond, 1), op1, 0);
-         if (!is_gimple_val (t))
-           return NULL_TREE;
-
-         new_cond = build2 (cond_code, boolean_type_node, op0, t);
-       }
-    }
+  if (final_name_p)
+    *final_name_p = name;
 
-  /* These cases require comparisons of a naked SSA_NAME or
-     comparison of an SSA_NAME against zero or one.  */
-  else if (TREE_CODE (cond) == SSA_NAME
-          || integer_zerop (TREE_OPERAND (cond, 1))
-          || integer_onep (TREE_OPERAND (cond, 1)))
-    {
-      /* If TEST_VAR is set from a relational operation
-        between two SSA_NAMEs or a combination of an SSA_NAME
-        and a constant, then it is interesting.  */
-      if (COMPARISON_CLASS_P (def_rhs))
-       {
-         tree op0 = TREE_OPERAND (def_rhs, 0);
-         tree op1 = TREE_OPERAND (def_rhs, 1);
-
-         /* Both operands of DEF_RHS must be SSA_NAMEs or
-            constants.  */
-         if ((TREE_CODE (op0) != SSA_NAME
-              && !is_gimple_min_invariant (op0))
-             || (TREE_CODE (op1) != SSA_NAME
-                 && !is_gimple_min_invariant (op1)))
-           return NULL_TREE;
-
-         /* Don't propagate if the first operand occurs in
-            an abnormal PHI.  */
-         if (TREE_CODE (op0) == SSA_NAME
-             && SSA_NAME_OCCURS_IN_ABNORMAL_PHI (op0))
-           return NULL_TREE;
-
-         /* Don't propagate if the second operand occurs in
-            an abnormal PHI.  */
-         if (TREE_CODE (op1) == SSA_NAME
-             && SSA_NAME_OCCURS_IN_ABNORMAL_PHI (op1))
-           return NULL_TREE;
-
-         if (has_single_use (test_var))
-           {
-             /* TEST_VAR was set from a relational operator.  */
-             new_cond = build2 (TREE_CODE (def_rhs),
-                                boolean_type_node, op0, op1);
-
-             /* Invert the conditional if necessary.  */
-             if ((cond_code == EQ_EXPR
-                  && integer_zerop (TREE_OPERAND (cond, 1)))
-                 || (cond_code == NE_EXPR
-                     && integer_onep (TREE_OPERAND (cond, 1))))
-               {
-                 new_cond = invert_truthvalue (new_cond);
-
-                 /* If we did not get a simple relational
-                    expression or bare SSA_NAME, then we can
-                    not optimize this case.  */
-                 if (!COMPARISON_CLASS_P (new_cond)
-                     && TREE_CODE (new_cond) != SSA_NAME)
-                   new_cond = NULL_TREE;
-               }
-           }
-       }
+  return use_stmt;
+}
 
-      /* If TEST_VAR is set from a TRUTH_NOT_EXPR, then it
-        is interesting.  */
-      else if (TREE_CODE (def_rhs) == TRUTH_NOT_EXPR)
-       {
-         enum tree_code new_code;
-
-         def_rhs = TREE_OPERAND (def_rhs, 0);
-
-         /* DEF_RHS must be an SSA_NAME or constant.  */
-         if (TREE_CODE (def_rhs) != SSA_NAME
-             && !is_gimple_min_invariant (def_rhs))
-           return NULL_TREE;
-
-         /* Don't propagate if the operand occurs in
-            an abnormal PHI.  */
-         if (TREE_CODE (def_rhs) == SSA_NAME
-             && SSA_NAME_OCCURS_IN_ABNORMAL_PHI (def_rhs))
-           return NULL_TREE;
-
-         if (cond_code == SSA_NAME
-             || (cond_code == NE_EXPR
-                 && integer_zerop (TREE_OPERAND (cond, 1)))
-             || (cond_code == EQ_EXPR
-                 && integer_onep (TREE_OPERAND (cond, 1))))
-           new_code = EQ_EXPR;
-         else
-           new_code = NE_EXPR;
+/* Get the statement we can propagate from into NAME skipping
+   trivial copies.  Returns the statement which defines the
+   propagation source or NULL_TREE if there is no such one.
+   If SINGLE_USE_ONLY is set considers only sources which have
+   a single use chain up to NAME.  If SINGLE_USE_P is non-null,
+   it is set to whether the chain to NAME is a single use chain
+   or not.  SINGLE_USE_P is not written to if SINGLE_USE_ONLY is set.  */
 
-         new_cond = build2 (new_code, boolean_type_node, def_rhs,
-                            fold_convert (TREE_TYPE (def_rhs),
-                                          integer_zero_node));
-       }
+static tree
+get_prop_source_stmt (tree name, bool single_use_only, bool *single_use_p)
+{
+  bool single_use = true;
+
+  do {
+    tree def_stmt = SSA_NAME_DEF_STMT (name);
+
+    if (!has_single_use (name))
+      {
+       single_use = false;
+       if (single_use_only)
+         return NULL_TREE;
+      }
+
+    /* If name is defined by a PHI node or is the default def, bail out.  */
+    if (TREE_CODE (def_stmt) != GIMPLE_MODIFY_STMT)
+      return NULL_TREE;
+
+    /* If name is not a simple copy destination, we found it.  */
+    if (TREE_CODE (GIMPLE_STMT_OPERAND (def_stmt, 1)) != SSA_NAME)
+      {
+       if (!single_use_only && single_use_p)
+         *single_use_p = single_use;
+
+       return def_stmt;
+      }
+
+    /* Continue searching the def of the copy source name.  */
+    name = GIMPLE_STMT_OPERAND (def_stmt, 1);
+  } while (1);
+}
 
-      /* If TEST_VAR was set from a cast of an integer type
-        to a boolean type or a cast of a boolean to an
-        integral, then it is interesting.  */
-      else if (TREE_CODE (def_rhs) == NOP_EXPR
-              || TREE_CODE (def_rhs) == CONVERT_EXPR)
-       {
-         tree outer_type;
-         tree inner_type;
-
-         outer_type = TREE_TYPE (def_rhs);
-         inner_type = TREE_TYPE (TREE_OPERAND (def_rhs, 0));
-
-         if ((TREE_CODE (outer_type) == BOOLEAN_TYPE
-              && INTEGRAL_TYPE_P (inner_type))
-             || (TREE_CODE (inner_type) == BOOLEAN_TYPE
-                 && INTEGRAL_TYPE_P (outer_type)))
-           ;
-         else if (INTEGRAL_TYPE_P (outer_type)
-                  && INTEGRAL_TYPE_P (inner_type)
-                  && TREE_CODE (TREE_OPERAND (def_rhs, 0)) == SSA_NAME
-                  && ssa_name_defined_by_comparison_p (TREE_OPERAND (def_rhs,
-                                                                     0)))
-           ;
-         else
-           return NULL_TREE;
+/* Checks if the destination ssa name in DEF_STMT can be used as
+   propagation source.  Returns true if so, otherwise false.  */
 
-         /* Don't propagate if the operand occurs in
-            an abnormal PHI.  */
-         if (TREE_CODE (TREE_OPERAND (def_rhs, 0)) == SSA_NAME
-             && SSA_NAME_OCCURS_IN_ABNORMAL_PHI (TREE_OPERAND
-                                                 (def_rhs, 0)))
-           return NULL_TREE;
+static bool
+can_propagate_from (tree def_stmt)
+{
+  tree rhs = GIMPLE_STMT_OPERAND (def_stmt, 1);
 
-         if (has_single_use (test_var))
-           {
-             enum tree_code new_code;
-             tree new_arg;
-
-             if (cond_code == SSA_NAME
-                 || (cond_code == NE_EXPR
-                     && integer_zerop (TREE_OPERAND (cond, 1)))
-                 || (cond_code == EQ_EXPR
-                     && integer_onep (TREE_OPERAND (cond, 1))))
-               new_code = NE_EXPR;
-             else
-               new_code = EQ_EXPR;
+  /* We cannot propagate ssa names that occur in abnormal phi nodes.  */
+  switch (TREE_CODE_LENGTH (TREE_CODE (rhs)))
+    {
+    case 3:
+      if (TREE_OPERAND (rhs, 2) != NULL_TREE
+         && TREE_CODE (TREE_OPERAND (rhs, 2)) == SSA_NAME
+         && SSA_NAME_OCCURS_IN_ABNORMAL_PHI (TREE_OPERAND (rhs, 2)))
+       return false;
+    case 2:
+      if (TREE_OPERAND (rhs, 1) != NULL_TREE
+         && TREE_CODE (TREE_OPERAND (rhs, 1)) == SSA_NAME
+         && SSA_NAME_OCCURS_IN_ABNORMAL_PHI (TREE_OPERAND (rhs, 1)))
+       return false;
+    case 1:
+      if (TREE_OPERAND (rhs, 0) != NULL_TREE
+         && TREE_CODE (TREE_OPERAND (rhs, 0)) == SSA_NAME
+         && SSA_NAME_OCCURS_IN_ABNORMAL_PHI (TREE_OPERAND (rhs, 0)))
+       return false;
+      break;
 
-             new_arg = TREE_OPERAND (def_rhs, 0);
-             new_cond = build2 (new_code, boolean_type_node, new_arg,
-                                fold_convert (TREE_TYPE (new_arg),
-                                              integer_zero_node));
-           }
-       }
+    default:
+      return false;
     }
 
-  *test_var_p = test_var;
-  return new_cond;
-}
+  /* If the definition is a conversion of a pointer to a function type,
+     then we can not apply optimizations as some targets require function
+     pointers to be canonicalized and in this case this optimization could
+     eliminate a necessary canonicalization.  */
+  if ((TREE_CODE (rhs) == NOP_EXPR
+       || TREE_CODE (rhs) == CONVERT_EXPR)
+      && POINTER_TYPE_P (TREE_TYPE (TREE_OPERAND (rhs, 0)))
+      && TREE_CODE (TREE_TYPE (TREE_TYPE
+                               (TREE_OPERAND (rhs, 0)))) == FUNCTION_TYPE)
+    return false;
 
-/* COND is a condition of the form:
+  return true;
+}
 
-     x == const or x != const
+/* Remove a copy chain ending in NAME along the defs but not
+   further or including UP_TO_STMT.  If NAME was replaced in
+   its only use then this function can be used to clean up
+   dead stmts.  Returns true if UP_TO_STMT can be removed
+   as well, otherwise false.  */
 
-   Look back to x's defining statement and see if x is defined as
+static bool
+remove_prop_source_from_use (tree name, tree up_to_stmt)
+{
+  block_stmt_iterator bsi;
+  tree stmt;
 
-     x = (type) y;
+  do {
+    if (!has_zero_uses (name))
+      return false;
 
-   If const is unchanged if we convert it to type, then we can build
-   the equivalent expression:
+    stmt = SSA_NAME_DEF_STMT (name);
+    if (stmt == up_to_stmt)
+      return true;
 
+    bsi = bsi_for_stmt (stmt);
+    release_defs (stmt);
+    bsi_remove (&bsi, true);
 
-      y == const or y != const
+    name = GIMPLE_STMT_OPERAND (stmt, 1);
+  } while (TREE_CODE (name) == SSA_NAME);
 
-   Which may allow further optimizations.
+  return false;
+}
 
-   Return the equivalent comparison or NULL if no such equivalent comparison
-   was found.  */
+/* Combine OP0 CODE OP1 in the context of a COND_EXPR.  Returns
+   the folded result in a form suitable for COND_EXPR_COND or
+   NULL_TREE, if there is no suitable simplified form.  If
+   INVARIANT_ONLY is true only gimple_min_invariant results are
+   considered simplified.  */
 
 static tree
-find_equivalent_equality_comparison (tree cond)
+combine_cond_expr_cond (enum tree_code code, tree type,
+                       tree op0, tree op1, bool invariant_only)
 {
-  tree op0 = TREE_OPERAND (cond, 0);
-  tree op1 = TREE_OPERAND (cond, 1);
-  tree def_stmt = SSA_NAME_DEF_STMT (op0);
-
-  while (def_stmt
-        && TREE_CODE (def_stmt) == GIMPLE_MODIFY_STMT
-        && TREE_CODE (GIMPLE_STMT_OPERAND (def_stmt, 1)) == SSA_NAME)
-    def_stmt = SSA_NAME_DEF_STMT (GIMPLE_STMT_OPERAND (def_stmt, 1));
-
-  /* OP0 might have been a parameter, so first make sure it
-     was defined by a GIMPLE_MODIFY_STMT.  */
-  if (def_stmt && TREE_CODE (def_stmt) == GIMPLE_MODIFY_STMT)
-    {
-      tree def_rhs = GIMPLE_STMT_OPERAND (def_stmt, 1);
-
-      /* If either operand to the comparison is a pointer to
-        a function, then we can not apply this optimization
-        as some targets require function pointers to be
-        canonicalized and in this case this optimization would
-        eliminate a necessary canonicalization.  */
-      if ((POINTER_TYPE_P (TREE_TYPE (op0))
-          && TREE_CODE (TREE_TYPE (TREE_TYPE (op0))) == FUNCTION_TYPE)
-         || (POINTER_TYPE_P (TREE_TYPE (op1))
-             && TREE_CODE (TREE_TYPE (TREE_TYPE (op1))) == FUNCTION_TYPE))
-       return NULL;
-             
-      /* Now make sure the RHS of the GIMPLE_MODIFY_STMT is a typecast.  */
-      if ((TREE_CODE (def_rhs) == NOP_EXPR
-          || TREE_CODE (def_rhs) == CONVERT_EXPR)
-         && TREE_CODE (TREE_OPERAND (def_rhs, 0)) == SSA_NAME)
-       {
-         tree def_rhs_inner = TREE_OPERAND (def_rhs, 0);
-         tree def_rhs_inner_type = TREE_TYPE (def_rhs_inner);
-         tree new;
-
-         if (TYPE_PRECISION (def_rhs_inner_type)
-             > TYPE_PRECISION (TREE_TYPE (def_rhs)))
-           return NULL;
-
-         /* If the inner type of the conversion is a pointer to
-            a function, then we can not apply this optimization
-            as some targets require function pointers to be
-            canonicalized.  This optimization would result in
-            canonicalization of the pointer when it was not originally
-            needed/intended.  */
-         if (POINTER_TYPE_P (def_rhs_inner_type)
-             && TREE_CODE (TREE_TYPE (def_rhs_inner_type)) == FUNCTION_TYPE)
-           return NULL;
-
-         /* What we want to prove is that if we convert OP1 to
-            the type of the object inside the NOP_EXPR that the
-            result is still equivalent to SRC. 
-
-            If that is true, the build and return new equivalent
-            condition which uses the source of the typecast and the
-            new constant (which has only changed its type).  */
-         new = fold_build1 (TREE_CODE (def_rhs), def_rhs_inner_type, op1);
-         STRIP_USELESS_TYPE_CONVERSION (new);
-         if (is_gimple_val (new) && tree_int_cst_equal (new, op1))
-           return build2 (TREE_CODE (cond), TREE_TYPE (cond),
-                          def_rhs_inner, new);
-       }
-    }
-  return NULL;
-}
+  tree t;
 
-/* EXPR is a COND_EXPR
-   STMT is the statement containing EXPR.
+  gcc_assert (TREE_CODE_CLASS (code) == tcc_comparison);
 
-   This routine attempts to find equivalent forms of the condition
-   which we may be able to optimize better.  */
+  t = fold_binary (code, type, op0, op1);
+  if (!t)
+    return NULL_TREE;
 
-static void
-simplify_cond (tree cond_expr, tree stmt)
-{
-  tree cond = COND_EXPR_COND (cond_expr);
+  /* Require that we got a boolean type out if we put one in.  */
+  gcc_assert (TREE_CODE (TREE_TYPE (t)) == TREE_CODE (type));
 
-  if (COMPARISON_CLASS_P (cond))
+  /* For (bool)x use x != 0.  */
+  if (TREE_CODE (t) == NOP_EXPR
+      && TREE_TYPE (t) == boolean_type_node)
     {
-      tree op0 = TREE_OPERAND (cond, 0);
-      tree op1 = TREE_OPERAND (cond, 1);
+      tree top0 = TREE_OPERAND (t, 0);
+      t = build2 (NE_EXPR, type,
+                 top0, build_int_cst (TREE_TYPE (top0), 0));
+    }
+  /* For !x use x == 0.  */
+  else if (TREE_CODE (t) == TRUTH_NOT_EXPR)
+    {
+      tree top0 = TREE_OPERAND (t, 0);
+      t = build2 (EQ_EXPR, type,
+                 top0, build_int_cst (TREE_TYPE (top0), 0));
+    }
+  /* For cmp ? 1 : 0 use cmp.  */
+  else if (TREE_CODE (t) == COND_EXPR
+          && COMPARISON_CLASS_P (TREE_OPERAND (t, 0))
+          && integer_onep (TREE_OPERAND (t, 1))
+          && integer_zerop (TREE_OPERAND (t, 2)))
+    {
+      tree top0 = TREE_OPERAND (t, 0);
+      t = build2 (TREE_CODE (top0), type,
+                 TREE_OPERAND (top0, 0), TREE_OPERAND (top0, 1));
+    }
 
-      if (TREE_CODE (op0) == SSA_NAME && is_gimple_min_invariant (op1))
-       {
-         /* First see if we have test of an SSA_NAME against a constant
-            where the SSA_NAME is defined by an earlier typecast which
-            is irrelevant when performing tests against the given
-            constant.  */
-         if (TREE_CODE (cond) == EQ_EXPR || TREE_CODE (cond) == NE_EXPR)
-           {
-             tree new_cond = find_equivalent_equality_comparison (cond);
+  /* Bail out if we required an invariant but didn't get one.  */
+  if (invariant_only
+      && !is_gimple_min_invariant (t))
+    return NULL_TREE;
 
-             if (new_cond)
-               {
-                 COND_EXPR_COND (cond_expr) = new_cond;
-                 update_stmt (stmt);
-               }
-           }
-       }
-    }
+  /* A valid conditional for a COND_EXPR is either a gimple value
+     or a comparison with two gimple value operands.  */
+  if (is_gimple_val (t)
+      || (COMPARISON_CLASS_P (t)
+         && is_gimple_val (TREE_OPERAND (t, 0))
+         && is_gimple_val (TREE_OPERAND (t, 1))))
+    return t;
+
+  return NULL_TREE;
 }
 
-/* Forward propagate a single-use variable into COND_EXPR as many
-   times as possible.  */
+/* Propagate from the ssa name definition statements of COND_EXPR
+   in statement STMT into the conditional if that simplifies it.  */
 
 static void
 forward_propagate_into_cond (tree cond_expr, tree stmt)
 {
-  gcc_assert (TREE_CODE (cond_expr) == COND_EXPR);
-
-  while (1)
-    {
-      tree test_var = NULL_TREE;
-      tree cond = COND_EXPR_COND (cond_expr);
-      tree new_cond = forward_propagate_into_cond_1 (cond, &test_var);
-
-      /* Return if unsuccessful.  */
-      if (new_cond == NULL_TREE)
-       break;
-
-      /* Dump details.  */
-      if (dump_file && (dump_flags & TDF_DETAILS))
-       {
-         fprintf (dump_file, "  Replaced '");
-         print_generic_expr (dump_file, cond, dump_flags);
-         fprintf (dump_file, "' with '");
-         print_generic_expr (dump_file, new_cond, dump_flags);
-         fprintf (dump_file, "'\n");
-       }
-
-      COND_EXPR_COND (cond_expr) = new_cond;
-      update_stmt (stmt);
-
-      if (has_zero_uses (test_var))
-       {
-         tree def = SSA_NAME_DEF_STMT (test_var);
-         block_stmt_iterator bsi = bsi_for_stmt (def);
-         bsi_remove (&bsi, true);
-         release_defs (def);
-       }
-    }
+  do {
+    tree tmp = NULL_TREE;
+    tree cond = COND_EXPR_COND (cond_expr);
+    tree name, def_stmt, rhs;
+    bool single_use_p;
+
+    /* We can do tree combining on SSA_NAME and comparison expressions.  */
+    if (COMPARISON_CLASS_P (cond)
+       && TREE_CODE (TREE_OPERAND (cond, 0)) == SSA_NAME)
+      {
+       /* For comparisons use the first operand, that is likely to
+          simplify comparisons against constants.  */
+       name = TREE_OPERAND (cond, 0);
+       def_stmt = get_prop_source_stmt (name, false, &single_use_p);
+       if (def_stmt != NULL_TREE
+           && can_propagate_from (def_stmt))
+         {
+           tree op1 = TREE_OPERAND (cond, 1);
+           rhs = GIMPLE_STMT_OPERAND (def_stmt, 1);
+           tmp = combine_cond_expr_cond (TREE_CODE (cond), boolean_type_node,
+                                         fold_convert (TREE_TYPE (op1), rhs),
+                                         op1, !single_use_p);
+         }
+       /* If that wasn't successful, try the second operand.  */
+       if (tmp == NULL_TREE
+           && TREE_CODE (TREE_OPERAND (cond, 1)) == SSA_NAME)
+         {
+           tree op0 = TREE_OPERAND (cond, 0);
+           name = TREE_OPERAND (cond, 1);
+           def_stmt = get_prop_source_stmt (name, false, &single_use_p);
+           if (def_stmt == NULL_TREE
+               || !can_propagate_from (def_stmt))
+             return;
+
+           rhs = GIMPLE_STMT_OPERAND (def_stmt, 1);
+           tmp = combine_cond_expr_cond (TREE_CODE (cond), boolean_type_node,
+                                         op0,
+                                         fold_convert (TREE_TYPE (op0), rhs),
+                                         !single_use_p);
+         }
+      }
+    else if (TREE_CODE (cond) == SSA_NAME)
+      {
+       name = cond;
+       def_stmt = get_prop_source_stmt (name, true, NULL);
+       if (def_stmt == NULL_TREE
+           || !can_propagate_from (def_stmt))
+         return;
+
+       rhs = GIMPLE_STMT_OPERAND (def_stmt, 1);
+       tmp = combine_cond_expr_cond (NE_EXPR, boolean_type_node, rhs,
+                                     build_int_cst (TREE_TYPE (rhs), 0),
+                                     false);
+      }
+
+    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");
+         }
+
+       COND_EXPR_COND (cond_expr) = unshare_expr (tmp);
+       update_stmt (stmt);
+
+       /* Remove defining statements.  */
+       remove_prop_source_from_use (name, NULL);
+
+       /* Continue combining.  */
+       continue;
+      }
 
-  /* There are further simplifications that can be performed
-     on COND_EXPRs.  Specifically, when comparing an SSA_NAME
-     against a constant where the SSA_NAME is the result of a
-     conversion.  Perhaps this should be folded into the rest
-     of the COND_EXPR simplification code.  */
-  simplify_cond (cond_expr, stmt);
+    break;
+  } while (1);
 }
 
 /* We've just substituted an ADDR_EXPR into stmt.  Update all the 
@@ -861,6 +741,104 @@ forward_propagate_addr_expr (tree name, tree rhs)
   return all;
 }
 
+/* Forward propagate the comparison COND 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 (tree cond, tree stmt)
+{
+  tree name = GIMPLE_STMT_OPERAND (stmt, 0);
+  tree use_stmt, tmp = NULL_TREE;
+
+  /* Don't propagate ssa names that occur in abnormal phis.  */
+  if ((TREE_CODE (TREE_OPERAND (cond, 0)) == SSA_NAME
+       && SSA_NAME_OCCURS_IN_ABNORMAL_PHI (TREE_OPERAND (cond, 0)))
+      || (TREE_CODE (TREE_OPERAND (cond, 1)) == SSA_NAME
+         && SSA_NAME_OCCURS_IN_ABNORMAL_PHI (TREE_OPERAND (cond, 1))))
+    return false;
+
+  /* Do not un-cse comparisons.  But propagate through copies.  */
+  use_stmt = get_prop_dest_stmt (name, &name);
+  if (use_stmt == NULL_TREE)
+    return false;
+
+  /* Conversion of the condition result to another integral type.  */
+  if (TREE_CODE (use_stmt) == GIMPLE_MODIFY_STMT
+      && (TREE_CODE (GIMPLE_STMT_OPERAND (use_stmt, 1)) == CONVERT_EXPR
+         || TREE_CODE (GIMPLE_STMT_OPERAND (use_stmt, 1)) == NOP_EXPR
+          || COMPARISON_CLASS_P (GIMPLE_STMT_OPERAND (use_stmt, 1))
+          || TREE_CODE (GIMPLE_STMT_OPERAND (use_stmt, 1)) == TRUTH_NOT_EXPR)
+      && INTEGRAL_TYPE_P (TREE_TYPE (GIMPLE_STMT_OPERAND (use_stmt, 0))))
+    {
+      tree lhs = GIMPLE_STMT_OPERAND (use_stmt, 0);
+      tree rhs = GIMPLE_STMT_OPERAND (use_stmt, 1);
+
+      /* We can propagate the condition into a conversion.  */
+      if (TREE_CODE (rhs) == CONVERT_EXPR
+         || TREE_CODE (rhs) == NOP_EXPR)
+       {
+         /* Avoid using fold here as that may create a COND_EXPR with
+            non-boolean condition as canonical form.  */
+         tmp = build2 (TREE_CODE (cond), TREE_TYPE (lhs),
+                       TREE_OPERAND (cond, 0), TREE_OPERAND (cond, 1));
+       }
+      /* We can propagate the condition into X op CST where op
+        is EQ_EXRP or NE_EXPR and CST is either one or zero.  */
+      else if (COMPARISON_CLASS_P (rhs)
+              && TREE_CODE (TREE_OPERAND (rhs, 0)) == SSA_NAME
+              && TREE_CODE (TREE_OPERAND (rhs, 1)) == INTEGER_CST)
+       {
+         enum tree_code code = TREE_CODE (rhs);
+         tree cst = TREE_OPERAND (rhs, 1);
+
+         tmp = combine_cond_expr_cond (code, TREE_TYPE (lhs),
+                                       fold_convert (TREE_TYPE (cst), 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 (TREE_CODE (rhs) == TRUTH_NOT_EXPR)
+       {
+         tree type = TREE_TYPE (TREE_OPERAND (cond, 0));
+         bool nans = HONOR_NANS (TYPE_MODE (type));
+         enum tree_code code;
+         code = invert_tree_comparison (TREE_CODE (cond), nans);
+         if (code == ERROR_MARK)
+           return false;
+
+         tmp = build2 (code, TREE_TYPE (lhs), TREE_OPERAND (cond, 0),
+                       TREE_OPERAND (cond, 1));
+       }
+      else
+       return false;
+
+      GIMPLE_STMT_OPERAND (use_stmt, 1) = unshare_expr (tmp);
+      update_stmt (use_stmt);
+
+      /* Remove defining statements.  */
+      remove_prop_source_from_use (name, stmt);
+
+      if (dump_file && (dump_flags & TDF_DETAILS))
+       {
+         fprintf (dump_file, "  Replaced '");
+         print_generic_expr (dump_file, rhs, dump_flags);
+         fprintf (dump_file, "' with '");
+         print_generic_expr (dump_file, tmp, dump_flags);
+         fprintf (dump_file, "'\n");
+       }
+
+      return true;
+    }
+
+  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. 
@@ -1011,6 +989,17 @@ tree_ssa_forward_propagate_single_use_vars (void)
                   forward_propagate_into_cond (rhs, stmt);
                  bsi_next (&bsi);
                 }
+             else if (COMPARISON_CLASS_P (rhs))
+               {
+                 if (forward_propagate_comparison (rhs, stmt))
+                   {
+                     release_defs (stmt);
+                     todoflags |= TODO_remove_unused_locals;
+                     bsi_remove (&bsi, true);
+                   }
+                 else
+                   bsi_next (&bsi);
+               }
              else
                bsi_next (&bsi);
            }