> -----Original Message-----
> From: Richard Biener <[email protected]>
> Sent: 20 August 2026 15:03
> To: [email protected]
> Cc: [email protected]; Tamar Christina
> <[email protected]>
> Subject: [PATCH 1/2] choose VF independent of externs/constants
> 
> 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.

LGTM FWIW, I was trying to figure out if this works with re-vectorization
and think it's fine. Specifically I was wondering what happens when
SLP_TREE_LANES () > SUBPARTS (vectype);

But I think this makes it easier to handle that case.

Thanks,
Tamar

> 
> 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

Reply via email to