]> git.ipfire.org Git - thirdparty/gcc.git/commitdiff
OpenMP 5.0: Allow multiple clauses mapping same variable
authorPaul-Antoine Arras <parras@baylibre.com>
Fri, 15 May 2026 15:45:01 +0000 (17:45 +0200)
committerPaul-Antoine Arras <parras@baylibre.com>
Tue, 19 May 2026 12:14:28 +0000 (14:14 +0200)
This patch allows multiple clauses on the same construct to map the same
variable, which was not valid in OpenMP 4.5, but allowed in 5.0.

Internally, map clauses have to be deduplicated or merged before reaching the
topological sort in gimplify.cc, lest they might result in a cycle. This happens
in two places: first in the respective front-ends before any clause expansion,
then in the gimplifier just before grouping. The second pass is necessary due to
early clause expansion in the FE reintroducing some duplication (see
map-multi-2.f90).

To make duplicate detection and folding easier in Fortran, enum gfc_omp_map_op
is adjusted to have the two least signficant bits mapped to FROM and TO, similar
to gomp_map_kind in gomp-constants.h

This version of the patch only allows multiple clauses mapping the same variable
on OpenMP code; similar OpenACC code will still be rejected (for now). It also
fixes some minor issues: allow array section bounds to be null and run
target-map-multi-2 only on offload device.

gcc/c/ChangeLog:

* c-typeck.cc (c_finish_omp_clauses): Call omp_remove_duplicate_maps
before clause expansion.

gcc/cp/ChangeLog:

* semantics.cc (finish_omp_clauses): Likewise.

gcc/ChangeLog:

* fold-const.cc (operand_compare::operand_equal_p): Handle
OMP_ARRAY_SECTION.
* gimplify.cc (gimplify_scan_omp_clauses): Call
omp_remove_duplicate_maps after partial clause expansion.
* omp-general.cc (omp_remove_duplicate_maps): New function.
* omp-general.h (omp_remove_duplicate_maps): Declare.
* omp-low.cc (install_var_field): Add new 'tree key_expr = NULL_TREE'
default parameter. Set splay-tree lookup key to key_expr instead of
var if key_expr is non-NULL. Adjust call to install_parm_decl.
Update comments.
(scan_sharing_clauses): Use clause tree expression as splay-tree key
for map/to/from and OpenACC firstprivate cases when installing the
variable field into the send/receive record type.
(lower_oacc_reductions): Adjust to find map-clause of reduction
variable, then create receiver-ref.
(lower_omp_target): Adjust to lookup var field using clause expression.

gcc/fortran/ChangeLog:

* gfortran.h (enum gfc_omp_map_op): Dedicate the two LSB to TO and FROM.
* openmp.cc (resolve_omp_clauses): Adjust to allow duplicate
mapped variables for OpenMP.
* trans-openmp.cc (gfc_trans_omp_clauses): Remove duplicates before
clause expansion.

libgomp/ChangeLog:

* testsuite/libgomp.c++/target-map-multi-1.C: New test.
* testsuite/libgomp.c-c++-common/target-map-iterators-6.c: New test.
* testsuite/libgomp.c-c++-common/target-map-multi-1.c: New test.
* testsuite/libgomp.c-c++-common/target-map-multi-2.c: New test.
* testsuite/libgomp.c-c++-common/target-map-multi-3.c: New test.
* testsuite/libgomp.c-c++-common/target-map-multi-4.c: New test.
* testsuite/libgomp.fortran/target-map-multi-1.f90: New test.
* testsuite/libgomp.fortran/target-map-multi-2.f90: New test.
* testsuite/libgomp.fortran/target-map-multi-3.f90: New test.
* testsuite/libgomp.fortran/target-map-multi-4.f90: New test.
* testsuite/libgomp.fortran/target-map-multi-5.f90: New test.

gcc/testsuite/ChangeLog:

* c-c++-common/gomp/clauses-2.c: Adjust testcase.
* c-c++-common/gomp/map-6.c: Adjust testcase.
* gfortran.dg/gomp/pr107214.f90: Adjust testcase.
* c-c++-common/gomp/map-multi-1.c: New test.
* c-c++-common/gomp/map-multi-2.c: New test.
* gfortran.dg/gomp/map-multi-1.f90: New test.
* gfortran.dg/gomp/map-multi-2.f90: New test.

Co-Authored-By: Chung-Lin Tang <cltang@baylibre.com>
Co-Authored-By: Sandra Loosemore <sloosemore@baylibre.com>
28 files changed:
gcc/c/c-typeck.cc
gcc/cp/semantics.cc
gcc/fold-const.cc
gcc/fortran/gfortran.h
gcc/fortran/openmp.cc
gcc/fortran/trans-openmp.cc
gcc/gimplify.cc
gcc/omp-general.cc
gcc/omp-general.h
gcc/omp-low.cc
gcc/testsuite/c-c++-common/gomp/clauses-2.c
gcc/testsuite/c-c++-common/gomp/map-6.c
gcc/testsuite/c-c++-common/gomp/map-multi-1.c [new file with mode: 0644]
gcc/testsuite/c-c++-common/gomp/map-multi-2.c [new file with mode: 0644]
gcc/testsuite/gfortran.dg/gomp/map-multi-1.f90 [new file with mode: 0644]
gcc/testsuite/gfortran.dg/gomp/map-multi-2.f90 [new file with mode: 0644]
gcc/testsuite/gfortran.dg/gomp/pr107214.f90
libgomp/testsuite/libgomp.c++/target-map-multi-1.C [new file with mode: 0644]
libgomp/testsuite/libgomp.c-c++-common/target-map-iterators-6.c [new file with mode: 0644]
libgomp/testsuite/libgomp.c-c++-common/target-map-multi-1.c [new file with mode: 0644]
libgomp/testsuite/libgomp.c-c++-common/target-map-multi-2.c [new file with mode: 0644]
libgomp/testsuite/libgomp.c-c++-common/target-map-multi-3.c [new file with mode: 0644]
libgomp/testsuite/libgomp.c-c++-common/target-map-multi-4.c [new file with mode: 0644]
libgomp/testsuite/libgomp.fortran/target-map-multi-1.f90 [new file with mode: 0644]
libgomp/testsuite/libgomp.fortran/target-map-multi-2.f90 [new file with mode: 0644]
libgomp/testsuite/libgomp.fortran/target-map-multi-3.f90 [new file with mode: 0644]
libgomp/testsuite/libgomp.fortran/target-map-multi-4.f90 [new file with mode: 0644]
libgomp/testsuite/libgomp.fortran/target-map-multi-5.f90 [new file with mode: 0644]

index f36bf539b68deca64534bae2a3ced0bb1a5cc9cb..0c702f1a7e6e06f87335a0e51c6f21a1e6d3caab 100644 (file)
@@ -16718,6 +16718,9 @@ c_finish_omp_clauses (tree clauses, enum c_omp_region_type ort)
   tree init_no_targetsync_clause = NULL_TREE;
   tree depend_clause = NULL_TREE;
 
+  if (!openacc)
+    clauses = omp_remove_duplicate_maps (clauses, true);
+
   bitmap_obstack_initialize (NULL);
   bitmap_initialize (&generic_head, &bitmap_default_obstack);
   bitmap_initialize (&firstprivate_head, &bitmap_default_obstack);
@@ -17914,6 +17917,7 @@ c_finish_omp_clauses (tree clauses, enum c_omp_region_type ort)
            else if (bitmap_bit_p (&map_head, DECL_UID (t))
                     && !bitmap_bit_p (&map_field_head, DECL_UID (t))
                     && ort != C_ORT_OMP
+                    && ort != C_ORT_OMP_TARGET
                     && ort != C_ORT_OMP_EXIT_DATA)
              {
                if (OMP_CLAUSE_CODE (c) != OMP_CLAUSE_MAP)
index 6564d9e37a6f77189f9c7a5d3003a056cd3f87a5..490441dba6c0f9277aa399fd81848c92b09e5f8d 100644 (file)
@@ -7873,6 +7873,9 @@ finish_omp_clauses (tree clauses, enum c_omp_region_type ort)
   tree init_no_targetsync_clause = NULL_TREE;
   tree depend_clause = NULL_TREE;
 
+  if (!openacc)
+    clauses = omp_remove_duplicate_maps (clauses, true);
+
   bitmap_obstack_initialize (NULL);
   bitmap_initialize (&generic_head, &bitmap_default_obstack);
   bitmap_initialize (&firstprivate_head, &bitmap_default_obstack);
@@ -9612,7 +9615,8 @@ finish_omp_clauses (tree clauses, enum c_omp_region_type ort)
              {
                if (bitmap_bit_p (&generic_head, DECL_UID (t))
                    || bitmap_bit_p (&firstprivate_head, DECL_UID (t))
-                   || bitmap_bit_p (&map_firstprivate_head, DECL_UID (t)))
+                   || (openacc
+                       && bitmap_bit_p (&map_firstprivate_head, DECL_UID (t))))
                  {
                    error_at (OMP_CLAUSE_LOCATION (c),
                              "%qD appears more than once in data clauses", t);
@@ -9636,6 +9640,7 @@ finish_omp_clauses (tree clauses, enum c_omp_region_type ort)
            else if (bitmap_bit_p (&map_head, DECL_UID (t))
                     && !bitmap_bit_p (&map_field_head, DECL_UID (t))
                     && ort != C_ORT_OMP
+                    && ort != C_ORT_OMP_TARGET
                     && ort != C_ORT_OMP_EXIT_DATA)
              {
                if (OMP_CLAUSE_CODE (c) != OMP_CLAUSE_MAP)
index a7351445eacb1f3cd44138308fe11d9e9e2c7b55..99056543a04b8d33c27c21abd630eb3b3543ccff 100644 (file)
@@ -3816,6 +3816,9 @@ operand_compare::operand_equal_p (tree type0, const_tree arg0,
          }
        return false;
 
+       case OMP_ARRAY_SECTION:
+         return OP_SAME (0) && OP_SAME_WITH_NULL (1) && OP_SAME_WITH_NULL (2);
+
        default:
          return false;
        }
index 37a8582e36d80eb55a1f71b4505ba3bb74e6955b..16fc5e52cd9b496f7fb5686e0dd53aa6cea237f3 100644 (file)
@@ -1347,33 +1347,33 @@ enum gfc_omp_depend_doacross_op
 
 enum gfc_omp_map_op
 {
-  OMP_MAP_ALLOC,
-  OMP_MAP_IF_PRESENT,
-  OMP_MAP_ATTACH,
-  OMP_MAP_TO,
-  OMP_MAP_FROM,
-  OMP_MAP_TOFROM,
-  OMP_MAP_DELETE,
-  OMP_MAP_DETACH,
-  OMP_MAP_FORCE_ALLOC,
-  OMP_MAP_FORCE_TO,
-  OMP_MAP_FORCE_FROM,
-  OMP_MAP_FORCE_TOFROM,
-  OMP_MAP_FORCE_PRESENT,
-  OMP_MAP_FORCE_DEVICEPTR,
-  OMP_MAP_DEVICE_RESIDENT,
-  OMP_MAP_LINK,
-  OMP_MAP_RELEASE,
-  OMP_MAP_ALWAYS_TO,
-  OMP_MAP_ALWAYS_FROM,
-  OMP_MAP_ALWAYS_TOFROM,
-  OMP_MAP_PRESENT_ALLOC,
-  OMP_MAP_PRESENT_TO,
-  OMP_MAP_PRESENT_FROM,
-  OMP_MAP_PRESENT_TOFROM,
-  OMP_MAP_ALWAYS_PRESENT_TO,
-  OMP_MAP_ALWAYS_PRESENT_FROM,
-  OMP_MAP_ALWAYS_PRESENT_TOFROM
+  OMP_MAP_ALLOC = 0,
+  OMP_MAP_TO = 1 << 0,
+  OMP_MAP_FROM = 1 << 1,
+  OMP_MAP_TOFROM = OMP_MAP_TO | OMP_MAP_FROM,
+  OMP_MAP_IF_PRESENT = 1 << 2,
+  OMP_MAP_ATTACH = 1 << 3,
+  OMP_MAP_DELETE = 1 << 4,
+  OMP_MAP_DETACH = 1 << 5,
+  OMP_MAP_FORCE_ALLOC = 1 << 6,
+  OMP_MAP_FORCE_TO = OMP_MAP_FORCE_ALLOC | OMP_MAP_TO,
+  OMP_MAP_FORCE_FROM = OMP_MAP_FORCE_ALLOC | OMP_MAP_FROM,
+  OMP_MAP_FORCE_TOFROM = OMP_MAP_FORCE_ALLOC | OMP_MAP_TOFROM,
+  OMP_MAP_FORCE_PRESENT = 1 << 7,
+  OMP_MAP_FORCE_DEVICEPTR = 1 << 8,
+  OMP_MAP_DEVICE_RESIDENT = 1 << 9,
+  OMP_MAP_LINK = 1 << 10,
+  OMP_MAP_RELEASE = 1 << 11,
+  OMP_MAP_ALWAYS_TO = (1 << 12) | OMP_MAP_TO,
+  OMP_MAP_ALWAYS_FROM = (1 << 12) | OMP_MAP_FROM,
+  OMP_MAP_ALWAYS_TOFROM = (1 << 12) | OMP_MAP_TOFROM,
+  OMP_MAP_PRESENT_ALLOC = 1 << 13,
+  OMP_MAP_PRESENT_TO = (1 << 13) | OMP_MAP_TO,
+  OMP_MAP_PRESENT_FROM = (1 << 13) | OMP_MAP_FROM,
+  OMP_MAP_PRESENT_TOFROM = (1 << 13) | OMP_MAP_TOFROM,
+  OMP_MAP_ALWAYS_PRESENT_TO = OMP_MAP_ALWAYS_TO | OMP_MAP_PRESENT_TO,
+  OMP_MAP_ALWAYS_PRESENT_FROM = OMP_MAP_ALWAYS_FROM | OMP_MAP_PRESENT_FROM,
+  OMP_MAP_ALWAYS_PRESENT_TOFROM = OMP_MAP_ALWAYS_TOFROM | OMP_MAP_PRESENT_TOFROM
 };
 
 enum gfc_omp_defaultmap
@@ -1421,7 +1421,7 @@ typedef struct gfc_omp_namelist
       gfc_omp_depend_doacross_op depend_doacross_op;
       struct
         {
-         ENUM_BITFIELD (gfc_omp_map_op) op:8;
+         ENUM_BITFIELD (gfc_omp_map_op) op : 16;
          bool readonly;
         } map;
       gfc_expr *align;
index a90600952f1ae5eb72b9a79b3dd4c328afcccc97..18a6d6ea5c54af8adca8f2fa2b821c62df905815 100644 (file)
@@ -9293,7 +9293,7 @@ resolve_omp_clauses (gfc_code *code, gfc_omp_clauses *omp_clauses,
                gfc_error ("Symbol %qs has mixed component and non-component "
                           "accesses at %L", n->sym->name, &n->where);
            }
-         else if (n->sym->mark)
+         else if ((openacc || list != OMP_LIST_MAP) && n->sym->mark)
            gfc_error ("Symbol %qs present on multiple clauses at %L",
                       n->sym->name, &n->where);
          else
index 680e43c1cc0214fa8fb102f2e70b491db37a263c..30b8e604486dc7c11ac0a5137ef5a79ad89aa2cc 100644 (file)
@@ -4308,6 +4308,35 @@ gfc_trans_omp_clauses (stmtblock_t *block, gfc_omp_clauses *clauses,
        case OMP_LIST_MAP:
          for (; n != NULL; n = n->next)
            {
+             if (!openacc)
+               {
+                 // Remove duplicates
+                 bool skip = false;
+                 for (gfc_omp_namelist *n2 = n->next; n2 != NULL;
+                      n2 = n2->next)
+                   {
+                     if (n2->sym == n->sym
+                         && gfc_dep_compare_expr (n2->expr, n->expr) == 0)
+                       {
+                         if (n2->u.map.op == n->u.map.op)
+                           {
+                             skip = true;
+                             break;
+                           }
+                         else if ((n2->u.map.op & ~OMP_MAP_TOFROM)
+                                  == (n->u.map.op & ~OMP_MAP_TOFROM))
+                           {
+                             n2->u.map.op = (enum gfc_omp_map_op) (
+                               n->u.map.op | n2->u.map.op);
+                             skip = true;
+                             break;
+                           }
+                       }
+                   }
+                 if (skip)
+                   continue;
+               }
+
              if (!n->sym->attr.referenced
                  || n->sym->attr.flavor == FL_PARAMETER)
                continue;
index cdd039eb2bde8df2694381827e0ab7c2abe6a4f7..dd5caf5662bf5ea1c23e987b2e517fdec8950e38 100644 (file)
@@ -13676,6 +13676,8 @@ gimplify_scan_omp_clauses (tree *list_p, gimple_seq *pre_p,
       || code == OACC_UPDATE
       || code == OACC_DECLARE)
     {
+      if (!(region_type & ORT_ACC))
+       *list_p = omp_remove_duplicate_maps (*list_p, false);
       groups = omp_gather_mapping_groups (list_p);
 
       if (groups)
index cf8cddc50bcfc23811e1dde02a0be2ab01ea7ab5..7ab9770b98f6f8ff765019354652dfe1eca9de3e 100644 (file)
@@ -5152,3 +5152,98 @@ omp_merge_context_selectors (location_t loc, tree outer_ctx, tree inner_ctx,
   merged_ctx = nreverse (merged_ctx);
   return omp_check_context_selector (loc, merged_ctx, directive);
 }
+
+/* Remove duplicate and merge clauses mapping the same variable. This function
+   is called twice: FIRST in the C and C++ front-ends before any clause
+   expansion happens, then in the gimplifier before gathering groups. This is
+   because it is easier to process most clauses earlier but some duplicates
+   still get introduced during the early clause expansion in the front-ends. */
+
+tree
+omp_remove_duplicate_maps (tree clauses, bool first)
+{
+  if (clauses == NULL_TREE)
+    return NULL_TREE;
+
+  tree outlist = NULL_TREE;
+  tree *outlist_p = &outlist;
+  bool remove = false;
+  tree c1;
+  for (c1 = clauses; OMP_CLAUSE_CHAIN (c1) != NULL_TREE;
+       c1 = OMP_CLAUSE_CHAIN (c1))
+    {
+      if (OMP_CLAUSE_CODE (c1) != OMP_CLAUSE_MAP)
+       {
+         *outlist_p = c1;
+         outlist_p = &OMP_CLAUSE_CHAIN (*outlist_p);
+         continue;
+       }
+
+      for (tree c2 = OMP_CLAUSE_CHAIN (c1); c2 != NULL_TREE;
+          c2 = OMP_CLAUSE_CHAIN (c2))
+       {
+         if (OMP_CLAUSE_CODE (c2) != OMP_CLAUSE_MAP)
+           continue;
+
+         bool maybe_dup_found
+           = (OMP_CLAUSE_CODE (c1) == OMP_CLAUSE_CODE (c2)
+              && ((/* In the current state, a map clause decl is not supposed
+                      to be NULL; but let's be defensive.  */
+                   OMP_CLAUSE_DECL (c1) == NULL_TREE
+                   && OMP_CLAUSE_DECL (c2) == NULL_TREE)
+                  || operand_equal_p (OMP_CLAUSE_DECL (c1),
+                                      OMP_CLAUSE_DECL (c2)))
+              && ((/* The clause size is generally not known right after
+                      parsing.  */
+                   OMP_CLAUSE_SIZE (c1) == NULL_TREE
+                   && OMP_CLAUSE_SIZE (c2) == NULL_TREE)
+                  || (OMP_CLAUSE_SIZE (c1) != NULL_TREE
+                      && OMP_CLAUSE_SIZE (c2) != NULL_TREE
+                      && operand_equal_p (OMP_CLAUSE_SIZE (c1),
+                                          OMP_CLAUSE_SIZE (c2))))
+              && ((OMP_CLAUSE_ITERATORS (c1) == NULL_TREE
+                   && OMP_CLAUSE_ITERATORS (c2) == NULL_TREE)
+                  || operand_equal_p (OMP_CLAUSE_ITERATORS (c1),
+                                      OMP_CLAUSE_ITERATORS (c2))));
+         if (maybe_dup_found)
+           {
+             if (first)
+               {
+                 if (OMP_CLAUSE_MAP_KIND (c1) == OMP_CLAUSE_MAP_KIND (c2))
+                   {
+                     remove = true;
+                     break;
+                   }
+                 else if ((OMP_CLAUSE_MAP_KIND (c1) & ~GOMP_MAP_TOFROM)
+                          == (OMP_CLAUSE_MAP_KIND (c2) & ~GOMP_MAP_TOFROM))
+                   {
+                     OMP_CLAUSE_SET_MAP_KIND (c2,
+                                              (OMP_CLAUSE_MAP_KIND (c1)
+                                               | OMP_CLAUSE_MAP_KIND (c2)));
+                     remove = true;
+                     break;
+                   }
+               }
+             /* When called from the gimplifier, remove duplicate map clauses
+                with identical kind only when the bits above
+                GOMP_MAP_FLAG_SPECIAL_2 are unset - as clauses with those flags
+                set may need to be present multiple times.  */
+             else if (OMP_CLAUSE_MAP_KIND (c1) == OMP_CLAUSE_MAP_KIND (c2)
+                      && (OMP_CLAUSE_MAP_KIND (c1) & ~0b11111) == 0)
+               {
+                 remove = true;
+                 break;
+               }
+           }
+       }
+      if (remove)
+       remove = false;
+      else
+       {
+         *outlist_p = c1;
+         outlist_p = &OMP_CLAUSE_CHAIN (*outlist_p);
+       }
+    }
+  *outlist_p = c1;
+  return outlist;
+}
index 4a6d58471de05d1468192f84a32fa04b8d967c1e..a5126269650c8f8b4415f1362e6e63b61130e0bc 100644 (file)
@@ -417,4 +417,6 @@ extern bool omp_parse_expr (vec<omp_addr_token *> &, tree);
 extern tree omp_loop_number_of_iterations (tree, int, tree * = NULL);
 extern void omp_maybe_apply_loop_xforms (tree *, tree);
 
+extern tree omp_remove_duplicate_maps (tree, bool);
+
 #endif /* GCC_OMP_GENERAL_H */
index b93012107f19d8942516074a813a582994d2bb4f..e8716d9fa37ee17ba9c4985d63b62e9decfb7001 100644 (file)
@@ -784,24 +784,40 @@ build_sender_ref (tree var, omp_context *ctx)
   return build_sender_ref ((splay_tree_key) var, ctx);
 }
 
-/* Add a new field for VAR inside the structure CTX->SENDER_DECL.  If
-   BASE_POINTERS_RESTRICT, declare the field with restrict.  */
+/* Add a new field for VAR inside the structure CTX.  If BY_REF is true,
+   use a pointer to the VAR rather than VAR itself.
+   MASK is a bit mask of other options.  Bits are interpreted as:
+      1: Install VAR in ctx->field_map.
+      2: Install VAR in ctx->sfield_map.
+      4: VAR is an array, convert it to a pointer.
+      8: Use DECL_UID (VAR) instead of VAR as key.
+     16: Use DECL_NAME (VAR) instead of VAR as key.
+     32: Don't dereference omp_is_reference types.
+   KEY_EXPR allows specifying something other than VAR as the lookup key.
+   If specified, it also overrides the 8 and 16 MASK bits.  */
 
 static void
-install_var_field (tree var, bool by_ref, int mask, omp_context *ctx)
+install_var_field (tree var, bool by_ref, int mask, omp_context *ctx,
+                  tree key_expr = NULL_TREE)
 {
   tree field, type, sfield = NULL_TREE;
   splay_tree_key key = (splay_tree_key) var;
 
-  if ((mask & 16) != 0)
-    {
-      key = (splay_tree_key) &DECL_NAME (var);
-      gcc_checking_assert (key != (splay_tree_key) var);
-    }
-  if ((mask & 8) != 0)
+  if (key_expr)
+    /* Allow caller to explicitly set the expression used as the key.  */
+    key = (splay_tree_key) key_expr;
+  else
     {
-      key = (splay_tree_key) &DECL_UID (var);
-      gcc_checking_assert (key != (splay_tree_key) var);
+      if ((mask & 16) != 0)
+       {
+         key = (splay_tree_key) &DECL_NAME (var);
+         gcc_checking_assert (key != (splay_tree_key) var);
+       }
+      if ((mask & 8) != 0)
+       {
+         key = (splay_tree_key) &DECL_UID (var);
+         gcc_checking_assert (key != (splay_tree_key) var);
+       }
     }
   gcc_assert ((mask & 1) == 0
              || !splay_tree_lookup (ctx->field_map, key));
@@ -1389,8 +1405,13 @@ scan_sharing_clauses (tree clauses, omp_context *ctx)
                  || (OMP_CLAUSE_CODE (c) == OMP_CLAUSE_HAS_DEVICE_ADDR
                      && lang_hooks.decls.omp_array_data (decl, true)))
                {
+                 /* OpenACC firstprivate clauses are later processed with same
+                    code path as map clauses in lower_omp_target, so follow
+                    the same convention of using the whole clause expression
+                    as splay-tree key.  */
+                 tree k = (is_oacc_parallel_or_serial (ctx) ? c : NULL_TREE);
                  by_ref = !omp_privatize_by_reference (decl);
-                 install_var_field (decl, by_ref, 3, ctx);
+                 install_var_field (decl, by_ref, 3, ctx, k);
                }
              else if (OMP_CLAUSE_CODE (c) == OMP_CLAUSE_HAS_DEVICE_ADDR)
                {
@@ -1683,7 +1704,7 @@ scan_sharing_clauses (tree clauses, omp_context *ctx)
                  gcc_assert (INDIRECT_REF_P (decl2));
                  decl2 = TREE_OPERAND (decl2, 0);
                  gcc_assert (DECL_P (decl2));
-                 install_var_field (decl2, true, 3, ctx);
+                 install_var_field (decl2, true, 3, ctx, c);
                  install_var_local (decl2, ctx);
                  install_var_local (decl, ctx);
                }
@@ -1693,9 +1714,9 @@ scan_sharing_clauses (tree clauses, omp_context *ctx)
                      && OMP_CLAUSE_MAP_KIND (c) == GOMP_MAP_POINTER
                      && !OMP_CLAUSE_MAP_ZERO_BIAS_ARRAY_SECTION (c)
                      && TREE_CODE (TREE_TYPE (decl)) == ARRAY_TYPE)
-                   install_var_field (decl, true, 7, ctx);
+                   install_var_field (decl, true, 7, ctx, c);
                  else
-                   install_var_field (decl, true, 3, ctx);
+                   install_var_field (decl, true, 3, ctx, c);
                  if (is_gimple_omp_offloaded (ctx->stmt)
                      && !(is_gimple_omp_oacc (ctx->stmt)
                           && OMP_CLAUSE_MAP_IN_REDUCTION (c)))
@@ -1730,7 +1751,7 @@ scan_sharing_clauses (tree clauses, omp_context *ctx)
                                  FIELD_DECL, NULL_TREE, ptr_type_node);
                  SET_DECL_ALIGN (field, TYPE_ALIGN (ptr_type_node));
                  insert_field_into_struct (ctx->record_type, field);
-                 splay_tree_insert (ctx->field_map, (splay_tree_key) decl,
+                 splay_tree_insert (ctx->field_map, (splay_tree_key) c,
                                     (splay_tree_value) field);
                }
            }
@@ -7453,6 +7474,7 @@ lower_oacc_reductions (location_t loc, tree clauses, tree level, bool inner,
        gcc_checking_assert (!is_oacc_kernels_decomposed_part (ctx));
 
        tree orig = OMP_CLAUSE_DECL (c);
+       tree orig_clause;
        tree var = maybe_lookup_decl (orig, ctx);
        tree ref_to_res = NULL_TREE;
        tree incoming, outgoing, v1, v2, v3;
@@ -7523,10 +7545,20 @@ lower_oacc_reductions (location_t loc, tree clauses, tree level, bool inner,
          do_lookup:
            /* This is the outermost construct with this reduction,
               see if there's a mapping for it.  */
-           if (gimple_code (outer->stmt) == GIMPLE_OMP_TARGET
-               && maybe_lookup_field (orig, outer) && !is_private)
+           orig_clause = NULL_TREE;
+           if (gimple_code (outer->stmt) == GIMPLE_OMP_TARGET)
+             for (tree cls = gimple_omp_target_clauses (outer->stmt);
+                  cls; cls = OMP_CLAUSE_CHAIN (cls))
+               if (OMP_CLAUSE_CODE (cls) == OMP_CLAUSE_MAP
+                   && orig == OMP_CLAUSE_DECL (cls)
+                   && maybe_lookup_field (cls, outer))
+                 {
+                   orig_clause = cls;
+                   break;
+                 }
+           if (orig_clause != NULL_TREE && !is_private)
              {
-               ref_to_res = build_receiver_ref (orig, false, outer);
+               ref_to_res = build_receiver_ref (orig_clause, false, outer);
                if (omp_privatize_by_reference (orig))
                  ref_to_res = build_simple_mem_ref (ref_to_res);
 
@@ -12949,7 +12981,7 @@ lower_omp_target (gimple_stmt_iterator *gsi_p, omp_context *ctx)
            continue;
          }
 
-       if (!maybe_lookup_field (var, ctx))
+       if (!maybe_lookup_field (c, ctx))
          continue;
 
        /* Don't remap compute constructs' reduction variables, because the
@@ -12958,7 +12990,7 @@ lower_omp_target (gimple_stmt_iterator *gsi_p, omp_context *ctx)
                           && is_gimple_omp_oacc (ctx->stmt)
                           && OMP_CLAUSE_MAP_IN_REDUCTION (c)))
          {
-           x = build_receiver_ref (var, true, ctx);
+           x = build_receiver_ref (c, true, ctx);
            tree new_var = lookup_decl (var, ctx);
 
            if (OMP_CLAUSE_CODE (c) == OMP_CLAUSE_MAP
@@ -13353,7 +13385,7 @@ lower_omp_target (gimple_stmt_iterator *gsi_p, omp_context *ctx)
                  }
                else
                  {
-                   tree x = build_sender_ref (ovar, ctx);
+                   tree x = build_sender_ref (c, ctx);
                    tree v = ovar;
                    if (in_reduction_clauses
                        && OMP_CLAUSE_CODE (c) == OMP_CLAUSE_MAP
@@ -13402,7 +13434,7 @@ lower_omp_target (gimple_stmt_iterator *gsi_p, omp_context *ctx)
                    gcc_assert (DECL_P (ovar2));
                    ovar = ovar2;
                  }
-               if (!maybe_lookup_field (ovar, ctx)
+               if (!maybe_lookup_field (c, ctx)
                    && !(OMP_CLAUSE_CODE (c) == OMP_CLAUSE_MAP
                         && (OMP_CLAUSE_MAP_KIND (c) == GOMP_MAP_ATTACH
                             || OMP_CLAUSE_MAP_KIND (c) == GOMP_MAP_DETACH)))
@@ -13452,7 +13484,7 @@ lower_omp_target (gimple_stmt_iterator *gsi_p, omp_context *ctx)
              }
            else if (nc)
              {
-               x = build_sender_ref (ovar, ctx);
+               x = build_sender_ref (nc, ctx);
 
                if (OMP_CLAUSE_CODE (c) == OMP_CLAUSE_MAP
                    && OMP_CLAUSE_MAP_KIND (c) == GOMP_MAP_POINTER
@@ -14416,7 +14448,7 @@ lower_omp_target (gimple_stmt_iterator *gsi_p, omp_context *ctx)
                    type = TREE_TYPE (type);
                    ref_to_ptr = true;
                  }
-               x = build_receiver_ref (OMP_CLAUSE_DECL (prev), false, ctx);
+               x = build_receiver_ref (prev, false, ctx);
                x = fold_convert_loc (clause_loc, type, x);
                if (!integer_zerop (OMP_CLAUSE_SIZE (c)))
                  {
index 8f98d57a312fa6d6ee14783f91e44edbaa5f2ea2..d5018fa1ffa6d5ba602d3c04565baaea982afb73 100644 (file)
@@ -15,9 +15,9 @@ foo (int *p, int q, struct S t, int i, int j, int k, int l)
     bar (p);
   #pragma omp target map (p) , map (p[0])
     bar (p);
-  #pragma omp target map (q) map (q) /* { dg-error "appears more than once in map clauses" } */
+  #pragma omp target map(q) map(q)
     bar (&q);
-  #pragma omp target map (p[0]) map (p[0]) /* { dg-error "appears more than once in data clauses" } */
+  #pragma omp target map(p[0]) map(p[0])
     bar (p);
   #pragma omp target map (t) map (t.r)
     bar (&t.r);
index c1f3d1079b4b7e1d2ca3464d8644aec63f308fd8..aa3e9786e5c78a253c52ee950a8f097a8bba5c7e 100644 (file)
@@ -157,10 +157,10 @@ foo (void)
   #pragma omp target map (always, close)
   ;
 
-  #pragma omp target map (always, always)  /* { dg-error "'always' appears more than once in map clauses" } */
+  #pragma omp target map (always, always)
   ;
 
-  #pragma omp target map (always, always, close)  /* { dg-error "'always' appears more than once in map clauses" } */
+  #pragma omp target map (always, always, close)
   ;
 
   #pragma omp target map (always, close, to: always, close, b7)
diff --git a/gcc/testsuite/c-c++-common/gomp/map-multi-1.c b/gcc/testsuite/c-c++-common/gomp/map-multi-1.c
new file mode 100644 (file)
index 0000000..496dcf6
--- /dev/null
@@ -0,0 +1,36 @@
+/* { dg-do compile } */
+/* { dg-additional-options "-fdump-tree-original" } */
+
+/* Check that extraneous clauses mapping the same variable multiple times are
+   either removed or merged.  */
+
+#define DIM 17
+
+void f (int *x)
+{
+  #pragma omp target map(alloc: x) map(to: x) map(alloc: x) map(from: x) map(alloc: x)
+    {
+      for (int i = 0; i < DIM; i++)
+        x[i] += i;
+    }
+  #pragma omp target map(alloc: x) map(alloc: x) map(alloc: x)
+    {
+      for (int i = 0; i < DIM; i++)
+        x[i] += i;
+    }
+  #pragma omp target map(alloc: x) map(to: x) map(alloc: x) map(to: x) map(alloc: x)
+    {
+      for (int i = 0; i < DIM; i++)
+        x[i] += i;
+    }
+  #pragma omp target map(alloc: x) map(from: x) map(alloc: x) map(from: x) map(alloc: x)
+    {
+      for (int i = 0; i < DIM; i++)
+        x[i] += i;
+    }
+}
+
+/* { dg-final { scan-tree-dump-times {map\(tofrom:x\)} 1 "original" } } */
+/* { dg-final { scan-tree-dump-times {map\(alloc:x\)} 1 "original" } } */
+/* { dg-final { scan-tree-dump-times {map\(to:x\)} 1 "original" } } */
+/* { dg-final { scan-tree-dump-times {map\(from:x\)} 1 "original" } } */
diff --git a/gcc/testsuite/c-c++-common/gomp/map-multi-2.c b/gcc/testsuite/c-c++-common/gomp/map-multi-2.c
new file mode 100644 (file)
index 0000000..3eb4c1c
--- /dev/null
@@ -0,0 +1,41 @@
+/* { dg-do compile } */
+/* { dg-additional-options "-fdump-tree-original" } */
+
+/* Check that map clauses are only merged if they have the same modifiers.  */
+
+#define DIM 17
+
+void f (int *x)
+{
+  #pragma omp target map(always, to: x) map(tofrom: x)
+    {
+      for (int i = 0; i < DIM; i++)
+        x[i] += i;
+    }
+  #pragma omp target map(tofrom: x) map(always, from: x)
+    {
+      for (int i = 0; i < DIM; i++)
+        x[i] += i;
+    }
+  #pragma omp target map(to: x) map(present, alloc: x) map(from: x)
+    {
+      for (int i = 0; i < DIM; i++)
+        x[i] += i;
+    }
+  #pragma omp target map(close,always,from: x) map(close,present,alloc: x) map(present,to: x)
+    {
+      for (int i = 0; i < DIM; i++)
+        x[i] += i;
+    }
+  #pragma omp target map(always,to: x) map(always,present,alloc: x) map(present,from: x)
+    {
+      for (int i = 0; i < DIM; i++)
+        x[i] += i;
+    }
+}
+
+/* { dg-final { scan-tree-dump-times {map\(tofrom:x\) map\(always,to:x\)} 1 "original" } } */
+/* { dg-final { scan-tree-dump-times {map\(always,from:x\) map\(tofrom:x\)} 1 "original" } } */
+/* { dg-final { scan-tree-dump-times {map\(present,alloc:x\) map\(tofrom:x\)} 1 "original" } } */
+/* { dg-final { scan-tree-dump-times {map\(present,to:x\) map\(always,from:x\)} 1 "original" } } */
+/* { dg-final { scan-tree-dump-times {map\(present,from:x\) map\(always,to:x\)} 1 "original" } } */
diff --git a/gcc/testsuite/gfortran.dg/gomp/map-multi-1.f90 b/gcc/testsuite/gfortran.dg/gomp/map-multi-1.f90
new file mode 100644 (file)
index 0000000..c12b6d4
--- /dev/null
@@ -0,0 +1,50 @@
+! { dg-do compile }
+! { dg-additional-options "-fdump-tree-original" }
+
+! Check that extraneous clauses mapping the same variable multiple times are
+! either removed or merged.
+
+subroutine f
+  implicit none
+  integer :: x, i
+
+  !$omp target map(alloc: x) map(to: x) map(alloc: x) map(from: x) map(alloc: x)
+    do i = 1, 17
+      x = x + i
+    end do
+  !$omp end target
+  !$omp target map(alloc: x) map(alloc: x) map(alloc: x)
+    do i = 1, 17
+      x = x + i
+    end do
+  !$omp end target
+  !$omp target map(alloc: x) map(to: x) map(alloc: x) map(to: x) map(alloc: x)
+    do i = 1, 17
+      x = x + i
+    end do
+  !$omp end target
+  !$omp target map(alloc: x) map(from: x) map(alloc: x) map(from: x) map(alloc: x)
+    do i = 1, 17
+      x = x + i
+    end do
+  !$omp end target
+end subroutine f
+
+! { dg-final { scan-tree-dump-times {map\(tofrom:x\)} 1 "original" } }
+! { dg-final { scan-tree-dump-times {map\(alloc:x\)} 1 "original" } }
+! { dg-final { scan-tree-dump-times {map\(to:x\)} 1 "original" } }
+! { dg-final { scan-tree-dump-times {map\(from:x\)} 1 "original" } }
+
+subroutine g (a)
+  integer, intent(inout) :: a(:)
+
+  !$omp target map(to: a) map(alloc: a) map(from: a)
+    a = a * 4
+  !$omp end target
+  !$omp target map(alloc: a) map(tofrom: a) map(alloc: a)
+    a = a * 8
+  !$omp end target
+
+end subroutine
+
+! { dg-final { scan-tree-dump-times {map\(tofrom:\*a\.0\) map\(alloc:a\.0 \[pointer assign, bias: 0\]\)} 2 "original" } }
diff --git a/gcc/testsuite/gfortran.dg/gomp/map-multi-2.f90 b/gcc/testsuite/gfortran.dg/gomp/map-multi-2.f90
new file mode 100644 (file)
index 0000000..fbd4dcb
--- /dev/null
@@ -0,0 +1,60 @@
+! { dg-do compile }
+! { dg-additional-options "-fdump-tree-original -fdump-tree-gimple" }
+
+! Check that map clauses are only merged if they have the same modifiers.
+! Also check that the GIMPLE pass removes duplicates resulting from early clause
+! expansion in the front-end.
+
+subroutine f
+  implicit none
+  integer :: x, i
+
+  !$omp target map(always,to: x) map(tofrom: x)
+    do i = 1, 17
+      x = x + i
+    end do
+  !$omp end target
+  !$omp target map(tofrom: x) map(always,from: x)
+    do i = 1, 17
+      x = x + i
+    end do
+  !$omp end target
+  !$omp target map(to: x) map(present,alloc: x) map(from: x)
+    do i = 1, 17
+      x = x + i
+    end do
+  !$omp end target
+  !$omp target map(close,always,from: x) map(close,present,alloc: x) map(present,to: x)
+    do i = 1, 17
+      x = x + i
+    end do
+  !$omp end target
+  !$omp target map(always,to: x) map(always,present,alloc: x) map(present,from: x)
+    do i = 1, 17
+      x = x + i
+    end do
+  !$omp end target
+end subroutine f
+
+! { dg-final { scan-tree-dump-times {map\(always,to:x\) map\(tofrom:x\)} 1 "original" } }
+! { dg-final { scan-tree-dump-times {map\(tofrom:x\) map\(always,from:x\)} 1 "original" } }
+! { dg-final { scan-tree-dump-times {map\(present,alloc:x\) map\(tofrom:x\)} 1 "original" } }
+! { dg-final { scan-tree-dump-times {map\(always,from:x\) map\(present,to:x\)} 1 "original" } }
+! { dg-final { scan-tree-dump-times {map\(always,to:x\) map\(present,from:x\)} 1 "original" } }
+
+subroutine g (a)
+  integer, intent(inout) :: a(:)
+
+  !$omp target map(always, to: a) map(tofrom: a)
+    a = a * 4
+  !$omp end target
+  !$omp target map(close, present, tofrom: a) map(always, from: a)
+    a = a * 8
+  !$omp end target
+
+end subroutine
+
+! { dg-final { scan-tree-dump-times {map\(alloc:a\.0 \[pointer assign, bias: 0\]\)} 4 "original" } }
+! { dg-final { scan-tree-dump-times {map\(alloc:a\.0 \[pointer assign, bias: 0\]\)} 2 "gimple" } }
+
+
index 25949934e840039da28839b3901691c8a7d03f89..84141b4372752d608fcf3e477f2521b95cf0afb0 100644 (file)
@@ -1,7 +1,10 @@
 ! { dg-do compile }
+! { dg-additional-options "-fdump-tree-original" }
 
 program p
    integer, allocatable :: a
-   !$omp target map(tofrom: a, a) ! { dg-error "Symbol 'a' present on multiple clauses" }
+   !$omp target map(tofrom: a, a)
    !$omp end target
 end
+
+! { dg-final { scan-tree-dump-times {map\(tofrom:\*a\)} 1 "original" } }
diff --git a/libgomp/testsuite/libgomp.c++/target-map-multi-1.C b/libgomp/testsuite/libgomp.c++/target-map-multi-1.C
new file mode 100644 (file)
index 0000000..5450717
--- /dev/null
@@ -0,0 +1,113 @@
+// Test multiple map clauses for the same variable using pointer array sections
+// and C++ references.
+// { dg-do run }
+
+extern "C" void abort ();
+
+static const int N = 100;
+
+/* Test fixed-size array with multiple map clauses.  */
+static void
+test_pointer ()
+{
+  int p[N];
+
+  /* map(to) + map(alloc) + map(from): three clauses on the same array.  */
+  for (int i = 0; i < N; i++) p[i] = i;
+  #pragma omp target map(to: p) map(alloc: p) map(from: p)
+    for (int i = 0; i < N; i++) p[i] *= 4;
+  for (int i = 0; i < N; i++) if (p[i] != i * 4) abort ();
+
+  /* map(to) + map(tofrom): tofrom covers both directions.  */
+  for (int i = 0; i < N; i++) p[i] = i;
+  #pragma omp target map(to: p) map(tofrom: p)
+    for (int i = 0; i < N; i++) p[i] *= 5;
+  for (int i = 0; i < N; i++) if (p[i] != i * 5) abort ();
+
+  /* map(alloc) + map(to): device gets host values via 'to'.  */
+  for (int i = 0; i < N; i++) p[i] = i;
+  int sum = 0;
+  #pragma omp target map(alloc: p) map(to: p) map(tofrom: sum)
+    for (int i = 0; i < N; i++) sum += p[i];
+  if (sum != N * (N - 1) / 2) abort ();
+
+  /* map(alloc) + map(from): device values come back via 'from'.  */
+  #pragma omp target map(alloc: p) map(from: p)
+    for (int i = 0; i < N; i++) p[i] = i * 7;
+  for (int i = 0; i < N; i++) if (p[i] != i * 7) abort ();
+
+  /* map(alloc) + map(tofrom) + map(alloc): three clauses, full bidirectional.  */
+  for (int i = 0; i < N; i++) p[i] = i;
+  #pragma omp target map(alloc: p) map(tofrom: p) map(alloc: p)
+    for (int i = 0; i < N; i++) p[i] *= 8;
+  for (int i = 0; i < N; i++) if (p[i] != i * 8) abort ();
+}
+
+/* Test C++ array references: r aliases a.  */
+static void
+test_reference ()
+{
+  int a[N];
+  int (&r)[N] = a;
+
+  /* map(to: r) + map(from: r): send and receive via the same reference.  */
+  for (int i = 0; i < N; i++) a[i] = i;
+  #pragma omp target map(to: r) map(from: r)
+    for (int i = 0; i < N; i++) r[i] *= 4;
+  for (int i = 0; i < N; i++) if (r[i] != i * 4) abort ();
+
+  /* map(to: r) + map(tofrom: r): to and tofrom on the same reference.  */
+  for (int i = 0; i < N; i++) a[i] = i;
+  #pragma omp target map(to: r) map(tofrom: r)
+    for (int i = 0; i < N; i++) r[i] *= 5;
+  for (int i = 0; i < N; i++) if (r[i] != i * 5) abort ();
+
+  /* map(to: r) + map(alloc: r) + map(from: r): three clauses on the same reference.  */
+  for (int i = 0; i < N; i++) a[i] = i;
+  #pragma omp target map(to: r) map(alloc: r) map(from: r)
+    for (int i = 0; i < N; i++) r[i] *= 6;
+  for (int i = 0; i < N; i++) if (r[i] != i * 6) abort ();
+
+  /* map(alloc: r) + map(to: r): alloc and to on the same reference.  */
+  for (int i = 0; i < N; i++) a[i] = i;
+  int sum = 0;
+  #pragma omp target map(alloc: r) map(to: r) map(tofrom: sum)
+    for (int i = 0; i < N; i++) sum += r[i];
+  if (sum != N * (N - 1) / 2) abort ();
+}
+
+/* Test pointer + array sections with enter/exit data using multiple map
+   clauses.  */
+static void
+test_pointer_enter_exit ()
+{
+  int *p = new int[N];
+
+  /* map(alloc) + map(to) on enter data.  */
+  for (int i = 0; i < N; i++) p[i] = i;
+  #pragma omp target enter data map(alloc: p[0:N]) map(to: p[0:N])
+  int sum = 0;
+  #pragma omp target map(alloc: p[0:N]) map(tofrom: sum)
+    for (int i = 0; i < N; i++) sum += p[i];
+  if (sum != N * (N - 1) / 2) abort ();
+  #pragma omp target exit data map(delete: p[0:N])
+
+  /* map(release) + map(from) on exit data: copy back then release.  */
+  for (int i = 0; i < N; i++) p[i] = i;
+  #pragma omp target enter data map(to: p[0:N])
+  #pragma omp target map(alloc: p[0:N])
+    for (int i = 0; i < N; i++) p[i] *= 3;
+  #pragma omp target exit data map(release: p[0:N]) map(from: p[0:N])
+  for (int i = 0; i < N; i++) if (p[i] != i * 3) abort ();
+
+  delete[] p;
+}
+
+int
+main ()
+{
+  test_pointer ();
+  test_reference ();
+  test_pointer_enter_exit ();
+  return 0;
+}
diff --git a/libgomp/testsuite/libgomp.c-c++-common/target-map-iterators-6.c b/libgomp/testsuite/libgomp.c-c++-common/target-map-iterators-6.c
new file mode 100644 (file)
index 0000000..b6013a9
--- /dev/null
@@ -0,0 +1,43 @@
+/* { dg-do run } */
+/* { dg-require-effective-target offload_device_nonshared_as } */
+
+/* This testcase used to ICE.  Test that an array can appear more than once
+   in map clauses in combination with iterators.  */
+
+#include <stdlib.h>
+
+#define DIM 128
+
+void
+make_array (int *x[], int dim)
+{
+  for (int i = 0; i < DIM; i++)
+    {
+      x[i] = (int *) malloc (sizeof (int));
+      *(x[i]) = i;
+    }
+}
+
+int
+check_array (int *x[], int dim)
+{
+  for (int i = 0; i < DIM; i++)
+    if (*(x[i]) != -i)
+      return 1;
+  return 0;
+}
+
+int
+main (void)
+{
+  int *x[DIM];
+  make_array (x, DIM);
+
+#pragma omp target map (iterator(it = 0:DIM), tofrom: x[it][:1]) map (to: x)
+  {
+    for (int i = 0; i < DIM; i++)
+      x[i][0] = -x[i][0];
+  }
+
+  return check_array (x, DIM);
+}
diff --git a/libgomp/testsuite/libgomp.c-c++-common/target-map-multi-1.c b/libgomp/testsuite/libgomp.c-c++-common/target-map-multi-1.c
new file mode 100644 (file)
index 0000000..6d4d4c3
--- /dev/null
@@ -0,0 +1,72 @@
+/* Test multiple map clauses for the same variable with various
+   combinations of map-types: alloc, to, from, tofrom.  */
+/* { dg-do run } */
+
+#define N 100
+
+int
+main (void)
+{
+  int a[N];
+  int sum;
+
+  /* map(to) + map(from) = map(tofrom).  */
+  for (int i = 0; i < N; i++)
+    a[i] = i;
+  #pragma omp target map(to: a) map(alloc: a) map(from: a)
+    for (int i = 0; i < N; i++)
+      a[i] *= 4;
+  for (int i = 0; i < N; i++)
+    if (a[i] != i * 4)
+      __builtin_abort ();
+
+  /* map(to) + map(tofrom): tofrom covers both directions.  */
+  for (int i = 0; i < N; i++)
+    a[i] = i;
+  #pragma omp target map(to: a) map(tofrom: a) map(to: a)
+    for (int i = 0; i < N; i++)
+      a[i] *= 5;
+  for (int i = 0; i < N; i++)
+    if (a[i] != i * 5)
+      __builtin_abort ();
+
+  /* map(from) + map(tofrom): tofrom covers both directions.  */
+  for (int i = 0; i < N; i++)
+    a[i] = i;
+  #pragma omp target map(from: a) map(tofrom: a) map(from: a)
+    for (int i = 0; i < N; i++)
+      a[i] *= 6;
+  for (int i = 0; i < N; i++)
+    if (a[i] != i * 6)
+      __builtin_abort ();
+
+  /* map(alloc) + map(to): device gets host values via 'to'.  */
+  for (int i = 0; i < N; i++)
+    a[i] = i;
+  sum = 0;
+  #pragma omp target map(alloc: a) map(to: a) map(tofrom: sum) map(alloc: sum)
+    for (int i = 0; i < N; i++)
+      sum += a[i];
+  if (sum != N * (N - 1) / 2)
+    __builtin_abort ();
+
+  /* map(alloc) + map(from): device values come back to host via 'from'.  */
+  #pragma omp target map(alloc: a) map(from: a) map(alloc: a)
+    for (int i = 0; i < N; i++)
+      a[i] = i * 7;
+  for (int i = 0; i < N; i++)
+    if (a[i] != i * 7)
+      __builtin_abort ();
+
+  /* map(alloc) + map(tofrom): full bidirectional transfer.  */
+  for (int i = 0; i < N; i++)
+    a[i] = i;
+  #pragma omp target map(alloc: a) map(tofrom: a) map(alloc: a)
+    for (int i = 0; i < N; i++)
+      a[i] *= 8;
+  for (int i = 0; i < N; i++)
+    if (a[i] != i * 8)
+      __builtin_abort ();
+
+  return 0;
+}
diff --git a/libgomp/testsuite/libgomp.c-c++-common/target-map-multi-2.c b/libgomp/testsuite/libgomp.c-c++-common/target-map-multi-2.c
new file mode 100644 (file)
index 0000000..84f1f2a
--- /dev/null
@@ -0,0 +1,54 @@
+/* Test multiple map clauses for the same variable with the 'always'
+   map-type modifier.  */
+/* { dg-do run { target offload_device_nonshared_as } } */
+
+#define N 100
+
+int
+main (void)
+{
+  int a[N];
+
+  for (int i = 0; i < N; i++)
+    a[i] = i;
+  #pragma omp target data map(tofrom: a)
+    {
+      /* Device has a[i] = i.  Update the host copy to create divergence.  */
+      for (int i = 0; i < N; i++)
+       a[i] = i * 2;
+
+      /* map(always, to) + map(tofrom): 'always' forces the updated host
+        values (i*2) onto the device despite the existing mapping.  */
+      #pragma omp target map(always, to: a) map(tofrom: a)
+       {
+         for (int i = 0; i < N; i++)
+           if (a[i] != i * 2)
+             __builtin_abort ();
+          for (int i = 0; i < N; i++)
+            a[i] = i * 3;
+       }
+
+      for (int i = 0; i < N; i++)
+        if (a[i] != i * 2)
+          __builtin_abort ();
+
+      /* Reset host for the next test; device retains i*3.  */
+      for (int i = 0; i < N; i++)
+        a[i] = i;
+
+      /* map(tofrom) + map(always, from): 'always' forces new device values
+        back to host.  */
+      #pragma omp target map(tofrom: a) map(always, from: a)
+       for (int i = 0; i < N; i++)
+         {
+           if (a[i] != i * 3)
+             __builtin_abort ();
+           a[i] = i * 4;
+         }
+      for (int i = 0; i < N; i++)
+       if (a[i] != i * 4)
+         __builtin_abort ();
+    }
+
+  return 0;
+}
diff --git a/libgomp/testsuite/libgomp.c-c++-common/target-map-multi-3.c b/libgomp/testsuite/libgomp.c-c++-common/target-map-multi-3.c
new file mode 100644 (file)
index 0000000..c7aac62
--- /dev/null
@@ -0,0 +1,22 @@
+/* Test multiple map clauses for the same variable with the 'present'
+   map-type modifier.  */
+/* { dg-do run } */
+
+#define N 100
+
+int
+main (void)
+{
+  int a[N];
+
+  for (int i = 0; i < N; i++)
+    a[i] = i;
+
+      /* { dg-output "libgomp: present clause: not present on the device \\(addr: 0x\[0-9a-f\]+, size: \[0-9\]+ \\(0x\[0-9a-f\]+\\), dev: \[0-9\]+\\\)" { target offload_device_nonshared_as } } */
+    /* { dg-shouldfail "present error triggered" { offload_device_nonshared_as } } */
+      #pragma omp target map(to: a) map(present, alloc: a) map(from: a)
+        for (int i = 0; i < N; i++)
+         a[i] *= 2;
+
+  return 0;
+}
diff --git a/libgomp/testsuite/libgomp.c-c++-common/target-map-multi-4.c b/libgomp/testsuite/libgomp.c-c++-common/target-map-multi-4.c
new file mode 100644 (file)
index 0000000..080ca93
--- /dev/null
@@ -0,0 +1,66 @@
+/* Test multiple map clauses for the same variable in target enter/exit data
+   constructs, including release and delete map-types.  */
+/* { dg-do run } */
+
+#define N 100
+
+int
+main (void)
+{
+  int a[N];
+  int sum;
+
+  /* delete + release  */
+  for (int i = 0; i < N; i++)
+    a[i] = i;
+  #pragma omp target enter data map(alloc: a) map(to: a)
+  sum = 0;
+  #pragma omp target map(alloc: a) map(tofrom: sum)
+    for (int i = 0; i < N; i++)
+      sum += a[i];
+  if (sum != N * (N - 1) / 2)
+    __builtin_abort ();
+  #pragma omp target exit data map(delete: a) map(release: a)
+
+
+  /* release + release: duplicate release
+     decrements the reference count once (deduplicated).  */
+  for (int i = 0; i < N; i++)
+    a[i] = i;
+  #pragma omp target enter data map(to: a)   /* refcount = 1 */
+  #pragma omp target enter data map(to: a)   /* refcount = 2 */
+  #pragma omp target exit data map(release: a) map(release: a)  /* refcount = 1 */
+  sum = 0;
+  #pragma omp target map(alloc: a) map(tofrom: sum)
+    for (int i = 0; i < N; i++)
+      sum += a[i];
+  if (sum != N * (N - 1) / 2)
+    __builtin_abort ();
+  #pragma omp target exit data map(delete: a)   /* refcount = 0 */
+
+  /* delete + delete: duplicate delete
+     removes the mapping unconditionally once (deduplicated).  */
+  for (int i = 0; i < N; i++)
+    a[i] = i;
+  #pragma omp target enter data map(to: a)
+  #pragma omp target exit data map(delete: a) map(delete: a)
+
+  /* from + release: copy device values back
+     to host and release the mapping.  */
+  for (int i = 0; i < N; i++)
+    a[i] = i;
+  #pragma omp target enter data map(to: a)   /* refcount = 1 */
+  #pragma omp target
+    for (int i = 0; i < N; i++)
+      a[i] *= 3;
+  #pragma omp target exit data map(release: a) map(from: a)  /* refcount = 0 */
+  for (int i = 0; i < N; i++)
+    if (a[i] != i * 3)
+      __builtin_abort ();
+
+  for (int i = 0; i < N; i++)
+    if (a[i] != i * 3)
+      __builtin_abort ();
+
+  return 0;
+}
diff --git a/libgomp/testsuite/libgomp.fortran/target-map-multi-1.f90 b/libgomp/testsuite/libgomp.fortran/target-map-multi-1.f90
new file mode 100644 (file)
index 0000000..0dc41bb
--- /dev/null
@@ -0,0 +1,83 @@
+! Test multiple map clauses for the same variable with various
+! combinations of map-types: alloc, to, from, tofrom.
+! { dg-do run }
+
+program main
+  implicit none
+  integer, parameter :: N = 100
+  integer :: a(N), s, i
+
+  ! map(to) + map(alloc) + map(from) = map(tofrom).
+  a = [(i, i=1,N)]
+  !$omp target map(to: a) map(alloc: a) map(from: a)
+    a = a * 4
+  !$omp end target
+  if (any (a /= [(i*4, i=1,N)])) stop 1
+
+  ! map(to) + map(tofrom) + map(to): tofrom covers both directions.
+  a = [(i, i=1,N)]
+  !$omp target map(to: a) map(tofrom: a) map(to: a)
+    a = a * 5
+  !$omp end target
+  if (any (a /= [(i*5, i=1,N)])) stop 2
+
+  ! map(from) + map(tofrom) + map(from): tofrom covers both directions.
+  a = [(i, i=1,N)]
+  !$omp target map(from: a) map(tofrom: a) map(from: a)
+    a = a * 6
+  !$omp end target
+  if (any (a /= [(i*6, i=1,N)])) stop 3
+
+  ! map(alloc) + map(to): device gets host values via 'to'.
+  ! Also tests map(tofrom) + map(alloc) on the scalar s.
+  a = [(i, i=1,N)]
+  s = 0
+  !$omp target map(alloc: a) map(to: a) map(tofrom: s) map(alloc: s)
+    do i = 1, N
+      s = s + a(i)
+    end do
+  !$omp end target
+  if (s /= N * (N + 1) / 2) stop 4
+
+  ! map(alloc) + map(from) + map(alloc): device values come back via 'from'.
+  !$omp target map(alloc: a) map(from: a) map(alloc: a)
+    do i = 1, N
+      a(i) = i * 7
+    end do
+  !$omp end target
+  if (any (a /= [(i*7, i=1,N)])) stop 5
+
+  ! map(alloc) + map(tofrom) + map(alloc): full bidirectional transfer.
+  a = [(i, i=1,N)]
+  !$omp target map(alloc: a) map(tofrom: a) map(alloc: a)
+    a = a * 8
+  !$omp end target
+  if (any (a /= [(i*8, i=1,N)])) stop 6
+
+  ! Same tests via a subroutine to verify correct behaviour with
+  ! dummy arguments (passed by reference).
+  call test_dummy (a)
+
+contains
+
+  subroutine test_dummy (a)
+    integer, intent(inout) :: a(:)
+    integer :: i
+
+    ! map(to) + map(alloc) + map(from) with dummy argument.
+    a = [(i, i=1,size(a))]
+    !$omp target map(to: a) map(alloc: a) map(from: a)
+      a = a * 4
+    !$omp end target
+    if (any (a /= [(i*4, i=1,size(a))])) stop 7
+
+    ! map(alloc) + map(tofrom) + map(alloc) with dummy argument.
+    a = [(i, i=1,size(a))]
+    !$omp target map(alloc: a) map(tofrom: a) map(alloc: a)
+      a = a * 8
+    !$omp end target
+    if (any (a /= [(i*8, i=1,size(a))])) stop 8
+
+  end subroutine
+
+end program
diff --git a/libgomp/testsuite/libgomp.fortran/target-map-multi-2.f90 b/libgomp/testsuite/libgomp.fortran/target-map-multi-2.f90
new file mode 100644 (file)
index 0000000..373074a
--- /dev/null
@@ -0,0 +1,84 @@
+! Test multiple map clauses for the same variable with the 'always'
+! map-type modifier.
+! { dg-do run { target offload_device_nonshared_as } }
+
+program main
+  implicit none
+  integer, parameter :: N = 100
+  integer :: a(N), i
+
+  a = [(i, i=1,N)]
+  !$omp target data map(tofrom: a)
+    ! Device has a(i) = i.  Update the host copy to create divergence.
+    a = [(i*2, i=1,N)]
+
+    ! map(always, to) + map(tofrom): 'always' forces the updated host
+    ! values (i*2) onto the device despite the existing mapping.
+    !$omp target map(always, to: a) map(tofrom: a)
+      do i = 1, N
+        if (a(i) /= i*2) stop 1
+      end do
+      a = [(i*3, i=1,N)]
+    !$omp end target
+
+    do i = 1, N
+      if (a(i) /= i*2) stop 2
+    end do  
+
+    ! Reset host for the next test; device retains i*3.
+    a = [(i, i=1,N)]
+
+    ! map(tofrom) + map(always, from): 'always' forces new device values
+    ! back to host.
+    !$omp target map(tofrom: a) map(always, from: a)
+      do i = 1, N
+        if (a(i) /= i*3) stop 3
+        a(i) = i*4
+      end do
+    !$omp end target
+    do i = 1, N
+      if (a(i) /= i*4) stop 4
+    end do
+  !$omp end target data
+
+  ! Same tests via a subroutine to verify correct behaviour with
+  ! dummy arguments (passed by reference).
+  call test_dummy (a)
+
+contains
+
+  subroutine test_dummy (a)
+    integer, intent(inout) :: a(:)
+    integer :: i
+
+    a = [(i, i=1,size(a))]
+    !$omp target data map(tofrom: a)
+      a = [(i*2, i=1,size(a))]
+
+      !$omp target map(always, to: a) map(tofrom: a)
+        do i = 1, size(a)
+          if (a(i) /= i*2) stop 5
+        end do
+        a = [(i*3, i=1,size(a))]
+      !$omp end target
+
+      do i = 1, size(a)
+        if (a(i) /= i*2) stop 6
+      end do
+
+      a = [(i, i=1,size(a))]
+
+      !$omp target map(tofrom: a) map(always, from: a)
+        do i = 1, size(a)
+          if (a(i) /= i*3) stop 7
+          a(i) = i*4
+        end do
+      !$omp end target
+      do i = 1, size(a)
+        if (a(i) /= i*4) stop 8
+      end do
+    !$omp end target data
+
+  end subroutine
+
+end program
diff --git a/libgomp/testsuite/libgomp.fortran/target-map-multi-3.f90 b/libgomp/testsuite/libgomp.fortran/target-map-multi-3.f90
new file mode 100644 (file)
index 0000000..44b6018
--- /dev/null
@@ -0,0 +1,18 @@
+! Test multiple map clauses for the same variable with the 'present'
+! modifier.
+! { dg-do run }
+
+program main
+  implicit none
+  integer, parameter :: N = 100
+  integer :: a(N), i
+
+  a = [(i, i=1,N)]
+
+  ! { dg-output "libgomp: present clause: not present on the device \\(addr: 0x\[0-9a-f\]+, size: \[0-9\]+ \\(0x\[0-9a-f\]+\\), dev: \[0-9\]+\\\)" { target offload_device_nonshared_as } }
+  ! { dg-shouldfail "present error triggered" { offload_device_nonshared_as } }
+  !$omp target map(to: a) map(present, alloc: a) map(from: a)
+    a = a * 2
+  !$omp end target
+
+end program
diff --git a/libgomp/testsuite/libgomp.fortran/target-map-multi-4.f90 b/libgomp/testsuite/libgomp.fortran/target-map-multi-4.f90
new file mode 100644 (file)
index 0000000..88a1491
--- /dev/null
@@ -0,0 +1,87 @@
+! Test multiple map clauses for the same variable in target enter/exit
+! data constructs, including release and delete map-types.
+! { dg-do run }
+
+program main
+  implicit none
+  integer, parameter :: N = 100
+  integer :: a(N), s, i
+
+  ! delete + release in exit data.
+  a = [(i, i=1,N)]
+  !$omp target enter data map(alloc: a) map(to: a)
+  s = 0
+  !$omp target map(alloc: a) map(tofrom: s)
+    do i = 1, N
+      s = s + a(i)
+    end do
+  !$omp end target
+  if (s /= N * (N + 1) / 2) stop 1
+  !$omp target exit data map(delete: a) map(release: a)
+
+  ! release + release: duplicate release decrements the reference count
+  ! once (deduplicated).  Two enter data calls set refcount to 2, so after
+  ! one deduplicated release refcount is 1 and the mapping remains.
+  a = [(i, i=1,N)]
+  !$omp target enter data map(to: a)   ! refcount = 1
+  !$omp target enter data map(to: a)   ! refcount = 2
+  !$omp target exit data map(release: a) map(release: a)   ! refcount = 1
+  s = 0
+  !$omp target map(alloc: a) map(tofrom: s)
+    do i = 1, N
+      s = s + a(i)
+    end do
+  !$omp end target
+  if (s /= N * (N + 1) / 2) stop 2
+  !$omp target exit data map(delete: a)   ! refcount = 0
+
+  ! delete + delete: duplicate delete removes the mapping unconditionally
+  ! once (deduplicated).
+  a = [(i, i=1,N)]
+  !$omp target enter data map(to: a)
+  !$omp target exit data map(delete: a) map(delete: a)
+
+  ! from + release: copy device values back to host and release the mapping.
+  a = [(i, i=1,N)]
+  !$omp target enter data map(to: a)   ! refcount = 1
+  !$omp target
+    a = a * 3
+  !$omp end target
+  !$omp target exit data map(release: a) map(from: a)   ! refcount = 0
+  if (any (a /= [(i*3, i=1,N)])) stop 3
+  if (any (a /= [(i*3, i=1,N)])) stop 4
+
+  ! Same tests via a subroutine to verify correct behaviour with
+  ! dummy arguments (passed by reference).
+  call test_dummy (a)
+
+contains
+
+  subroutine test_dummy (a)
+    integer, intent(inout) :: a(:)
+    integer :: s, i
+
+    ! delete + release with dummy argument.
+    a = [(i, i=1,size(a))]
+    !$omp target enter data map(alloc: a) map(to: a)
+    s = 0
+    !$omp target map(alloc: a) map(tofrom: s)
+      do i = 1, size(a)
+        s = s + a(i)
+      end do
+    !$omp end target
+    if (s /= size(a) * (size(a) + 1) / 2) stop 5
+    !$omp target exit data map(delete: a) map(release: a)
+
+    ! from + release with dummy argument.
+    a = [(i, i=1,size(a))]
+    !$omp target enter data map(to: a)
+    !$omp target
+      a = a * 3
+    !$omp end target
+    !$omp target exit data map(release: a) map(from: a)
+    if (any (a /= [(i*3, i=1,size(a))])) stop 6
+
+  end subroutine
+
+end program
diff --git a/libgomp/testsuite/libgomp.fortran/target-map-multi-5.f90 b/libgomp/testsuite/libgomp.fortran/target-map-multi-5.f90
new file mode 100644 (file)
index 0000000..94c7c94
--- /dev/null
@@ -0,0 +1,142 @@
+! Test multiple map clauses for the same variable using Fortran array section
+! notation (subscript triplets): fixed-size, allocatable, and dummy arrays.
+! { dg-do run }
+
+program main
+  implicit none
+  integer, parameter :: N = 100
+  integer :: a(N), s, i
+  integer, allocatable :: b(:)
+
+  ! --- Fixed-size array ---
+
+  ! map(to) + map(alloc) + map(from) on the same section.
+  a = [(i, i=1,N)]
+  !$omp target map(to: a(1:N)) map(alloc: a(1:N)) map(from: a(1:N))
+    a = a * 4
+  !$omp end target
+  if (any (a /= [(i*4, i=1,N)])) stop 1
+
+  ! map(to) + map(tofrom) on the same section.
+  a = [(i, i=1,N)]
+  !$omp target map(to: a(1:N)) map(tofrom: a(1:N))
+    a = a * 5
+  !$omp end target
+  if (any (a /= [(i*5, i=1,N)])) stop 2
+
+  ! map(alloc) + map(to): device gets host values via 'to'.
+  a = [(i, i=1,N)]
+  s = 0
+  !$omp target map(alloc: a(1:N)) map(to: a(1:N)) map(tofrom: s)
+    do i = 1, N
+      s = s + a(i)
+    end do
+  !$omp end target
+  if (s /= N*(N+1)/2) stop 3
+
+  ! map(alloc) + map(from): device values come back via 'from'.
+  !$omp target map(alloc: a(1:N)) map(from: a(1:N))
+    do i = 1, N
+      a(i) = i * 7
+    end do
+  !$omp end target
+  if (any (a /= [(i*7, i=1,N)])) stop 4
+
+  ! map(alloc) + map(tofrom) + map(alloc): full bidirectional.
+  a = [(i, i=1,N)]
+  !$omp target map(alloc: a(1:N)) map(tofrom: a(1:N)) map(alloc: a(1:N))
+    a = a * 8
+  !$omp end target
+  if (any (a /= [(i*8, i=1,N)])) stop 5
+
+  ! --- Allocatable array ---
+
+  allocate (b(N))
+
+  ! map(to) + map(alloc) + map(from) on the same section.
+  b = [(i, i=1,N)]
+  !$omp target map(to: b(1:N)) map(alloc: b(1:N)) map(from: b(1:N))
+    b = b * 4
+  !$omp end target
+  if (any (b /= [(i*4, i=1,N)])) stop 6
+
+  ! map(to) + map(tofrom) on the same section.
+  b = [(i, i=1,N)]
+  !$omp target map(to: b(1:N)) map(tofrom: b(1:N))
+    b = b * 5
+  !$omp end target
+  if (any (b /= [(i*5, i=1,N)])) stop 7
+
+  ! map(alloc) + map(to): device gets host values via 'to'.
+  b = [(i, i=1,N)]
+  s = 0
+  !$omp target map(alloc: b(1:N)) map(to: b(1:N)) map(tofrom: s)
+    do i = 1, N
+      s = s + b(i)
+    end do
+  !$omp end target
+  if (s /= N*(N+1)/2) stop 8
+
+  ! map(alloc) + map(from): device values come back via 'from'.
+  !$omp target map(alloc: b(1:N)) map(from: b(1:N))
+    do i = 1, N
+      b(i) = i * 7
+    end do
+  !$omp end target
+  if (any (b /= [(i*7, i=1,N)])) stop 9
+
+  ! map(alloc) + map(tofrom) + map(alloc): full bidirectional.
+  b = [(i, i=1,N)]
+  !$omp target map(alloc: b(1:N)) map(tofrom: b(1:N)) map(alloc: b(1:N))
+    b = b * 8
+  !$omp end target
+  if (any (b /= [(i*8, i=1,N)])) stop 10
+
+  deallocate (b)
+
+  ! Same tests via a subroutine to verify correct behaviour with
+  ! dummy arguments (passed by reference / array descriptor).
+  call test_dummy (a)
+
+contains
+
+  subroutine test_dummy (a)
+    integer, intent(inout) :: a(:)
+    integer :: i, s, n
+
+    n = size(a)
+
+    ! map(to) + map(alloc) + map(from) on the same section.
+    a = [(i, i=1,n)]
+    !$omp target map(to: a(1:n)) map(alloc: a(1:n)) map(from: a(1:n))
+      a = a * 4
+    !$omp end target
+    if (any (a /= [(i*4, i=1,n)])) stop 11
+
+    ! map(to) + map(tofrom) on the same section.
+    a = [(i, i=1,n)]
+    !$omp target map(to: a(1:n)) map(tofrom: a(1:n))
+      a = a * 5
+    !$omp end target
+    if (any (a /= [(i*5, i=1,n)])) stop 12
+
+    ! map(alloc) + map(to): device gets host values via 'to'.
+    a = [(i, i=1,n)]
+    s = 0
+    !$omp target map(alloc: a(1:n)) map(to: a(1:n)) map(tofrom: s)
+      do i = 1, n
+        s = s + a(i)
+      end do
+    !$omp end target
+    if (s /= n*(n+1)/2) stop 13
+
+    ! map(alloc) + map(tofrom) + map(alloc): full bidirectional.
+    a = [(i, i=1,n)]
+    !$omp target map(alloc: a(1:n)) map(tofrom: a(1:n)) map(alloc: a(1:n))
+      a = a * 8
+    !$omp end target
+    if (any (a /= [(i*8, i=1,n)])) stop 14
+
+  end subroutine
+
+end program