On Wed, 20 May 2026, Xin Wang wrote:
> Factor out the base-and-offset comparison shared by mem_ref_hasher::equal
> and is_self_write. The helper handles MEM_REF bases by comparing their
> underlying pointer operands and by folding the MEM_REF offset into the
> ao_ref offset before comparing the final access position.
> Keep the callers' higher-level semantics separate. mem_ref_hasher::equal
> still performs its hashtable-specific size, max_size, volatile, alias-set
> and type checks after the shared base/offset comparison.
> For is_self_write, require both references to describe the same exact access
> before treating them as the same write: size must match, max_size must be
> known and match, and max_size must equal size for both references. This
> addresses the partial-overlap concern by avoiding exact self-write
> classification when either access may cover more bytes than its nominal size.
>
> Signed-off-by: Xin Wang <[email protected]>
> ---
> gcc/tree-ssa-loop-im.cc | 69 ++++++++++++++++++++++++-----------------
> 1 file changed, 41 insertions(+), 28 deletions(-)
>
> diff --git a/gcc/tree-ssa-loop-im.cc b/gcc/tree-ssa-loop-im.cc
> index 1e99728218f..63325ab5fbf 100644
> --- a/gcc/tree-ssa-loop-im.cc
> +++ b/gcc/tree-ssa-loop-im.cc
> @@ -196,6 +196,26 @@ mem_ref_hasher::hash (const im_mem_ref *mem)
> return mem->hash;
> }
>
> +/* Return true if REF1 and REF2 have the same base and offset. This handles
> + MEM_REF bases specially by comparing their underlying pointer operands and
> + merging the MEM_REF offset with the ao_ref offset. */
> +
> +static bool
> +im_refs_same_base_and_offset_p (const ao_ref *ref1, const ao_ref *ref2)
> +{
> + if (TREE_CODE (ref1->base) == MEM_REF
> + && TREE_CODE (ref2->base) == MEM_REF)
> + return (operand_equal_p (TREE_OPERAND (ref1->base, 0),
> + TREE_OPERAND (ref2->base, 0), 0)
> + && known_eq (mem_ref_offset (ref1->base) * BITS_PER_UNIT
> + + ref1->offset,
> + mem_ref_offset (ref2->base) * BITS_PER_UNIT
> + + ref2->offset));
> +
> + return (operand_equal_p (ref1->base, ref2->base, 0)
> + && known_eq (ref1->offset, ref2->offset));
So this refactoring doesn't fall back to the latter if the
former return condition evaluates false. I'd prefer to
split the refs_equal condition out in full.
> +}
> +
> /* An equality function for class im_mem_ref object MEM1 with
> memory reference OBJ2. */
>
> @@ -210,17 +230,7 @@ mem_ref_hasher::equal (const im_mem_ref *mem1, const
> ao_ref *obj2)
>
> /* obj2 is now known decomposable and the hash is based on offset,
> size and base, matching gather_mem_refs_stmt. */
> - bool refs_equal
> - = ((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)));
> + bool refs_equal = im_refs_same_base_and_offset_p (&mem1->mem, obj2);
>
> return (refs_equal
> && known_eq (mem1->mem.size, obj2->size)
> @@ -3163,6 +3173,25 @@ ref_always_accessed_p (class loop *loop, im_mem_ref
> *ref, bool stored_p)
> ref_always_accessed (loop, stored_p));
> }
>
> +/* Returns true if REF1 and REF2 describe the same exact access. */
> +
> +static bool
> +im_refs_same_exact_location_p (const im_mem_ref *ref1, const im_mem_ref
> *ref2)
> +{
> + if (!ref1->ref_decomposed || !ref2->ref_decomposed)
> + return operand_equal_p (ref1->mem.ref, ref2->mem.ref, 0);
> +
> + if (!im_refs_same_base_and_offset_p (&ref1->mem, &ref2->mem))
> + return false;
> +
> + return (known_eq (ref1->mem.size, ref2->mem.size)
> + && ref1->mem.max_size_known_p ()
> + && ref2->mem.max_size_known_p ()
> + && known_eq (ref1->mem.max_size, ref2->mem.max_size)
> + && known_eq (ref1->mem.max_size, ref1->mem.size)
> + && known_eq (ref2->mem.max_size, ref2->mem.size));
I think this is one equality too much, I'd drop the last. Likewise
one max_size_known_p too much.
Otherwise the patch fails to git am due to some context changes.
Thanks,
Richard.
> +}
> +
> /* 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. */
> @@ -3192,23 +3221,8 @@ is_self_write (im_mem_ref *load_ref, im_mem_ref
> *store_ref)
> if (stored_val != loaded_val)
> return false;
>
> -
> - /* 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_refs_same_exact_location_p (load_ref, store_ref);
>
> }
>
> @@ -3883,4 +3897,3 @@ make_pass_lim (gcc::context *ctxt)
> {
> return new pass_lim (ctxt);
> }
> -
>
--
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)