On Wed, 24 Jun 2026, [email protected] wrote:
> From: Reshma Roy <[email protected]>
>
> Implement analysis using SCEV information to detect when gather/scatter
> offset expressions evaluate to the same value for all vector lanes within
> each vector iteration. For example, if maximum VF=8, iterations 0-7 of
> "i >> 6" all evaluate to 0, enabling optimization to broadcast. If the
> offset is uniform at max VF, it is uniform at any smaller VF, so we
> only need to check once. The uniformity information is propagated to
> SLP nodes for use in the transformation phase.
>
> Example: For "M[i >> 6]" with VF=8:
> - Iterations 0-7: i >> 6 = 0 (uniform)
> - Iterations 64-71: i >> 6 = 1 (uniform)
> Here we can optimize each vector iteration to load once + broadcast
>
> v2 changes
> - Pattern-match offset defs instead of SCEV on off
> - SCEV only RHS1; RHS2 is constant
> - Store uniformity in gather_scatter_info
> - Use TYPE_VECTOR_SUBPARTS(vectype) for VF
> - Drop max_autovectorize_vf and offset cache
> - Update SLP/stmt paths to read gs_info.offset_uniform
>
> TODO: Replace per-lane simulation with arithmetic reasoning
>
> gcc/ChangeLog:
>
> * tree-vect-data-refs.cc (vect_check_gather_load_offset_uniform): New.
> Return whether the gather offset is the same for every lane at the
> given VF.
> (vect_check_gather_scatter): Call vect_check_gather_load_offset_uniform
> and record the result in gather_scatter_info::offset_uniform.
> * tree-vect-slp.cc (_slp_tree::_slp_tree): Initialize and propagate
> gs_offset_uniform_p on SLP trees.
> * tree-vect-stmts.cc (get_load_store_type): Copy gather uniform
> flag from gather_scatter_info to SLP nodes.
> * tree-vectorizer.h (struct _slp_tree): Add gs_offset_uniform_p field.
> (SLP_TREE_GS_OFFSET_UNIFORM_P): New macro.
> (struct gather_scatter_info): New field offset_uniform for flagging
> uniformity
>
> ---
>
> Hi Richard,
>
> The suggested comments except one were addressed and the patch is updated.
> The detailed description are added inline.
>
> Thanks,
> Reshma Roy
>
> Original Message-----
> > From: Richard Biener <[email protected]>
> > Sent: Tuesday, June 9, 2026 7:24 PM
> > To: Roy, Reshma <[email protected]>
> > Cc: [email protected]; Kumar, Venkataramanan
> > <[email protected]>
> > Subject: Re: [PATCH 1/2] Loop Vectorizer: Detect uniform gather offsets
> > using
> > SCEV analysis
> >
> > Caution: This message originated from an External Source. Use proper caution
> > when opening attachments, clicking links, or responding.
> >
> >
> > On Mon, 25 May 2026, [email protected] wrote:
> >
> > > From: Reshma Roy <[email protected]>
> > >
> > > Implement analysis using SCEV information to detect when
> > > gather/scatter offset expressions evaluate to the same value for all
> > > vector lanes within each vector iteration. For example, if maximum
> > > VF=8, iterations 0-7 of "i >> 6" all evaluate to 0, enabling
> > > optimization to broadcast. If the offset is uniform at max VF, it is
> > > uniform at any smaller VF, so we only need to check once. The
> > > uniformity information is propagated to SLP nodes for use in the
> > > transformation
> > phase.
> > >
> > > Example: For "M[i >> 6]" with VF=8:
> > > - Iterations 0-7: i >> 6 = 0 (uniform)
> > > - Iterations 64-71: i >> 6 = 1 (uniform) Here we can optimize each
> > > vector iteration to load once + broadcast
> > >
> > > gcc/ChangeLog:
> > >
> > > * tree-vect-data-refs.cc (vect_describe_gather_scatter_call):
> > > Initialize
> > > STMT_VINFO_GATHER_UNIFORM_P in stmt_vec_info to false.
> > > (vect_check_gather_load_offset_uniform): New. Return whether the
> > > gather offset is the same for every lane at the given VF.
> > > (vect_check_gather_scatter): Run the uniformity check, cache by
> > > offset
> > > to avoid recomputing for the same offset, and set
> > > STMT_VINFO_GATHER_UNIFORM_P.
> > > * tree-vect-loop.cc (vect_analyze_loop): Compute maximum VF over
> > > candidate vector modes for the uniformity check.
> > > * tree-vect-slp.cc (_slp_tree::_slp_tree): Initialize and propagate
> > > gs_offset_uniform_p on SLP trees.
> > > * tree-vect-stmts.cc (get_load_store_type): Copy gather uniform flag
> > > from stmt_vec_info to SLP nodes.
> > > * tree-vectorizer.cc (vec_info_shared::vec_info_shared): Initialize
> > > max_autovectorize_vf, which is the maximum VF over all candidate
> > > vector modes.
> > > (vec_info::new_stmt_vec_info): Initialize gather_offset_uniform_p.
> > > * tree-vectorizer.h (struct _slp_tree): Add uniformity fields,
> > > cache,
> > > and accessors for emulated gather offset analysis.
> > > (SLP_TREE_GS_OFFSET_UNIFORM_P): New. Uniformity flag for SLP trees.
> > > (struct gather_scatter_info): Added STMT_VINFO_GATHER_UNIFORM_P
> > field.
> > > (STMT_VINFO_GATHER_UNIFORM_P): New. Uniformity flag for
> > stmt_vec_info.
> > > (gs_offset_uniform_p): New field in _slp_tree for uniformity flag.
> > >
> > > ---
> > > gcc/tree-vect-data-refs.cc | 138 ++++++++++++++++++++++++++++++++++++-
> > > gcc/tree-vect-loop.cc | 14 ++++
> > > gcc/tree-vect-slp.cc | 4 ++
> > > gcc/tree-vect-stmts.cc | 10 +++
> > > gcc/tree-vectorizer.cc | 5 +-
> > > gcc/tree-vectorizer.h | 15 ++++
> > > 6 files changed, 184 insertions(+), 2 deletions(-)
> > >
> > > diff --git a/gcc/tree-vect-data-refs.cc b/gcc/tree-vect-data-refs.cc
> > > index da65f1d652c..fe7fc34aeac 100644
> > > --- a/gcc/tree-vect-data-refs.cc
> > > +++ b/gcc/tree-vect-data-refs.cc
> > > @@ -4764,6 +4764,7 @@ void
> > > vect_describe_gather_scatter_call (stmt_vec_info stmt_info,
> > > gather_scatter_info *info) {
> > > + STMT_VINFO_GATHER_UNIFORM_P (stmt_info) = false;
> > > gcall *call = as_a <gcall *> (stmt_info->stmt);
> > > tree vectype = STMT_VINFO_VECTYPE (stmt_info);
> > > data_reference *dr = STMT_VINFO_DATA_REF (stmt_info); @@ -4781,17
> > > +4782,119 @@ vect_describe_gather_scatter_call (stmt_vec_info stmt_info,
> > > info->element_type = TREE_TYPE (vectype);
> > > info->memory_type = TREE_TYPE (DR_REF (dr)); }
> > > +/* Check whether the offset in gather load is uniform across all the VF
> > > lanes.
> > > + If true then If uniform, record it in STMT_VINFO_GATHER_UNIFORM_P
> > > + (stmt_info) so later gather-load lowering can use scalar-load +
> > > broadcast
> > > + instead of full emulated gather. */ static bool
> > > +vect_check_gather_load_offset_uniform (class loop *loop,
> > > + HOST_WIDE_INT const_vf,
> > > + tree off) {
> > > + /* Check ensures that the offset is an SSA_NAME which is qualified for
> > > + uniform detection. */
> > > + if (!off || TREE_CODE (off) != SSA_NAME)
> > > + return false;
> > > + tree off_scev = analyze_scalar_evolution (loop, off);
> > > + if (off_scev && off_scev != chrec_dont_know
> > > + && TREE_CODE (off_scev) != SSA_NAME)
> > > + off_scev = instantiate_parameters (loop, off_scev);
> >
> > As you are looking for an 'off' that evaluates to the same value for N
> > iterations it can
> > never be an affine evolution, so no need to analyze 'off' itself.
> >
> > Instead you are looking for BIT_AND_EXPR or TRUNC_DIV_EXPR, both with a
> > constant 2nd operand.
> Done.
> >
> > > + tree chrec1 = NULL_TREE, chrec2 = NULL_TREE; tree_code op_code =
> > > + ERROR_MARK; tree op_type = TREE_TYPE (off); if (!off_scev ||
> > > + off_scev == chrec_dont_know
> > > + || TREE_CODE (off_scev) == SSA_NAME)
> > > + {
> > > + gimple *def_stmt = SSA_NAME_DEF_STMT (off);
> > > + if (is_gimple_assign (def_stmt))
> > > + {
> > > + op_code = gimple_assign_rhs_code (def_stmt);
> > > + tree rhs2 = gimple_assign_rhs2 (def_stmt);
> > > + if (rhs2 && TREE_CODE_CLASS (op_code) == tcc_binary)
> > > + {
> > > + tree rhs1 = gimple_assign_rhs1 (def_stmt);
> > > + chrec1 = analyze_scalar_evolution (loop, rhs1);
> >
> > so only analyzing RHS1 is required (and RHS2 can be used to pre-filter
> > interesting
> > cases).
> Done.
> >
> > > + chrec2 = analyze_scalar_evolution (loop, rhs2);
> > > + chrec1 = instantiate_parameters (loop, chrec1);
> > > + chrec2 = instantiate_parameters (loop, chrec2);
> > > + if (dump_enabled_p ())
> > > + {
> > > + dump_printf_loc (MSG_NOTE, vect_location, "chrec 1: ");
> > > + dump_generic_expr (MSG_NOTE, TDF_SLIM, chrec1);
> > > + dump_printf (MSG_NOTE, "\nchrec2: ");
> > > + dump_generic_expr (MSG_NOTE, TDF_SLIM, chrec2);
> > > + dump_printf (MSG_NOTE, "\n");
> > > + }
> > > + }
> > > + }
> > > + }
> > > + if (!chrec1 || !chrec2 || chrec1 == chrec_dont_know
> > > + || chrec2 ==chrec_dont_know)
> > > + return false;
> > > +
> > > + /* Strict check for operand 1 to be poly rec and
> > > + operand 2 to be constant. */
> > > + if (!(TREE_CODE (chrec1) == POLYNOMIAL_CHREC)
> > > + || TREE_CODE (chrec2) != INTEGER_CST)
> > > + return false;
> > > + tree scev_base = CHREC_LEFT (chrec1); tree scev_step = CHREC_RIGHT
> > > + (chrec1);
> > > + /* PoC valid for {0, +, 1} induction pattern now.
> > > + TODO Extend to handle general case. */ if ( TREE_CODE
> > > + (scev_base) != INTEGER_CST
> > > + || TREE_CODE (scev_step) != INTEGER_CST
> > > + || !integer_zerop (scev_base) || !integer_onep (scev_step))
> > > + {
> > > + if (dump_enabled_p ())
> > > + dump_printf_loc (MSG_NOTE, vect_location,
> > > + "returning because of non-zero base or"
> > > + "non-one step\n");
> > > + return false;
> > > + }
> > > + /* Iterate from scev_base advancing scev_step each lane, for the VF
> > > lanes.
> > > + Then evaluate each operand at the current iteration value,
> > > + fold and compare. */
> > > + tree first_val = NULL_TREE;
> > > + tree scev_end = fold_build2 (PLUS_EXPR, TREE_TYPE (scev_base),
> > > + scev_base,
> > > + build_int_cst (TREE_TYPE (scev_base),
> > > + const_vf)); for (tree
> > > + iter_val = scev_base;
> > > + tree_int_cst_lt (iter_val, scev_end);
> > > + iter_val = fold_build2 (PLUS_EXPR, TREE_TYPE (iter_val),
> > > + iter_val, scev_step))
> > > + {
> > > + tree chrec1_at_iter = (TREE_CODE (chrec1) == POLYNOMIAL_CHREC
> > > + ? chrec_apply (loop->num, chrec1, iter_val)
> > > + : chrec1);
> > > + tree concrete_val = fold_build2 (op_code, op_type, chrec1_at_iter,
> > > + chrec2);
> > > + if (!first_val)
> > > + first_val = concrete_val;
> > > + else if (!operand_equal_p (concrete_val, first_val, 0))
> > > + return false;
> > > + if (first_val && TREE_CODE (first_val) == SSA_NAME
> > > + && !expr_invariant_in_loop_p (loop, first_val))
> > > + return false;
> >
> > Uh. I think we want something more "programmatic", given we have a binop
> > with
> > constant operand and the CHREC_RIGHT is constant as well we should be able
> > to
> > comptute arithmetically whether (CHREC_LEFT + N * CHREC_RIGHT) <op> RHS2
> > is zero for all N in [0, VF].
> >
> I am a bit unclear here. Could you please elaborate on this ?
We can only handle constant CHREC_LEFT and CHREC_RIGHT, so you
can do the computation on wide_ints? That said, I believe
using chrec_apply is a bit of overkill for computing
base + const_vf * step ...
> On separate note, our further analysis revealed that the uniformity
> check is only done for the lanes in the first vector chunk.
What's a vector chunk?
But sure, you want to know whether { base, +, step } OP CST is
equal for iterations [0, VF - 1], [VF, 2*VF - 1], ... but since
SCEV cannot tell you that given the whole expression isn't affine
you have to prove this in other ways. But I think for division
and shift this is provable by induction but it depends on both
base and step.
> Ideally we should prove the uniformity for all the vector chunks
> which is what LLVM also does.
> https://github.com/llvm/llvm-project/commit/572cfa3fde5433c889b339e9cfa6dfaa23e5f2ee
> It looks like llvm does check the subsequent chunks as well implicitly by
> changing
> the chrec to reflect the VF as well in the step.
> i.e., if chrec is {0,+,1} then chrec is rewritten as {0,+,VF} and then
> check uniformity for all N in [0, VF-1]
> Do we need to go in that direction as well ?
> > > + }
> > > + if (dump_enabled_p ())
> > > + dump_printf_loc (MSG_OPTIMIZED_LOCATIONS, vect_location,
> > > + "gather offset is uniform across VF=%d, "
> > > + "broadcast optimization possible\n",
> > > + (int) const_vf);
> > > + return true;
> > > +}
> > >
> > > /* Return true if a non-affine read or write in STMT_INFO is suitable
> > > for a
> > > gather load or scatter store with VECTYPE. Describe the operation in
> > > *INFO
> > > if so. If it is suitable and ELSVALS is nonzero store the supported
> > > else
> > > values in the vector it points to. */
> > > -
> > > bool
> > > vect_check_gather_scatter (stmt_vec_info stmt_info, tree vectype,
> > > loop_vec_info loop_vinfo,
> > > gather_scatter_info *info, vec<int> *elsvals)
> > > {
> > > + STMT_VINFO_GATHER_UNIFORM_P (stmt_info) = false;
> >
> > please do not record into stmt_info, instead record into ...
> >
> > > HOST_WIDE_INT scale = 1;
> > > poly_int64 pbitpos, pbitsize;
> > > class loop *loop = LOOP_VINFO_LOOP (loop_vinfo); @@ -5114,6
> > > +5217,39 @@ vect_check_gather_scatter (stmt_vec_info stmt_info, tree
> > > vectype,
> > > info->scale = scale;
> > > info->element_type = TREE_TYPE (vectype);
> > > info->memory_type = memory_type;
> >
> > ... 'info'.
> Done.
> >
> > > + /* Check whether uniform gather load for all the vector lanes for
> > > + maximum VF. */
> > > + unsigned HOST_WIDE_INT max_vf =
> > > + loop_vinfo->shared->max_autovectorize_vf;
> >
> > Not sure why you need a max_vf here, you want to perform the analysis when
> > 'vectype' is not NULL and for TYPE_VECTOR_SUBPARTS (vectype) I think.
> The max_vf was used here in order to avoid redundant computation of uniformity
> checks for all vector factors. Rather find the uniformity for the maximum
> vector factor and cache the result to reuse while trying multiple VF.
> The reason being that if uniformity is true for the maximum possible
> vector factor then its true for lower, not vice versa.
> I have removed the caching here and try to recompute for each VF.
> >
> > > + if (max_vf == 0)
> > > + {
> > > + /* If not set then skip the uniforme check. */
> > > + return true;
> > > + }
> > > + HOST_WIDE_INT const_vf = (HOST_WIDE_INT) max_vf; bool uniform =
> > > + false; if (const_vf > 0)
> > > + {
> > > + /* If the uniformity is true for one VF, we do not have to try
> > > another
> > > + VF. We always establish uniformity for max_vfs. Uniformity
> > > (max_vf)
> > > + implies uniformity (lower_vfs). So reuse cached result for this
> > > offset.
> > > + re-do the same check. */
> > > + bool *cached = loop_vinfo->offset_uniformity_cache.get (off);
> > > + if (cached)
> > > + uniform = *cached;
> > > + else
> > > + {
> > > + if (dump_enabled_p ())
> > > + {
> > > + dump_printf_loc (MSG_NOTE, vect_location,
> > > + "offset uniformity check at max VF=%wd\n",
> > > + const_vf);
> > > + }
> > > + uniform = vect_check_gather_load_offset_uniform (loop, const_vf,
> > > off);
> > > + loop_vinfo->offset_uniformity_cache.put (off, uniform);
> > > + }
> > > + }
> > > + /* The information is stored in stm_vinfo for subsequent stages.
> > > + */ STMT_VINFO_GATHER_UNIFORM_P (stmt_info) = uniform;
> > > return true;
> > > }
> > >
> > > diff --git a/gcc/tree-vect-loop.cc b/gcc/tree-vect-loop.cc index
> > > ac7e08cf205..899934306c1 100644
> > > --- a/gcc/tree-vect-loop.cc
> > > +++ b/gcc/tree-vect-loop.cc
> > > @@ -2965,6 +2965,20 @@ vect_analyze_loop (class loop *loop, gimple
> > *loop_vectorized_call,
> > > unsigned int autovec_flags
> > > = targetm.vectorize.autovectorize_vector_modes (&vector_modes,
> > > loop->simdlen != 0);
> > > + /* Get the maximum VF from all the vector_modes to use in checking
> > > whether
> > > + offset is uniform. */
> > > + unsigned HOST_WIDE_INT max_munits = 0; for (unsigned i = 0; i <
> > > + vector_modes.length (); i++)
> > > + {
> > > + machine_mode mode = vector_modes[i];
> > > + if (mode == VOIDmode)
> > > + continue;
> > > + poly_uint64 m_units = GET_MODE_NUNITS (mode);
> > > + unsigned HOST_WIDE_INT c;
> > > + if (m_units.is_constant (&c) && c > 0 && c > max_munits)
> > > + max_munits = c;
> > > + }
> > > + shared->max_autovectorize_vf = max_munits;
> > > bool pick_lowest_cost_p = ((autovec_flags & VECT_COMPARE_COSTS)
> > > && !unlimited_cost_model (loop));
> > > machine_mode autodetected_vector_mode = VOIDmode; diff --git
> > > a/gcc/tree-vect-slp.cc b/gcc/tree-vect-slp.cc index
> > > ea49c32b780..7224d6c77db 100644
> > > --- a/gcc/tree-vect-slp.cc
> > > +++ b/gcc/tree-vect-slp.cc
> > > @@ -123,6 +123,7 @@ _slp_tree::_slp_tree ()
> > > SLP_TREE_CODE (this) = ERROR_MARK;
> > > SLP_TREE_GS_SCALE (this) = 0;
> > > SLP_TREE_GS_BASE (this) = NULL_TREE;
> > > + this->gs_offset_uniform_p = false;
> >
> > SLP_TREE_GS_OFFSET_UNIFORM_P
> Done.
> >
> > > this->ldst_lanes = false;
> > > this->avoid_stlf_fail = false;
> > > SLP_TREE_VECTYPE (this) = NULL_TREE; @@ -2809,6 +2810,7 @@ out:
> > > int reduc_idx = -1;
> > > int gs_scale = 0;
> > > tree gs_base = NULL_TREE;
> > > + bool gs_offset_uniform_p = false;
> > >
> > > /* Create SLP_TREE nodes for the definition node/s. */
> > > FOR_EACH_VEC_ELT (oprnds_info, i, oprnd_info)
> > > @@ -2836,6 +2838,7 @@ out:
> > > {
> > > gs_scale = oprnd_info->first_gs_info.scale;
> > > gs_base = oprnd_info->first_gs_info.base;
> > > + gs_offset_uniform_p = STMT_VINFO_GATHER_UNIFORM_P (stmt_info);
> > > }
> > >
> > > if (is_a <bb_vec_info> (vinfo)
> > > @@ -3256,6 +3259,7 @@ fail:
> > > SLP_TREE_CHILDREN (node).splice (children);
> > > SLP_TREE_GS_SCALE (node) = gs_scale;
> > > SLP_TREE_GS_BASE (node) = gs_base;
> > > + SLP_TREE_GS_OFFSET_UNIFORM_P (node) = gs_offset_uniform_p;
> > > if (reduc_idx != -1)
> > > {
> > > gcc_assert (STMT_VINFO_REDUC_IDX (stmt_info) != -1
> > > diff --git a/gcc/tree-vect-stmts.cc b/gcc/tree-vect-stmts.cc
> > > index da87b329715..b68fc5072af 100644
> > > --- a/gcc/tree-vect-stmts.cc
> > > +++ b/gcc/tree-vect-stmts.cc
> > > @@ -2167,6 +2167,12 @@ get_load_store_type (vec_info *vinfo,
> > > stmt_vec_info
> > stmt_info,
> > > }
> > > else if (STMT_VINFO_GATHER_SCATTER_P (stmt_info))
> > > {
> > > + slp_node->gs_offset_uniform_p
> > > + = STMT_VINFO_GATHER_UNIFORM_P (stmt_info);
> > > + if (dump_enabled_p ())
> > > + dump_printf_loc (MSG_NOTE, vect_location,
> > > + "gs_offset_uniform_p is set to: %d \n",
> > > + STMT_VINFO_GATHER_UNIFORM_P (stmt_info));
> > > slp_tree offset_node = SLP_TREE_CHILDREN (slp_node)[0];
> > > tree offset_vectype = SLP_TREE_VECTYPE (offset_node);
> > > int scale = SLP_TREE_GS_SCALE (slp_node);
> > > @@ -2474,6 +2480,8 @@ get_load_store_type (vec_info *vinfo, stmt_vec_info
> > stmt_info,
> > >
> > > SLP_TREE_GS_SCALE (slp_node) = gs_info.scale;
> > > SLP_TREE_GS_BASE (slp_node) = error_mark_node;
> > > + SLP_TREE_GS_OFFSET_UNIFORM_P (slp_node)
> > > + = STMT_VINFO_GATHER_UNIFORM_P (stmt_info);
> > > ls->gs.ifn = gs_info.ifn;
> > > ls->strided_offset_vectype = gs_info.offset_vectype;
> > > *memory_access_type = VMAT_GATHER_SCATTER_IFN;
> > > @@ -2488,6 +2496,8 @@ get_load_store_type (vec_info *vinfo, stmt_vec_info
> > stmt_info,
> > > {
> > > SLP_TREE_GS_SCALE (slp_node) = gs_info.scale;
> > > SLP_TREE_GS_BASE (slp_node) = error_mark_node;
> > > + SLP_TREE_GS_OFFSET_UNIFORM_P (slp_node)
> > > + = STMT_VINFO_GATHER_UNIFORM_P (stmt_info);
> > > grouped_gather_fallback = *memory_access_type;
> > > *memory_access_type = VMAT_GATHER_SCATTER_IFN;
> > > ls->gs.ifn = gs_info.ifn;
> > > diff --git a/gcc/tree-vectorizer.cc b/gcc/tree-vectorizer.cc
> > > index 205a07b0be5..d4c915ddd8b 100644
> > > --- a/gcc/tree-vectorizer.cc
> > > +++ b/gcc/tree-vectorizer.cc
> > > @@ -483,7 +483,9 @@ vec_info::~vec_info ()
> > > vec_info_shared::vec_info_shared ()
> > > : datarefs (vNULL),
> > > datarefs_copy (vNULL),
> > > - ddrs (vNULL)
> > > + ddrs (vNULL),
> > > + max_autovectorize_vf (0)
> > > +
> > > {
> > > }
> > >
> > > @@ -718,6 +720,7 @@ vec_info::new_stmt_vec_info (gimple *stmt)
> > >
> > > STMT_VINFO_RELEVANT (res) = vect_unused_in_scope;
> > > STMT_VINFO_VECTORIZABLE (res) = true;
> > > + STMT_VINFO_GATHER_UNIFORM_P (res) = false;
> > > STMT_VINFO_REDUC_TYPE (res) = TREE_CODE_REDUCTION;
> > > STMT_VINFO_REDUC_CODE (res) = ERROR_MARK;
> > > STMT_VINFO_REDUC_IDX (res) = -1;
> > > diff --git a/gcc/tree-vectorizer.h b/gcc/tree-vectorizer.h
> > > index 3a01e1be0f1..b2a55f21421 100644
> > > --- a/gcc/tree-vectorizer.h
> > > +++ b/gcc/tree-vectorizer.h
> > > @@ -369,6 +369,8 @@ struct _slp_tree {
> > > /* For gather/scatter memory operations the scale each offset element
> > > should be multiplied by before being added to the base. */
> > > int gs_scale;
> > > + /* For gather/scatter, when the offset is uniform across VF
> > > iterations. */
> > > + bool gs_offset_uniform_p;
> > > /* For gather/scatter memory operations the loop-invariant base value.
> > > */
> > > tree gs_base;
> > > /* Whether uses of this load or feeders of this store are suitable
> > > @@ -474,6 +476,7 @@ public:
> > > #define SLP_TREE_TYPE(S) (S)->type
> > > #define SLP_TREE_GS_SCALE(S) (S)->gs_scale
> > > #define SLP_TREE_GS_BASE(S) (S)->gs_base
> > > +#define SLP_TREE_GS_OFFSET_UNIFORM_P(S) (S)->gs_offset_uniform_p
> > > #define SLP_TREE_REDUC_IDX(S)
> > > (S)->cycle_info.reduc_idx
> > > #define SLP_TREE_PERMUTE_P(S) ((S)->code ==
> > VEC_PERM_EXPR)
> > >
> > > @@ -612,6 +615,10 @@ public:
> > > /* All data dependences. Freed by free_dependence_relations, so not
> > > an auto_vec. */
> > > vec<ddr_p> ddrs;
> > > +
> > > + /* Maximum VF over all autovectorize vector modes for the loop. */
> > > + unsigned HOST_WIDE_INT max_autovectorize_vf;
> > > +
> > > };
> > >
> > > /* Vectorizer state common between loop and basic-block vectorization.
> > > */
> > > @@ -1117,6 +1124,9 @@ public:
> > > rhs of the store of the initializer. */
> > > hash_map<tree, tree> *scan_map;
> > >
> > > + /* Cache for uniformity in gather/scatter to avoid recomputation. */
> > > + hash_map<tree_operand_hash, bool> offset_uniformity_cache;
> > > +
> > > /* The factor used to over weight those statements in an inner loop
> > > relative to the loop being vectorized. */
> > > unsigned int inner_loop_cost_factor;
> > > @@ -1595,6 +1605,9 @@ public:
> > > /* For loads if this is a gather, for stores if this is a scatter. */
> > > bool gather_scatter_p;
> > >
> > > + /* For loads if this is a uniform broadcast. */
> > > + bool gather_offset_uniform_p;
> > > +
> > > /* True if this is an access with loop-invariant stride. */
> > > bool strided_p;
> > >
> > > @@ -1685,6 +1698,7 @@ struct gather_scatter_info {
> > >
> > > /* The type of the scalar elements being loaded or stored. */
> > > tree memory_type;
> > > +
> > > };
> > >
> > > /* Access Functions. */
> > > @@ -1695,6 +1709,7 @@ struct gather_scatter_info {
> > > #define STMT_VINFO_VECTORIZABLE(S) (S)->vectorizable
> > > #define STMT_VINFO_DATA_REF(S) ((S)->dr_aux.dr + 0)
> > > #define STMT_VINFO_GATHER_SCATTER_P(S) (S)->gather_scatter_p
> > > +#define STMT_VINFO_GATHER_UNIFORM_P(S) (S)-
> > >gather_offset_uniform_p
> > > #define STMT_VINFO_STRIDED_P(S) (S)->strided_p
> > > #define STMT_VINFO_SIMD_LANE_ACCESS_P(S) (S)->simd_lane_access_p
> > > #define STMT_VINFO_REDUC_IDX(S) (S)->reduc_idx
> > >
> >
> > --
> > Richard Biener <[email protected]>
> > SUSE Software Solutions Germany GmbH,
> > Frankenstrasse 146, 90461 Nuernberg, Germany;
> > GF: Jochen Jaser, Andrew McDonald, Werner Knoblich; (HRB 36809, AG
> > Nuernberg)
>
>
>
>
>
> gcc/tree-vect-data-refs.cc | 121 ++++++++++++++++++++++++++++++++++++-
> gcc/tree-vect-slp.cc | 4 ++
> gcc/tree-vect-stmts.cc | 8 +++
> gcc/tree-vectorizer.h | 8 +++
> 4 files changed, 140 insertions(+), 1 deletion(-)
>
> diff --git a/gcc/tree-vect-data-refs.cc b/gcc/tree-vect-data-refs.cc
> index 3b27beb9c5b..0beeef5306c 100644
> --- a/gcc/tree-vect-data-refs.cc
> +++ b/gcc/tree-vect-data-refs.cc
> @@ -4818,12 +4818,105 @@ vect_describe_gather_scatter_call (stmt_vec_info
> stmt_info,
> info->element_type = TREE_TYPE (vectype);
> info->memory_type = TREE_TYPE (DR_REF (dr));
> }
> +/* Check whether the offset in gather load is uniform across all the VF
> lanes.
> + If true then If uniform, record it in gather_scatter_info
> + so later gather-load lowering can use scalar-load + broadcast
> + instead of full emulated gather. */
> +static bool
> +vect_check_gather_load_offset_uniform (class loop *loop,
> + HOST_WIDE_INT const_vf,
> + tree off)
> +{
> + /* Check ensures that the offset is an SSA_NAME which is qualified for
> + uniform detection. */
> + if (!off || TREE_CODE (off) != SSA_NAME)
> + return false;
> + tree chrec1 = NULL_TREE, rhs1 = NULL_TREE, rhs2 = NULL_TREE;
> + tree_code op_code = ERROR_MARK;
> + tree op_type = TREE_TYPE (off);
> + gimple *def_stmt = SSA_NAME_DEF_STMT (off);
> + if (is_gimple_assign (def_stmt))
> + {
> + op_code = gimple_assign_rhs_code (def_stmt);
> + if (op_code == BIT_AND_EXPR || op_code == TRUNC_DIV_EXPR
> + || op_code == RSHIFT_EXPR)
line up || on new lines
> + {
> + rhs1 = gimple_assign_rhs1 (def_stmt);
> + rhs2 = gimple_assign_rhs2 (def_stmt);
> + chrec1 = analyze_scalar_evolution (loop, rhs1);
> + chrec1 = instantiate_parameters (loop, chrec1);
> + }
> + else
> + return false;
> + if (dump_enabled_p ())
> + {
> + dump_printf_loc (MSG_NOTE, vect_location, "chrec 1: ");
> + dump_generic_expr (MSG_NOTE, TDF_SLIM, chrec1);
> + dump_printf (MSG_NOTE, "\n");
> + }
> + }
> + else
> + return false;
It's easier to follow
if (!is_gimple_assign (def_stmt))
return false;
> + if (!chrec1 || chrec1 == chrec_dont_know)
> + return false;
> +
> + /* Strict check for operand 1 to be poly rec and
> + operand 2 to be constant. */
> + if (!(TREE_CODE (chrec1) == POLYNOMIAL_CHREC)
!=
> + || TREE_CODE (rhs2) != INTEGER_CST)
> + return false;
this check should be done before doing the SCEV analysis
because it's cheaper
> + tree scev_base = CHREC_LEFT (chrec1);
> + tree scev_step = CHREC_RIGHT (chrec1);
> + /* PoC valid for {0, +, 1} induction pattern now.
> + TODO Extend to handle general case. */
> + if ( TREE_CODE (scev_base) != INTEGER_CST
> + || TREE_CODE (scev_step) != INTEGER_CST
> + || !integer_zerop (scev_base) || !integer_onep (scev_step))
I'll note that in the end we want to handle different bases/steps,
not only to prove that the validation code is robust.
> + {
> + if (dump_enabled_p ())
> + dump_printf_loc (MSG_NOTE, vect_location,
> + "returning because of non-zero base or"
> + "non-one step\n");
> + return false;
> + }
> + /* Iterate from scev_base advancing scev_step each lane, for the VF lanes.
> + Then evaluate each operand at the current iteration value,
> + fold and compare. */
> + tree first_val = NULL_TREE;
> + tree scev_end = fold_build2 (PLUS_EXPR, TREE_TYPE (scev_base),
> + scev_base,
> + build_int_cst (TREE_TYPE (scev_base),
> + const_vf));
> + for (tree iter_val = scev_base;
> + tree_int_cst_lt (iter_val, scev_end);
> + iter_val = fold_build2 (PLUS_EXPR, TREE_TYPE (iter_val),
> + iter_val, scev_step))
> + {
> + tree chrec1_at_iter = (TREE_CODE (chrec1) == POLYNOMIAL_CHREC
> + ? chrec_apply (loop->num, chrec1, iter_val)
> + : chrec1);
> + tree concrete_val = fold_build2 (op_code, op_type, chrec1_at_iter,
> + rhs2);
> + if (!first_val)
> + first_val = concrete_val;
> + else if (!operand_equal_p (concrete_val, first_val, 0))
> + return false;
> + if (first_val && TREE_CODE (first_val) == SSA_NAME
> + && !expr_invariant_in_loop_p (loop, first_val))
> + return false;
As said I'd like to see a more formal proof, given - as you say - we
have to prove this holds not only for [0, VF-1] but to all iterations.
> + }
> + if (dump_enabled_p ())
> + dump_printf_loc (MSG_OPTIMIZED_LOCATIONS, vect_location,
> + "gather offset is uniform across VF=%d, "
> + "broadcast optimization possible\n",
> + (int) const_vf);
> + return true;
> +}
>
> /* Return true if a non-affine read or write in STMT_INFO is suitable for a
> gather load or scatter store with VECTYPE. Describe the operation in
> *INFO
> if so. If it is suitable and ELSVALS is nonzero store the supported else
> values in the vector it points to. */
> -
> bool
> vect_check_gather_scatter (stmt_vec_info stmt_info, tree vectype,
> loop_vec_info loop_vinfo,
> @@ -5151,6 +5244,32 @@ vect_check_gather_scatter (stmt_vec_info stmt_info,
> tree vectype,
> info->scale = scale;
> info->element_type = TREE_TYPE (vectype);
> info->memory_type = memory_type;
> + /* Check whether uniform gather load for all the vector lanes for
> + VF. */
> + HOST_WIDE_INT const_vf = 0;
> + unsigned HOST_WIDE_INT nunits;
> + if (TYPE_VECTOR_SUBPARTS (vectype).is_constant (&nunits))
Ah, so it's not the vectorization factor you are looking at. Please
use const_nunits then.
> + {
> + const_vf = (HOST_WIDE_INT) nunits;
Not sure why you need both.
> + }
> + if (const_vf == 0)
> + {
> + /* If not set then skip the uniforme check. */
?
> + return true;
> + }
> + bool uniform = false;
> + if (const_vf > 0)
?
> + {
> + if (dump_enabled_p ())
> + {
> + dump_printf_loc (MSG_NOTE, vect_location,
> + "offset uniformity check at VF=%wd\n",
> + const_vf);
> + }
> + uniform = vect_check_gather_load_offset_uniform (loop, const_vf, off);
> + }
> + /* The information is stored in stm_vinfo for subsequent stages. */
> + info->offset_uniform = uniform;
> return true;
> }
>
> diff --git a/gcc/tree-vect-slp.cc b/gcc/tree-vect-slp.cc
> index 2250f6f74a1..315cd683fab 100644
> --- a/gcc/tree-vect-slp.cc
> +++ b/gcc/tree-vect-slp.cc
> @@ -123,6 +123,7 @@ _slp_tree::_slp_tree ()
> SLP_TREE_CODE (this) = ERROR_MARK;
> SLP_TREE_GS_SCALE (this) = 0;
> SLP_TREE_GS_BASE (this) = NULL_TREE;
> + SLP_TREE_GS_OFFSET_UNIFORM_P (this) = false;
> this->ldst_lanes = false;
> this->avoid_stlf_fail = false;
> SLP_TREE_VECTYPE (this) = NULL_TREE;
> @@ -2815,6 +2816,7 @@ out:
> int reduc_idx = -1;
> int gs_scale = 0;
> tree gs_base = NULL_TREE;
> + bool gs_offset_uniform_p = false;
>
> /* Create SLP_TREE nodes for the definition node/s. */
> FOR_EACH_VEC_ELT (oprnds_info, i, oprnd_info)
> @@ -2846,6 +2848,7 @@ out:
> {
> gs_scale = oprnd_info->first_gs_info.scale;
> gs_base = oprnd_info->first_gs_info.base;
> + gs_offset_uniform_p = oprnd_info->first_gs_info.offset_uniform;
> }
>
> if (is_a <bb_vec_info> (vinfo)
> @@ -3290,6 +3293,7 @@ fail:
> SLP_TREE_CHILDREN (node).splice (children);
> SLP_TREE_GS_SCALE (node) = gs_scale;
> SLP_TREE_GS_BASE (node) = gs_base;
> + SLP_TREE_GS_OFFSET_UNIFORM_P (node) = gs_offset_uniform_p;
> if (reduc_idx != -1)
> {
> gcc_assert (STMT_VINFO_REDUC_IDX (stmt_info) != -1
> diff --git a/gcc/tree-vect-stmts.cc b/gcc/tree-vect-stmts.cc
> index e991e861525..afd5109c9f7 100644
> --- a/gcc/tree-vect-stmts.cc
> +++ b/gcc/tree-vect-stmts.cc
> @@ -2168,6 +2168,10 @@ get_load_store_type (vec_info *vinfo, stmt_vec_info
> stmt_info,
> }
> else if (STMT_VINFO_GATHER_SCATTER_P (stmt_info))
> {
> + if (dump_enabled_p ())
> + dump_printf_loc (MSG_NOTE, vect_location,
> + "gs_offset_uniform_p is set to: %d\n",
> + SLP_TREE_GS_OFFSET_UNIFORM_P (slp_node));
> slp_tree offset_node = SLP_TREE_CHILDREN (slp_node)[0];
> tree offset_vectype = SLP_TREE_VECTYPE (offset_node);
> int scale = SLP_TREE_GS_SCALE (slp_node);
> @@ -2475,6 +2479,8 @@ get_load_store_type (vec_info *vinfo, stmt_vec_info
> stmt_info,
>
> SLP_TREE_GS_SCALE (slp_node) = gs_info.scale;
> SLP_TREE_GS_BASE (slp_node) = error_mark_node;
> + SLP_TREE_GS_OFFSET_UNIFORM_P (slp_node)
> + = gs_info.offset_uniform;
> ls->gs.ifn = gs_info.ifn;
> ls->strided_offset_vectype = gs_info.offset_vectype;
> *memory_access_type = VMAT_GATHER_SCATTER_IFN;
> @@ -2489,6 +2495,8 @@ get_load_store_type (vec_info *vinfo, stmt_vec_info
> stmt_info,
> {
> SLP_TREE_GS_SCALE (slp_node) = gs_info.scale;
> SLP_TREE_GS_BASE (slp_node) = error_mark_node;
> + SLP_TREE_GS_OFFSET_UNIFORM_P (slp_node)
> + = gs_info.offset_uniform;
> grouped_gather_fallback = *memory_access_type;
> *memory_access_type = VMAT_GATHER_SCATTER_IFN;
> ls->gs.ifn = gs_info.ifn;
> diff --git a/gcc/tree-vectorizer.h b/gcc/tree-vectorizer.h
> index 0b646d6b29e..24a19aff606 100644
> --- a/gcc/tree-vectorizer.h
> +++ b/gcc/tree-vectorizer.h
> @@ -372,6 +372,8 @@ struct _slp_tree {
> /* For gather/scatter memory operations the scale each offset element
> should be multiplied by before being added to the base. */
> int gs_scale;
> + /* For gather/scatter, when the offset is uniform across VF iterations. */
across each individual vector number of lanes iterations.
> + bool gs_offset_uniform_p;
> /* For gather/scatter memory operations the loop-invariant base value. */
> tree gs_base;
> /* Whether uses of this load or feeders of this store are suitable
> @@ -477,6 +479,7 @@ public:
> #define SLP_TREE_TYPE(S) (S)->type
> #define SLP_TREE_GS_SCALE(S) (S)->gs_scale
> #define SLP_TREE_GS_BASE(S) (S)->gs_base
> +#define SLP_TREE_GS_OFFSET_UNIFORM_P(S) (S)->gs_offset_uniform_p
> #define SLP_TREE_REDUC_IDX(S)
> (S)->cycle_info.reduc_idx
> #define SLP_TREE_PERMUTE_P(S) ((S)->code ==
> VEC_PERM_EXPR)
>
> @@ -615,6 +618,7 @@ public:
> /* All data dependences. Freed by free_dependence_relations, so not
> an auto_vec. */
> vec<ddr_p> ddrs;
> +
please avoid spurious whitespace changes
> };
>
> /* Vectorizer state common between loop and basic-block vectorization. */
> @@ -1693,6 +1697,10 @@ struct gather_scatter_info {
>
> /* The type of the scalar elements being loaded or stored. */
> tree memory_type;
> +
> + /* The field to record if the offset is uniform accross VF. */
> + bool offset_uniform;
> +
likewise - no extra vertial space before }
> };
>
> /* Access Functions. */
>
--
Richard Biener <[email protected]>
SUSE Software Solutions Germany GmbH,
Frankenstrasse 146, 90461 Nuernberg, Germany;
GF: Jochen Jaser, Andrew McDonald, Werner Knoblich; (HRB 36809, AG Nuernberg)