From 59f47635a4edc8bac0ebd9bf3144fa104c17503c Mon Sep 17 00:00:00 2001
From: Kugan Vivekanandarajah <kvivekananda@nvidia.com>
Date: Tue, 9 Dec 2025 17:16:49 -0800
Subject: [PATCH] [Bug 123067][V3] LICM wrong code

Check for partial aliasing in self write test.

gcc/ChangeLog:

2025-12-14  Kugan Vivekanandarajah  <kvivekananda@nvidia.com>

	PR middle-end/123067
	* tree-ssa-loop-im.cc(is_self_write): Check
	load and store refer to same location.

gcc/testsuite/ChangeLog:

2025-12-14  Kugan Vivekanandarajah  <kvivekananda@nvidia.com>

	PR middle-end/123067
	* gcc.dg/licm-self-write-partial-alias.c: New test.

Signed-off-by: Kugan Vivekanandarajah <kvivekananda@nvidia.com>
---
 .../gcc.dg/licm-self-write-partial-alias.c    | 31 +++++++++++++++++++
 gcc/tree-ssa-loop-im.cc                       | 22 ++++++++++++-
 2 files changed, 52 insertions(+), 1 deletion(-)
 create mode 100644 gcc/testsuite/gcc.dg/licm-self-write-partial-alias.c

diff --git a/gcc/testsuite/gcc.dg/licm-self-write-partial-alias.c b/gcc/testsuite/gcc.dg/licm-self-write-partial-alias.c
new file mode 100644
index 00000000000..7b8792feb75
--- /dev/null
+++ b/gcc/testsuite/gcc.dg/licm-self-write-partial-alias.c
@@ -0,0 +1,31 @@
+/* { dg-do run } */
+/* { dg-options "-O2" } */
+
+int
+main (void)
+{
+  /* Array element shifting - partial aliasing.  */
+  {
+    int a[6] = {0, 0, 1, 2, 0, 0};
+    unsigned char i, j;
+    for (i = 1; i != 0; ++i)
+      {
+        for (j = 1; j <= 4; j++)
+          a[j] = a[j + 1];
+      }
+    if (a[1] != 0)
+      __builtin_abort ();
+  }
+
+  /* Memmove with overlapping regions - partial aliasing.  */
+  {
+    unsigned char a[6] = {0, 0, 1, 2, 0, 0};
+    for (int i = 0; i < 256; i++)
+      __builtin_memmove (&a[1], &a[2], 4);
+    if (a[1] != 0)
+      __builtin_abort ();
+  }
+
+  return 0;
+}
+
diff --git a/gcc/tree-ssa-loop-im.cc b/gcc/tree-ssa-loop-im.cc
index 61f08beb9ff..35628befc50 100644
--- a/gcc/tree-ssa-loop-im.cc
+++ b/gcc/tree-ssa-loop-im.cc
@@ -3174,7 +3174,27 @@ is_self_write (im_mem_ref *load_ref, im_mem_ref *store_ref)
     return false;
 
   /* Self write: stored value is the loaded value.  */
-  return stored_val == loaded_val;
+  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));
+
 }
 
 /* Returns true if REF1 and REF2 are independent.  */
-- 
2.34.1

