From 9505eab3fe37eff575e67be3c6c9731d8d4c5561 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][V2] 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, correctly as in
	mem_ref_hasher::equal.

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                       | 25 ++++++++++++++-
 2 files changed, 55 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..735435608b6 100644
--- a/gcc/tree-ssa-loop-im.cc
+++ b/gcc/tree-ssa-loop-im.cc
@@ -3174,7 +3174,30 @@ 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;
+
+  /* Both references must have known max_size and be decomposed to
+     do the detailed ao_ref comparison.  */
+  return (operand_equal_p (load_ref->mem.base, store_ref->mem.base, 0)
+	  && load_ref->ref_decomposed
+	  && load_ref->mem.max_size_known_p ()
+	  && store_ref->mem.max_size_known_p ()
+	  && known_eq (load_ref->mem.offset, store_ref->mem.offset)
+	  && known_eq (load_ref->mem.size, store_ref->mem.size)
+	  && load_ref->mem.volatile_p == store_ref->mem.volatile_p
+	  && (load_ref->mem.ref_alias_set == store_ref->mem.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.  */
+	      || (!load_ref->ref_canonical
+		  && (TREE_CODE (store_ref->mem.ref) == MEM_REF
+		      || TREE_CODE (store_ref->mem.ref) == TARGET_MEM_REF)
+		  && store_ref->mem.ref_alias_set == 0)
+	      /* Likewise if there's a canonical ref with alias-set zero.  */
+	      || (load_ref->ref_canonical
+		  && load_ref->mem.ref_alias_set == 0)));
 }
 
 /* Returns true if REF1 and REF2 are independent.  */
-- 
2.34.1

