diff --git a/gcc/ChangeLog b/gcc/ChangeLog index 0e4f9873190..34b05fefa0a 100644 --- a/gcc/ChangeLog +++ b/gcc/ChangeLog @@ -1,3 +1,22 @@ +2007-04-27 Richard Guenther + + PR tree-optimization/30965 + PR tree-optimization/30978 + * Makefile.in (tree-ssa-forwprop.o): Depend on $(FLAGS_H). + * tree-ssa-forwprop.c (forward_propagate_into_cond_1): Remove. + (find_equivalent_equality_comparison): Likewise. + (simplify_cond): Likewise. + (get_prop_source_stmt): New helper. + (get_prop_dest_stmt): Likewise. + (can_propagate_from): Likewise. + (remove_prop_source_from_use): Likewise. + (combine_cond_expr_cond): Likewise. + (forward_propagate_comparison): New function. + (forward_propagate_into_cond): Rewrite to use fold for + tree combining. + (tree_ssa_forward_propagate_single_use_vars): Call + forward_propagate_comparison to propagate comparisons. + 2007-04-27 Richard Guenther PR tree-optimization/31715 diff --git a/gcc/Makefile.in b/gcc/Makefile.in index 13d93b046db..8856c641579 100644 --- a/gcc/Makefile.in +++ b/gcc/Makefile.in @@ -1934,7 +1934,7 @@ tree-ssa-dse.o : tree-ssa-dse.c $(CONFIG_H) $(SYSTEM_H) coretypes.h \ tree-ssa-forwprop.o : tree-ssa-forwprop.c $(CONFIG_H) $(SYSTEM_H) coretypes.h \ $(TM_H) $(GGC_H) $(TREE_H) $(RTL_H) $(TM_P_H) $(BASIC_BLOCK_H) \ $(TREE_FLOW_H) tree-pass.h $(TREE_DUMP_H) $(DIAGNOSTIC_H) $(TIMEVAR_H) \ - langhooks.h + langhooks.h $(FLAGS_H) tree-ssa-phiopt.o : tree-ssa-phiopt.c $(CONFIG_H) $(SYSTEM_H) coretypes.h \ $(TM_H) $(GGC_H) $(TREE_H) $(RTL_H) $(TM_P_H) $(BASIC_BLOCK_H) \ $(TREE_FLOW_H) tree-pass.h $(TREE_DUMP_H) langhooks.h $(FLAGS_H) \ diff --git a/gcc/testsuite/ChangeLog b/gcc/testsuite/ChangeLog index c9e601eb57d..49ed2ba7f83 100644 --- a/gcc/testsuite/ChangeLog +++ b/gcc/testsuite/ChangeLog @@ -1,3 +1,12 @@ +2007-04-27 Richard Guenther + + PR tree-optimization/30965 + PR tree-optimization/30978 + * gcc.dg/tree-ssa/pr30978.c: New testcase. + * gcc.dg/tree-ssa/bool-3.c: Remove XFAIL, explain why. + * gcc.dg/tree-ssa/ssa-fre-4.c: Use char instead of bool + * gcc.dg/strict-overflow-5.c: Adjust tree dump scanning. + 2007-04-27 Richard Guenther PR tree-optimization/31715 diff --git a/gcc/testsuite/gcc.dg/strict-overflow-5.c b/gcc/testsuite/gcc.dg/strict-overflow-5.c index 26e0174ba6a..816bfe440f0 100644 --- a/gcc/testsuite/gcc.dg/strict-overflow-5.c +++ b/gcc/testsuite/gcc.dg/strict-overflow-5.c @@ -1,5 +1,5 @@ /* { dg-do compile } */ -/* { dg-options "-fstrict-overflow -O2 -fdump-tree-final_cleanup" } */ +/* { dg-options "-fstrict-overflow -O2 -fdump-tree-optimized" } */ /* We can only unroll when using strict overflow semantics. */ @@ -14,6 +14,6 @@ int foo (int i) return r; } -/* { dg-final { scan-tree-dump-times "r = 3" 1 "final_cleanup" } } */ -/* { dg-final { cleanup-tree-dump "final_cleanup" } } */ +/* { dg-final { scan-tree-dump "return 3" "optimized" } } */ +/* { dg-final { cleanup-tree-dump "optimized" } } */ diff --git a/gcc/testsuite/gcc.dg/tree-ssa/bool-3.c b/gcc/testsuite/gcc.dg/tree-ssa/bool-3.c index f2ee9b864dd..8fa2a57c412 100644 --- a/gcc/testsuite/gcc.dg/tree-ssa/bool-3.c +++ b/gcc/testsuite/gcc.dg/tree-ssa/bool-3.c @@ -11,8 +11,8 @@ int f(_Bool x) return y; } -/* There should be no == 0. Though PHI-OPT or invert_truth does not - fold its tree. */ -/* { dg-final { scan-tree-dump-times "== 0" 0 "optimized" { xfail *-*-* } } } */ +/* There should be no == 0. Though PHI-OPT or invert_truth does not + fold its tree forwprop is able to clean up the mess. */ +/* { dg-final { scan-tree-dump-times "== 0" 0 "optimized" } } */ /* { dg-final { cleanup-tree-dump "optimized" } } */ diff --git a/gcc/testsuite/gcc.dg/tree-ssa/pr30978.c b/gcc/testsuite/gcc.dg/tree-ssa/pr30978.c new file mode 100644 index 00000000000..5444e2d197c --- /dev/null +++ b/gcc/testsuite/gcc.dg/tree-ssa/pr30978.c @@ -0,0 +1,14 @@ +/* { dg-do compile } */ +/* { dg-options "-O -fdump-tree-optimized" } */ + +int foo(int a) +{ + unsigned int b = a > 0; + char c = b; + _Bool d = c == 0; + int e = !d; + return e; +} + +/* { dg-final { scan-tree-dump "return a > 0;" "optimized" } } */ +/* { dg-final { cleanup-tree-dump "optimized" } } */ diff --git a/gcc/testsuite/gcc.dg/tree-ssa/ssa-fre-4.c b/gcc/testsuite/gcc.dg/tree-ssa/ssa-fre-4.c index 3966e6a8c43..bc62255d666 100644 --- a/gcc/testsuite/gcc.dg/tree-ssa/ssa-fre-4.c +++ b/gcc/testsuite/gcc.dg/tree-ssa/ssa-fre-4.c @@ -3,13 +3,12 @@ /* From PR21608. */ -#define bool _Bool -static inline bool wrap(bool f) { return f; } -bool bar(bool f) +static inline char wrap(char f) { return f; } +char bar(char f) { return wrap(f); } -/* { dg-final { scan-tree-dump "Replaced \\\(_Bool\\\) .*with " "fre" } } */ +/* { dg-final { scan-tree-dump "Replaced \\\(char\\\) .*with " "fre" } } */ /* { dg-final { scan-tree-dump "Replaced \\\(int\\\) .*with " "fre" } } */ /* { dg-final { cleanup-tree-dump "fre" } } */ diff --git a/gcc/tree-ssa-forwprop.c b/gcc/tree-ssa-forwprop.c index 4e7f19f62c8..08bc5ff31c3 100644 --- a/gcc/tree-ssa-forwprop.c +++ b/gcc/tree-ssa-forwprop.c @@ -33,6 +33,7 @@ Boston, MA 02110-1301, USA. */ #include "tree-pass.h" #include "tree-dump.h" #include "langhooks.h" +#include "flags.h" /* This pass propagates the RHS of assignment statements into use sites of the LHS of the assignment. It's basically a specialized @@ -155,424 +156,303 @@ static bool forward_propagate_addr_expr (tree name, tree rhs); static bool cfg_changed; -/* Given an SSA_NAME VAR, return true if and only if VAR is defined by - a comparison. */ +/* Get the next statement we can propagate NAMEs value into skipping + trivial copies. Returns the statement that is suitable as a + propagation destination or NULL_TREE if there is no such one. + This only returns destinations in a single-use chain. FINAL_NAME_P + if non-NULL is written to the ssa name that represents the use. */ + +static tree +get_prop_dest_stmt (tree name, tree *final_name_p) +{ + use_operand_p use; + tree use_stmt; + + do { + /* If name has multiple uses, bail out. */ + if (!single_imm_use (name, &use, &use_stmt)) + return NULL_TREE; + + /* If this is not a trivial copy, we found it. */ + if (TREE_CODE (use_stmt) != GIMPLE_MODIFY_STMT + || TREE_CODE (GIMPLE_STMT_OPERAND (use_stmt, 0)) != SSA_NAME + || GIMPLE_STMT_OPERAND (use_stmt, 1) != name) + break; + + /* Continue searching uses of the copy destination. */ + name = GIMPLE_STMT_OPERAND (use_stmt, 0); + } while (1); + + if (final_name_p) + *final_name_p = name; + + return use_stmt; +} + +/* Get the statement we can propagate from into NAME skipping + trivial copies. Returns the statement which defines the + propagation source or NULL_TREE if there is no such one. + If SINGLE_USE_ONLY is set considers only sources which have + a single use chain up to NAME. If SINGLE_USE_P is non-null, + it is set to whether the chain to NAME is a single use chain + or not. SINGLE_USE_P is not written to if SINGLE_USE_ONLY is set. */ + +static tree +get_prop_source_stmt (tree name, bool single_use_only, bool *single_use_p) +{ + bool single_use = true; + + do { + tree def_stmt = SSA_NAME_DEF_STMT (name); + + if (!has_single_use (name)) + { + single_use = false; + if (single_use_only) + return NULL_TREE; + } + + /* If name is defined by a PHI node or is the default def, bail out. */ + if (TREE_CODE (def_stmt) != GIMPLE_MODIFY_STMT) + return NULL_TREE; + + /* If name is not a simple copy destination, we found it. */ + if (TREE_CODE (GIMPLE_STMT_OPERAND (def_stmt, 1)) != SSA_NAME) + { + if (!single_use_only && single_use_p) + *single_use_p = single_use; + + return def_stmt; + } + + /* Continue searching the def of the copy source name. */ + name = GIMPLE_STMT_OPERAND (def_stmt, 1); + } while (1); +} + +/* Checks if the destination ssa name in DEF_STMT can be used as + propagation source. Returns true if so, otherwise false. */ static bool -ssa_name_defined_by_comparison_p (tree var) +can_propagate_from (tree def_stmt) { - tree def = SSA_NAME_DEF_STMT (var); + tree rhs = GIMPLE_STMT_OPERAND (def_stmt, 1); - if (TREE_CODE (def) == GIMPLE_MODIFY_STMT) + /* We cannot propagate ssa names that occur in abnormal phi nodes. */ + switch (TREE_CODE_LENGTH (TREE_CODE (rhs))) { - tree rhs = GIMPLE_STMT_OPERAND (def, 1); - return COMPARISON_CLASS_P (rhs); + case 3: + if (TREE_OPERAND (rhs, 2) != NULL_TREE + && TREE_CODE (TREE_OPERAND (rhs, 2)) == SSA_NAME + && SSA_NAME_OCCURS_IN_ABNORMAL_PHI (TREE_OPERAND (rhs, 2))) + return false; + case 2: + if (TREE_OPERAND (rhs, 1) != NULL_TREE + && TREE_CODE (TREE_OPERAND (rhs, 1)) == SSA_NAME + && SSA_NAME_OCCURS_IN_ABNORMAL_PHI (TREE_OPERAND (rhs, 1))) + return false; + case 1: + if (TREE_OPERAND (rhs, 0) != NULL_TREE + && TREE_CODE (TREE_OPERAND (rhs, 0)) == SSA_NAME + && SSA_NAME_OCCURS_IN_ABNORMAL_PHI (TREE_OPERAND (rhs, 0))) + return false; + break; + + default: + return false; } - return 0; + /* If the definition is a conversion of a pointer to a function type, + then we can not apply optimizations as some targets require function + pointers to be canonicalized and in this case this optimization could + eliminate a necessary canonicalization. */ + if ((TREE_CODE (rhs) == NOP_EXPR + || TREE_CODE (rhs) == CONVERT_EXPR) + && POINTER_TYPE_P (TREE_TYPE (TREE_OPERAND (rhs, 0))) + && TREE_CODE (TREE_TYPE (TREE_TYPE + (TREE_OPERAND (rhs, 0)))) == FUNCTION_TYPE) + return false; + + return true; } -/* Forward propagate a single-use variable into COND once. Return a - new condition if successful. Return NULL_TREE otherwise. */ +/* Remove a copy chain ending in NAME along the defs but not + further or including UP_TO_STMT. If NAME was replaced in + its only use then this function can be used to clean up + dead stmts. Returns true if UP_TO_STMT can be removed + as well, otherwise false. */ + +static bool +remove_prop_source_from_use (tree name, tree up_to_stmt) +{ + block_stmt_iterator bsi; + tree stmt; + + do { + if (!has_zero_uses (name)) + return false; + + stmt = SSA_NAME_DEF_STMT (name); + if (stmt == up_to_stmt) + return true; + + bsi = bsi_for_stmt (stmt); + release_defs (stmt); + bsi_remove (&bsi, true); + + name = GIMPLE_STMT_OPERAND (stmt, 1); + } while (TREE_CODE (name) == SSA_NAME); + + return false; +} + +/* Combine OP0 CODE OP1 in the context of a COND_EXPR. Returns + the folded result in a form suitable for COND_EXPR_COND or + NULL_TREE, if there is no suitable simplified form. If + INVARIANT_ONLY is true only gimple_min_invariant results are + considered simplified. */ static tree -forward_propagate_into_cond_1 (tree cond, tree *test_var_p) +combine_cond_expr_cond (enum tree_code code, tree type, + tree op0, tree op1, bool invariant_only) { - tree new_cond = NULL_TREE; - enum tree_code cond_code = TREE_CODE (cond); - tree test_var = NULL_TREE; - tree def; - tree def_rhs; + tree t; - /* If the condition is not a lone variable or an equality test of an - SSA_NAME against an integral constant, then we do not have an - optimizable case. + gcc_assert (TREE_CODE_CLASS (code) == tcc_comparison); - Note these conditions also ensure the COND_EXPR has no - virtual operands or other side effects. */ - if (cond_code != SSA_NAME - && !((cond_code == EQ_EXPR || cond_code == NE_EXPR) - && TREE_CODE (TREE_OPERAND (cond, 0)) == SSA_NAME - && CONSTANT_CLASS_P (TREE_OPERAND (cond, 1)) - && INTEGRAL_TYPE_P (TREE_TYPE (TREE_OPERAND (cond, 1))))) + t = fold_binary (code, type, op0, op1); + if (!t) return NULL_TREE; - /* Extract the single variable used in the test into TEST_VAR. */ - if (cond_code == SSA_NAME) - test_var = cond; - else - test_var = TREE_OPERAND (cond, 0); + /* Require that we got a boolean type out if we put one in. */ + gcc_assert (TREE_CODE (TREE_TYPE (t)) == TREE_CODE (type)); - /* Now get the defining statement for TEST_VAR. Skip this case if - it's not defined by some GIMPLE_MODIFY_STMT. */ - def = SSA_NAME_DEF_STMT (test_var); - if (TREE_CODE (def) != GIMPLE_MODIFY_STMT) + /* For (bool)x use x != 0. */ + if (TREE_CODE (t) == NOP_EXPR + && TREE_TYPE (t) == boolean_type_node) + { + tree top0 = TREE_OPERAND (t, 0); + t = build2 (NE_EXPR, type, + top0, build_int_cst (TREE_TYPE (top0), 0)); + } + /* For !x use x == 0. */ + else if (TREE_CODE (t) == TRUTH_NOT_EXPR) + { + tree top0 = TREE_OPERAND (t, 0); + t = build2 (EQ_EXPR, type, + top0, build_int_cst (TREE_TYPE (top0), 0)); + } + /* For cmp ? 1 : 0 use cmp. */ + else if (TREE_CODE (t) == COND_EXPR + && COMPARISON_CLASS_P (TREE_OPERAND (t, 0)) + && integer_onep (TREE_OPERAND (t, 1)) + && integer_zerop (TREE_OPERAND (t, 2))) + { + tree top0 = TREE_OPERAND (t, 0); + t = build2 (TREE_CODE (top0), type, + TREE_OPERAND (top0, 0), TREE_OPERAND (top0, 1)); + } + + /* Bail out if we required an invariant but didn't get one. */ + if (invariant_only + && !is_gimple_min_invariant (t)) return NULL_TREE; - def_rhs = GIMPLE_STMT_OPERAND (def, 1); + /* A valid conditional for a COND_EXPR is either a gimple value + or a comparison with two gimple value operands. */ + if (is_gimple_val (t) + || (COMPARISON_CLASS_P (t) + && is_gimple_val (TREE_OPERAND (t, 0)) + && is_gimple_val (TREE_OPERAND (t, 1)))) + return t; - /* If TEST_VAR is set by adding or subtracting a constant - from an SSA_NAME, then it is interesting to us as we - can adjust the constant in the conditional and thus - eliminate the arithmetic operation. */ - if (TREE_CODE (def_rhs) == PLUS_EXPR - || TREE_CODE (def_rhs) == MINUS_EXPR) - { - tree op0 = TREE_OPERAND (def_rhs, 0); - tree op1 = TREE_OPERAND (def_rhs, 1); - - /* The first operand must be an SSA_NAME and the second - operand must be a constant. */ - if (TREE_CODE (op0) != SSA_NAME - || !CONSTANT_CLASS_P (op1) - || !INTEGRAL_TYPE_P (TREE_TYPE (op1))) - return NULL_TREE; - - /* Don't propagate if the first operand occurs in - an abnormal PHI. */ - if (SSA_NAME_OCCURS_IN_ABNORMAL_PHI (op0)) - return NULL_TREE; - - if (has_single_use (test_var)) - { - enum tree_code new_code; - tree t; - - /* If the variable was defined via X + C, then we must - subtract C from the constant in the conditional. - Otherwise we add C to the constant in the - conditional. The result must fold into a valid - gimple operand to be optimizable. */ - new_code = (TREE_CODE (def_rhs) == PLUS_EXPR - ? MINUS_EXPR : PLUS_EXPR); - t = int_const_binop (new_code, TREE_OPERAND (cond, 1), op1, 0); - if (!is_gimple_val (t)) - return NULL_TREE; - - new_cond = build2 (cond_code, boolean_type_node, op0, t); - } - } - - /* These cases require comparisons of a naked SSA_NAME or - comparison of an SSA_NAME against zero or one. */ - else if (TREE_CODE (cond) == SSA_NAME - || integer_zerop (TREE_OPERAND (cond, 1)) - || integer_onep (TREE_OPERAND (cond, 1))) - { - /* If TEST_VAR is set from a relational operation - between two SSA_NAMEs or a combination of an SSA_NAME - and a constant, then it is interesting. */ - if (COMPARISON_CLASS_P (def_rhs)) - { - tree op0 = TREE_OPERAND (def_rhs, 0); - tree op1 = TREE_OPERAND (def_rhs, 1); - - /* Both operands of DEF_RHS must be SSA_NAMEs or - constants. */ - if ((TREE_CODE (op0) != SSA_NAME - && !is_gimple_min_invariant (op0)) - || (TREE_CODE (op1) != SSA_NAME - && !is_gimple_min_invariant (op1))) - return NULL_TREE; - - /* Don't propagate if the first operand occurs in - an abnormal PHI. */ - if (TREE_CODE (op0) == SSA_NAME - && SSA_NAME_OCCURS_IN_ABNORMAL_PHI (op0)) - return NULL_TREE; - - /* Don't propagate if the second operand occurs in - an abnormal PHI. */ - if (TREE_CODE (op1) == SSA_NAME - && SSA_NAME_OCCURS_IN_ABNORMAL_PHI (op1)) - return NULL_TREE; - - if (has_single_use (test_var)) - { - /* TEST_VAR was set from a relational operator. */ - new_cond = build2 (TREE_CODE (def_rhs), - boolean_type_node, op0, op1); - - /* Invert the conditional if necessary. */ - if ((cond_code == EQ_EXPR - && integer_zerop (TREE_OPERAND (cond, 1))) - || (cond_code == NE_EXPR - && integer_onep (TREE_OPERAND (cond, 1)))) - { - new_cond = invert_truthvalue (new_cond); - - /* If we did not get a simple relational - expression or bare SSA_NAME, then we can - not optimize this case. */ - if (!COMPARISON_CLASS_P (new_cond) - && TREE_CODE (new_cond) != SSA_NAME) - new_cond = NULL_TREE; - } - } - } - - /* If TEST_VAR is set from a TRUTH_NOT_EXPR, then it - is interesting. */ - else if (TREE_CODE (def_rhs) == TRUTH_NOT_EXPR) - { - enum tree_code new_code; - - def_rhs = TREE_OPERAND (def_rhs, 0); - - /* DEF_RHS must be an SSA_NAME or constant. */ - if (TREE_CODE (def_rhs) != SSA_NAME - && !is_gimple_min_invariant (def_rhs)) - return NULL_TREE; - - /* Don't propagate if the operand occurs in - an abnormal PHI. */ - if (TREE_CODE (def_rhs) == SSA_NAME - && SSA_NAME_OCCURS_IN_ABNORMAL_PHI (def_rhs)) - return NULL_TREE; - - if (cond_code == SSA_NAME - || (cond_code == NE_EXPR - && integer_zerop (TREE_OPERAND (cond, 1))) - || (cond_code == EQ_EXPR - && integer_onep (TREE_OPERAND (cond, 1)))) - new_code = EQ_EXPR; - else - new_code = NE_EXPR; - - new_cond = build2 (new_code, boolean_type_node, def_rhs, - fold_convert (TREE_TYPE (def_rhs), - integer_zero_node)); - } - - /* If TEST_VAR was set from a cast of an integer type - to a boolean type or a cast of a boolean to an - integral, then it is interesting. */ - else if (TREE_CODE (def_rhs) == NOP_EXPR - || TREE_CODE (def_rhs) == CONVERT_EXPR) - { - tree outer_type; - tree inner_type; - - outer_type = TREE_TYPE (def_rhs); - inner_type = TREE_TYPE (TREE_OPERAND (def_rhs, 0)); - - if ((TREE_CODE (outer_type) == BOOLEAN_TYPE - && INTEGRAL_TYPE_P (inner_type)) - || (TREE_CODE (inner_type) == BOOLEAN_TYPE - && INTEGRAL_TYPE_P (outer_type))) - ; - else if (INTEGRAL_TYPE_P (outer_type) - && INTEGRAL_TYPE_P (inner_type) - && TREE_CODE (TREE_OPERAND (def_rhs, 0)) == SSA_NAME - && ssa_name_defined_by_comparison_p (TREE_OPERAND (def_rhs, - 0))) - ; - else - return NULL_TREE; - - /* Don't propagate if the operand occurs in - an abnormal PHI. */ - if (TREE_CODE (TREE_OPERAND (def_rhs, 0)) == SSA_NAME - && SSA_NAME_OCCURS_IN_ABNORMAL_PHI (TREE_OPERAND - (def_rhs, 0))) - return NULL_TREE; - - if (has_single_use (test_var)) - { - enum tree_code new_code; - tree new_arg; - - if (cond_code == SSA_NAME - || (cond_code == NE_EXPR - && integer_zerop (TREE_OPERAND (cond, 1))) - || (cond_code == EQ_EXPR - && integer_onep (TREE_OPERAND (cond, 1)))) - new_code = NE_EXPR; - else - new_code = EQ_EXPR; - - new_arg = TREE_OPERAND (def_rhs, 0); - new_cond = build2 (new_code, boolean_type_node, new_arg, - fold_convert (TREE_TYPE (new_arg), - integer_zero_node)); - } - } - } - - *test_var_p = test_var; - return new_cond; + return NULL_TREE; } -/* COND is a condition of the form: - - x == const or x != const - - Look back to x's defining statement and see if x is defined as - - x = (type) y; - - If const is unchanged if we convert it to type, then we can build - the equivalent expression: - - - y == const or y != const - - Which may allow further optimizations. - - Return the equivalent comparison or NULL if no such equivalent comparison - was found. */ - -static tree -find_equivalent_equality_comparison (tree cond) -{ - tree op0 = TREE_OPERAND (cond, 0); - tree op1 = TREE_OPERAND (cond, 1); - tree def_stmt = SSA_NAME_DEF_STMT (op0); - - while (def_stmt - && TREE_CODE (def_stmt) == GIMPLE_MODIFY_STMT - && TREE_CODE (GIMPLE_STMT_OPERAND (def_stmt, 1)) == SSA_NAME) - def_stmt = SSA_NAME_DEF_STMT (GIMPLE_STMT_OPERAND (def_stmt, 1)); - - /* OP0 might have been a parameter, so first make sure it - was defined by a GIMPLE_MODIFY_STMT. */ - if (def_stmt && TREE_CODE (def_stmt) == GIMPLE_MODIFY_STMT) - { - tree def_rhs = GIMPLE_STMT_OPERAND (def_stmt, 1); - - /* If either operand to the comparison is a pointer to - a function, then we can not apply this optimization - as some targets require function pointers to be - canonicalized and in this case this optimization would - eliminate a necessary canonicalization. */ - if ((POINTER_TYPE_P (TREE_TYPE (op0)) - && TREE_CODE (TREE_TYPE (TREE_TYPE (op0))) == FUNCTION_TYPE) - || (POINTER_TYPE_P (TREE_TYPE (op1)) - && TREE_CODE (TREE_TYPE (TREE_TYPE (op1))) == FUNCTION_TYPE)) - return NULL; - - /* Now make sure the RHS of the GIMPLE_MODIFY_STMT is a typecast. */ - if ((TREE_CODE (def_rhs) == NOP_EXPR - || TREE_CODE (def_rhs) == CONVERT_EXPR) - && TREE_CODE (TREE_OPERAND (def_rhs, 0)) == SSA_NAME) - { - tree def_rhs_inner = TREE_OPERAND (def_rhs, 0); - tree def_rhs_inner_type = TREE_TYPE (def_rhs_inner); - tree new; - - if (TYPE_PRECISION (def_rhs_inner_type) - > TYPE_PRECISION (TREE_TYPE (def_rhs))) - return NULL; - - /* If the inner type of the conversion is a pointer to - a function, then we can not apply this optimization - as some targets require function pointers to be - canonicalized. This optimization would result in - canonicalization of the pointer when it was not originally - needed/intended. */ - if (POINTER_TYPE_P (def_rhs_inner_type) - && TREE_CODE (TREE_TYPE (def_rhs_inner_type)) == FUNCTION_TYPE) - return NULL; - - /* What we want to prove is that if we convert OP1 to - the type of the object inside the NOP_EXPR that the - result is still equivalent to SRC. - - If that is true, the build and return new equivalent - condition which uses the source of the typecast and the - new constant (which has only changed its type). */ - new = fold_build1 (TREE_CODE (def_rhs), def_rhs_inner_type, op1); - STRIP_USELESS_TYPE_CONVERSION (new); - if (is_gimple_val (new) && tree_int_cst_equal (new, op1)) - return build2 (TREE_CODE (cond), TREE_TYPE (cond), - def_rhs_inner, new); - } - } - return NULL; -} - -/* EXPR is a COND_EXPR - STMT is the statement containing EXPR. - - This routine attempts to find equivalent forms of the condition - which we may be able to optimize better. */ - -static void -simplify_cond (tree cond_expr, tree stmt) -{ - tree cond = COND_EXPR_COND (cond_expr); - - if (COMPARISON_CLASS_P (cond)) - { - tree op0 = TREE_OPERAND (cond, 0); - tree op1 = TREE_OPERAND (cond, 1); - - if (TREE_CODE (op0) == SSA_NAME && is_gimple_min_invariant (op1)) - { - /* First see if we have test of an SSA_NAME against a constant - where the SSA_NAME is defined by an earlier typecast which - is irrelevant when performing tests against the given - constant. */ - if (TREE_CODE (cond) == EQ_EXPR || TREE_CODE (cond) == NE_EXPR) - { - tree new_cond = find_equivalent_equality_comparison (cond); - - if (new_cond) - { - COND_EXPR_COND (cond_expr) = new_cond; - update_stmt (stmt); - } - } - } - } -} - -/* Forward propagate a single-use variable into COND_EXPR as many - times as possible. */ +/* Propagate from the ssa name definition statements of COND_EXPR + in statement STMT into the conditional if that simplifies it. */ static void forward_propagate_into_cond (tree cond_expr, tree stmt) { - gcc_assert (TREE_CODE (cond_expr) == COND_EXPR); + do { + tree tmp = NULL_TREE; + tree cond = COND_EXPR_COND (cond_expr); + tree name, def_stmt, rhs; + bool single_use_p; - while (1) - { - tree test_var = NULL_TREE; - tree cond = COND_EXPR_COND (cond_expr); - tree new_cond = forward_propagate_into_cond_1 (cond, &test_var); + /* We can do tree combining on SSA_NAME and comparison expressions. */ + if (COMPARISON_CLASS_P (cond) + && TREE_CODE (TREE_OPERAND (cond, 0)) == SSA_NAME) + { + /* For comparisons use the first operand, that is likely to + simplify comparisons against constants. */ + name = TREE_OPERAND (cond, 0); + def_stmt = get_prop_source_stmt (name, false, &single_use_p); + if (def_stmt != NULL_TREE + && can_propagate_from (def_stmt)) + { + tree op1 = TREE_OPERAND (cond, 1); + rhs = GIMPLE_STMT_OPERAND (def_stmt, 1); + tmp = combine_cond_expr_cond (TREE_CODE (cond), boolean_type_node, + fold_convert (TREE_TYPE (op1), rhs), + op1, !single_use_p); + } + /* If that wasn't successful, try the second operand. */ + if (tmp == NULL_TREE + && TREE_CODE (TREE_OPERAND (cond, 1)) == SSA_NAME) + { + tree op0 = TREE_OPERAND (cond, 0); + name = TREE_OPERAND (cond, 1); + def_stmt = get_prop_source_stmt (name, false, &single_use_p); + if (def_stmt == NULL_TREE + || !can_propagate_from (def_stmt)) + return; - /* Return if unsuccessful. */ - if (new_cond == NULL_TREE) - break; + rhs = GIMPLE_STMT_OPERAND (def_stmt, 1); + tmp = combine_cond_expr_cond (TREE_CODE (cond), boolean_type_node, + op0, + fold_convert (TREE_TYPE (op0), rhs), + !single_use_p); + } + } + else if (TREE_CODE (cond) == SSA_NAME) + { + name = cond; + def_stmt = get_prop_source_stmt (name, true, NULL); + if (def_stmt == NULL_TREE + || !can_propagate_from (def_stmt)) + return; - /* Dump details. */ - if (dump_file && (dump_flags & TDF_DETAILS)) - { - fprintf (dump_file, " Replaced '"); - print_generic_expr (dump_file, cond, dump_flags); - fprintf (dump_file, "' with '"); - print_generic_expr (dump_file, new_cond, dump_flags); - fprintf (dump_file, "'\n"); - } + rhs = GIMPLE_STMT_OPERAND (def_stmt, 1); + tmp = combine_cond_expr_cond (NE_EXPR, boolean_type_node, rhs, + build_int_cst (TREE_TYPE (rhs), 0), + false); + } - COND_EXPR_COND (cond_expr) = new_cond; - update_stmt (stmt); + if (tmp) + { + if (dump_file && tmp) + { + fprintf (dump_file, " Replaced '"); + print_generic_expr (dump_file, cond, 0); + fprintf (dump_file, "' with '"); + print_generic_expr (dump_file, tmp, 0); + fprintf (dump_file, "'\n"); + } - if (has_zero_uses (test_var)) - { - tree def = SSA_NAME_DEF_STMT (test_var); - block_stmt_iterator bsi = bsi_for_stmt (def); - bsi_remove (&bsi, true); - release_defs (def); - } - } + COND_EXPR_COND (cond_expr) = unshare_expr (tmp); + update_stmt (stmt); - /* There are further simplifications that can be performed - on COND_EXPRs. Specifically, when comparing an SSA_NAME - against a constant where the SSA_NAME is the result of a - conversion. Perhaps this should be folded into the rest - of the COND_EXPR simplification code. */ - simplify_cond (cond_expr, stmt); + /* Remove defining statements. */ + remove_prop_source_from_use (name, NULL); + + /* Continue combining. */ + continue; + } + + break; + } while (1); } /* We've just substituted an ADDR_EXPR into stmt. Update all the @@ -861,6 +741,104 @@ forward_propagate_addr_expr (tree name, tree rhs) return all; } +/* Forward propagate the comparison COND defined in STMT like + cond_1 = x CMP y to uses of the form + a_1 = (T')cond_1 + a_1 = !cond_1 + a_1 = cond_1 != 0 + Returns true if stmt is now unused. */ + +static bool +forward_propagate_comparison (tree cond, tree stmt) +{ + tree name = GIMPLE_STMT_OPERAND (stmt, 0); + tree use_stmt, tmp = NULL_TREE; + + /* Don't propagate ssa names that occur in abnormal phis. */ + if ((TREE_CODE (TREE_OPERAND (cond, 0)) == SSA_NAME + && SSA_NAME_OCCURS_IN_ABNORMAL_PHI (TREE_OPERAND (cond, 0))) + || (TREE_CODE (TREE_OPERAND (cond, 1)) == SSA_NAME + && SSA_NAME_OCCURS_IN_ABNORMAL_PHI (TREE_OPERAND (cond, 1)))) + return false; + + /* Do not un-cse comparisons. But propagate through copies. */ + use_stmt = get_prop_dest_stmt (name, &name); + if (use_stmt == NULL_TREE) + return false; + + /* Conversion of the condition result to another integral type. */ + if (TREE_CODE (use_stmt) == GIMPLE_MODIFY_STMT + && (TREE_CODE (GIMPLE_STMT_OPERAND (use_stmt, 1)) == CONVERT_EXPR + || TREE_CODE (GIMPLE_STMT_OPERAND (use_stmt, 1)) == NOP_EXPR + || COMPARISON_CLASS_P (GIMPLE_STMT_OPERAND (use_stmt, 1)) + || TREE_CODE (GIMPLE_STMT_OPERAND (use_stmt, 1)) == TRUTH_NOT_EXPR) + && INTEGRAL_TYPE_P (TREE_TYPE (GIMPLE_STMT_OPERAND (use_stmt, 0)))) + { + tree lhs = GIMPLE_STMT_OPERAND (use_stmt, 0); + tree rhs = GIMPLE_STMT_OPERAND (use_stmt, 1); + + /* We can propagate the condition into a conversion. */ + if (TREE_CODE (rhs) == CONVERT_EXPR + || TREE_CODE (rhs) == NOP_EXPR) + { + /* Avoid using fold here as that may create a COND_EXPR with + non-boolean condition as canonical form. */ + tmp = build2 (TREE_CODE (cond), TREE_TYPE (lhs), + TREE_OPERAND (cond, 0), TREE_OPERAND (cond, 1)); + } + /* We can propagate the condition into X op CST where op + is EQ_EXRP or NE_EXPR and CST is either one or zero. */ + else if (COMPARISON_CLASS_P (rhs) + && TREE_CODE (TREE_OPERAND (rhs, 0)) == SSA_NAME + && TREE_CODE (TREE_OPERAND (rhs, 1)) == INTEGER_CST) + { + enum tree_code code = TREE_CODE (rhs); + tree cst = TREE_OPERAND (rhs, 1); + + tmp = combine_cond_expr_cond (code, TREE_TYPE (lhs), + fold_convert (TREE_TYPE (cst), cond), + cst, false); + if (tmp == NULL_TREE) + return false; + } + /* We can propagate the condition into a statement that + computes the logical negation of the comparison result. */ + else if (TREE_CODE (rhs) == TRUTH_NOT_EXPR) + { + tree type = TREE_TYPE (TREE_OPERAND (cond, 0)); + bool nans = HONOR_NANS (TYPE_MODE (type)); + enum tree_code code; + code = invert_tree_comparison (TREE_CODE (cond), nans); + if (code == ERROR_MARK) + return false; + + tmp = build2 (code, TREE_TYPE (lhs), TREE_OPERAND (cond, 0), + TREE_OPERAND (cond, 1)); + } + else + return false; + + GIMPLE_STMT_OPERAND (use_stmt, 1) = unshare_expr (tmp); + update_stmt (use_stmt); + + /* Remove defining statements. */ + remove_prop_source_from_use (name, stmt); + + if (dump_file && (dump_flags & TDF_DETAILS)) + { + fprintf (dump_file, " Replaced '"); + print_generic_expr (dump_file, rhs, dump_flags); + fprintf (dump_file, "' with '"); + print_generic_expr (dump_file, tmp, dump_flags); + fprintf (dump_file, "'\n"); + } + + return true; + } + + return false; +} + /* If we have lhs = ~x (STMT), look and see if earlier we had x = ~y. If so, we can change STMT into lhs = y which can later be copy propagated. Similarly for negation. @@ -1011,6 +989,17 @@ tree_ssa_forward_propagate_single_use_vars (void) forward_propagate_into_cond (rhs, stmt); bsi_next (&bsi); } + else if (COMPARISON_CLASS_P (rhs)) + { + if (forward_propagate_comparison (rhs, stmt)) + { + release_defs (stmt); + todoflags |= TODO_remove_unused_locals; + bsi_remove (&bsi, true); + } + else + bsi_next (&bsi); + } else bsi_next (&bsi); }