2010-07-10 Sandra Loosemore Backport from mainline: 2010-05-08 Sandra Loosemore PR middle-end/28685 gcc/ * tree-ssa-reassoc.c (eliminate_redundant_comparison): New function. (optimize_ops_list): Call it. gcc/testsuite/ * gcc.dg/pr28685-1.c: New file. 2010-06-08 Sandra Loosemore PR tree-optimization/39874 PR middle-end/28685 gcc/ * gimple.h (maybe_fold_and_comparisons, maybe_fold_or_comparisons): Declare. * gimple-fold.c (canonicalize_bool, same_bool_comparison_p, same_bool_result_p): New. (and_var_with_comparison, and_var_with_comparison_1, and_comparisons_1, and_comparisons, maybe_fold_and_comparisons): New. (or_var_with_comparison, or_var_with_comparison_1, or_comparisons_1, or_comparisons, maybe_fold_or_comparisons): New. * tree-ssa-reassoc.c (eliminate_redundant_comparison): Use maybe_fold_and_comparisons or maybe_fold_or_comparisons instead of combine_comparisons. * tree-ssa-ifcombine.c (ifcombine_ifandif, ifcombine_iforif): Likewise. gcc/testsuite/ * gcc.dg/pr39874.c: New file. 2010-07-10 Yao Qi Merge from Sourcery G++ 4.4: === modified file 'gcc/gimple.h' --- old/gcc/gimple.h 2010-04-02 18:54:46 +0000 +++ new/gcc/gimple.h 2010-07-30 13:21:51 +0000 @@ -4743,4 +4743,9 @@ extern void dump_gimple_statistics (void); +extern tree maybe_fold_and_comparisons (enum tree_code, tree, tree, + enum tree_code, tree, tree); +extern tree maybe_fold_or_comparisons (enum tree_code, tree, tree, + enum tree_code, tree, tree); + #endif /* GCC_GIMPLE_H */ === added file 'gcc/testsuite/gcc.dg/pr28685-1.c' --- old/gcc/testsuite/gcc.dg/pr28685-1.c 1970-01-01 00:00:00 +0000 +++ new/gcc/testsuite/gcc.dg/pr28685-1.c 2010-07-30 13:21:51 +0000 @@ -0,0 +1,50 @@ +/* { dg-do compile } */ +/* { dg-options "-O2 -fdump-tree-optimized" } */ + +/* Should produce <=. */ +int test1 (int a, int b) +{ + return (a < b || a == b); +} + +/* Should produce <=. */ +int test2 (int a, int b) +{ + int lt = a < b; + int eq = a == b; + + return (lt || eq); +} + +/* Should produce <= (just deleting redundant test). */ +int test3 (int a, int b) +{ + int lt = a <= b; + int eq = a == b; + + return (lt || eq); +} + +/* Should produce <= (operands reversed to test the swap logic). */ +int test4 (int a, int b) +{ + int lt = a < b; + int eq = b == a; + + return (lt || eq); +} + +/* Should produce constant 0. */ +int test5 (int a, int b) +{ + int lt = a < b; + int eq = a == b; + + return (lt && eq); +} + +/* { dg-final { scan-tree-dump-times " <= " 4 "optimized" } } */ +/* { dg-final { scan-tree-dump-times "return 0" 1 "optimized" } } */ +/* { dg-final { scan-tree-dump-not " < " "optimized" } } */ +/* { dg-final { scan-tree-dump-not " == " "optimized" } } */ +/* { dg-final { cleanup-tree-dump "optimized" } } */ === added file 'gcc/testsuite/gcc.dg/pr39874.c' --- old/gcc/testsuite/gcc.dg/pr39874.c 1970-01-01 00:00:00 +0000 +++ new/gcc/testsuite/gcc.dg/pr39874.c 2010-07-30 13:21:51 +0000 @@ -0,0 +1,29 @@ +/* { dg-do compile } */ +/* { dg-options "-O2 -fdump-tree-optimized" } */ + +extern void func(); + +void test1(char *signature) +{ + char ch = signature[0]; + if (ch == 15 || ch == 3) + { + if (ch == 15) func(); + } +} + + +void test2(char *signature) +{ + char ch = signature[0]; + if (ch == 15 || ch == 3) + { + if (ch > 14) func(); + } +} + +/* { dg-final { scan-tree-dump-times " == 15" 2 "optimized" } } */ +/* { dg-final { scan-tree-dump-not " == 3" "optimized" } } */ +/* { dg-final { cleanup-tree-dump "optimized" } } */ + + === modified file 'gcc/tree-ssa-ccp.c' --- old/gcc/tree-ssa-ccp.c 2010-04-02 15:50:04 +0000 +++ new/gcc/tree-ssa-ccp.c 2010-07-30 13:21:51 +0000 @@ -3176,6 +3176,1056 @@ return changed; } +/* Canonicalize and possibly invert the boolean EXPR; return NULL_TREE + if EXPR is null or we don't know how. + If non-null, the result always has boolean type. */ + +static tree +canonicalize_bool (tree expr, bool invert) +{ + if (!expr) + return NULL_TREE; + else if (invert) + { + if (integer_nonzerop (expr)) + return boolean_false_node; + else if (integer_zerop (expr)) + return boolean_true_node; + else if (TREE_CODE (expr) == SSA_NAME) + return fold_build2 (EQ_EXPR, boolean_type_node, expr, + build_int_cst (TREE_TYPE (expr), 0)); + else if (TREE_CODE_CLASS (TREE_CODE (expr)) == tcc_comparison) + return fold_build2 (invert_tree_comparison (TREE_CODE (expr), false), + boolean_type_node, + TREE_OPERAND (expr, 0), + TREE_OPERAND (expr, 1)); + else + return NULL_TREE; + } + else + { + if (TREE_CODE (TREE_TYPE (expr)) == BOOLEAN_TYPE) + return expr; + if (integer_nonzerop (expr)) + return boolean_true_node; + else if (integer_zerop (expr)) + return boolean_false_node; + else if (TREE_CODE (expr) == SSA_NAME) + return fold_build2 (NE_EXPR, boolean_type_node, expr, + build_int_cst (TREE_TYPE (expr), 0)); + else if (TREE_CODE_CLASS (TREE_CODE (expr)) == tcc_comparison) + return fold_build2 (TREE_CODE (expr), + boolean_type_node, + TREE_OPERAND (expr, 0), + TREE_OPERAND (expr, 1)); + else + return NULL_TREE; + } +} + +/* Check to see if a boolean expression EXPR is logically equivalent to the + comparison (OP1 CODE OP2). Check for various identities involving + SSA_NAMEs. */ + +static bool +same_bool_comparison_p (const_tree expr, enum tree_code code, + const_tree op1, const_tree op2) +{ + gimple s; + + /* The obvious case. */ + if (TREE_CODE (expr) == code + && operand_equal_p (TREE_OPERAND (expr, 0), op1, 0) + && operand_equal_p (TREE_OPERAND (expr, 1), op2, 0)) + return true; + + /* Check for comparing (name, name != 0) and the case where expr + is an SSA_NAME with a definition matching the comparison. */ + if (TREE_CODE (expr) == SSA_NAME + && TREE_CODE (TREE_TYPE (expr)) == BOOLEAN_TYPE) + { + if (operand_equal_p (expr, op1, 0)) + return ((code == NE_EXPR && integer_zerop (op2)) + || (code == EQ_EXPR && integer_nonzerop (op2))); + s = SSA_NAME_DEF_STMT (expr); + if (is_gimple_assign (s) + && gimple_assign_rhs_code (s) == code + && operand_equal_p (gimple_assign_rhs1 (s), op1, 0) + && operand_equal_p (gimple_assign_rhs2 (s), op2, 0)) + return true; + } + + /* If op1 is of the form (name != 0) or (name == 0), and the definition + of name is a comparison, recurse. */ + if (TREE_CODE (op1) == SSA_NAME + && TREE_CODE (TREE_TYPE (op1)) == BOOLEAN_TYPE) + { + s = SSA_NAME_DEF_STMT (op1); + if (is_gimple_assign (s) + && TREE_CODE_CLASS (gimple_assign_rhs_code (s)) == tcc_comparison) + { + enum tree_code c = gimple_assign_rhs_code (s); + if ((c == NE_EXPR && integer_zerop (op2)) + || (c == EQ_EXPR && integer_nonzerop (op2))) + return same_bool_comparison_p (expr, c, + gimple_assign_rhs1 (s), + gimple_assign_rhs2 (s)); + if ((c == EQ_EXPR && integer_zerop (op2)) + || (c == NE_EXPR && integer_nonzerop (op2))) + return same_bool_comparison_p (expr, + invert_tree_comparison (c, false), + gimple_assign_rhs1 (s), + gimple_assign_rhs2 (s)); + } + } + return false; +} + +/* Check to see if two boolean expressions OP1 and OP2 are logically + equivalent. */ + +static bool +same_bool_result_p (const_tree op1, const_tree op2) +{ + /* Simple cases first. */ + if (operand_equal_p (op1, op2, 0)) + return true; + + /* Check the cases where at least one of the operands is a comparison. + These are a bit smarter than operand_equal_p in that they apply some + identifies on SSA_NAMEs. */ + if (TREE_CODE_CLASS (TREE_CODE (op2)) == tcc_comparison + && same_bool_comparison_p (op1, TREE_CODE (op2), + TREE_OPERAND (op2, 0), + TREE_OPERAND (op2, 1))) + return true; + if (TREE_CODE_CLASS (TREE_CODE (op1)) == tcc_comparison + && same_bool_comparison_p (op2, TREE_CODE (op1), + TREE_OPERAND (op1, 0), + TREE_OPERAND (op1, 1))) + return true; + + /* Default case. */ + return false; +} + +/* Forward declarations for some mutually recursive functions. */ + +static tree +and_comparisons_1 (enum tree_code code1, tree op1a, tree op1b, + enum tree_code code2, tree op2a, tree op2b); +static tree +and_var_with_comparison (tree var, bool invert, + enum tree_code code2, tree op2a, tree op2b); +static tree +and_var_with_comparison_1 (gimple stmt, + enum tree_code code2, tree op2a, tree op2b); +static tree +or_comparisons_1 (enum tree_code code1, tree op1a, tree op1b, + enum tree_code code2, tree op2a, tree op2b); +static tree +or_var_with_comparison (tree var, bool invert, + enum tree_code code2, tree op2a, tree op2b); +static tree +or_var_with_comparison_1 (gimple stmt, + enum tree_code code2, tree op2a, tree op2b); + +/* Helper function for and_comparisons_1: try to simplify the AND of the + ssa variable VAR with the comparison specified by (OP2A CODE2 OP2B). + If INVERT is true, invert the value of the VAR before doing the AND. + Return NULL_EXPR if we can't simplify this to a single expression. */ + +static tree +and_var_with_comparison (tree var, bool invert, + enum tree_code code2, tree op2a, tree op2b) +{ + tree t; + gimple stmt = SSA_NAME_DEF_STMT (var); + + /* We can only deal with variables whose definitions are assignments. */ + if (!is_gimple_assign (stmt)) + return NULL_TREE; + + /* If we have an inverted comparison, apply DeMorgan's law and rewrite + !var AND (op2a code2 op2b) => !(var OR !(op2a code2 op2b)) + Then we only have to consider the simpler non-inverted cases. */ + if (invert) + t = or_var_with_comparison_1 (stmt, + invert_tree_comparison (code2, false), + op2a, op2b); + else + t = and_var_with_comparison_1 (stmt, code2, op2a, op2b); + return canonicalize_bool (t, invert); +} + +/* Try to simplify the AND of the ssa variable defined by the assignment + STMT with the comparison specified by (OP2A CODE2 OP2B). + Return NULL_EXPR if we can't simplify this to a single expression. */ + +static tree +and_var_with_comparison_1 (gimple stmt, + enum tree_code code2, tree op2a, tree op2b) +{ + tree var = gimple_assign_lhs (stmt); + tree true_test_var = NULL_TREE; + tree false_test_var = NULL_TREE; + enum tree_code innercode = gimple_assign_rhs_code (stmt); + + /* Check for identities like (var AND (var == 0)) => false. */ + if (TREE_CODE (op2a) == SSA_NAME + && TREE_CODE (TREE_TYPE (var)) == BOOLEAN_TYPE) + { + if ((code2 == NE_EXPR && integer_zerop (op2b)) + || (code2 == EQ_EXPR && integer_nonzerop (op2b))) + { + true_test_var = op2a; + if (var == true_test_var) + return var; + } + else if ((code2 == EQ_EXPR && integer_zerop (op2b)) + || (code2 == NE_EXPR && integer_nonzerop (op2b))) + { + false_test_var = op2a; + if (var == false_test_var) + return boolean_false_node; + } + } + + /* If the definition is a comparison, recurse on it. */ + if (TREE_CODE_CLASS (innercode) == tcc_comparison) + { + tree t = and_comparisons_1 (innercode, + gimple_assign_rhs1 (stmt), + gimple_assign_rhs2 (stmt), + code2, + op2a, + op2b); + if (t) + return t; + } + + /* If the definition is an AND or OR expression, we may be able to + simplify by reassociating. */ + if (innercode == TRUTH_AND_EXPR + || innercode == TRUTH_OR_EXPR + || (TREE_CODE (TREE_TYPE (var)) == BOOLEAN_TYPE + && (innercode == BIT_AND_EXPR || innercode == BIT_IOR_EXPR))) + { + tree inner1 = gimple_assign_rhs1 (stmt); + tree inner2 = gimple_assign_rhs2 (stmt); + gimple s; + tree t; + tree partial = NULL_TREE; + bool is_and = (innercode == TRUTH_AND_EXPR || innercode == BIT_AND_EXPR); + + /* Check for boolean identities that don't require recursive examination + of inner1/inner2: + inner1 AND (inner1 AND inner2) => inner1 AND inner2 => var + inner1 AND (inner1 OR inner2) => inner1 + !inner1 AND (inner1 AND inner2) => false + !inner1 AND (inner1 OR inner2) => !inner1 AND inner2 + Likewise for similar cases involving inner2. */ + if (inner1 == true_test_var) + return (is_and ? var : inner1); + else if (inner2 == true_test_var) + return (is_and ? var : inner2); + else if (inner1 == false_test_var) + return (is_and + ? boolean_false_node + : and_var_with_comparison (inner2, false, code2, op2a, op2b)); + else if (inner2 == false_test_var) + return (is_and + ? boolean_false_node + : and_var_with_comparison (inner1, false, code2, op2a, op2b)); + + /* Next, redistribute/reassociate the AND across the inner tests. + Compute the first partial result, (inner1 AND (op2a code op2b)) */ + if (TREE_CODE (inner1) == SSA_NAME + && is_gimple_assign (s = SSA_NAME_DEF_STMT (inner1)) + && TREE_CODE_CLASS (gimple_assign_rhs_code (s)) == tcc_comparison + && (t = maybe_fold_and_comparisons (gimple_assign_rhs_code (s), + gimple_assign_rhs1 (s), + gimple_assign_rhs2 (s), + code2, op2a, op2b))) + { + /* Handle the AND case, where we are reassociating: + (inner1 AND inner2) AND (op2a code2 op2b) + => (t AND inner2) + If the partial result t is a constant, we win. Otherwise + continue on to try reassociating with the other inner test. */ + if (is_and) + { + if (integer_onep (t)) + return inner2; + else if (integer_zerop (t)) + return boolean_false_node; + } + + /* Handle the OR case, where we are redistributing: + (inner1 OR inner2) AND (op2a code2 op2b) + => (t OR (inner2 AND (op2a code2 op2b))) */ + else + { + if (integer_onep (t)) + return boolean_true_node; + else + /* Save partial result for later. */ + partial = t; + } + } + + /* Compute the second partial result, (inner2 AND (op2a code op2b)) */ + if (TREE_CODE (inner2) == SSA_NAME + && is_gimple_assign (s = SSA_NAME_DEF_STMT (inner2)) + && TREE_CODE_CLASS (gimple_assign_rhs_code (s)) == tcc_comparison + && (t = maybe_fold_and_comparisons (gimple_assign_rhs_code (s), + gimple_assign_rhs1 (s), + gimple_assign_rhs2 (s), + code2, op2a, op2b))) + { + /* Handle the AND case, where we are reassociating: + (inner1 AND inner2) AND (op2a code2 op2b) + => (inner1 AND t) */ + if (is_and) + { + if (integer_onep (t)) + return inner1; + else if (integer_zerop (t)) + return boolean_false_node; + } + + /* Handle the OR case. where we are redistributing: + (inner1 OR inner2) AND (op2a code2 op2b) + => (t OR (inner1 AND (op2a code2 op2b))) + => (t OR partial) */ + else + { + if (integer_onep (t)) + return boolean_true_node; + else if (partial) + { + /* We already got a simplification for the other + operand to the redistributed OR expression. The + interesting case is when at least one is false. + Or, if both are the same, we can apply the identity + (x OR x) == x. */ + if (integer_zerop (partial)) + return t; + else if (integer_zerop (t)) + return partial; + else if (same_bool_result_p (t, partial)) + return t; + } + } + } + } + return NULL_TREE; +} + +/* Try to simplify the AND of two comparisons defined by + (OP1A CODE1 OP1B) and (OP2A CODE2 OP2B), respectively. + If this can be done without constructing an intermediate value, + return the resulting tree; otherwise NULL_TREE is returned. + This function is deliberately asymmetric as it recurses on SSA_DEFs + in the first comparison but not the second. */ + +static tree +and_comparisons_1 (enum tree_code code1, tree op1a, tree op1b, + enum tree_code code2, tree op2a, tree op2b) +{ + /* First check for ((x CODE1 y) AND (x CODE2 y)). */ + if (operand_equal_p (op1a, op2a, 0) + && operand_equal_p (op1b, op2b, 0)) + { + tree t = combine_comparisons (UNKNOWN_LOCATION, + TRUTH_ANDIF_EXPR, code1, code2, + boolean_type_node, op1a, op1b); + if (t) + return t; + } + + /* Likewise the swapped case of the above. */ + if (operand_equal_p (op1a, op2b, 0) + && operand_equal_p (op1b, op2a, 0)) + { + tree t = combine_comparisons (UNKNOWN_LOCATION, + TRUTH_ANDIF_EXPR, code1, + swap_tree_comparison (code2), + boolean_type_node, op1a, op1b); + if (t) + return t; + } + + /* If both comparisons are of the same value against constants, we might + be able to merge them. */ + if (operand_equal_p (op1a, op2a, 0) + && TREE_CODE (op1b) == INTEGER_CST + && TREE_CODE (op2b) == INTEGER_CST) + { + int cmp = tree_int_cst_compare (op1b, op2b); + + /* If we have (op1a == op1b), we should either be able to + return that or FALSE, depending on whether the constant op1b + also satisfies the other comparison against op2b. */ + if (code1 == EQ_EXPR) + { + bool done = true; + bool val; + switch (code2) + { + case EQ_EXPR: val = (cmp == 0); break; + case NE_EXPR: val = (cmp != 0); break; + case LT_EXPR: val = (cmp < 0); break; + case GT_EXPR: val = (cmp > 0); break; + case LE_EXPR: val = (cmp <= 0); break; + case GE_EXPR: val = (cmp >= 0); break; + default: done = false; + } + if (done) + { + if (val) + return fold_build2 (code1, boolean_type_node, op1a, op1b); + else + return boolean_false_node; + } + } + /* Likewise if the second comparison is an == comparison. */ + else if (code2 == EQ_EXPR) + { + bool done = true; + bool val; + switch (code1) + { + case EQ_EXPR: val = (cmp == 0); break; + case NE_EXPR: val = (cmp != 0); break; + case LT_EXPR: val = (cmp > 0); break; + case GT_EXPR: val = (cmp < 0); break; + case LE_EXPR: val = (cmp >= 0); break; + case GE_EXPR: val = (cmp <= 0); break; + default: done = false; + } + if (done) + { + if (val) + return fold_build2 (code2, boolean_type_node, op2a, op2b); + else + return boolean_false_node; + } + } + + /* Same business with inequality tests. */ + else if (code1 == NE_EXPR) + { + bool val; + switch (code2) + { + case EQ_EXPR: val = (cmp != 0); break; + case NE_EXPR: val = (cmp == 0); break; + case LT_EXPR: val = (cmp >= 0); break; + case GT_EXPR: val = (cmp <= 0); break; + case LE_EXPR: val = (cmp > 0); break; + case GE_EXPR: val = (cmp < 0); break; + default: + val = false; + } + if (val) + return fold_build2 (code2, boolean_type_node, op2a, op2b); + } + else if (code2 == NE_EXPR) + { + bool val; + switch (code1) + { + case EQ_EXPR: val = (cmp == 0); break; + case NE_EXPR: val = (cmp != 0); break; + case LT_EXPR: val = (cmp <= 0); break; + case GT_EXPR: val = (cmp >= 0); break; + case LE_EXPR: val = (cmp < 0); break; + case GE_EXPR: val = (cmp > 0); break; + default: + val = false; + } + if (val) + return fold_build2 (code1, boolean_type_node, op1a, op1b); + } + + /* Chose the more restrictive of two < or <= comparisons. */ + else if ((code1 == LT_EXPR || code1 == LE_EXPR) + && (code2 == LT_EXPR || code2 == LE_EXPR)) + { + if ((cmp < 0) || (cmp == 0 && code1 == LT_EXPR)) + return fold_build2 (code1, boolean_type_node, op1a, op1b); + else + return fold_build2 (code2, boolean_type_node, op2a, op2b); + } + + /* Likewise chose the more restrictive of two > or >= comparisons. */ + else if ((code1 == GT_EXPR || code1 == GE_EXPR) + && (code2 == GT_EXPR || code2 == GE_EXPR)) + { + if ((cmp > 0) || (cmp == 0 && code1 == GT_EXPR)) + return fold_build2 (code1, boolean_type_node, op1a, op1b); + else + return fold_build2 (code2, boolean_type_node, op2a, op2b); + } + + /* Check for singleton ranges. */ + else if (cmp == 0 + && ((code1 == LE_EXPR && code2 == GE_EXPR) + || (code1 == GE_EXPR && code2 == LE_EXPR))) + return fold_build2 (EQ_EXPR, boolean_type_node, op1a, op2b); + + /* Check for disjoint ranges. */ + else if (cmp <= 0 + && (code1 == LT_EXPR || code1 == LE_EXPR) + && (code2 == GT_EXPR || code2 == GE_EXPR)) + return boolean_false_node; + else if (cmp >= 0 + && (code1 == GT_EXPR || code1 == GE_EXPR) + && (code2 == LT_EXPR || code2 == LE_EXPR)) + return boolean_false_node; + } + + /* Perhaps the first comparison is (NAME != 0) or (NAME == 1) where + NAME's definition is a truth value. See if there are any simplifications + that can be done against the NAME's definition. */ + if (TREE_CODE (op1a) == SSA_NAME + && (code1 == NE_EXPR || code1 == EQ_EXPR) + && (integer_zerop (op1b) || integer_onep (op1b))) + { + bool invert = ((code1 == EQ_EXPR && integer_zerop (op1b)) + || (code1 == NE_EXPR && integer_onep (op1b))); + gimple stmt = SSA_NAME_DEF_STMT (op1a); + switch (gimple_code (stmt)) + { + case GIMPLE_ASSIGN: + /* Try to simplify by copy-propagating the definition. */ + return and_var_with_comparison (op1a, invert, code2, op2a, op2b); + + case GIMPLE_PHI: + /* If every argument to the PHI produces the same result when + ANDed with the second comparison, we win. + Do not do this unless the type is bool since we need a bool + result here anyway. */ + if (TREE_CODE (TREE_TYPE (op1a)) == BOOLEAN_TYPE) + { + tree result = NULL_TREE; + unsigned i; + for (i = 0; i < gimple_phi_num_args (stmt); i++) + { + tree arg = gimple_phi_arg_def (stmt, i); + + /* If this PHI has itself as an argument, ignore it. + If all the other args produce the same result, + we're still OK. */ + if (arg == gimple_phi_result (stmt)) + continue; + else if (TREE_CODE (arg) == INTEGER_CST) + { + if (invert ? integer_nonzerop (arg) : integer_zerop (arg)) + { + if (!result) + result = boolean_false_node; + else if (!integer_zerop (result)) + return NULL_TREE; + } + else if (!result) + result = fold_build2 (code2, boolean_type_node, + op2a, op2b); + else if (!same_bool_comparison_p (result, + code2, op2a, op2b)) + return NULL_TREE; + } + else if (TREE_CODE (arg) == SSA_NAME) + { + tree temp = and_var_with_comparison (arg, invert, + code2, op2a, op2b); + if (!temp) + return NULL_TREE; + else if (!result) + result = temp; + else if (!same_bool_result_p (result, temp)) + return NULL_TREE; + } + else + return NULL_TREE; + } + return result; + } + + default: + break; + } + } + return NULL_TREE; +} + +/* Try to simplify the AND of two comparisons, specified by + (OP1A CODE1 OP1B) and (OP2B CODE2 OP2B), respectively. + If this can be simplified to a single expression (without requiring + introducing more SSA variables to hold intermediate values), + return the resulting tree. Otherwise return NULL_TREE. + If the result expression is non-null, it has boolean type. */ + +tree +maybe_fold_and_comparisons (enum tree_code code1, tree op1a, tree op1b, + enum tree_code code2, tree op2a, tree op2b) +{ + tree t = and_comparisons_1 (code1, op1a, op1b, code2, op2a, op2b); + if (t) + return t; + else + return and_comparisons_1 (code2, op2a, op2b, code1, op1a, op1b); +} + +/* Helper function for or_comparisons_1: try to simplify the OR of the + ssa variable VAR with the comparison specified by (OP2A CODE2 OP2B). + If INVERT is true, invert the value of VAR before doing the OR. + Return NULL_EXPR if we can't simplify this to a single expression. */ + +static tree +or_var_with_comparison (tree var, bool invert, + enum tree_code code2, tree op2a, tree op2b) +{ + tree t; + gimple stmt = SSA_NAME_DEF_STMT (var); + + /* We can only deal with variables whose definitions are assignments. */ + if (!is_gimple_assign (stmt)) + return NULL_TREE; + + /* If we have an inverted comparison, apply DeMorgan's law and rewrite + !var OR (op2a code2 op2b) => !(var AND !(op2a code2 op2b)) + Then we only have to consider the simpler non-inverted cases. */ + if (invert) + t = and_var_with_comparison_1 (stmt, + invert_tree_comparison (code2, false), + op2a, op2b); + else + t = or_var_with_comparison_1 (stmt, code2, op2a, op2b); + return canonicalize_bool (t, invert); +} + +/* Try to simplify the OR of the ssa variable defined by the assignment + STMT with the comparison specified by (OP2A CODE2 OP2B). + Return NULL_EXPR if we can't simplify this to a single expression. */ + +static tree +or_var_with_comparison_1 (gimple stmt, + enum tree_code code2, tree op2a, tree op2b) +{ + tree var = gimple_assign_lhs (stmt); + tree true_test_var = NULL_TREE; + tree false_test_var = NULL_TREE; + enum tree_code innercode = gimple_assign_rhs_code (stmt); + + /* Check for identities like (var OR (var != 0)) => true . */ + if (TREE_CODE (op2a) == SSA_NAME + && TREE_CODE (TREE_TYPE (var)) == BOOLEAN_TYPE) + { + if ((code2 == NE_EXPR && integer_zerop (op2b)) + || (code2 == EQ_EXPR && integer_nonzerop (op2b))) + { + true_test_var = op2a; + if (var == true_test_var) + return var; + } + else if ((code2 == EQ_EXPR && integer_zerop (op2b)) + || (code2 == NE_EXPR && integer_nonzerop (op2b))) + { + false_test_var = op2a; + if (var == false_test_var) + return boolean_true_node; + } + } + + /* If the definition is a comparison, recurse on it. */ + if (TREE_CODE_CLASS (innercode) == tcc_comparison) + { + tree t = or_comparisons_1 (innercode, + gimple_assign_rhs1 (stmt), + gimple_assign_rhs2 (stmt), + code2, + op2a, + op2b); + if (t) + return t; + } + + /* If the definition is an AND or OR expression, we may be able to + simplify by reassociating. */ + if (innercode == TRUTH_AND_EXPR + || innercode == TRUTH_OR_EXPR + || (TREE_CODE (TREE_TYPE (var)) == BOOLEAN_TYPE + && (innercode == BIT_AND_EXPR || innercode == BIT_IOR_EXPR))) + { + tree inner1 = gimple_assign_rhs1 (stmt); + tree inner2 = gimple_assign_rhs2 (stmt); + gimple s; + tree t; + tree partial = NULL_TREE; + bool is_or = (innercode == TRUTH_OR_EXPR || innercode == BIT_IOR_EXPR); + + /* Check for boolean identities that don't require recursive examination + of inner1/inner2: + inner1 OR (inner1 OR inner2) => inner1 OR inner2 => var + inner1 OR (inner1 AND inner2) => inner1 + !inner1 OR (inner1 OR inner2) => true + !inner1 OR (inner1 AND inner2) => !inner1 OR inner2 + */ + if (inner1 == true_test_var) + return (is_or ? var : inner1); + else if (inner2 == true_test_var) + return (is_or ? var : inner2); + else if (inner1 == false_test_var) + return (is_or + ? boolean_true_node + : or_var_with_comparison (inner2, false, code2, op2a, op2b)); + else if (inner2 == false_test_var) + return (is_or + ? boolean_true_node + : or_var_with_comparison (inner1, false, code2, op2a, op2b)); + + /* Next, redistribute/reassociate the OR across the inner tests. + Compute the first partial result, (inner1 OR (op2a code op2b)) */ + if (TREE_CODE (inner1) == SSA_NAME + && is_gimple_assign (s = SSA_NAME_DEF_STMT (inner1)) + && TREE_CODE_CLASS (gimple_assign_rhs_code (s)) == tcc_comparison + && (t = maybe_fold_or_comparisons (gimple_assign_rhs_code (s), + gimple_assign_rhs1 (s), + gimple_assign_rhs2 (s), + code2, op2a, op2b))) + { + /* Handle the OR case, where we are reassociating: + (inner1 OR inner2) OR (op2a code2 op2b) + => (t OR inner2) + If the partial result t is a constant, we win. Otherwise + continue on to try reassociating with the other inner test. */ + if (innercode == TRUTH_OR_EXPR) + { + if (integer_onep (t)) + return boolean_true_node; + else if (integer_zerop (t)) + return inner2; + } + + /* Handle the AND case, where we are redistributing: + (inner1 AND inner2) OR (op2a code2 op2b) + => (t AND (inner2 OR (op2a code op2b))) */ + else + { + if (integer_zerop (t)) + return boolean_false_node; + else + /* Save partial result for later. */ + partial = t; + } + } + + /* Compute the second partial result, (inner2 OR (op2a code op2b)) */ + if (TREE_CODE (inner2) == SSA_NAME + && is_gimple_assign (s = SSA_NAME_DEF_STMT (inner2)) + && TREE_CODE_CLASS (gimple_assign_rhs_code (s)) == tcc_comparison + && (t = maybe_fold_or_comparisons (gimple_assign_rhs_code (s), + gimple_assign_rhs1 (s), + gimple_assign_rhs2 (s), + code2, op2a, op2b))) + { + /* Handle the OR case, where we are reassociating: + (inner1 OR inner2) OR (op2a code2 op2b) + => (inner1 OR t) */ + if (innercode == TRUTH_OR_EXPR) + { + if (integer_zerop (t)) + return inner1; + else if (integer_onep (t)) + return boolean_true_node; + } + + /* Handle the AND case, where we are redistributing: + (inner1 AND inner2) OR (op2a code2 op2b) + => (t AND (inner1 OR (op2a code2 op2b))) + => (t AND partial) */ + else + { + if (integer_zerop (t)) + return boolean_false_node; + else if (partial) + { + /* We already got a simplification for the other + operand to the redistributed AND expression. The + interesting case is when at least one is true. + Or, if both are the same, we can apply the identity + (x AND x) == true. */ + if (integer_onep (partial)) + return t; + else if (integer_onep (t)) + return partial; + else if (same_bool_result_p (t, partial)) + return boolean_true_node; + } + } + } + } + return NULL_TREE; +} + +/* Try to simplify the OR of two comparisons defined by + (OP1A CODE1 OP1B) and (OP2A CODE2 OP2B), respectively. + If this can be done without constructing an intermediate value, + return the resulting tree; otherwise NULL_TREE is returned. + This function is deliberately asymmetric as it recurses on SSA_DEFs + in the first comparison but not the second. */ + +static tree +or_comparisons_1 (enum tree_code code1, tree op1a, tree op1b, + enum tree_code code2, tree op2a, tree op2b) +{ + /* First check for ((x CODE1 y) OR (x CODE2 y)). */ + if (operand_equal_p (op1a, op2a, 0) + && operand_equal_p (op1b, op2b, 0)) + { + tree t = combine_comparisons (UNKNOWN_LOCATION, + TRUTH_ORIF_EXPR, code1, code2, + boolean_type_node, op1a, op1b); + if (t) + return t; + } + + /* Likewise the swapped case of the above. */ + if (operand_equal_p (op1a, op2b, 0) + && operand_equal_p (op1b, op2a, 0)) + { + tree t = combine_comparisons (UNKNOWN_LOCATION, + TRUTH_ORIF_EXPR, code1, + swap_tree_comparison (code2), + boolean_type_node, op1a, op1b); + if (t) + return t; + } + + /* If both comparisons are of the same value against constants, we might + be able to merge them. */ + if (operand_equal_p (op1a, op2a, 0) + && TREE_CODE (op1b) == INTEGER_CST + && TREE_CODE (op2b) == INTEGER_CST) + { + int cmp = tree_int_cst_compare (op1b, op2b); + + /* If we have (op1a != op1b), we should either be able to + return that or TRUE, depending on whether the constant op1b + also satisfies the other comparison against op2b. */ + if (code1 == NE_EXPR) + { + bool done = true; + bool val; + switch (code2) + { + case EQ_EXPR: val = (cmp == 0); break; + case NE_EXPR: val = (cmp != 0); break; + case LT_EXPR: val = (cmp < 0); break; + case GT_EXPR: val = (cmp > 0); break; + case LE_EXPR: val = (cmp <= 0); break; + case GE_EXPR: val = (cmp >= 0); break; + default: done = false; + } + if (done) + { + if (val) + return boolean_true_node; + else + return fold_build2 (code1, boolean_type_node, op1a, op1b); + } + } + /* Likewise if the second comparison is a != comparison. */ + else if (code2 == NE_EXPR) + { + bool done = true; + bool val; + switch (code1) + { + case EQ_EXPR: val = (cmp == 0); break; + case NE_EXPR: val = (cmp != 0); break; + case LT_EXPR: val = (cmp > 0); break; + case GT_EXPR: val = (cmp < 0); break; + case LE_EXPR: val = (cmp >= 0); break; + case GE_EXPR: val = (cmp <= 0); break; + default: done = false; + } + if (done) + { + if (val) + return boolean_true_node; + else + return fold_build2 (code2, boolean_type_node, op2a, op2b); + } + } + + /* See if an equality test is redundant with the other comparison. */ + else if (code1 == EQ_EXPR) + { + bool val; + switch (code2) + { + case EQ_EXPR: val = (cmp == 0); break; + case NE_EXPR: val = (cmp != 0); break; + case LT_EXPR: val = (cmp < 0); break; + case GT_EXPR: val = (cmp > 0); break; + case LE_EXPR: val = (cmp <= 0); break; + case GE_EXPR: val = (cmp >= 0); break; + default: + val = false; + } + if (val) + return fold_build2 (code2, boolean_type_node, op2a, op2b); + } + else if (code2 == EQ_EXPR) + { + bool val; + switch (code1) + { + case EQ_EXPR: val = (cmp == 0); break; + case NE_EXPR: val = (cmp != 0); break; + case LT_EXPR: val = (cmp > 0); break; + case GT_EXPR: val = (cmp < 0); break; + case LE_EXPR: val = (cmp >= 0); break; + case GE_EXPR: val = (cmp <= 0); break; + default: + val = false; + } + if (val) + return fold_build2 (code1, boolean_type_node, op1a, op1b); + } + + /* Chose the less restrictive of two < or <= comparisons. */ + else if ((code1 == LT_EXPR || code1 == LE_EXPR) + && (code2 == LT_EXPR || code2 == LE_EXPR)) + { + if ((cmp < 0) || (cmp == 0 && code1 == LT_EXPR)) + return fold_build2 (code2, boolean_type_node, op2a, op2b); + else + return fold_build2 (code1, boolean_type_node, op1a, op1b); + } + + /* Likewise chose the less restrictive of two > or >= comparisons. */ + else if ((code1 == GT_EXPR || code1 == GE_EXPR) + && (code2 == GT_EXPR || code2 == GE_EXPR)) + { + if ((cmp > 0) || (cmp == 0 && code1 == GT_EXPR)) + return fold_build2 (code2, boolean_type_node, op2a, op2b); + else + return fold_build2 (code1, boolean_type_node, op1a, op1b); + } + + /* Check for singleton ranges. */ + else if (cmp == 0 + && ((code1 == LT_EXPR && code2 == GT_EXPR) + || (code1 == GT_EXPR && code2 == LT_EXPR))) + return fold_build2 (NE_EXPR, boolean_type_node, op1a, op2b); + + /* Check for less/greater pairs that don't restrict the range at all. */ + else if (cmp >= 0 + && (code1 == LT_EXPR || code1 == LE_EXPR) + && (code2 == GT_EXPR || code2 == GE_EXPR)) + return boolean_true_node; + else if (cmp <= 0 + && (code1 == GT_EXPR || code1 == GE_EXPR) + && (code2 == LT_EXPR || code2 == LE_EXPR)) + return boolean_true_node; + } + + /* Perhaps the first comparison is (NAME != 0) or (NAME == 1) where + NAME's definition is a truth value. See if there are any simplifications + that can be done against the NAME's definition. */ + if (TREE_CODE (op1a) == SSA_NAME + && (code1 == NE_EXPR || code1 == EQ_EXPR) + && (integer_zerop (op1b) || integer_onep (op1b))) + { + bool invert = ((code1 == EQ_EXPR && integer_zerop (op1b)) + || (code1 == NE_EXPR && integer_onep (op1b))); + gimple stmt = SSA_NAME_DEF_STMT (op1a); + switch (gimple_code (stmt)) + { + case GIMPLE_ASSIGN: + /* Try to simplify by copy-propagating the definition. */ + return or_var_with_comparison (op1a, invert, code2, op2a, op2b); + + case GIMPLE_PHI: + /* If every argument to the PHI produces the same result when + ORed with the second comparison, we win. + Do not do this unless the type is bool since we need a bool + result here anyway. */ + if (TREE_CODE (TREE_TYPE (op1a)) == BOOLEAN_TYPE) + { + tree result = NULL_TREE; + unsigned i; + for (i = 0; i < gimple_phi_num_args (stmt); i++) + { + tree arg = gimple_phi_arg_def (stmt, i); + + /* If this PHI has itself as an argument, ignore it. + If all the other args produce the same result, + we're still OK. */ + if (arg == gimple_phi_result (stmt)) + continue; + else if (TREE_CODE (arg) == INTEGER_CST) + { + if (invert ? integer_zerop (arg) : integer_nonzerop (arg)) + { + if (!result) + result = boolean_true_node; + else if (!integer_onep (result)) + return NULL_TREE; + } + else if (!result) + result = fold_build2 (code2, boolean_type_node, + op2a, op2b); + else if (!same_bool_comparison_p (result, + code2, op2a, op2b)) + return NULL_TREE; + } + else if (TREE_CODE (arg) == SSA_NAME) + { + tree temp = or_var_with_comparison (arg, invert, + code2, op2a, op2b); + if (!temp) + return NULL_TREE; + else if (!result) + result = temp; + else if (!same_bool_result_p (result, temp)) + return NULL_TREE; + } + else + return NULL_TREE; + } + return result; + } + + default: + break; + } + } + return NULL_TREE; +} + +/* Try to simplify the OR of two comparisons, specified by + (OP1A CODE1 OP1B) and (OP2B CODE2 OP2B), respectively. + If this can be simplified to a single expression (without requiring + introducing more SSA variables to hold intermediate values), + return the resulting tree. Otherwise return NULL_TREE. + If the result expression is non-null, it has boolean type. */ + +tree +maybe_fold_or_comparisons (enum tree_code code1, tree op1a, tree op1b, + enum tree_code code2, tree op2a, tree op2b) +{ + tree t = or_comparisons_1 (code1, op1a, op1b, code2, op2a, op2b); + if (t) + return t; + else + return or_comparisons_1 (code2, op2a, op2b, code1, op1a, op1b); +} + /* Try to optimize out __builtin_stack_restore. Optimize it out if there is another __builtin_stack_restore in the same basic block and no calls or ASM_EXPRs are in between, or if this block's === modified file 'gcc/tree-ssa-ifcombine.c' --- old/gcc/tree-ssa-ifcombine.c 2009-11-25 10:55:54 +0000 +++ new/gcc/tree-ssa-ifcombine.c 2010-07-30 13:21:51 +0000 @@ -366,21 +366,16 @@ /* See if we have two comparisons that we can merge into one. */ else if (TREE_CODE_CLASS (gimple_cond_code (inner_cond)) == tcc_comparison - && TREE_CODE_CLASS (gimple_cond_code (outer_cond)) == tcc_comparison - && operand_equal_p (gimple_cond_lhs (inner_cond), - gimple_cond_lhs (outer_cond), 0) - && operand_equal_p (gimple_cond_rhs (inner_cond), - gimple_cond_rhs (outer_cond), 0)) + && TREE_CODE_CLASS (gimple_cond_code (outer_cond)) == tcc_comparison) { - enum tree_code code1 = gimple_cond_code (inner_cond); - enum tree_code code2 = gimple_cond_code (outer_cond); tree t; - if (!(t = combine_comparisons (UNKNOWN_LOCATION, - TRUTH_ANDIF_EXPR, code1, code2, - boolean_type_node, - gimple_cond_lhs (outer_cond), - gimple_cond_rhs (outer_cond)))) + if (!(t = maybe_fold_and_comparisons (gimple_cond_code (inner_cond), + gimple_cond_lhs (inner_cond), + gimple_cond_rhs (inner_cond), + gimple_cond_code (outer_cond), + gimple_cond_lhs (outer_cond), + gimple_cond_rhs (outer_cond)))) return false; t = canonicalize_cond_expr_cond (t); if (!t) @@ -518,22 +513,17 @@ /* See if we have two comparisons that we can merge into one. This happens for C++ operator overloading where for example GE_EXPR is implemented as GT_EXPR || EQ_EXPR. */ - else if (TREE_CODE_CLASS (gimple_cond_code (inner_cond)) == tcc_comparison - && TREE_CODE_CLASS (gimple_cond_code (outer_cond)) == tcc_comparison - && operand_equal_p (gimple_cond_lhs (inner_cond), - gimple_cond_lhs (outer_cond), 0) - && operand_equal_p (gimple_cond_rhs (inner_cond), - gimple_cond_rhs (outer_cond), 0)) + else if (TREE_CODE_CLASS (gimple_cond_code (inner_cond)) == tcc_comparison + && TREE_CODE_CLASS (gimple_cond_code (outer_cond)) == tcc_comparison) { - enum tree_code code1 = gimple_cond_code (inner_cond); - enum tree_code code2 = gimple_cond_code (outer_cond); tree t; - if (!(t = combine_comparisons (UNKNOWN_LOCATION, - TRUTH_ORIF_EXPR, code1, code2, - boolean_type_node, - gimple_cond_lhs (outer_cond), - gimple_cond_rhs (outer_cond)))) + if (!(t = maybe_fold_or_comparisons (gimple_cond_code (inner_cond), + gimple_cond_lhs (inner_cond), + gimple_cond_rhs (inner_cond), + gimple_cond_code (outer_cond), + gimple_cond_lhs (outer_cond), + gimple_cond_rhs (outer_cond)))) return false; t = canonicalize_cond_expr_cond (t); if (!t) === modified file 'gcc/tree-ssa-reassoc.c' --- old/gcc/tree-ssa-reassoc.c 2010-01-13 15:04:38 +0000 +++ new/gcc/tree-ssa-reassoc.c 2010-07-30 13:21:51 +0000 @@ -1159,6 +1159,117 @@ return changed; } +/* If OPCODE is BIT_IOR_EXPR or BIT_AND_EXPR and CURR is a comparison + expression, examine the other OPS to see if any of them are comparisons + of the same values, which we may be able to combine or eliminate. + For example, we can rewrite (a < b) | (a == b) as (a <= b). */ + +static bool +eliminate_redundant_comparison (enum tree_code opcode, + VEC (operand_entry_t, heap) **ops, + unsigned int currindex, + operand_entry_t curr) +{ + tree op1, op2; + enum tree_code lcode, rcode; + gimple def1, def2; + int i; + operand_entry_t oe; + + if (opcode != BIT_IOR_EXPR && opcode != BIT_AND_EXPR) + return false; + + /* Check that CURR is a comparison. */ + if (TREE_CODE (curr->op) != SSA_NAME) + return false; + def1 = SSA_NAME_DEF_STMT (curr->op); + if (!is_gimple_assign (def1)) + return false; + lcode = gimple_assign_rhs_code (def1); + if (TREE_CODE_CLASS (lcode) != tcc_comparison) + return false; + op1 = gimple_assign_rhs1 (def1); + op2 = gimple_assign_rhs2 (def1); + + /* Now look for a similar comparison in the remaining OPS. */ + for (i = currindex + 1; + VEC_iterate (operand_entry_t, *ops, i, oe); + i++) + { + tree t; + + if (TREE_CODE (oe->op) != SSA_NAME) + continue; + def2 = SSA_NAME_DEF_STMT (oe->op); + if (!is_gimple_assign (def2)) + continue; + rcode = gimple_assign_rhs_code (def2); + if (TREE_CODE_CLASS (rcode) != tcc_comparison) + continue; + + /* If we got here, we have a match. See if we can combine the + two comparisons. */ + if (opcode == BIT_IOR_EXPR) + t = maybe_fold_or_comparisons (lcode, op1, op2, + rcode, gimple_assign_rhs1 (def2), + gimple_assign_rhs2 (def2)); + else + t = maybe_fold_and_comparisons (lcode, op1, op2, + rcode, gimple_assign_rhs1 (def2), + gimple_assign_rhs2 (def2)); + if (!t) + continue; + + /* maybe_fold_and_comparisons and maybe_fold_or_comparisons + always give us a boolean_type_node value back. If the original + BIT_AND_EXPR or BIT_IOR_EXPR was of a wider integer type, + we need to convert. */ + if (!useless_type_conversion_p (TREE_TYPE (curr->op), TREE_TYPE (t))) + t = fold_convert (TREE_TYPE (curr->op), t); + + if (dump_file && (dump_flags & TDF_DETAILS)) + { + fprintf (dump_file, "Equivalence: "); + print_generic_expr (dump_file, curr->op, 0); + fprintf (dump_file, " %s ", op_symbol_code (opcode)); + print_generic_expr (dump_file, oe->op, 0); + fprintf (dump_file, " -> "); + print_generic_expr (dump_file, t, 0); + fprintf (dump_file, "\n"); + } + + /* Now we can delete oe, as it has been subsumed by the new combined + expression t. */ + VEC_ordered_remove (operand_entry_t, *ops, i); + reassociate_stats.ops_eliminated ++; + + /* If t is the same as curr->op, we're done. Otherwise we must + replace curr->op with t. Special case is if we got a constant + back, in which case we add it to the end instead of in place of + the current entry. */ + if (TREE_CODE (t) == INTEGER_CST) + { + VEC_ordered_remove (operand_entry_t, *ops, currindex); + add_to_ops_vec (ops, t); + } + else if (!operand_equal_p (t, curr->op, 0)) + { + tree tmpvar; + gimple sum; + enum tree_code subcode; + tree newop1; + tree newop2; + tmpvar = create_tmp_var (TREE_TYPE (t), NULL); + add_referenced_var (tmpvar); + extract_ops_from_tree (t, &subcode, &newop1, &newop2); + sum = build_and_add_sum (tmpvar, newop1, newop2, subcode); + curr->op = gimple_get_lhs (sum); + } + return true; + } + + return false; +} /* Perform various identities and other optimizations on the list of operand entries, stored in OPS. The tree code for the binary @@ -1220,7 +1331,8 @@ if (eliminate_not_pairs (opcode, ops, i, oe)) return; if (eliminate_duplicate_pair (opcode, ops, &done, i, oe, oelast) - || (!done && eliminate_plus_minus_pair (opcode, ops, i, oe))) + || (!done && eliminate_plus_minus_pair (opcode, ops, i, oe)) + || (!done && eliminate_redundant_comparison (opcode, ops, i, oe))) { if (done) return;