From: Andrew Pinski <apin...@marvell.com> To simplify PHI-OPT and future improvements to it in most (but not all) cases, using match-and-simplify simplifies how much code is needed to be added.
This depends on the following two patches: https://gcc.gnu.org/pipermail/gcc-patches/2021-May/571033.html https://gcc.gnu.org/pipermail/gcc-patches/2021-May/571054.html As this patch removes those parts from phiopt. Note I will be looking to move two_value_replacement and value_replacement to match-and-simplify next. Note also there is one latent bug found while working on this: https://gcc.gnu.org/PR100733 . OK? Bootstrapped and tested on x86_64-linux-gnu with no regressions and all languages. Thanks, Andrew Pinski gcc/ChangeLog: * tree-ssa-phiopt.c: Include explow.h. Fix up comment before the pass struction. (conditional_replacement): Remove function. (xor_replacement): Remove function. (match_simplify_replacement): New function. (tree_ssa_phiopt_worker): Don't call conditional_replacement or xor_replacement. Call match_simplify_replacement if everything else fails to happen. (block_with_single_simple_statement): New function. gcc/testsuite/ChangeLog: * gcc.dg/tree-ssa/pr96928-1.c: Fix testcase for now that ~ happens on the outside of the bit_xor. --- gcc/testsuite/gcc.dg/tree-ssa/pr96928-1.c | 4 +- gcc/tree-ssa-phiopt.c | 478 ++++++++++------------ 2 files changed, 229 insertions(+), 253 deletions(-) diff --git a/gcc/testsuite/gcc.dg/tree-ssa/pr96928-1.c b/gcc/testsuite/gcc.dg/tree-ssa/pr96928-1.c index a2770e5e896..2e86620da11 100644 --- a/gcc/testsuite/gcc.dg/tree-ssa/pr96928-1.c +++ b/gcc/testsuite/gcc.dg/tree-ssa/pr96928-1.c @@ -1,9 +1,9 @@ /* PR tree-optimization/96928 */ /* { dg-do compile } */ -/* { dg-options "-O2 -fdump-tree-phiopt2" } */ +/* { dg-options "-O2 -fdump-tree-phiopt2 -fdump-tree-optimized" } */ /* { dg-final { scan-tree-dump-times " = a_\[0-9]*\\\(D\\\) >> " 5 "phiopt2" } } */ /* { dg-final { scan-tree-dump-times " = ~c_\[0-9]*\\\(D\\\);" 1 "phiopt2" } } */ -/* { dg-final { scan-tree-dump-times " = ~" 1 "phiopt2" } } */ +/* { dg-final { scan-tree-dump-times " = ~" 1 "optimized" } } */ /* { dg-final { scan-tree-dump-times " = \[abc_0-9\\\(\\\)D]* \\\^ " 5 "phiopt2" } } */ /* { dg-final { scan-tree-dump-not "a < 0" "phiopt2" } } */ diff --git a/gcc/tree-ssa-phiopt.c b/gcc/tree-ssa-phiopt.c index f133659a781..f7c82cf192f 100644 --- a/gcc/tree-ssa-phiopt.c +++ b/gcc/tree-ssa-phiopt.c @@ -48,12 +48,11 @@ along with GCC; see the file COPYING3. If not see #include "tree-eh.h" #include "gimple-fold.h" #include "internal-fn.h" +#include "explow.h" /* For promote_mode. */ static unsigned int tree_ssa_phiopt_worker (bool, bool, bool); static bool two_value_replacement (basic_block, basic_block, edge, gphi *, tree, tree); -static bool conditional_replacement (basic_block, basic_block, - edge, edge, gphi *, tree, tree); static gphi *factor_out_conditional_conversion (edge, edge, gphi *, tree, tree, gimple *); static int value_replacement (basic_block, basic_block, @@ -62,8 +61,6 @@ static bool minmax_replacement (basic_block, basic_block, edge, edge, gphi *, tree, tree); static bool abs_replacement (basic_block, basic_block, edge, edge, gphi *, tree, tree); -static bool xor_replacement (basic_block, basic_block, - edge, edge, gphi *, tree, tree); static bool spaceship_replacement (basic_block, basic_block, edge, edge, gphi *, tree, tree); static bool cond_removal_in_popcount_clz_ctz_pattern (basic_block, basic_block, @@ -71,6 +68,8 @@ static bool cond_removal_in_popcount_clz_ctz_pattern (basic_block, basic_block, tree, tree); static bool cond_store_replacement (basic_block, basic_block, edge, edge, hash_set<tree> *); +static bool match_simplify_replacement (basic_block, basic_block, + edge, edge, gimple_seq, bool, bool); static bool cond_if_else_store_replacement (basic_block, basic_block, basic_block); static hash_set<tree> * get_non_trapping (); static void replace_phi_edge_with_variable (basic_block, edge, gphi *, tree); @@ -319,7 +318,7 @@ tree_ssa_phiopt_worker (bool do_store_elim, bool do_hoist_loads, bool early_p) phi = single_non_singleton_phi_for_edges (phis, e1, e2); if (!phi) - continue; + goto try_match_simplify; arg0 = gimple_phi_arg_def (phi, e1->dest_idx); arg1 = gimple_phi_arg_def (phi, e2->dest_idx); @@ -345,14 +344,9 @@ tree_ssa_phiopt_worker (bool do_store_elim, bool do_hoist_loads, bool early_p) /* Do the replacement of conditional if it can be done. */ if (!early_p && two_value_replacement (bb, bb1, e2, phi, arg0, arg1)) cfgchanged = true; - else if (!early_p - && conditional_replacement (bb, bb1, e1, e2, phi, - arg0, arg1)) - cfgchanged = true; else if (abs_replacement (bb, bb1, e1, e2, phi, arg0, arg1)) cfgchanged = true; - else if (!early_p - && xor_replacement (bb, bb1, e1, e2, phi, arg0, arg1)) + else if (abs_replacement (bb, bb1, e1, e2, phi, arg0, arg1)) cfgchanged = true; else if (!early_p && cond_removal_in_popcount_clz_ctz_pattern (bb, bb1, e1, @@ -363,7 +357,14 @@ tree_ssa_phiopt_worker (bool do_store_elim, bool do_hoist_loads, bool early_p) cfgchanged = true; else if (spaceship_replacement (bb, bb1, e1, e2, phi, arg0, arg1)) cfgchanged = true; + else + goto try_match_simplify; } + continue; +try_match_simplify: + if (match_simplify_replacement (bb, bb1, e1, e2, phi_nodes (bb2), + early_p, /*late_p = */false)) + cfgchanged = true; } free (bb_order); @@ -775,142 +776,6 @@ two_value_replacement (basic_block cond_bb, basic_block middle_bb, return true; } -/* The function conditional_replacement does the main work of doing the - conditional replacement. Return true if the replacement is done. - Otherwise return false. - BB is the basic block where the replacement is going to be done on. ARG0 - is argument 0 from PHI. Likewise for ARG1. */ - -static bool -conditional_replacement (basic_block cond_bb, basic_block middle_bb, - edge e0, edge e1, gphi *phi, - tree arg0, tree arg1) -{ - tree result; - gimple *stmt; - gassign *new_stmt; - tree cond; - gimple_stmt_iterator gsi; - edge true_edge, false_edge; - tree new_var, new_var2; - bool neg = false; - int shift = 0; - tree nonzero_arg; - - /* FIXME: Gimplification of complex type is too hard for now. */ - /* We aren't prepared to handle vectors either (and it is a question - if it would be worthwhile anyway). */ - if (!(INTEGRAL_TYPE_P (TREE_TYPE (arg0)) - || POINTER_TYPE_P (TREE_TYPE (arg0))) - || !(INTEGRAL_TYPE_P (TREE_TYPE (arg1)) - || POINTER_TYPE_P (TREE_TYPE (arg1)))) - return false; - - /* The PHI arguments have the constants 0 and 1, or 0 and -1 or - 0 and (1 << cst), then convert it to the conditional. */ - if (integer_zerop (arg0)) - nonzero_arg = arg1; - else if (integer_zerop (arg1)) - nonzero_arg = arg0; - else - return false; - if (integer_pow2p (nonzero_arg)) - { - shift = tree_log2 (nonzero_arg); - if (shift && POINTER_TYPE_P (TREE_TYPE (nonzero_arg))) - return false; - } - else if (integer_all_onesp (nonzero_arg)) - neg = true; - else - return false; - - if (!empty_block_p (middle_bb)) - return false; - - /* At this point we know we have a GIMPLE_COND with two successors. - One successor is BB, the other successor is an empty block which - falls through into BB. - - There is a single PHI node at the join point (BB) and its arguments - are constants (0, 1) or (0, -1) or (0, (1 << shift)). - - So, given the condition COND, and the two PHI arguments, we can - rewrite this PHI into non-branching code: - - dest = (COND) or dest = COND' or dest = (COND) << shift - - We use the condition as-is if the argument associated with the - true edge has the value one or the argument associated with the - false edge as the value zero. Note that those conditions are not - the same since only one of the outgoing edges from the GIMPLE_COND - will directly reach BB and thus be associated with an argument. */ - - stmt = last_stmt (cond_bb); - result = PHI_RESULT (phi); - - /* To handle special cases like floating point comparison, it is easier and - less error-prone to build a tree and gimplify it on the fly though it is - less efficient. */ - cond = fold_build2_loc (gimple_location (stmt), - gimple_cond_code (stmt), boolean_type_node, - gimple_cond_lhs (stmt), gimple_cond_rhs (stmt)); - - /* We need to know which is the true edge and which is the false - edge so that we know when to invert the condition below. */ - extract_true_false_edges_from_block (cond_bb, &true_edge, &false_edge); - if ((e0 == true_edge && integer_zerop (arg0)) - || (e0 == false_edge && !integer_zerop (arg0)) - || (e1 == true_edge && integer_zerop (arg1)) - || (e1 == false_edge && !integer_zerop (arg1))) - cond = fold_build1_loc (gimple_location (stmt), - TRUTH_NOT_EXPR, TREE_TYPE (cond), cond); - - if (neg) - { - cond = fold_convert_loc (gimple_location (stmt), - TREE_TYPE (result), cond); - cond = fold_build1_loc (gimple_location (stmt), - NEGATE_EXPR, TREE_TYPE (cond), cond); - } - else if (shift) - { - cond = fold_convert_loc (gimple_location (stmt), - TREE_TYPE (result), cond); - cond = fold_build2_loc (gimple_location (stmt), - LSHIFT_EXPR, TREE_TYPE (cond), cond, - build_int_cst (integer_type_node, shift)); - } - - /* Insert our new statements at the end of conditional block before the - COND_STMT. */ - gsi = gsi_for_stmt (stmt); - new_var = force_gimple_operand_gsi (&gsi, cond, true, NULL, true, - GSI_SAME_STMT); - - if (!useless_type_conversion_p (TREE_TYPE (result), TREE_TYPE (new_var))) - { - location_t locus_0, locus_1; - - new_var2 = make_ssa_name (TREE_TYPE (result)); - new_stmt = gimple_build_assign (new_var2, CONVERT_EXPR, new_var); - gsi_insert_before (&gsi, new_stmt, GSI_SAME_STMT); - new_var = new_var2; - - /* Set the locus to the first argument, unless is doesn't have one. */ - locus_0 = gimple_phi_arg_location (phi, 0); - locus_1 = gimple_phi_arg_location (phi, 1); - if (locus_0 == UNKNOWN_LOCATION) - locus_0 = locus_1; - gimple_set_location (new_stmt, locus_0); - } - - replace_phi_edge_with_variable (cond_bb, e1, phi, new_var); - - /* Note that we optimized this PHI. */ - return true; -} - /* Update *ARG which is defined in STMT so that it contains the computed value if that seems profitable. Return true if the statement is made dead by that rewriting. */ @@ -1413,6 +1278,219 @@ value_replacement (basic_block cond_bb, basic_block middle_bb, return 0; } +/* Return true if basic block BB has no statements or + a statement (that has no side effects) with one cheap + preparation statement. Const/pure functions can only be the last + statmenent. */ +static bool +block_with_single_simple_statement (basic_block bb, gimple **stmt) +{ + /* BB must have no executable statements. */ + gimple *stmt1; + gimple_stmt_iterator gsi = gsi_after_labels (bb); + *stmt = NULL; + if (phi_nodes (bb)) + return false; + if (gsi_end_p (gsi)) + return true; + if (is_gimple_debug (gsi_stmt (gsi))) + gsi_next_nondebug (&gsi); + if (gsi_end_p (gsi)) + return true; + stmt1 = gsi_stmt (gsi); + gsi_next_nondebug (&gsi); + if (!gsi_end_p (gsi)) + return false; + if (gimple_could_trap_p (stmt1)) + return false; + if (gimple_has_side_effects (stmt1)) + return false; + *stmt = stmt1; + if (is_gimple_assign (stmt1) + && TREE_CODE (gimple_assign_lhs (stmt1)) == SSA_NAME) + return true; + if (is_gimple_call (stmt1) + && gimple_inexpensive_call_p (as_a <gcall *> (stmt1))) + return true; + return false; +} + +/* The function match_simplify_replacement does the main work of using the + match and simplify infastructure to do the replacement. Return true if the replacement is done. Otherwise return false. + BB is the basic block where the replacement is going to be done on. ARG0 + is argument 0 from the PHI. Likewise for ARG1. */ + +static bool +match_simplify_replacement (basic_block cond_bb, basic_block middle_bb, + edge e0, edge e1, gimple_seq phis, bool early_p, + bool late_p) +{ + gimple *stmt; + edge true_edge, false_edge; + tree cond; + tree type; + gimple_stmt_iterator gsi; + int reverse = false; + gimple *stmt_to_move; + gimple_seq seq = NULL; + bool allhappened = true; + bool happened_once = false; + auto_vec<std::pair<gimple *,tree>, 4> changedphis; + + if (!block_with_single_simple_statement (middle_bb, &stmt_to_move)) + return false; + + /* If there is a statement to move and is early, then don't do. */ + if (early_p && stmt_to_move) + return false; + + stmt = last_stmt (cond_bb); + + /* We need to know which is the true edge and which is the false + edge so that we know if have abs or negative abs. */ + extract_true_false_edges_from_block (cond_bb, &true_edge, &false_edge); + if (e1 == true_edge || e0 == false_edge) + reverse = true; + + /* Don't use fold_build2 here as that might create (bool)a instead of just + "a != 0". */ + cond = build2_loc (gimple_location (stmt), gimple_cond_code (stmt), + boolean_type_node, gimple_cond_lhs (stmt), + gimple_cond_rhs (stmt)); + + for (gsi = gsi_start (phis); !gsi_end_p (gsi); gsi_next (&gsi)) + { + gimple *p = gsi_stmt (gsi); + tree conditional; + tree arg0, arg1; + gimple_seq seq1 = NULL; + bool happened = false; + + /* If the PHI arguments are equal then we can skip this PHI. */ + arg0 = gimple_phi_arg_def (p, e0->dest_idx); + arg1 = gimple_phi_arg_def (p, e1->dest_idx); + + /* Skip the equal case as it will be optimize to the same thing. */ + if (operand_equal_for_phi_arg_p (arg0, arg1)) + continue; + + if (reverse) + std::swap (arg0, arg1); + + type = TREE_TYPE (gimple_phi_result (p)); + + conditional = gimple_simplify (COND_EXPR, type, + unshare_expr (cond), + arg0, arg1, + &seq1, NULL); + + /* If we did not simplify, then emit a COND_EXPR if we can. */ + if (!conditional) + { + gimple *stmt; + + /* Only emit a COND_EXPR if this is the late PHI-OPT. */ + if (!late_p) + { + /* Note some transformations can happen even without having + extra staments. */ + allhappened = false; + continue; + } + + if (!can_conditionally_move_p (TYPE_MODE (TREE_TYPE (PHI_RESULT (p))))) + { + tree type = TREE_TYPE (PHI_RESULT (p)); + enum machine_mode mode = TYPE_MODE (type); + int unsignedp = TYPE_UNSIGNED (type); + mode = promote_mode (type, mode, &unsignedp); + if (!can_conditionally_move_p (mode)) + { + allhappened = false; + continue; + } + } + /* Manually create the final assignment so the aliasing info + for the ssa name will be up todate. */ + conditional = duplicate_ssa_name (PHI_RESULT (p), NULL); + stmt = gimple_build_assign (conditional, COND_EXPR, + unshare_expr (cond), arg0, arg1); + gimple_seq_add_stmt_without_update (&seq1, stmt); + } + /* If the simplification can be done without any extra statement, + do this transformation all the time. + For an example a == 0 ? 0 : a -> a . + This requires there to be no statement to be moved as: + a == 0 ? 0 : (t = a * b) will be replaced with just t and then + later on the statement might not be moved. */ + if (!seq1 && !stmt_to_move) + { + /* Set both of the edges of the PHI node to be the result. */ + SET_USE (PHI_ARG_DEF_PTR (p, e1->dest_idx), conditional); + SET_USE (PHI_ARG_DEF_PTR (p, e0->dest_idx), conditional); + happened = true; + } + else if (early_p || !allhappened) + { + happened = false; + allhappened = false; + } + else + { + gimple_seq_add_seq_without_update (&seq, seq1); + changedphis.safe_push (std::make_pair (p, conditional)); + happened = true; + } + + if (happened && dump_file && (dump_flags & TDF_DETAILS)) + { + if (!seq1 && !stmt_to_move) + fprintf (dump_file, "Committed: "); + fprintf (dump_file, "PHI "); + print_generic_expr (dump_file, gimple_phi_result (p)); + fprintf (dump_file, + " trying change to straight line code for COND_EXPR in block %d: COND_EXPR: ", + cond_bb->index); + print_generic_expr (dump_file, cond); + fprintf (dump_file, "PHI elements to: "); + print_generic_expr (dump_file, conditional); + fprintf (dump_file, "\n"); + } + happened_once |= happened; + } + + if (!happened_once || !allhappened) + return false; + + if (dump_file && (dump_flags & TDF_DETAILS)) + fprintf (dump_file, "commited the above changes.\n"); + + for(auto phi_to_change : changedphis) + { + gimple *phi = phi_to_change.first; + tree conditional = phi_to_change.second; + /* Set both of the edges of the PHI node to be the result. */ + SET_USE (PHI_ARG_DEF_PTR (phi, e1->dest_idx), conditional); + SET_USE (PHI_ARG_DEF_PTR (phi, e0->dest_idx), conditional); + } + + gsi = gsi_for_stmt (stmt); + if (stmt_to_move) + { + if (dump_file && (dump_flags & TDF_DETAILS)) + { + fprintf (dump_file, "statement un-sinked:\n"); + print_gimple_stmt (dump_file, stmt_to_move, 0, + TDF_VOPS|TDF_MEMSYMS); + } + gimple_stmt_iterator gsi1 = gsi_for_stmt (stmt_to_move); + gsi_move_before (&gsi1, &gsi); + } + + gsi_insert_seq_before (&gsi, seq, GSI_SAME_STMT); + return true; +} + /* The function minmax_replacement does the main work of doing the minmax replacement. Return true if the replacement is done. Otherwise return false. @@ -2649,108 +2727,6 @@ abs_replacement (basic_block cond_bb, basic_block middle_bb, return true; } -/* Optimize x < 0 ? ~y : y into (x >> (prec-1)) ^ y. */ - -static bool -xor_replacement (basic_block cond_bb, basic_block middle_bb, - edge e0 ATTRIBUTE_UNUSED, edge e1, - gphi *phi, tree arg0, tree arg1) -{ - if (!INTEGRAL_TYPE_P (TREE_TYPE (arg1))) - return false; - - /* OTHER_BLOCK must have only one executable statement which must have the - form arg0 = ~arg1 or arg1 = ~arg0. */ - - gimple *assign = last_and_only_stmt (middle_bb); - /* If we did not find the proper one's complement assignment, then we cannot - optimize. */ - if (assign == NULL) - return false; - - /* If we got here, then we have found the only executable statement - in OTHER_BLOCK. If it is anything other than arg = ~arg1 or - arg1 = ~arg0, then we cannot optimize. */ - if (!is_gimple_assign (assign)) - return false; - - if (gimple_assign_rhs_code (assign) != BIT_NOT_EXPR) - return false; - - tree lhs = gimple_assign_lhs (assign); - tree rhs = gimple_assign_rhs1 (assign); - - /* The assignment has to be arg0 = -arg1 or arg1 = -arg0. */ - if (!(lhs == arg0 && rhs == arg1) && !(lhs == arg1 && rhs == arg0)) - return false; - - gimple *cond = last_stmt (cond_bb); - tree result = PHI_RESULT (phi); - - /* Only relationals comparing arg[01] against zero are interesting. */ - enum tree_code cond_code = gimple_cond_code (cond); - if (cond_code != LT_EXPR && cond_code != GE_EXPR) - return false; - - /* Make sure the conditional is x OP 0. */ - tree clhs = gimple_cond_lhs (cond); - if (TREE_CODE (clhs) != SSA_NAME - || !INTEGRAL_TYPE_P (TREE_TYPE (clhs)) - || TYPE_UNSIGNED (TREE_TYPE (clhs)) - || TYPE_PRECISION (TREE_TYPE (clhs)) != TYPE_PRECISION (TREE_TYPE (arg1)) - || !integer_zerop (gimple_cond_rhs (cond))) - return false; - - /* We need to know which is the true edge and which is the false - edge so that we know if have xor or inverted xor. */ - edge true_edge, false_edge; - extract_true_false_edges_from_block (cond_bb, &true_edge, &false_edge); - - /* For GE_EXPR, if the true edge goes to OTHER_BLOCK, then we - will need to invert the result. Similarly for LT_EXPR if - the false edge goes to OTHER_BLOCK. */ - edge e; - if (cond_code == GE_EXPR) - e = true_edge; - else - e = false_edge; - - bool invert = e->dest == middle_bb; - - result = duplicate_ssa_name (result, NULL); - - gimple_stmt_iterator gsi = gsi_last_bb (cond_bb); - - int prec = TYPE_PRECISION (TREE_TYPE (clhs)); - gimple *new_stmt - = gimple_build_assign (make_ssa_name (TREE_TYPE (clhs)), RSHIFT_EXPR, clhs, - build_int_cst (integer_type_node, prec - 1)); - gsi_insert_before (&gsi, new_stmt, GSI_SAME_STMT); - - if (!useless_type_conversion_p (TREE_TYPE (result), TREE_TYPE (clhs))) - { - new_stmt = gimple_build_assign (make_ssa_name (TREE_TYPE (result)), - NOP_EXPR, gimple_assign_lhs (new_stmt)); - gsi_insert_before (&gsi, new_stmt, GSI_SAME_STMT); - } - lhs = gimple_assign_lhs (new_stmt); - - if (invert) - { - new_stmt = gimple_build_assign (make_ssa_name (TREE_TYPE (result)), - BIT_NOT_EXPR, rhs); - gsi_insert_before (&gsi, new_stmt, GSI_SAME_STMT); - rhs = gimple_assign_lhs (new_stmt); - } - - new_stmt = gimple_build_assign (result, BIT_XOR_EXPR, lhs, rhs); - gsi_insert_before (&gsi, new_stmt, GSI_NEW_STMT); - - replace_phi_edge_with_variable (cond_bb, e1, phi, result); - - /* Note that we optimized this PHI. */ - return true; -} /* Auxiliary functions to determine the set of memory accesses which can't trap because they are preceded by accesses to the same memory @@ -3618,8 +3594,8 @@ gate_hoist_loads (void) Conditional Replacement ----------------------- - This transformation, implemented in conditional_replacement, - replaces + This transformation, using the match and simplify infastructure and implemented in + match_simplify_replacement, replaces bb0: if (cond) goto bb2; else goto bb1; -- 2.17.1