diff mbox series

[3/4] SRA: Also propagate accesses from LHS to RHS [PR92706]

Message ID ri6y2tsa0pm.fsf@suse.cz
State New
Headers show
Series [1/4] SRA: Add verification of accesses | expand

Commit Message

Martin Jambor Jan. 28, 2020, 12:23 a.m. UTC
Hi,

the previous patch unfortunately does not fix the first testcase in PR
92706 and since I am afraid it might be the important one, I also
focused on that.  The issue here is again total scalarization accesses
clashing with those representing accesses in the IL - on another
aggregate but here the sides are switched.  Whereas in the previous
case the total scalarization accesses prevented propagation along
assignments, here we have the user accesses on the LHS, so even though
we do not create anything there, we totally scalarize the RHS and
again end up with assignments with different scalarizations leading to
bad code.

So we clearly need to propagate information about accesses from RHSs
to LHSs too, which the patch below does.  Because the intent is only
preventing bad total scalarization - which the last patch now performs
late enough - and do not care about grp_write flag and so forth, the
propagation is a bit simpler and so I did not try to unify all of the
code for both directions.

More information and some discussion is in the thread from the initial
submission, the code has not changed in any (substantial) way.  See
https://gcc.gnu.org/ml/gcc-patches/2019-12/msg01184.html and
https://gcc.gnu.org/ml/gcc-patches/2020-01/msg00698.html.

Bootstrapped and tested on x86_64-linux.

Thanks,

Martin

2019-12-11  Martin Jambor  <mjambor@suse.cz>

	PR tree-optimization/92706
	* tree-sra.c (struct access): Fields first_link, last_link,
	next_queued and grp_queued renamed to first_rhs_link, last_rhs_link,
	next_rhs_queued and grp_rhs_queued respectively, new fields
	first_lhs_link, last_lhs_link, next_lhs_queued and grp_lhs_queued.
	(struct assign_link): Field next renamed to next_rhs, new field
	next_lhs.  Updated comment.
	(work_queue_head): Renamed to rhs_work_queue_head.
	(lhs_work_queue_head): New variable.
	(add_link_to_lhs): New function.
	(relink_to_new_repr): Also relink LHS lists.
	(add_access_to_work_queue): Renamed to add_access_to_rhs_work_queue.
	(add_access_to_lhs_work_queue): New function.
	(pop_access_from_work_queue): Renamed to
	pop_access_from_rhs_work_queue.
	(pop_access_from_lhs_work_queue): New function.
	(build_accesses_from_assign): Also add links to LHS lists and to LHS
	work_queue.
	(child_would_conflict_in_lacc): Renamed to
	child_would_conflict_in_acc.  Adjusted parameter names.
	(create_artificial_child_access): New parameter set_grp_read, use it.
	(subtree_mark_written_and_enqueue): Renamed to
	subtree_mark_written_and_rhs_enqueue.
	(propagate_subaccesses_across_link): Renamed to
	propagate_subaccesses_from_rhs.
	(propagate_subaccesses_from_lhs): New function.
	(propagate_all_subaccesses): Also propagate subaccesses from LHSs to
	RHSs.

	testsuite/
	* gcc.dg/tree-ssa/pr92706-1.c: New test.
---
 gcc/testsuite/gcc.dg/tree-ssa/pr92706-1.c |  17 ++
 gcc/tree-sra.c                            | 306 ++++++++++++++++------
 2 files changed, 248 insertions(+), 75 deletions(-)
 create mode 100644 gcc/testsuite/gcc.dg/tree-ssa/pr92706-1.c

Comments

Richard Biener Jan. 29, 2020, 10:10 a.m. UTC | #1
On Tue, 28 Jan 2020, Martin Jambor wrote:

> Hi,
> 
> the previous patch unfortunately does not fix the first testcase in PR
> 92706 and since I am afraid it might be the important one, I also
> focused on that.  The issue here is again total scalarization accesses
> clashing with those representing accesses in the IL - on another
> aggregate but here the sides are switched.  Whereas in the previous
> case the total scalarization accesses prevented propagation along
> assignments, here we have the user accesses on the LHS, so even though
> we do not create anything there, we totally scalarize the RHS and
> again end up with assignments with different scalarizations leading to
> bad code.
> 
> So we clearly need to propagate information about accesses from RHSs
> to LHSs too, which the patch below does.  Because the intent is only
> preventing bad total scalarization - which the last patch now performs
> late enough - and do not care about grp_write flag and so forth, the
> propagation is a bit simpler and so I did not try to unify all of the
> code for both directions.
> 
> More information and some discussion is in the thread from the initial
> submission, the code has not changed in any (substantial) way.  See
> https://gcc.gnu.org/ml/gcc-patches/2019-12/msg01184.html and
> https://gcc.gnu.org/ml/gcc-patches/2020-01/msg00698.html.
> 
> Bootstrapped and tested on x86_64-linux.

OK.  Guess you should commit the series up to here separately
from the followup PR92486.

Thanks,
Richard.

> Thanks,
> 
> Martin
> 
> 2019-12-11  Martin Jambor  <mjambor@suse.cz>
> 
> 	PR tree-optimization/92706
> 	* tree-sra.c (struct access): Fields first_link, last_link,
> 	next_queued and grp_queued renamed to first_rhs_link, last_rhs_link,
> 	next_rhs_queued and grp_rhs_queued respectively, new fields
> 	first_lhs_link, last_lhs_link, next_lhs_queued and grp_lhs_queued.
> 	(struct assign_link): Field next renamed to next_rhs, new field
> 	next_lhs.  Updated comment.
> 	(work_queue_head): Renamed to rhs_work_queue_head.
> 	(lhs_work_queue_head): New variable.
> 	(add_link_to_lhs): New function.
> 	(relink_to_new_repr): Also relink LHS lists.
> 	(add_access_to_work_queue): Renamed to add_access_to_rhs_work_queue.
> 	(add_access_to_lhs_work_queue): New function.
> 	(pop_access_from_work_queue): Renamed to
> 	pop_access_from_rhs_work_queue.
> 	(pop_access_from_lhs_work_queue): New function.
> 	(build_accesses_from_assign): Also add links to LHS lists and to LHS
> 	work_queue.
> 	(child_would_conflict_in_lacc): Renamed to
> 	child_would_conflict_in_acc.  Adjusted parameter names.
> 	(create_artificial_child_access): New parameter set_grp_read, use it.
> 	(subtree_mark_written_and_enqueue): Renamed to
> 	subtree_mark_written_and_rhs_enqueue.
> 	(propagate_subaccesses_across_link): Renamed to
> 	propagate_subaccesses_from_rhs.
> 	(propagate_subaccesses_from_lhs): New function.
> 	(propagate_all_subaccesses): Also propagate subaccesses from LHSs to
> 	RHSs.
> 
> 	testsuite/
> 	* gcc.dg/tree-ssa/pr92706-1.c: New test.
> ---
>  gcc/testsuite/gcc.dg/tree-ssa/pr92706-1.c |  17 ++
>  gcc/tree-sra.c                            | 306 ++++++++++++++++------
>  2 files changed, 248 insertions(+), 75 deletions(-)
>  create mode 100644 gcc/testsuite/gcc.dg/tree-ssa/pr92706-1.c
> 
> diff --git a/gcc/testsuite/gcc.dg/tree-ssa/pr92706-1.c b/gcc/testsuite/gcc.dg/tree-ssa/pr92706-1.c
> new file mode 100644
> index 00000000000..c36d103798e
> --- /dev/null
> +++ b/gcc/testsuite/gcc.dg/tree-ssa/pr92706-1.c
> @@ -0,0 +1,17 @@
> +/* { dg-do compile } */
> +/* { dg-options "-O2 -fdump-tree-esra-details" } */
> +
> +struct S { int i[4]; } __attribute__((aligned(128)));
> +typedef __int128_t my_int128 __attribute__((may_alias));
> +__int128_t load (void *p)
> +{
> +  struct S v;
> +  __builtin_memcpy (&v, p, sizeof (struct S));
> +  struct S u;
> +  u = v;
> +  struct S w;
> +  w = u;
> +  return *(my_int128 *)&w;
> +}
> +
> +/* { dg-final { scan-tree-dump-not "Created a replacement for u offset: \[^0\]" "esra" } } */
> diff --git a/gcc/tree-sra.c b/gcc/tree-sra.c
> index 2b0849858de..ea8594db193 100644
> --- a/gcc/tree-sra.c
> +++ b/gcc/tree-sra.c
> @@ -167,11 +167,15 @@ struct access
>    struct access *next_sibling;
>  
>    /* Pointers to the first and last element in the linked list of assign
> -     links.  */
> -  struct assign_link *first_link, *last_link;
> +     links for propagation from LHS to RHS.  */
> +  struct assign_link *first_rhs_link, *last_rhs_link;
>  
> -  /* Pointer to the next access in the work queue.  */
> -  struct access *next_queued;
> +  /* Pointers to the first and last element in the linked list of assign
> +     links for propagation from LHS to RHS.  */
> +  struct assign_link *first_lhs_link, *last_lhs_link;
> +
> +  /* Pointer to the next access in the work queues.  */
> +  struct access *next_rhs_queued, *next_lhs_queued;
>  
>    /* Replacement variable for this access "region."  Never to be accessed
>       directly, always only by the means of get_access_replacement() and only
> @@ -184,8 +188,11 @@ struct access
>    /* Is this particular access write access? */
>    unsigned write : 1;
>  
> -  /* Is this access currently in the work queue?  */
> -  unsigned grp_queued : 1;
> +  /* Is this access currently in the rhs work queue?  */
> +  unsigned grp_rhs_queued : 1;
> +
> +  /* Is this access currently in the lhs work queue?  */
> +  unsigned grp_lhs_queued : 1;
>  
>    /* Does this group contain a write access?  This flag is propagated down the
>       access tree.  */
> @@ -262,12 +269,14 @@ typedef struct access *access_p;
>  static object_allocator<struct access> access_pool ("SRA accesses");
>  
>  /* A structure linking lhs and rhs accesses from an aggregate assignment.  They
> -   are used to propagate subaccesses from rhs to lhs as long as they don't
> -   conflict with what is already there.  */
> +   are used to propagate subaccesses from rhs to lhs and vice versa as long as
> +   they don't conflict with what is already there.  In the RHS->LHS direction,
> +   we also propagate grp_write flag to lazily mark that the access contains any
> +   meaningful data.  */
>  struct assign_link
>  {
>    struct access *lacc, *racc;
> -  struct assign_link *next;
> +  struct assign_link *next_rhs, *next_lhs;
>  };
>  
>  /* Alloc pool for allocating assign link structures.  */
> @@ -327,7 +336,7 @@ static struct obstack name_obstack;
>  
>  /* Head of a linked list of accesses that need to have its subaccesses
>     propagated to their assignment counterparts. */
> -static struct access *work_queue_head;
> +static struct access *rhs_work_queue_head, *lhs_work_queue_head;
>  
>  /* Dump contents of ACCESS to file F in a human friendly way.  If GRP is true,
>     representative fields are dumped, otherwise those which only describe the
> @@ -534,79 +543,155 @@ get_var_base_offset_size_access (tree base, HOST_WIDE_INT offset,
>  }
>  
>  /* Add LINK to the linked list of assign links of RACC.  */
> +
>  static void
>  add_link_to_rhs (struct access *racc, struct assign_link *link)
>  {
>    gcc_assert (link->racc == racc);
>  
> -  if (!racc->first_link)
> +  if (!racc->first_rhs_link)
>      {
> -      gcc_assert (!racc->last_link);
> -      racc->first_link = link;
> +      gcc_assert (!racc->last_rhs_link);
> +      racc->first_rhs_link = link;
>      }
>    else
> -    racc->last_link->next = link;
> +    racc->last_rhs_link->next_rhs = link;
>  
> -  racc->last_link = link;
> -  link->next = NULL;
> +  racc->last_rhs_link = link;
> +  link->next_rhs = NULL;
>  }
>  
> -/* Move all link structures in their linked list in OLD_RACC to the linked list
> -   in NEW_RACC.  */
> +/* Add LINK to the linked list of lhs assign links of LACC.  */
> +
>  static void
> -relink_to_new_repr (struct access *new_racc, struct access *old_racc)
> +add_link_to_lhs (struct access *lacc, struct assign_link *link)
>  {
> -  if (!old_racc->first_link)
> +  gcc_assert (link->lacc == lacc);
> +
> +  if (!lacc->first_lhs_link)
>      {
> -      gcc_assert (!old_racc->last_link);
> -      return;
> +      gcc_assert (!lacc->last_lhs_link);
> +      lacc->first_lhs_link = link;
>      }
> +  else
> +    lacc->last_lhs_link->next_lhs = link;
> +
> +  lacc->last_lhs_link = link;
> +  link->next_lhs = NULL;
> +}
>  
> -  if (new_racc->first_link)
> +/* Move all link structures in their linked list in OLD_ACC to the linked list
> +   in NEW_ACC.  */
> +static void
> +relink_to_new_repr (struct access *new_acc, struct access *old_acc)
> +{
> +  if (old_acc->first_rhs_link)
>      {
> -      gcc_assert (!new_racc->last_link->next);
> -      gcc_assert (!old_racc->last_link || !old_racc->last_link->next);
>  
> -      new_racc->last_link->next = old_racc->first_link;
> -      new_racc->last_link = old_racc->last_link;
> +      if (new_acc->first_rhs_link)
> +	{
> +	  gcc_assert (!new_acc->last_rhs_link->next_rhs);
> +	  gcc_assert (!old_acc->last_rhs_link
> +		      || !old_acc->last_rhs_link->next_rhs);
> +
> +	  new_acc->last_rhs_link->next_rhs = old_acc->first_rhs_link;
> +	  new_acc->last_rhs_link = old_acc->last_rhs_link;
> +	}
> +      else
> +	{
> +	  gcc_assert (!new_acc->last_rhs_link);
> +
> +	  new_acc->first_rhs_link = old_acc->first_rhs_link;
> +	  new_acc->last_rhs_link = old_acc->last_rhs_link;
> +	}
> +      old_acc->first_rhs_link = old_acc->last_rhs_link = NULL;
>      }
>    else
> +    gcc_assert (!old_acc->last_rhs_link);
> +
> +  if (old_acc->first_lhs_link)
>      {
> -      gcc_assert (!new_racc->last_link);
>  
> -      new_racc->first_link = old_racc->first_link;
> -      new_racc->last_link = old_racc->last_link;
> +      if (new_acc->first_lhs_link)
> +	{
> +	  gcc_assert (!new_acc->last_lhs_link->next_lhs);
> +	  gcc_assert (!old_acc->last_lhs_link
> +		      || !old_acc->last_lhs_link->next_lhs);
> +
> +	  new_acc->last_lhs_link->next_lhs = old_acc->first_lhs_link;
> +	  new_acc->last_lhs_link = old_acc->last_lhs_link;
> +	}
> +      else
> +	{
> +	  gcc_assert (!new_acc->last_lhs_link);
> +
> +	  new_acc->first_lhs_link = old_acc->first_lhs_link;
> +	  new_acc->last_lhs_link = old_acc->last_lhs_link;
> +	}
> +      old_acc->first_lhs_link = old_acc->last_lhs_link = NULL;
>      }
> -  old_racc->first_link = old_racc->last_link = NULL;
> +  else
> +    gcc_assert (!old_acc->last_lhs_link);
> +
>  }
>  
> -/* Add ACCESS to the work queue (which is actually a stack).  */
> +/* Add ACCESS to the work to queue for propagation of subaccesses from RHS to
> +   LHS (which is actually a stack).  */
>  
>  static void
> -add_access_to_work_queue (struct access *access)
> +add_access_to_rhs_work_queue (struct access *access)
>  {
> -  if (access->first_link && !access->grp_queued)
> +  if (access->first_rhs_link && !access->grp_rhs_queued)
>      {
> -      gcc_assert (!access->next_queued);
> -      access->next_queued = work_queue_head;
> -      access->grp_queued = 1;
> -      work_queue_head = access;
> +      gcc_assert (!access->next_rhs_queued);
> +      access->next_rhs_queued = rhs_work_queue_head;
> +      access->grp_rhs_queued = 1;
> +      rhs_work_queue_head = access;
>      }
>  }
>  
> -/* Pop an access from the work queue, and return it, assuming there is one.  */
> +/* Add ACCESS to the work to queue for propagation of subaccesses from LHS to
> +   RHS (which is actually a stack).  */
> +
> +static void
> +add_access_to_lhs_work_queue (struct access *access)
> +{
> +  if (access->first_lhs_link && !access->grp_lhs_queued)
> +    {
> +      gcc_assert (!access->next_lhs_queued);
> +      access->next_lhs_queued = lhs_work_queue_head;
> +      access->grp_lhs_queued = 1;
> +      lhs_work_queue_head = access;
> +    }
> +}
> +
> +/* Pop an access from the work queue for propagating from RHS to LHS, and
> +   return it, assuming there is one.  */
>  
>  static struct access *
> -pop_access_from_work_queue (void)
> +pop_access_from_rhs_work_queue (void)
>  {
> -  struct access *access = work_queue_head;
> +  struct access *access = rhs_work_queue_head;
>  
> -  work_queue_head = access->next_queued;
> -  access->next_queued = NULL;
> -  access->grp_queued = 0;
> +  rhs_work_queue_head = access->next_rhs_queued;
> +  access->next_rhs_queued = NULL;
> +  access->grp_rhs_queued = 0;
>    return access;
>  }
>  
> +/* Pop an access from the work queue for propagating from LHS to RHS, and
> +   return it, assuming there is one.  */
> +
> +static struct access *
> +pop_access_from_lhs_work_queue (void)
> +{
> +  struct access *access = lhs_work_queue_head;
> +
> +  lhs_work_queue_head = access->next_lhs_queued;
> +  access->next_lhs_queued = NULL;
> +  access->grp_lhs_queued = 0;
> +  return access;
> +}
>  
>  /* Allocate necessary structures.  */
>  
> @@ -1203,7 +1288,9 @@ build_accesses_from_assign (gimple *stmt)
>        link->lacc = lacc;
>        link->racc = racc;
>        add_link_to_rhs (racc, link);
> -      add_access_to_work_queue (racc);
> +      add_link_to_lhs (lacc, link);
> +      add_access_to_rhs_work_queue (racc);
> +      add_access_to_lhs_work_queue (lacc);
>  
>        /* Let's delay marking the areas as written until propagation of accesses
>  	 across link, unless the nature of rhs tells us that its data comes
> @@ -2492,17 +2579,17 @@ analyze_access_trees (struct access *access)
>    return ret;
>  }
>  
> -/* Return true iff a potential new child of LACC at offset OFFSET and with size
> +/* Return true iff a potential new child of ACC at offset OFFSET and with size
>     SIZE would conflict with an already existing one.  If exactly such a child
> -   already exists in LACC, store a pointer to it in EXACT_MATCH.  */
> +   already exists in ACC, store a pointer to it in EXACT_MATCH.  */
>  
>  static bool
> -child_would_conflict_in_lacc (struct access *lacc, HOST_WIDE_INT norm_offset,
> +child_would_conflict_in_acc (struct access *acc, HOST_WIDE_INT norm_offset,
>  			      HOST_WIDE_INT size, struct access **exact_match)
>  {
>    struct access *child;
>  
> -  for (child = lacc->first_child; child; child = child->next_sibling)
> +  for (child = acc->first_child; child; child = child->next_sibling)
>      {
>        if (child->offset == norm_offset && child->size == size)
>  	{
> @@ -2528,7 +2615,7 @@ child_would_conflict_in_lacc (struct access *lacc, HOST_WIDE_INT norm_offset,
>  static struct access *
>  create_artificial_child_access (struct access *parent, struct access *model,
>  				HOST_WIDE_INT new_offset,
> -				bool set_grp_write)
> +				bool set_grp_read, bool set_grp_write)
>  {
>    struct access **child;
>    tree expr = parent->base;
> @@ -2551,8 +2638,8 @@ create_artificial_child_access (struct access *parent, struct access *model,
>    access->size = model->size;
>    access->type = model->type;
>    access->parent = parent;
> +  access->grp_read = set_grp_read;
>    access->grp_write = set_grp_write;
> -  access->grp_read = false;
>    access->reverse = model->reverse;
>  
>    child = &parent->first_child;
> @@ -2571,16 +2658,16 @@ create_artificial_child_access (struct access *parent, struct access *model,
>     and has assignment links leading from it, re-enqueue it.  */
>  
>  static void
> -subtree_mark_written_and_enqueue (struct access *access)
> +subtree_mark_written_and_rhs_enqueue (struct access *access)
>  {
>    if (access->grp_write)
>      return;
>    access->grp_write = true;
> -  add_access_to_work_queue (access);
> +  add_access_to_rhs_work_queue (access);
>  
>    struct access *child;
>    for (child = access->first_child; child; child = child->next_sibling)
> -    subtree_mark_written_and_enqueue (child);
> +    subtree_mark_written_and_rhs_enqueue (child);
>  }
>  
>  /* Propagate subaccesses and grp_write flags of RACC across an assignment link
> @@ -2590,7 +2677,7 @@ subtree_mark_written_and_enqueue (struct access *access)
>     possible.  */
>  
>  static bool
> -propagate_subaccesses_across_link (struct access *lacc, struct access *racc)
> +propagate_subaccesses_from_rhs (struct access *lacc, struct access *racc)
>  {
>    struct access *rchild;
>    HOST_WIDE_INT norm_delta = lacc->offset - racc->offset;
> @@ -2603,7 +2690,7 @@ propagate_subaccesses_across_link (struct access *lacc, struct access *racc)
>        gcc_checking_assert (!comes_initialized_p (racc->base));
>        if (racc->grp_write)
>  	{
> -	  subtree_mark_written_and_enqueue (lacc);
> +	  subtree_mark_written_and_rhs_enqueue (lacc);
>  	  ret = true;
>  	}
>      }
> @@ -2615,7 +2702,7 @@ propagate_subaccesses_across_link (struct access *lacc, struct access *racc)
>        if (!lacc->grp_write)
>  	{
>  	  ret = true;
> -	  subtree_mark_written_and_enqueue (lacc);
> +	  subtree_mark_written_and_rhs_enqueue (lacc);
>  	}
>        return ret;
>      }
> @@ -2625,7 +2712,7 @@ propagate_subaccesses_across_link (struct access *lacc, struct access *racc)
>        if (!lacc->grp_write)
>  	{
>  	  ret = true;
> -	  subtree_mark_written_and_enqueue (lacc);
> +	  subtree_mark_written_and_rhs_enqueue (lacc);
>  	}
>        if (!lacc->first_child && !racc->first_child)
>  	{
> @@ -2655,7 +2742,7 @@ propagate_subaccesses_across_link (struct access *lacc, struct access *racc)
>        struct access *new_acc = NULL;
>        HOST_WIDE_INT norm_offset = rchild->offset + norm_delta;
>  
> -      if (child_would_conflict_in_lacc (lacc, norm_offset, rchild->size,
> +      if (child_would_conflict_in_acc (lacc, norm_offset, rchild->size,
>  					&new_acc))
>  	{
>  	  if (new_acc)
> @@ -2663,17 +2750,17 @@ propagate_subaccesses_across_link (struct access *lacc, struct access *racc)
>  	      if (!new_acc->grp_write && rchild->grp_write)
>  		{
>  		  gcc_assert (!lacc->grp_write);
> -		  subtree_mark_written_and_enqueue (new_acc);
> +		  subtree_mark_written_and_rhs_enqueue (new_acc);
>  		  ret = true;
>  		}
>  
>  	      rchild->grp_hint = 1;
>  	      new_acc->grp_hint |= new_acc->grp_read;
>  	      if (rchild->first_child
> -		  && propagate_subaccesses_across_link (new_acc, rchild))
> +		  && propagate_subaccesses_from_rhs (new_acc, rchild))
>  		{
>  		  ret = 1;
> -		  add_access_to_work_queue (new_acc);
> +		  add_access_to_rhs_work_queue (new_acc);
>  		}
>  	    }
>  	  else
> @@ -2681,7 +2768,7 @@ propagate_subaccesses_across_link (struct access *lacc, struct access *racc)
>  	      if (!lacc->grp_write)
>  		{
>  		  ret = true;
> -		  subtree_mark_written_and_enqueue (lacc);
> +		  subtree_mark_written_and_rhs_enqueue (lacc);
>  		}
>  	    }
>  	  continue;
> @@ -2692,41 +2779,85 @@ propagate_subaccesses_across_link (struct access *lacc, struct access *racc)
>  	  if (rchild->grp_write && !lacc->grp_write)
>  	    {
>  	      ret = true;
> -	      subtree_mark_written_and_enqueue (lacc);
> +	      subtree_mark_written_and_rhs_enqueue (lacc);
>  	    }
>  	  continue;
>  	}
>  
>        rchild->grp_hint = 1;
>        new_acc = create_artificial_child_access (lacc, rchild, norm_offset,
> -						lacc->grp_write
> -						|| rchild->grp_write);
> +						false, (lacc->grp_write
> +							|| rchild->grp_write));
>        gcc_checking_assert (new_acc);
>        if (racc->first_child)
> -	propagate_subaccesses_across_link (new_acc, rchild);
> +	propagate_subaccesses_from_rhs (new_acc, rchild);
>  
> -      add_access_to_work_queue (lacc);
> +      add_access_to_rhs_work_queue (lacc);
>        ret = true;
>      }
>  
>    return ret;
>  }
>  
> +/* Propagate subaccesses of LACC across an assignment link to RACC if they
> +   should inhibit total scalarization of the corresponding area.  No flags are
> +   being propagated in the process.  Return true if anything changed.  */
> +
> +static bool
> +propagate_subaccesses_from_lhs (struct access *lacc, struct access *racc)
> +{
> +  if (is_gimple_reg_type (racc->type)
> +      || lacc->grp_unscalarizable_region
> +      || racc->grp_unscalarizable_region)
> +    return false;
> +
> +  /* TODO: Do we want set some new racc flag to stop potential total
> +     scalarization if lacc is a scalar access (and none fo the two have
> +     children)?  */
> +
> +  bool ret = false;
> +  HOST_WIDE_INT norm_delta = racc->offset - lacc->offset;
> +  for (struct access *lchild = lacc->first_child;
> +       lchild;
> +       lchild = lchild->next_sibling)
> +    {
> +      struct access *matching_acc = NULL;
> +      HOST_WIDE_INT norm_offset = lchild->offset + norm_delta;
> +
> +      if (lchild->grp_unscalarizable_region
> +	  || child_would_conflict_in_acc (racc, norm_offset, lchild->size,
> +					  &matching_acc))
> +	{
> +	  if (matching_acc
> +	      && propagate_subaccesses_from_lhs (lchild, matching_acc))
> +	    add_access_to_lhs_work_queue (matching_acc);
> +	  continue;
> +	}
> +
> +      struct access *new_acc
> +	=  create_artificial_child_access (racc, lchild, norm_offset,
> +					   true, false);
> +      propagate_subaccesses_from_lhs (lchild, new_acc);
> +      ret = true;
> +    }
> +  return ret;
> +}
> +
>  /* Propagate all subaccesses across assignment links.  */
>  
>  static void
>  propagate_all_subaccesses (void)
>  {
> -  while (work_queue_head)
> +  while (rhs_work_queue_head)
>      {
> -      struct access *racc = pop_access_from_work_queue ();
> +      struct access *racc = pop_access_from_rhs_work_queue ();
>        struct assign_link *link;
>  
>        if (racc->group_representative)
>  	racc= racc->group_representative;
> -      gcc_assert (racc->first_link);
> +      gcc_assert (racc->first_rhs_link);
>  
> -      for (link = racc->first_link; link; link = link->next)
> +      for (link = racc->first_rhs_link; link; link = link->next_rhs)
>  	{
>  	  struct access *lacc = link->lacc;
>  
> @@ -2739,22 +2870,47 @@ propagate_all_subaccesses (void)
>  	    {
>  	      if (!lacc->grp_write)
>  		{
> -		  subtree_mark_written_and_enqueue (lacc);
> +		  subtree_mark_written_and_rhs_enqueue (lacc);
>  		  reque_parents = true;
>  		}
>  	    }
> -	  else if (propagate_subaccesses_across_link (lacc, racc))
> +	  else if (propagate_subaccesses_from_rhs (lacc, racc))
>  	    reque_parents = true;
>  
>  	  if (reque_parents)
>  	    do
>  	      {
> -		add_access_to_work_queue (lacc);
> +		add_access_to_rhs_work_queue (lacc);
>  		lacc = lacc->parent;
>  	      }
>  	    while (lacc);
>  	}
>      }
> +
> +  while (lhs_work_queue_head)
> +    {
> +      struct access *lacc = pop_access_from_lhs_work_queue ();
> +      struct assign_link *link;
> +
> +      if (lacc->group_representative)
> +	lacc = lacc->group_representative;
> +      gcc_assert (lacc->first_lhs_link);
> +
> +      if (!bitmap_bit_p (candidate_bitmap, DECL_UID (lacc->base)))
> +	continue;
> +
> +      for (link = lacc->first_lhs_link; link; link = link->next_lhs)
> +	{
> +	  struct access *racc = link->racc;
> +
> +	  if (racc->group_representative)
> +	    racc = racc->group_representative;
> +	  if (!bitmap_bit_p (candidate_bitmap, DECL_UID (racc->base)))
> +	    continue;
> +	  if (propagate_subaccesses_from_lhs (lacc, racc))
> +	    add_access_to_lhs_work_queue (racc);
> +	}
> +    }
>  }
>  
>  /* Return true if the forest beginning with ROOT does not contain
>
diff mbox series

Patch

diff --git a/gcc/testsuite/gcc.dg/tree-ssa/pr92706-1.c b/gcc/testsuite/gcc.dg/tree-ssa/pr92706-1.c
new file mode 100644
index 00000000000..c36d103798e
--- /dev/null
+++ b/gcc/testsuite/gcc.dg/tree-ssa/pr92706-1.c
@@ -0,0 +1,17 @@ 
+/* { dg-do compile } */
+/* { dg-options "-O2 -fdump-tree-esra-details" } */
+
+struct S { int i[4]; } __attribute__((aligned(128)));
+typedef __int128_t my_int128 __attribute__((may_alias));
+__int128_t load (void *p)
+{
+  struct S v;
+  __builtin_memcpy (&v, p, sizeof (struct S));
+  struct S u;
+  u = v;
+  struct S w;
+  w = u;
+  return *(my_int128 *)&w;
+}
+
+/* { dg-final { scan-tree-dump-not "Created a replacement for u offset: \[^0\]" "esra" } } */
diff --git a/gcc/tree-sra.c b/gcc/tree-sra.c
index 2b0849858de..ea8594db193 100644
--- a/gcc/tree-sra.c
+++ b/gcc/tree-sra.c
@@ -167,11 +167,15 @@  struct access
   struct access *next_sibling;
 
   /* Pointers to the first and last element in the linked list of assign
-     links.  */
-  struct assign_link *first_link, *last_link;
+     links for propagation from LHS to RHS.  */
+  struct assign_link *first_rhs_link, *last_rhs_link;
 
-  /* Pointer to the next access in the work queue.  */
-  struct access *next_queued;
+  /* Pointers to the first and last element in the linked list of assign
+     links for propagation from LHS to RHS.  */
+  struct assign_link *first_lhs_link, *last_lhs_link;
+
+  /* Pointer to the next access in the work queues.  */
+  struct access *next_rhs_queued, *next_lhs_queued;
 
   /* Replacement variable for this access "region."  Never to be accessed
      directly, always only by the means of get_access_replacement() and only
@@ -184,8 +188,11 @@  struct access
   /* Is this particular access write access? */
   unsigned write : 1;
 
-  /* Is this access currently in the work queue?  */
-  unsigned grp_queued : 1;
+  /* Is this access currently in the rhs work queue?  */
+  unsigned grp_rhs_queued : 1;
+
+  /* Is this access currently in the lhs work queue?  */
+  unsigned grp_lhs_queued : 1;
 
   /* Does this group contain a write access?  This flag is propagated down the
      access tree.  */
@@ -262,12 +269,14 @@  typedef struct access *access_p;
 static object_allocator<struct access> access_pool ("SRA accesses");
 
 /* A structure linking lhs and rhs accesses from an aggregate assignment.  They
-   are used to propagate subaccesses from rhs to lhs as long as they don't
-   conflict with what is already there.  */
+   are used to propagate subaccesses from rhs to lhs and vice versa as long as
+   they don't conflict with what is already there.  In the RHS->LHS direction,
+   we also propagate grp_write flag to lazily mark that the access contains any
+   meaningful data.  */
 struct assign_link
 {
   struct access *lacc, *racc;
-  struct assign_link *next;
+  struct assign_link *next_rhs, *next_lhs;
 };
 
 /* Alloc pool for allocating assign link structures.  */
@@ -327,7 +336,7 @@  static struct obstack name_obstack;
 
 /* Head of a linked list of accesses that need to have its subaccesses
    propagated to their assignment counterparts. */
-static struct access *work_queue_head;
+static struct access *rhs_work_queue_head, *lhs_work_queue_head;
 
 /* Dump contents of ACCESS to file F in a human friendly way.  If GRP is true,
    representative fields are dumped, otherwise those which only describe the
@@ -534,79 +543,155 @@  get_var_base_offset_size_access (tree base, HOST_WIDE_INT offset,
 }
 
 /* Add LINK to the linked list of assign links of RACC.  */
+
 static void
 add_link_to_rhs (struct access *racc, struct assign_link *link)
 {
   gcc_assert (link->racc == racc);
 
-  if (!racc->first_link)
+  if (!racc->first_rhs_link)
     {
-      gcc_assert (!racc->last_link);
-      racc->first_link = link;
+      gcc_assert (!racc->last_rhs_link);
+      racc->first_rhs_link = link;
     }
   else
-    racc->last_link->next = link;
+    racc->last_rhs_link->next_rhs = link;
 
-  racc->last_link = link;
-  link->next = NULL;
+  racc->last_rhs_link = link;
+  link->next_rhs = NULL;
 }
 
-/* Move all link structures in their linked list in OLD_RACC to the linked list
-   in NEW_RACC.  */
+/* Add LINK to the linked list of lhs assign links of LACC.  */
+
 static void
-relink_to_new_repr (struct access *new_racc, struct access *old_racc)
+add_link_to_lhs (struct access *lacc, struct assign_link *link)
 {
-  if (!old_racc->first_link)
+  gcc_assert (link->lacc == lacc);
+
+  if (!lacc->first_lhs_link)
     {
-      gcc_assert (!old_racc->last_link);
-      return;
+      gcc_assert (!lacc->last_lhs_link);
+      lacc->first_lhs_link = link;
     }
+  else
+    lacc->last_lhs_link->next_lhs = link;
+
+  lacc->last_lhs_link = link;
+  link->next_lhs = NULL;
+}
 
-  if (new_racc->first_link)
+/* Move all link structures in their linked list in OLD_ACC to the linked list
+   in NEW_ACC.  */
+static void
+relink_to_new_repr (struct access *new_acc, struct access *old_acc)
+{
+  if (old_acc->first_rhs_link)
     {
-      gcc_assert (!new_racc->last_link->next);
-      gcc_assert (!old_racc->last_link || !old_racc->last_link->next);
 
-      new_racc->last_link->next = old_racc->first_link;
-      new_racc->last_link = old_racc->last_link;
+      if (new_acc->first_rhs_link)
+	{
+	  gcc_assert (!new_acc->last_rhs_link->next_rhs);
+	  gcc_assert (!old_acc->last_rhs_link
+		      || !old_acc->last_rhs_link->next_rhs);
+
+	  new_acc->last_rhs_link->next_rhs = old_acc->first_rhs_link;
+	  new_acc->last_rhs_link = old_acc->last_rhs_link;
+	}
+      else
+	{
+	  gcc_assert (!new_acc->last_rhs_link);
+
+	  new_acc->first_rhs_link = old_acc->first_rhs_link;
+	  new_acc->last_rhs_link = old_acc->last_rhs_link;
+	}
+      old_acc->first_rhs_link = old_acc->last_rhs_link = NULL;
     }
   else
+    gcc_assert (!old_acc->last_rhs_link);
+
+  if (old_acc->first_lhs_link)
     {
-      gcc_assert (!new_racc->last_link);
 
-      new_racc->first_link = old_racc->first_link;
-      new_racc->last_link = old_racc->last_link;
+      if (new_acc->first_lhs_link)
+	{
+	  gcc_assert (!new_acc->last_lhs_link->next_lhs);
+	  gcc_assert (!old_acc->last_lhs_link
+		      || !old_acc->last_lhs_link->next_lhs);
+
+	  new_acc->last_lhs_link->next_lhs = old_acc->first_lhs_link;
+	  new_acc->last_lhs_link = old_acc->last_lhs_link;
+	}
+      else
+	{
+	  gcc_assert (!new_acc->last_lhs_link);
+
+	  new_acc->first_lhs_link = old_acc->first_lhs_link;
+	  new_acc->last_lhs_link = old_acc->last_lhs_link;
+	}
+      old_acc->first_lhs_link = old_acc->last_lhs_link = NULL;
     }
-  old_racc->first_link = old_racc->last_link = NULL;
+  else
+    gcc_assert (!old_acc->last_lhs_link);
+
 }
 
-/* Add ACCESS to the work queue (which is actually a stack).  */
+/* Add ACCESS to the work to queue for propagation of subaccesses from RHS to
+   LHS (which is actually a stack).  */
 
 static void
-add_access_to_work_queue (struct access *access)
+add_access_to_rhs_work_queue (struct access *access)
 {
-  if (access->first_link && !access->grp_queued)
+  if (access->first_rhs_link && !access->grp_rhs_queued)
     {
-      gcc_assert (!access->next_queued);
-      access->next_queued = work_queue_head;
-      access->grp_queued = 1;
-      work_queue_head = access;
+      gcc_assert (!access->next_rhs_queued);
+      access->next_rhs_queued = rhs_work_queue_head;
+      access->grp_rhs_queued = 1;
+      rhs_work_queue_head = access;
     }
 }
 
-/* Pop an access from the work queue, and return it, assuming there is one.  */
+/* Add ACCESS to the work to queue for propagation of subaccesses from LHS to
+   RHS (which is actually a stack).  */
+
+static void
+add_access_to_lhs_work_queue (struct access *access)
+{
+  if (access->first_lhs_link && !access->grp_lhs_queued)
+    {
+      gcc_assert (!access->next_lhs_queued);
+      access->next_lhs_queued = lhs_work_queue_head;
+      access->grp_lhs_queued = 1;
+      lhs_work_queue_head = access;
+    }
+}
+
+/* Pop an access from the work queue for propagating from RHS to LHS, and
+   return it, assuming there is one.  */
 
 static struct access *
-pop_access_from_work_queue (void)
+pop_access_from_rhs_work_queue (void)
 {
-  struct access *access = work_queue_head;
+  struct access *access = rhs_work_queue_head;
 
-  work_queue_head = access->next_queued;
-  access->next_queued = NULL;
-  access->grp_queued = 0;
+  rhs_work_queue_head = access->next_rhs_queued;
+  access->next_rhs_queued = NULL;
+  access->grp_rhs_queued = 0;
   return access;
 }
 
+/* Pop an access from the work queue for propagating from LHS to RHS, and
+   return it, assuming there is one.  */
+
+static struct access *
+pop_access_from_lhs_work_queue (void)
+{
+  struct access *access = lhs_work_queue_head;
+
+  lhs_work_queue_head = access->next_lhs_queued;
+  access->next_lhs_queued = NULL;
+  access->grp_lhs_queued = 0;
+  return access;
+}
 
 /* Allocate necessary structures.  */
 
@@ -1203,7 +1288,9 @@  build_accesses_from_assign (gimple *stmt)
       link->lacc = lacc;
       link->racc = racc;
       add_link_to_rhs (racc, link);
-      add_access_to_work_queue (racc);
+      add_link_to_lhs (lacc, link);
+      add_access_to_rhs_work_queue (racc);
+      add_access_to_lhs_work_queue (lacc);
 
       /* Let's delay marking the areas as written until propagation of accesses
 	 across link, unless the nature of rhs tells us that its data comes
@@ -2492,17 +2579,17 @@  analyze_access_trees (struct access *access)
   return ret;
 }
 
-/* Return true iff a potential new child of LACC at offset OFFSET and with size
+/* Return true iff a potential new child of ACC at offset OFFSET and with size
    SIZE would conflict with an already existing one.  If exactly such a child
-   already exists in LACC, store a pointer to it in EXACT_MATCH.  */
+   already exists in ACC, store a pointer to it in EXACT_MATCH.  */
 
 static bool
-child_would_conflict_in_lacc (struct access *lacc, HOST_WIDE_INT norm_offset,
+child_would_conflict_in_acc (struct access *acc, HOST_WIDE_INT norm_offset,
 			      HOST_WIDE_INT size, struct access **exact_match)
 {
   struct access *child;
 
-  for (child = lacc->first_child; child; child = child->next_sibling)
+  for (child = acc->first_child; child; child = child->next_sibling)
     {
       if (child->offset == norm_offset && child->size == size)
 	{
@@ -2528,7 +2615,7 @@  child_would_conflict_in_lacc (struct access *lacc, HOST_WIDE_INT norm_offset,
 static struct access *
 create_artificial_child_access (struct access *parent, struct access *model,
 				HOST_WIDE_INT new_offset,
-				bool set_grp_write)
+				bool set_grp_read, bool set_grp_write)
 {
   struct access **child;
   tree expr = parent->base;
@@ -2551,8 +2638,8 @@  create_artificial_child_access (struct access *parent, struct access *model,
   access->size = model->size;
   access->type = model->type;
   access->parent = parent;
+  access->grp_read = set_grp_read;
   access->grp_write = set_grp_write;
-  access->grp_read = false;
   access->reverse = model->reverse;
 
   child = &parent->first_child;
@@ -2571,16 +2658,16 @@  create_artificial_child_access (struct access *parent, struct access *model,
    and has assignment links leading from it, re-enqueue it.  */
 
 static void
-subtree_mark_written_and_enqueue (struct access *access)
+subtree_mark_written_and_rhs_enqueue (struct access *access)
 {
   if (access->grp_write)
     return;
   access->grp_write = true;
-  add_access_to_work_queue (access);
+  add_access_to_rhs_work_queue (access);
 
   struct access *child;
   for (child = access->first_child; child; child = child->next_sibling)
-    subtree_mark_written_and_enqueue (child);
+    subtree_mark_written_and_rhs_enqueue (child);
 }
 
 /* Propagate subaccesses and grp_write flags of RACC across an assignment link
@@ -2590,7 +2677,7 @@  subtree_mark_written_and_enqueue (struct access *access)
    possible.  */
 
 static bool
-propagate_subaccesses_across_link (struct access *lacc, struct access *racc)
+propagate_subaccesses_from_rhs (struct access *lacc, struct access *racc)
 {
   struct access *rchild;
   HOST_WIDE_INT norm_delta = lacc->offset - racc->offset;
@@ -2603,7 +2690,7 @@  propagate_subaccesses_across_link (struct access *lacc, struct access *racc)
       gcc_checking_assert (!comes_initialized_p (racc->base));
       if (racc->grp_write)
 	{
-	  subtree_mark_written_and_enqueue (lacc);
+	  subtree_mark_written_and_rhs_enqueue (lacc);
 	  ret = true;
 	}
     }
@@ -2615,7 +2702,7 @@  propagate_subaccesses_across_link (struct access *lacc, struct access *racc)
       if (!lacc->grp_write)
 	{
 	  ret = true;
-	  subtree_mark_written_and_enqueue (lacc);
+	  subtree_mark_written_and_rhs_enqueue (lacc);
 	}
       return ret;
     }
@@ -2625,7 +2712,7 @@  propagate_subaccesses_across_link (struct access *lacc, struct access *racc)
       if (!lacc->grp_write)
 	{
 	  ret = true;
-	  subtree_mark_written_and_enqueue (lacc);
+	  subtree_mark_written_and_rhs_enqueue (lacc);
 	}
       if (!lacc->first_child && !racc->first_child)
 	{
@@ -2655,7 +2742,7 @@  propagate_subaccesses_across_link (struct access *lacc, struct access *racc)
       struct access *new_acc = NULL;
       HOST_WIDE_INT norm_offset = rchild->offset + norm_delta;
 
-      if (child_would_conflict_in_lacc (lacc, norm_offset, rchild->size,
+      if (child_would_conflict_in_acc (lacc, norm_offset, rchild->size,
 					&new_acc))
 	{
 	  if (new_acc)
@@ -2663,17 +2750,17 @@  propagate_subaccesses_across_link (struct access *lacc, struct access *racc)
 	      if (!new_acc->grp_write && rchild->grp_write)
 		{
 		  gcc_assert (!lacc->grp_write);
-		  subtree_mark_written_and_enqueue (new_acc);
+		  subtree_mark_written_and_rhs_enqueue (new_acc);
 		  ret = true;
 		}
 
 	      rchild->grp_hint = 1;
 	      new_acc->grp_hint |= new_acc->grp_read;
 	      if (rchild->first_child
-		  && propagate_subaccesses_across_link (new_acc, rchild))
+		  && propagate_subaccesses_from_rhs (new_acc, rchild))
 		{
 		  ret = 1;
-		  add_access_to_work_queue (new_acc);
+		  add_access_to_rhs_work_queue (new_acc);
 		}
 	    }
 	  else
@@ -2681,7 +2768,7 @@  propagate_subaccesses_across_link (struct access *lacc, struct access *racc)
 	      if (!lacc->grp_write)
 		{
 		  ret = true;
-		  subtree_mark_written_and_enqueue (lacc);
+		  subtree_mark_written_and_rhs_enqueue (lacc);
 		}
 	    }
 	  continue;
@@ -2692,41 +2779,85 @@  propagate_subaccesses_across_link (struct access *lacc, struct access *racc)
 	  if (rchild->grp_write && !lacc->grp_write)
 	    {
 	      ret = true;
-	      subtree_mark_written_and_enqueue (lacc);
+	      subtree_mark_written_and_rhs_enqueue (lacc);
 	    }
 	  continue;
 	}
 
       rchild->grp_hint = 1;
       new_acc = create_artificial_child_access (lacc, rchild, norm_offset,
-						lacc->grp_write
-						|| rchild->grp_write);
+						false, (lacc->grp_write
+							|| rchild->grp_write));
       gcc_checking_assert (new_acc);
       if (racc->first_child)
-	propagate_subaccesses_across_link (new_acc, rchild);
+	propagate_subaccesses_from_rhs (new_acc, rchild);
 
-      add_access_to_work_queue (lacc);
+      add_access_to_rhs_work_queue (lacc);
       ret = true;
     }
 
   return ret;
 }
 
+/* Propagate subaccesses of LACC across an assignment link to RACC if they
+   should inhibit total scalarization of the corresponding area.  No flags are
+   being propagated in the process.  Return true if anything changed.  */
+
+static bool
+propagate_subaccesses_from_lhs (struct access *lacc, struct access *racc)
+{
+  if (is_gimple_reg_type (racc->type)
+      || lacc->grp_unscalarizable_region
+      || racc->grp_unscalarizable_region)
+    return false;
+
+  /* TODO: Do we want set some new racc flag to stop potential total
+     scalarization if lacc is a scalar access (and none fo the two have
+     children)?  */
+
+  bool ret = false;
+  HOST_WIDE_INT norm_delta = racc->offset - lacc->offset;
+  for (struct access *lchild = lacc->first_child;
+       lchild;
+       lchild = lchild->next_sibling)
+    {
+      struct access *matching_acc = NULL;
+      HOST_WIDE_INT norm_offset = lchild->offset + norm_delta;
+
+      if (lchild->grp_unscalarizable_region
+	  || child_would_conflict_in_acc (racc, norm_offset, lchild->size,
+					  &matching_acc))
+	{
+	  if (matching_acc
+	      && propagate_subaccesses_from_lhs (lchild, matching_acc))
+	    add_access_to_lhs_work_queue (matching_acc);
+	  continue;
+	}
+
+      struct access *new_acc
+	=  create_artificial_child_access (racc, lchild, norm_offset,
+					   true, false);
+      propagate_subaccesses_from_lhs (lchild, new_acc);
+      ret = true;
+    }
+  return ret;
+}
+
 /* Propagate all subaccesses across assignment links.  */
 
 static void
 propagate_all_subaccesses (void)
 {
-  while (work_queue_head)
+  while (rhs_work_queue_head)
     {
-      struct access *racc = pop_access_from_work_queue ();
+      struct access *racc = pop_access_from_rhs_work_queue ();
       struct assign_link *link;
 
       if (racc->group_representative)
 	racc= racc->group_representative;
-      gcc_assert (racc->first_link);
+      gcc_assert (racc->first_rhs_link);
 
-      for (link = racc->first_link; link; link = link->next)
+      for (link = racc->first_rhs_link; link; link = link->next_rhs)
 	{
 	  struct access *lacc = link->lacc;
 
@@ -2739,22 +2870,47 @@  propagate_all_subaccesses (void)
 	    {
 	      if (!lacc->grp_write)
 		{
-		  subtree_mark_written_and_enqueue (lacc);
+		  subtree_mark_written_and_rhs_enqueue (lacc);
 		  reque_parents = true;
 		}
 	    }
-	  else if (propagate_subaccesses_across_link (lacc, racc))
+	  else if (propagate_subaccesses_from_rhs (lacc, racc))
 	    reque_parents = true;
 
 	  if (reque_parents)
 	    do
 	      {
-		add_access_to_work_queue (lacc);
+		add_access_to_rhs_work_queue (lacc);
 		lacc = lacc->parent;
 	      }
 	    while (lacc);
 	}
     }
+
+  while (lhs_work_queue_head)
+    {
+      struct access *lacc = pop_access_from_lhs_work_queue ();
+      struct assign_link *link;
+
+      if (lacc->group_representative)
+	lacc = lacc->group_representative;
+      gcc_assert (lacc->first_lhs_link);
+
+      if (!bitmap_bit_p (candidate_bitmap, DECL_UID (lacc->base)))
+	continue;
+
+      for (link = lacc->first_lhs_link; link; link = link->next_lhs)
+	{
+	  struct access *racc = link->racc;
+
+	  if (racc->group_representative)
+	    racc = racc->group_representative;
+	  if (!bitmap_bit_p (candidate_bitmap, DECL_UID (racc->base)))
+	    continue;
+	  if (propagate_subaccesses_from_lhs (lacc, racc))
+	    add_access_to_lhs_work_queue (racc);
+	}
+    }
 }
 
 /* Return true if the forest beginning with ROOT does not contain