On Wed, 17 Jun 2026, [email protected] wrote:
> From: Kyrylo Tkachov <[email protected]>
>
> This is preparatory analysis with no effect on its own; the following
> patch consumes it.
>
> ivopts splits the memory references of a loop into address use-groups.
> Two references that share the same base object and step but differ by a
> constant offset (e.g. src[i] and src[i] + 16 after vectorisation and
> unrolling) end up in separate groups.
But the intention of grouping in IVOPTs is to group exactly that,
bases that differ by only a constant.
So what you do seems backwards to me. If the existing mechanism
doesn't work, fix it.
Richard.
> Whether such references can later
> be combined into a load/store pair depends on how ivopts chooses to
> address them: an immediate offset from a shared base register keeps them
> pairable, a register offset (a single shared "index" IV) does not.
>
> Record, for each address use, whether another address use in the loop
> refers to the same base object with the same step but a different base,
> i.e. a different constant offset ("a pairing sibling"). The flag is
> computed by a new helper called from find_interesting_uses; it is not
> read anywhere yet. The next patch uses it in the address cost model.
>
> The scan is O(n^2) in the number of address uses, so it is skipped for
> loops with more groups than MAX_CONSIDERED_GROUPS, mirroring the
> bail-out that ivopts already applies to such loops before candidate
> selection. The new field is zero-initialised (record_use allocates
> iv_use with XCNEW), so it defaults to false and no other code path needs
> changing. The patch is therefore codegen-neutral by construction: it
> only sets a flag that nothing consumes yet.
>
> gcc/ChangeLog:
>
> PR target/121315
> * tree-ssa-loop-ivopts.cc (struct iv_use): Add has_pairing_sibling.
> (determine_addr_use_pairing_siblings): New function.
> (find_interesting_uses): Call it, subject to MAX_CONSIDERED_GROUPS.
>
> Signed-off-by: Kyrylo Tkachov <[email protected]>
> ---
> gcc/tree-ssa-loop-ivopts.cc | 56 +++++++++++++++++++++++++++++++++++++
> 1 file changed, 56 insertions(+)
>
> diff --git a/gcc/tree-ssa-loop-ivopts.cc b/gcc/tree-ssa-loop-ivopts.cc
> index bbd3cfaab24..557b35ed2a1 100644
> --- a/gcc/tree-ssa-loop-ivopts.cc
> +++ b/gcc/tree-ssa-loop-ivopts.cc
> @@ -415,6 +415,13 @@ struct iv_use
> tree addr_base; /* Base address with const offset stripped. */
> poly_uint64 addr_offset;
> /* Const offset stripped from base address. */
> + bool has_pairing_sibling;
> + /* For address uses, true if another address use in the
> + loop refers to the same base object with the same
> + step but a different base (i.e. a different constant
> + offset). A register-offset address for such a use
> + prevents the accesses from being combined into a
> + load/store pair. */
> };
>
> /* Group of uses. */
> @@ -2676,6 +2683,50 @@ split_address_groups (struct ivopts_data *data)
> }
> }
>
> +/* Mark each address-type use for which another address use in the loop
> + refers to the same base object, with the same step, but a different base
> + (i.e. a different constant offset). Such accesses can be combined into a
> + load/store pair when addressed with an immediate offset from a shared
> base,
> + but not when addressed with a register offset. The flag lets candidate
> + selection (via the cost in get_address_cost) steer away from
> register-offset
> + addressing in that case. */
> +
> +static void
> +determine_addr_use_pairing_siblings (struct ivopts_data *data)
> +{
> + auto_vec<struct iv_use *, 16> addr_uses;
> + unsigned i, j;
> + struct iv_group *group;
> +
> + FOR_EACH_VEC_ELT (data->vgroups, i, group)
> + {
> + if (!address_p (group->type))
> + continue;
> + struct iv_use *use;
> + FOR_EACH_VEC_ELT (group->vuses, j, use)
> + /* integer_zero_node is determine_base_object's "multiple base objects"
> + sentinel; it must not be treated as a shared base. */
> + if (use->iv->base_object != NULL_TREE
> + && use->iv->base_object != integer_zero_node)
> + addr_uses.safe_push (use);
> + }
> +
> + struct iv_use *u, *v;
> + FOR_EACH_VEC_ELT (addr_uses, i, u)
> + FOR_EACH_VEC_ELT (addr_uses, j, v)
> + {
> + if (u == v)
> + continue;
> + if (operand_equal_p (u->iv->base_object, v->iv->base_object, 0)
> + && operand_equal_p (u->iv->step, v->iv->step, OEP_ASSUME_WRAPV)
> + && !operand_equal_p (u->iv->base, v->iv->base, OEP_ASSUME_WRAPV))
> + {
> + u->has_pairing_sibling = true;
> + break;
> + }
> + }
> +}
> +
> /* Finds uses of the induction variables that are interesting. */
>
> static void
> @@ -2705,6 +2756,11 @@ find_interesting_uses (struct ivopts_data *data,
> basic_block *body)
>
> split_address_groups (data);
>
> + /* Skip the sibling analysis for loops the optimizer will not consider
> + anyway (cf. the MAX_CONSIDERED_GROUPS bail-out in its caller). */
> + if (data->vgroups.length () <= MAX_CONSIDERED_GROUPS)
> + determine_addr_use_pairing_siblings (data);
> +
> if (dump_file && (dump_flags & TDF_DETAILS))
> {
> fprintf (dump_file, "\n<IV Groups>:\n");
>
--
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)