This is the mail archive of the gcc-patches@gcc.gnu.org mailing list for the GCC project.


Index Nav: [Date Index] [Subject Index] [Author Index] [Thread Index]
Message Nav: [Date Prev] [Date Next] [Thread Prev] [Thread Next]
Other format: [Raw text]

[Committed] Fold (X & Y) ^ Y and (X ^ Y) & Y as ~X & Y


Many thanks to Hakan Hjort to pointing out a generalization of a
constant folding transformation that I recently added to fold_binary.
I'd added some optimizations to transform "((X ^ 4) & 4) == 0" into
"(X & 4) != 0" where the constants under test were single bits.
Hakan pointed out that this is a special case, of the more general
transformation that "(X ^ Y) & Y" is "~X & Y" for any Y, and
likewise that "(X & Y) ^ Y" is also "~X & Y".  The total number of
instructions on most architectures isn't changed, but the middle-end
prefers/canonicalizes unary operators over binary operators, which
in turn potentially enables more transformations and can reduce
register usage.

The following patch has been tested on x86_64-unknown-linux-gnu, with
a full "make bootstrap", all default languages, and regression tested
with a top-level "make -k check" with no new failures.

Committed to mainline as revision 112459.



2006-03-28  Roger Sayle  <roger@eyesopen.com>

	* fold-const.c (fold_binary) <BIT_XOR_EXPR>: Fold (X & Y) ^ Y as
	the equivalent ~X & Y, and the symmetry related transformations.
	(fold_binary) <BIT_AND_EXPR>: Similarly, fold (X ^ Y) & Y as
	~X & Y, and symmetry related transforms.

	* gcc.dg/fold-andxor-1.c: New test case.
	* gcc.dg/fold-xorand-1.c: Likewise.


Index: fold-const.c
===================================================================
*** fold-const.c	(revision 112052)
--- fold-const.c	(working copy)
*************** fold_binary (enum tree_code code, tree t
*** 8786,8791 ****
--- 8786,8830 ----
  	return fold_build2 (EQ_EXPR, type, arg0,
  			    build_int_cst (TREE_TYPE (arg0), 0));

+       /* Fold (X & Y) ^ Y as ~X & Y.  */
+       if (TREE_CODE (arg0) == BIT_AND_EXPR
+ 	  && operand_equal_p (TREE_OPERAND (arg0, 1), arg1, 0))
+ 	{
+ 	  tem = fold_convert (type, TREE_OPERAND (arg0, 0));
+ 	  return fold_build2 (BIT_AND_EXPR, type,
+ 			      fold_build1 (BIT_NOT_EXPR, type, tem),
+ 			      fold_convert (type, arg1));
+ 	}
+       /* Fold (X & Y) ^ X as ~Y & X.  */
+       if (TREE_CODE (arg0) == BIT_AND_EXPR
+ 	  && operand_equal_p (TREE_OPERAND (arg0, 0), arg1, 0)
+ 	  && reorder_operands_p (TREE_OPERAND (arg0, 1), arg1))
+ 	{
+ 	  tem = fold_convert (type, TREE_OPERAND (arg0, 1));
+ 	  return fold_build2 (BIT_AND_EXPR, type,
+ 			      fold_build1 (BIT_NOT_EXPR, type, tem),
+ 			      fold_convert (type, arg1));
+ 	}
+       /* Fold X ^ (X & Y) as X & ~Y.  */
+       if (TREE_CODE (arg1) == BIT_AND_EXPR
+ 	  && operand_equal_p (arg0, TREE_OPERAND (arg1, 0), 0))
+ 	{
+ 	  tem = fold_convert (type, TREE_OPERAND (arg1, 1));
+ 	  return fold_build2 (BIT_AND_EXPR, type,
+ 			      fold_convert (type, arg0),
+ 			      fold_build1 (BIT_NOT_EXPR, type, tem));
+ 	}
+       /* Fold X ^ (Y & X) as ~Y & X.  */
+       if (TREE_CODE (arg1) == BIT_AND_EXPR
+ 	  && operand_equal_p (arg0, TREE_OPERAND (arg1, 1), 0)
+ 	  && reorder_operands_p (arg0, TREE_OPERAND (arg1, 0)))
+ 	{
+ 	  tem = fold_convert (type, TREE_OPERAND (arg1, 0));
+ 	  return fold_build2 (BIT_AND_EXPR, type,
+ 			      fold_build1 (BIT_NOT_EXPR, type, tem),
+ 			      fold_convert (type, arg0));
+ 	}
+
        /* See if this can be simplified into a rotate first.  If that
  	 is unsuccessful continue in the association code.  */
        goto bit_rotate;
*************** fold_binary (enum tree_code code, tree t
*** 8860,8865 ****
--- 8899,8943 ----
  			      build_int_cst (TREE_TYPE (tem), 0));
  	}

+       /* Fold (X ^ Y) & Y as ~X & Y.  */
+       if (TREE_CODE (arg0) == BIT_XOR_EXPR
+ 	  && operand_equal_p (TREE_OPERAND (arg0, 1), arg1, 0))
+ 	{
+ 	  tem = fold_convert (type, TREE_OPERAND (arg0, 0));
+ 	  return fold_build2 (BIT_AND_EXPR, type,
+ 			      fold_build1 (BIT_NOT_EXPR, type, tem),
+ 			      fold_convert (type, arg1));
+ 	}
+       /* Fold (X ^ Y) & X as ~Y & X.  */
+       if (TREE_CODE (arg0) == BIT_XOR_EXPR
+ 	  && operand_equal_p (TREE_OPERAND (arg0, 0), arg1, 0)
+ 	  && reorder_operands_p (TREE_OPERAND (arg0, 1), arg1))
+ 	{
+ 	  tem = fold_convert (type, TREE_OPERAND (arg0, 1));
+ 	  return fold_build2 (BIT_AND_EXPR, type,
+ 			      fold_build1 (BIT_NOT_EXPR, type, tem),
+ 			      fold_convert (type, arg1));
+ 	}
+       /* Fold X & (X ^ Y) as X & ~Y.  */
+       if (TREE_CODE (arg1) == BIT_XOR_EXPR
+ 	  && operand_equal_p (arg0, TREE_OPERAND (arg1, 0), 0))
+ 	{
+ 	  tem = fold_convert (type, TREE_OPERAND (arg1, 1));
+ 	  return fold_build2 (BIT_AND_EXPR, type,
+ 			      fold_convert (type, arg0),
+ 			      fold_build1 (BIT_NOT_EXPR, type, tem));
+ 	}
+       /* Fold X & (Y ^ X) as ~Y & X.  */
+       if (TREE_CODE (arg1) == BIT_XOR_EXPR
+ 	  && operand_equal_p (arg0, TREE_OPERAND (arg1, 1), 0)
+ 	  && reorder_operands_p (arg0, TREE_OPERAND (arg1, 0)))
+ 	{
+ 	  tem = fold_convert (type, TREE_OPERAND (arg1, 0));
+ 	  return fold_build2 (BIT_AND_EXPR, type,
+ 			      fold_build1 (BIT_NOT_EXPR, type, tem),
+ 			      fold_convert (type, arg0));
+ 	}
+
        t1 = distribute_bit_expr (code, type, arg0, arg1);
        if (t1 != NULL_TREE)
  	return t1;


/* { dg-do compile } */
/* { dg-options "-O2 -fdump-tree-original" } */

int test1(int a, int b)
{
  return (a ^ b) & a;
}

int test2(int c, int d)
{
  return (c ^ d) & d;
}

int test3(int e, int f)
{
  return e & (e ^ f);
}

int test4(int g, int h)
{
  return g & (h ^ g);
}

/* { dg-final { scan-tree-dump-times "~b \& a" 1 "original" } } */
/* { dg-final { scan-tree-dump-times "~c \& d" 1 "original" } } */
/* { dg-final { scan-tree-dump-times "~f \& e" 1 "original" } } */
/* { dg-final { scan-tree-dump-times "~h \& g" 1 "original" } } */
/* { dg-final { cleanup-tree-dump "original" } } */


/* { dg-do compile } */
/* { dg-options "-O2 -fdump-tree-original" } */

int test1(int a, int b)
{
  return (a & b) ^ a;
}

int test2(int c, int d)
{
  return (c & d) ^ d;
}

int test3(int e, int f)
{
  return e ^ (e & f);
}

int test4(int g, int h)
{
  return g ^ (h & g);
}

/* { dg-final { scan-tree-dump-times "~b \& a" 1 "original" } } */
/* { dg-final { scan-tree-dump-times "~c \& d" 1 "original" } } */
/* { dg-final { scan-tree-dump-times "~f \& e" 1 "original" } } */
/* { dg-final { scan-tree-dump-times "~h \& g" 1 "original" } } */
/* { dg-final { cleanup-tree-dump "original" } } */


Roger
--


Index Nav: [Date Index] [Subject Index] [Author Index] [Thread Index]
Message Nav: [Date Prev] [Date Next] [Thread Prev] [Thread Next]