The following decides on the VF based on assigned SLP_TREE_VECTYPE
rather than on the tracked max_nunits in the SLP graph which also
factors in external and constant nodes. Those get their vector
type assigned in vectorizable_* via vect_maybe_update_slp_op_vectype
and there's freedom to vectorizable_* to pick a suitable one.
Specifically conversions of externs/constants can cause a higher
than necessary VF which would be a missed optimization.
Changing the VF as outlined will run into both vect_prologue_cost_for_slp
and vect_create_constant_vectors ICEing when computing
vect_get_num_copies since that asserts it can exact_div the
number of lanes as in the unrolled loop by the number of lanes in the
(out of loop) vector type. But vectorizable_* can opt to just
use the lowpart of such vectors but require it in full due to target
constraints. So this RFC patch introduces vect_get_num_copies_for_invariant
which performs a ceil_div instead (I think we can maybe change the global
copy to do that). I added verification to vect_slp_analyze_node_operations
which should make the vect_maybe_update_slp_op_vectype change in
the predicated tails series unneeded.
This shifts the ICE to vectorizable_conversion which is mightly confused
by invariant vectors with too many lanes (in O3-vect-pr32243.c it is
unswitching that exposes a loop invariant conversion in the loop).
I have installed a narrow fix that is likely incomplete and I expect
similar issues to appear in multi-operand widening/narrowing operations.
The fundamental issue is that we expose constant/externals to
vectorizable_* that might have an excess number of lanes, and some
vectorizable_* might not be prepared (and most should be unaffected).
As vectorizable_* control the vector types themselves the fix lies
within them. Once fuzzers increase coverage.
Bootstrapped and tested on x86_64-unknown-linux-gnu and
on aarch64-linux-gnu. I've also built SPEC CPU 2017 on x86_64
without hitting additional issues.
OK? As said, this should elide two patches from the predicated
tails series which is the motivation to come back to this now.
Thanks,
Richard.
* tree-vect-slp.cc (vect_update_slp_vf_for_node): Compute
VF based on SLP_TREE_VECTYPE only.
(vect_get_num_copies_for_invariant): New.
(vect_prologue_cost_for_slp): Take nvectors as argument.
(vect_slp_analyze_node_operations): Verify that we can
div_away_from_zero for vect_prologue_cost_for_slp.
(vect_create_constant_vectors): Use vect_get_num_copies_for_invariant.
* tree-vect-stmts.cc (vect_create_vectorized_promotion_stmts):
Support creating half of the promoted results.
(vectorizable_conversion): When we have excess input elements
request half of the promoted results.
---
gcc/tree-vect-slp.cc | 52 +++++++++++++++++++++++++++++++++---------
gcc/tree-vect-stmts.cc | 43 +++++++++++++++++++---------------
2 files changed, 66 insertions(+), 29 deletions(-)
diff --git a/gcc/tree-vect-slp.cc b/gcc/tree-vect-slp.cc
index ed0ebad744e..505fe1dbed4 100644
--- a/gcc/tree-vect-slp.cc
+++ b/gcc/tree-vect-slp.cc
@@ -8678,16 +8678,19 @@ vect_update_slp_vf_for_node (slp_tree node, poly_uint64
&vf,
vect_update_slp_vf_for_node (child, vf, visited);
/* We do not visit SLP nodes for constants or externals - those neither
- have a vector type set yet (vectorizable_* does this) nor do they
- have max_nunits set. Instead we rely on internal nodes max_nunit
- to cover constant/external operands.
+ have a vector type set yet (vectorizable_* does this).
Note that when we stop using fixed size vectors externs and constants
shouldn't influence the (minimum) vectorization factor, instead
vectorizable_* should honor the vectorization factor when trying to
assign vector types to constants and externals and cause iteration
to a higher vectorization factor when required. */
+ tree vectype = SLP_TREE_VECTYPE (node);
+ if (!vectype)
+ /* OMP SIMD calls w/o LHS have no SLP_TREE_VECTYPE set. */
+ return;
poly_uint64 node_vf
- = calculate_unrolling_factor (node->max_nunits, SLP_TREE_LANES (node));
+ = calculate_unrolling_factor (TYPE_VECTOR_SUBPARTS (vectype),
+ SLP_TREE_LANES (node));
vf = force_common_multiple (vf, node_vf);
/* For permute nodes that are fed from externs or constants we have to
@@ -8697,7 +8700,7 @@ vect_update_slp_vf_for_node (slp_tree node, poly_uint64
&vf,
if (SLP_TREE_DEF_TYPE (child) != vect_internal_def)
{
poly_uint64 child_vf
- = calculate_unrolling_factor (node->max_nunits,
+ = calculate_unrolling_factor (TYPE_VECTOR_SUBPARTS (vectype),
SLP_TREE_LANES (child));
vf = force_common_multiple (vf, child_vf);
}
@@ -8994,11 +8997,33 @@ vect_scalar_ops_slice_hash::equal (const value_type &s1,
return true;
}
+/* Like vect_get_num_copies but N copies of the vector might have
+ excess elements in the last vector. Returns false if *NVECTORS
+ cannot be computed. */
+
+static bool
+vect_get_num_copies_for_invariant (vec_info *vinfo, slp_tree node,
+ unsigned *nvectors)
+{
+ poly_uint64 vf;
+
+ if (loop_vec_info loop_vinfo = dyn_cast <loop_vec_info> (vinfo))
+ vf = LOOP_VINFO_VECT_FACTOR (loop_vinfo);
+ else
+ vf = 1;
+ vf *= SLP_TREE_LANES (node);
+
+ tree vectype = SLP_TREE_VECTYPE (node);
+ bool res = can_div_away_from_zero_p (vf, TYPE_VECTOR_SUBPARTS (vectype),
+ nvectors);
+ return res;
+}
+
/* Compute the prologue cost for invariant or constant operands represented
by NODE. */
static void
-vect_prologue_cost_for_slp (vec_info *vinfo, slp_tree node,
+vect_prologue_cost_for_slp (slp_tree node, unsigned nvectors,
stmt_vector_for_cost *cost_vec)
{
/* There's a special case of an existing vector, that costs nothing. */
@@ -9012,7 +9037,6 @@ vect_prologue_cost_for_slp (vec_info *vinfo, slp_tree
node,
unsigned group_size = SLP_TREE_LANES (node);
unsigned HOST_WIDE_INT const_nunits;
unsigned nelt_limit;
- unsigned nvectors = vect_get_num_copies (vinfo, node);
auto ops = &SLP_TREE_SCALAR_OPS (node);
auto_vec<unsigned int> starts (nvectors);
if (TYPE_VECTOR_SUBPARTS (vectype).is_constant (&const_nunits)
@@ -9071,7 +9095,7 @@ vect_slp_analyze_node_operations (vec_info *vinfo,
slp_tree node,
int i, j;
slp_tree child;
- /* Assume we can code-generate all invariants. */
+ /* Costing and analysis of invariants is delayed. */
if (!node
|| SLP_TREE_DEF_TYPE (node) == vect_constant_def
|| SLP_TREE_DEF_TYPE (node) == vect_external_def)
@@ -9174,8 +9198,11 @@ vect_slp_analyze_node_operations (vec_info *vinfo,
slp_tree node,
continue;
}
- /* And cost them. */
- vect_prologue_cost_for_slp (vinfo, child, cost_vec);
+ /* Make sure we can generate them and then cost them. */
+ unsigned nvectors;
+ if (!vect_get_num_copies_for_invariant (vinfo, node, &nvectors))
+ return false;
+ vect_prologue_cost_for_slp (child, nvectors, cost_vec);
}
/* If this node or any of its children can't be vectorized, try pruning
@@ -10942,7 +10969,10 @@ vect_create_constant_vectors (vec_info *vinfo,
slp_tree op_node)
/* We always want SLP_TREE_VECTYPE (op_node) here correctly set. */
vector_type = SLP_TREE_VECTYPE (op_node);
- unsigned int number_of_vectors = vect_get_num_copies (vinfo, op_node);
+ unsigned int number_of_vectors;
+ bool res = vect_get_num_copies_for_invariant (vinfo, op_node,
+ &number_of_vectors);
+ gcc_assert (res);
SLP_TREE_VEC_DEFS (op_node).create (number_of_vectors);
auto_vec<tree> voprnds (number_of_vectors);
diff --git a/gcc/tree-vect-stmts.cc b/gcc/tree-vect-stmts.cc
index 38a17e29a84..a8a2ec62920 100644
--- a/gcc/tree-vect-stmts.cc
+++ b/gcc/tree-vect-stmts.cc
@@ -5160,7 +5160,8 @@ vect_create_vectorized_demotion_stmts (vec_info *vinfo,
vec<tree> *vec_oprnds,
/* Create vectorized promotion statements for vector operands from VEC_OPRNDS0
and VEC_OPRNDS1, for a binary operation associated with scalar statement
STMT_INFO. For multi-step conversions store the resulting vectors and
- call the function recursively. */
+ call the function recursively. When HALF is true only generate half
+ of the result. */
static void
vect_create_vectorized_promotion_stmts (vec_info *vinfo,
@@ -5169,14 +5170,15 @@ vect_create_vectorized_promotion_stmts (vec_info *vinfo,
stmt_vec_info stmt_info, tree vec_dest,
gimple_stmt_iterator *gsi,
code_helper ch1,
- code_helper ch2, int op_type)
+ code_helper ch2, int op_type,
+ bool half)
{
int i;
tree vop0, vop1, new_tmp1, new_tmp2;
gimple *new_stmt1, *new_stmt2;
vec<tree> vec_tmp = vNULL;
- vec_tmp.create (vec_oprnds0->length () * 2);
+ vec_tmp.create ((half ? 1 : 2) * vec_oprnds0->length ());
FOR_EACH_VEC_ELT (*vec_oprnds0, i, vop0)
{
if (op_type == binary_op)
@@ -5188,23 +5190,20 @@ vect_create_vectorized_promotion_stmts (vec_info *vinfo,
new_stmt1 = vect_gen_widened_results_half (vinfo, ch1, vop0, vop1,
op_type, vec_dest, gsi,
stmt_info);
- new_stmt2 = vect_gen_widened_results_half (vinfo, ch2, vop0, vop1,
- op_type, vec_dest, gsi,
- stmt_info);
- if (is_gimple_call (new_stmt1))
- {
- new_tmp1 = gimple_call_lhs (new_stmt1);
- new_tmp2 = gimple_call_lhs (new_stmt2);
- }
- else
+ new_tmp1 = gimple_get_lhs (new_stmt1);
+ vec_tmp.quick_push (new_tmp1);
+
+ if (vec_tmp.space (1))
{
- new_tmp1 = gimple_assign_lhs (new_stmt1);
- new_tmp2 = gimple_assign_lhs (new_stmt2);
+ new_stmt2 = vect_gen_widened_results_half (vinfo, ch2, vop0, vop1,
+ op_type, vec_dest, gsi,
+ stmt_info);
+ new_tmp2 = gimple_get_lhs (new_stmt2);
+ vec_tmp.quick_push (new_tmp2);
}
- /* Store the results for the next step. */
- vec_tmp.quick_push (new_tmp1);
- vec_tmp.quick_push (new_tmp2);
+ if (!vec_tmp.space (1))
+ break;
}
vec_oprnds0->release ();
@@ -5760,6 +5759,7 @@ vectorizable_conversion (vec_info *vinfo,
widen_or_narrow_float_p
? vectype_out : cvt_type);
+ unsigned num_vectors = vect_get_num_copies (vinfo, slp_node);
switch (modifier)
{
case NONE:
@@ -5843,10 +5843,17 @@ vectorizable_conversion (vec_info *vinfo,
stmt_info, this_dest, gsi, c1,
op_type);
else
+ /* ??? For constant/external inputs we can end up with
+ excess lanes. When the number of inputs already match
+ the number of required outputs request half of the
+ lanes (gcc.dg/vect/O3-vect-pr32243.c). Low coverage
+ makes this likely incomplete. */
vect_create_vectorized_promotion_stmts (vinfo, &vec_oprnds0,
&vec_oprnds1, stmt_info,
this_dest, gsi,
- c1, c2, op_type);
+ c1, c2, op_type,
+ vec_oprnds0.length ()
+ == num_vectors);
}
FOR_EACH_VEC_ELT (vec_oprnds0, i, vop0)
--
2.51.0