This is part of enablement of support for partial vectors in basic
block SLP vectorization.
Adapt the existing vect_verify_loop_lens function used by the loop
vectorizer to verify that partial vector length requirements recorded
during the analysis phase can be satisfied. Unlike masked partial
vectors, the elements-to-subelements scaling factor is not always one,
so apply it when determining the maximum precision needed to store
lengths used to implement SLP group sizes.
A different scheme is used for recording lengths for BB SLP
vectorization but it reuses as much loop vectorizer code as possible:
instead of using the passed-in number of vectors as an index to find
the relevant rgroup controls in vect_record_len and vect_get_len,
rgroup controls are instead indexed by the number of lanes in an SLP
tree node. Since there is no scheme for mapping nvectors to
rgroup_controls, nvectors must always be one. Consequently, the number
of controls per rgroup_controls instance is also one for BB SLP. (One
length for each unique number of lanes.)
The max_nscalars_per_iter of an rgroup is already assumed by
vect_set_bb_slp_controls_directly to hold the number of lanes in a
mask to be generated. Carry that assumption over to LEN-style partial
vectors: in vect_record_len, record the largest SLP group size for a
given rgroup as max_nscalars_per_iter. Because of how rgroups are
indexed, the value of max_nscalars_per_iter will either be zero (on
initialization) or one more than the rgroup index.
As part of analyzing whether an SLP region can be vectorized, after
analyzing all statements in the region, if lengths have been recorded
then call vect_verify_loop_lens to check whether we can generate
the necessary length controls. If not, return false from
vect_slp_analyze_bb_1.
Costing in vect_bb_vectorization_profitable_p is extended to
account for partial vector setup costs for an SLP vectorization
region that uses LEN style controls. This is similar to WHILE_ULT
and AVX512 costing except that the cost kind is scalar_stmt instead
of vector_stmt (as in the equivalent loop vectorization code).
As in the case of masks, this is imprecise because lengths can be
shared between SLP nodes and some subgraphs might not be scheduled.
After scheduling any profitable subgraphs and generating any
invariant statements, unless AVX512-style partial vectors are in
use, vect_set_bb_slp_controls_partial_vectors is called. This
function is readily adapted to iterate over the recorded lens
instead of the recorded masks. vect_set_bb_slp_controls_directly
now scales the SLP group size by the rgroup's factor (which will
be 1 for WHILE_ULT-style partial vectors). If using LEN-style
partial vectors, it assigns that scaled length to the SSA name
created by vect_get_len for the control.
Dead stmts that set up lengths for unprofitable subgraphs are
expected to be eliminated by later passes.
gcc/ChangeLog:
* tree-vect-loop.cc (vect_min_prec_for_max_group_size):
Permit the elements-to-subelements scaling factor to be other
than 1 and use it to scale group_size.
(vect_verify_loop_lens): Change parameter type from loop_vec_info
to vec_info *. Use dyn_cast to get a loop_vec_info or null from
the argument.
Access lengths directly instead of via LOOP_VINFO_LENS. Avoid
calling vect_min_prec_for_max_niters for BB SLP because the
concept of iterations is not applicable. Instead, call
vect_min_prec_for_max_group_size to get an alternative value of
min_ni_prec. Set rgroup_compare_type, rgroup_iv_type and
partial_vector_style directly instead of via LOOP_VINFO_
macros.
(vect_record_len): Use dyn_cast to get
a loop_vec_info or null from the vec_info argument.
If used for BB SLP, get the group size from the SLP tree
node and use that as the index of the rgroup control to be
populated (instead of the nvectors argument). Assert that
nvectors is 1, if used for BB SLP. When doing BB SLP, use the
SLP group size instead of the number of scalar values operated
on by each scalar iteration as max_nscalars_per_iter.
(vect_get_len): Don't call make_temp_ssa_name with "loop_len"
as the name, if used for BB SLP. Assert that nvectors is
1 and only the first vector is queried, if used for BB SLP.
If used for BB SLP, get the group size from the SLP tree node
and use that (instead of the nvectors argument) as the index
of the rgroup control. Assert that no SSA name is created for
"adjusted_loop_len" if used for BB SLP.
If the passed-in elements-to-subelements factor differs from
that recorded for the SLP group size then calculate the
overall scaling factor required to build EXACT_DIV_EXPR or
MULT_EXPR by using the constant lower bound of the number of
subparts in the passed-in vector type, to match the
calculation of max_nscalars_per_iter for the rgroup.
* tree-vect-slp.cc (vect_bb_vectorization_profitable_p):
Permit the set of recorded lengths to be non-empty.
Add code to count setup costs for LEN-style partial vectors.
(vect_slp_analyze_bb_1): Return false if both mask and length
requirements for partial vectors were recorded for a BB SLP
vectorization region. Permit the set of recorded lengths to be
non-empty. If lengths have been recorded for an SLP region then
call vect_verify_loop_lens to check whether we can generate the
necessary controls. If not, return false.
(vect_set_bb_slp_controls_directly): If the partial vector
style is not WHILE_ULT then require it to be LEN and scale
max_nscalars_per_iter by subelements-per-element before using
it as the righthand side of the assignment to the SSA name made
by vect_get_len.
(vect_set_bb_slp_controls_partial_vectors): Permit the set of
recorded lengths to be non-empty if the partial vector style is
LEN. If the set of recorded masks is empty then iterate over
the set of recorded lengths instead.
* tree-vectorizer.h (rgroup_controls): Update the description
of max_nscalars_per_iter.
(vect_verify_loop_lens): Add function declaration for use by
vect_slp_analyze_bb_1. This function no longer has static
linkage.
---
gcc/tree-vect-loop.cc | 138 ++++++++++++++++++++++++++++--------------
gcc/tree-vect-slp.cc | 55 ++++++++++++++---
gcc/tree-vectorizer.h | 8 ++-
3 files changed, 142 insertions(+), 59 deletions(-)
diff --git a/gcc/tree-vect-loop.cc b/gcc/tree-vect-loop.cc
index 4709a749181..b5443bef376 100644
--- a/gcc/tree-vect-loop.cc
+++ b/gcc/tree-vect-loop.cc
@@ -1002,8 +1002,7 @@ vect_min_prec_for_max_group_size (vec<rgroup_controls>
*controls)
{
if (!rgc->type)
continue;
- gcc_assert (rgc->factor == 1);
- unsigned int group_size = i + 1;
+ unsigned int group_size = (i + 1) * rgc->factor;
max_group_size = MAX (max_group_size, group_size);
}
@@ -1339,20 +1338,22 @@ vect_verify_full_masking_avx512 (vec_info *vinfo)
precision of the target supported length is larger than the precision
required by loop niters. */
-static bool
-vect_verify_loop_lens (loop_vec_info loop_vinfo)
+bool
+vect_verify_loop_lens (vec_info *vinfo)
{
- if (LOOP_VINFO_LENS (loop_vinfo).is_empty ())
+ loop_vec_info loop_vinfo = dyn_cast<loop_vec_info> (vinfo);
+
+ if (vinfo->lens.is_empty ())
return false;
- if (!VECTOR_MODE_P (loop_vinfo->vector_mode))
+ if (!VECTOR_MODE_P (vinfo->vector_mode))
return false;
machine_mode len_load_mode, len_store_mode;
- if (!get_len_load_store_mode (loop_vinfo->vector_mode, true)
+ if (!get_len_load_store_mode (vinfo->vector_mode, true)
.exists (&len_load_mode))
return false;
- if (!get_len_load_store_mode (loop_vinfo->vector_mode, false)
+ if (!get_len_load_store_mode (vinfo->vector_mode, false)
.exists (&len_store_mode))
return false;
@@ -1370,39 +1371,47 @@ vect_verify_loop_lens (loop_vec_info loop_vinfo)
/* If the backend requires a bias of -1 for LEN_LOAD, we must not emit
len_loads with a length of zero. In order to avoid that we prohibit
more than one loop length here. */
- if (partial_load_bias == -1
- && LOOP_VINFO_LENS (loop_vinfo).length () > 1)
+ if (partial_load_bias == -1 && vinfo->lens.length () > 1)
return false;
- LOOP_VINFO_PARTIAL_LOAD_STORE_BIAS (loop_vinfo) = partial_load_bias;
+ vinfo->partial_load_store_bias = partial_load_bias;
- unsigned int max_nitems_per_iter = 1;
+ unsigned int min_ni_prec;
unsigned int i;
rgroup_controls *rgl;
- /* Find the maximum number of items per iteration for every rgroup. */
- FOR_EACH_VEC_ELT (LOOP_VINFO_LENS (loop_vinfo), i, rgl)
+
+ if (loop_vinfo)
{
- unsigned nitems_per_iter = rgl->max_nscalars_per_iter * rgl->factor;
- max_nitems_per_iter = MAX (max_nitems_per_iter, nitems_per_iter);
- }
+ unsigned int max_nitems_per_iter = 1;
- /* Work out how many bits we need to represent the length limit. */
- unsigned int min_ni_prec
- = vect_min_prec_for_max_niters (loop_vinfo, max_nitems_per_iter);
+ /* Find the maximum number of items per iteration for every rgroup. */
+ FOR_EACH_VEC_ELT (LOOP_VINFO_LENS (loop_vinfo), i, rgl)
+ {
+ unsigned nitems_per_iter = rgl->max_nscalars_per_iter * rgl->factor;
+ max_nitems_per_iter = MAX (max_nitems_per_iter, nitems_per_iter);
+ }
- /* Now use the maximum of below precisions for one suitable IV type:
- - the IV's natural precision
- - the precision needed to hold: the maximum number of scalar
- iterations multiplied by the scale factor (min_ni_prec above)
- - the Pmode precision
+ /* Work out how many bits we need to represent the length limit. */
+ min_ni_prec
+ = vect_min_prec_for_max_niters (loop_vinfo, max_nitems_per_iter);
- If min_ni_prec is less than the precision of the current niters,
- we prefer to still use the niters type. Prefer to use Pmode and
- wider IV to avoid narrow conversions. */
+ /* Now use the maximum of below precisions for one suitable IV type:
+ - the IV's natural precision
+ - the precision needed to hold: the maximum number of scalar
+ iterations multiplied by the scale factor (min_ni_prec above)
+ - the Pmode precision
+
+ If min_ni_prec is less than the precision of the current niters,
+ we prefer to still use the niters type. Prefer to use Pmode and
+ wider IV to avoid narrow conversions. */
+
+ unsigned int ni_prec
+ = TYPE_PRECISION (TREE_TYPE (LOOP_VINFO_NITERS (loop_vinfo)));
+ min_ni_prec = MAX (min_ni_prec, ni_prec);
+ }
+ else
+ min_ni_prec = vect_min_prec_for_max_group_size (&vinfo->lens);
- unsigned int ni_prec
- = TYPE_PRECISION (TREE_TYPE (LOOP_VINFO_NITERS (loop_vinfo)));
- min_ni_prec = MAX (min_ni_prec, ni_prec);
min_ni_prec = MAX (min_ni_prec, GET_MODE_BITSIZE (Pmode));
tree iv_type = NULL_TREE;
@@ -1434,9 +1443,9 @@ vect_verify_loop_lens (loop_vec_info loop_vinfo)
return false;
}
- LOOP_VINFO_RGROUP_COMPARE_TYPE (loop_vinfo) = iv_type;
- LOOP_VINFO_RGROUP_IV_TYPE (loop_vinfo) = iv_type;
- LOOP_VINFO_PARTIAL_VECTORS_STYLE (loop_vinfo) = vect_partial_vectors_len;
+ vinfo->rgroup_compare_type = iv_type;
+ vinfo->rgroup_iv_type = iv_type;
+ vinfo->partial_vector_style = vect_partial_vectors_len;
return true;
}
@@ -10874,16 +10883,31 @@ vect_record_len (vec_info *vinfo, unsigned int
nvectors, tree vectype,
gcc_assert (!is_a<bb_vec_info> (vinfo) || slp_node != nullptr);
gcc_assert (nvectors != 0);
vec_lens *lens = &vinfo->lens;
- if (lens->length () < nvectors)
- lens->safe_grow_cleared (nvectors, true);
- rgroup_controls *rgl = &(*lens)[nvectors - 1];
+ loop_vec_info loop_vinfo = dyn_cast<loop_vec_info> (vinfo);
+ unsigned int key;
+
+ if (loop_vinfo)
+ key = nvectors;
+ else
+ {
+ gcc_assert (nvectors == 1);
+ key = SLP_TREE_LANES (slp_node);
+ }
+
+ gcc_assert (key != 0);
+ if (lens->length () < key)
+ lens->safe_grow_cleared (key, true);
+ rgroup_controls *rgl = &(*lens)[key - 1];
/* The number of scalars per iteration, scalar occupied bytes and
the number of vectors are both compile-time constants. */
- unsigned int nscalars_per_iter
- = exact_div (nvectors * TYPE_VECTOR_SUBPARTS (vectype),
- vinfo->vectorization_factor)
- .to_constant ();
+ unsigned int nscalars_per_iter;
+ if (loop_vinfo)
+ nscalars_per_iter = exact_div (nvectors * TYPE_VECTOR_SUBPARTS (vectype),
+ vinfo->vectorization_factor)
+ .to_constant ();
+ else
+ nscalars_per_iter = SLP_TREE_LANES (slp_node);
if (rgl->max_nscalars_per_iter < nscalars_per_iter)
{
@@ -10912,8 +10936,21 @@ vect_get_len (vec_info *vinfo, gimple_stmt_iterator
*gsi, unsigned int nvectors,
bool adjusted, slp_tree slp_node)
{
gcc_assert (!is_a<bb_vec_info> (vinfo) || slp_node != nullptr);
+ gcc_assert (nvectors != 0);
+ gcc_assert (index < nvectors);
+
vec_lens *lens = &vinfo->lens;
- rgroup_controls *rgl = &(*lens)[nvectors - 1];
+ loop_vec_info loop_vinfo = dyn_cast<loop_vec_info> (vinfo);
+ unsigned int key;
+ if (loop_vinfo)
+ key = nvectors;
+ else
+ {
+ gcc_assert (nvectors == 1);
+ key = SLP_TREE_LANES (slp_node);
+ }
+ gcc_assert (key != 0);
+ rgroup_controls *rgl = &(*lens)[key - 1];
bool use_bias_adjusted_len =
vinfo->partial_load_store_bias != 0;
@@ -10927,7 +10964,8 @@ vect_get_len (vec_info *vinfo, gimple_stmt_iterator
*gsi, unsigned int nvectors,
tree len_type = vinfo->rgroup_compare_type;
gcc_assert (len_type != NULL_TREE);
- tree len = make_temp_ssa_name (len_type, NULL, "loop_len");
+ tree len = make_temp_ssa_name (len_type, NULL,
+ loop_vinfo ? "loop_len" : "slp_len");
/* Provide a dummy definition until the real one is available. */
SSA_NAME_DEF_STMT (len) = gimple_build_nop ();
@@ -10936,6 +10974,7 @@ vect_get_len (vec_info *vinfo, gimple_stmt_iterator
*gsi, unsigned int nvectors,
if (use_bias_adjusted_len)
{
gcc_assert (i == 0);
+ gcc_assert (loop_vinfo);
tree adjusted_len =
make_temp_ssa_name (len_type, NULL, "adjusted_loop_len");
SSA_NAME_DEF_STMT (adjusted_len) = gimple_build_nop ();
@@ -10971,9 +11010,16 @@ vect_get_len (vec_info *vinfo, gimple_stmt_iterator
*gsi, unsigned int nvectors,
{
/* The number of scalars per iteration, scalar occupied bytes and
the number of vectors are both compile-time constants. */
- unsigned int nscalars_per_iter
- = exact_div (nvectors * TYPE_VECTOR_SUBPARTS (vectype),
- vinfo->vectorization_factor).to_constant ();
+ unsigned int nscalars_per_iter;
+ if (loop_vinfo)
+ nscalars_per_iter
+ = exact_div (nvectors * TYPE_VECTOR_SUBPARTS (vectype),
+ vinfo->vectorization_factor)
+ .to_constant ();
+ else
+ nscalars_per_iter
+ = constant_lower_bound (TYPE_VECTOR_SUBPARTS (vectype));
+
unsigned int rglvecsize = rgl->factor * rgl->max_nscalars_per_iter;
unsigned int vecsize = nscalars_per_iter * factor;
if (rglvecsize > vecsize)
diff --git a/gcc/tree-vect-slp.cc b/gcc/tree-vect-slp.cc
index fcd889945bd..e919282bd43 100644
--- a/gcc/tree-vect-slp.cc
+++ b/gcc/tree-vect-slp.cc
@@ -9678,8 +9678,16 @@ vect_bb_vectorization_profitable_p (bb_vec_info bb_vinfo,
if (rgc.type)
control_count++;
}
+ else if (!bb_vinfo->lens.is_empty ())
+ {
+ gcc_assert (bb_vinfo->partial_vector_style == vect_partial_vectors_len);
- gcc_assert (bb_vinfo->lens.is_empty ());
+ for (auto rgc : bb_vinfo->lens)
+ if (rgc.type)
+ control_count++;
+
+ control_kind = scalar_stmt;
+ }
const int slp_instance_count = slp_instances.length (),
control_cost_per_inst_q = control_count / slp_instance_count;
@@ -10281,14 +10289,31 @@ vect_slp_analyze_bb_1 (bb_vec_info bb_vinfo, int
n_stmts, bool &fatal,
return false;
}
+ /* For now, we don't expect to mix both masking and length approaches for one
+ basic block. Fail if both are recorded. */
+ if (!bb_vinfo->masks.is_empty () && !bb_vinfo->lens.is_empty ())
+ {
+ if (dump_enabled_p ())
+ dump_printf_loc (
+ MSG_MISSED_OPTIMIZATION, vect_location,
+ "not vectorized: can't vectorize a basic block with partial vectors "
+ "because we don't expect to mix different approaches with partial "
+ "vectors for the same basic block.\n");
+ return false;
+ }
+
/* Check whether we can generate the necessary controls. */
- gcc_assert (bb_vinfo->lens.is_empty ());
if (!bb_vinfo->masks.is_empty ())
{
if (!vect_verify_full_masking (bb_vinfo)
&& !vect_verify_full_masking_avx512 (bb_vinfo))
return false;
}
+ else if (!bb_vinfo->lens.is_empty ())
+ {
+ if (!vect_verify_loop_lens (bb_vinfo))
+ return false;
+ }
/* Mark all the statements that we vectorize. */
vect_bb_slp_mark_stmts_vectorized (bb_vinfo);
@@ -10311,8 +10336,7 @@ vect_set_bb_slp_controls_directly (bb_vec_info
bb_vinfo, gimple_seq *seq,
tree compare_type = bb_vinfo->rgroup_compare_type;
tree ctrl_type = rgc->type;
- gcc_assert (rgc->factor == 1);
- unsigned int nitems = rgc->max_nscalars_per_iter;
+ unsigned int nitems = rgc->max_nscalars_per_iter * rgc->factor;
tree zero_index = build_int_cst (compare_type, 0);
tree nitems_cst = build_int_cst (compare_type, nitems);
@@ -10320,9 +10344,16 @@ vect_set_bb_slp_controls_directly (bb_vec_info
bb_vinfo, gimple_seq *seq,
tree ctrl = rgc->controls[0];
tree init_ctrl;
- gcc_assert (bb_vinfo->partial_vector_style ==
vect_partial_vectors_while_ult);
- init_ctrl
- = vect_gen_while (seq, ctrl_type, zero_index, nitems_cst, "max_mask");
+ if (bb_vinfo->partial_vector_style == vect_partial_vectors_while_ult)
+ {
+ init_ctrl
+ = vect_gen_while (seq, ctrl_type, zero_index, nitems_cst, "max_mask");
+ }
+ else
+ {
+ gcc_assert (bb_vinfo->partial_vector_style == vect_partial_vectors_len);
+ init_ctrl = nitems_cst;
+ }
gimple *stmt = gimple_build_assign (ctrl, init_ctrl);
gimple_seq_add_stmt (seq, stmt);
@@ -10334,15 +10365,19 @@ vect_set_bb_slp_controls_directly (bb_vec_info
bb_vinfo, gimple_seq *seq,
static void
vect_set_bb_slp_controls_partial_vectors (bb_vec_info bb_vinfo)
{
- gcc_assert (bb_vinfo->partial_vector_style ==
vect_partial_vectors_while_ult);
- gcc_assert (!bb_vinfo->masks.is_empty ());
+ gcc_assert ((bb_vinfo->partial_vector_style == vect_partial_vectors_while_ult
+ && !bb_vinfo->masks.is_empty ())
+ || (bb_vinfo->partial_vector_style == vect_partial_vectors_len
+ && !bb_vinfo->lens.is_empty ()));
gimple_seq seq = NULL;
+ bool use_masks_p = !bb_vinfo->masks.is_empty ();
/* Iterate over all the rgroups and fill in their controls. */
rgroup_controls *rgc;
unsigned int i;
- auto_vec<rgroup_controls> *controls = &bb_vinfo->masks.rgc_vec;
+ auto_vec<rgroup_controls> *controls
+ = use_masks_p ? &bb_vinfo->masks.rgc_vec : &bb_vinfo->lens;
FOR_EACH_VEC_ELT (*controls, i, rgc)
if (!rgc->controls.is_empty ())
{
diff --git a/gcc/tree-vectorizer.h b/gcc/tree-vectorizer.h
index 5ba9331cfcb..634c02e675f 100644
--- a/gcc/tree-vectorizer.h
+++ b/gcc/tree-vectorizer.h
@@ -706,9 +706,10 @@ struct rgroup_controls {
/* The largest nS for all rgroups that use these controls.
For vect_partial_vectors_avx512 this is the constant nscalars_per_iter
for all members of the group, or 1 for BB SLP vectorization.
- For BB SLP vectorization with vect_partial_vectors_while_ult, this is the
- highest minimum number of subparts of all the vector types that use this
- rgroup (e.g., 16 from VNx4HI, VNx4SI, VNx4HF and VNx16QI). */
+ For BB SLP vectorization with vect_partial_vectors_while_ult or
+ vect_partial_vectors_len, this is the highest minimum number of subparts
of
+ all the vector types that use this rgroup (e.g., 16 from VNx4HI, VNx4SI,
+ VNx4HF and VNx16QI). */
unsigned int max_nscalars_per_iter;
/* For the largest nS recorded above, the loop controls divide each scalar
@@ -2779,6 +2780,7 @@ extern int vect_get_known_peeling_cost (loop_vec_info,
int);
extern tree cse_and_gimplify_to_preheader (loop_vec_info, tree);
extern bool vect_verify_full_masking (vec_info *);
extern bool vect_verify_full_masking_avx512 (vec_info *);
+extern bool vect_verify_loop_lens (vec_info *);
/* Nonlinear induction. */
extern tree vect_peel_nonlinear_iv_init (gimple_seq*, tree, tree,
--
2.43.0