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