On Wed, 28 Jun 2023, Tamar Christina wrote: > Hi All, > > This patch updates the peeling code to maintain LCSSA during peeling. > The rewrite also naturally takes into account multiple exits and so it didn't > make sense to split them off. > > For the purposes of peeling the only change for multiple exits is that the > secondary exits are all wired to the start of the new loop preheader when > doing > epilogue peeling. > > When doing prologue peeling the CFG is kept in tact. > > For both epilogue and prologue peeling we wire through between the two loops > any > PHI nodes that escape the first loop into the second loop if flow_loops is > specified. The reason for this conditionality is because > slpeel_tree_duplicate_loop_to_edge_cfg is used in the compiler in 3 ways: > - prologue peeling > - epilogue peeling > - loop distribution > > for the last case the loops should remain independent, and so not be > connected. > Because of this propagation of only used phi nodes get_current_def can be used > to easily find the previous definitions. However live statements that are > not used inside the loop itself are not propagated (since if unused, the > moment > we add the guard in between the two loops the value across the bypass edge can > be wrong if the loop has been peeled.) > > This is dealt with easily enough in find_guard_arg. > > For multiple exits, while we are in LCSSA form, and have a correct DOM tree, > the > moment we add the guard block we will change the dominators again. To deal > with > this slpeel_tree_duplicate_loop_to_edge_cfg can optionally return the blocks > to > update without having to recompute the list of blocks to update again. > > When multiple exits and doing epilogue peeling we will also temporarily have > an > incorrect VUSES chain for the secondary exits as it anticipates the final > result > after the VDEFs have been moved. This will thus be corrected once the code > motion is applied. > > Lastly by doing things this way we can remove the helper functions that > previously did lock step iterations to update things as it went along. > > Bootstrapped Regtested on aarch64-none-linux-gnu and no issues. > > Ok for master?
Not sure if I get through all of this in one go - so be prepared that the rest of the review follows another day. > Thanks, > Tamar > > gcc/ChangeLog: > > * tree-loop-distribution.cc (copy_loop_before): Pass flow_loops = false. > * tree-ssa-loop-niter.cc (loop_only_exit_p): Fix bug when exit==null. > * tree-vect-loop-manip.cc (adjust_phi_and_debug_stmts): Add additional > assert. > (vect_set_loop_condition_normal): Skip modifying loop IV for multiple > exits. > (slpeel_tree_duplicate_loop_to_edge_cfg): Support multiple exit peeling. > (slpeel_can_duplicate_loop_p): Likewise. > (vect_update_ivs_after_vectorizer): Don't enter this... > (vect_update_ivs_after_early_break): ...but instead enter here. > (find_guard_arg): Update for new peeling code. > (slpeel_update_phi_nodes_for_loops): Remove. > (slpeel_update_phi_nodes_for_guard2): Remove hardcoded edge 0 checks. > (slpeel_update_phi_nodes_for_lcssa): Remove. > (vect_do_peeling): Fix VF for multiple exits and force epilogue. > * tree-vect-loop.cc (_loop_vec_info::_loop_vec_info): Initialize > non_break_control_flow and early_breaks. > (vect_need_peeling_or_partial_vectors_p): Force partial vector if > multiple exits and VLA. > (vect_analyze_loop_form): Support inner loop multiple exits. > (vect_create_loop_vinfo): Set LOOP_VINFO_EARLY_BREAKS. > (vect_create_epilog_for_reduction): Update live phi nodes. > (vectorizable_live_operation): Ignore live operations in vector loop > when multiple exits. > (vect_transform_loop): Force unrolling for VF loops and multiple exits. > * tree-vect-stmts.cc (vect_stmt_relevant_p): Analyze ctrl statements. > (vect_mark_stmts_to_be_vectorized): Check for non-exit control flow and > analyze gcond params. > (vect_analyze_stmt): Support gcond. > * tree-vectorizer.cc (pass_vectorize::execute): Support multiple exits > in RPO pass. > * tree-vectorizer.h (enum vect_def_type): Add vect_early_exit_def. > (LOOP_VINFO_EARLY_BREAKS, LOOP_VINFO_GENERAL_CTR_FLOW): New. > (loop_vec_info_for_loop): Change to const and static. > (is_loop_header_bb_p): Drop assert. > (slpeel_can_duplicate_loop_p): Update prototype. > (class loop): Add early_breaks and non_break_control_flow. > > --- inline copy of patch -- > diff --git a/gcc/tree-loop-distribution.cc b/gcc/tree-loop-distribution.cc > index > 97879498db46dd3c34181ae9aa6e5476004dd5b5..d790ce5fffab3aa3dfc40d833a968314a4442b9e > 100644 > --- a/gcc/tree-loop-distribution.cc > +++ b/gcc/tree-loop-distribution.cc > @@ -948,7 +948,7 @@ copy_loop_before (class loop *loop, bool > redirect_lc_phi_defs) > edge preheader = loop_preheader_edge (loop); > > initialize_original_copy_tables (); > - res = slpeel_tree_duplicate_loop_to_edge_cfg (loop, NULL, preheader); > + res = slpeel_tree_duplicate_loop_to_edge_cfg (loop, NULL, preheader, > false); > gcc_assert (res != NULL); > > /* When a not last partition is supposed to keep the LC PHIs computed > diff --git a/gcc/tree-ssa-loop-niter.cc b/gcc/tree-ssa-loop-niter.cc > index > 5d398b67e68c7076760854119590f18b19c622b6..79686f6c4945b7139ba377300430c04b7aeefe6c > 100644 > --- a/gcc/tree-ssa-loop-niter.cc > +++ b/gcc/tree-ssa-loop-niter.cc > @@ -3072,7 +3072,12 @@ loop_only_exit_p (const class loop *loop, basic_block > *body, const_edge exit) > gimple_stmt_iterator bsi; > unsigned i; > > - if (exit != single_exit (loop)) > + /* We need to check for alternative exits since exit can be NULL. */ You mean we pass in exit == NULL in some cases? I'm not sure what the desired behavior in that case is - can you point out the callers you are fixing here? I think we should add gcc_assert (exit != nullptr) > for (i = 0; i < loop->num_nodes; i++) > diff --git a/gcc/tree-vect-loop-manip.cc b/gcc/tree-vect-loop-manip.cc > index > 6b93fb3f9af8f2bbdf5dec28f0009177aa5171ab..550d7f40002cf0b58f8a927cb150edd7c2aa9999 > 100644 > --- a/gcc/tree-vect-loop-manip.cc > +++ b/gcc/tree-vect-loop-manip.cc > @@ -252,6 +252,9 @@ adjust_phi_and_debug_stmts (gimple *update_phi, edge e, > tree new_def) > { > tree orig_def = PHI_ARG_DEF_FROM_EDGE (update_phi, e); > > + gcc_assert (TREE_CODE (orig_def) != SSA_NAME > + || orig_def != new_def); > + > SET_PHI_ARG_DEF (update_phi, e->dest_idx, new_def); > > if (MAY_HAVE_DEBUG_BIND_STMTS) > @@ -1292,7 +1295,8 @@ vect_set_loop_condition_normal (loop_vec_info > loop_vinfo, > gsi_insert_before (&loop_cond_gsi, cond_stmt, GSI_SAME_STMT); > > /* Record the number of latch iterations. */ > - if (limit == niters) > + if (limit == niters > + || LOOP_VINFO_EARLY_BREAKS (loop_vinfo)) > /* Case A: the loop iterates NITERS times. Subtract one to get the > latch count. */ > loop->nb_iterations = fold_build2 (MINUS_EXPR, niters_type, niters, > @@ -1303,7 +1307,13 @@ vect_set_loop_condition_normal (loop_vec_info > loop_vinfo, > loop->nb_iterations = fold_build2 (TRUNC_DIV_EXPR, niters_type, > limit, step); > > - if (final_iv) > + /* For multiple exits we've already maintained LCSSA form and handled > + the scalar iteration update in the code that deals with the merge > + block and its updated guard. I could move that code here instead > + of in vect_update_ivs_after_early_break but I have to still deal > + with the updates to the counter `i`. So for now I'll keep them > + together. */ > + if (final_iv && !LOOP_VINFO_EARLY_BREAKS (loop_vinfo)) > { > gassign *assign; > edge exit = LOOP_VINFO_IV_EXIT (loop_vinfo); > @@ -1509,11 +1519,19 @@ vec_init_exit_info (class loop *loop) > 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 > basic blocks from SCALAR_LOOP instead of LOOP, but to either the > - entry or exit of LOOP. */ > + entry or exit of LOOP. If FLOW_LOOPS then connect LOOP to SCALAR_LOOP as > a > + continuation. This is correct for cases where one loop continues from the > + other like in the vectorizer, but not true for uses in e.g. loop > distribution > + where the loop is duplicated and then modified. > + but for loop distribution the flow also continues? I'm not sure what you are refering to here. Do you by chance have a branch with the patches installed? > + If UPDATED_DOMS is not NULL it is update with the list of basic blocks > whoms > + dominators were updated during the peeling. */ > > class loop * > slpeel_tree_duplicate_loop_to_edge_cfg (class loop *loop, > - class loop *scalar_loop, edge e) > + class loop *scalar_loop, edge e, > + bool flow_loops, > + vec<basic_block> *updated_doms) > { > class loop *new_loop; > basic_block *new_bbs, *bbs, *pbbs; > @@ -1602,6 +1620,19 @@ slpeel_tree_duplicate_loop_to_edge_cfg (class loop > *loop, > for (unsigned i = (at_exit ? 0 : 1); i < scalar_loop->num_nodes + 1; i++) > rename_variables_in_bb (new_bbs[i], duplicate_outer_loop); > > + /* Rename the exit uses. */ > + for (edge exit : get_loop_exit_edges (new_loop)) > + for (auto gsi = gsi_start_phis (exit->dest); > + !gsi_end_p (gsi); gsi_next (&gsi)) > + { > + tree orig_def = PHI_ARG_DEF_FROM_EDGE (gsi.phi (), exit); > + rename_use_op (PHI_ARG_DEF_PTR_FROM_EDGE (gsi.phi (), exit)); > + if (MAY_HAVE_DEBUG_BIND_STMTS) > + 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 > @@ -1616,28 +1647,106 @@ slpeel_tree_duplicate_loop_to_edge_cfg (class loop > *loop, > EDGE_SUCC (loop->latch, 0)); > } > > + vec<edge> alt_exits = loop->vec_loop_alt_exits; So 'e' is not one of alt_exits, right? I wonder if we can simply compute the vector from all exits of 'loop' and removing 'e'? > + bool multiple_exits_p = !alt_exits.is_empty (); > + auto_vec<basic_block> doms; > + class loop *update_loop = NULL; > + > if (at_exit) /* Add the loop copy at exit. */ > { > - if (scalar_loop != loop) > + if (scalar_loop != loop && new_exit->dest != exit_dest) > { > - gphi_iterator gsi; > new_exit = redirect_edge_and_branch (new_exit, exit_dest); > + flush_pending_stmts (new_exit); > + } > > - for (gsi = gsi_start_phis (exit_dest); !gsi_end_p (gsi); > - gsi_next (&gsi)) > + auto loop_exits = get_loop_exit_edges (loop); > + for (edge exit : loop_exits) > + redirect_edge_and_branch (exit, new_preheader); > + > + one line vertical space too much > + /* Copy the current loop LC PHI nodes between the original loop exit > + block and the new loop header. This allows us to later split the > + preheader block and still find the right LC nodes. */ > + edge latch_new = single_succ_edge (new_preheader); > + edge latch_old = loop_latch_edge (loop); > + hash_set <tree> lcssa_vars; > + for (auto gsi_from = gsi_start_phis (latch_old->dest), so that's loop->header (and makes it more clear which PHI nodes you are looking at) > + gsi_to = gsi_start_phis (latch_new->dest); likewise new_loop->header > + flow_loops && !gsi_end_p (gsi_from) && !gsi_end_p (gsi_to); > + gsi_next (&gsi_from), gsi_next (&gsi_to)) > + { > + gimple *from_phi = gsi_stmt (gsi_from); > + gimple *to_phi = gsi_stmt (gsi_to); > + tree new_arg = PHI_ARG_DEF_FROM_EDGE (from_phi, latch_old); > + /* In all cases, even in early break situations we're only > + interested in the number of fully executed loop iters. As such > + we discard any partially done iteration. So we simply propagate > + the phi nodes from the latch to the merge block. */ > + tree new_res = copy_ssa_name (gimple_phi_result (from_phi)); > + gphi *lcssa_phi = create_phi_node (new_res, e->dest); > + > + lcssa_vars.add (new_arg); > + > + /* Main loop exit should use the final iter value. */ > + add_phi_arg (lcssa_phi, new_arg, loop->vec_loop_iv, UNKNOWN_LOCATION); above you are creating the PHI node at e->dest but here add the PHI arg to loop->vec_loop_iv - that's 'e' here, no? Consistency makes it easier to follow. I _think_ this code doesn't need to know about the "special" edge. > + > + /* All other exits use the previous iters. */ > + for (edge e : alt_exits) > + add_phi_arg (lcssa_phi, gimple_phi_result (from_phi), e, > + UNKNOWN_LOCATION); > + > + adjust_phi_and_debug_stmts (to_phi, latch_new, new_res); > + } > + > + /* Copy over any live SSA vars that may not have been materialized in > the > + loops themselves but would be in the exit block. However when the live > + value is not used inside the loop then we don't need to do this, if > we do > + then when we split the guard block the branch edge can end up > containing the > + wrong reference, particularly if it shares an edge with something > that has > + bypassed the loop. This is not something peeling can check so we need > to > + anticipate the usage of the live variable here. */ > + auto exit_map = redirect_edge_var_map_vector (exit); Hmm, did I use that in my attemt to refactor things? ... > + if (exit_map) > + for (auto vm : exit_map) > + { > + if (lcssa_vars.contains (vm.def) > + || TREE_CODE (vm.def) != SSA_NAME) the latter check is cheaper so it should come first > + continue; > + > + imm_use_iterator imm_iter; > + use_operand_p use_p; > + bool use_in_loop = false; > + > + FOR_EACH_IMM_USE_FAST (use_p, imm_iter, vm.def) > { > - gphi *phi = gsi.phi (); > - tree orig_arg = PHI_ARG_DEF_FROM_EDGE (phi, e); > - location_t orig_locus > - = gimple_phi_arg_location_from_edge (phi, e); > + basic_block bb = gimple_bb (USE_STMT (use_p)); > + if (flow_bb_inside_loop_p (loop, bb) > + && !gimple_vuse (USE_STMT (use_p))) > + { > + use_in_loop = true; > + break; > + } > + } > > - add_phi_arg (phi, orig_arg, new_exit, orig_locus); > + if (!use_in_loop) > + { > + /* Do a final check to see if it's perhaps defined in the loop. > This > + mirrors the relevancy analysis's used_outside_scope. */ > + gimple *stmt = SSA_NAME_DEF_STMT (vm.def); > + if (!stmt || !flow_bb_inside_loop_p (loop, gimple_bb (stmt))) > + continue; > } > + > + tree new_res = copy_ssa_name (vm.result); > + gphi *lcssa_phi = create_phi_node (new_res, e->dest); > + for (edge exit : loop_exits) > + add_phi_arg (lcssa_phi, vm.def, exit, vm.locus); not sure what you are doing above - I guess I have to play with it in a debug session. > } > - redirect_edge_and_branch_force (e, new_preheader); > - flush_pending_stmts (e); > + > set_immediate_dominator (CDI_DOMINATORS, new_preheader, e->src); > - if (was_imm_dom || duplicate_outer_loop) > + > + if ((was_imm_dom || duplicate_outer_loop) && !multiple_exits_p) > set_immediate_dominator (CDI_DOMINATORS, exit_dest, new_exit->src); > > /* And remove the non-necessary forwarder again. Keep the other > @@ -1647,9 +1756,42 @@ slpeel_tree_duplicate_loop_to_edge_cfg (class loop > *loop, > delete_basic_block (preheader); > set_immediate_dominator (CDI_DOMINATORS, scalar_loop->header, > loop_preheader_edge (scalar_loop)->src); > + > + /* Finally after wiring the new epilogue we need to update its main > exit > + to the original function exit we recorded. Other exits are already > + correct. */ > + if (multiple_exits_p) > + { > + for (edge e : get_loop_exit_edges (loop)) > + doms.safe_push (e->dest); > + update_loop = new_loop; > + doms.safe_push (exit_dest); > + > + /* Likely a fall-through edge, so update if needed. */ > + if (single_succ_p (exit_dest)) > + doms.safe_push (single_succ (exit_dest)); > + } > } > else /* Add the copy at entry. */ > { > + /* Copy the current loop LC PHI nodes between the original loop exit > + block and the new loop header. This allows us to later split the > + preheader block and still find the right LC nodes. */ > + edge old_latch_loop = loop_latch_edge (loop); > + edge old_latch_init = loop_preheader_edge (loop); > + edge new_latch_loop = loop_latch_edge (new_loop); > + edge new_latch_init = loop_preheader_edge (new_loop); > + for (auto gsi_from = gsi_start_phis (new_latch_init->dest), see above > + gsi_to = gsi_start_phis (old_latch_loop->dest); > + flow_loops && !gsi_end_p (gsi_from) && !gsi_end_p (gsi_to); > + gsi_next (&gsi_from), gsi_next (&gsi_to)) > + { > + gimple *from_phi = gsi_stmt (gsi_from); > + gimple *to_phi = gsi_stmt (gsi_to); > + tree new_arg = PHI_ARG_DEF_FROM_EDGE (from_phi, new_latch_loop); > + adjust_phi_and_debug_stmts (to_phi, old_latch_init, new_arg); > + } > + > if (scalar_loop != loop) > { > /* Remove the non-necessary forwarder of scalar_loop again. */ > @@ -1677,31 +1819,36 @@ slpeel_tree_duplicate_loop_to_edge_cfg (class loop > *loop, > delete_basic_block (new_preheader); > set_immediate_dominator (CDI_DOMINATORS, new_loop->header, > loop_preheader_edge (new_loop)->src); > + > + if (multiple_exits_p) > + update_loop = loop; > } > > - if (scalar_loop != loop) > + if (multiple_exits_p) > { > - /* Update new_loop->header PHIs, so that on the preheader > - edge they are the ones from loop rather than scalar_loop. */ > - gphi_iterator gsi_orig, gsi_new; > - edge orig_e = loop_preheader_edge (loop); > - edge new_e = loop_preheader_edge (new_loop); > - > - for (gsi_orig = gsi_start_phis (loop->header), > - gsi_new = gsi_start_phis (new_loop->header); > - !gsi_end_p (gsi_orig) && !gsi_end_p (gsi_new); > - gsi_next (&gsi_orig), gsi_next (&gsi_new)) > + for (edge e : get_loop_exit_edges (update_loop)) > { > - gphi *orig_phi = gsi_orig.phi (); > - gphi *new_phi = gsi_new.phi (); > - tree orig_arg = PHI_ARG_DEF_FROM_EDGE (orig_phi, orig_e); > - location_t orig_locus > - = gimple_phi_arg_location_from_edge (orig_phi, orig_e); > - > - add_phi_arg (new_phi, orig_arg, new_e, orig_locus); > + edge ex; > + edge_iterator ei; > + FOR_EACH_EDGE (ex, ei, e->dest->succs) > + { > + /* Find the first non-fallthrough block as fall-throughs can't > + dominate other blocks. */ > + while ((ex->flags & EDGE_FALLTHRU) I don't think EDGE_FALLTHRU is set correctly, what's wrong with just using single_succ_p here? A fallthru edge src dominates the fallthru edge dest, so the sentence above doesn't make sense. > + && single_succ_p (ex->dest)) > + { > + doms.safe_push (ex->dest); > + ex = single_succ_edge (ex->dest); > + } > + doms.safe_push (ex->dest); > + } > + doms.safe_push (e->dest); > } > - } > > + iterate_fix_dominators (CDI_DOMINATORS, doms, false); > + if (updated_doms) > + updated_doms->safe_splice (doms); > + } > free (new_bbs); > free (bbs); > > @@ -1777,6 +1924,9 @@ slpeel_can_duplicate_loop_p (const loop_vec_info > loop_vinfo, const_edge e) > gimple_stmt_iterator loop_exit_gsi = gsi_last_bb (exit_e->src); > unsigned int num_bb = loop->inner? 5 : 2; > > + if (LOOP_VINFO_EARLY_BREAKS (loop_vinfo)) > + num_bb += LOOP_VINFO_ALT_EXITS (loop_vinfo).length (); > + I think checking the number of BBs is odd, I don't remember anything in slpeel is specifically tied to that? I think we can simply drop this or do you remember anything that would depend on ->num_nodes being only exactly 5 or 2? > /* All loops have an outer scope; the only case loop->outer is NULL is for > the function itself. */ > if (!loop_outer (loop) > @@ -2044,6 +2194,11 @@ vect_update_ivs_after_vectorizer (loop_vec_info > loop_vinfo, > class loop *loop = LOOP_VINFO_LOOP (loop_vinfo); > basic_block update_bb = update_e->dest; > > + /* For early exits we'll update the IVs in > + vect_update_ivs_after_early_break. */ > + if (LOOP_VINFO_EARLY_BREAKS (loop_vinfo)) > + return; > + > basic_block exit_bb = LOOP_VINFO_IV_EXIT (loop_vinfo)->dest; > > /* Make sure there exists a single-predecessor exit bb: */ > @@ -2131,6 +2286,208 @@ vect_update_ivs_after_vectorizer (loop_vec_info > loop_vinfo, > /* Fix phi expressions in the successor bb. */ > adjust_phi_and_debug_stmts (phi1, update_e, ni_name); > } > + return; we don't usually place a return at the end of void functions > +} > + > +/* Function vect_update_ivs_after_early_break. > + > + "Advance" the induction variables of LOOP to the value they should take > + after the execution of LOOP. This is currently necessary because the > + vectorizer does not handle induction variables that are used after the > + loop. Such a situation occurs when the last iterations of LOOP are > + peeled, because of the early exit. With an early exit we always peel > the > + loop. > + > + Input: > + - LOOP_VINFO - a loop info structure for the loop that is going to be > + vectorized. The last few iterations of LOOP were peeled. > + - LOOP - a loop that is going to be vectorized. The last few iterations > + of LOOP were peeled. > + - VF - The loop vectorization factor. > + - NITERS_ORIG - the number of iterations that LOOP executes (before it > is > + vectorized). i.e, the number of times the ivs should be > + bumped. > + - NITERS_VECTOR - The number of iterations that the vector LOOP > executes. > + - UPDATE_E - a successor edge of LOOP->exit that is on the (only) path > + coming out from LOOP on which there are uses of the LOOP ivs > + (this is the path from LOOP->exit to epilog_loop->preheader). > + > + The new definitions of the ivs are placed in LOOP->exit. > + The phi args associated with the edge UPDATE_E in the bb > + UPDATE_E->dest are updated accordingly. > + > + Output: > + - If available, the LCSSA phi node for the loop IV temp. > + > + Assumption 1: Like the rest of the vectorizer, this function assumes > + a single loop exit that has a single predecessor. > + > + Assumption 2: The phi nodes in the LOOP header and in update_bb are > + organized in the same order. > + > + Assumption 3: The access function of the ivs is simple enough (see > + vect_can_advance_ivs_p). This assumption will be relaxed in the future. > + > + Assumption 4: Exactly one of the successors of LOOP exit-bb is on a path > + coming out of LOOP on which the ivs of LOOP are used (this is the path > + that leads to the epilog loop; other paths skip the epilog loop). This > + path starts with the edge UPDATE_E, and its destination (denoted > update_bb) > + needs to have its phis updated. > + */ > + > +static tree > +vect_update_ivs_after_early_break (loop_vec_info loop_vinfo, class loop * > epilog, > + poly_int64 vf, tree niters_orig, > + tree niters_vector, edge update_e) > +{ > + if (!LOOP_VINFO_EARLY_BREAKS (loop_vinfo)) > + return NULL; > + > + gphi_iterator gsi, gsi1; > + tree ni_name, ivtmp = NULL; > + basic_block update_bb = update_e->dest; > + vec<edge> alt_exits = LOOP_VINFO_ALT_EXITS (loop_vinfo); > + edge loop_iv = LOOP_VINFO_IV_EXIT (loop_vinfo); > + basic_block exit_bb = loop_iv->dest; > + class loop *loop = LOOP_VINFO_LOOP (loop_vinfo); > + gcond *cond = LOOP_VINFO_LOOP_IV_COND (loop_vinfo); > + > + gcc_assert (cond); > + > + for (gsi = gsi_start_phis (loop->header), gsi1 = gsi_start_phis > (update_bb); > + !gsi_end_p (gsi) && !gsi_end_p (gsi1); > + gsi_next (&gsi), gsi_next (&gsi1)) > + { > + tree init_expr, final_expr, step_expr; > + tree type; > + tree var, ni, off; > + gimple_stmt_iterator last_gsi; > + > + gphi *phi = gsi1.phi (); > + tree phi_ssa = PHI_ARG_DEF_FROM_EDGE (phi, loop_preheader_edge > (epilog)); I'm confused about the setup. update_bb looks like the block with the loop-closed PHI nodes of 'loop' and the exit (update_e)? How does loop_preheader_edge (epilog) come into play here? That would feed into epilog->header PHIs?! It would be nice to name 'gsi[1]', 'update_e' and 'update_bb' in a better way? Is update_bb really epilog->header?! We're missing checking in PHI_ARG_DEF_FROM_EDGE, namely that E->dest == gimple_bb (PHI) - we're just using E->dest_idx there which "works" even for totally unrelated edges. > + gphi *phi1 = dyn_cast <gphi *> (SSA_NAME_DEF_STMT (phi_ssa)); > + if (!phi1) shouldn't that be an assert? > + continue; > + stmt_vec_info phi_info = loop_vinfo->lookup_stmt (gsi.phi ()); > + if (dump_enabled_p ()) > + dump_printf_loc (MSG_NOTE, vect_location, > + "vect_update_ivs_after_early_break: phi: %G", > + (gimple *)phi); > + > + /* Skip reduction and virtual phis. */ > + if (!iv_phi_p (phi_info)) > + { > + if (dump_enabled_p ()) > + dump_printf_loc (MSG_NOTE, vect_location, > + "reduc or virtual phi. skip.\n"); > + continue; > + } > + > + /* For multiple exits where we handle early exits we need to carry on > + with the previous IV as loop iteration was not done because we exited > + early. As such just grab the original IV. */ > + phi_ssa = PHI_ARG_DEF_FROM_EDGE (gsi.phi (), loop_latch_edge (loop)); but this should be taken care of by LC SSA? OK, have to continue tomorrow from here. Richard. > + if (gimple_cond_lhs (cond) != phi_ssa > + && gimple_cond_rhs (cond) != phi_ssa) > + { > + type = TREE_TYPE (gimple_phi_result (phi)); > + step_expr = STMT_VINFO_LOOP_PHI_EVOLUTION_PART (phi_info); > + step_expr = unshare_expr (step_expr); > + > + /* We previously generated the new merged phi in the same BB as the > + guard. So use that to perform the scaling on rather than the > + normal loop phi which don't take the early breaks into account. */ > + final_expr = gimple_phi_result (phi1); > + init_expr = PHI_ARG_DEF_FROM_EDGE (gsi.phi (), loop_preheader_edge > (loop)); > + > + tree stype = TREE_TYPE (step_expr); > + /* For early break the final loop IV is: > + init + (final - init) * vf which takes into account peeling > + values and non-single steps. */ > + off = fold_build2 (MINUS_EXPR, stype, > + fold_convert (stype, final_expr), > + fold_convert (stype, init_expr)); > + /* Now adjust for VF to get the final iteration value. */ > + off = fold_build2 (MULT_EXPR, stype, off, build_int_cst (stype, vf)); > + > + /* Adjust the value with the offset. */ > + if (POINTER_TYPE_P (type)) > + ni = fold_build_pointer_plus (init_expr, off); > + else > + ni = fold_convert (type, > + fold_build2 (PLUS_EXPR, stype, > + fold_convert (stype, init_expr), > + off)); > + var = create_tmp_var (type, "tmp"); > + > + last_gsi = gsi_last_bb (exit_bb); > + gimple_seq new_stmts = NULL; > + ni_name = force_gimple_operand (ni, &new_stmts, false, var); > + /* Exit_bb shouldn't be empty. */ > + if (!gsi_end_p (last_gsi)) > + gsi_insert_seq_after (&last_gsi, new_stmts, GSI_SAME_STMT); > + else > + gsi_insert_seq_before (&last_gsi, new_stmts, GSI_SAME_STMT); > + > + /* Fix phi expressions in the successor bb. */ > + adjust_phi_and_debug_stmts (phi, update_e, ni_name); > + } > + else > + { > + type = TREE_TYPE (gimple_phi_result (phi)); > + step_expr = STMT_VINFO_LOOP_PHI_EVOLUTION_PART (phi_info); > + step_expr = unshare_expr (step_expr); > + > + /* We previously generated the new merged phi in the same BB as the > + guard. So use that to perform the scaling on rather than the > + normal loop phi which don't take the early breaks into account. */ > + init_expr = PHI_ARG_DEF_FROM_EDGE (phi1, loop_preheader_edge (loop)); > + tree stype = TREE_TYPE (step_expr); > + > + if (vf.is_constant ()) > + { > + ni = fold_build2 (MULT_EXPR, stype, > + fold_convert (stype, > + niters_vector), > + build_int_cst (stype, vf)); > + > + ni = fold_build2 (MINUS_EXPR, stype, > + fold_convert (stype, > + niters_orig), > + fold_convert (stype, ni)); > + } > + else > + /* If the loop's VF isn't constant then the loop must have been > + masked, so at the end of the loop we know we have finished > + the entire loop and found nothing. */ > + ni = build_zero_cst (stype); > + > + ni = fold_convert (type, ni); > + /* We don't support variable n in this version yet. */ > + gcc_assert (TREE_CODE (ni) == INTEGER_CST); > + > + var = create_tmp_var (type, "tmp"); > + > + last_gsi = gsi_last_bb (exit_bb); > + gimple_seq new_stmts = NULL; > + ni_name = force_gimple_operand (ni, &new_stmts, false, var); > + /* Exit_bb shouldn't be empty. */ > + if (!gsi_end_p (last_gsi)) > + gsi_insert_seq_after (&last_gsi, new_stmts, GSI_SAME_STMT); > + else > + gsi_insert_seq_before (&last_gsi, new_stmts, GSI_SAME_STMT); > + > + adjust_phi_and_debug_stmts (phi1, loop_iv, ni_name); > + > + for (edge exit : alt_exits) > + adjust_phi_and_debug_stmts (phi1, exit, > + build_int_cst (TREE_TYPE (step_expr), > + vf)); > + ivtmp = gimple_phi_result (phi1); > + } > + } > + > + return ivtmp; > } > > /* Return a gimple value containing the misalignment (measured in vector > @@ -2632,137 +2989,34 @@ vect_gen_vector_loop_niters_mult_vf (loop_vec_info > loop_vinfo, > > /* 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. If it is found, return the phi result; otherwise return > - NULL. */ > + 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, class loop *epilog ATTRIBUTE_UNUSED, > - gphi *lcssa_phi) > + gphi *lcssa_phi, int lcssa_edge = 0) > { > gphi_iterator gsi; > edge e = loop->vec_loop_iv; > > - gcc_assert (single_pred_p (e->dest)); > for (gsi = gsi_start_phis (e->dest); !gsi_end_p (gsi); gsi_next (&gsi)) > { > gphi *phi = gsi.phi (); > - if (operand_equal_p (PHI_ARG_DEF (phi, 0), > - PHI_ARG_DEF (lcssa_phi, 0), 0)) > - return PHI_RESULT (phi); > - } > - return NULL_TREE; > -} > - > -/* Function slpeel_tree_duplicate_loop_to_edge_cfg duplciates FIRST/SECOND > - from SECOND/FIRST and puts it at the original loop's preheader/exit > - edge, the two loops are arranged as below: > - > - preheader_a: > - first_loop: > - header_a: > - i_1 = PHI<i_0, i_2>; > - ... > - i_2 = i_1 + 1; > - if (cond_a) > - goto latch_a; > - else > - goto between_bb; > - latch_a: > - goto header_a; > - > - between_bb: > - ;; i_x = PHI<i_2>; ;; LCSSA phi node to be created for FIRST, > - > - second_loop: > - header_b: > - i_3 = PHI<i_0, i_4>; ;; Use of i_0 to be replaced with i_x, > - or with i_2 if no LCSSA phi is created > - under condition of CREATE_LCSSA_FOR_IV_PHIS. > - ... > - i_4 = i_3 + 1; > - if (cond_b) > - goto latch_b; > - else > - goto exit_bb; > - latch_b: > - goto header_b; > - > - exit_bb: > - > - This function creates loop closed SSA for the first loop; update the > - second loop's PHI nodes by replacing argument on incoming edge with the > - result of newly created lcssa PHI nodes. IF CREATE_LCSSA_FOR_IV_PHIS > - is false, Loop closed ssa phis will only be created for non-iv phis for > - the first loop. > - > - This function assumes exit bb of the first loop is preheader bb of the > - second loop, i.e, between_bb in the example code. With PHIs updated, > - the second loop will execute rest iterations of the first. */ > - > -static void > -slpeel_update_phi_nodes_for_loops (loop_vec_info loop_vinfo, > - class loop *first, class loop *second, > - bool create_lcssa_for_iv_phis) > -{ > - gphi_iterator gsi_update, gsi_orig; > - class loop *loop = LOOP_VINFO_LOOP (loop_vinfo); > - > - edge first_latch_e = EDGE_SUCC (first->latch, 0); > - edge second_preheader_e = loop_preheader_edge (second); > - basic_block between_bb = single_exit (first)->dest; > - > - gcc_assert (between_bb == second_preheader_e->src); > - gcc_assert (single_pred_p (between_bb) && single_succ_p (between_bb)); > - /* Either the first loop or the second is the loop to be vectorized. */ > - gcc_assert (loop == first || loop == second); > - > - for (gsi_orig = gsi_start_phis (first->header), > - gsi_update = gsi_start_phis (second->header); > - !gsi_end_p (gsi_orig) && !gsi_end_p (gsi_update); > - gsi_next (&gsi_orig), gsi_next (&gsi_update)) > - { > - gphi *orig_phi = gsi_orig.phi (); > - gphi *update_phi = gsi_update.phi (); > - > - tree arg = PHI_ARG_DEF_FROM_EDGE (orig_phi, first_latch_e); > - /* Generate lcssa PHI node for the first loop. */ > - gphi *vect_phi = (loop == first) ? orig_phi : update_phi; > - stmt_vec_info vect_phi_info = loop_vinfo->lookup_stmt (vect_phi); > - if (create_lcssa_for_iv_phis || !iv_phi_p (vect_phi_info)) > + /* 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) > e->dest_idx) > { > - tree new_res = copy_ssa_name (PHI_RESULT (orig_phi)); > - gphi *lcssa_phi = create_phi_node (new_res, between_bb); > - add_phi_arg (lcssa_phi, arg, single_exit (first), UNKNOWN_LOCATION); > - arg = new_res; > - } > - > - /* Update PHI node in the second loop by replacing arg on the loop's > - incoming edge. */ > - adjust_phi_and_debug_stmts (update_phi, second_preheader_e, arg); > - } > - > - /* For epilogue peeling we have to make sure to copy all LC PHIs > - for correct vectorization of live stmts. */ > - if (loop == first) > - { > - basic_block orig_exit = single_exit (second)->dest; > - for (gsi_orig = gsi_start_phis (orig_exit); > - !gsi_end_p (gsi_orig); gsi_next (&gsi_orig)) > - { > - gphi *orig_phi = gsi_orig.phi (); > - tree orig_arg = PHI_ARG_DEF (orig_phi, 0); > - if (TREE_CODE (orig_arg) != SSA_NAME || virtual_operand_p (orig_arg)) > - continue; > - > - /* Already created in the above loop. */ > - if (find_guard_arg (first, second, orig_phi)) > + tree var = PHI_ARG_DEF (phi, e->dest_idx); > + if (TREE_CODE (var) != SSA_NAME) > continue; > > - tree new_res = copy_ssa_name (orig_arg); > - gphi *lcphi = create_phi_node (new_res, between_bb); > - add_phi_arg (lcphi, orig_arg, single_exit (first), UNKNOWN_LOCATION); > + if (operand_equal_p (get_current_def (var), > + 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 > @@ -2910,13 +3164,11 @@ slpeel_update_phi_nodes_for_guard2 (class loop *loop, > class loop *epilog, > gcc_assert (single_succ_p (merge_bb)); > edge e = single_succ_edge (merge_bb); > basic_block exit_bb = e->dest; > - gcc_assert (single_pred_p (exit_bb)); > - gcc_assert (single_pred (exit_bb) == single_exit (epilog)->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, 0); > + tree old_arg = PHI_ARG_DEF (update_phi, e->dest_idx); > > tree merge_arg = NULL_TREE; > > @@ -2928,7 +3180,7 @@ slpeel_update_phi_nodes_for_guard2 (class loop *loop, > class loop *epilog, > if (!merge_arg) > merge_arg = old_arg; > > - tree guard_arg = find_guard_arg (loop, epilog, update_phi); > + tree guard_arg = find_guard_arg (loop, 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) > @@ -2948,21 +3200,6 @@ slpeel_update_phi_nodes_for_guard2 (class loop *loop, > class loop *epilog, > } > } > > -/* EPILOG loop is duplicated from the original loop for vectorizing, > - the arg of its loop closed ssa PHI needs to be updated. */ > - > -static void > -slpeel_update_phi_nodes_for_lcssa (class loop *epilog) > -{ > - gphi_iterator gsi; > - basic_block exit_bb = single_exit (epilog)->dest; > - > - gcc_assert (single_pred_p (exit_bb)); > - edge e = EDGE_PRED (exit_bb, 0); > - for (gsi = gsi_start_phis (exit_bb); !gsi_end_p (gsi); gsi_next (&gsi)) > - rename_use_op (PHI_ARG_DEF_PTR_FROM_EDGE (gsi.phi (), e)); > -} > - > /* EPILOGUE_VINFO is an epilogue loop that we now know would need to > iterate exactly CONST_NITERS times. Make a final decision about > whether the epilogue loop should be used, returning true if so. */ > @@ -3138,6 +3375,14 @@ vect_do_peeling (loop_vec_info loop_vinfo, tree > niters, tree nitersm1, > bound_epilog += vf - 1; > if (LOOP_VINFO_PEELING_FOR_GAPS (loop_vinfo)) > bound_epilog += 1; > + /* For early breaks the scalar loop needs to execute at most VF times > + to find the element that caused the break. */ > + if (LOOP_VINFO_EARLY_BREAKS (loop_vinfo)) > + { > + bound_epilog = vf; > + /* Force a scalar epilogue as we can't vectorize the index finding. */ > + vect_epilogues = false; > + } > bool epilog_peeling = maybe_ne (bound_epilog, 0U); > poly_uint64 bound_scalar = bound_epilog; > > @@ -3297,16 +3542,24 @@ vect_do_peeling (loop_vec_info loop_vinfo, tree > niters, tree nitersm1, > bound_prolog + bound_epilog) > : (!LOOP_REQUIRES_VERSIONING (loop_vinfo) > || vect_epilogues)); > + > + /* We only support early break vectorization on known bounds at this time. > + This means that if the vector loop can't be entered then we won't > generate > + it at all. So for now force skip_vector off because the additional > control > + flow messes with the BB exits and we've already analyzed them. */ > + skip_vector = skip_vector && !LOOP_VINFO_EARLY_BREAKS (loop_vinfo); > + > /* Epilog loop must be executed if the number of iterations for epilog > loop is known at compile time, otherwise we need to add a check at > the end of vector loop and skip to the end of epilog loop. */ > bool skip_epilog = (prolog_peeling < 0 > || !LOOP_VINFO_NITERS_KNOWN_P (loop_vinfo) > || !vf.is_constant ()); > - /* PEELING_FOR_GAPS is special because epilog loop must be executed. */ > - if (LOOP_VINFO_PEELING_FOR_GAPS (loop_vinfo)) > + /* PEELING_FOR_GAPS and peeling for early breaks are special because epilog > + loop must be executed. */ > + if (LOOP_VINFO_PEELING_FOR_GAPS (loop_vinfo) > + || LOOP_VINFO_EARLY_BREAKS (loop_vinfo)) > skip_epilog = false; > - > class loop *scalar_loop = LOOP_VINFO_SCALAR_LOOP (loop_vinfo); > auto_vec<profile_count> original_counts; > basic_block *original_bbs = NULL; > @@ -3344,13 +3597,13 @@ vect_do_peeling (loop_vec_info loop_vinfo, tree > niters, tree nitersm1, > if (prolog_peeling) > { > e = loop_preheader_edge (loop); > - gcc_checking_assert (slpeel_can_duplicate_loop_p (loop, e)); > - > + gcc_checking_assert (slpeel_can_duplicate_loop_p (loop_vinfo, e)); > /* Peel prolog and put it on preheader edge of loop. */ > - prolog = slpeel_tree_duplicate_loop_to_edge_cfg (loop, scalar_loop, e); > + prolog = slpeel_tree_duplicate_loop_to_edge_cfg (loop, scalar_loop, e, > + true); > gcc_assert (prolog); > prolog->force_vectorize = false; > - slpeel_update_phi_nodes_for_loops (loop_vinfo, prolog, loop, true); > + > first_loop = prolog; > reset_original_copy_tables (); > > @@ -3420,11 +3673,12 @@ vect_do_peeling (loop_vec_info loop_vinfo, tree > niters, tree nitersm1, > as the transformations mentioned above make less or no sense when not > vectorizing. */ > epilog = vect_epilogues ? get_loop_copy (loop) : scalar_loop; > - epilog = slpeel_tree_duplicate_loop_to_edge_cfg (loop, epilog, e); > + auto_vec<basic_block> doms; > + epilog = slpeel_tree_duplicate_loop_to_edge_cfg (loop, epilog, e, true, > + &doms); > gcc_assert (epilog); > > epilog->force_vectorize = false; > - slpeel_update_phi_nodes_for_loops (loop_vinfo, loop, epilog, false); > > /* Scalar version loop may be preferred. In this case, add guard > and skip to epilog. Note this only happens when the number of > @@ -3496,6 +3750,54 @@ vect_do_peeling (loop_vec_info loop_vinfo, tree > niters, tree nitersm1, > vect_update_ivs_after_vectorizer (loop_vinfo, niters_vector_mult_vf, > update_e); > > + /* For early breaks we must create a guard to check how many iterations > + of the scalar loop are yet to be performed. */ > + if (LOOP_VINFO_EARLY_BREAKS (loop_vinfo)) > + { > + tree ivtmp = > + vect_update_ivs_after_early_break (loop_vinfo, epilog, vf, niters, > + *niters_vector, update_e); > + > + gcc_assert (ivtmp); > + tree guard_cond = fold_build2 (EQ_EXPR, boolean_type_node, > + fold_convert (TREE_TYPE (niters), > + ivtmp), > + build_zero_cst (TREE_TYPE (niters))); > + basic_block guard_bb = LOOP_VINFO_IV_EXIT (loop_vinfo)->dest; > + > + /* If we had a fallthrough edge, the guard will the threaded through > + and so we may need to find the actual final edge. */ > + edge final_edge = epilog->vec_loop_iv; > + /* slpeel_update_phi_nodes_for_guard2 expects an empty block in > + between the guard and the exit edge. It only adds new nodes and > + doesn't update existing one in the current scheme. */ > + basic_block guard_to = split_edge (final_edge); > + edge guard_e = slpeel_add_loop_guard (guard_bb, guard_cond, guard_to, > + guard_bb, prob_epilog.invert (), > + irred_flag); > + doms.safe_push (guard_bb); > + > + iterate_fix_dominators (CDI_DOMINATORS, doms, false); > + > + /* We must update all the edges from the new guard_bb. */ > + slpeel_update_phi_nodes_for_guard2 (loop, epilog, guard_e, > + final_edge); > + > + /* If the loop was versioned we'll have an intermediate BB between > + the guard and the exit. This intermediate block is required > + because in the current scheme of things the guard block phi > + updating can only maintain LCSSA by creating new blocks. In this > + case we just need to update the uses in this block as well. */ > + if (loop != scalar_loop) > + { > + for (gphi_iterator gsi = gsi_start_phis (guard_to); > + !gsi_end_p (gsi); gsi_next (&gsi)) > + rename_use_op (PHI_ARG_DEF_PTR_FROM_EDGE (gsi.phi (), guard_e)); > + } > + > + flush_pending_stmts (guard_e); > + } > + > if (skip_epilog) > { > guard_cond = fold_build2 (EQ_EXPR, boolean_type_node, > @@ -3520,8 +3822,6 @@ vect_do_peeling (loop_vec_info loop_vinfo, tree niters, > tree nitersm1, > } > scale_loop_profile (epilog, prob_epilog, 0); > } > - else > - slpeel_update_phi_nodes_for_lcssa (epilog); > > unsigned HOST_WIDE_INT bound; > if (bound_scalar.is_constant (&bound)) > diff --git a/gcc/tree-vect-loop.cc b/gcc/tree-vect-loop.cc > index > b4a98de80aa39057fc9b17977dd0e347b4f0fb5d..ab9a2048186f461f5ec49f21421958e7ee25eada > 100644 > --- a/gcc/tree-vect-loop.cc > +++ b/gcc/tree-vect-loop.cc > @@ -1007,6 +1007,8 @@ _loop_vec_info::_loop_vec_info (class loop *loop_in, > vec_info_shared *shared) > partial_load_store_bias (0), > peeling_for_gaps (false), > peeling_for_niter (false), > + early_breaks (false), > + non_break_control_flow (false), > no_data_dependencies (false), > has_mask_store (false), > scalar_loop_scaling (profile_probability::uninitialized ()), > @@ -1199,6 +1201,14 @@ vect_need_peeling_or_partial_vectors_p (loop_vec_info > loop_vinfo) > th = LOOP_VINFO_COST_MODEL_THRESHOLD (LOOP_VINFO_ORIG_LOOP_INFO > (loop_vinfo)); > > + /* When we have multiple exits and VF is unknown, we must require partial > + vectors because the loop bounds is not a minimum but a maximum. That > is to > + say we cannot unpredicate the main loop unless we peel or use partial > + vectors in the epilogue. */ > + if (LOOP_VINFO_EARLY_BREAKS (loop_vinfo) > + && !LOOP_VINFO_VECT_FACTOR (loop_vinfo).is_constant ()) > + return true; > + > if (LOOP_VINFO_NITERS_KNOWN_P (loop_vinfo) > && LOOP_VINFO_PEELING_FOR_ALIGNMENT (loop_vinfo) >= 0) > { > @@ -1652,12 +1662,12 @@ vect_compute_single_scalar_iteration_cost > (loop_vec_info loop_vinfo) > loop_vinfo->scalar_costs->finish_cost (nullptr); > } > > - > /* Function vect_analyze_loop_form. > > Verify that certain CFG restrictions hold, including: > - the loop has a pre-header > - - the loop has a single entry and exit > + - the loop has a single entry > + - nested loops can have only a single exit. > - the loop exit condition is simple enough > - the number of iterations can be analyzed, i.e, a countable loop. The > niter could be analyzed under some assumptions. */ > @@ -1693,11 +1703,6 @@ vect_analyze_loop_form (class loop *loop, > vect_loop_form_info *info) > | > (exit-bb) */ > > - if (loop->num_nodes != 2) > - return opt_result::failure_at (vect_location, > - "not vectorized:" > - " control flow in loop.\n"); > - > if (empty_block_p (loop->header)) > return opt_result::failure_at (vect_location, > "not vectorized: empty loop.\n"); > @@ -1768,11 +1773,13 @@ vect_analyze_loop_form (class loop *loop, > vect_loop_form_info *info) > dump_printf_loc (MSG_NOTE, vect_location, > "Considering outer-loop vectorization.\n"); > info->inner_loop_cond = inner.loop_cond; > + > + if (!single_exit (loop)) > + return opt_result::failure_at (vect_location, > + "not vectorized: multiple exits.\n"); > + > } > > - if (!single_exit (loop)) > - return opt_result::failure_at (vect_location, > - "not vectorized: multiple exits.\n"); > if (EDGE_COUNT (loop->header->preds) != 2) > return opt_result::failure_at (vect_location, > "not vectorized:" > @@ -1788,11 +1795,36 @@ vect_analyze_loop_form (class loop *loop, > vect_loop_form_info *info) > "not vectorized: latch block not empty.\n"); > > /* Make sure the exit is not abnormal. */ > - edge e = single_exit (loop); > - if (e->flags & EDGE_ABNORMAL) > - return opt_result::failure_at (vect_location, > - "not vectorized:" > - " abnormal loop exit edge.\n"); > + auto_vec<edge> exits = get_loop_exit_edges (loop); > + edge nexit = loop->vec_loop_iv; > + for (edge e : exits) > + { > + if (e->flags & EDGE_ABNORMAL) > + return opt_result::failure_at (vect_location, > + "not vectorized:" > + " abnormal loop exit edge.\n"); > + /* Early break BB must be after the main exit BB. In theory we should > + be able to vectorize the inverse order, but the current flow in the > + the vectorizer always assumes you update successor PHI nodes, not > + preds. */ > + if (e != nexit && !dominated_by_p (CDI_DOMINATORS, nexit->src, e->src)) > + return opt_result::failure_at (vect_location, > + "not vectorized:" > + " abnormal loop exit edge order.\n"); > + } > + > + /* We currently only support early exit loops with known bounds. */ > + if (exits.length () > 1) > + { > + class tree_niter_desc niter; > + if (!number_of_iterations_exit_assumptions (loop, nexit, &niter, NULL) > + || chrec_contains_undetermined (niter.niter) > + || !evolution_function_is_constant_p (niter.niter)) > + return opt_result::failure_at (vect_location, > + "not vectorized:" > + " early breaks only supported on loops" > + " with known iteration bounds.\n"); > + } > > info->conds > = vect_get_loop_niters (loop, &info->assumptions, > @@ -1866,6 +1898,10 @@ vect_create_loop_vinfo (class loop *loop, > vec_info_shared *shared, > LOOP_VINFO_LOOP_CONDS (loop_vinfo).safe_splice (info->alt_loop_conds); > LOOP_VINFO_LOOP_IV_COND (loop_vinfo) = info->loop_cond; > > + /* Check to see if we're vectorizing multiple exits. */ > + LOOP_VINFO_EARLY_BREAKS (loop_vinfo) > + = !LOOP_VINFO_LOOP_CONDS (loop_vinfo).is_empty (); > + > if (info->inner_loop_cond) > { > stmt_vec_info inner_loop_cond_info > @@ -3070,7 +3106,8 @@ start_over: > > /* If an epilogue loop is required make sure we can create one. */ > if (LOOP_VINFO_PEELING_FOR_GAPS (loop_vinfo) > - || LOOP_VINFO_PEELING_FOR_NITER (loop_vinfo)) > + || LOOP_VINFO_PEELING_FOR_NITER (loop_vinfo) > + || LOOP_VINFO_EARLY_BREAKS (loop_vinfo)) > { > if (dump_enabled_p ()) > dump_printf_loc (MSG_NOTE, vect_location, "epilog loop required\n"); > @@ -5797,7 +5834,7 @@ vect_create_epilog_for_reduction (loop_vec_info > loop_vinfo, > basic_block exit_bb; > tree scalar_dest; > tree scalar_type; > - gimple *new_phi = NULL, *phi; > + gimple *new_phi = NULL, *phi = NULL; > gimple_stmt_iterator exit_gsi; > tree new_temp = NULL_TREE, new_name, new_scalar_dest; > gimple *epilog_stmt = NULL; > @@ -6039,6 +6076,33 @@ vect_create_epilog_for_reduction (loop_vec_info > loop_vinfo, > new_def = gimple_convert (&stmts, vectype, new_def); > reduc_inputs.quick_push (new_def); > } > + > + /* Update the other exits. */ > + if (LOOP_VINFO_EARLY_BREAKS (loop_vinfo)) > + { > + vec<edge> alt_exits = LOOP_VINFO_ALT_EXITS (loop_vinfo); > + gphi_iterator gsi, gsi1; > + for (edge exit : alt_exits) > + { > + /* Find the phi node to propaget into the exit block for each > + exit edge. */ > + for (gsi = gsi_start_phis (exit_bb), > + gsi1 = gsi_start_phis (exit->src); > + !gsi_end_p (gsi) && !gsi_end_p (gsi1); > + gsi_next (&gsi), gsi_next (&gsi1)) > + { > + /* There really should be a function to just get the number > + of phis inside a bb. */ > + if (phi && phi == gsi.phi ()) > + { > + gphi *phi1 = gsi1.phi (); > + SET_PHI_ARG_DEF (phi, exit->dest_idx, > + PHI_RESULT (phi1)); > + break; > + } > + } > + } > + } > gsi_insert_seq_before (&exit_gsi, stmts, GSI_SAME_STMT); > } > > @@ -10355,6 +10419,13 @@ vectorizable_live_operation (vec_info *vinfo, > new_tree = lane_extract <vec_lhs', ...>; > lhs' = new_tree; */ > > + /* When vectorizing an early break, any live statement that is used > + outside of the loop are dead. The loop will never get to them. > + We could change the liveness value during analysis instead but since > + the below code is invalid anyway just ignore it during codegen. */ > + if (LOOP_VINFO_EARLY_BREAKS (loop_vinfo)) > + return true; > + > class loop *loop = LOOP_VINFO_LOOP (loop_vinfo); > basic_block exit_bb = LOOP_VINFO_IV_EXIT (loop_vinfo)->dest; > gcc_assert (single_pred_p (exit_bb)); > @@ -11277,7 +11348,7 @@ vect_transform_loop (loop_vec_info loop_vinfo, gimple > *loop_vectorized_call) > /* Make sure there exists a single-predecessor exit bb. Do this before > versioning. */ > edge e = LOOP_VINFO_IV_EXIT (loop_vinfo); > - if (! single_pred_p (e->dest)) > + if (e && ! single_pred_p (e->dest) && !LOOP_VINFO_EARLY_BREAKS > (loop_vinfo)) > { > split_loop_exit_edge (e, true); > if (dump_enabled_p ()) > @@ -11303,7 +11374,7 @@ vect_transform_loop (loop_vec_info loop_vinfo, gimple > *loop_vectorized_call) > if (LOOP_VINFO_SCALAR_LOOP (loop_vinfo)) > { > e = single_exit (LOOP_VINFO_SCALAR_LOOP (loop_vinfo)); > - if (! single_pred_p (e->dest)) > + if (e && ! single_pred_p (e->dest)) > { > split_loop_exit_edge (e, true); > if (dump_enabled_p ()) > @@ -11641,7 +11712,8 @@ vect_transform_loop (loop_vec_info loop_vinfo, gimple > *loop_vectorized_call) > > /* Loops vectorized with a variable factor won't benefit from > unrolling/peeling. */ > - if (!vf.is_constant ()) > + if (!vf.is_constant () > + && !LOOP_VINFO_EARLY_BREAKS (loop_vinfo)) > { > loop->unroll = 1; > if (dump_enabled_p ()) > diff --git a/gcc/tree-vect-stmts.cc b/gcc/tree-vect-stmts.cc > index > 87c4353fa5180fcb7f60b192897456cf24f3fdbe..03524e8500ee06df42f82afe78ee2a7c627be45b > 100644 > --- a/gcc/tree-vect-stmts.cc > +++ b/gcc/tree-vect-stmts.cc > @@ -344,9 +344,34 @@ vect_stmt_relevant_p (stmt_vec_info stmt_info, > loop_vec_info loop_vinfo, > *live_p = false; > > /* cond stmt other than loop exit cond. */ > - if (is_ctrl_stmt (stmt_info->stmt) > - && STMT_VINFO_TYPE (stmt_info) != loop_exit_ctrl_vec_info_type) > - *relevant = vect_used_in_scope; > + if (is_ctrl_stmt (stmt_info->stmt)) > + { > + /* Ideally EDGE_LOOP_EXIT would have been set on the exit edge, but > + it looks like loop_manip doesn't do that.. So we have to do it > + the hard way. */ > + basic_block bb = gimple_bb (stmt_info->stmt); > + bool exit_bb = false, early_exit = false; > + edge_iterator ei; > + edge e; > + FOR_EACH_EDGE (e, ei, bb->succs) > + if (!flow_bb_inside_loop_p (loop, e->dest)) > + { > + exit_bb = true; > + early_exit = loop->vec_loop_iv->src != bb; > + break; > + } > + > + /* We should have processed any exit edge, so an edge not an early > + break must be a loop IV edge. We need to distinguish between the > + two as we don't want to generate code for the main loop IV. */ > + if (exit_bb) > + { > + if (early_exit) > + *relevant = vect_used_in_scope; > + } > + else if (bb->loop_father == loop) > + LOOP_VINFO_GENERAL_CTR_FLOW (loop_vinfo) = true; > + } > > /* changing memory. */ > if (gimple_code (stmt_info->stmt) != GIMPLE_PHI) > @@ -359,6 +384,11 @@ vect_stmt_relevant_p (stmt_vec_info stmt_info, > loop_vec_info loop_vinfo, > *relevant = vect_used_in_scope; > } > > + auto_vec<edge> exits = get_loop_exit_edges (loop); > + auto_bitmap exit_bbs; > + for (edge exit : exits) > + bitmap_set_bit (exit_bbs, exit->dest->index); > + > /* uses outside the loop. */ > FOR_EACH_PHI_OR_STMT_DEF (def_p, stmt_info->stmt, op_iter, SSA_OP_DEF) > { > @@ -377,7 +407,7 @@ vect_stmt_relevant_p (stmt_vec_info stmt_info, > loop_vec_info loop_vinfo, > /* We expect all such uses to be in the loop exit phis > (because of loop closed form) */ > gcc_assert (gimple_code (USE_STMT (use_p)) == GIMPLE_PHI); > - gcc_assert (bb == single_exit (loop)->dest); > + gcc_assert (bitmap_bit_p (exit_bbs, bb->index)); > > *live_p = true; > } > @@ -683,6 +713,13 @@ vect_mark_stmts_to_be_vectorized (loop_vec_info > loop_vinfo, bool *fatal) > } > } > > + /* Ideally this should be in vect_analyze_loop_form but we haven't seen all > + the conds yet at that point and there's no quick way to retrieve them. > */ > + if (LOOP_VINFO_GENERAL_CTR_FLOW (loop_vinfo)) > + return opt_result::failure_at (vect_location, > + "not vectorized:" > + " unsupported control flow in loop.\n"); > + > /* 2. Process_worklist */ > while (worklist.length () > 0) > { > @@ -778,6 +815,20 @@ vect_mark_stmts_to_be_vectorized (loop_vec_info > loop_vinfo, bool *fatal) > return res; > } > } > + } > + else if (gcond *cond = dyn_cast <gcond *> (stmt_vinfo->stmt)) > + { > + enum tree_code rhs_code = gimple_cond_code (cond); > + gcc_assert (TREE_CODE_CLASS (rhs_code) == tcc_comparison); > + opt_result res > + = process_use (stmt_vinfo, gimple_cond_lhs (cond), > + loop_vinfo, relevant, &worklist, false); > + if (!res) > + return res; > + res = process_use (stmt_vinfo, gimple_cond_rhs (cond), > + loop_vinfo, relevant, &worklist, false); > + if (!res) > + return res; > } > else if (gcall *call = dyn_cast <gcall *> (stmt_vinfo->stmt)) > { > @@ -11919,11 +11970,15 @@ vect_analyze_stmt (vec_info *vinfo, > node_instance, cost_vec); > if (!res) > return res; > - } > + } > + > + if (is_ctrl_stmt (stmt_info->stmt)) > + STMT_VINFO_DEF_TYPE (stmt_info) = vect_early_exit_def; > > switch (STMT_VINFO_DEF_TYPE (stmt_info)) > { > case vect_internal_def: > + case vect_early_exit_def: > break; > > case vect_reduction_def: > @@ -11956,6 +12011,7 @@ vect_analyze_stmt (vec_info *vinfo, > { > gcall *call = dyn_cast <gcall *> (stmt_info->stmt); > gcc_assert (STMT_VINFO_VECTYPE (stmt_info) > + || gimple_code (stmt_info->stmt) == GIMPLE_COND > || (call && gimple_call_lhs (call) == NULL_TREE)); > *need_to_vectorize = true; > } > diff --git a/gcc/tree-vectorizer.h b/gcc/tree-vectorizer.h > index > ec65b65b5910e9cbad0a8c7e83c950b6168b98bf..24a0567a2f23f1b3d8b340baff61d18da8e242dd > 100644 > --- a/gcc/tree-vectorizer.h > +++ b/gcc/tree-vectorizer.h > @@ -63,6 +63,7 @@ enum vect_def_type { > vect_internal_def, > vect_induction_def, > vect_reduction_def, > + vect_early_exit_def, > vect_double_reduction_def, > vect_nested_cycle, > vect_first_order_recurrence, > @@ -876,6 +877,13 @@ public: > we need to peel off iterations at the end to form an epilogue loop. */ > bool peeling_for_niter; > > + /* When the loop has early breaks that we can vectorize we need to peel > + the loop for the break finding loop. */ > + bool early_breaks; > + > + /* When the loop has a non-early break control flow inside. */ > + bool non_break_control_flow; > + > /* List of loop additional IV conditionals found in the loop. */ > auto_vec<gcond *> conds; > > @@ -985,9 +993,11 @@ public: > #define LOOP_VINFO_REDUCTION_CHAINS(L) (L)->reduction_chains > #define LOOP_VINFO_PEELING_FOR_GAPS(L) (L)->peeling_for_gaps > #define LOOP_VINFO_PEELING_FOR_NITER(L) (L)->peeling_for_niter > +#define LOOP_VINFO_EARLY_BREAKS(L) (L)->early_breaks > #define LOOP_VINFO_EARLY_BRK_CONFLICT_STMTS(L) (L)->early_break_conflict > #define LOOP_VINFO_EARLY_BRK_DEST_BB(L) (L)->early_break_dest_bb > #define LOOP_VINFO_EARLY_BRK_VUSES(L) (L)->early_break_vuses > +#define LOOP_VINFO_GENERAL_CTR_FLOW(L) (L)->non_break_control_flow > #define LOOP_VINFO_LOOP_CONDS(L) (L)->conds > #define LOOP_VINFO_LOOP_IV_COND(L) (L)->loop_iv_cond > #define LOOP_VINFO_NO_DATA_DEPENDENCIES(L) (L)->no_data_dependencies > @@ -1038,8 +1048,8 @@ public: > stack. */ > typedef opt_pointer_wrapper <loop_vec_info> opt_loop_vec_info; > > -inline loop_vec_info > -loop_vec_info_for_loop (class loop *loop) > +static inline loop_vec_info > +loop_vec_info_for_loop (const class loop *loop) > { > return (loop_vec_info) loop->aux; > } > @@ -1789,7 +1799,7 @@ is_loop_header_bb_p (basic_block bb) > { > if (bb == (bb->loop_father)->header) > return true; > - gcc_checking_assert (EDGE_COUNT (bb->preds) == 1); > + > return false; > } > > @@ -2176,9 +2186,10 @@ class auto_purge_vect_location > in tree-vect-loop-manip.cc. */ > extern void vect_set_loop_condition (class loop *, loop_vec_info, > tree, tree, tree, bool); > -extern bool slpeel_can_duplicate_loop_p (const class loop *, const_edge); > +extern bool slpeel_can_duplicate_loop_p (const loop_vec_info, const_edge); > class loop *slpeel_tree_duplicate_loop_to_edge_cfg (class loop *, > - class loop *, edge); > + class loop *, edge, bool, > + vec<basic_block> * = NULL); > class loop *vect_loop_versioning (loop_vec_info, gimple *); > extern class loop *vect_do_peeling (loop_vec_info, tree, tree, > tree *, tree *, tree *, int, bool, bool, > diff --git a/gcc/tree-vectorizer.cc b/gcc/tree-vectorizer.cc > index > a048e9d89178a37455bd7b83ab0f2a238a4ce69e..0dc5479dc92058b6c70c67f29f5dc9a8d72235f4 > 100644 > --- a/gcc/tree-vectorizer.cc > +++ b/gcc/tree-vectorizer.cc > @@ -1379,7 +1379,9 @@ pass_vectorize::execute (function *fun) > predicates that need to be shared for optimal predicate usage. > However reassoc will re-order them and prevent CSE from working > as it should. CSE only the loop body, not the entry. */ > - bitmap_set_bit (exit_bbs, single_exit (loop)->dest->index); > + auto_vec<edge> exits = get_loop_exit_edges (loop); > + for (edge exit : exits) > + bitmap_set_bit (exit_bbs, exit->dest->index); > > edge entry = EDGE_PRED (loop_preheader_edge (loop)->src, 0); > do_rpo_vn (fun, entry, exit_bbs); > > > > > -- Richard Biener <rguent...@suse.de> SUSE Software Solutions Germany GmbH, Frankenstrasse 146, 90461 Nuernberg, Germany; GF: Ivo Totev, Andrew Myers, Andrew McDonald, Boudien Moerman; HRB 36809 (AG Nuernberg)