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
