https://gcc.gnu.org/g:0d276cd378e7a41b9004577a30b9a8ca16ec6b4c

commit r16-2729-g0d276cd378e7a41b9004577a30b9a8ca16ec6b4c
Author: Richard Biener <rguent...@suse.de>
Date:   Sun Aug 3 12:02:31 2025 +0200

    tree-optimization/121362 - missed FRE through aggregate copy
    
    The following streamlines and generalizes how we find the common
    base of the lookup ref and a kill ref when looking through
    aggregate copies.  In particular this tries to deal with all
    variants of punning that happens on the inner MEM_REF after
    forwarding of address taken components of the common base.
    
            PR tree-optimization/121362
            * tree-ssa-sccvn.cc (vn_reference_lookup_3): Generalize
            aggregate copy handling.
    
            * gcc.dg/tree-ssa/ssa-fre-105.c: New testcase.
            * gcc.dg/tree-ssa/ssa-fre-106.c: Likewise.

Diff:
---
 gcc/testsuite/gcc.dg/tree-ssa/ssa-fre-105.c |  33 +++++++++
 gcc/testsuite/gcc.dg/tree-ssa/ssa-fre-106.c |  33 +++++++++
 gcc/tree-ssa-sccvn.cc                       | 108 ++++++++++++++++++++++------
 3 files changed, 153 insertions(+), 21 deletions(-)

diff --git a/gcc/testsuite/gcc.dg/tree-ssa/ssa-fre-105.c 
b/gcc/testsuite/gcc.dg/tree-ssa/ssa-fre-105.c
new file mode 100644
index 000000000000..61b93c011b8a
--- /dev/null
+++ b/gcc/testsuite/gcc.dg/tree-ssa/ssa-fre-105.c
@@ -0,0 +1,33 @@
+/* { dg-do compile } */
+/* { dg-options "-O -fno-tree-sra -fdump-tree-fre1-details" } */
+
+struct s1
+{
+  int t, t1;
+};
+
+struct s3
+{
+  struct s1 t;
+};
+
+struct s2
+{
+  struct s3 t;
+};
+
+void f(int, int);
+void l();
+void g(int a, int b, int *p)
+{
+  struct s2 c;
+  {
+    struct s1 tmp = {a,b};
+    struct s3 *t = &c.t;
+    t->t = tmp;
+  }
+  f(c.t.t.t, c.t.t.t1);
+}
+
+/* { dg-final { scan-tree-dump "Replaced c.t.t.t1 with b" "fre1" } } */
+/* { dg-final { scan-tree-dump "Replaced c.t.t.t with a" "fre1" } } */
diff --git a/gcc/testsuite/gcc.dg/tree-ssa/ssa-fre-106.c 
b/gcc/testsuite/gcc.dg/tree-ssa/ssa-fre-106.c
new file mode 100644
index 000000000000..6da420111a47
--- /dev/null
+++ b/gcc/testsuite/gcc.dg/tree-ssa/ssa-fre-106.c
@@ -0,0 +1,33 @@
+/* { dg-do compile } */
+/* { dg-options "-O -fno-tree-sra -fdump-tree-fre1-details" } */
+
+struct s1
+{
+  int t, t1;
+};
+
+struct s3
+{
+  struct s1 t;
+};
+
+struct s2
+{
+  struct s3 t;
+};
+
+void f(int, int);
+void l();
+void g(int a, int b, int *p)
+{
+  struct s2 c;
+  {
+    struct s1 tmp = {a,b};
+    c.t.t = tmp;
+  }
+  struct s1 *t = &c.t.t;
+  f(t->t, t->t1);
+}
+
+/* { dg-final { scan-tree-dump "Replaced \[^\r\n\]*.t1 with b" "fre1" } } */
+/* { dg-final { scan-tree-dump "Replaced \[^\r\n\]*.t with a" "fre1" } } */
diff --git a/gcc/tree-ssa-sccvn.cc b/gcc/tree-ssa-sccvn.cc
index 3974c4d45830..00315d154e4e 100644
--- a/gcc/tree-ssa-sccvn.cc
+++ b/gcc/tree-ssa-sccvn.cc
@@ -3549,8 +3549,95 @@ vn_reference_lookup_3 (ao_ref *ref, tree vuse, void 
*data_,
 
       /* Find the common base of ref and the lhs.  lhs_ops already
          contains valueized operands for the lhs.  */
+      poly_int64 extra_off = 0;
       i = vr->operands.length () - 1;
       j = lhs_ops.length () - 1;
+
+      /* The base should be always equal due to the above check.  */
+      if (! vn_reference_op_eq (&vr->operands[i], &lhs_ops[j]))
+       return (void *)-1;
+      i--, j--;
+
+      /* The 2nd component should always exist and be a MEM_REF.  */
+      if (!(i >= 0 && j >= 0))
+       ;
+      else if (vn_reference_op_eq (&vr->operands[i], &lhs_ops[j]))
+       i--, j--;
+      else if (vr->operands[i].opcode == MEM_REF
+              && lhs_ops[j].opcode == MEM_REF
+              && known_ne (lhs_ops[j].off, -1)
+              && known_ne (vr->operands[i].off, -1))
+       {
+         bool found = false;
+         /* When we ge a mismatch at a MEM_REF that is not the sole component
+            try finding a match in one of the outer components and continue
+            stripping there.  This happens when addresses of components get
+            forwarded into dereferences.  */
+         if (j > 0)
+           {
+             int temi = i - 1;
+             extra_off = vr->operands[i].off;
+             while (temi >= 0
+                    && known_ne (vr->operands[temi].off, -1))
+               {
+                 if (vr->operands[temi].type
+                     && lhs_ops[j].type
+                     && (TYPE_MAIN_VARIANT (vr->operands[temi].type)
+                         == TYPE_MAIN_VARIANT (lhs_ops[j].type)))
+                   {
+                     i = temi;
+                     /* Strip the component that was type matched to
+                        the MEM_REF.  */
+                     extra_off += vr->operands[i].off - lhs_ops[j].off;
+                     i--, j--;
+                     /* Strip further equal components.  */
+                     found = true;
+                     break;
+                   }
+                 extra_off += vr->operands[temi].off;
+                 temi--;
+               }
+           }
+         if (!found && i > 0)
+           {
+             int temj = j - 1;
+             extra_off = -lhs_ops[j].off;
+             while (temj >= 0
+                    && known_ne (lhs_ops[temj].off, -1))
+               {
+                 if (vr->operands[i].type
+                     && lhs_ops[temj].type
+                     && (TYPE_MAIN_VARIANT (vr->operands[i].type)
+                         == TYPE_MAIN_VARIANT (lhs_ops[temj].type)))
+                   {
+                     j = temj;
+                     /* Strip the component that was type matched to
+                        the MEM_REF.  */
+                     extra_off += vr->operands[i].off - lhs_ops[j].off;
+                     i--, j--;
+                     /* Strip further equal components.  */
+                     found = true;
+                     break;
+                   }
+                 extra_off += -lhs_ops[temj].off;
+                 temj--;
+               }
+           }
+         /* When the LHS is already at the outermost level simply
+            adjust for any offset difference.  Further lookups
+            will fail when there's too gross of a type compatibility
+            issue.  */
+         if (!found && j == 0)
+           {
+             extra_off = vr->operands[i].off - lhs_ops[j].off;
+             i--, j--;
+           }
+       }
+      else
+       return (void *)-1;
+
+      /* Strip further common components, attempting to consume lhs_ops
+        in full.  */
       while (j >= 0 && i >= 0
             && vn_reference_op_eq (&vr->operands[i], &lhs_ops[j]))
        {
@@ -3558,27 +3645,6 @@ vn_reference_lookup_3 (ao_ref *ref, tree vuse, void 
*data_,
          j--;
        }
 
-      /* ???  The innermost op should always be a MEM_REF and we already
-         checked that the assignment to the lhs kills vr.  Thus for
-        aggregate copies using char[] types the vn_reference_op_eq
-        may fail when comparing types for compatibility.  But we really
-        don't care here - further lookups with the rewritten operands
-        will simply fail if we messed up types too badly.  */
-      poly_int64 extra_off = 0;
-      if (j == 0 && i >= 0
-         && lhs_ops[0].opcode == MEM_REF
-         && maybe_ne (lhs_ops[0].off, -1))
-       {
-         if (known_eq (lhs_ops[0].off, vr->operands[i].off))
-           i--, j--;
-         else if (vr->operands[i].opcode == MEM_REF
-                  && maybe_ne (vr->operands[i].off, -1))
-           {
-             extra_off = vr->operands[i].off - lhs_ops[0].off;
-             i--, j--;
-           }
-       }
-
       /* i now points to the first additional op.
         ???  LHS may not be completely contained in VR, one or more
         VIEW_CONVERT_EXPRs could be in its way.  We could at least

Reply via email to