The following simplifies LC-PHI arg population during epilog peeling, thereby fixing the testcase in this PR.
Bootstrapped and tested on x86_64-unknown-linux-gnu, also built SPEC CPU 2017 with and without LTO, pushed. PR tree-optimization/111950 * tre-vect-loop-manip.cc (slpeel_duplicate_current_defs_from_edges): Remove. (find_guard_arg): Likewise. (slpeel_update_phi_nodes_for_guard2): Likewise. (slpeel_tree_duplicate_loop_to_edge_cfg): Remove calls to slpeel_duplicate_current_defs_from_edges, do not elide LC-PHIs for invariant values. (vect_do_peeling): Materialize PHI arguments for the edge around the epilog from the PHI defs of the main loop exit. * gcc.dg/torture/pr111950.c: New testcase. --- gcc/testsuite/gcc.dg/torture/pr111950.c | 16 ++ gcc/tree-vect-loop-manip.cc | 242 +++--------------------- 2 files changed, 41 insertions(+), 217 deletions(-) create mode 100644 gcc/testsuite/gcc.dg/torture/pr111950.c diff --git a/gcc/testsuite/gcc.dg/torture/pr111950.c b/gcc/testsuite/gcc.dg/torture/pr111950.c new file mode 100644 index 00000000000..4eeffeb6827 --- /dev/null +++ b/gcc/testsuite/gcc.dg/torture/pr111950.c @@ -0,0 +1,16 @@ +/* { dg-do compile } */ +/* { dg-additional-options "-ftree-vectorize -fno-vect-cost-model" } */ + +int a, b, d; +int c[4]; +unsigned e; +void f() { + char g; + for (; d; d++) { + g = 1; + for (; g >= 0; g--) { + e = b >= 2 || a >> b ?: a; + c[g] = e; + } + } +} diff --git a/gcc/tree-vect-loop-manip.cc b/gcc/tree-vect-loop-manip.cc index 43ca985c53c..b9161274ce4 100644 --- a/gcc/tree-vect-loop-manip.cc +++ b/gcc/tree-vect-loop-manip.cc @@ -1392,58 +1392,6 @@ vect_set_loop_condition (class loop *loop, edge loop_e, loop_vec_info loop_vinfo (gimple *) cond_stmt); } -/* Helper routine of slpeel_tree_duplicate_loop_to_edge_cfg. - For all PHI arguments in FROM->dest and TO->dest from those - edges ensure that TO->dest PHI arguments have current_def - to that in from. */ - -static void -slpeel_duplicate_current_defs_from_edges (edge from, edge to) -{ - gimple_stmt_iterator gsi_from, gsi_to; - - for (gsi_from = gsi_start_phis (from->dest), - gsi_to = gsi_start_phis (to->dest); - !gsi_end_p (gsi_from) && !gsi_end_p (gsi_to);) - { - gimple *from_phi = gsi_stmt (gsi_from); - gimple *to_phi = gsi_stmt (gsi_to); - tree from_arg = PHI_ARG_DEF_FROM_EDGE (from_phi, from); - tree to_arg = PHI_ARG_DEF_FROM_EDGE (to_phi, to); - if (virtual_operand_p (from_arg)) - { - gsi_next (&gsi_from); - continue; - } - if (virtual_operand_p (to_arg)) - { - gsi_next (&gsi_to); - continue; - } - if (TREE_CODE (from_arg) != SSA_NAME) - gcc_assert (operand_equal_p (from_arg, to_arg, 0)); - else if (TREE_CODE (to_arg) == SSA_NAME - && from_arg != to_arg) - { - if (get_current_def (to_arg) == NULL_TREE) - { - gcc_assert (types_compatible_p (TREE_TYPE (to_arg), - TREE_TYPE (get_current_def - (from_arg)))); - set_current_def (to_arg, get_current_def (from_arg)); - } - } - gsi_next (&gsi_from); - gsi_next (&gsi_to); - } - - gphi *from_phi = get_virtual_phi (from->dest); - gphi *to_phi = get_virtual_phi (to->dest); - if (from_phi) - set_current_def (PHI_ARG_DEF_FROM_EDGE (to_phi, to), - get_current_def (PHI_ARG_DEF_FROM_EDGE (from_phi, from))); -} - /* Given LOOP this function generates a new copy of it and puts it on E which is either the entry or exit of LOOP. If SCALAR_LOOP is non-NULL, assume LOOP and SCALAR_LOOP are equivalent and copy the @@ -1577,21 +1525,6 @@ slpeel_tree_duplicate_loop_to_edge_cfg (class loop *loop, edge loop_exit, adjust_debug_stmts (orig_def, PHI_RESULT (gsi.phi ()), exit->dest); } - /* This condition happens when the loop has been versioned. e.g. due to ifcvt - versioning the loop. */ - if (scalar_loop != loop) - { - /* If we copied from SCALAR_LOOP rather than LOOP, SSA_NAMEs from - SCALAR_LOOP will have current_def set to SSA_NAMEs in the new_loop, - but LOOP will not. slpeel_update_phi_nodes_for_guard{1,2} expects - the LOOP SSA_NAMEs (on the exit edge and edge from latch to - header) to have current_def set, so copy them over. */ - slpeel_duplicate_current_defs_from_edges (scalar_exit, exit); - slpeel_duplicate_current_defs_from_edges (EDGE_SUCC (scalar_loop->latch, - 0), - EDGE_SUCC (loop->latch, 0)); - } - auto loop_exits = get_loop_exit_edges (loop); auto_vec<basic_block> doms; @@ -1655,18 +1588,6 @@ slpeel_tree_duplicate_loop_to_edge_cfg (class loop *loop, edge loop_exit, if (TREE_CODE (new_arg) != SSA_NAME) continue; - /* If the PHI node dominates the loop then we shouldn't create - a new LC-SSSA PHI for it in the intermediate block. Unless the - the loop has been versioned. If it has then we need the PHI - node such that later when the loop guard is added the original - dominating PHI can be found. */ - basic_block def_bb = gimple_bb (SSA_NAME_DEF_STMT (new_arg)); - if (loop == scalar_loop - && (!def_bb || !flow_bb_inside_loop_p (loop, def_bb))) - { - auto gsi = gsi_for_stmt (phi); - remove_phi_node (&gsi, true); - } } /* Copy the current loop LC PHI nodes between the original loop exit @@ -2711,40 +2632,6 @@ vect_gen_vector_loop_niters_mult_vf (loop_vec_info loop_vinfo, *niters_vector_mult_vf_ptr = niters_vector_mult_vf; } -/* LCSSA_PHI is a lcssa phi of EPILOG loop which is copied from LOOP, - this function searches for the corresponding lcssa phi node in exit - bb of LOOP following the LCSSA_EDGE to the exit node. If it is found, - return the phi result; otherwise return NULL. */ - -static tree -find_guard_arg (class loop *loop ATTRIBUTE_UNUSED, const_edge loop_e, - class loop *epilog ATTRIBUTE_UNUSED, gphi *lcssa_phi, - int lcssa_edge = 0) -{ - gphi_iterator gsi; - - for (gsi = gsi_start_phis (loop_e->dest); !gsi_end_p (gsi); gsi_next (&gsi)) - { - gphi *phi = gsi.phi (); - /* Nested loops with multiple exits can have different no# phi node - arguments between the main loop and epilog as epilog falls to the - second loop. */ - if (gimple_phi_num_args (phi) > loop_e->dest_idx) - { - tree var = PHI_ARG_DEF (phi, loop_e->dest_idx); - if (TREE_CODE (var) != SSA_NAME) - continue; - tree def = get_current_def (var); - if (!def) - continue; - if (operand_equal_p (def, - PHI_ARG_DEF (lcssa_phi, lcssa_edge), 0)) - return PHI_RESULT (phi); - } - } - return NULL_TREE; -} - /* Function slpeel_add_loop_guard adds guard skipping from the beginning of SKIP_LOOP to the beginning of UPDATE_LOOP. GUARD_EDGE and MERGE_EDGE are two pred edges of the merge point before UPDATE_LOOP. The two loops @@ -2827,107 +2714,6 @@ slpeel_update_phi_nodes_for_guard1 (class loop *skip_loop, } } -/* LOOP and EPILOG are two consecutive loops in CFG connected by LOOP_EXIT edge - and EPILOG is copied from LOOP. Function slpeel_add_loop_guard adds guard - skipping from a point between the two loops to the end of EPILOG. Edges - GUARD_EDGE and MERGE_EDGE are the two pred edges of merge_bb at the end of - EPILOG. The CFG looks like: - - loop: - header_a: - i_1 = PHI<i_0, i_2>; - ... - i_2 = i_1 + 1; - if (cond_a) - goto latch_a; - else - goto exit_a; - latch_a: - goto header_a; - - exit_a: - - guard_bb: - if (cond) - goto merge_bb; - else - goto epilog_loop; - - ;; fall_through_bb - - epilog_loop: - header_b: - i_3 = PHI<i_2, i_4>; - ... - i_4 = i_3 + 1; - if (cond_b) - goto latch_b; - else - goto merge_bb; - latch_b: - goto header_b; - - merge_bb: - ; PHI node (i_y = PHI<i_2, i_4>) to be created at merge point. - - exit_bb: - i_x = PHI<i_4>; ;Use of i_4 to be replaced with i_y in merge_bb. - - For each name used out side EPILOG (i.e - for each name that has a lcssa - phi in exit_bb) we create a new PHI in merge_bb. The new PHI has two - args corresponding to GUARD_EDGE and MERGE_EDGE. Arg for MERGE_EDGE is - the arg of the original PHI in exit_bb, arg for GUARD_EDGE is defined - by LOOP and is found in the exit bb of LOOP. Arg of the original PHI - in exit_bb will also be updated. */ - -static void -slpeel_update_phi_nodes_for_guard2 (class loop *loop, class loop *epilog, - const_edge loop_exit, - edge guard_edge, edge merge_edge) -{ - gphi_iterator gsi; - basic_block merge_bb = guard_edge->dest; - - gcc_assert (single_succ_p (merge_bb)); - edge e = single_succ_edge (merge_bb); - basic_block exit_bb = e->dest; - - for (gsi = gsi_start_phis (exit_bb); !gsi_end_p (gsi); gsi_next (&gsi)) - { - gphi *update_phi = gsi.phi (); - tree old_arg = PHI_ARG_DEF (update_phi, e->dest_idx); - - tree merge_arg = NULL_TREE; - - /* If the old argument is a SSA_NAME use its current_def. */ - if (TREE_CODE (old_arg) == SSA_NAME) - merge_arg = get_current_def (old_arg); - /* If it's a constant or doesn't have a current_def, just use the old - argument. */ - if (!merge_arg) - merge_arg = old_arg; - - tree guard_arg = find_guard_arg (loop, loop_exit, epilog, - update_phi, e->dest_idx); - /* If the var is live after loop but not a reduction, we simply - use the old arg. */ - if (!guard_arg) - guard_arg = old_arg; - - /* Create new phi node in MERGE_BB: */ - tree new_res = copy_ssa_name (PHI_RESULT (update_phi)); - gphi *merge_phi = create_phi_node (new_res, merge_bb); - - /* MERGE_BB has two incoming edges: GUARD_EDGE and MERGE_EDGE, Set - the two PHI args in merge_phi for these edges. */ - add_phi_arg (merge_phi, merge_arg, merge_edge, UNKNOWN_LOCATION); - add_phi_arg (merge_phi, guard_arg, guard_edge, UNKNOWN_LOCATION); - - /* Update the original phi in exit_bb. */ - adjust_phi_and_debug_stmts (update_phi, e, new_res); - } -} - /* LOOP_VINFO is an epilogue loop whose corresponding main loop can be skipped. Return a value that equals: @@ -3435,7 +3221,7 @@ vect_do_peeling (loop_vec_info loop_vinfo, tree niters, tree nitersm1, niters, niters_vector_mult_vf); guard_bb = LOOP_VINFO_IV_EXIT (loop_vinfo)->dest; edge epilog_e = LOOP_VINFO_EPILOGUE_IV_EXIT (loop_vinfo); - guard_to = split_edge (epilog_e); + guard_to = epilog_e->dest; guard_e = slpeel_add_loop_guard (guard_bb, guard_cond, guard_to, skip_vector ? anchor : guard_bb, prob_epilog.invert (), @@ -3443,8 +3229,30 @@ vect_do_peeling (loop_vec_info loop_vinfo, tree niters, tree nitersm1, if (vect_epilogues) epilogue_vinfo->skip_this_loop_edge = guard_e; edge main_iv = LOOP_VINFO_IV_EXIT (loop_vinfo); - slpeel_update_phi_nodes_for_guard2 (loop, epilog, main_iv, guard_e, - epilog_e); + gphi_iterator gsi2 = gsi_start_phis (main_iv->dest); + for (gphi_iterator gsi = gsi_start_phis (guard_to); + !gsi_end_p (gsi); gsi_next (&gsi)) + { + /* We are expecting all of the PHIs we have on epilog_e + to be also on the main loop exit. But sometimes + a stray virtual definition can appear at epilog_e + which we can then take as the same on all exits, + we've removed the LC SSA PHI on the main exit before + so we wouldn't need to create a loop PHI for it. */ + if (virtual_operand_p (gimple_phi_result (*gsi)) + && (gsi_end_p (gsi2) + || !virtual_operand_p (gimple_phi_result (*gsi2)))) + add_phi_arg (*gsi, + gimple_phi_arg_def_from_edge (*gsi, epilog_e), + guard_e, UNKNOWN_LOCATION); + else + { + add_phi_arg (*gsi, gimple_phi_result (*gsi2), guard_e, + UNKNOWN_LOCATION); + gsi_next (&gsi2); + } + } + /* Only need to handle basic block before epilog loop if it's not the guard_bb, which is the case when skip_vector is true. */ if (guard_bb != bb_before_epilog) -- 2.35.3