===================================================================
@@ -1674,6 +1674,189 @@ 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;
+
+ 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)
+ {
+ gimple 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);
+ }
+ else
+ break;
+ }
+ 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 for counting
+ ignored type casts in CNT_CASTS, and finding possible match
+ returned in NEXPR. This function returns the first expression
+ not being a cast. */
+static tree
+find_possible_not_expr_argument (tree name, int *cnt_casts, tree *nexpr)
+{
+ enum tree_code code = ERROR_MARK;
+ *cnt_casts = 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;
+ if (code == EQ_EXPR || code == BIT_XOR_EXPR)
+ op2 = gimple_assign_rhs2 (def_stmt);
+ else if (!CONVERT_EXPR_CODE_P (code)
+ && code != TRUTH_NOT_EXPR)
+ break;
+ if (code == TRUTH_NOT_EXPR
+ || (code == BIT_NOT_EXPR
+ && INTEGRAL_TYPE_P (name)
+ && operand_precision_onep (name)))
+ {
+ *nexpr = op1;
+ break;
+ }
+ 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;
+ else if (code == BIT_XOR_EXPR
+ && operand_precision_onep (op2)
+ && TREE_CODE (op1) == INTEGER_CST
+ && integer_onep (op1))
+ *nexpr = op2;
+ break;
+ }
+ else if (!CONVERT_EXPR_CODE_P (code))
+ break;
+ /* Just allow sinking over one cast and this only for
+ integeral types. Otherwise value truncation
+ or intermediate float conversions need to be
+ considered. */
+ if (cnt_casts[0] != 0)
+ break;
+ cnt_casts[0] += 1;
+ name = op1;
+ }
+ return name;
+}
+
+/* Try to optimize patterns X & !X -> zero, X | !X -> one, and X ^ !X -> one,
+ if type of X allows this. Additional handle the variants X & X -> X,
+ X | X -> X, and X ^ X -> zero.
+ For integral type-casts we also pattern match (type) X op !X,
+ (type) X op (type) !X, and X op (type) !X sub-variants. */
+static tree
+simplify_bitwise_binary_1 (enum tree_code code, tree arg1, tree arg2)
+{
+ int a1cnt = 0, a2cnt = 0;
+ tree a1strip, a1expr;
+ tree a2strip, a2expr;
+ tree op = NULL_TREE;
+
+ if (code != BIT_AND_EXPR && code != BIT_IOR_EXPR
+ && code != BIT_XOR_EXPR)
+ return NULL_TREE;
+
+ if (operand_equal_p (arg1, arg2, 0))
+ {
+ if (code == BIT_AND_EXPR || code == BIT_IOR_EXPR)
+ return arg1;
+ return integer_zero_node;
+ }
+ a1strip = find_possible_not_expr_argument (arg1, &a1cnt, &a1expr);
+ a2strip = find_possible_not_expr_argument (arg2, &a2cnt, &a2expr);
+
+ if (a1cnt == a2cnt && operand_equal_p (a1strip, a2strip, 0))
+ {
+ if (code == BIT_AND_EXPR || code == BIT_IOR_EXPR)
+ return arg1;
+ return integer_zero_node;
+ }
+
+ if (!a1expr && !a2expr)
+ return NULL_TREE;
+
+ if (a2expr)
+ {
+ if (operand_equal_p (arg1, a2expr, 0)
+ || operand_equal_p (a1strip, a2expr, 0))
+ op = arg1;
+ }
+ if (!op && a1expr)
+ {
+ if (operand_equal_p (arg2, a1expr, 0)
+ || operand_equal_p (a2strip, a1expr, 0))
+ op = arg2;
+ }
+ if (!op)
+ return NULL_TREE;
+
+ /* We have found a X op !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 ((op == arg1 && operand_precision_onep (a1strip))
+ || (op == arg2 && operand_precision_onep (a2strip)))
+ 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 ((op == arg1 && operand_precision_onep (a1strip))
+ || (op == arg2 && operand_precision_onep (a2strip)))
+ 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. */
@@ -1862,7 +2045,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;
}
===================================================================
@@ -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" } } */
===================================================================
@@ -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" } } */
===================================================================
@@ -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" } } */
===================================================================
@@ -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" } } */
===================================================================
@@ -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" } } */
===================================================================
@@ -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" } } */
===================================================================
@@ -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" } } */
===================================================================
@@ -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" } } */
===================================================================
@@ -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" } } */
===================================================================
@@ -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" } } */