From: Xin Wang <[email protected]>

This patch centralizes the delicate handling of MEM_REF offsets and base
pointer equality into a new helper function im_compare_access_position_and
_size, which is now used by both mem_ref_hasher::equal and is_self_write.

gcc/ChangeLog:
* tree-ssa-loop-im.cc (im_compare_access_position_and_size): New helper
function to compare ao_ref position and size. (mem_ref_hasher::equal):
Use the new helper for position and size comparison, keeping additional
hash table specific checks. (is_self_write): Likewise, using the
centralized helper after checking ref_decomposed.

Signed-off-by: Xin Wang <[email protected]>
---
 gcc/tree-ssa-loop-im.cc | 117 ++++++++++++++++++++++++++--------------
 1 file changed, 76 insertions(+), 41 deletions(-)

diff --git a/gcc/tree-ssa-loop-im.cc b/gcc/tree-ssa-loop-im.cc
index b9b1d92b518..bf9ae4a5fd9 100644
--- a/gcc/tree-ssa-loop-im.cc
+++ b/gcc/tree-ssa-loop-im.cc
@@ -180,6 +180,9 @@ query_loop_dependence (class loop *loop, im_mem_ref *ref, 
dep_kind kind)
   return dep_unknown;
 }
 
+/* Forward declaration.  */
+static bool im_compare_access_position_and_size (const ao_ref *, const ao_ref 
*);
+
 /* Mem_ref hashtable helpers.  */
 
 struct mem_ref_hasher : nofree_ptr_hash <im_mem_ref>
@@ -204,31 +207,30 @@ inline bool
 mem_ref_hasher::equal (const im_mem_ref *mem1, const ao_ref *obj2)
 {
   if (obj2->max_size_known_p ())
-    return (mem1->ref_decomposed
-           && ((TREE_CODE (mem1->mem.base) == MEM_REF
-                && TREE_CODE (obj2->base) == MEM_REF
-                && operand_equal_p (TREE_OPERAND (mem1->mem.base, 0),
-                                    TREE_OPERAND (obj2->base, 0), 0)
-                && known_eq (mem_ref_offset (mem1->mem.base) * BITS_PER_UNIT + 
mem1->mem.offset,
-                             mem_ref_offset (obj2->base) * BITS_PER_UNIT + 
obj2->offset))
-               || (operand_equal_p (mem1->mem.base, obj2->base, 0)
-                   && known_eq (mem1->mem.offset, obj2->offset)))
-           && known_eq (mem1->mem.size, obj2->size)
-           && known_eq (mem1->mem.max_size, obj2->max_size)
-           && mem1->mem.volatile_p == obj2->volatile_p
-           && (mem1->mem.ref_alias_set == obj2->ref_alias_set
-               /* We are not canonicalizing alias-sets but for the
-                  special-case we didn't canonicalize yet and the
-                  incoming ref is a alias-set zero MEM we pick
-                  the correct one already.  */
-               || (!mem1->ref_canonical
-                   && (TREE_CODE (obj2->ref) == MEM_REF
-                       || TREE_CODE (obj2->ref) == TARGET_MEM_REF)
-                   && obj2->ref_alias_set == 0)
-               /* Likewise if there's a canonical ref with alias-set zero.  */
-               || (mem1->ref_canonical && mem1->mem.ref_alias_set == 0))
-           && types_compatible_p (TREE_TYPE (mem1->mem.ref),
-                                  TREE_TYPE (obj2->ref)));
+    {
+      if (!mem1->ref_decomposed)
+       return false;
+
+      /* Use the centralized helper for position and size comparison.  */
+      if (!im_compare_access_position_and_size (&mem1->mem, obj2))
+       return false;
+
+      /* Additional checks specific to hash table lookup.  */
+      return (mem1->mem.volatile_p == obj2->volatile_p
+             && (mem1->mem.ref_alias_set == obj2->ref_alias_set
+                 /* We are not canonicalizing alias-sets but for the
+                    special-case we didn't canonicalize yet and the
+                    incoming ref is a alias-set zero MEM we pick
+                    the correct one already.  */
+                 || (!mem1->ref_canonical
+                     && (TREE_CODE (obj2->ref) == MEM_REF
+                         || TREE_CODE (obj2->ref) == TARGET_MEM_REF)
+                     && obj2->ref_alias_set == 0)
+                 /* Likewise if there's a canonical ref with alias-set zero.  
*/
+                 || (mem1->ref_canonical && mem1->mem.ref_alias_set == 0))
+             && types_compatible_p (TREE_TYPE (mem1->mem.ref),
+                                    TREE_TYPE (obj2->ref)));
+    }
   else
     return operand_equal_p (mem1->mem.ref, obj2->ref, 0);
 }
@@ -3148,6 +3150,50 @@ ref_always_accessed_p (class loop *loop, im_mem_ref 
*ref, bool stored_p)
                               ref_always_accessed (loop, stored_p));
 }
 
+/* Returns true if the positions and sizes of two ao_ref structures are
+   equal.  This handles MEM_REF offsets specially by merging the offset
+   from the MEM_REF base with the ao_ref offset.
+
+   This helper centralizes the delicate handling of memory reference
+   comparison, used by both mem_ref_hasher::equal and is_self_write.  */
+
+static bool
+im_compare_access_position_and_size (const ao_ref *ref1, const ao_ref *ref2)
+{
+  /* Check base pointer equality.  For MEM_REF types, we need to
+     compare the underlying pointer operands and merge the offsets.  */
+  if (TREE_CODE (ref1->base) == MEM_REF && TREE_CODE (ref2->base) == MEM_REF)
+    {
+      if (!operand_equal_p (TREE_OPERAND (ref1->base, 0),
+                           TREE_OPERAND (ref2->base, 0), 0))
+       return false;
+
+      /* Both are MEM_REF - compare merged offsets from base and offset.  */
+      if (!known_eq (mem_ref_offset (ref1->base) * BITS_PER_UNIT
+                    + ref1->offset,
+                    mem_ref_offset (ref2->base) * BITS_PER_UNIT
+                    + ref2->offset))
+       return false;
+    }
+  else if (!operand_equal_p (ref1->base, ref2->base, 0)
+          || !known_eq (ref1->offset, ref2->offset))
+    return false;
+
+  /* Compare sizes.  */
+  if (!known_eq (ref1->size, ref2->size))
+    return false;
+
+  /* If both max_sizes are known, they must agree to ensure
+     no partial overlaps are possible.  */
+  if (ref1->max_size_known_p () && ref2->max_size_known_p ())
+    {
+      if (!known_eq (ref1->max_size, ref2->max_size))
+       return false;
+    }
+
+  return true;
+}
+
 /* Returns true if LOAD_REF and STORE_REF form a "self write" pattern
    where the stored value comes from the loaded value via SSA.
    Example: a[i] = a[0] is safe to hoist a[0] even when i==0.  */
@@ -3177,23 +3223,12 @@ is_self_write (im_mem_ref *load_ref, im_mem_ref 
*store_ref)
   if (stored_val != loaded_val)
     return false;
 
+  /* They may alias.  Verify exact same location.
+     Use the centralized helper to handle MEM_REF offsets properly.  */
+  if (!load_ref->ref_decomposed || !store_ref->ref_decomposed)
+    return operand_equal_p (load_ref->mem.ref, store_ref->mem.ref, 0);
 
-  /* TODO: Try to factor this out with mem_ref_hasher::equal
-     into im_compare_access_position_and_size
-     or a similar helper to centralize this delicate handling
-     complete for MEM_REF offsets and base pointer equality.
-
-     TODO: Also ensure max_size_known_p agrees or resort to
-     alignment considerations to rule out partial overlaps.
-
-     See:
-     https://gcc.gnu.org/pipermail/gcc-patches/2025-December/704155.html
-     For more context on TODOs above.  */
-
-  /* They may alias.  Verify exact same location.  */
-  return (operand_equal_p (load_ref->mem.base, store_ref->mem.base, 0)
-         && known_eq (load_ref->mem.size, store_ref->mem.size)
-         && known_eq (load_ref->mem.offset, store_ref->mem.offset));
+  return im_compare_access_position_and_size (&load_ref->mem, &store_ref->mem);
 
 }
 
-- 
2.34.1

Reply via email to