diff --git a/gcc/ChangeLog b/gcc/ChangeLog
index 13fad12d0f43c95ef572ca5aa6322b85e59866d1..ec210d638ddf16c05d8170f3d68637d8154e3d11 100644
--- a/gcc/ChangeLog
+++ b/gcc/ChangeLog
@@ -1,3 +1,11 @@
+2013-02-25  Richard Biener  <rguenther@suse.de>
+
+	PR tree-optimization/56175
+	* tree-ssa-forwprop.c (hoist_conversion_for_bitop_p): New predicate,
+	split out from ...
+	(simplify_bitwise_binary): ... here.  Also guard the conversion
+	of (type) X op CST to (type) (X op ((type-x) CST)) with it.
+
 2013-02-25  Catherine Moore  <clm@codesourcery.com>
 
 	Revert:
diff --git a/gcc/testsuite/ChangeLog b/gcc/testsuite/ChangeLog
index a33bed4e7f000de74e5de3eb960a05442c954bea..bd2b6d6a63dc4f5b6665449baf9b1a75efa2ac49 100644
--- a/gcc/testsuite/ChangeLog
+++ b/gcc/testsuite/ChangeLog
@@ -1,3 +1,8 @@
+2013-02-25  Richard Biener  <rguenther@suse.de>
+
+	PR tree-optimization/56175
+	* gcc.dg/tree-ssa/forwprop-24.c: New testcase.
+
 2013-02-24  Jakub Jelinek  <jakub@redhat.com>
 
 	PR c++/56403
diff --git a/gcc/testsuite/gcc.dg/tree-ssa/forwprop-24.c b/gcc/testsuite/gcc.dg/tree-ssa/forwprop-24.c
new file mode 100644
index 0000000000000000000000000000000000000000..74207cf1a812ea09cb4ffd53dbfe8e1146079f9f
--- /dev/null
+++ b/gcc/testsuite/gcc.dg/tree-ssa/forwprop-24.c
@@ -0,0 +1,18 @@
+/* { dg-do compile } */
+/* { dg-options "-O -fdump-tree-cddce1" } */
+
+void bar (void);
+unsigned short
+foo (unsigned char x, unsigned short y)
+{
+  unsigned char t = (unsigned char)((x & 1) ^ ((unsigned char)y & 1));
+  if (t == 1)
+    bar ();
+  return y;
+}
+
+/* We should have combined this to require only one bitwise and
+   as in (x ^ (char) y) & 1.  */
+
+/* { dg-final { scan-tree-dump-times " & " 1 "cddce1" } } */
+/* { dg-final { cleanup-tree-dump "cddce1" } } */
diff --git a/gcc/tree-ssa-forwprop.c b/gcc/tree-ssa-forwprop.c
index 855c212b0ca296fc38ff97a0b95de8b24dfdf755..edcf92918b7d597e704222cee4d447c3c2e9a6dd 100644
--- a/gcc/tree-ssa-forwprop.c
+++ b/gcc/tree-ssa-forwprop.c
@@ -1772,6 +1772,29 @@ defcodefor_name (tree name, enum tree_code *code, tree *arg1, tree *arg2)
   /* Ignore arg3 currently. */
 }
 
+/* Return true if a conversion of an operand from type FROM to type TO
+   should be applied after performing the operation instead.  */
+
+static bool
+hoist_conversion_for_bitop_p (tree to, tree from)
+{
+  /* That's a good idea if the conversion widens the operand, thus
+     after hoisting the conversion the operation will be narrower.  */
+  if (TYPE_PRECISION (from) < TYPE_PRECISION (to))
+    return true;
+
+  /* It's also a good idea if the conversion is to a non-integer mode.  */
+  if (GET_MODE_CLASS (TYPE_MODE (to)) != MODE_INT)
+    return true;
+
+  /* Or if the precision of TO is not the same as the precision
+     of its mode.  */
+  if (TYPE_PRECISION (to) != GET_MODE_PRECISION (TYPE_MODE (to)))
+    return true;
+
+  return false;
+}
+
 /* Simplify bitwise binary operations.
    Return true if a transformation applied, otherwise return false.  */
 
@@ -1789,9 +1812,11 @@ simplify_bitwise_binary (gimple_stmt_iterator *gsi)
   defcodefor_name (arg1, &def1_code, &def1_arg1, &def1_arg2);
   defcodefor_name (arg2, &def2_code, &def2_arg1, &def2_arg2);
 
-  /* Try to fold (type) X op CST -> (type) (X op ((type-x) CST)).  */
+  /* Try to fold (type) X op CST -> (type) (X op ((type-x) CST))
+     when profitable.  */
   if (TREE_CODE (arg2) == INTEGER_CST
       && CONVERT_EXPR_CODE_P (def1_code)
+      && hoist_conversion_for_bitop_p (TREE_TYPE (arg1), TREE_TYPE (def1_arg1))
       && INTEGRAL_TYPE_P (TREE_TYPE (def1_arg1))
       && int_fits_type_p (arg2, TREE_TYPE (def1_arg1)))
     {
@@ -1816,15 +1841,7 @@ simplify_bitwise_binary (gimple_stmt_iterator *gsi)
   if (CONVERT_EXPR_CODE_P (def1_code)
       && CONVERT_EXPR_CODE_P (def2_code)
       && types_compatible_p (TREE_TYPE (def1_arg1), TREE_TYPE (def2_arg1))
-      /* Make sure that the conversion widens the operands, or has same
-	 precision,  or that it changes the operation to a bitfield
-	 precision.  */
-      && ((TYPE_PRECISION (TREE_TYPE (def1_arg1))
-	   <= TYPE_PRECISION (TREE_TYPE (arg1)))
-	  || (GET_MODE_CLASS (TYPE_MODE (TREE_TYPE (arg1)))
-	      != MODE_INT)
-	  || (TYPE_PRECISION (TREE_TYPE (arg1))
-	      != GET_MODE_PRECISION (TYPE_MODE (TREE_TYPE (arg1))))))
+      && hoist_conversion_for_bitop_p (TREE_TYPE (arg1), TREE_TYPE (def1_arg1)))
     {
       gimple newop;
       tree tem = make_ssa_name (TREE_TYPE (def1_arg1), NULL);