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 <mjam...@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 > -- Richard Biener <rguent...@suse.de> SUSE Software Solutions Germany GmbH, Maxfeldstrasse 5, 90409 Nuernberg, Germany; GF: Felix Imendörffer; HRB 36809 (AG Nuernberg)