Currently the code rejects: ``` tmp = *a; *b = tmp; ``` (unless *a == *b). This can be improved such that if a and b are known to share the same base, then only reject it if they overlap; that is the difference of the offsets (from the base) is maybe less than the size.
This fixes the testcase in comment #0 of PR 107051. PR tree-optimization/107051 gcc/ChangeLog: * tree-ssa-forwprop.cc (optimize_agr_copyprop_1): Allow for memory sharing the same base if they known not to overlap over the size. gcc/testsuite/ChangeLog: * gcc.dg/tree-ssa/copy-prop-aggregate-union-1.c: New test. Signed-off-by: Andrew Pinski <andrew.pin...@oss.qualcomm.com> --- .../tree-ssa/copy-prop-aggregate-union-1.c | 24 +++++++++++++++++ gcc/tree-ssa-forwprop.cc | 27 ++++++++++++++++++- 2 files changed, 50 insertions(+), 1 deletion(-) create mode 100644 gcc/testsuite/gcc.dg/tree-ssa/copy-prop-aggregate-union-1.c diff --git a/gcc/testsuite/gcc.dg/tree-ssa/copy-prop-aggregate-union-1.c b/gcc/testsuite/gcc.dg/tree-ssa/copy-prop-aggregate-union-1.c new file mode 100644 index 00000000000..206f6e1be55 --- /dev/null +++ b/gcc/testsuite/gcc.dg/tree-ssa/copy-prop-aggregate-union-1.c @@ -0,0 +1,24 @@ +/* { dg-do compile } */ +/* { dg-options "-O1 -fdump-tree-forwprop1-details" } */ +/* PR tree-optimization/107051 */ + + +union U2 { + unsigned f0; + char * f1; +}; + +/* Since g_284[0] and g_284[1] are known not overlap, + copy prop can happen. */ +union U2 g_284[2] = {{0UL},{0xC2488F72L}}; + +int e; +void func_1() { + union U2 c = {7}; + int *d[2]; + for (; e;) + *d[1] = 0; + g_284[0] = c = g_284[1]; +} + +/* { dg-final { scan-tree-dump-times "after previous" 1 "forwprop1" } } */ diff --git a/gcc/tree-ssa-forwprop.cc b/gcc/tree-ssa-forwprop.cc index e0f25a12f34..82344f4020d 100644 --- a/gcc/tree-ssa-forwprop.cc +++ b/gcc/tree-ssa-forwprop.cc @@ -1455,7 +1455,32 @@ optimize_agr_copyprop_1 (gimple *stmt, gimple *use_stmt, */ if (!operand_equal_p (dest2, src, 0) && !DECL_P (dest2) && !DECL_P (src)) - return false; + { + /* If *a and *b have the same base see if + the offset between the two is greater than + or equal to the size of the type. */ + poly_int64 offset1, offset2; + tree len = TYPE_SIZE_UNIT (TREE_TYPE (src)); + if (len == NULL_TREE + || !tree_fits_poly_int64_p (len)) + return false; + tree base1 = get_addr_base_and_unit_offset (dest2, &offset1); + if (!base1) + return false; + tree base2 = get_addr_base_and_unit_offset (src, &offset2); + if (!base2) + return false; + if (!operand_equal_p (base1, base2)) + return false; + poly_int64 size = tree_to_poly_int64 (len); + /* Make sure [offset1, offset1 + len - 1] does + not overlap with [offset2, offset2 + len - 1] + or overlaps fully. */ + if (!known_eq (offset2, offset1) + && !known_ge (offset2 - offset1, size) + && !known_ge (offset1 - offset2, size)) + return false; + } if (dump_file && (dump_flags & TDF_DETAILS)) { -- 2.43.0