(Previously submitted here:
https://gcc.gnu.org/pipermail/gcc-patches/2021-May/570398.html)

This patch is a second attempt at refactoring struct component mapping
handling for OpenACC/OpenMP during gimplification, after the patch I
posted here:

  https://gcc.gnu.org/pipermail/gcc-patches/2018-November/510503.html

And improved here, post-review:

  https://gcc.gnu.org/pipermail/gcc-patches/2019-November/533394.html

This patch goes further, in that the struct-handling code is outlined
into its own function (to create the "GOMP_MAP_STRUCT" node and the
sorted list of nodes immediately following it, from a set of mappings
of components of a given struct or derived type). I've also gone through
the list-handling code and attempted to add comments documenting how it
works to the best of my understanding, and broken out a couple of helper
functions in order to (hopefully) have the code self-document better also.

2021-06-02  Julian Brown  <jul...@codesourcery.com>

gcc/
        * gimplify.c (insert_struct_comp_map): Refactor function into...
        (build_struct_comp_nodes): This new function.  Remove list handling
        and improve self-documentation.
        (insert_node_after, move_node_after, move_nodes_after,
        move_concat_nodes_after): New helper functions.
        (build_struct_group): New function to build up GOMP_MAP_STRUCT node
        groups to map struct components. Outlined from...
        (gimplify_scan_omp_clauses): Here.  Call above function.
---
 gcc/gimplify.c | 975 +++++++++++++++++++++++++++++++------------------
 1 file changed, 611 insertions(+), 364 deletions(-)

diff --git a/gcc/gimplify.c b/gcc/gimplify.c
index 974d25b2d05..8558dda079f 100644
--- a/gcc/gimplify.c
+++ b/gcc/gimplify.c
@@ -8403,73 +8403,66 @@ gimplify_omp_depend (tree *list_p, gimple_seq *pre_p)
   return 1;
 }
 
-/* Insert a GOMP_MAP_ALLOC or GOMP_MAP_RELEASE node following a
-   GOMP_MAP_STRUCT mapping.  C is an always_pointer mapping.  STRUCT_NODE is
-   the struct node to insert the new mapping after (when the struct node is
-   initially created).  PREV_NODE is the first of two or three mappings for a
-   pointer, and is either:
-     - the node before C, when a pair of mappings is used, e.g. for a C/C++
-       array section.
-     - not the node before C.  This is true when we have a reference-to-pointer
-       type (with a mapping for the reference and for the pointer), or for
-       Fortran derived-type mappings with a GOMP_MAP_TO_PSET.
-   If SCP is non-null, the new node is inserted before *SCP.
-   if SCP is null, the new node is inserted before PREV_NODE.
-   The return type is:
-     - PREV_NODE, if SCP is non-null.
-     - The newly-created ALLOC or RELEASE node, if SCP is null.
-     - The second newly-created ALLOC or RELEASE node, if we are mapping a
-       reference to a pointer.  */
+/* For a set of mappings describing an array section pointed to by a struct
+   (or derived type, etc.) component, create an "alloc" or "release" node to
+   insert into a list following a GOMP_MAP_STRUCT node.  For some types of
+   mapping (e.g. Fortran arrays with descriptors), an additional mapping may
+   be created that is inserted into the list of mapping nodes attached to the
+   directive being processed -- not part of the sorted list of nodes after
+   GOMP_MAP_STRUCT.
+
+   CODE is the code of the directive being processed.  GRP_START and GRP_END
+   are the first and last of two or three nodes representing this array section
+   mapping (e.g. a data movement node like GOMP_MAP_{TO,FROM}, optionally a
+   GOMP_MAP_TO_PSET, and finally a GOMP_MAP_ALWAYS_POINTER).  EXTRA_NODE is
+   filled with the additional node described above, if needed.
+
+   This function does not add the new nodes to any lists itself.  It is the
+   responsibility of the caller to do that.  */
 
 static tree
-insert_struct_comp_map (enum tree_code code, tree c, tree struct_node,
-                       tree prev_node, tree *scp)
+build_struct_comp_nodes (enum tree_code code, tree grp_start, tree grp_end,
+                        tree *extra_node)
 {
   enum gomp_map_kind mkind
     = (code == OMP_TARGET_EXIT_DATA || code == OACC_EXIT_DATA)
       ? GOMP_MAP_RELEASE : GOMP_MAP_ALLOC;
 
-  tree c2 = build_omp_clause (OMP_CLAUSE_LOCATION (c), OMP_CLAUSE_MAP);
-  tree cl = scp ? prev_node : c2;
+  gcc_assert (grp_start != grp_end);
+
+  tree c2 = build_omp_clause (OMP_CLAUSE_LOCATION (grp_end), OMP_CLAUSE_MAP);
   OMP_CLAUSE_SET_MAP_KIND (c2, mkind);
-  OMP_CLAUSE_DECL (c2) = unshare_expr (OMP_CLAUSE_DECL (c));
-  OMP_CLAUSE_CHAIN (c2) = scp ? *scp : prev_node;
-  if (OMP_CLAUSE_CHAIN (prev_node) != c
-      && OMP_CLAUSE_CODE (OMP_CLAUSE_CHAIN (prev_node)) == OMP_CLAUSE_MAP
-      && (OMP_CLAUSE_MAP_KIND (OMP_CLAUSE_CHAIN (prev_node))
-         == GOMP_MAP_TO_PSET))
-    OMP_CLAUSE_SIZE (c2) = OMP_CLAUSE_SIZE (OMP_CLAUSE_CHAIN (prev_node));
+  OMP_CLAUSE_DECL (c2) = unshare_expr (OMP_CLAUSE_DECL (grp_end));
+  OMP_CLAUSE_CHAIN (c2) = NULL_TREE;
+  tree grp_mid = NULL_TREE;
+  if (OMP_CLAUSE_CHAIN (grp_start) != grp_end)
+    grp_mid = OMP_CLAUSE_CHAIN (grp_start);
+
+  if (grp_mid
+      && OMP_CLAUSE_CODE (grp_mid) == OMP_CLAUSE_MAP
+      && OMP_CLAUSE_MAP_KIND (grp_mid) == GOMP_MAP_TO_PSET)
+    OMP_CLAUSE_SIZE (c2) = OMP_CLAUSE_SIZE (grp_mid);
   else
     OMP_CLAUSE_SIZE (c2) = TYPE_SIZE_UNIT (ptr_type_node);
-  if (struct_node)
-    OMP_CLAUSE_CHAIN (struct_node) = c2;
 
-  /* We might need to create an additional mapping if we have a reference to a
-     pointer (in C++).  Don't do this if we have something other than a
-     GOMP_MAP_ALWAYS_POINTER though, i.e. a GOMP_MAP_TO_PSET.  */
-  if (OMP_CLAUSE_CHAIN (prev_node) != c
-      && OMP_CLAUSE_CODE (OMP_CLAUSE_CHAIN (prev_node)) == OMP_CLAUSE_MAP
-      && ((OMP_CLAUSE_MAP_KIND (OMP_CLAUSE_CHAIN (prev_node))
-          == GOMP_MAP_ALWAYS_POINTER)
-         || (OMP_CLAUSE_MAP_KIND (OMP_CLAUSE_CHAIN (prev_node))
-             == GOMP_MAP_ATTACH_DETACH)))
+  if (grp_mid
+      && OMP_CLAUSE_CODE (grp_mid) == OMP_CLAUSE_MAP
+      && (OMP_CLAUSE_MAP_KIND (grp_mid) == GOMP_MAP_ALWAYS_POINTER
+         || OMP_CLAUSE_MAP_KIND (grp_mid) == GOMP_MAP_ATTACH_DETACH))
     {
-      tree c4 = OMP_CLAUSE_CHAIN (prev_node);
-      tree c3 = build_omp_clause (OMP_CLAUSE_LOCATION (c), OMP_CLAUSE_MAP);
+      tree c3
+       = build_omp_clause (OMP_CLAUSE_LOCATION (grp_end), OMP_CLAUSE_MAP);
       OMP_CLAUSE_SET_MAP_KIND (c3, mkind);
-      OMP_CLAUSE_DECL (c3) = unshare_expr (OMP_CLAUSE_DECL (c4));
+      OMP_CLAUSE_DECL (c3) = unshare_expr (OMP_CLAUSE_DECL (grp_mid));
       OMP_CLAUSE_SIZE (c3) = TYPE_SIZE_UNIT (ptr_type_node);
-      OMP_CLAUSE_CHAIN (c3) = prev_node;
-      if (!scp)
-       OMP_CLAUSE_CHAIN (c2) = c3;
-      else
-       cl = c3;
+      OMP_CLAUSE_CHAIN (c3) = NULL_TREE;
+
+      *extra_node = c3;
     }
+  else
+    *extra_node = NULL_TREE;
 
-  if (scp)
-    *scp = c2;
-
-  return cl;
+  return c2;
 }
 
 /* Strip ARRAY_REFS or an indirect ref off BASE, find the containing object,
@@ -8807,6 +8800,558 @@ omp_lastprivate_for_combined_outer_constructs (struct 
gimplify_omp_ctx *octx,
     omp_notice_variable (octx, decl, true);
 }
 
+/* Link node NEWNODE so it is pointed to by chain INSERT_AT.  NEWNODE's chain
+   is linked to the previous node pointed to by INSERT_AT.  */
+
+static tree *
+insert_node_after (tree newnode, tree *insert_at)
+{
+  OMP_CLAUSE_CHAIN (newnode) = *insert_at;
+  *insert_at = newnode;
+  return &OMP_CLAUSE_CHAIN (newnode);
+}
+
+/* Move NODE (which is currently pointed to by the chain OLD_POS) so it is
+   pointed to by chain MOVE_AFTER instead.  */
+
+static void
+move_node_after (tree node, tree *old_pos, tree *move_after)
+{
+  gcc_assert (node == *old_pos);
+  *old_pos = OMP_CLAUSE_CHAIN (node);
+  OMP_CLAUSE_CHAIN (node) = *move_after;
+  *move_after = node;
+}
+
+/* Move nodes from FIRST_PTR (pointed to by previous node's chain) to
+   LAST_NODE to after MOVE_AFTER chain.  Similar to below function, but no
+   new nodes are prepended to the list before splicing into the new position.
+   Return the position we should continue scanning the list at, or NULL to
+   stay where we were.  */
+
+static tree *
+move_nodes_after (tree *first_ptr, tree last_node, tree *move_after)
+{
+  if (first_ptr == move_after)
+    return NULL;
+
+  tree tmp = *first_ptr;
+  *first_ptr = OMP_CLAUSE_CHAIN (last_node);
+  OMP_CLAUSE_CHAIN (last_node) = *move_after;
+  *move_after = tmp;
+
+  return first_ptr;
+}
+
+/* Concatenate two lists described by [FIRST_NEW, LAST_NEW_TAIL] and
+   [FIRST_PTR, LAST_NODE], and insert them in the OMP clause list after chain
+   pointer MOVE_AFTER.
+
+   The latter list was previously part of the OMP clause list, and the former
+   (prepended) part is comprised of new nodes.
+
+   We start with a list of nodes starting with a struct mapping node.  We
+   rearrange the list so that new nodes starting from FIRST_NEW and whose last
+   node's chain is LAST_NEW_TAIL comes directly after MOVE_AFTER, followed by
+   the group of mapping nodes we are currently processing (from the chain
+   FIRST_PTR to LAST_NODE).  The return value is the pointer to the next chain
+   we should continue processing from, or NULL to stay where we were.
+
+   The transformation (in the case where MOVE_AFTER and FIRST_PTR are
+   different) is worked through below.  Here we are processing LAST_NODE, and
+   FIRST_PTR points at the preceding mapping clause:
+
+  #. mapping node              chain
+  ---------------------------------------------------
+  A. struct_node               [->B]
+  B. comp_1                    [->C]
+  C. comp_2                    [->D (move_after)]
+  D. map_to_3                  [->E]
+  E. attach_3                  [->F (first_ptr)]
+  F. map_to_4                  [->G (continue_at)]
+  G. attach_4 (last_node)      [->H]
+  H. ...
+
+     *last_new_tail = *first_ptr;
+
+  I. new_node (first_new)      [->F (last_new_tail)]
+
+     *first_ptr = OMP_CLAUSE_CHAIN (last_node)
+
+  #. mapping node              chain
+  ----------------------------------------------------
+  A. struct_node               [->B]
+  B. comp_1                    [->C]
+  C. comp_2                    [->D (move_after)]
+  D. map_to_3                  [->E]
+  E. attach_3                  [->H (first_ptr)]
+  F. map_to_4                  [->G (continue_at)]
+  G. attach_4 (last_node)      [->H]
+  H. ...
+
+  I. new_node (first_new)      [->F  (last_new_tail)]
+
+     OMP_CLAUSE_CHAIN (last_node) = *move_after;
+
+  #. mapping node              chain
+  ---------------------------------------------------
+  A. struct_node               [->B]
+  B. comp_1                    [->C]
+  C. comp_2                    [->D (move_after)]
+  D. map_to_3                  [->E]
+  E. attach_3                  [->H (continue_at)]
+  F. map_to_4                  [->G]
+  G. attach_4 (last_node)      [->D]
+  H. ...
+
+  I. new_node (first_new)      [->F  (last_new_tail)]
+
+     *move_after = first_new;
+
+  #. mapping node              chain
+  ---------------------------------------------------
+  A. struct_node               [->B]
+  B. comp_1                    [->C]
+  C. comp_2                    [->I (move_after)]
+  D. map_to_3                  [->E]
+  E. attach_3                  [->H (continue_at)]
+  F. map_to_4                  [->G]
+  G. attach_4 (last_node)      [->D]
+  H. ...
+  I. new_node (first_new)      [->F (last_new_tail)]
+
+  or, in order:
+
+  #. mapping node              chain
+  ---------------------------------------------------
+  A. struct_node               [->B]
+  B. comp_1                    [->C]
+  C. comp_2                    [->I (move_after)]
+  I. new_node (first_new)      [->F (last_new_tail)]
+  F. map_to_4                  [->G]
+  G. attach_4 (last_node)      [->D]
+  D. map_to_3                  [->E]
+  E. attach_3                  [->H (continue_at)]
+  H. ...
+*/
+
+static tree *
+move_concat_nodes_after (tree first_new, tree *last_new_tail, tree *first_ptr,
+                        tree last_node, tree *move_after)
+{
+  tree *continue_at = NULL;
+  *last_new_tail = *first_ptr;
+  if (first_ptr == move_after)
+    *move_after = first_new;
+  else
+    {
+      *first_ptr = OMP_CLAUSE_CHAIN (last_node);
+      continue_at = first_ptr;
+      OMP_CLAUSE_CHAIN (last_node) = *move_after;
+      *move_after = first_new;
+    }
+  return continue_at;
+}
+
+/* Mapping struct members causes an additional set of nodes to be created,
+   starting with GOMP_MAP_STRUCT followed by a number of mappings equal to the
+   number of members being mapped, in order of ascending position (address or
+   bitwise).
+
+   We scan through the list of mapping clauses, calling this function for each
+   struct member mapping we find, and build up the list of mappings after the
+   initial GOMP_MAP_STRUCT node.  For pointer members, these will be
+   newly-created ALLOC nodes.  For non-pointer members, the existing mapping is
+   moved into place in the sorted list.
+
+     struct {
+       int *a;
+       int *b;
+       int c;
+       int *d;
+     };
+
+     #pragma (acc|omp directive) copy(struct.a[0:n], struct.b[0:n], struct.c,
+                                     struct.d[0:n])
+
+     GOMP_MAP_STRUCT (4)
+     [GOMP_MAP_FIRSTPRIVATE_REFERENCE -- for refs to structs]
+     GOMP_MAP_ALLOC  (struct.a)
+     GOMP_MAP_ALLOC  (struct.b)
+     GOMP_MAP_TO     (struct.c)
+     GOMP_MAP_ALLOC  (struct.d)
+     ...
+
+   In the case where we are mapping references to pointers, or in Fortran if
+   we are mapping an array with a descriptor, additional nodes may be created
+   after the struct node list also.
+
+   The return code is:
+     - DECL, if we just created the initial GOMP_MAP_STRUCT node.
+     - NULL_TREE, if we inserted the new struct member successfully.
+     - error_mark_node if an error occurred.
+
+   *CONT is set to TRUE if we should skip further processing and move to the
+   next node.  PREV_LIST_P and LIST_P may be modified by the function when a
+   list rearrangement has taken place.  */
+
+static tree
+build_struct_group (struct gimplify_omp_ctx *ctx,
+                   enum omp_region_type region_type, enum tree_code code,
+                   tree decl, tree *pd, bool component_ref_p,
+                   unsigned int *flags, tree c,
+                   hash_map<tree_operand_hash, tree> *&struct_map_to_clause,
+                   hash_map<tree_operand_hash, tree *> *&struct_seen_clause,
+                   tree *&prev_list_p, tree *&list_p, gimple_seq *pre_p,
+                   bool *cont)
+{
+  poly_offset_int coffset;
+  poly_int64 cbitpos;
+  tree base_ref, tree_coffset;
+  tree ocd = OMP_CLAUSE_DECL (c);
+
+  while (TREE_CODE (ocd) == ARRAY_REF)
+    ocd = TREE_OPERAND (ocd, 0);
+
+  if (TREE_CODE (ocd) == INDIRECT_REF)
+    ocd = TREE_OPERAND (ocd, 0);
+
+  tree base = extract_base_bit_offset (ocd, &base_ref,
+                                      &cbitpos, &coffset, &tree_coffset);
+
+  bool do_map_struct = (base == decl && !tree_coffset);
+
+  /* Here, DECL is usually a DECL_P, unless we have chained indirect member
+     accesses, e.g. mystruct->a->b.  In that case it'll be the "mystruct->a"
+     part.  */
+  splay_tree_node n
+    = (DECL_P (decl)
+       ? splay_tree_lookup (ctx->variables, (splay_tree_key) decl)
+       : NULL);
+  bool ptr = (OMP_CLAUSE_MAP_KIND (c) == GOMP_MAP_ALWAYS_POINTER);
+  bool attach_detach = (OMP_CLAUSE_MAP_KIND (c) == GOMP_MAP_ATTACH_DETACH);
+  bool attach = (OMP_CLAUSE_MAP_KIND (c) == GOMP_MAP_ATTACH
+                || OMP_CLAUSE_MAP_KIND (c) == GOMP_MAP_DETACH);
+  bool has_attachments = false;
+
+  /* For OpenACC, pointers in structs should trigger an attach action.  */
+  if (attach_detach
+      && ((region_type & (ORT_ACC | ORT_TARGET | ORT_TARGET_DATA))
+         || code == OMP_TARGET_ENTER_DATA
+         || code == OMP_TARGET_EXIT_DATA))
+    {
+      /* Turn a GOMP_MAP_ATTACH_DETACH clause into a GOMP_MAP_ATTACH or
+        GOMP_MAP_DETACH clause after we have detected a case that needs a
+        GOMP_MAP_STRUCT mapping added.  */
+      gomp_map_kind k
+       = ((code == OACC_EXIT_DATA || code == OMP_TARGET_EXIT_DATA)
+          ? GOMP_MAP_DETACH : GOMP_MAP_ATTACH);
+      OMP_CLAUSE_SET_MAP_KIND (c, k);
+      has_attachments = true;
+    }
+
+  /* We currently don't handle non-constant offset accesses wrt to
+     GOMP_MAP_STRUCT elements.  */
+  if (!do_map_struct)
+    return NULL_TREE;
+
+  /* Nor for attach_detach for OpenMP.  */
+  if ((code == OMP_TARGET
+       || code == OMP_TARGET_DATA
+       || code == OMP_TARGET_UPDATE
+       || code == OMP_TARGET_ENTER_DATA
+       || code == OMP_TARGET_EXIT_DATA)
+      && attach_detach)
+    {
+      if (DECL_P (decl))
+       {
+         if (struct_seen_clause == NULL)
+           struct_seen_clause = new hash_map<tree_operand_hash, tree *>;
+         if (!struct_seen_clause->get (decl))
+           struct_seen_clause->put (decl, list_p);
+       }
+
+      return NULL_TREE;
+    }
+
+  if ((DECL_P (decl) && (n == NULL || (n->value & GOVD_MAP) == 0))
+      || (!DECL_P (decl)
+         && (!struct_map_to_clause
+             || struct_map_to_clause->get (decl) == NULL)))
+    {
+      tree l = build_omp_clause (OMP_CLAUSE_LOCATION (c), OMP_CLAUSE_MAP);
+      gomp_map_kind k = attach ? GOMP_MAP_FORCE_PRESENT : GOMP_MAP_STRUCT;
+
+      OMP_CLAUSE_SET_MAP_KIND (l, k);
+
+      if (base_ref)
+       OMP_CLAUSE_DECL (l) = unshare_expr (base_ref);
+      else
+       {
+         OMP_CLAUSE_DECL (l) = unshare_expr (decl);
+         if (!DECL_P (OMP_CLAUSE_DECL (l))
+             && (gimplify_expr (&OMP_CLAUSE_DECL (l), pre_p, NULL,
+                                is_gimple_lvalue, fb_lvalue) == GS_ERROR))
+           return error_mark_node;
+       }
+      OMP_CLAUSE_SIZE (l)
+       = (!attach ? size_int (1)
+          : (DECL_P (OMP_CLAUSE_DECL (l))
+             ? DECL_SIZE_UNIT (OMP_CLAUSE_DECL (l))
+             : TYPE_SIZE_UNIT (TREE_TYPE (OMP_CLAUSE_DECL (l)))));
+      if (struct_map_to_clause == NULL)
+       struct_map_to_clause = new hash_map<tree_operand_hash, tree>;
+      struct_map_to_clause->put (decl, l);
+
+      if (ptr || attach_detach)
+       {
+         tree extra_node;
+         tree alloc_node
+           = build_struct_comp_nodes (code, *prev_list_p, c, &extra_node);
+         OMP_CLAUSE_CHAIN (l) = alloc_node;
+
+         tree **sc = (struct_seen_clause
+                      ? struct_seen_clause->get (decl)
+                      : NULL);
+         tree *insert_node_pos = sc ? *sc : prev_list_p;
+
+         if (extra_node)
+           {
+             OMP_CLAUSE_CHAIN (extra_node) = *insert_node_pos;
+             OMP_CLAUSE_CHAIN (alloc_node) = extra_node;
+           }
+         else
+           OMP_CLAUSE_CHAIN (alloc_node) = *insert_node_pos;
+
+         *insert_node_pos = l;
+         prev_list_p = NULL;
+       }
+      else
+       list_p = insert_node_after (l, list_p);
+
+       /* Handle pointers to structs and references to structs: these cases
+          have an additional GOMP_MAP_FIRSTPRIVATE_{REFERENCE,POINTER} node
+          inserted after the GOMP_MAP_STRUCT node.  References to pointers
+          use GOMP_MAP_FIRSTPRIVATE_REFERENCE.  */
+      if (base_ref && code == OMP_TARGET)
+       {
+         tree c2 = build_omp_clause (OMP_CLAUSE_LOCATION (c),
+                                     OMP_CLAUSE_MAP);
+         enum gomp_map_kind mkind
+           = GOMP_MAP_FIRSTPRIVATE_REFERENCE;
+         OMP_CLAUSE_SET_MAP_KIND (c2, mkind);
+         OMP_CLAUSE_DECL (c2) = decl;
+         OMP_CLAUSE_SIZE (c2) = size_zero_node;
+         OMP_CLAUSE_CHAIN (c2) = OMP_CLAUSE_CHAIN (l);
+         OMP_CLAUSE_CHAIN (l) = c2;
+       }
+      *flags = GOVD_MAP | GOVD_EXPLICIT;
+      if (GOMP_MAP_ALWAYS_P (OMP_CLAUSE_MAP_KIND (c)) || ptr || attach_detach)
+       *flags |= GOVD_SEEN;
+      if (has_attachments)
+       *flags |= GOVD_MAP_HAS_ATTACHMENTS;
+
+      /* If this is a *pointer-to-struct expression, make sure a
+        firstprivate map of the base-pointer exists.  */
+      if (component_ref_p
+         && ((TREE_CODE (decl) == MEM_REF
+              && integer_zerop (TREE_OPERAND (decl, 1)))
+             || INDIRECT_REF_P (decl))
+         && DECL_P (TREE_OPERAND (decl, 0))
+         && !splay_tree_lookup (ctx->variables,
+                                ((splay_tree_key) TREE_OPERAND (decl, 0))))
+       {
+         decl = TREE_OPERAND (decl, 0);
+         tree c2 = build_omp_clause (OMP_CLAUSE_LOCATION (c), OMP_CLAUSE_MAP);
+         enum gomp_map_kind mkind = GOMP_MAP_FIRSTPRIVATE_POINTER;
+         OMP_CLAUSE_SET_MAP_KIND (c2, mkind);
+         OMP_CLAUSE_DECL (c2) = decl;
+         OMP_CLAUSE_SIZE (c2) = size_zero_node;
+         OMP_CLAUSE_CHAIN (c2) = OMP_CLAUSE_CHAIN (c);
+         OMP_CLAUSE_CHAIN (c) = c2;
+       }
+
+      return decl;
+    }
+  else if (struct_map_to_clause)
+    {
+      tree *osc = struct_map_to_clause->get (decl);
+      tree *sc = NULL, *scp = NULL;
+      if (n && (GOMP_MAP_ALWAYS_P (OMP_CLAUSE_MAP_KIND (c))
+               || ptr
+               || attach_detach))
+       n->value |= GOVD_SEEN;
+      sc = &OMP_CLAUSE_CHAIN (*osc);
+      /* The struct mapping might be immediately followed by a
+        FIRSTPRIVATE_REFERENCE if it is a reference.  (This added node is
+        removed in omp-low.c after it has been processed there.)  */
+      if (*sc != c
+         && OMP_CLAUSE_MAP_KIND (*sc) == GOMP_MAP_FIRSTPRIVATE_REFERENCE)
+       sc = &OMP_CLAUSE_CHAIN (*sc);
+      for (; *sc != c; sc = &OMP_CLAUSE_CHAIN (*sc))
+       if ((ptr || attach_detach) && sc == prev_list_p)
+         break;
+       else if (TREE_CODE (OMP_CLAUSE_DECL (*sc)) != COMPONENT_REF
+                && TREE_CODE (OMP_CLAUSE_DECL (*sc)) != INDIRECT_REF
+                && TREE_CODE (OMP_CLAUSE_DECL (*sc)) != ARRAY_REF)
+         break;
+       else
+         {
+           tree sc_decl = OMP_CLAUSE_DECL (*sc);
+           poly_offset_int offset;
+           poly_int64 bitpos;
+           tree tree_offset;
+
+           if (TREE_CODE (sc_decl) == ARRAY_REF)
+             {
+               while (TREE_CODE (sc_decl) == ARRAY_REF)
+                 sc_decl = TREE_OPERAND (sc_decl, 0);
+               if (TREE_CODE (sc_decl) != COMPONENT_REF
+                   || TREE_CODE (TREE_TYPE (sc_decl)) != ARRAY_TYPE)
+                 break;
+             }
+           else if (TREE_CODE (sc_decl) == INDIRECT_REF
+                    && TREE_CODE (TREE_OPERAND (sc_decl, 0)) == COMPONENT_REF
+                    && (TREE_CODE (TREE_TYPE (TREE_OPERAND (sc_decl, 0)))
+                        == REFERENCE_TYPE))
+             sc_decl = TREE_OPERAND (sc_decl, 0);
+
+           tree base = extract_base_bit_offset (sc_decl, NULL, &bitpos,
+                                                &offset, &tree_offset);
+           if (base != decl)
+             break;
+           if (scp)
+             continue;
+           if ((region_type & ORT_ACC) != 0)
+             {
+               /* This duplicate checking code is currently only enabled for
+                  OpenACC.  */
+               tree d1 = OMP_CLAUSE_DECL (*sc);
+               tree d2 = OMP_CLAUSE_DECL (c);
+               while (TREE_CODE (d1) == ARRAY_REF)
+                 d1 = TREE_OPERAND (d1, 0);
+               while (TREE_CODE (d2) == ARRAY_REF)
+                 d2 = TREE_OPERAND (d2, 0);
+               if (TREE_CODE (d1) == INDIRECT_REF)
+                 d1 = TREE_OPERAND (d1, 0);
+               if (TREE_CODE (d2) == INDIRECT_REF)
+                 d2 = TREE_OPERAND (d2, 0);
+               while (TREE_CODE (d1) == COMPONENT_REF)
+                 if (TREE_CODE (d2) == COMPONENT_REF
+                     && TREE_OPERAND (d1, 1) == TREE_OPERAND (d2, 1))
+                   {
+                     d1 = TREE_OPERAND (d1, 0);
+                     d2 = TREE_OPERAND (d2, 0);
+                   }
+                 else
+                   break;
+               if (d1 == d2)
+                 {
+                   error_at (OMP_CLAUSE_LOCATION (c),
+                             "%qE appears more than once in map clauses",
+                             OMP_CLAUSE_DECL (c));
+                   return error_mark_node;
+                 }
+             }
+           if (maybe_lt (coffset, offset)
+               || (known_eq (coffset, offset)
+                   && maybe_lt (cbitpos, bitpos)))
+             {
+               if (ptr || attach_detach)
+                 scp = sc;
+               else
+                 break;
+             }
+         }
+
+      if (!attach)
+       OMP_CLAUSE_SIZE (*osc)
+         = size_binop (PLUS_EXPR, OMP_CLAUSE_SIZE (*osc), size_one_node);
+      if (ptr || attach_detach)
+       {
+         tree cl = NULL_TREE, extra_node;
+         tree alloc_node = build_struct_comp_nodes (code, *prev_list_p, c,
+                                                    &extra_node);
+         tree *tail_chain = NULL;
+
+         /* Here, we have:
+
+            c : the currently-processed node.
+            prev_list_p : pointer to the first node in a pointer mapping group
+                          up to and including C.
+            sc : pointer to the chain for the end of the struct component
+                 list.
+            scp : pointer to the chain for the sorted position at which we
+                  should insert in the middle of the struct component list
+                  (else NULL to insert at end).
+            alloc_node : the "alloc" node for the structure (pointer-type)
+                         component. We insert at SCP (if present), else SC
+                         (the end of the struct component list).
+            extra_node : a newly-synthesized node for an additional indirect
+                         pointer mapping or a Fortran pointer set, if needed.
+            cl : first node to prepend before prev_list_p.
+            tail_chain : pointer to chain of last prepended node.
+
+            The general idea is we move the nodes for this struct mapping
+            together: the alloc node goes into the sorted list directly after
+            the struct mapping, and any extra nodes (together with the nodes
+            mapping arrays pointed to by struct components) get moved after
+            that list.  When SCP is NULL, we insert the nodes at SC, i.e. at
+            the end of the struct component mapping list.  It's important that
+            the alloc_node comes first in that case because it's part of the
+            sorted component mapping list (but subsequent nodes are not!).  */
+
+         if (scp)
+           insert_node_after (alloc_node, scp);
+
+         /* Make [cl,tail_chain] a list of the alloc node (if we haven't
+            already inserted it) and the extra_node (if it is present).  The
+            list can be empty if we added alloc_node above and there is no
+            extra node.  */
+         if (scp && extra_node)
+           {
+             cl = extra_node;
+             tail_chain = &OMP_CLAUSE_CHAIN (extra_node);
+           }
+         else if (extra_node)
+           {
+             OMP_CLAUSE_CHAIN (alloc_node) = extra_node;
+             cl = alloc_node;
+             tail_chain = &OMP_CLAUSE_CHAIN (extra_node);
+           }
+         else if (!scp)
+           {
+             cl = alloc_node;
+             tail_chain = &OMP_CLAUSE_CHAIN (alloc_node);
+           }
+
+         tree *continue_at
+           = cl ? move_concat_nodes_after (cl, tail_chain, prev_list_p, c, sc)
+                : move_nodes_after (prev_list_p, c, sc);
+
+         prev_list_p = NULL;
+
+         if (continue_at)
+           {
+             list_p = continue_at;
+             *cont = true;
+           }
+       }
+      else if (*sc != c)
+       {
+         if (gimplify_expr (pd, pre_p, NULL, is_gimple_lvalue, fb_lvalue)
+             == GS_ERROR)
+           return error_mark_node;
+         /* In the non-pointer case, the mapping clause itself is moved into
+            the correct position in the struct component list, which in this
+            case is just SC.  */
+         move_node_after (c, list_p, sc);
+         *cont = true;
+       }
+    }
+  return NULL_TREE;
+}
+
 /* Scan the OMP clauses in *LIST_P, installing mappings into a new
    and previous omp contexts.  */
 
@@ -9454,323 +9999,25 @@ gimplify_scan_omp_clauses (tree *list_p, gimple_seq 
*pre_p,
                            }
                        }
                    }
-
-                 poly_offset_int offset1;
-                 poly_int64 bitpos1;
-                 tree tree_offset1;
-                 tree base_ref, ocd = OMP_CLAUSE_DECL (c);
-
-                 while (TREE_CODE (ocd) == ARRAY_REF)
-                   ocd = TREE_OPERAND (ocd, 0);
-
-                 if (TREE_CODE (ocd) == INDIRECT_REF)
-                   ocd = TREE_OPERAND (ocd, 0);
-
-                 tree base = extract_base_bit_offset (ocd, &base_ref,
-                                                      &bitpos1, &offset1,
-                                                      &tree_offset1);
-
-                 bool do_map_struct = (base == decl && !tree_offset1);
-
-                 splay_tree_node n
-                   = (DECL_P (decl)
-                      ? splay_tree_lookup (ctx->variables,
-                                           (splay_tree_key) decl)
-                      : NULL);
-                 bool ptr = (OMP_CLAUSE_MAP_KIND (c)
-                             == GOMP_MAP_ALWAYS_POINTER);
-                 bool attach_detach = (OMP_CLAUSE_MAP_KIND (c)
-                                       == GOMP_MAP_ATTACH_DETACH);
-                 bool attach = OMP_CLAUSE_MAP_KIND (c) == GOMP_MAP_ATTACH
-                               || OMP_CLAUSE_MAP_KIND (c) == GOMP_MAP_DETACH;
-                 bool has_attachments = false;
-                 /* For OpenACC, pointers in structs should trigger an
-                    attach action.  */
-                 if (attach_detach
-                     && ((region_type & (ORT_ACC | ORT_TARGET | 
ORT_TARGET_DATA))
-                         || code == OMP_TARGET_ENTER_DATA
-                         || code == OMP_TARGET_EXIT_DATA))
-
+                 bool cont = false;
+                 tree add_decl
+                   = build_struct_group (ctx, region_type, code, decl, pd,
+                                         component_ref_p, &flags, c,
+                                         struct_map_to_clause,
+                                         struct_seen_clause, prev_list_p,
+                                         list_p, pre_p, &cont);
+                 if (add_decl == error_mark_node)
                    {
-                     /* Turn a GOMP_MAP_ATTACH_DETACH clause into a
-                        GOMP_MAP_ATTACH or GOMP_MAP_DETACH clause after we
-                        have detected a case that needs a GOMP_MAP_STRUCT
-                        mapping added.  */
-                     gomp_map_kind k
-                       = ((code == OACC_EXIT_DATA || code == 
OMP_TARGET_EXIT_DATA)
-                          ? GOMP_MAP_DETACH : GOMP_MAP_ATTACH);
-                     OMP_CLAUSE_SET_MAP_KIND (c, k);
-                     has_attachments = true;
+                     remove = true;
+                     break;
                    }
-
-                 /* We currently don't handle non-constant offset accesses wrt 
to
-                    GOMP_MAP_STRUCT elements.  */
-                 if (!do_map_struct)
-                   goto skip_map_struct;
-
-                 /* Nor for attach_detach for OpenMP.  */
-                 if ((code == OMP_TARGET
-                      || code == OMP_TARGET_DATA
-                      || code == OMP_TARGET_UPDATE
-                      || code == OMP_TARGET_ENTER_DATA
-                      || code == OMP_TARGET_EXIT_DATA)
-                     && attach_detach)
+                 else if (add_decl && DECL_P (add_decl))
                    {
-                     if (DECL_P (decl))
-                       {
-                         if (struct_seen_clause == NULL)
-                           struct_seen_clause
-                             = new hash_map<tree_operand_hash, tree *>;
-                         if (!struct_seen_clause->get (decl))
-                           struct_seen_clause->put (decl, list_p);
-                       }
-
-                     goto skip_map_struct;
+                     decl = add_decl;
+                     goto do_add_decl;
                    }
-
-                 if ((DECL_P (decl)
-                      && (n == NULL || (n->value & GOVD_MAP) == 0))
-                     || (!DECL_P (decl)
-                         && (!struct_map_to_clause
-                             || struct_map_to_clause->get (decl) == NULL)))
-                   {
-                     tree l = build_omp_clause (OMP_CLAUSE_LOCATION (c),
-                                                OMP_CLAUSE_MAP);
-                     gomp_map_kind k = attach ? GOMP_MAP_FORCE_PRESENT
-                                              : GOMP_MAP_STRUCT;
-
-                     OMP_CLAUSE_SET_MAP_KIND (l, k);
-                     if (base_ref)
-                       OMP_CLAUSE_DECL (l) = unshare_expr (base_ref);
-                     else
-                       {
-                         OMP_CLAUSE_DECL (l) = unshare_expr (decl);
-                         if (!DECL_P (OMP_CLAUSE_DECL (l))
-                             && (gimplify_expr (&OMP_CLAUSE_DECL (l),
-                                                pre_p, NULL, is_gimple_lvalue,
-                                                fb_lvalue)
-                                 == GS_ERROR))
-                           {
-                             remove = true;
-                             break;
-                           }
-                       }
-                     OMP_CLAUSE_SIZE (l)
-                       = (!attach
-                          ? size_int (1)
-                          : DECL_P (OMP_CLAUSE_DECL (l))
-                          ? DECL_SIZE_UNIT (OMP_CLAUSE_DECL (l))
-                          : TYPE_SIZE_UNIT (TREE_TYPE (OMP_CLAUSE_DECL (l))));
-                     if (struct_map_to_clause == NULL)
-                       struct_map_to_clause
-                         = new hash_map<tree_operand_hash, tree>;
-                     struct_map_to_clause->put (decl, l);
-                     if (ptr || attach_detach)
-                       {
-                         tree **sc = (struct_seen_clause
-                                      ? struct_seen_clause->get (decl)
-                                      : NULL);
-                         tree *insert_node_pos = sc ? *sc : prev_list_p;
-
-                         insert_struct_comp_map (code, c, l, *insert_node_pos,
-                                                 NULL);
-                         *insert_node_pos = l;
-                         prev_list_p = NULL;
-                       }
-                     else
-                       {
-                         OMP_CLAUSE_CHAIN (l) = c;
-                         *list_p = l;
-                         list_p = &OMP_CLAUSE_CHAIN (l);
-                       }
-                     if (base_ref && code == OMP_TARGET)
-                       {
-                         tree c2 = build_omp_clause (OMP_CLAUSE_LOCATION (c),
-                                                     OMP_CLAUSE_MAP);
-                         enum gomp_map_kind mkind
-                           = GOMP_MAP_FIRSTPRIVATE_REFERENCE;
-                         OMP_CLAUSE_SET_MAP_KIND (c2, mkind);
-                         OMP_CLAUSE_DECL (c2) = decl;
-                         OMP_CLAUSE_SIZE (c2) = size_zero_node;
-                         OMP_CLAUSE_CHAIN (c2) = OMP_CLAUSE_CHAIN (l);
-                         OMP_CLAUSE_CHAIN (l) = c2;
-                       }
-                     flags = GOVD_MAP | GOVD_EXPLICIT;
-                     if (GOMP_MAP_ALWAYS_P (OMP_CLAUSE_MAP_KIND (c))
-                         || ptr
-                         || attach_detach)
-                       flags |= GOVD_SEEN;
-                     if (has_attachments)
-                       flags |= GOVD_MAP_HAS_ATTACHMENTS;
-
-                     /* If this is a *pointer-to-struct expression, make sure a
-                        firstprivate map of the base-pointer exists.  */
-                     if (component_ref_p
-                         && ((TREE_CODE (decl) == MEM_REF
-                              && integer_zerop (TREE_OPERAND (decl, 1)))
-                             || INDIRECT_REF_P (decl))
-                         && DECL_P (TREE_OPERAND (decl, 0))
-                         && !splay_tree_lookup (ctx->variables,
-                                                ((splay_tree_key)
-                                                 TREE_OPERAND (decl, 0))))
-                       {
-                         decl = TREE_OPERAND (decl, 0);
-                         tree c2 = build_omp_clause (OMP_CLAUSE_LOCATION (c),
-                                                     OMP_CLAUSE_MAP);
-                         enum gomp_map_kind mkind
-                           = GOMP_MAP_FIRSTPRIVATE_POINTER;
-                         OMP_CLAUSE_SET_MAP_KIND (c2, mkind);
-                         OMP_CLAUSE_DECL (c2) = decl;
-                         OMP_CLAUSE_SIZE (c2) = size_zero_node;
-                         OMP_CLAUSE_CHAIN (c2) = OMP_CLAUSE_CHAIN (c);
-                         OMP_CLAUSE_CHAIN (c) = c2;
-                       }
-
-                     if (DECL_P (decl))
-                       goto do_add_decl;
-                   }
-                 else if (struct_map_to_clause)
-                   {
-                     tree *osc = struct_map_to_clause->get (decl);
-                     tree *sc = NULL, *scp = NULL;
-                     if (GOMP_MAP_ALWAYS_P (OMP_CLAUSE_MAP_KIND (c))
-                         || ptr
-                         || attach_detach)
-                       n->value |= GOVD_SEEN;
-                     sc = &OMP_CLAUSE_CHAIN (*osc);
-                     if (*sc != c
-                         && (OMP_CLAUSE_MAP_KIND (*sc)
-                             == GOMP_MAP_FIRSTPRIVATE_REFERENCE))
-                       sc = &OMP_CLAUSE_CHAIN (*sc);
-                     /* Here "prev_list_p" is the end of the inserted
-                        alloc/release nodes after the struct node, OSC.  */
-                     for (; *sc != c; sc = &OMP_CLAUSE_CHAIN (*sc))
-                       if ((ptr || attach_detach) && sc == prev_list_p)
-                         break;
-                       else if (TREE_CODE (OMP_CLAUSE_DECL (*sc))
-                                != COMPONENT_REF
-                                && (TREE_CODE (OMP_CLAUSE_DECL (*sc))
-                                    != INDIRECT_REF)
-                                && (TREE_CODE (OMP_CLAUSE_DECL (*sc))
-                                    != ARRAY_REF))
-                         break;
-                       else
-                         {
-                           tree sc_decl = OMP_CLAUSE_DECL (*sc);
-                           poly_offset_int offsetn;
-                           poly_int64 bitposn;
-                           tree tree_offsetn;
-
-                           if (TREE_CODE (sc_decl) == ARRAY_REF)
-                             {
-                               while (TREE_CODE (sc_decl) == ARRAY_REF)
-                                 sc_decl = TREE_OPERAND (sc_decl, 0);
-                               if (TREE_CODE (sc_decl) != COMPONENT_REF
-                                   || (TREE_CODE (TREE_TYPE (sc_decl))
-                                       != ARRAY_TYPE))
-                                 break;
-                             }
-                           else if (TREE_CODE (sc_decl) == INDIRECT_REF
-                                    && (TREE_CODE (TREE_OPERAND (sc_decl, 0))
-                                        == COMPONENT_REF)
-                                    && (TREE_CODE (TREE_TYPE
-                                         (TREE_OPERAND (sc_decl, 0)))
-                                        == REFERENCE_TYPE))
-                             sc_decl = TREE_OPERAND (sc_decl, 0);
-
-                           tree base
-                             = extract_base_bit_offset (sc_decl, NULL,
-                                                        &bitposn, &offsetn,
-                                                        &tree_offsetn);
-                           if (base != decl)
-                             break;
-                           if (scp)
-                             continue;
-                           if ((region_type & ORT_ACC) != 0)
-                             {
-                               /* This duplicate checking code is currently 
only
-                                  enabled for OpenACC.  */
-                               tree d1 = OMP_CLAUSE_DECL (*sc);
-                               tree d2 = OMP_CLAUSE_DECL (c);
-                               while (TREE_CODE (d1) == ARRAY_REF)
-                                 d1 = TREE_OPERAND (d1, 0);
-                               while (TREE_CODE (d2) == ARRAY_REF)
-                                 d2 = TREE_OPERAND (d2, 0);
-                               if (TREE_CODE (d1) == INDIRECT_REF)
-                                 d1 = TREE_OPERAND (d1, 0);
-                               if (TREE_CODE (d2) == INDIRECT_REF)
-                                 d2 = TREE_OPERAND (d2, 0);
-                               while (TREE_CODE (d1) == COMPONENT_REF)
-                                 if (TREE_CODE (d2) == COMPONENT_REF
-                                     && TREE_OPERAND (d1, 1)
-                                     == TREE_OPERAND (d2, 1))
-                                   {
-                                     d1 = TREE_OPERAND (d1, 0);
-                                     d2 = TREE_OPERAND (d2, 0);
-                                   }
-                                 else
-                                   break;
-                               if (d1 == d2)
-                                 {
-                                   error_at (OMP_CLAUSE_LOCATION (c),
-                                             "%qE appears more than once in 
map "
-                                             "clauses", OMP_CLAUSE_DECL (c));
-                                   remove = true;
-                                   break;
-                                 }
-                             }
-                           if (maybe_lt (offset1, offsetn)
-                               || (known_eq (offset1, offsetn)
-                                   && maybe_lt (bitpos1, bitposn)))
-                             {
-                               if (ptr || attach_detach)
-                                 scp = sc;
-                               else
-                                 break;
-                             }
-                         }
-                     if (remove)
-                       break;
-                     if (!attach)
-                       OMP_CLAUSE_SIZE (*osc)
-                         = size_binop (PLUS_EXPR, OMP_CLAUSE_SIZE (*osc),
-                                       size_one_node);
-                     if (ptr || attach_detach)
-                       {
-                         tree cl = insert_struct_comp_map (code, c, NULL,
-                                                           *prev_list_p, scp);
-                         if (sc == prev_list_p)
-                           {
-                             *sc = cl;
-                             prev_list_p = NULL;
-                           }
-                         else
-                           {
-                             *prev_list_p = OMP_CLAUSE_CHAIN (c);
-                             list_p = prev_list_p;
-                             prev_list_p = NULL;
-                             OMP_CLAUSE_CHAIN (c) = *sc;
-                             *sc = cl;
-                             continue;
-                           }
-                       }
-                     else if (*sc != c)
-                       {
-                         if (gimplify_expr (pd, pre_p, NULL, is_gimple_lvalue,
-                                            fb_lvalue)
-                             == GS_ERROR)
-                           {
-                             remove = true;
-                             break;
-                           }
-                         *list_p = OMP_CLAUSE_CHAIN (c);
-                         OMP_CLAUSE_CHAIN (c) = *sc;
-                         *sc = c;
-                         continue;
-                       }
-                   }
-               skip_map_struct:
-                 ;
+                 if (cont)
+                   continue;
                }
              else if (OMP_CLAUSE_MAP_KIND (c) == GOMP_MAP_ATTACH_DETACH)
                {
-- 
2.29.2

Reply via email to