diff mbox

Make SRA create statements with the correct alias type

Message ID 20140425133812.GG12215@virgil.suse
State New
Headers show

Commit Message

Martin Jambor April 25, 2014, 1:38 p.m. UTC
Hi,

the patch below is inspired by PR 57297 (the most relevant comments
are #4 and #5).  The problem is that currently SRA creates memory
loads and stores with alias type of whatever happens to be in
access->base.  However, at least when using placement or some nasty
type-casting, it is possible that the same aggregate, represented by
the same access structure, is accessed using different alias types in
one function.  This might lead to bogus memory access reordering, at
least in theory.  This patch therefore makes sure all SRA created
accesses have the same alias type as the load/store they originated
from.

Because load_assign_lhs_subreplacements did not look like it could
accept one more parameter, I encapsulated all of them in a structure.
I wrote this patch in December, I admit I don't remember what the new
testcase aims for, but I assume I added it for a reason :-)

Bootstrapped and tested on x86_64-linux.  OK for trunk?

Thanks,

Martin


2014-04-24  Martin Jambor  <mjambor@suse.cz>

	* tree-sra.c (sra_modify_expr): Generate new memory accesses with
	same alias type as the original statement.
	(subreplacement_assignment_data): New type.
	(handle_unscalarized_data_in_subtree): New type of parameter,
	generate new memory accesses with same alias type as the original
	statement.
	(load_assign_lhs_subreplacements): Likewise.
	(sra_modify_constructor_assign): Generate new memory accesses with
	same alias type as the original statement.

testsuite/
	* gcc.dg/tree-ssa/sra-14.c: New test.

Comments

Richard Biener April 28, 2014, 10:43 a.m. UTC | #1
On Fri, 25 Apr 2014, Martin Jambor wrote:

> Hi,
> 
> the patch below is inspired by PR 57297 (the most relevant comments
> are #4 and #5).  The problem is that currently SRA creates memory
> loads and stores with alias type of whatever happens to be in
> access->base.  However, at least when using placement or some nasty
> type-casting, it is possible that the same aggregate, represented by
> the same access structure, is accessed using different alias types in
> one function.  This might lead to bogus memory access reordering, at
> least in theory.  This patch therefore makes sure all SRA created
> accesses have the same alias type as the load/store they originated
> from.
> 
> Because load_assign_lhs_subreplacements did not look like it could
> accept one more parameter, I encapsulated all of them in a structure.
> I wrote this patch in December, I admit I don't remember what the new
> testcase aims for, but I assume I added it for a reason :-)
> 
> Bootstrapped and tested on x86_64-linux.  OK for trunk?

Ok.

Thanks,
Richard.

> Thanks,
> 
> Martin
> 
> 
> 2014-04-24  Martin Jambor  <mjambor@suse.cz>
> 
> 	* tree-sra.c (sra_modify_expr): Generate new memory accesses with
> 	same alias type as the original statement.
> 	(subreplacement_assignment_data): New type.
> 	(handle_unscalarized_data_in_subtree): New type of parameter,
> 	generate new memory accesses with same alias type as the original
> 	statement.
> 	(load_assign_lhs_subreplacements): Likewise.
> 	(sra_modify_constructor_assign): Generate new memory accesses with
> 	same alias type as the original statement.
> 
> testsuite/
> 	* gcc.dg/tree-ssa/sra-14.c: New test.
> 
> diff --git a/gcc/testsuite/gcc.dg/tree-ssa/sra-14.c b/gcc/testsuite/gcc.dg/tree-ssa/sra-14.c
> new file mode 100644
> index 0000000..6cbc0b4
> --- /dev/null
> +++ b/gcc/testsuite/gcc.dg/tree-ssa/sra-14.c
> @@ -0,0 +1,70 @@
> +/* { dg-do run } */
> +/* { dg-options "-O1" } */
> +
> +struct S
> +{
> +  int i, j;
> +};
> +
> +struct Z
> +{
> +  struct S d, s;
> +};
> +
> +struct S __attribute__ ((noinline, noclone))
> +get_s (void)
> +{
> +  struct S s;
> +  s.i = 5;
> +  s.j = 6;
> +
> +  return s;
> +}
> +
> +struct S __attribute__ ((noinline, noclone))
> +get_d (void)
> +{
> +  struct S d;
> +  d.i = 0;
> +  d.j = 0;
> +
> +  return d;
> +}
> +
> +int __attribute__ ((noinline, noclone))
> +get_c (void)
> +{
> +  return 1;
> +}
> +
> +int __attribute__ ((noinline, noclone))
> +my_nop (int i)
> +{
> +  return i;
> +}
> +
> +int __attribute__ ((noinline, noclone))
> +foo (void)
> +{
> +  struct Z z;
> +  int i, c = get_c ();
> +
> +  z.d = get_d ();
> +  z.s = get_s ();
> +
> +  for (i = 0; i < c; i++)
> +    {
> +      z.s.i = my_nop (z.s.i);
> +      z.s.j = my_nop (z.s.j);
> +    }
> +
> +  return z.s.i + z.s.j;
> +}
> +
> +int main (int argc, char *argv[])
> +{
> +  if (foo () != 11)
> +    __builtin_abort ();
> +  return 0;
> +}
> +
> diff --git a/gcc/tree-sra.c b/gcc/tree-sra.c
> index 49bbee3..4a24e6a 100644
> --- a/gcc/tree-sra.c
> +++ b/gcc/tree-sra.c
> @@ -2769,7 +2769,7 @@ sra_modify_expr (tree *expr, gimple_stmt_iterator *gsi, bool write)
>  {
>    location_t loc;
>    struct access *access;
> -  tree type, bfr;
> +  tree type, bfr, orig_expr;
>  
>    if (TREE_CODE (*expr) == BIT_FIELD_REF)
>      {
> @@ -2785,6 +2785,7 @@ sra_modify_expr (tree *expr, gimple_stmt_iterator *gsi, bool write)
>    if (!access)
>      return false;
>    type = TREE_TYPE (*expr);
> +  orig_expr = *expr;
>  
>    loc = gimple_location (gsi_stmt (*gsi));
>    gimple_stmt_iterator alt_gsi = gsi_none ();
> @@ -2811,8 +2812,7 @@ sra_modify_expr (tree *expr, gimple_stmt_iterator *gsi, bool write)
>  	{
>  	  tree ref;
>  
> -	  ref = build_ref_for_model (loc, access->base, access->offset, access,
> -				     NULL, false);
> +	  ref = build_ref_for_model (loc, orig_expr, 0, access, NULL, false);
>  
>  	  if (write)
>  	    {
> @@ -2863,7 +2863,7 @@ sra_modify_expr (tree *expr, gimple_stmt_iterator *gsi, bool write)
>        else
>  	start_offset = chunk_size = 0;
>  
> -      generate_subtree_copies (access->first_child, access->base, 0,
> +      generate_subtree_copies (access->first_child, orig_expr, access->offset,
>  			       start_offset, chunk_size, gsi, write, write,
>  			       loc);
>      }
> @@ -2877,53 +2877,70 @@ enum unscalarized_data_handling { SRA_UDH_NONE,  /* Nothing done so far. */
>  				  SRA_UDH_RIGHT, /* Data flushed to the RHS. */
>  				  SRA_UDH_LEFT }; /* Data flushed to the LHS. */
>  
> +struct subreplacement_assignment_data
> +{
> +  /* Offset of the access representing the lhs of the assignment.  */
> +  HOST_WIDE_INT left_offset;
> +
> +  /* LHS and RHS of the original assignment.  */
> +  tree assignment_lhs, assignment_rhs;
> +
> +  /* Access representing the rhs of the whole assignment.  */
> +  struct access *top_racc;
> +
> +  /* Stmt iterator used for statement insertions after the original assignment.
> +   It points to the main GSI used to traverse a BB during function body
> +   modification.  */
> +  gimple_stmt_iterator *new_gsi;
> +
> +  /* Stmt iterator used for statement insertions before the original
> +   assignment.  Keeps on pointing to the original statement.  */
> +  gimple_stmt_iterator old_gsi;
> +
> +  /* Location of the assignment.   */
> +  location_t loc;
> +
> +  /* Keeps the information whether we have needed to refresh replacements of
> +   the LHS and from which side of the assignments this takes place.  */
> +  enum unscalarized_data_handling refreshed;
> +};
> +
>  /* Store all replacements in the access tree rooted in TOP_RACC either to their
>     base aggregate if there are unscalarized data or directly to LHS of the
>     statement that is pointed to by GSI otherwise.  */
>  
> -static enum unscalarized_data_handling
> -handle_unscalarized_data_in_subtree (struct access *top_racc,
> -				     gimple_stmt_iterator *gsi)
> +static void
> +handle_unscalarized_data_in_subtree (struct subreplacement_assignment_data *sad)
>  {
> -  if (top_racc->grp_unscalarized_data)
> +  tree src;
> +  if (sad->top_racc->grp_unscalarized_data)
>      {
> -      generate_subtree_copies (top_racc->first_child, top_racc->base, 0, 0, 0,
> -			       gsi, false, false,
> -			       gimple_location (gsi_stmt (*gsi)));
> -      return SRA_UDH_RIGHT;
> +      src = sad->assignment_rhs;
> +      sad->refreshed = SRA_UDH_RIGHT;
>      }
>    else
>      {
> -      tree lhs = gimple_assign_lhs (gsi_stmt (*gsi));
> -      generate_subtree_copies (top_racc->first_child, lhs, top_racc->offset,
> -			       0, 0, gsi, false, false,
> -			       gimple_location (gsi_stmt (*gsi)));
> -      return SRA_UDH_LEFT;
> +      src = sad->assignment_lhs;
> +      sad->refreshed = SRA_UDH_LEFT;
>      }
> +  generate_subtree_copies (sad->top_racc->first_child, src,
> +			   sad->top_racc->offset, 0, 0,
> +			   &sad->old_gsi, false, false, sad->loc);
>  }
>  
> -
>  /* Try to generate statements to load all sub-replacements in an access subtree
> -   formed by children of LACC from scalar replacements in the TOP_RACC subtree.
> -   If that is not possible, refresh the TOP_RACC base aggregate and load the
> -   accesses from it.  LEFT_OFFSET is the offset of the left whole subtree being
> -   copied. NEW_GSI is stmt iterator used for statement insertions after the
> -   original assignment, OLD_GSI is used to insert statements before the
> -   assignment.  *REFRESHED keeps the information whether we have needed to
> -   refresh replacements of the LHS and from which side of the assignments this
> -   takes place.  */
> +   formed by children of LACC from scalar replacements in the SAD->top_racc
> +   subtree.  If that is not possible, refresh the SAD->top_racc base aggregate
> +   and load the accesses from it.  */
>  
>  static void
> -load_assign_lhs_subreplacements (struct access *lacc, struct access *top_racc,
> -				 HOST_WIDE_INT left_offset,
> -				 gimple_stmt_iterator *old_gsi,
> -				 gimple_stmt_iterator *new_gsi,
> -				 enum unscalarized_data_handling *refreshed)
> +load_assign_lhs_subreplacements (struct access *lacc,
> +				 struct subreplacement_assignment_data *sad)
>  {
> -  location_t loc = gimple_location (gsi_stmt (*old_gsi));
>    for (lacc = lacc->first_child; lacc; lacc = lacc->next_sibling)
>      {
> -      HOST_WIDE_INT offset = lacc->offset - left_offset + top_racc->offset;
> +      HOST_WIDE_INT offset;
> +      offset = lacc->offset - sad->left_offset + sad->top_racc->offset;
>  
>        if (lacc->grp_to_be_replaced)
>  	{
> @@ -2931,53 +2948,57 @@ load_assign_lhs_subreplacements (struct access *lacc, struct access *top_racc,
>  	  gimple stmt;
>  	  tree rhs;
>  
> -	  racc = find_access_in_subtree (top_racc, offset, lacc->size);
> +	  racc = find_access_in_subtree (sad->top_racc, offset, lacc->size);
>  	  if (racc && racc->grp_to_be_replaced)
>  	    {
>  	      rhs = get_access_replacement (racc);
>  	      if (!useless_type_conversion_p (lacc->type, racc->type))
> -		rhs = fold_build1_loc (loc, VIEW_CONVERT_EXPR, lacc->type, rhs);
> +		rhs = fold_build1_loc (sad->loc, VIEW_CONVERT_EXPR,
> +				       lacc->type, rhs);
>  
>  	      if (racc->grp_partial_lhs && lacc->grp_partial_lhs)
> -		rhs = force_gimple_operand_gsi (old_gsi, rhs, true, NULL_TREE,
> -						true, GSI_SAME_STMT);
> +		rhs = force_gimple_operand_gsi (&sad->old_gsi, rhs, true,
> +						NULL_TREE, true, GSI_SAME_STMT);
>  	    }
>  	  else
>  	    {
>  	      /* No suitable access on the right hand side, need to load from
>  		 the aggregate.  See if we have to update it first... */
> -	      if (*refreshed == SRA_UDH_NONE)
> -		*refreshed = handle_unscalarized_data_in_subtree (top_racc,
> -								  old_gsi);
> +	      if (sad->refreshed == SRA_UDH_NONE)
> +		handle_unscalarized_data_in_subtree (sad);
>  
> -	      if (*refreshed == SRA_UDH_LEFT)
> -		rhs = build_ref_for_model (loc, lacc->base, lacc->offset, lacc,
> -					    new_gsi, true);
> +	      if (sad->refreshed == SRA_UDH_LEFT)
> +		rhs = build_ref_for_model (sad->loc, sad->assignment_lhs,
> +					   lacc->offset - sad->left_offset,
> +					   lacc, sad->new_gsi, true);
>  	      else
> -		rhs = build_ref_for_model (loc, top_racc->base, offset, lacc,
> -					    new_gsi, true);
> +		rhs = build_ref_for_model (sad->loc, sad->assignment_rhs,
> +					   lacc->offset - sad->left_offset,
> +					   lacc, sad->new_gsi, true);
>  	      if (lacc->grp_partial_lhs)
> -		rhs = force_gimple_operand_gsi (new_gsi, rhs, true, NULL_TREE,
> +		rhs = force_gimple_operand_gsi (sad->new_gsi,
> +						rhs, true, NULL_TREE,
>  						false, GSI_NEW_STMT);
>  	    }
>  
>  	  stmt = gimple_build_assign (get_access_replacement (lacc), rhs);
> -	  gsi_insert_after (new_gsi, stmt, GSI_NEW_STMT);
> -	  gimple_set_location (stmt, loc);
> +	  gsi_insert_after (sad->new_gsi, stmt, GSI_NEW_STMT);
> +	  gimple_set_location (stmt, sad->loc);
>  	  update_stmt (stmt);
>  	  sra_stats.subreplacements++;
>  	}
>        else
>  	{
> -	  if (*refreshed == SRA_UDH_NONE
> +	  if (sad->refreshed == SRA_UDH_NONE
>  	      && lacc->grp_read && !lacc->grp_covered)
> -	    *refreshed = handle_unscalarized_data_in_subtree (top_racc,
> -							      old_gsi);
> +	    handle_unscalarized_data_in_subtree (sad);
> +
>  	  if (lacc && lacc->grp_to_be_debug_replaced)
>  	    {
>  	      gimple ds;
>  	      tree drhs;
> -	      struct access *racc = find_access_in_subtree (top_racc, offset,
> +	      struct access *racc = find_access_in_subtree (sad->top_racc,
> +							    offset,
>  							    lacc->size);
>  
>  	      if (racc && racc->grp_to_be_replaced)
> @@ -2987,27 +3008,26 @@ load_assign_lhs_subreplacements (struct access *lacc, struct access *top_racc,
>  		  else
>  		    drhs = NULL;
>  		}
> -	      else if (*refreshed == SRA_UDH_LEFT)
> -		drhs = build_debug_ref_for_model (loc, lacc->base, lacc->offset,
> -						  lacc);
> -	      else if (*refreshed == SRA_UDH_RIGHT)
> -		drhs = build_debug_ref_for_model (loc, top_racc->base, offset,
> -						  lacc);
> +	      else if (sad->refreshed == SRA_UDH_LEFT)
> +		drhs = build_debug_ref_for_model (sad->loc, lacc->base,
> +						  lacc->offset, lacc);
> +	      else if (sad->refreshed == SRA_UDH_RIGHT)
> +		drhs = build_debug_ref_for_model (sad->loc, sad->top_racc->base,
> +						  offset, lacc);
>  	      else
>  		drhs = NULL_TREE;
>  	      if (drhs
>  		  && !useless_type_conversion_p (lacc->type, TREE_TYPE (drhs)))
> -		drhs = fold_build1_loc (loc, VIEW_CONVERT_EXPR,
> +		drhs = fold_build1_loc (sad->loc, VIEW_CONVERT_EXPR,
>  					lacc->type, drhs);
>  	      ds = gimple_build_debug_bind (get_access_replacement (lacc),
> -					    drhs, gsi_stmt (*old_gsi));
> -	      gsi_insert_after (new_gsi, ds, GSI_NEW_STMT);
> +					    drhs, gsi_stmt (sad->old_gsi));
> +	      gsi_insert_after (sad->new_gsi, ds, GSI_NEW_STMT);
>  	    }
>  	}
>  
>        if (lacc->first_child)
> -	load_assign_lhs_subreplacements (lacc, top_racc, left_offset,
> -					 old_gsi, new_gsi, refreshed);
> +	load_assign_lhs_subreplacements (lacc, sad);
>      }
>  }
>  
> @@ -3053,7 +3073,7 @@ sra_modify_constructor_assign (gimple *stmt, gimple_stmt_iterator *gsi)
>        /* I have never seen this code path trigger but if it can happen the
>  	 following should handle it gracefully.  */
>        if (access_has_children_p (acc))
> -	generate_subtree_copies (acc->first_child, acc->base, 0, 0, 0, gsi,
> +	generate_subtree_copies (acc->first_child, lhs, acc->offset, 0, 0, gsi,
>  				 true, true, loc);
>        return SRA_AM_MODIFIED;
>      }
> @@ -3261,7 +3281,7 @@ sra_modify_assign (gimple *stmt, gimple_stmt_iterator *gsi)
>        || stmt_ends_bb_p (*stmt))
>      {
>        if (access_has_children_p (racc))
> -	generate_subtree_copies (racc->first_child, racc->base, 0, 0, 0,
> +	generate_subtree_copies (racc->first_child, rhs, racc->offset, 0, 0,
>  				 gsi, false, false, loc);
>        if (access_has_children_p (lacc))
>  	{
> @@ -3271,7 +3291,7 @@ sra_modify_assign (gimple *stmt, gimple_stmt_iterator *gsi)
>  	      alt_gsi = gsi_start_edge (single_non_eh_succ (gsi_bb (*gsi)));
>  	      gsi = &alt_gsi;
>  	    }
> -	  generate_subtree_copies (lacc->first_child, lacc->base, 0, 0, 0,
> +	  generate_subtree_copies (lacc->first_child, lhs, lacc->offset, 0, 0,
>  				   gsi, true, true, loc);
>  	}
>        sra_stats.separate_lhs_rhs_handling++;
> @@ -3301,21 +3321,26 @@ sra_modify_assign (gimple *stmt, gimple_stmt_iterator *gsi)
>  	  && !lacc->grp_unscalarizable_region
>  	  && !racc->grp_unscalarizable_region)
>  	{
> -	  gimple_stmt_iterator orig_gsi = *gsi;
> -	  enum unscalarized_data_handling refreshed;
> +	  struct subreplacement_assignment_data sad;
> +
> +	  sad.left_offset = lacc->offset;
> +	  sad.assignment_lhs = lhs;
> +	  sad.assignment_rhs = rhs;
> +	  sad.top_racc = racc;
> +	  sad.old_gsi = *gsi;
> +	  sad.new_gsi = gsi;
> +	  sad.loc = gimple_location (*stmt);
> +	  sad.refreshed = SRA_UDH_NONE;
>  
>  	  if (lacc->grp_read && !lacc->grp_covered)
> -	    refreshed = handle_unscalarized_data_in_subtree (racc, gsi);
> -	  else
> -	    refreshed = SRA_UDH_NONE;
> +	    handle_unscalarized_data_in_subtree (&sad);
>  
> -	  load_assign_lhs_subreplacements (lacc, racc, lacc->offset,
> -					   &orig_gsi, gsi, &refreshed);
> -	  if (refreshed != SRA_UDH_RIGHT)
> +	  load_assign_lhs_subreplacements (lacc, &sad);
> +	  if (sad.refreshed != SRA_UDH_RIGHT)
>  	    {
>  	      gsi_next (gsi);
>  	      unlink_stmt_vdef (*stmt);
> -	      gsi_remove (&orig_gsi, true);
> +	      gsi_remove (&sad.old_gsi, true);
>  	      release_defs (*stmt);
>  	      sra_stats.deleted++;
>  	      return SRA_AM_REMOVED;
> @@ -3344,7 +3369,7 @@ sra_modify_assign (gimple *stmt, gimple_stmt_iterator *gsi)
>  	  /* Restore the aggregate RHS from its components so the
>  	     prevailing aggregate copy does the right thing.  */
>  	  if (access_has_children_p (racc))
> -	    generate_subtree_copies (racc->first_child, racc->base, 0, 0, 0,
> +	    generate_subtree_copies (racc->first_child, rhs, racc->offset, 0, 0,
>  				     gsi, false, false, loc);
>  	  /* Re-load the components of the aggregate copy destination.
>  	     But use the RHS aggregate to load from to expose more
> 
> 
>
diff mbox

Patch

diff --git a/gcc/testsuite/gcc.dg/tree-ssa/sra-14.c b/gcc/testsuite/gcc.dg/tree-ssa/sra-14.c
new file mode 100644
index 0000000..6cbc0b4
--- /dev/null
+++ b/gcc/testsuite/gcc.dg/tree-ssa/sra-14.c
@@ -0,0 +1,70 @@ 
+/* { dg-do run } */
+/* { dg-options "-O1" } */
+
+struct S
+{
+  int i, j;
+};
+
+struct Z
+{
+  struct S d, s;
+};
+
+struct S __attribute__ ((noinline, noclone))
+get_s (void)
+{
+  struct S s;
+  s.i = 5;
+  s.j = 6;
+
+  return s;
+}
+
+struct S __attribute__ ((noinline, noclone))
+get_d (void)
+{
+  struct S d;
+  d.i = 0;
+  d.j = 0;
+
+  return d;
+}
+
+int __attribute__ ((noinline, noclone))
+get_c (void)
+{
+  return 1;
+}
+
+int __attribute__ ((noinline, noclone))
+my_nop (int i)
+{
+  return i;
+}
+
+int __attribute__ ((noinline, noclone))
+foo (void)
+{
+  struct Z z;
+  int i, c = get_c ();
+
+  z.d = get_d ();
+  z.s = get_s ();
+
+  for (i = 0; i < c; i++)
+    {
+      z.s.i = my_nop (z.s.i);
+      z.s.j = my_nop (z.s.j);
+    }
+
+  return z.s.i + z.s.j;
+}
+
+int main (int argc, char *argv[])
+{
+  if (foo () != 11)
+    __builtin_abort ();
+  return 0;
+}
+
diff --git a/gcc/tree-sra.c b/gcc/tree-sra.c
index 49bbee3..4a24e6a 100644
--- a/gcc/tree-sra.c
+++ b/gcc/tree-sra.c
@@ -2769,7 +2769,7 @@  sra_modify_expr (tree *expr, gimple_stmt_iterator *gsi, bool write)
 {
   location_t loc;
   struct access *access;
-  tree type, bfr;
+  tree type, bfr, orig_expr;
 
   if (TREE_CODE (*expr) == BIT_FIELD_REF)
     {
@@ -2785,6 +2785,7 @@  sra_modify_expr (tree *expr, gimple_stmt_iterator *gsi, bool write)
   if (!access)
     return false;
   type = TREE_TYPE (*expr);
+  orig_expr = *expr;
 
   loc = gimple_location (gsi_stmt (*gsi));
   gimple_stmt_iterator alt_gsi = gsi_none ();
@@ -2811,8 +2812,7 @@  sra_modify_expr (tree *expr, gimple_stmt_iterator *gsi, bool write)
 	{
 	  tree ref;
 
-	  ref = build_ref_for_model (loc, access->base, access->offset, access,
-				     NULL, false);
+	  ref = build_ref_for_model (loc, orig_expr, 0, access, NULL, false);
 
 	  if (write)
 	    {
@@ -2863,7 +2863,7 @@  sra_modify_expr (tree *expr, gimple_stmt_iterator *gsi, bool write)
       else
 	start_offset = chunk_size = 0;
 
-      generate_subtree_copies (access->first_child, access->base, 0,
+      generate_subtree_copies (access->first_child, orig_expr, access->offset,
 			       start_offset, chunk_size, gsi, write, write,
 			       loc);
     }
@@ -2877,53 +2877,70 @@  enum unscalarized_data_handling { SRA_UDH_NONE,  /* Nothing done so far. */
 				  SRA_UDH_RIGHT, /* Data flushed to the RHS. */
 				  SRA_UDH_LEFT }; /* Data flushed to the LHS. */
 
+struct subreplacement_assignment_data
+{
+  /* Offset of the access representing the lhs of the assignment.  */
+  HOST_WIDE_INT left_offset;
+
+  /* LHS and RHS of the original assignment.  */
+  tree assignment_lhs, assignment_rhs;
+
+  /* Access representing the rhs of the whole assignment.  */
+  struct access *top_racc;
+
+  /* Stmt iterator used for statement insertions after the original assignment.
+   It points to the main GSI used to traverse a BB during function body
+   modification.  */
+  gimple_stmt_iterator *new_gsi;
+
+  /* Stmt iterator used for statement insertions before the original
+   assignment.  Keeps on pointing to the original statement.  */
+  gimple_stmt_iterator old_gsi;
+
+  /* Location of the assignment.   */
+  location_t loc;
+
+  /* Keeps the information whether we have needed to refresh replacements of
+   the LHS and from which side of the assignments this takes place.  */
+  enum unscalarized_data_handling refreshed;
+};
+
 /* Store all replacements in the access tree rooted in TOP_RACC either to their
    base aggregate if there are unscalarized data or directly to LHS of the
    statement that is pointed to by GSI otherwise.  */
 
-static enum unscalarized_data_handling
-handle_unscalarized_data_in_subtree (struct access *top_racc,
-				     gimple_stmt_iterator *gsi)
+static void
+handle_unscalarized_data_in_subtree (struct subreplacement_assignment_data *sad)
 {
-  if (top_racc->grp_unscalarized_data)
+  tree src;
+  if (sad->top_racc->grp_unscalarized_data)
     {
-      generate_subtree_copies (top_racc->first_child, top_racc->base, 0, 0, 0,
-			       gsi, false, false,
-			       gimple_location (gsi_stmt (*gsi)));
-      return SRA_UDH_RIGHT;
+      src = sad->assignment_rhs;
+      sad->refreshed = SRA_UDH_RIGHT;
     }
   else
     {
-      tree lhs = gimple_assign_lhs (gsi_stmt (*gsi));
-      generate_subtree_copies (top_racc->first_child, lhs, top_racc->offset,
-			       0, 0, gsi, false, false,
-			       gimple_location (gsi_stmt (*gsi)));
-      return SRA_UDH_LEFT;
+      src = sad->assignment_lhs;
+      sad->refreshed = SRA_UDH_LEFT;
     }
+  generate_subtree_copies (sad->top_racc->first_child, src,
+			   sad->top_racc->offset, 0, 0,
+			   &sad->old_gsi, false, false, sad->loc);
 }
 
-
 /* Try to generate statements to load all sub-replacements in an access subtree
-   formed by children of LACC from scalar replacements in the TOP_RACC subtree.
-   If that is not possible, refresh the TOP_RACC base aggregate and load the
-   accesses from it.  LEFT_OFFSET is the offset of the left whole subtree being
-   copied. NEW_GSI is stmt iterator used for statement insertions after the
-   original assignment, OLD_GSI is used to insert statements before the
-   assignment.  *REFRESHED keeps the information whether we have needed to
-   refresh replacements of the LHS and from which side of the assignments this
-   takes place.  */
+   formed by children of LACC from scalar replacements in the SAD->top_racc
+   subtree.  If that is not possible, refresh the SAD->top_racc base aggregate
+   and load the accesses from it.  */
 
 static void
-load_assign_lhs_subreplacements (struct access *lacc, struct access *top_racc,
-				 HOST_WIDE_INT left_offset,
-				 gimple_stmt_iterator *old_gsi,
-				 gimple_stmt_iterator *new_gsi,
-				 enum unscalarized_data_handling *refreshed)
+load_assign_lhs_subreplacements (struct access *lacc,
+				 struct subreplacement_assignment_data *sad)
 {
-  location_t loc = gimple_location (gsi_stmt (*old_gsi));
   for (lacc = lacc->first_child; lacc; lacc = lacc->next_sibling)
     {
-      HOST_WIDE_INT offset = lacc->offset - left_offset + top_racc->offset;
+      HOST_WIDE_INT offset;
+      offset = lacc->offset - sad->left_offset + sad->top_racc->offset;
 
       if (lacc->grp_to_be_replaced)
 	{
@@ -2931,53 +2948,57 @@  load_assign_lhs_subreplacements (struct access *lacc, struct access *top_racc,
 	  gimple stmt;
 	  tree rhs;
 
-	  racc = find_access_in_subtree (top_racc, offset, lacc->size);
+	  racc = find_access_in_subtree (sad->top_racc, offset, lacc->size);
 	  if (racc && racc->grp_to_be_replaced)
 	    {
 	      rhs = get_access_replacement (racc);
 	      if (!useless_type_conversion_p (lacc->type, racc->type))
-		rhs = fold_build1_loc (loc, VIEW_CONVERT_EXPR, lacc->type, rhs);
+		rhs = fold_build1_loc (sad->loc, VIEW_CONVERT_EXPR,
+				       lacc->type, rhs);
 
 	      if (racc->grp_partial_lhs && lacc->grp_partial_lhs)
-		rhs = force_gimple_operand_gsi (old_gsi, rhs, true, NULL_TREE,
-						true, GSI_SAME_STMT);
+		rhs = force_gimple_operand_gsi (&sad->old_gsi, rhs, true,
+						NULL_TREE, true, GSI_SAME_STMT);
 	    }
 	  else
 	    {
 	      /* No suitable access on the right hand side, need to load from
 		 the aggregate.  See if we have to update it first... */
-	      if (*refreshed == SRA_UDH_NONE)
-		*refreshed = handle_unscalarized_data_in_subtree (top_racc,
-								  old_gsi);
+	      if (sad->refreshed == SRA_UDH_NONE)
+		handle_unscalarized_data_in_subtree (sad);
 
-	      if (*refreshed == SRA_UDH_LEFT)
-		rhs = build_ref_for_model (loc, lacc->base, lacc->offset, lacc,
-					    new_gsi, true);
+	      if (sad->refreshed == SRA_UDH_LEFT)
+		rhs = build_ref_for_model (sad->loc, sad->assignment_lhs,
+					   lacc->offset - sad->left_offset,
+					   lacc, sad->new_gsi, true);
 	      else
-		rhs = build_ref_for_model (loc, top_racc->base, offset, lacc,
-					    new_gsi, true);
+		rhs = build_ref_for_model (sad->loc, sad->assignment_rhs,
+					   lacc->offset - sad->left_offset,
+					   lacc, sad->new_gsi, true);
 	      if (lacc->grp_partial_lhs)
-		rhs = force_gimple_operand_gsi (new_gsi, rhs, true, NULL_TREE,
+		rhs = force_gimple_operand_gsi (sad->new_gsi,
+						rhs, true, NULL_TREE,
 						false, GSI_NEW_STMT);
 	    }
 
 	  stmt = gimple_build_assign (get_access_replacement (lacc), rhs);
-	  gsi_insert_after (new_gsi, stmt, GSI_NEW_STMT);
-	  gimple_set_location (stmt, loc);
+	  gsi_insert_after (sad->new_gsi, stmt, GSI_NEW_STMT);
+	  gimple_set_location (stmt, sad->loc);
 	  update_stmt (stmt);
 	  sra_stats.subreplacements++;
 	}
       else
 	{
-	  if (*refreshed == SRA_UDH_NONE
+	  if (sad->refreshed == SRA_UDH_NONE
 	      && lacc->grp_read && !lacc->grp_covered)
-	    *refreshed = handle_unscalarized_data_in_subtree (top_racc,
-							      old_gsi);
+	    handle_unscalarized_data_in_subtree (sad);
+
 	  if (lacc && lacc->grp_to_be_debug_replaced)
 	    {
 	      gimple ds;
 	      tree drhs;
-	      struct access *racc = find_access_in_subtree (top_racc, offset,
+	      struct access *racc = find_access_in_subtree (sad->top_racc,
+							    offset,
 							    lacc->size);
 
 	      if (racc && racc->grp_to_be_replaced)
@@ -2987,27 +3008,26 @@  load_assign_lhs_subreplacements (struct access *lacc, struct access *top_racc,
 		  else
 		    drhs = NULL;
 		}
-	      else if (*refreshed == SRA_UDH_LEFT)
-		drhs = build_debug_ref_for_model (loc, lacc->base, lacc->offset,
-						  lacc);
-	      else if (*refreshed == SRA_UDH_RIGHT)
-		drhs = build_debug_ref_for_model (loc, top_racc->base, offset,
-						  lacc);
+	      else if (sad->refreshed == SRA_UDH_LEFT)
+		drhs = build_debug_ref_for_model (sad->loc, lacc->base,
+						  lacc->offset, lacc);
+	      else if (sad->refreshed == SRA_UDH_RIGHT)
+		drhs = build_debug_ref_for_model (sad->loc, sad->top_racc->base,
+						  offset, lacc);
 	      else
 		drhs = NULL_TREE;
 	      if (drhs
 		  && !useless_type_conversion_p (lacc->type, TREE_TYPE (drhs)))
-		drhs = fold_build1_loc (loc, VIEW_CONVERT_EXPR,
+		drhs = fold_build1_loc (sad->loc, VIEW_CONVERT_EXPR,
 					lacc->type, drhs);
 	      ds = gimple_build_debug_bind (get_access_replacement (lacc),
-					    drhs, gsi_stmt (*old_gsi));
-	      gsi_insert_after (new_gsi, ds, GSI_NEW_STMT);
+					    drhs, gsi_stmt (sad->old_gsi));
+	      gsi_insert_after (sad->new_gsi, ds, GSI_NEW_STMT);
 	    }
 	}
 
       if (lacc->first_child)
-	load_assign_lhs_subreplacements (lacc, top_racc, left_offset,
-					 old_gsi, new_gsi, refreshed);
+	load_assign_lhs_subreplacements (lacc, sad);
     }
 }
 
@@ -3053,7 +3073,7 @@  sra_modify_constructor_assign (gimple *stmt, gimple_stmt_iterator *gsi)
       /* I have never seen this code path trigger but if it can happen the
 	 following should handle it gracefully.  */
       if (access_has_children_p (acc))
-	generate_subtree_copies (acc->first_child, acc->base, 0, 0, 0, gsi,
+	generate_subtree_copies (acc->first_child, lhs, acc->offset, 0, 0, gsi,
 				 true, true, loc);
       return SRA_AM_MODIFIED;
     }
@@ -3261,7 +3281,7 @@  sra_modify_assign (gimple *stmt, gimple_stmt_iterator *gsi)
       || stmt_ends_bb_p (*stmt))
     {
       if (access_has_children_p (racc))
-	generate_subtree_copies (racc->first_child, racc->base, 0, 0, 0,
+	generate_subtree_copies (racc->first_child, rhs, racc->offset, 0, 0,
 				 gsi, false, false, loc);
       if (access_has_children_p (lacc))
 	{
@@ -3271,7 +3291,7 @@  sra_modify_assign (gimple *stmt, gimple_stmt_iterator *gsi)
 	      alt_gsi = gsi_start_edge (single_non_eh_succ (gsi_bb (*gsi)));
 	      gsi = &alt_gsi;
 	    }
-	  generate_subtree_copies (lacc->first_child, lacc->base, 0, 0, 0,
+	  generate_subtree_copies (lacc->first_child, lhs, lacc->offset, 0, 0,
 				   gsi, true, true, loc);
 	}
       sra_stats.separate_lhs_rhs_handling++;
@@ -3301,21 +3321,26 @@  sra_modify_assign (gimple *stmt, gimple_stmt_iterator *gsi)
 	  && !lacc->grp_unscalarizable_region
 	  && !racc->grp_unscalarizable_region)
 	{
-	  gimple_stmt_iterator orig_gsi = *gsi;
-	  enum unscalarized_data_handling refreshed;
+	  struct subreplacement_assignment_data sad;
+
+	  sad.left_offset = lacc->offset;
+	  sad.assignment_lhs = lhs;
+	  sad.assignment_rhs = rhs;
+	  sad.top_racc = racc;
+	  sad.old_gsi = *gsi;
+	  sad.new_gsi = gsi;
+	  sad.loc = gimple_location (*stmt);
+	  sad.refreshed = SRA_UDH_NONE;
 
 	  if (lacc->grp_read && !lacc->grp_covered)
-	    refreshed = handle_unscalarized_data_in_subtree (racc, gsi);
-	  else
-	    refreshed = SRA_UDH_NONE;
+	    handle_unscalarized_data_in_subtree (&sad);
 
-	  load_assign_lhs_subreplacements (lacc, racc, lacc->offset,
-					   &orig_gsi, gsi, &refreshed);
-	  if (refreshed != SRA_UDH_RIGHT)
+	  load_assign_lhs_subreplacements (lacc, &sad);
+	  if (sad.refreshed != SRA_UDH_RIGHT)
 	    {
 	      gsi_next (gsi);
 	      unlink_stmt_vdef (*stmt);
-	      gsi_remove (&orig_gsi, true);
+	      gsi_remove (&sad.old_gsi, true);
 	      release_defs (*stmt);
 	      sra_stats.deleted++;
 	      return SRA_AM_REMOVED;
@@ -3344,7 +3369,7 @@  sra_modify_assign (gimple *stmt, gimple_stmt_iterator *gsi)
 	  /* Restore the aggregate RHS from its components so the
 	     prevailing aggregate copy does the right thing.  */
 	  if (access_has_children_p (racc))
-	    generate_subtree_copies (racc->first_child, racc->base, 0, 0, 0,
+	    generate_subtree_copies (racc->first_child, rhs, racc->offset, 0, 0,
 				     gsi, false, false, loc);
 	  /* Re-load the components of the aggregate copy destination.
 	     But use the RHS aggregate to load from to expose more