From e7342fdc66cb9943786f076aa6c13ba2d90fdd16 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] LICM wrong code

Check for partial aliasing in self write test.

gcc/ChangeLog:

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

	PR middle-end/123067
	* tree-ssa-loop-im.cc (is_self_write):

gcc/testsuite/ChangeLog:

2025-12-09  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                       | 13 +++++++-
 2 files changed, 43 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..46e470389e1 100644
--- a/gcc/tree-ssa-loop-im.cc
+++ b/gcc/tree-ssa-loop-im.cc
@@ -3174,7 +3174,18 @@ 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;
+
+  /* Verify there is no partial aliasing.  */
+  if (!mem_refs_may_alias_p (load_ref, store_ref,
+			     &memory_accesses.ttae_cache, true))
+    return true;  /* Disjoint: safe to hoist.  */
+
+  /* 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

