On Wed, 1 Jul 2026, liuhongt wrote:

> Override the add_slp_cost hook so the x86 cost model gets the recorded
> stmt costs grouped per SLP node.  The node-dependent costing that used to
> live in add_stmt_cost -- the vec_construct/vec_deconstruct/scalar_to_vec
> construction cost and the cross-lane vec_perm accounting -- is moved to
> add_slp_cost, which handles those kinds and delegates the rest to
> add_stmt_cost.  The shared tail (default-cost fallback, Bonnell/Silvermont
> fixups and frequency scaling) is factored into finish_stmt_cost, called by
> both paths.
> 
> This is a refactor with no intended functional change.
> 
> Bootstrapped and regtested on x86_64-pc-linux-gnu{-m32,}.
> Ok for trunk?

Sorry for not commenting earlier

> gcc/ChangeLog:
> 
>       * config/i386/i386.cc (ix86_vector_costs): Declare add_slp_cost,
>       ix86_slp_construct_cost, ix86_slp_account_perm and finish_stmt_cost.
>       (ix86_vector_costs::add_stmt_cost): Move the vec_construct and
>       vec_perm node costing out; finish via finish_stmt_cost.
>       (ix86_vector_costs::ix86_slp_construct_cost): New.
>       (ix86_vector_costs::ix86_slp_account_perm): New.
>       (ix86_vector_costs::finish_stmt_cost): New.
>       (ix86_vector_costs::add_slp_cost): New.
> ---
>  gcc/config/i386/i386.cc | 166 ++++++++++++++++++++++++++++++++--------
>  1 file changed, 135 insertions(+), 31 deletions(-)
> 
> diff --git a/gcc/config/i386/i386.cc b/gcc/config/i386/i386.cc
> index e66958db7ac..94c2b3c0090 100644
> --- a/gcc/config/i386/i386.cc
> +++ b/gcc/config/i386/i386.cc
> @@ -26165,6 +26165,9 @@ public:
>                             stmt_vec_info stmt_info, slp_tree node,
>                             tree vectype, int misalign,
>                             vect_cost_model_location where) override;
> +  unsigned int add_slp_cost (slp_tree node,
> +                          const array_slice<stmt_info_for_cost> &)
> +                          override;
>    void finish_cost (const vector_costs *) override;
>    bool better_main_loop_than_p (const vector_costs *) const override;
>    bool better_epilogue_loop_than_p (const vector_costs *other,
> @@ -26174,6 +26177,20 @@ private:
>  
>    /* Estimate register pressure of the vectorized code.  */
>    void ix86_vect_estimate_reg_pressure ();
> +  /* Cost of a vec_construct/vec_deconstruct/scalar_to_vec of SLP NODE,
> +     or -1 to fall back to the default vector cost.  */
> +  int ix86_slp_construct_cost (slp_tree node, vect_cost_for_stmt kind,
> +                            tree vectype, machine_mode mode, bool fp,
> +                            vect_cost_model_location where);
> +  /* Account a vec_perm of SLP NODE to the cross-lane permute counters.  */
> +  void ix86_slp_account_perm (slp_tree node, int count, tree vectype,
> +                           stmt_vec_info stmt_info,
> +                           vect_cost_model_location where);
> +  /* Common cost finalization shared by add_stmt_cost and add_slp_cost.  */
> +  unsigned int finish_stmt_cost (int stmt_cost, int count,
> +                              vect_cost_for_stmt kind,
> +                              stmt_vec_info stmt_info, machine_mode mode,
> +                              tree vectype, vect_cost_model_location where);
>    /* Number of GENERAL_REGS/SSE_REGS used in the vectorizer, it's used for
>       estimation of register pressure.
>       ??? Currently it's only used by vec_construct/scalar_to_vec
> @@ -26255,7 +26272,6 @@ ix86_vector_costs::add_stmt_cost (int count, 
> vect_cost_for_stmt kind,
>                                 tree vectype, int,
>                                 vect_cost_model_location where)
>  {
> -  unsigned retval = 0;
>    bool scalar_p
>      = (kind == scalar_stmt || kind == scalar_load || kind == scalar_store);
>    int stmt_cost = - 1;
> @@ -26732,18 +26748,35 @@ ix86_vector_costs::add_stmt_cost (int count, 
> vect_cost_for_stmt kind,
>                   + ix86_vec_cost (mode, ix86_cost->sse_op);
>      }
>  
> +  /* vec_construct/vec_deconstruct/scalar_to_vec and vec_perm are costed
> +     per SLP node in add_slp_cost, which does the node-specific work before
> +     finishing here.  */
> +  return finish_stmt_cost (stmt_cost, count, kind, stmt_info, mode, vectype,
> +                        where);
> +}
> +
> +/* See the comment above the declaration for details.  */
> +
> +int
> +ix86_vector_costs::ix86_slp_construct_cost (slp_tree node,
> +                                         vect_cost_for_stmt kind,
> +                                         tree vectype, machine_mode mode,
> +                                         bool fp,
> +                                         vect_cost_model_location where)
> +{
> +  int stmt_cost = -1;
> +
>    /* If we do elementwise loads into a vector then we are bound by
>       latency and execution resources for the many scalar loads
>       (AGU and load ports).  Try to account for this by scaling the
>       construction cost by the number of elements involved.  */
>    if ((kind == vec_construct || kind == vec_deconstruct)
> -      && ((node
> -        && (((SLP_TREE_MEMORY_ACCESS_TYPE (node) == VMAT_ELEMENTWISE
> -              || SLP_TREE_MEMORY_ACCESS_TYPE (node) == VMAT_STRIDED_SLP)
> -             && (TREE_CODE (DR_STEP (STMT_VINFO_DATA_REF
> -                                     (SLP_TREE_REPRESENTATIVE (node))))
> -                 != INTEGER_CST))
> -            || mat_gather_scatter_p (SLP_TREE_MEMORY_ACCESS_TYPE (node))))))
> +      && (((SLP_TREE_MEMORY_ACCESS_TYPE (node) == VMAT_ELEMENTWISE
> +         || SLP_TREE_MEMORY_ACCESS_TYPE (node) == VMAT_STRIDED_SLP)
> +        && (TREE_CODE (DR_STEP (STMT_VINFO_DATA_REF
> +                                (SLP_TREE_REPRESENTATIVE (node))))
> +            != INTEGER_CST))
> +       || mat_gather_scatter_p (SLP_TREE_MEMORY_ACCESS_TYPE (node))))
>      {
>        auto lsdata = static_cast<vect_load_store_data *> (node->data);
>        tree ls_type = lsdata->ls_type ? lsdata->ls_type : vectype;
> @@ -26755,7 +26788,6 @@ ix86_vector_costs::add_stmt_cost (int count, 
> vect_cost_for_stmt kind,
>                   / GET_MODE_BITSIZE (TYPE_MODE (ls_eltype)) + 1);
>      }
>    else if ((kind == vec_construct || kind == scalar_to_vec)
> -        && node
>          && SLP_TREE_DEF_TYPE (node) == vect_external_def)
>      {
>        stmt_cost = ix86_default_vector_cost (kind, mode);
> @@ -26823,34 +26855,58 @@ ix86_vector_costs::add_stmt_cost (int count, 
> vect_cost_for_stmt kind,
>       if (TREE_CODE (op) == SSA_NAME)
>         TREE_VISITED (op) = 0;
>      }
> -  if (stmt_cost == -1)
> -    stmt_cost = ix86_default_vector_cost (kind, mode);
>  
> +  return stmt_cost;
> +}
> +
> +/* See the comment above the declaration for details.  */
> +
> +void
> +ix86_vector_costs::ix86_slp_account_perm (slp_tree node, int count,
> +                                       tree vectype,
> +                                       stmt_vec_info stmt_info,
> +                                       vect_cost_model_location where)
> +{
>    /* BIT_FIELD_REF <vect_**, 64, 0> with count 0 costs 0 in body.  */
> -  if (kind == vec_perm && vectype && count != 0)
> -    {
> -      unsigned vec_size = GET_MODE_SIZE (TYPE_MODE (vectype));
> -      unsigned nunits = TYPE_VECTOR_SUBPARTS (vectype);
> -      unsigned *num_vec_perm = NULL;
> +  if (!vectype || count == 0)
> +    return;
>  
> -      if (vec_size == 32)
> -     num_vec_perm = m_num_avx256_vec_perm;
> -      else if (vec_size == 64)
> -     num_vec_perm = m_num_avx512_vec_perm;
> +  unsigned vec_size = GET_MODE_SIZE (TYPE_MODE (vectype));
> +  unsigned nunits = TYPE_VECTOR_SUBPARTS (vectype);
> +  unsigned *num_vec_perm = NULL;
>  
> -      if (num_vec_perm && ix86_count_cross_lane_perm_p (m_vinfo, node, 
> nunits))
> +  if (vec_size == 32)
> +    num_vec_perm = m_num_avx256_vec_perm;
> +  else if (vec_size == 64)
> +    num_vec_perm = m_num_avx512_vec_perm;
> +
> +  if (num_vec_perm && ix86_count_cross_lane_perm_p (m_vinfo, node, nunits))
> +    {
> +      num_vec_perm[where] += count;
> +      if (dump_file && (dump_flags & TDF_DETAILS))
>       {
> -       num_vec_perm[where] += count;
> -       if (dump_file && (dump_flags & TDF_DETAILS))
> -         {
> -           fprintf (dump_file,
> -                    "Detected avx%u cross-lane permutation: ", vec_size * 8);
> -           if (stmt_info)
> -             print_gimple_expr (dump_file, stmt_info->stmt, 0, TDF_SLIM);
> -           fprintf (dump_file, " \n");
> -         }
> +       fprintf (dump_file,
> +                "Detected avx%u cross-lane permutation: ", vec_size * 8);
> +       if (stmt_info)
> +         print_gimple_expr (dump_file, stmt_info->stmt, 0, TDF_SLIM);
> +       fprintf (dump_file, " \n");
>       }
>      }
> +}
> +
> +/* Apply the default-cost fallback, uarch fixups and frequency scaling to
> +   STMT_COST (-1 if not yet computed), accumulate into m_costs[WHERE] and
> +   return the result.  */
> +
> +unsigned int
> +ix86_vector_costs::finish_stmt_cost (int stmt_cost, int count,
> +                                  vect_cost_for_stmt kind,
> +                                  stmt_vec_info stmt_info,
> +                                  machine_mode mode, tree vectype,
> +                                  vect_cost_model_location where)
> +{
> +  if (stmt_cost == -1)
> +    stmt_cost = ix86_default_vector_cost (kind, mode);
>  
>    /* Penalize DFmode vector operations for Bonnell.  */
>    if (TARGET_CPU_P (BONNELL) && kind == vector_stmt
> @@ -26860,7 +26916,8 @@ ix86_vector_costs::add_stmt_cost (int count, 
> vect_cost_for_stmt kind,
>    /* Statements in an inner loop relative to the loop being
>       vectorized are weighted more heavily.  The value here is
>       arbitrary and could potentially be improved with analysis.  */
> -  retval = adjust_cost_for_freq (stmt_info, where, count * stmt_cost);
> +  unsigned int retval
> +    = adjust_cost_for_freq (stmt_info, where, count * stmt_cost);
>  
>    /* We need to multiply all vector stmt cost by 1.7 (estimated cost)
>       for Silvermont as it has out of order integer pipeline and can execute
> @@ -26879,6 +26936,53 @@ ix86_vector_costs::add_stmt_cost (int count, 
> vect_cost_for_stmt kind,
>    return retval;
>  }
>  
> +/* Cost the entries of COST_VEC, which all belong to a single SLP node.
> +   vec_construct/vec_deconstruct/scalar_to_vec and vec_perm get x86-specific
> +   node-level costing here; everything else is handed to add_stmt_cost.  */
> +
> +unsigned int
> +ix86_vector_costs::add_slp_cost (slp_tree,
> +                              const array_slice<stmt_info_for_cost> 
> &cost_vec)
> +{
> +  unsigned int sum = 0;
> +  for (auto item : cost_vec)

So my original intent of the ::add_slp_cost hook was to make it
possible to key only on the 'node' - the passed cost_vec should
have been possibly just an afterthought.

So if you iterate over the cost_vec and ignore the common node here
there's no immediate benefit to how we deal with this previously?

As said I didn't yet get to experiment on how that works out, but
I'd have expected sth like

    if (mat_gather_scatter_p (SLP_TREE_MEMORY_ACCESS_TYPE (node)))
      ix86_slp_account_gather_scatter (node, cost_vec);
    else if (SLP_TREE_MEMORY_ACCESS_TYPE (node) == VMAT_STRIDED_SLP)
      ...
    else
      return ::add_slp_cost (node, cost_vec);

but maybe this doesn't work out to simplify things.

> +    {
> +      tree vectype = item.vectype;
> +      /* vec_perm is accounted even without a node (treated as cross-lane);
> +      the construct cases need a node.  */
> +      if (item.kind == vec_perm
> +       || (item.node
> +           && (item.kind == vec_construct
> +               || item.kind == vec_deconstruct
> +               || item.kind == scalar_to_vec)))
> +     {
> +       machine_mode mode = vectype ? TYPE_MODE (vectype) : TImode;
> +       bool fp = vectype ? FLOAT_TYPE_P (vectype) : false;
> +       int stmt_cost = -1;
> +
> +       if (item.kind == vec_perm)
> +         ix86_slp_account_perm (item.node, item.count, vectype,
> +                                item.stmt_info, item.where);
> +       else
> +         stmt_cost = ix86_slp_construct_cost (item.node, item.kind, vectype,
> +                                              mode, fp, item.where);
> +
> +       unsigned int cost
> +         = finish_stmt_cost (stmt_cost, item.count, item.kind,
> +                             item.stmt_info, mode, vectype, item.where);
> +       if (dump_file && (dump_flags & TDF_DETAILS))
> +         dump_stmt_cost (dump_file, item.count, item.kind, item.stmt_info,
> +                         item.node, vectype, item.misalign, cost,
> +                         item.where);
> +       sum += cost;
> +     }
> +      else
> +     sum += ::add_stmt_cost (this, item.count, item.kind, item.stmt_info,
> +                             item.node, vectype, item.misalign, item.where);
> +    }
> +  return sum;
> +}
> +
>  void
>  ix86_vector_costs::ix86_vect_estimate_reg_pressure ()
>  {
> 

-- 
Richard Biener <[email protected]>
SUSE Software Solutions Germany GmbH,
Frankenstrasse 146, 90461 Nuernberg, Germany;
GF: Jochen Jaser, Andrew McDonald; (HRB 36809, AG Nuernberg)

Reply via email to