aboutsummaryrefslogtreecommitdiff
path: root/gcc/tree-vect-loop.cc
diff options
context:
space:
mode:
authorThomas Schwinge <tschwinge@baylibre.com>2024-03-11 22:51:28 +0100
committerThomas Schwinge <tschwinge@baylibre.com>2024-03-11 22:51:28 +0100
commita95e21151a6366e7344d0f1983f99e318c5a7097 (patch)
tree11d987406d9ce8399ec1736477d971ef09344df2 /gcc/tree-vect-loop.cc
parent02d394b2736afa9a24ab3e1b8ad56fd6ac37e0f4 (diff)
parentaf4bb221153359f5948da917d5ef2df738bb1e61 (diff)
downloadgcc-a95e21151a6366e7344d0f1983f99e318c5a7097.zip
gcc-a95e21151a6366e7344d0f1983f99e318c5a7097.tar.gz
gcc-a95e21151a6366e7344d0f1983f99e318c5a7097.tar.bz2
Merge commit 'af4bb221153359f5948da917d5ef2df738bb1e61' into HEAD
Diffstat (limited to 'gcc/tree-vect-loop.cc')
-rw-r--r--gcc/tree-vect-loop.cc262
1 files changed, 172 insertions, 90 deletions
diff --git a/gcc/tree-vect-loop.cc b/gcc/tree-vect-loop.cc
index 23c6e82..ebab195 100644
--- a/gcc/tree-vect-loop.cc
+++ b/gcc/tree-vect-loop.cc
@@ -851,80 +851,137 @@ vect_fixup_scalar_cycles_with_patterns (loop_vec_info loop_vinfo)
in NUMBER_OF_ITERATIONSM1. Place the condition under which the
niter information holds in ASSUMPTIONS.
- Return the loop exit condition. */
+ Return the loop exit conditions. */
-static gcond *
-vect_get_loop_niters (class loop *loop, tree *assumptions,
+static vec<gcond *>
+vect_get_loop_niters (class loop *loop, const_edge main_exit, tree *assumptions,
tree *number_of_iterations, tree *number_of_iterationsm1)
{
- edge exit = single_exit (loop);
+ auto_vec<edge> exits = get_loop_exit_edges (loop);
+ vec<gcond *> conds;
+ conds.create (exits.length ());
class tree_niter_desc niter_desc;
tree niter_assumptions, niter, may_be_zero;
- gcond *cond = get_loop_exit_condition (loop);
*assumptions = boolean_true_node;
*number_of_iterationsm1 = chrec_dont_know;
*number_of_iterations = chrec_dont_know;
+
DUMP_VECT_SCOPE ("get_loop_niters");
- if (!exit)
- return cond;
+ if (exits.is_empty ())
+ return conds;
+
+ if (dump_enabled_p ())
+ dump_printf_loc (MSG_NOTE, vect_location, "Loop has %d exits.\n",
+ exits.length ());
- may_be_zero = NULL_TREE;
- if (!number_of_iterations_exit_assumptions (loop, exit, &niter_desc, NULL)
- || chrec_contains_undetermined (niter_desc.niter))
- return cond;
+ edge exit;
+ unsigned int i;
+ FOR_EACH_VEC_ELT (exits, i, exit)
+ {
+ gcond *cond = get_loop_exit_condition (exit);
+ if (cond)
+ conds.safe_push (cond);
- niter_assumptions = niter_desc.assumptions;
- may_be_zero = niter_desc.may_be_zero;
- niter = niter_desc.niter;
+ if (dump_enabled_p ())
+ dump_printf_loc (MSG_NOTE, vect_location, "Analyzing exit %d...\n", i);
- if (may_be_zero && integer_zerop (may_be_zero))
- may_be_zero = NULL_TREE;
+ if (exit != main_exit)
+ continue;
- if (may_be_zero)
- {
- if (COMPARISON_CLASS_P (may_be_zero))
+ may_be_zero = NULL_TREE;
+ if (!number_of_iterations_exit_assumptions (loop, exit, &niter_desc, NULL)
+ || chrec_contains_undetermined (niter_desc.niter))
+ continue;
+
+ niter_assumptions = niter_desc.assumptions;
+ may_be_zero = niter_desc.may_be_zero;
+ niter = niter_desc.niter;
+
+ if (may_be_zero && integer_zerop (may_be_zero))
+ may_be_zero = NULL_TREE;
+
+ if (may_be_zero)
{
- /* Try to combine may_be_zero with assumptions, this can simplify
- computation of niter expression. */
- if (niter_assumptions && !integer_nonzerop (niter_assumptions))
- niter_assumptions = fold_build2 (TRUTH_AND_EXPR, boolean_type_node,
- niter_assumptions,
- fold_build1 (TRUTH_NOT_EXPR,
- boolean_type_node,
- may_be_zero));
+ if (COMPARISON_CLASS_P (may_be_zero))
+ {
+ /* Try to combine may_be_zero with assumptions, this can simplify
+ computation of niter expression. */
+ if (niter_assumptions && !integer_nonzerop (niter_assumptions))
+ niter_assumptions = fold_build2 (TRUTH_AND_EXPR, boolean_type_node,
+ niter_assumptions,
+ fold_build1 (TRUTH_NOT_EXPR,
+ boolean_type_node,
+ may_be_zero));
+ else
+ niter = fold_build3 (COND_EXPR, TREE_TYPE (niter), may_be_zero,
+ build_int_cst (TREE_TYPE (niter), 0),
+ rewrite_to_non_trapping_overflow (niter));
+
+ may_be_zero = NULL_TREE;
+ }
+ else if (integer_nonzerop (may_be_zero))
+ {
+ *number_of_iterationsm1 = build_int_cst (TREE_TYPE (niter), 0);
+ *number_of_iterations = build_int_cst (TREE_TYPE (niter), 1);
+ continue;
+ }
else
- niter = fold_build3 (COND_EXPR, TREE_TYPE (niter), may_be_zero,
- build_int_cst (TREE_TYPE (niter), 0),
- rewrite_to_non_trapping_overflow (niter));
+ continue;
+ }
- may_be_zero = NULL_TREE;
- }
- else if (integer_nonzerop (may_be_zero))
- {
- *number_of_iterationsm1 = build_int_cst (TREE_TYPE (niter), 0);
- *number_of_iterations = build_int_cst (TREE_TYPE (niter), 1);
- return cond;
- }
- else
- return cond;
+ /* Loop assumptions are based off the normal exit. */
+ *assumptions = niter_assumptions;
+ *number_of_iterationsm1 = niter;
+
+ /* We want the number of loop header executions which is the number
+ of latch executions plus one.
+ ??? For UINT_MAX latch executions this number overflows to zero
+ for loops like do { n++; } while (n != 0); */
+ if (niter && !chrec_contains_undetermined (niter))
+ niter = fold_build2 (PLUS_EXPR, TREE_TYPE (niter),
+ unshare_expr (niter),
+ build_int_cst (TREE_TYPE (niter), 1));
+ *number_of_iterations = niter;
}
- *assumptions = niter_assumptions;
- *number_of_iterationsm1 = niter;
+ if (dump_enabled_p ())
+ dump_printf_loc (MSG_NOTE, vect_location, "All loop exits successfully analyzed.\n");
- /* We want the number of loop header executions which is the number
- of latch executions plus one.
- ??? For UINT_MAX latch executions this number overflows to zero
- for loops like do { n++; } while (n != 0); */
- if (niter && !chrec_contains_undetermined (niter))
- niter = fold_build2 (PLUS_EXPR, TREE_TYPE (niter), unshare_expr (niter),
- build_int_cst (TREE_TYPE (niter), 1));
- *number_of_iterations = niter;
+ return conds;
+}
- return cond;
+/* Determine the main loop exit for the vectorizer. */
+
+edge
+vec_init_loop_exit_info (class loop *loop)
+{
+ /* Before we begin we must first determine which exit is the main one and
+ which are auxilary exits. */
+ auto_vec<edge> exits = get_loop_exit_edges (loop);
+ if (exits.length () == 1)
+ return exits[0];
+
+ /* If we have multiple exits we only support counting IV at the moment. Analyze
+ all exits and return one */
+ class tree_niter_desc niter_desc;
+ edge candidate = NULL;
+ for (edge exit : exits)
+ {
+ if (!get_loop_exit_condition (exit))
+ continue;
+
+ if (number_of_iterations_exit_assumptions (loop, exit, &niter_desc, NULL)
+ && !chrec_contains_undetermined (niter_desc.niter))
+ {
+ if (!niter_desc.may_be_zero || !candidate)
+ candidate = exit;
+ }
+ }
+
+ return candidate;
}
/* Function bb_in_loop_p
@@ -987,7 +1044,10 @@ _loop_vec_info::_loop_vec_info (class loop *loop_in, vec_info_shared *shared)
has_mask_store (false),
scalar_loop_scaling (profile_probability::uninitialized ()),
scalar_loop (NULL),
- orig_loop_info (NULL)
+ orig_loop_info (NULL),
+ vec_loop_iv_exit (NULL),
+ vec_epilogue_loop_iv_exit (NULL),
+ scalar_loop_iv_exit (NULL)
{
/* CHECKME: We want to visit all BBs before their successors (except for
latch blocks, for which this assertion wouldn't hold). In the simple
@@ -1646,6 +1706,18 @@ vect_analyze_loop_form (class loop *loop, vect_loop_form_info *info)
{
DUMP_VECT_SCOPE ("vect_analyze_loop_form");
+ edge exit_e = vec_init_loop_exit_info (loop);
+ if (!exit_e)
+ return opt_result::failure_at (vect_location,
+ "not vectorized:"
+ " could not determine main exit from"
+ " loop with multiple exits.\n");
+ info->loop_exit = exit_e;
+ if (dump_enabled_p ())
+ dump_printf_loc (MSG_NOTE, vect_location,
+ "using as main loop exit: %d -> %d [AUX: %p]\n",
+ exit_e->src->index, exit_e->dest->index, exit_e->aux);
+
/* Different restrictions apply when we are considering an inner-most loop,
vs. an outer (nested) loop.
(FORNOW. May want to relax some of these restrictions in the future). */
@@ -1739,7 +1811,7 @@ vect_analyze_loop_form (class loop *loop, vect_loop_form_info *info)
if (dump_enabled_p ())
dump_printf_loc (MSG_NOTE, vect_location,
"Considering outer-loop vectorization.\n");
- info->inner_loop_cond = inner.loop_cond;
+ info->inner_loop_cond = inner.conds[0];
}
if (!single_exit (loop))
@@ -1760,31 +1832,39 @@ 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)
+ if (exit_e->flags & EDGE_ABNORMAL)
return opt_result::failure_at (vect_location,
"not vectorized:"
" abnormal loop exit edge.\n");
- info->loop_cond
- = vect_get_loop_niters (loop, &info->assumptions,
+ info->conds
+ = vect_get_loop_niters (loop, exit_e, &info->assumptions,
&info->number_of_iterations,
&info->number_of_iterationsm1);
- if (!info->loop_cond)
+
+ if (info->conds.is_empty ())
return opt_result::failure_at
(vect_location,
"not vectorized: complicated exit condition.\n");
+ /* Determine what the primary and alternate exit conds are. */
+ for (unsigned i = 0; i < info->conds.length (); i++)
+ {
+ gcond *cond = info->conds[i];
+ if (exit_e->src == gimple_bb (cond))
+ std::swap (info->conds[0], info->conds[i]);
+ }
+
if (integer_zerop (info->assumptions)
|| !info->number_of_iterations
|| chrec_contains_undetermined (info->number_of_iterations))
return opt_result::failure_at
- (info->loop_cond,
+ (info->conds[0],
"not vectorized: number of iterations cannot be computed.\n");
if (integer_zerop (info->number_of_iterations))
return opt_result::failure_at
- (info->loop_cond,
+ (info->conds[0],
"not vectorized: number of iterations = 0.\n");
if (!(tree_fits_shwi_p (info->number_of_iterations)
@@ -1819,8 +1899,18 @@ vect_create_loop_vinfo (class loop *loop, vec_info_shared *shared,
if (!integer_onep (info->assumptions) && !main_loop_info)
LOOP_VINFO_NITERS_ASSUMPTIONS (loop_vinfo) = info->assumptions;
- stmt_vec_info loop_cond_info = loop_vinfo->lookup_stmt (info->loop_cond);
- STMT_VINFO_TYPE (loop_cond_info) = loop_exit_ctrl_vec_info_type;
+ for (gcond *cond : info->conds)
+ {
+ stmt_vec_info loop_cond_info = loop_vinfo->lookup_stmt (cond);
+ STMT_VINFO_TYPE (loop_cond_info) = loop_exit_ctrl_vec_info_type;
+ }
+
+ for (unsigned i = 1; i < info->conds.length (); i ++)
+ LOOP_VINFO_LOOP_CONDS (loop_vinfo).safe_push (info->conds[i]);
+ LOOP_VINFO_LOOP_IV_COND (loop_vinfo) = info->conds[0];
+
+ LOOP_VINFO_IV_EXIT (loop_vinfo) = info->loop_exit;
+
if (info->inner_loop_cond)
{
stmt_vec_info inner_loop_cond_info
@@ -3063,9 +3153,9 @@ start_over:
if (dump_enabled_p ())
dump_printf_loc (MSG_NOTE, vect_location, "epilog loop required\n");
if (!vect_can_advance_ivs_p (loop_vinfo)
- || !slpeel_can_duplicate_loop_p (LOOP_VINFO_LOOP (loop_vinfo),
- single_exit (LOOP_VINFO_LOOP
- (loop_vinfo))))
+ || !slpeel_can_duplicate_loop_p (loop,
+ LOOP_VINFO_IV_EXIT (loop_vinfo),
+ LOOP_VINFO_IV_EXIT (loop_vinfo)))
{
ok = opt_result::failure_at (vect_location,
"not vectorized: can't create required "
@@ -3986,24 +4076,15 @@ pop:
??? We could relax this and handle arbitrary live stmts by
forcing a scalar epilogue for example. */
imm_use_iterator imm_iter;
+ use_operand_p use_p;
gimple *op_use_stmt;
unsigned cnt = 0;
FOR_EACH_IMM_USE_STMT (op_use_stmt, imm_iter, op.ops[opi])
if (!is_gimple_debug (op_use_stmt)
&& (*code != ERROR_MARK
|| flow_bb_inside_loop_p (loop, gimple_bb (op_use_stmt))))
- {
- /* We want to allow x + x but not x < 1 ? x : 2. */
- if (is_gimple_assign (op_use_stmt)
- && gimple_assign_rhs_code (op_use_stmt) == COND_EXPR)
- {
- use_operand_p use_p;
- FOR_EACH_IMM_USE_ON_STMT (use_p, imm_iter)
- cnt++;
- }
- else
- cnt++;
- }
+ FOR_EACH_IMM_USE_ON_STMT (use_p, imm_iter)
+ cnt++;
if (cnt != 1)
{
fail = true;
@@ -5780,7 +5861,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;
@@ -6002,7 +6083,7 @@ vect_create_epilog_for_reduction (loop_vec_info loop_vinfo,
Store them in NEW_PHIS. */
if (double_reduc)
loop = outer_loop;
- exit_bb = single_exit (loop)->dest;
+ exit_bb = LOOP_VINFO_IV_EXIT (loop_vinfo)->dest;
exit_gsi = gsi_after_labels (exit_bb);
reduc_inputs.create (slp_node ? vec_num : ncopies);
for (unsigned i = 0; i < vec_num; i++)
@@ -6018,7 +6099,7 @@ vect_create_epilog_for_reduction (loop_vec_info loop_vinfo,
phi = create_phi_node (new_def, exit_bb);
if (j)
def = gimple_get_lhs (STMT_VINFO_VEC_STMTS (rdef_info)[j]);
- SET_PHI_ARG_DEF (phi, single_exit (loop)->dest_idx, def);
+ SET_PHI_ARG_DEF (phi, LOOP_VINFO_IV_EXIT (loop_vinfo)->dest_idx, def);
new_def = gimple_convert (&stmts, vectype, new_def);
reduc_inputs.quick_push (new_def);
}
@@ -10416,12 +10497,12 @@ vectorizable_live_operation (vec_info *vinfo, stmt_vec_info stmt_info,
lhs' = new_tree; */
class loop *loop = LOOP_VINFO_LOOP (loop_vinfo);
- basic_block exit_bb = single_exit (loop)->dest;
+ basic_block exit_bb = LOOP_VINFO_IV_EXIT (loop_vinfo)->dest;
gcc_assert (single_pred_p (exit_bb));
tree vec_lhs_phi = copy_ssa_name (vec_lhs);
gimple *phi = create_phi_node (vec_lhs_phi, exit_bb);
- SET_PHI_ARG_DEF (phi, single_exit (loop)->dest_idx, vec_lhs);
+ SET_PHI_ARG_DEF (phi, LOOP_VINFO_IV_EXIT (loop_vinfo)->dest_idx, vec_lhs);
gimple_seq stmts = NULL;
tree new_tree;
@@ -10965,7 +11046,7 @@ vect_get_loop_len (loop_vec_info loop_vinfo, gimple_stmt_iterator *gsi,
profile. */
static void
-scale_profile_for_vect_loop (class loop *loop, unsigned vf, bool flat)
+scale_profile_for_vect_loop (class loop *loop, edge exit_e, unsigned vf, bool flat)
{
/* For flat profiles do not scale down proportionally by VF and only
cap by known iteration count bounds. */
@@ -10980,7 +11061,6 @@ scale_profile_for_vect_loop (class loop *loop, unsigned vf, bool flat)
return;
}
/* Loop body executes VF fewer times and exit increases VF times. */
- edge exit_e = single_exit (loop);
profile_count entry_count = loop_preheader_edge (loop)->count ();
/* If we have unreliable loop profile avoid dropping entry
@@ -11350,7 +11430,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 = single_exit (loop);
+ edge e = LOOP_VINFO_IV_EXIT (loop_vinfo);
if (! single_pred_p (e->dest))
{
split_loop_exit_edge (e, true);
@@ -11376,7 +11456,7 @@ vect_transform_loop (loop_vec_info loop_vinfo, gimple *loop_vectorized_call)
loop closed PHI nodes on the exit. */
if (LOOP_VINFO_SCALAR_LOOP (loop_vinfo))
{
- e = single_exit (LOOP_VINFO_SCALAR_LOOP (loop_vinfo));
+ e = LOOP_VINFO_SCALAR_IV_EXIT (loop_vinfo);
if (! single_pred_p (e->dest))
{
split_loop_exit_edge (e, true);
@@ -11625,8 +11705,9 @@ vect_transform_loop (loop_vec_info loop_vinfo, gimple *loop_vectorized_call)
a zero NITERS becomes a nonzero NITERS_VECTOR. */
if (integer_onep (step_vector))
niters_no_overflow = true;
- vect_set_loop_condition (loop, loop_vinfo, niters_vector, step_vector,
- niters_vector_mult_vf, !niters_no_overflow);
+ vect_set_loop_condition (loop, LOOP_VINFO_IV_EXIT (loop_vinfo), loop_vinfo,
+ niters_vector, step_vector, niters_vector_mult_vf,
+ !niters_no_overflow);
unsigned int assumed_vf = vect_vf_for_cost (loop_vinfo);
@@ -11681,7 +11762,7 @@ vect_transform_loop (loop_vec_info loop_vinfo, gimple *loop_vectorized_call)
LOOP_VINFO_VECT_FACTOR (loop_vinfo),
&bound))
loop->nb_iterations_upper_bound
- = wi::umin ((widest_int) (bound - 1),
+ = wi::umin ((bound_wide_int) (bound - 1),
loop->nb_iterations_upper_bound);
}
}
@@ -11699,7 +11780,8 @@ vect_transform_loop (loop_vec_info loop_vinfo, gimple *loop_vectorized_call)
assumed_vf) - 1
: wi::udiv_floor (loop->nb_iterations_estimate + bias_for_assumed,
assumed_vf) - 1);
- scale_profile_for_vect_loop (loop, assumed_vf, flat);
+ scale_profile_for_vect_loop (loop, LOOP_VINFO_IV_EXIT (loop_vinfo),
+ assumed_vf, flat);
if (dump_enabled_p ())
{