Jakub noticed that we don't forward &a[i] into a dereference of said pointer when the resulting struct is partially accessed. I suppose I just didn't implement that initially because I was lazy. It turns out to be comparatively easy to do though.
Bootstrapped and tested on x86_64-unknown-linux-gnu, applied to trunk. Richard. 2013-09-12 Richard Biener <rguent...@suse.de> PR tree-optimization/58404 * tree-ssa-forwprop.c (forward_propagate_addr_expr_1): Also propagate non-invariant addresses into dereferences wrapped in component references. * g++.dg/tree-ssa/pr58404.C: New testcase. Index: gcc/tree-ssa-forwprop.c =================================================================== *** gcc/tree-ssa-forwprop.c (revision 202515) --- gcc/tree-ssa-forwprop.c (working copy) *************** forward_propagate_addr_expr_1 (tree name *** 786,794 **** /* Now strip away any outer COMPONENT_REF/ARRAY_REF nodes from the LHS. ADDR_EXPR will not appear on the LHS. */ ! lhs = gimple_assign_lhs (use_stmt); ! while (handled_component_p (lhs)) ! lhs = TREE_OPERAND (lhs, 0); /* Now see if the LHS node is a MEM_REF using NAME. If so, propagate the ADDR_EXPR into the use of NAME and fold the result. */ --- 786,795 ---- /* Now strip away any outer COMPONENT_REF/ARRAY_REF nodes from the LHS. ADDR_EXPR will not appear on the LHS. */ ! tree *lhsp = gimple_assign_lhs_ptr (use_stmt); ! while (handled_component_p (*lhsp)) ! lhsp = &TREE_OPERAND (*lhsp, 0); ! lhs = *lhsp; /* Now see if the LHS node is a MEM_REF using NAME. If so, propagate the ADDR_EXPR into the use of NAME and fold the result. */ *************** forward_propagate_addr_expr_1 (tree name *** 822,832 **** /* If the LHS is a plain dereference and the value type is the same as that of the pointed-to type of the address we can put the dereferenced address on the LHS preserving the original alias-type. */ ! else if (gimple_assign_lhs (use_stmt) == lhs ! && integer_zerop (TREE_OPERAND (lhs, 1)) ! && useless_type_conversion_p ! (TREE_TYPE (TREE_OPERAND (def_rhs, 0)), ! TREE_TYPE (gimple_assign_rhs1 (use_stmt))) /* Don't forward anything into clobber stmts if it would result in the lhs no longer being a MEM_REF. */ && (!gimple_clobber_p (use_stmt) --- 823,835 ---- /* If the LHS is a plain dereference and the value type is the same as that of the pointed-to type of the address we can put the dereferenced address on the LHS preserving the original alias-type. */ ! else if (integer_zerop (TREE_OPERAND (lhs, 1)) ! && ((gimple_assign_lhs (use_stmt) == lhs ! && useless_type_conversion_p ! (TREE_TYPE (TREE_OPERAND (def_rhs, 0)), ! TREE_TYPE (gimple_assign_rhs1 (use_stmt)))) ! || types_compatible_p (TREE_TYPE (lhs), ! TREE_TYPE (TREE_OPERAND (def_rhs, 0)))) /* Don't forward anything into clobber stmts if it would result in the lhs no longer being a MEM_REF. */ && (!gimple_clobber_p (use_stmt) *************** forward_propagate_addr_expr_1 (tree name *** 854,860 **** TREE_SIDE_EFFECTS (*def_rhs_basep) = TREE_SIDE_EFFECTS (lhs); TREE_THIS_NOTRAP (*def_rhs_basep) = TREE_THIS_NOTRAP (lhs); new_lhs = unshare_expr (TREE_OPERAND (def_rhs, 0)); ! gimple_assign_set_lhs (use_stmt, new_lhs); TREE_THIS_VOLATILE (new_lhs) = TREE_THIS_VOLATILE (lhs); TREE_SIDE_EFFECTS (new_lhs) = TREE_SIDE_EFFECTS (lhs); *def_rhs_basep = saved; --- 857,863 ---- TREE_SIDE_EFFECTS (*def_rhs_basep) = TREE_SIDE_EFFECTS (lhs); TREE_THIS_NOTRAP (*def_rhs_basep) = TREE_THIS_NOTRAP (lhs); new_lhs = unshare_expr (TREE_OPERAND (def_rhs, 0)); ! *lhsp = new_lhs; TREE_THIS_VOLATILE (new_lhs) = TREE_THIS_VOLATILE (lhs); TREE_SIDE_EFFECTS (new_lhs) = TREE_SIDE_EFFECTS (lhs); *def_rhs_basep = saved; *************** forward_propagate_addr_expr_1 (tree name *** 873,883 **** /* Strip away any outer COMPONENT_REF, ARRAY_REF or ADDR_EXPR nodes from the RHS. */ ! rhs = gimple_assign_rhs1 (use_stmt); ! if (TREE_CODE (rhs) == ADDR_EXPR) ! rhs = TREE_OPERAND (rhs, 0); ! while (handled_component_p (rhs)) ! rhs = TREE_OPERAND (rhs, 0); /* Now see if the RHS node is a MEM_REF using NAME. If so, propagate the ADDR_EXPR into the use of NAME and fold the result. */ --- 876,887 ---- /* Strip away any outer COMPONENT_REF, ARRAY_REF or ADDR_EXPR nodes from the RHS. */ ! tree *rhsp = gimple_assign_rhs1_ptr (use_stmt); ! if (TREE_CODE (*rhsp) == ADDR_EXPR) ! rhsp = &TREE_OPERAND (*rhsp, 0); ! while (handled_component_p (*rhsp)) ! rhsp = &TREE_OPERAND (*rhsp, 0); ! rhs = *rhsp; /* Now see if the RHS node is a MEM_REF using NAME. If so, propagate the ADDR_EXPR into the use of NAME and fold the result. */ *************** forward_propagate_addr_expr_1 (tree name *** 909,919 **** /* If the RHS is a plain dereference and the value type is the same as that of the pointed-to type of the address we can put the dereferenced address on the RHS preserving the original alias-type. */ ! else if (gimple_assign_rhs1 (use_stmt) == rhs ! && integer_zerop (TREE_OPERAND (rhs, 1)) ! && useless_type_conversion_p ! (TREE_TYPE (gimple_assign_lhs (use_stmt)), ! TREE_TYPE (TREE_OPERAND (def_rhs, 0)))) { tree *def_rhs_basep = &TREE_OPERAND (def_rhs, 0); tree new_offset, new_base, saved, new_rhs; --- 913,925 ---- /* If the RHS is a plain dereference and the value type is the same as that of the pointed-to type of the address we can put the dereferenced address on the RHS preserving the original alias-type. */ ! else if (integer_zerop (TREE_OPERAND (rhs, 1)) ! && ((gimple_assign_rhs1 (use_stmt) == rhs ! && useless_type_conversion_p ! (TREE_TYPE (gimple_assign_lhs (use_stmt)), ! TREE_TYPE (TREE_OPERAND (def_rhs, 0)))) ! || types_compatible_p (TREE_TYPE (rhs), ! TREE_TYPE (TREE_OPERAND (def_rhs, 0))))) { tree *def_rhs_basep = &TREE_OPERAND (def_rhs, 0); tree new_offset, new_base, saved, new_rhs; *************** forward_propagate_addr_expr_1 (tree name *** 937,943 **** TREE_SIDE_EFFECTS (*def_rhs_basep) = TREE_SIDE_EFFECTS (rhs); TREE_THIS_NOTRAP (*def_rhs_basep) = TREE_THIS_NOTRAP (rhs); new_rhs = unshare_expr (TREE_OPERAND (def_rhs, 0)); ! gimple_assign_set_rhs1 (use_stmt, new_rhs); TREE_THIS_VOLATILE (new_rhs) = TREE_THIS_VOLATILE (rhs); TREE_SIDE_EFFECTS (new_rhs) = TREE_SIDE_EFFECTS (rhs); *def_rhs_basep = saved; --- 943,949 ---- TREE_SIDE_EFFECTS (*def_rhs_basep) = TREE_SIDE_EFFECTS (rhs); TREE_THIS_NOTRAP (*def_rhs_basep) = TREE_THIS_NOTRAP (rhs); new_rhs = unshare_expr (TREE_OPERAND (def_rhs, 0)); ! *rhsp = new_rhs; TREE_THIS_VOLATILE (new_rhs) = TREE_THIS_VOLATILE (rhs); TREE_SIDE_EFFECTS (new_rhs) = TREE_SIDE_EFFECTS (rhs); *def_rhs_basep = saved; Index: gcc/testsuite/g++.dg/tree-ssa/pr58404.C =================================================================== *** gcc/testsuite/g++.dg/tree-ssa/pr58404.C (revision 0) --- gcc/testsuite/g++.dg/tree-ssa/pr58404.C (working copy) *************** *** 0 **** --- 1,20 ---- + // { dg-do compile } + // { dg-options "-O -fdump-tree-cddce1" } + + struct S { int s; }; + S a[1024]; + + void + foo () + { + for (int i = 0; i < 1024; i++) + { + S &r = a[i]; + r.s++; + } + } + + // We should propagate the reference into both memory accesses + // during the first forwprop pass + // { dg-final { scan-tree-dump-times "= &a" 0 "cddce1" } } + // { dg-final { cleanup-tree-dump "cddce1" } }