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.  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");
-- 
2.50.1 (Apple Git-155)

Reply via email to