On Fri, 27 May 2011, Richard Guenther wrote: > > This patch fixes the lack of combining comparisons that appear > on the RHS of assignments (as opposed to in gimple-conds or > in cond-exprs on the RHS of assignments). > > Bootstrapped on x86_64-unknown-linux-gnu, testing in progress.
Testing revealed strict-overflow warning issues (sigh), so the following is what I ended up committing. Richard. 2011-05-27 Richard Guenther <rguent...@suse.de> * tree-ssa-forwprop.c (forward_propagate_into_comparison): New function split out from ... (forward_propagate_into_gimple_cond): ... here. Adjust. (forward_propagate_into_cond): Likewise. (forward_propagate_comparison): Also propagate into comparisons on assignment RHS. Change return value to behave similar to forward_propagate_into_cond. (tree_ssa_forward_propagate_single_use_vars): Handle strict-overflow warnings properly for forward_propagate_comparison. Index: gcc/tree-ssa-forwprop.c =================================================================== *** gcc/tree-ssa-forwprop.c (revision 174331) --- gcc/tree-ssa-forwprop.c (working copy) *************** combine_cond_expr_cond (location_t loc, *** 387,392 **** --- 387,581 ---- return t; } + /* Combine the comparison OP0 CODE OP1 at LOC with the defining statements + of its operand. Return a new comparison tree or NULL_TREE if there + were no simplifying combines. */ + + static tree + forward_propagate_into_comparison (location_t loc, + enum tree_code code, tree type, + tree op0, tree op1) + { + tree tmp = NULL_TREE; + tree rhs0 = NULL_TREE, rhs1 = NULL_TREE; + bool single_use0_p = false, single_use1_p = false; + + /* For comparisons use the first operand, that is likely to + simplify comparisons against constants. */ + if (TREE_CODE (op0) == SSA_NAME) + { + gimple def_stmt = get_prop_source_stmt (op0, false, &single_use0_p); + if (def_stmt && can_propagate_from (def_stmt)) + { + rhs0 = rhs_to_tree (TREE_TYPE (op1), def_stmt); + tmp = combine_cond_expr_cond (loc, code, type, + rhs0, op1, !single_use0_p); + if (tmp) + return tmp; + } + } + + /* If that wasn't successful, try the second operand. */ + if (TREE_CODE (op1) == SSA_NAME) + { + gimple def_stmt = get_prop_source_stmt (op1, false, &single_use1_p); + if (def_stmt && can_propagate_from (def_stmt)) + { + rhs1 = rhs_to_tree (TREE_TYPE (op0), def_stmt); + tmp = combine_cond_expr_cond (loc, code, type, + op0, rhs1, !single_use1_p); + if (tmp) + return tmp; + } + } + + /* If that wasn't successful either, try both operands. */ + if (rhs0 != NULL_TREE + && rhs1 != NULL_TREE) + tmp = combine_cond_expr_cond (loc, code, type, + rhs0, rhs1, + !(single_use0_p && single_use1_p)); + + return tmp; + } + + /* Forward propagate the comparison 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 1 if a transformation was done and 2 if the CFG should + be cleaned up. Else returns 0. */ + + static int + forward_propagate_comparison (gimple stmt) + { + tree name = gimple_assign_lhs (stmt); + gimple use_stmt; + tree tmp = NULL_TREE; + int did_something = 0; + + /* Combine the comparison with defining statements. */ + do + { + tmp = forward_propagate_into_comparison (gimple_location (stmt), + gimple_assign_rhs_code (stmt), + TREE_TYPE (name), + gimple_assign_rhs1 (stmt), + gimple_assign_rhs2 (stmt)); + if (tmp) + { + gimple_stmt_iterator gsi = gsi_for_stmt (stmt); + bool inv = is_gimple_min_invariant (tmp); + gimple_assign_set_rhs_from_tree (&gsi, tmp); + gcc_assert (gsi_stmt (gsi) == stmt); + update_stmt (stmt); + did_something = MAX (did_something, inv ? 2 : 1); + if (TREE_CODE_CLASS (gimple_assign_rhs_code (stmt)) != tcc_comparison) + return did_something; + } + } + while (tmp); + + /* Don't propagate ssa names that occur in abnormal phis. */ + if ((TREE_CODE (gimple_assign_rhs1 (stmt)) == SSA_NAME + && SSA_NAME_OCCURS_IN_ABNORMAL_PHI (gimple_assign_rhs1 (stmt))) + || (TREE_CODE (gimple_assign_rhs2 (stmt)) == SSA_NAME + && SSA_NAME_OCCURS_IN_ABNORMAL_PHI (gimple_assign_rhs2 (stmt)))) + return did_something; + + /* Do not un-cse comparisons. But propagate through copies. */ + use_stmt = get_prop_dest_stmt (name, &name); + if (!use_stmt) + return did_something; + + /* Conversion of the condition result to another integral type. */ + if (is_gimple_assign (use_stmt) + && (CONVERT_EXPR_CODE_P (gimple_assign_rhs_code (use_stmt)) + || TREE_CODE_CLASS (gimple_assign_rhs_code (use_stmt)) + == tcc_comparison + || gimple_assign_rhs_code (use_stmt) == TRUTH_NOT_EXPR) + && INTEGRAL_TYPE_P (TREE_TYPE (gimple_assign_lhs (use_stmt)))) + { + tree lhs = gimple_assign_lhs (use_stmt); + + /* We can propagate the condition into a conversion. */ + if (CONVERT_EXPR_CODE_P (gimple_assign_rhs_code (use_stmt))) + { + /* Avoid using fold here as that may create a COND_EXPR with + non-boolean condition as canonical form. */ + tmp = build2 (gimple_assign_rhs_code (stmt), TREE_TYPE (lhs), + gimple_assign_rhs1 (stmt), gimple_assign_rhs2 (stmt)); + } + /* We can propagate the condition into X op CST where op + is EQ_EXPR or NE_EXPR and CST is either one or zero. */ + else if (TREE_CODE_CLASS (gimple_assign_rhs_code (use_stmt)) + == tcc_comparison + && TREE_CODE (gimple_assign_rhs1 (use_stmt)) == SSA_NAME + && TREE_CODE (gimple_assign_rhs2 (use_stmt)) == INTEGER_CST) + { + enum tree_code code = gimple_assign_rhs_code (use_stmt); + tree cst = gimple_assign_rhs2 (use_stmt); + tree cond; + + cond = build2 (gimple_assign_rhs_code (stmt), + TREE_TYPE (cst), + gimple_assign_rhs1 (stmt), + gimple_assign_rhs2 (stmt)); + + tmp = combine_cond_expr_cond (gimple_location (use_stmt), + code, TREE_TYPE (lhs), + cond, cst, false); + if (tmp == NULL_TREE) + return did_something; + } + /* We can propagate the condition into a statement that + computes the logical negation of the comparison result. */ + else if (gimple_assign_rhs_code (use_stmt) == TRUTH_NOT_EXPR) + { + tree type = TREE_TYPE (gimple_assign_rhs1 (stmt)); + bool nans = HONOR_NANS (TYPE_MODE (type)); + enum tree_code code; + code = invert_tree_comparison (gimple_assign_rhs_code (stmt), nans); + if (code == ERROR_MARK) + return did_something; + + tmp = build2 (code, TREE_TYPE (lhs), gimple_assign_rhs1 (stmt), + gimple_assign_rhs2 (stmt)); + } + else + return did_something; + + { + gimple_stmt_iterator gsi = gsi_for_stmt (use_stmt); + bool inv = is_gimple_min_invariant (tmp); + gimple_assign_set_rhs_from_tree (&gsi, unshare_expr (tmp)); + did_something = MAX (did_something, inv ? 2 : 1); + use_stmt = gsi_stmt (gsi); + update_stmt (use_stmt); + } + + if (dump_file && (dump_flags & TDF_DETAILS)) + { + tree old_rhs = rhs_to_tree (TREE_TYPE (gimple_assign_lhs (stmt)), + stmt); + fprintf (dump_file, " Replaced '"); + print_generic_expr (dump_file, old_rhs, dump_flags); + fprintf (dump_file, "' with '"); + print_generic_expr (dump_file, tmp, dump_flags); + fprintf (dump_file, "'\n"); + } + + /* Remove defining statements. */ + if (remove_prop_source_from_use (name)) + did_something = 2; + else + did_something = MAX (did_something, 1); + } + + return did_something; + } + /* Propagate from the ssa name definition statements of COND_EXPR in GIMPLE_COND statement STMT into the conditional if that simplifies it. Returns zero if no statement was changed, one if there were *************** forward_propagate_into_gimple_cond (gimp *** 402,453 **** do { tree tmp = NULL_TREE; - tree name = NULL_TREE, rhs0 = NULL_TREE, rhs1 = NULL_TREE; - gimple def_stmt; - bool single_use0_p = false, single_use1_p = false; enum tree_code code = gimple_cond_code (stmt); /* We can do tree combining on SSA_NAME and comparison expressions. */ if (TREE_CODE_CLASS (gimple_cond_code (stmt)) == tcc_comparison) ! { ! /* For comparisons use the first operand, that is likely to ! simplify comparisons against constants. */ ! if (TREE_CODE (gimple_cond_lhs (stmt)) == SSA_NAME) ! { ! name = gimple_cond_lhs (stmt); ! def_stmt = get_prop_source_stmt (name, false, &single_use0_p); ! if (def_stmt && can_propagate_from (def_stmt)) ! { ! tree op1 = gimple_cond_rhs (stmt); ! rhs0 = rhs_to_tree (TREE_TYPE (op1), def_stmt); ! tmp = combine_cond_expr_cond (loc, code, boolean_type_node, ! rhs0, op1, !single_use0_p); ! } ! } ! /* If that wasn't successful, try the second operand. */ ! if (tmp == NULL_TREE ! && TREE_CODE (gimple_cond_rhs (stmt)) == SSA_NAME) ! { ! tree op0 = gimple_cond_lhs (stmt); ! name = gimple_cond_rhs (stmt); ! def_stmt = get_prop_source_stmt (name, false, &single_use1_p); ! if (!def_stmt || !can_propagate_from (def_stmt)) ! return did_something; ! ! rhs1 = rhs_to_tree (TREE_TYPE (op0), def_stmt); ! tmp = combine_cond_expr_cond (loc, code, boolean_type_node, op0, ! rhs1, !single_use1_p); ! } ! /* If that wasn't successful either, try both operands. */ ! if (tmp == NULL_TREE ! && rhs0 != NULL_TREE ! && rhs1 != NULL_TREE) ! tmp = combine_cond_expr_cond (loc, code, boolean_type_node, rhs0, ! fold_convert_loc (loc, ! TREE_TYPE (rhs0), ! rhs1), ! !(single_use0_p && single_use1_p)); ! } if (tmp) { --- 591,604 ---- do { tree tmp = NULL_TREE; enum tree_code code = gimple_cond_code (stmt); /* We can do tree combining on SSA_NAME and comparison expressions. */ if (TREE_CODE_CLASS (gimple_cond_code (stmt)) == tcc_comparison) ! tmp = forward_propagate_into_comparison (loc, code, ! boolean_type_node, ! gimple_cond_lhs (stmt), ! gimple_cond_rhs (stmt)); if (tmp) { *************** forward_propagate_into_gimple_cond (gimp *** 468,475 **** update_stmt (stmt); /* Remove defining statements. */ ! if (remove_prop_source_from_use (name) ! || is_gimple_min_invariant (tmp)) did_something = 2; else if (did_something == 0) did_something = 1; --- 619,625 ---- update_stmt (stmt); /* Remove defining statements. */ ! if (is_gimple_min_invariant (tmp)) did_something = 2; else if (did_something == 0) did_something = 1; *************** forward_propagate_into_cond (gimple_stmt *** 502,558 **** do { tree tmp = NULL_TREE; tree cond = gimple_assign_rhs1 (stmt); - tree name, rhs0 = NULL_TREE, rhs1 = NULL_TREE; - gimple def_stmt; - bool single_use0_p = false, single_use1_p = false; /* 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_use0_p); ! if (def_stmt && can_propagate_from (def_stmt)) ! { ! tree op1 = TREE_OPERAND (cond, 1); ! rhs0 = rhs_to_tree (TREE_TYPE (op1), def_stmt); ! tmp = combine_cond_expr_cond (loc, TREE_CODE (cond), ! boolean_type_node, ! rhs0, op1, !single_use0_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_use1_p); ! if (!def_stmt || !can_propagate_from (def_stmt)) ! return did_something; ! ! rhs1 = rhs_to_tree (TREE_TYPE (op0), def_stmt); ! tmp = combine_cond_expr_cond (loc, TREE_CODE (cond), ! boolean_type_node, ! op0, rhs1, !single_use1_p); ! } ! /* If that wasn't successful either, try both operands. */ ! if (tmp == NULL_TREE ! && rhs0 != NULL_TREE ! && rhs1 != NULL_TREE) ! tmp = combine_cond_expr_cond (loc, TREE_CODE (cond), ! boolean_type_node, ! rhs0, ! fold_convert_loc (loc, ! TREE_TYPE (rhs0), ! rhs1), ! !(single_use0_p && single_use1_p)); ! } else if (TREE_CODE (cond) == SSA_NAME) { ! name = cond; ! def_stmt = get_prop_source_stmt (name, true, NULL); if (!def_stmt || !can_propagate_from (def_stmt)) return did_something; --- 652,668 ---- do { tree tmp = NULL_TREE; tree cond = gimple_assign_rhs1 (stmt); /* We can do tree combining on SSA_NAME and comparison expressions. */ ! if (COMPARISON_CLASS_P (cond)) ! tmp = forward_propagate_into_comparison (loc, TREE_CODE (cond), ! boolean_type_node, ! TREE_OPERAND (cond, 0), ! TREE_OPERAND (cond, 1)); else if (TREE_CODE (cond) == SSA_NAME) { ! tree name = cond, rhs0; ! gimple def_stmt = get_prop_source_stmt (name, true, NULL); if (!def_stmt || !can_propagate_from (def_stmt)) return did_something; *************** forward_propagate_into_cond (gimple_stmt *** 578,585 **** update_stmt (stmt); /* Remove defining statements. */ ! if (remove_prop_source_from_use (name) ! || is_gimple_min_invariant (tmp)) did_something = 2; else if (did_something == 0) did_something = 1; --- 688,694 ---- update_stmt (stmt); /* Remove defining statements. */ ! if (is_gimple_min_invariant (tmp)) did_something = 2; else if (did_something == 0) did_something = 1; *************** forward_propagate_addr_expr (tree name, *** 1115,1228 **** return all && has_zero_uses (name); } - /* Forward propagate the comparison 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 (gimple stmt) - { - tree name = gimple_assign_lhs (stmt); - gimple use_stmt; - tree tmp = NULL_TREE; - - /* Don't propagate ssa names that occur in abnormal phis. */ - if ((TREE_CODE (gimple_assign_rhs1 (stmt)) == SSA_NAME - && SSA_NAME_OCCURS_IN_ABNORMAL_PHI (gimple_assign_rhs1 (stmt))) - || (TREE_CODE (gimple_assign_rhs2 (stmt)) == SSA_NAME - && SSA_NAME_OCCURS_IN_ABNORMAL_PHI (gimple_assign_rhs2 (stmt)))) - return false; - - /* Do not un-cse comparisons. But propagate through copies. */ - use_stmt = get_prop_dest_stmt (name, &name); - if (!use_stmt) - return false; - - /* Conversion of the condition result to another integral type. */ - if (is_gimple_assign (use_stmt) - && (CONVERT_EXPR_CODE_P (gimple_assign_rhs_code (use_stmt)) - || TREE_CODE_CLASS (gimple_assign_rhs_code (use_stmt)) - == tcc_comparison - || gimple_assign_rhs_code (use_stmt) == TRUTH_NOT_EXPR) - && INTEGRAL_TYPE_P (TREE_TYPE (gimple_assign_lhs (use_stmt)))) - { - tree lhs = gimple_assign_lhs (use_stmt); - - /* We can propagate the condition into a conversion. */ - if (CONVERT_EXPR_CODE_P (gimple_assign_rhs_code (use_stmt))) - { - /* Avoid using fold here as that may create a COND_EXPR with - non-boolean condition as canonical form. */ - tmp = build2 (gimple_assign_rhs_code (stmt), TREE_TYPE (lhs), - gimple_assign_rhs1 (stmt), gimple_assign_rhs2 (stmt)); - } - /* We can propagate the condition into X op CST where op - is EQ_EXPR or NE_EXPR and CST is either one or zero. */ - else if (TREE_CODE_CLASS (gimple_assign_rhs_code (use_stmt)) - == tcc_comparison - && TREE_CODE (gimple_assign_rhs1 (use_stmt)) == SSA_NAME - && TREE_CODE (gimple_assign_rhs2 (use_stmt)) == INTEGER_CST) - { - enum tree_code code = gimple_assign_rhs_code (use_stmt); - tree cst = gimple_assign_rhs2 (use_stmt); - tree cond; - - cond = build2 (gimple_assign_rhs_code (stmt), - TREE_TYPE (cst), - gimple_assign_rhs1 (stmt), - gimple_assign_rhs2 (stmt)); - - tmp = combine_cond_expr_cond (gimple_location (use_stmt), - code, TREE_TYPE (lhs), - 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 (gimple_assign_rhs_code (use_stmt) == TRUTH_NOT_EXPR) - { - tree type = TREE_TYPE (gimple_assign_rhs1 (stmt)); - bool nans = HONOR_NANS (TYPE_MODE (type)); - enum tree_code code; - code = invert_tree_comparison (gimple_assign_rhs_code (stmt), nans); - if (code == ERROR_MARK) - return false; - - tmp = build2 (code, TREE_TYPE (lhs), gimple_assign_rhs1 (stmt), - gimple_assign_rhs2 (stmt)); - } - else - return false; - - { - gimple_stmt_iterator gsi = gsi_for_stmt (use_stmt); - gimple_assign_set_rhs_from_tree (&gsi, unshare_expr (tmp)); - use_stmt = gsi_stmt (gsi); - update_stmt (use_stmt); - } - - if (dump_file && (dump_flags & TDF_DETAILS)) - { - tree old_rhs = rhs_to_tree (TREE_TYPE (gimple_assign_lhs (stmt)), - stmt); - fprintf (dump_file, " Replaced '"); - print_generic_expr (dump_file, old_rhs, dump_flags); - fprintf (dump_file, "' with '"); - print_generic_expr (dump_file, tmp, dump_flags); - fprintf (dump_file, "'\n"); - } - - /* Remove defining statements. */ - return remove_prop_source_from_use (name); - } - - 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. --- 1224,1229 ---- *************** tree_ssa_forward_propagate_single_use_va *** 2314,2321 **** } else if (TREE_CODE_CLASS (code) == tcc_comparison) { ! if (forward_propagate_comparison (stmt)) cfg_changed = true; gsi_next (&gsi); } else if (code == BIT_AND_EXPR --- 2315,2329 ---- } else if (TREE_CODE_CLASS (code) == tcc_comparison) { ! bool no_warning = gimple_no_warning_p (stmt); ! int did_something; ! fold_defer_overflow_warnings (); ! did_something = forward_propagate_comparison (stmt); ! if (did_something == 2) cfg_changed = true; + fold_undefer_overflow_warnings + (!no_warning && did_something, + stmt, WARN_STRICT_OVERFLOW_CONDITIONAL); gsi_next (&gsi); } else if (code == BIT_AND_EXPR