Patchwork [tree-optimization] : Do bitwise operator optimizations for X op !X patterns

login
register
mail settings
Submitter Kai Tietz
Date June 29, 2011, 1 p.m.
Message ID <1271876452.839877.1309352401219.JavaMail.root@zmail06.collab.prod.int.phx2.redhat.com>
Download mbox | patch
Permalink /patch/102592/
State New
Headers show

Comments

Kai Tietz - June 29, 2011, 1 p.m.
----- Original Message -----
From: "Kai Tietz" <ktietz@redhat.com>
To: "Richard Guenther" <richard.guenther@gmail.com>
Cc: gcc-patches@gcc.gnu.org
Sent: Wednesday, June 29, 2011 1:33:30 PM
Subject: Re: [patch tree-optimization]: Do bitwise operator optimizations for X op !X patterns

----- Original Message -----
From: "Richard Guenther" <richard.guenther@gmail.com>
To: "Kai Tietz" <ktietz@redhat.com>
Cc: gcc-patches@gcc.gnu.org
Sent: Wednesday, June 29, 2011 12:14:10 PM
Subject: Re: [patch tree-optimization]: Do bitwise operator optimizations for X op !X patterns

On Tue, Jun 28, 2011 at 5:05 PM, Kai Tietz <ktietz@redhat.com> wrote:
> Hello,
>
> this patch implements the X op !X patterns within tree-ssa-forwprop.c without using here const-fold routines.  Additionally it does some trivial folding for X op X.  Implementation
> also looks through [(type)] X op [(type)] !X, if type of X is integral and precision is suitable
> for operation.
>
> ChangeLog gcc/
>
> 2011-06-28  Kai Tietz  <ktietz@redhat.com>
>
>        * tree-ssa-forwprop.c (operand_precision_onep): New
>        function.
>        (find_possible_not_expr_argument): Likewise.
>        (simplify_bitwise_binary_1): Likewise.
>        (simplify_bitwise_binary): Use simplify_bitwise_binary_1
>        for detecting various X op !X optimizations.
>
> ChangeLog gcc/testsuite
>
> 2011-06-28  Kai Tietz  <ktietz@redhat.com>
>
>        * gcc.dg/binop-notand1a.c: New test.
>        * gcc.dg/binop-notand2a.c: New test.
>        * gcc.dg/binop-notand3a.c: New test.
>        * gcc.dg/binop-notand4a.c: New test.
>        * gcc.dg/binop-notand5a.c: New test.
>        * gcc.dg/binop-notand6a.c: New test.
>        * gcc.dg/binop-notor1.c: New test.
>        * gcc.dg/binop-notor2.c: New test.
>        * gcc.dg/binop-notxor1.c: New test.
>        * gcc.dg/binop-notxor2.c: New test.
>
> Bootstrapped and regression tested for all languages plus Ada and Obj-C for x86_64-pc-linux-gnu. Ok for apply?

I can't follow the code in find_possible_not_expr_argument or its uses
at all.  Please try to produce patches that look more obvious in what
they are doing - don't try to solve every testcase you can come up with
in a single patch.  Especially don't write functions like
find_possible_not_expr_argument which seems to have evolved a lot
after you wrote the overall function comment.

Thanks,
Richard.

> Regards,
> Kai
>

Well, I added some comments to these functions and renamed the find_possible_not_expr_argument function to detect_not_expr_operand, which hits its use better.
The cause for this function is, that there are more then one variant of expressing a logical-not and all of them are used.
This routine simply tries to detect different variants used for not. Eg ~X == !X and (X ^ 1) == !X for integral type of X with precision one. For X with integral type, (X == 0) == !X.

The folding for the three different bitwise-operations is pretty easy and it makes sense to implement them at once.  I see here no good point to separate them into different patches.  To separate them might even lead to questions about abstracting some code-pieces out of the main function.
I didn't added testcases for all variants I am aware now. Just those, which are now handled.

So hope you can read and understand logic of patch better by updated patch.

Regards,
Kai

I found that in version I've sent there is an unclosed comment.  So here is updated patch, which additionally simplify some code to ease reading.

Regards,
Kai
Richard Guenther - June 30, 2011, 11:36 a.m.
On Wed, Jun 29, 2011 at 3:00 PM, Kai Tietz <ktietz@redhat.com> wrote:
> ----- Original Message -----
> From: "Kai Tietz" <ktietz@redhat.com>
> To: "Richard Guenther" <richard.guenther@gmail.com>
> Cc: gcc-patches@gcc.gnu.org
> Sent: Wednesday, June 29, 2011 1:33:30 PM
> Subject: Re: [patch tree-optimization]: Do bitwise operator optimizations for X op !X patterns
>
> ----- Original Message -----
> From: "Richard Guenther" <richard.guenther@gmail.com>
> To: "Kai Tietz" <ktietz@redhat.com>
> Cc: gcc-patches@gcc.gnu.org
> Sent: Wednesday, June 29, 2011 12:14:10 PM
> Subject: Re: [patch tree-optimization]: Do bitwise operator optimizations for X op !X patterns
>
> On Tue, Jun 28, 2011 at 5:05 PM, Kai Tietz <ktietz@redhat.com> wrote:
>> Hello,
>>
>> this patch implements the X op !X patterns within tree-ssa-forwprop.c without using here const-fold routines.  Additionally it does some trivial folding for X op X.  Implementation
>> also looks through [(type)] X op [(type)] !X, if type of X is integral and precision is suitable
>> for operation.
>>
>> ChangeLog gcc/
>>
>> 2011-06-28  Kai Tietz  <ktietz@redhat.com>
>>
>>        * tree-ssa-forwprop.c (operand_precision_onep): New
>>        function.
>>        (find_possible_not_expr_argument): Likewise.
>>        (simplify_bitwise_binary_1): Likewise.
>>        (simplify_bitwise_binary): Use simplify_bitwise_binary_1
>>        for detecting various X op !X optimizations.
>>
>> ChangeLog gcc/testsuite
>>
>> 2011-06-28  Kai Tietz  <ktietz@redhat.com>
>>
>>        * gcc.dg/binop-notand1a.c: New test.
>>        * gcc.dg/binop-notand2a.c: New test.
>>        * gcc.dg/binop-notand3a.c: New test.
>>        * gcc.dg/binop-notand4a.c: New test.
>>        * gcc.dg/binop-notand5a.c: New test.
>>        * gcc.dg/binop-notand6a.c: New test.
>>        * gcc.dg/binop-notor1.c: New test.
>>        * gcc.dg/binop-notor2.c: New test.
>>        * gcc.dg/binop-notxor1.c: New test.
>>        * gcc.dg/binop-notxor2.c: New test.
>>
>> Bootstrapped and regression tested for all languages plus Ada and Obj-C for x86_64-pc-linux-gnu. Ok for apply?
>
> I can't follow the code in find_possible_not_expr_argument or its uses
> at all.  Please try to produce patches that look more obvious in what
> they are doing - don't try to solve every testcase you can come up with
> in a single patch.  Especially don't write functions like
> find_possible_not_expr_argument which seems to have evolved a lot
> after you wrote the overall function comment.
>
> Thanks,
> Richard.
>
>> Regards,
>> Kai
>>
>
> Well, I added some comments to these functions and renamed the find_possible_not_expr_argument function to detect_not_expr_operand, which hits its use better.
> The cause for this function is, that there are more then one variant of expressing a logical-not and all of them are used.
> This routine simply tries to detect different variants used for not. Eg ~X == !X and (X ^ 1) == !X for integral type of X with precision one. For X with integral type, (X == 0) == !X.
>
> The folding for the three different bitwise-operations is pretty easy and it makes sense to implement them at once.  I see here no good point to separate them into different patches.  To separate them might even lead to questions about abstracting some code-pieces out of the main function.
> I didn't added testcases for all variants I am aware now. Just those, which are now handled.
>
> So hope you can read and understand logic of patch better by updated patch.
>
> Regards,
> Kai
>
> I found that in version I've sent there is an unclosed comment.  So here is updated patch, which additionally simplify some code to ease reading.

Ok, I'm going to comment on a few things in the patch.

+/* Checks if expression has type of one-bit precision, or is result of
+   a known boolean expression.  */
+static bool
+operand_precision_onep (tree expr)
+{
+  enum tree_code code;
+  gimple def_stmt;
+
+  do
+    {
+      code = TREE_CODE (expr);
+      if (!INTEGRAL_TYPE_P (TREE_TYPE (expr)))
+	return false;
+      if (TYPE_PRECISION (TREE_TYPE (expr)) == 1)
+	return true;
+      if (code != SSA_NAME)
+	break;
+      def_stmt = SSA_NAME_DEF_STMT (expr);
+      if (!def_stmt || !is_gimple_assign (def_stmt))
+	break;
+      code = gimple_assign_rhs_code (def_stmt);
+      if (!CONVERT_EXPR_CODE_P (code))
+	break;
+      expr = gimple_assign_rhs1 (def_stmt);
+    }
+  while (CONVERT_EXPR_CODE_P (code));
+
+  if (code == TRUTH_NOT_EXPR || TREE_CODE_CLASS (code) == tcc_comparison)
+    return true;
+  return false;
+}

Please don't do arbitrary deep lookups like this.  Instead this
function should be

bool
truth_valued_ssa_name_p (tree name)
{
  tree type = TREE_TYPE (name);
  gimple def_stmt;

  if (!INTEGRAL_TYPE_P (type))
    return false;
  if (TREE_CODE (type) == BOOLEAN_TYPE
      || TYPE_PRECISION (type) == 1)
    return true;

  def_stmt = SSA_NAME_DEF_STMT (name);
  if (is_gimple_assign (def_stmt))
    return truth_value_p (gimple_assign_rhs_code (def_stmt));
  return false;
}

+static tree
+detect_not_expr_operand (tree name, tree *nexpr)

same, don't do arbitrary deep lookups.  Do simple matches by
enumerating them.  The code is not followable or easily verifiable
for correctness.  Look at how all the code in fold-const.c is written - it's
very easy to follow what is matched and what is produced.  This is not
at all the case for your code.

Richard.

> Regards,
> Kai
>

Patch

Index: gcc-head/gcc/tree-ssa-forwprop.c
===================================================================
--- gcc-head.orig/gcc/tree-ssa-forwprop.c
+++ gcc-head/gcc/tree-ssa-forwprop.c
@@ -1674,6 +1674,223 @@  simplify_builtin_call (gimple_stmt_itera
   return false;
 }
 
+/* Checks if expression has type of one-bit precision, or is result of
+   a known boolean expression.  */
+static bool
+operand_precision_onep (tree expr)
+{
+  enum tree_code code;
+  gimple def_stmt;
+
+  do
+    {
+      code = TREE_CODE (expr);
+      if (!INTEGRAL_TYPE_P (TREE_TYPE (expr)))
+	return false;
+      if (TYPE_PRECISION (TREE_TYPE (expr)) == 1)
+	return true;
+      if (code != SSA_NAME)
+	break;
+      def_stmt = SSA_NAME_DEF_STMT (expr);
+      if (!def_stmt || !is_gimple_assign (def_stmt))
+	break;
+      code = gimple_assign_rhs_code (def_stmt);
+      if (!CONVERT_EXPR_CODE_P (code))
+	break;
+      expr = gimple_assign_rhs1 (def_stmt);
+    }
+  while (CONVERT_EXPR_CODE_P (code));
+
+  if (code == TRUTH_NOT_EXPR || TREE_CODE_CLASS (code) == tcc_comparison)
+    return true;
+  return false;
+}
+
+/* Helper routine for simplify_bitwise_binary_1 function.
+   If a NOT-expression is found, the operand of the NOT-expression is
+   stored in NEXPR.
+   This function returns either the expression after the first
+   integral cast expression, or NAME.
+   Detected not-patterns are !X or X == 0 for X with integral type, and
+   X ^ 1 or ~X for X with integral type with precision of one.
+   The value of CNT_CASTS is either zero, or one.   */
+static tree
+detect_not_expr_operand (tree name, tree *nexpr)
+{
+  enum tree_code code = ERROR_MARK;
+  int seen_cast = 0;
+
+  *nexpr = NULL_TREE;
+
+  while (1)
+    {
+      tree op1, op2;
+      gimple def_stmt;
+      code = TREE_CODE (name);
+      /* If name has none-intergal type, or isn't a SSA_NAME, then
+	 stop search.  */
+      if (code != SSA_NAME || !INTEGRAL_TYPE_P (TREE_TYPE (name)))
+	break;
+      def_stmt = SSA_NAME_DEF_STMT (name);
+      if (!def_stmt || !is_gimple_assign (def_stmt))
+	 break;
+
+      code = gimple_assign_rhs_code (def_stmt);
+      op1 = gimple_assign_rhs1 (def_stmt);
+      op2 = NULL_TREE;
+
+      /* Is expression a conversion?.  */
+      if (CONVERT_EXPR_CODE_P (code))
+	{
+	  /* Just allow sinking over one cast and this only for
+	     integeral types.  Otherwise value truncation
+	     or intermediate float conversions need to be
+	     considered.  */
+	  if (seen_cast)
+	    break;
+	  ++seen_cast;
+	  name = op1;
+	  continue;
+	}
+
+      /* Get for EQ_EXPR or BIT_XOR_EXPR operation the second operand.
+	 If CODE isn't an EQ_EXPR, BIT_XOR_EXPR, TRUTH_NOT_EXPR,
+	 or BIT_NOT_EXPR, then break search.  */
+      if (code == EQ_EXPR || code == BIT_XOR_EXPR)
+	op2 = gimple_assign_rhs2 (def_stmt);
+      else if (code != TRUTH_NOT_EXPR
+	       && code != BIT_NOT_EXPR)
+	 break;
+      /* If !X or ~X is found, set NEXPR and break search.  */
+      if (code == TRUTH_NOT_EXPR
+          || (code == BIT_NOT_EXPR
+	      && INTEGRAL_TYPE_P (name)
+	      && operand_precision_onep (name)))
+	*nexpr = op1;
+      /* If X == 0 or X ^ 1 is found, set NEXPR and break search.  */
+      else if (code == EQ_EXPR || code == BIT_XOR_EXPR)
+        {
+	  if (!INTEGRAL_TYPE_P (TREE_TYPE (op1)))
+	    break;
+	  if (code == EQ_EXPR
+	      && TREE_CODE (op2) == INTEGER_CST
+	      && integer_zerop (op2))
+	    *nexpr = op1;
+	  else if (code == EQ_EXPR
+		   && TREE_CODE (op1) == INTEGER_CST
+		   && integer_zerop (op1))
+	    *nexpr = op2;
+	  else if (code == BIT_XOR_EXPR
+		   && operand_precision_onep (op1)
+		   && TREE_CODE (op2) == INTEGER_CST
+		   && integer_onep (op2))
+	    *nexpr = op1;
+	}
+      break;
+    }
+
+  return name;
+}
+
+/* Try to optimize patterns X & not(X) -> zero, X | not(X) -> one, and
+   X ^ not(X) -> one, if type of X is valid for this.
+   Additional handle the variants X & X -> X, X | X -> X, and X ^ X -> zero.
+   We also pattern match optional integral type casts on operation's
+   operands.
+
+   See for detected not-patterns the detect_not_expr_operand function.  */
+static tree
+simplify_bitwise_binary_1 (enum tree_code code, tree arg1,
+			   tree arg2)
+{
+  tree a1strip, a1expr;
+  tree a2strip, a2expr;
+  tree op = NULL_TREE;
+
+  /* If CODE isn't a bitwise binary operation, return NULL_TREE.  */
+  if (code != BIT_AND_EXPR && code != BIT_IOR_EXPR
+      && code != BIT_XOR_EXPR)
+    return NULL_TREE;
+
+  /* First check if operands ARG1 and ARG2 are equal, if so we
+     won't have a not pattern match.  Fold these patterns, as
+     we have detected it already.  */
+  if (operand_equal_p (arg1, arg2, 0))
+    {
+      /* X & X -> X, and X | X -> X.  */
+      if (code == BIT_AND_EXPR || code == BIT_IOR_EXPR)
+        return arg1;
+      /* X ^ X -> 0.  */
+      return integer_zero_node;
+    }
+  /* Get for each operation operand its optional by one integral typed
+     cast stripped argument. And get the not-expression's operand, if
+     argument represents an not-expression.  */
+  a1strip = detect_not_expr_operand (arg1, &a1expr);
+  a2strip = detect_not_expr_operand (arg2, &a2expr);
+
+  /* If there are no not-expressions found,  return NULL_TREE. */
+  if (!a1expr && !a2expr)
+    return NULL_TREE;
+
+  /* Do we have case not(X) op not(X)?  */
+  if (a1expr && a2expr)
+    {
+      /* Check if operands not(ARG1) and not(ARG2) are equal and
+	 fold them if so.  */
+      if (operand_equal_p (a1expr, a2expr, 0))
+	{
+	  /* !X & !X -> !X, and !X | i!X -> !X.  */
+	  if (code == BIT_AND_EXPR || code == BIT_IOR_EXPR)
+	    return arg1;
+	  /* !X ^ !X -> 0.  */
+	  return integer_zero_node;
+	}
+      return NULL_TREE;
+    }
+
+  if (a2expr)
+    {
+      /* Check if X is equal for cases X CODE not(X), or
+	 (type) X not(X).  */
+      if (operand_equal_p (arg1, a2expr, 0)
+          || operand_equal_p (a1strip, a2expr, 0))
+        op = arg1;
+    }
+  /* If haven't found already a match, then see if we have
+     equal X for cases not(X) op X, or (not(X) op (type)X.  */
+  if (!op && a1expr)
+    {
+      if (operand_equal_p (arg2, a1expr, 0)
+          || operand_equal_p (a2strip, a1expr, 0))
+        op = arg2;
+    }
+  /* Did we found no match, return NULL_TREE.  */
+  if (!op)
+    return NULL_TREE;
+
+  /* We have found a X op not(X) operation. */
+  if (code == BIT_AND_EXPR)
+    return integer_zero_node;
+  else if (code == BIT_IOR_EXPR)
+    {
+      /* If X has type precision of one, then result is 1.  */
+      if (operand_precision_onep (op))
+	return integer_one_node;
+
+      /* ??? Otherwise result is (X != 0 ? X : 1).  not handled.  */
+    }
+  else
+    {
+      /* Result for XOR for X with type precision of one is 1.  */
+      if (operand_precision_onep (op))
+	return integer_one_node;
+
+      /* ??? Otherwise result is (X != 0 ? X : 1.  not handled.  */
+    }
+  return NULL_TREE;
+}
+
 /* Simplify bitwise binary operations.
    Return true if a transformation applied, otherwise return false.  */
 
@@ -1840,7 +2057,31 @@  simplify_bitwise_binary (gimple_stmt_ite
       update_stmt (stmt);
       return true;
     }
+  /* Try simple folding for X op !X, and X op X.  */
+  res = simplify_bitwise_binary_1 (code, arg1, arg2);
+  if (res != NULL_TREE && TREE_CODE (res) == INTEGER_CST)
+    {
+      res = fold_convert_loc (gimple_location (stmt),
+			      TREE_TYPE (arg1), res);
+      gimple_assign_set_rhs_from_tree (gsi, res);
+      update_stmt (gsi_stmt (*gsi));
+      return true;
+    }
+  else if (res)
+    {
+      if (!useless_type_conversion_p (TREE_TYPE (arg1), TREE_TYPE (res)))
+	{
+	  res = force_gimple_operand_gsi (gsi, res, true, NULL_TREE, true,
+					  GSI_SAME_STMT);
 
+	  gimple_assign_set_rhs_with_ops_1 (gsi, NOP_EXPR,
+					    res, NULL_TREE, NULL_TREE);
+	}
+      else
+	gimple_assign_set_rhs_from_tree (gsi, res);
+      update_stmt (gsi_stmt (*gsi));
+      return true;
+    }
   return false;
 }
 
Index: gcc-head/gcc/testsuite/gcc.dg/binop-notand1a.c
===================================================================
--- /dev/null
+++ gcc-head/gcc/testsuite/gcc.dg/binop-notand1a.c
@@ -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-head/gcc/testsuite/gcc.dg/binop-notand2a.c
===================================================================
--- /dev/null
+++ gcc-head/gcc/testsuite/gcc.dg/binop-notand2a.c
@@ -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-head/gcc/testsuite/gcc.dg/binop-notand3a.c
===================================================================
--- /dev/null
+++ gcc-head/gcc/testsuite/gcc.dg/binop-notand3a.c
@@ -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-head/gcc/testsuite/gcc.dg/binop-notand4a.c
===================================================================
--- /dev/null
+++ gcc-head/gcc/testsuite/gcc.dg/binop-notand4a.c
@@ -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-head/gcc/testsuite/gcc.dg/binop-notand5a.c
===================================================================
--- /dev/null
+++ gcc-head/gcc/testsuite/gcc.dg/binop-notand5a.c
@@ -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-head/gcc/testsuite/gcc.dg/binop-notand6a.c
===================================================================
--- /dev/null
+++ gcc-head/gcc/testsuite/gcc.dg/binop-notand6a.c
@@ -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" } } */
Index: gcc-head/gcc/testsuite/gcc.dg/binop-notor1.c
===================================================================
--- /dev/null
+++ gcc-head/gcc/testsuite/gcc.dg/binop-notor1.c
@@ -0,0 +1,11 @@ 
+/* { dg-do compile } */
+/* { dg-options "-O2 -fdump-tree-optimized" } */
+
+int
+foo (_Bool a, _Bool b)
+{
+  return (a | !a) | (!b | b);
+}
+
+/* { dg-final { scan-tree-dump-times "return 1" 1 "optimized" } } */
+/* { dg-final { cleanup-tree-dump "optimized" } } */
Index: gcc-head/gcc/testsuite/gcc.dg/binop-notor2.c
===================================================================
--- /dev/null
+++ gcc-head/gcc/testsuite/gcc.dg/binop-notor2.c
@@ -0,0 +1,11 @@ 
+/* { dg-do compile } */
+/* { dg-options "-O2 -fdump-tree-optimized" } */
+
+int
+foo (_Bool a, _Bool b)
+{
+  return (a | (a == 0)) | ((b ^ 1) | b);
+}
+
+/* { dg-final { scan-tree-dump-times "return 1" 1 "optimized" } } */
+/* { dg-final { cleanup-tree-dump "optimized" } } */
Index: gcc-head/gcc/testsuite/gcc.dg/binop-notxor1.c
===================================================================
--- /dev/null
+++ gcc-head/gcc/testsuite/gcc.dg/binop-notxor1.c
@@ -0,0 +1,11 @@ 
+/* { dg-do compile } */
+/* { dg-options "-O2 -fdump-tree-optimized" } */
+
+int
+foo (_Bool a, _Bool b)
+{
+  return (a ^ !a) | (!b ^ b);
+}
+
+/* { dg-final { scan-tree-dump-times "return 1" 1 "optimized" } } */
+/* { dg-final { cleanup-tree-dump "optimized" } } */
Index: gcc-head/gcc/testsuite/gcc.dg/binop-notxor2.c
===================================================================
--- /dev/null
+++ gcc-head/gcc/testsuite/gcc.dg/binop-notxor2.c
@@ -0,0 +1,11 @@ 
+/* { dg-do compile } */
+/* { dg-options "-O2 -fdump-tree-optimized" } */
+
+int
+foo (_Bool a, _Bool b)
+{
+  return (a ^ (a == 0)) | ((b == 0) ^ b);
+}
+
+/* { dg-final { scan-tree-dump-times "return 1" 1 "optimized" } } */
+/* { dg-final { cleanup-tree-dump "optimized" } } */