Index: gcc/gcc/tree-ssa-forwprop.c
===================================================================
--- gcc.orig/gcc/tree-ssa-forwprop.c	2011-06-17 11:52:51.000000000 +0200
+++ gcc/gcc/tree-ssa-forwprop.c	2011-06-22 12:36:49.432774100 +0200
@@ -1682,10 +1682,11 @@ simplify_bitwise_binary (gimple_stmt_ite
   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 that it
-	 changes the operation to a bitfield precision.  */
+      /* 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)))
+	   <= TYPE_PRECISION (TREE_TYPE (arg1)))
 	  || (GET_MODE_CLASS (TYPE_MODE (TREE_TYPE (arg1)))
 	      != MODE_INT)
 	  || (TYPE_PRECISION (TREE_TYPE (arg1))
@@ -1704,6 +1705,88 @@ simplify_bitwise_binary (gimple_stmt_ite
       return true;
     }
 
+  /* Try to fold (TYPE) X op (Y CMP Z), or (TYPE) X op !Y, if type
+     of X is compatible to type of Y.  */
+  if (CONVERT_EXPR_CODE_P (def1_code)
+      && (TREE_CODE_CLASS (def2_code) == tcc_comparison
+	  || def2_code == TRUTH_NOT_EXPR)
+      && types_compatible_p (TREE_TYPE (def1_arg1), TREE_TYPE (def2_arg1)))
+    {
+      tree t;
+      if (def2_code == TRUTH_NOT_EXPR)
+	t = fold_build1_loc (gimple_location (def2), def2_code,
+			     TREE_TYPE (def1_arg1), def2_arg1);
+      else
+	t = fold_build2_loc (gimple_location (def2), def2_code,
+			     TREE_TYPE (def1_arg1), def2_arg1,
+			       gimple_assign_rhs2 (def2));
+      t = fold_binary_loc (gimple_location (stmt), code,
+			   TREE_TYPE (def1_arg1), def1_arg1, t);
+      if (t && TREE_CODE (t) == INTEGER_CST)
+	{
+	  t = fold_convert (TREE_TYPE (arg1), t);
+	  gimple_assign_set_rhs_from_tree (gsi, t);
+	  update_stmt (gsi_stmt (*gsi));
+	  return true;
+        }
+      else if (t && TREE_CODE_CLASS (TREE_CODE (t)) == tcc_comparison)
+	{
+	  gimple newop;
+	  tree tem = create_tmp_reg (boolean_type_node, NULL);
+	  newop = gimple_build_assign_with_ops (TREE_CODE (t), tem,
+	  					TREE_OPERAND (t, 0),
+	  					TREE_OPERAND (t, 1));
+	  tem = make_ssa_name (tem, newop);
+	  gimple_assign_set_lhs (newop, tem);
+	  gsi_insert_before (gsi, newop, GSI_SAME_STMT);
+	  gimple_assign_set_rhs_with_ops_1 (gsi, NOP_EXPR,
+					    tem, NULL_TREE, NULL_TREE);
+	  update_stmt (gsi_stmt (*gsi));
+	  return true;
+	}
+    }
+
+  /* Try to fold (Y CMP Z) op (TYPE) X, or !Y op (TYPE) X, if type
+     of X is compatible to type of Y.  */
+  if (CONVERT_EXPR_CODE_P (def2_code)
+      && (TREE_CODE_CLASS (def1_code) == tcc_comparison
+	  || def1_code == TRUTH_NOT_EXPR)
+      && types_compatible_p (TREE_TYPE (def2_arg1), TREE_TYPE (def1_arg1)))
+    {
+      tree t;
+      if (def1_code == TRUTH_NOT_EXPR)
+	t = fold_build1_loc (gimple_location (def1), def1_code,
+			     TREE_TYPE (def2_arg1), def1_arg1);
+      else
+	t = fold_build2_loc (gimple_location (def1), def1_code,
+			     TREE_TYPE (def2_arg1), def1_arg1,
+			       gimple_assign_rhs2 (def1));
+      t = fold_binary_loc (gimple_location (stmt), code,
+			   TREE_TYPE (def2_arg1), t, def2_arg1);
+      if (t && TREE_CODE (t) == INTEGER_CST)
+	{
+	  t = fold_convert (TREE_TYPE (arg2), t);
+	  gimple_assign_set_rhs_from_tree (gsi, t);
+	  update_stmt (gsi_stmt (*gsi));
+	  return true;
+        }
+      else if (t && TREE_CODE_CLASS (TREE_CODE (t)) == tcc_comparison)
+	{
+	  gimple newop;
+	  tree tem = create_tmp_reg (boolean_type_node, NULL);
+	  newop = gimple_build_assign_with_ops (TREE_CODE (t), tem,
+	  					TREE_OPERAND (t, 0),
+	  					TREE_OPERAND (t, 1));
+	  tem = make_ssa_name (tem, newop);
+	  gimple_assign_set_lhs (newop, tem);
+	  gsi_insert_before (gsi, newop, GSI_SAME_STMT);
+	  gimple_assign_set_rhs_with_ops_1 (gsi, NOP_EXPR,
+					    tem, NULL_TREE, NULL_TREE);
+	  update_stmt (gsi_stmt (*gsi));
+	  return true;
+	}
+    }
+
   /* (a | CST1) & CST2  ->  (a & CST2) | (CST1 & CST2).  */
   if (code == BIT_AND_EXPR
       && def1_code == BIT_IOR_EXPR
Index: gcc/gcc/testsuite/gcc.dg/binop-notand1a.c
===================================================================
--- /dev/null	1970-01-01 00:00:00.000000000 +0000
+++ gcc/gcc/testsuite/gcc.dg/binop-notand1a.c	2011-06-22 11:03:03.735901300 +0200
@@ -0,0 +1,11 @@
+/* { dg-do compile } */
+/* { dg-options "-O2 -fdump-tree-optimized" } */
+
+int
+foo (char a, unsigned short b)
+{
+  return (a & !a) | (b & !b);
+}
+
+/* { dg-final { scan-tree-dump-times "return 0" 1 "optimized" } } */
+/* { dg-final { cleanup-tree-dump "optimized" } } */
Index: gcc/gcc/testsuite/gcc.dg/binop-notand2a.c
===================================================================
--- /dev/null	1970-01-01 00:00:00.000000000 +0000
+++ gcc/gcc/testsuite/gcc.dg/binop-notand2a.c	2011-06-22 11:05:43.629705200 +0200
@@ -0,0 +1,11 @@
+/* { dg-do compile } */
+/* { dg-options "-O2 -fdump-tree-optimized" } */
+
+int
+foo (int a)
+{
+  return (!a & 1) != (a == 0);
+}
+
+/* { dg-final { scan-tree-dump-times "return 0" 1 "optimized" } } */
+/* { dg-final { cleanup-tree-dump "optimized" } } */
Index: gcc/gcc/testsuite/gcc.dg/binop-notand3a.c
===================================================================
--- /dev/null	1970-01-01 00:00:00.000000000 +0000
+++ gcc/gcc/testsuite/gcc.dg/binop-notand3a.c	2011-06-22 11:09:05.107289600 +0200
@@ -0,0 +1,11 @@
+/* { dg-do compile } */
+/* { dg-options "-O2 -fdump-tree-optimized" } */
+
+int
+foo (short a)
+{
+  return (!a & 1) != ((a == 0) & 1);
+}
+
+/* { dg-final { scan-tree-dump-times "return 0" 1 "optimized" } } */
+/* { dg-final { cleanup-tree-dump "optimized" } } */
Index: gcc/gcc/testsuite/gcc.dg/binop-notand4a.c
===================================================================
--- /dev/null	1970-01-01 00:00:00.000000000 +0000
+++ gcc/gcc/testsuite/gcc.dg/binop-notand4a.c	2011-06-22 11:09:43.515666900 +0200
@@ -0,0 +1,11 @@
+/* { dg-do compile } */
+/* { dg-options "-O2 -fdump-tree-optimized" } */
+
+int
+foo (unsigned char a, _Bool b)
+{
+  return (!a & a) | (b & !b);
+}
+
+/* { dg-final { scan-tree-dump-times "return 0" 1 "optimized" } } */
+/* { dg-final { cleanup-tree-dump "optimized" } } */
Index: gcc/gcc/testsuite/gcc.dg/binop-notand5a.c
===================================================================
--- /dev/null	1970-01-01 00:00:00.000000000 +0000
+++ gcc/gcc/testsuite/gcc.dg/binop-notand5a.c	2011-06-22 11:10:11.556727600 +0200
@@ -0,0 +1,11 @@
+/* { dg-do compile } */
+/* { dg-options "-O2 -fdump-tree-optimized" } */
+
+int
+foo (long a, unsigned long b)
+{
+  return (a & (a == 0)) | (b & !b);
+}
+
+/* { dg-final { scan-tree-dump-times "return 0" 1 "optimized" } } */
+/* { dg-final { cleanup-tree-dump "optimized" } } */
Index: gcc/gcc/testsuite/gcc.dg/binop-notand6a.c
===================================================================
--- /dev/null	1970-01-01 00:00:00.000000000 +0000
+++ gcc/gcc/testsuite/gcc.dg/binop-notand6a.c	2011-06-22 11:10:30.816673300 +0200
@@ -0,0 +1,11 @@
+/* { dg-do compile } */
+/* { dg-options "-O2 -fdump-tree-optimized" } */
+
+int
+foo (unsigned long a, long b)
+{
+  return (a & !a) | (b & (b == 0));
+}
+
+/* { dg-final { scan-tree-dump-times "return 0" 1 "optimized" } } */
+/* { dg-final { cleanup-tree-dump "optimized" } } */
