I apologize for the delayed response; I haven't been able to check my
emails promptly due to work commitments. Thank you for your review. I will
send the second version of the patch.

Tnanks,
Xin Wang

Andrew Pinski <[email protected]> 于2026年1月16日周五 10:25写道:

> On Thu, Jan 15, 2026 at 4:11 AM Xin Wang <[email protected]> wrote:
> >
> > 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 *);
>
> Why not move this function up and not add the forward declaration? It
> does not depend on anything defined in this file.
> That is the only thing I have to add here as the TODO was added for
> Richard to review it later. Plus we are in stage 4 so this
> might have to wait until stage 1 of GCC 17.
>
> Thanks,
> Andrew
>
> > +
> >  /* 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