public inbox for fortran@gcc.gnu.org
 help / color / mirror / Atom feed
* [PATCH 0/5] OpenMP: Array-shaping operator and strided/rectangular 'target update' support
@ 2023-09-06  9:34 Julian Brown
  2023-09-06  9:34 ` [PATCH 1/5] OpenMP, NVPTX: memcpy[23]D bias correction Julian Brown
                   ` (4 more replies)
  0 siblings, 5 replies; 10+ messages in thread
From: Julian Brown @ 2023-09-06  9:34 UTC (permalink / raw)
  To: gcc-patches; +Cc: fortran, tobias, jakub

This patch series provides support for the OpenMP 5.0+ array-shaping
operator and for strided/rectangular updates for 'target update'
directives.  Each of C, C++ and Fortran is supported (using existing
base language syntax for the last).

This series applies on top of the "infrastructure" support series:

  https://gcc.gnu.org/pipermail/gcc-patches/2023-August/627895.html

and the lvalue parsing/declare mapper series:

  https://gcc.gnu.org/pipermail/gcc-patches/2023-September/629363.html

The og13 version of this series was posted here:

  https://gcc.gnu.org/pipermail/gcc-patches/2023-July/623502.html

This version of the series contains several follow-up fixes relative
to the og13 version, and omits some parts that are only present on the
branch for now.  Further comments on individual patches.

Tested with offloading to NVPTX and bootstrapped. OK?

Julian Brown (5):
  OpenMP, NVPTX: memcpy[23]D bias correction
  OpenMP: Allow complete replacement of clause during map/to/from
    expansion
  OpenMP: Support strided and shaped-array updates for C++
  OpenMP: Array shaping operator and strided "target update" for C
  OpenMP: Noncontiguous "target update" for Fortran

 gcc/c-family/c-common.h                       |  12 +-
 gcc/c-family/c-omp.cc                         | 277 ++++++++--
 gcc/c-family/c-pretty-print.cc                |   5 +
 gcc/c/c-parser.cc                             | 330 +++++++++++-
 gcc/c/c-tree.h                                |   6 +-
 gcc/c/c-typeck.cc                             | 281 ++++++++--
 gcc/cp/cp-objcp-common.cc                     |   1 +
 gcc/cp/cp-tree.def                            |   1 +
 gcc/cp/cp-tree.h                              |  13 +-
 gcc/cp/decl.cc                                |  75 +++
 gcc/cp/decl2.cc                               |  19 +-
 gcc/cp/error.cc                               |   5 +
 gcc/cp/mangle.cc                              |   1 +
 gcc/cp/operators.def                          |   1 +
 gcc/cp/parser.cc                              | 303 ++++++++++-
 gcc/cp/parser.h                               |   7 +
 gcc/cp/pt.cc                                  |  39 +-
 gcc/cp/semantics.cc                           | 289 ++++++++--
 gcc/cp/typeck.cc                              |  12 +-
 gcc/fortran/openmp.cc                         |   5 +-
 gcc/fortran/trans-openmp.cc                   | 499 ++++++++++++++++++
 gcc/gimplify.cc                               |  54 +-
 gcc/omp-general.cc                            |  47 ++
 gcc/omp-general.h                             |   4 +-
 gcc/omp-low.cc                                | 476 ++++++++++++++++-
 gcc/testsuite/g++.dg/gomp/array-shaping-1.C   |  22 +
 gcc/testsuite/g++.dg/gomp/array-shaping-2.C   | 134 +++++
 .../g++.dg/gomp/bad-array-shaping-1.C         |  47 ++
 .../g++.dg/gomp/bad-array-shaping-2.C         |  52 ++
 .../g++.dg/gomp/bad-array-shaping-3.C         |  53 ++
 .../g++.dg/gomp/bad-array-shaping-4.C         |  60 +++
 .../g++.dg/gomp/bad-array-shaping-5.C         |  55 ++
 .../g++.dg/gomp/bad-array-shaping-6.C         |  59 +++
 .../g++.dg/gomp/bad-array-shaping-7.C         |  48 ++
 .../g++.dg/gomp/bad-array-shaping-8.C         |  50 ++
 .../gcc.dg/gomp/bad-array-shaping-c-1.c       |  26 +
 .../gcc.dg/gomp/bad-array-shaping-c-2.c       |  24 +
 .../gcc.dg/gomp/bad-array-shaping-c-3.c       |  30 ++
 .../gcc.dg/gomp/bad-array-shaping-c-4.c       |  27 +
 .../gcc.dg/gomp/bad-array-shaping-c-5.c       |  17 +
 .../gcc.dg/gomp/bad-array-shaping-c-6.c       |  26 +
 .../gcc.dg/gomp/bad-array-shaping-c-7.c       |  15 +
 .../gfortran.dg/gomp/noncontig-updates-1.f90  |  19 +
 .../gfortran.dg/gomp/noncontig-updates-2.f90  |  16 +
 .../gfortran.dg/gomp/noncontig-updates-3.f90  |  16 +
 .../gfortran.dg/gomp/noncontig-updates-4.f90  |  15 +
 gcc/tree-pretty-print.cc                      |  17 +
 gcc/tree.def                                  |   2 +-
 include/gomp-constants.h                      |   7 +-
 libgomp/libgomp.h                             |  15 +
 libgomp/plugin/plugin-nvptx.c                 |  67 +++
 libgomp/target.c                              | 271 +++++++---
 .../testsuite/libgomp.c++/array-shaping-1.C   | 469 ++++++++++++++++
 .../testsuite/libgomp.c++/array-shaping-10.C  |  61 +++
 .../testsuite/libgomp.c++/array-shaping-11.C  |  63 +++
 .../testsuite/libgomp.c++/array-shaping-12.C  |  65 +++
 .../testsuite/libgomp.c++/array-shaping-13.C  |  89 ++++
 .../testsuite/libgomp.c++/array-shaping-2.C   |  38 ++
 .../testsuite/libgomp.c++/array-shaping-3.C   |  38 ++
 .../testsuite/libgomp.c++/array-shaping-4.C   |  38 ++
 .../testsuite/libgomp.c++/array-shaping-5.C   |  38 ++
 .../testsuite/libgomp.c++/array-shaping-6.C   |  54 ++
 .../testsuite/libgomp.c++/array-shaping-7.C   |  54 ++
 .../testsuite/libgomp.c++/array-shaping-8.C   |  65 +++
 .../testsuite/libgomp.c++/array-shaping-9.C   |  95 ++++
 .../libgomp.c-c++-common/array-shaping-14.c   |  34 ++
 libgomp/testsuite/libgomp.c/array-shaping-1.c | 236 +++++++++
 libgomp/testsuite/libgomp.c/array-shaping-2.c |  39 ++
 libgomp/testsuite/libgomp.c/array-shaping-3.c |  42 ++
 libgomp/testsuite/libgomp.c/array-shaping-4.c |  36 ++
 libgomp/testsuite/libgomp.c/array-shaping-5.c |  38 ++
 libgomp/testsuite/libgomp.c/array-shaping-6.c |  45 ++
 .../libgomp.fortran/noncontig-updates-1.f90   |  54 ++
 .../libgomp.fortran/noncontig-updates-10.f90  |  29 +
 .../libgomp.fortran/noncontig-updates-11.f90  |  51 ++
 .../libgomp.fortran/noncontig-updates-12.f90  |  59 +++
 .../libgomp.fortran/noncontig-updates-13.f90  |  42 ++
 .../libgomp.fortran/noncontig-updates-2.f90   | 101 ++++
 .../libgomp.fortran/noncontig-updates-3.f90   |  47 ++
 .../libgomp.fortran/noncontig-updates-4.f90   |  78 +++
 .../libgomp.fortran/noncontig-updates-5.f90   |  55 ++
 .../libgomp.fortran/noncontig-updates-6.f90   |  34 ++
 .../libgomp.fortran/noncontig-updates-7.f90   |  36 ++
 .../libgomp.fortran/noncontig-updates-8.f90   |  39 ++
 .../libgomp.fortran/noncontig-updates-9.f90   |  34 ++
 85 files changed, 5933 insertions(+), 315 deletions(-)
 create mode 100644 gcc/testsuite/g++.dg/gomp/array-shaping-1.C
 create mode 100644 gcc/testsuite/g++.dg/gomp/array-shaping-2.C
 create mode 100644 gcc/testsuite/g++.dg/gomp/bad-array-shaping-1.C
 create mode 100644 gcc/testsuite/g++.dg/gomp/bad-array-shaping-2.C
 create mode 100644 gcc/testsuite/g++.dg/gomp/bad-array-shaping-3.C
 create mode 100644 gcc/testsuite/g++.dg/gomp/bad-array-shaping-4.C
 create mode 100644 gcc/testsuite/g++.dg/gomp/bad-array-shaping-5.C
 create mode 100644 gcc/testsuite/g++.dg/gomp/bad-array-shaping-6.C
 create mode 100644 gcc/testsuite/g++.dg/gomp/bad-array-shaping-7.C
 create mode 100644 gcc/testsuite/g++.dg/gomp/bad-array-shaping-8.C
 create mode 100644 gcc/testsuite/gcc.dg/gomp/bad-array-shaping-c-1.c
 create mode 100644 gcc/testsuite/gcc.dg/gomp/bad-array-shaping-c-2.c
 create mode 100644 gcc/testsuite/gcc.dg/gomp/bad-array-shaping-c-3.c
 create mode 100644 gcc/testsuite/gcc.dg/gomp/bad-array-shaping-c-4.c
 create mode 100644 gcc/testsuite/gcc.dg/gomp/bad-array-shaping-c-5.c
 create mode 100644 gcc/testsuite/gcc.dg/gomp/bad-array-shaping-c-6.c
 create mode 100644 gcc/testsuite/gcc.dg/gomp/bad-array-shaping-c-7.c
 create mode 100644 gcc/testsuite/gfortran.dg/gomp/noncontig-updates-1.f90
 create mode 100644 gcc/testsuite/gfortran.dg/gomp/noncontig-updates-2.f90
 create mode 100644 gcc/testsuite/gfortran.dg/gomp/noncontig-updates-3.f90
 create mode 100644 gcc/testsuite/gfortran.dg/gomp/noncontig-updates-4.f90
 create mode 100644 libgomp/testsuite/libgomp.c++/array-shaping-1.C
 create mode 100644 libgomp/testsuite/libgomp.c++/array-shaping-10.C
 create mode 100644 libgomp/testsuite/libgomp.c++/array-shaping-11.C
 create mode 100644 libgomp/testsuite/libgomp.c++/array-shaping-12.C
 create mode 100644 libgomp/testsuite/libgomp.c++/array-shaping-13.C
 create mode 100644 libgomp/testsuite/libgomp.c++/array-shaping-2.C
 create mode 100644 libgomp/testsuite/libgomp.c++/array-shaping-3.C
 create mode 100644 libgomp/testsuite/libgomp.c++/array-shaping-4.C
 create mode 100644 libgomp/testsuite/libgomp.c++/array-shaping-5.C
 create mode 100644 libgomp/testsuite/libgomp.c++/array-shaping-6.C
 create mode 100644 libgomp/testsuite/libgomp.c++/array-shaping-7.C
 create mode 100644 libgomp/testsuite/libgomp.c++/array-shaping-8.C
 create mode 100644 libgomp/testsuite/libgomp.c++/array-shaping-9.C
 create mode 100644 libgomp/testsuite/libgomp.c-c++-common/array-shaping-14.c
 create mode 100644 libgomp/testsuite/libgomp.c/array-shaping-1.c
 create mode 100644 libgomp/testsuite/libgomp.c/array-shaping-2.c
 create mode 100644 libgomp/testsuite/libgomp.c/array-shaping-3.c
 create mode 100644 libgomp/testsuite/libgomp.c/array-shaping-4.c
 create mode 100644 libgomp/testsuite/libgomp.c/array-shaping-5.c
 create mode 100644 libgomp/testsuite/libgomp.c/array-shaping-6.c
 create mode 100644 libgomp/testsuite/libgomp.fortran/noncontig-updates-1.f90
 create mode 100644 libgomp/testsuite/libgomp.fortran/noncontig-updates-10.f90
 create mode 100644 libgomp/testsuite/libgomp.fortran/noncontig-updates-11.f90
 create mode 100644 libgomp/testsuite/libgomp.fortran/noncontig-updates-12.f90
 create mode 100644 libgomp/testsuite/libgomp.fortran/noncontig-updates-13.f90
 create mode 100644 libgomp/testsuite/libgomp.fortran/noncontig-updates-2.f90
 create mode 100644 libgomp/testsuite/libgomp.fortran/noncontig-updates-3.f90
 create mode 100644 libgomp/testsuite/libgomp.fortran/noncontig-updates-4.f90
 create mode 100644 libgomp/testsuite/libgomp.fortran/noncontig-updates-5.f90
 create mode 100644 libgomp/testsuite/libgomp.fortran/noncontig-updates-6.f90
 create mode 100644 libgomp/testsuite/libgomp.fortran/noncontig-updates-7.f90
 create mode 100644 libgomp/testsuite/libgomp.fortran/noncontig-updates-8.f90
 create mode 100644 libgomp/testsuite/libgomp.fortran/noncontig-updates-9.f90

-- 
2.41.0


^ permalink raw reply	[flat|nested] 10+ messages in thread

* [PATCH 1/5] OpenMP, NVPTX: memcpy[23]D bias correction
  2023-09-06  9:34 [PATCH 0/5] OpenMP: Array-shaping operator and strided/rectangular 'target update' support Julian Brown
@ 2023-09-06  9:34 ` Julian Brown
  2023-09-26 22:57   ` Thomas Schwinge
  2023-09-06  9:34 ` [PATCH 2/5] OpenMP: Allow complete replacement of clause during map/to/from expansion Julian Brown
                   ` (3 subsequent siblings)
  4 siblings, 1 reply; 10+ messages in thread
From: Julian Brown @ 2023-09-06  9:34 UTC (permalink / raw)
  To: gcc-patches; +Cc: fortran, tobias, jakub

This patch works around behaviour of the 2D and 3D memcpy operations in
the CUDA driver runtime.  Particularly in Fortran, the "base pointer"
of an array (used for either source or destination of a host/device copy)
may lie outside of data that is actually stored on the device.  The fix
is to make sure that we use the first element of data to be transferred
instead, and adjust parameters accordingly.

2023-09-05  Julian Brown  <julian@codesourcery.com>

libgomp/
	* plugin/plugin-nvptx.c (GOMP_OFFLOAD_memcpy2d): Adjust parameters to
	avoid out-of-bounds array checks in CUDA runtime.
	(GOMP_OFFLOAD_memcpy3d): Likewise.
---
 libgomp/plugin/plugin-nvptx.c | 67 +++++++++++++++++++++++++++++++++++
 1 file changed, 67 insertions(+)

diff --git a/libgomp/plugin/plugin-nvptx.c b/libgomp/plugin/plugin-nvptx.c
index 00d4241ae02b..cefe288a8aab 100644
--- a/libgomp/plugin/plugin-nvptx.c
+++ b/libgomp/plugin/plugin-nvptx.c
@@ -1827,6 +1827,35 @@ GOMP_OFFLOAD_memcpy2d (int dst_ord, int src_ord, size_t dim1_size,
   data.srcXInBytes = src_offset1_size;
   data.srcY = src_offset0_len;
 
+  if (data.srcXInBytes != 0 || data.srcY != 0)
+    {
+      /* Adjust origin to the actual array data, else the CUDA 2D memory
+	 copy API calls below may fail to validate source/dest pointers
+	 correctly (especially for Fortran where the "virtual origin" of an
+	 array is often outside the stored data).  */
+      if (src_ord == -1)
+	data.srcHost = (const void *) ((const char *) data.srcHost
+				      + data.srcY * data.srcPitch
+				      + data.srcXInBytes);
+      else
+	data.srcDevice += data.srcY * data.srcPitch + data.srcXInBytes;
+      data.srcXInBytes = 0;
+      data.srcY = 0;
+    }
+
+  if (data.dstXInBytes != 0 || data.dstY != 0)
+    {
+      /* As above.  */
+      if (dst_ord == -1)
+	data.dstHost = (void *) ((char *) data.dstHost
+				 + data.dstY * data.dstPitch
+				 + data.dstXInBytes);
+      else
+	data.dstDevice += data.dstY * data.dstPitch + data.dstXInBytes;
+      data.dstXInBytes = 0;
+      data.dstY = 0;
+    }
+
   CUresult res = CUDA_CALL_NOCHECK (cuMemcpy2D, &data);
   if (res == CUDA_ERROR_INVALID_VALUE)
     /* If pitch > CU_DEVICE_ATTRIBUTE_MAX_PITCH or for device-to-device
@@ -1895,6 +1924,44 @@ GOMP_OFFLOAD_memcpy3d (int dst_ord, int src_ord, size_t dim2_size,
   data.srcY = src_offset1_len;
   data.srcZ = src_offset0_len;
 
+  if (data.srcXInBytes != 0 || data.srcY != 0 || data.srcZ != 0)
+    {
+      /* Adjust origin to the actual array data, else the CUDA 3D memory
+	 copy API call below may fail to validate source/dest pointers
+	 correctly (especially for Fortran where the "virtual origin" of an
+	 array is often outside the stored data).  */
+      if (src_ord == -1)
+	data.srcHost
+	  = (const void *) ((const char *) data.srcHost
+			    + (data.srcZ * data.srcHeight + data.srcY)
+			      * data.srcPitch
+			    + data.srcXInBytes);
+      else
+	data.srcDevice
+	  += (data.srcZ * data.srcHeight + data.srcY) * data.srcPitch
+	     + data.srcXInBytes;
+      data.srcXInBytes = 0;
+      data.srcY = 0;
+      data.srcZ = 0;
+    }
+
+  if (data.dstXInBytes != 0 || data.dstY != 0 || data.dstZ != 0)
+    {
+      /* As above.  */
+      if (dst_ord == -1)
+	data.dstHost = (void *) ((char *) data.dstHost
+				 + (data.dstZ * data.dstHeight + data.dstY)
+				   * data.dstPitch
+				 + data.dstXInBytes);
+      else
+	data.dstDevice
+	  += (data.dstZ * data.dstHeight + data.dstY) * data.dstPitch
+	     + data.dstXInBytes;
+      data.dstXInBytes = 0;
+      data.dstY = 0;
+      data.dstZ = 0;
+    }
+
   CUDA_CALL (cuMemcpy3D, &data);
   return true;
 }
-- 
2.41.0


^ permalink raw reply	[flat|nested] 10+ messages in thread

* [PATCH 2/5] OpenMP: Allow complete replacement of clause during map/to/from expansion
  2023-09-06  9:34 [PATCH 0/5] OpenMP: Array-shaping operator and strided/rectangular 'target update' support Julian Brown
  2023-09-06  9:34 ` [PATCH 1/5] OpenMP, NVPTX: memcpy[23]D bias correction Julian Brown
@ 2023-09-06  9:34 ` Julian Brown
  2023-09-06  9:34 ` [PATCH 3/5] OpenMP: Support strided and shaped-array updates for C++ Julian Brown
                   ` (2 subsequent siblings)
  4 siblings, 0 replies; 10+ messages in thread
From: Julian Brown @ 2023-09-06  9:34 UTC (permalink / raw)
  To: gcc-patches; +Cc: fortran, tobias, jakub

At present, map/to/from clauses on OpenMP "target" directives may be
expanded into several mapping nodes if they describe array sections with
pointer or reference bases, or similar.  This patch allows the original
clause to be replaced during that expansion, mostly by passing the list
pointer to the node to various functions rather than the node itself.

This is needed by the following patch. There shouldn't be any functional
changes introduced by this patch itself.

2023-09-05  Julian Brown  <julian@codesourcery.com>

gcc/c-family/
	* c-common.h (expand_array_base, expand_component_selector,
	expand_map_clause): Adjust member declarations.
	* c-omp.cc (omp_expand_access_chain): Pass and return pointer to
	clause.
	(c_omp_address_inspector::expand_array_base): Likewise.
	(c_omp_address_inspector::expand_component_selector): Likewise.
	(c_omp_address_inspector::expand_map_clause): Likewise.

gcc/c/
	* c-typeck.cc (handle_omp_array_sections): Pass pointer to clause to
	process instead of clause.
	(c_finish_omp_clauses): Update calls to handle_omp_array_sections.
	Handle cases where initial clause might be replaced.

gcc/cp/
	* semantics.cc (handle_omp_array_sections): Pass pointer to clause
	instead of clause.  Add PNEXT return parameter for next clause in list
	to process.
	(finish_omp_clauses): Update calls to handle_omp_array_sections.
	Handle cases where initial clause might be replaced.
---
 gcc/c-family/c-common.h | 12 +++----
 gcc/c-family/c-omp.cc   | 75 +++++++++++++++++++++--------------------
 gcc/c/c-typeck.cc       | 32 +++++++++++-------
 gcc/cp/semantics.cc     | 37 +++++++++++++-------
 4 files changed, 88 insertions(+), 68 deletions(-)

diff --git a/gcc/c-family/c-common.h b/gcc/c-family/c-common.h
index b4e49c7d0cbc..e2b85d394d32 100644
--- a/gcc/c-family/c-common.h
+++ b/gcc/c-family/c-common.h
@@ -1383,12 +1383,12 @@ public:
 
   bool maybe_zero_length_array_section (tree);
 
-  tree expand_array_base (tree, vec<omp_addr_token *> &, tree, unsigned *,
-			  c_omp_region_type, bool);
-  tree expand_component_selector (tree, vec<omp_addr_token *> &, tree,
-				  unsigned *);
-  tree expand_map_clause (tree, tree, vec<omp_addr_token *> &,
-			  c_omp_region_type);
+  tree * expand_array_base (tree *, vec<omp_addr_token *> &, tree, unsigned *,
+			    c_omp_region_type, bool);
+  tree * expand_component_selector (tree *, vec<omp_addr_token *> &, tree,
+				    unsigned *);
+  tree * expand_map_clause (tree *, tree, vec<omp_addr_token *> &,
+			    c_omp_region_type);
 };
 
 enum c_omp_directive_kind {
diff --git a/gcc/c-family/c-omp.cc b/gcc/c-family/c-omp.cc
index b73f9682f460..3c6b79107edc 100644
--- a/gcc/c-family/c-omp.cc
+++ b/gcc/c-family/c-omp.cc
@@ -3331,11 +3331,12 @@ c_omp_address_inspector::maybe_zero_length_array_section (tree clause)
    expression types here, because e.g. you can't have an array of
    references.  See also gimplify.cc:omp_expand_access_chain.  */
 
-static tree
-omp_expand_access_chain (tree c, tree expr, vec<omp_addr_token *> &addr_tokens,
-			 unsigned *idx)
+static tree *
+omp_expand_access_chain (tree *pc, tree expr,
+			 vec<omp_addr_token *> &addr_tokens, unsigned *idx)
 {
   using namespace omp_addr_tokenizer;
+  tree c = *pc;
   location_t loc = OMP_CLAUSE_LOCATION (c);
   unsigned i = *idx;
   tree c2 = NULL_TREE;
@@ -3373,35 +3374,36 @@ omp_expand_access_chain (tree c, tree expr, vec<omp_addr_token *> &addr_tokens,
       break;
 
     default:
-      return error_mark_node;
+      return NULL;
     }
 
   if (c2)
     {
       OMP_CLAUSE_CHAIN (c2) = OMP_CLAUSE_CHAIN (c);
       OMP_CLAUSE_CHAIN (c) = c2;
-      c = c2;
+      pc = &OMP_CLAUSE_CHAIN (c);
     }
 
   *idx = ++i;
 
   if (i < addr_tokens.length ()
       && addr_tokens[i]->type == ACCESS_METHOD)
-    return omp_expand_access_chain (c, expr, addr_tokens, idx);
+    return omp_expand_access_chain (pc, expr, addr_tokens, idx);
 
-  return c;
+  return pc;
 }
 
 /* Translate "array_base_decl access_method" to OMP mapping clauses.  */
 
-tree
-c_omp_address_inspector::expand_array_base (tree c,
+tree *
+c_omp_address_inspector::expand_array_base (tree *pc,
 					    vec<omp_addr_token *> &addr_tokens,
 					    tree expr, unsigned *idx,
 					    c_omp_region_type ort,
 					    bool decl_p)
 {
   using namespace omp_addr_tokenizer;
+  tree c = *pc;
   location_t loc = OMP_CLAUSE_LOCATION (c);
   int i = *idx;
   tree decl = addr_tokens[i + 1]->expr;
@@ -3426,7 +3428,7 @@ c_omp_address_inspector::expand_array_base (tree c,
 	  || OMP_CLAUSE_MAP_KIND (c) == GOMP_MAP_DETACH))
     {
       *idx = ++i;
-      return c;
+      return pc;
     }
 
   switch (addr_tokens[i + 1]->u.access_kind)
@@ -3672,7 +3674,7 @@ c_omp_address_inspector::expand_array_base (tree c,
 
     default:
       *idx = i + consume_tokens;
-      return error_mark_node;
+      return NULL;
     }
 
   if (c3)
@@ -3685,7 +3687,7 @@ c_omp_address_inspector::expand_array_base (tree c,
 	  OMP_CLAUSE_MAP_IMPLICIT (c2) = 1;
 	  OMP_CLAUSE_MAP_IMPLICIT (c3) = 1;
 	}
-      c = c3;
+      pc = &OMP_CLAUSE_CHAIN (c2);
     }
   else if (c2)
     {
@@ -3693,27 +3695,28 @@ c_omp_address_inspector::expand_array_base (tree c,
       OMP_CLAUSE_CHAIN (c) = c2;
       if (implicit_p)
 	OMP_CLAUSE_MAP_IMPLICIT (c2) = 1;
-      c = c2;
+      pc = &OMP_CLAUSE_CHAIN (c);
     }
 
   i += consume_tokens;
   *idx = i;
 
   if (chain_p && map_p)
-    return omp_expand_access_chain (c, expr, addr_tokens, idx);
+    return omp_expand_access_chain (pc, expr, addr_tokens, idx);
 
-  return c;
+  return pc;
 }
 
 /* Translate "component_selector access_method" to OMP mapping clauses.  */
 
-tree
-c_omp_address_inspector::expand_component_selector (tree c,
+tree *
+c_omp_address_inspector::expand_component_selector (tree *pc,
 						    vec<omp_addr_token *>
 						      &addr_tokens,
 						    tree expr, unsigned *idx)
 {
   using namespace omp_addr_tokenizer;
+  tree c = *pc;
   location_t loc = OMP_CLAUSE_LOCATION (c);
   unsigned i = *idx;
   tree c2 = NULL_TREE, c3 = NULL_TREE;
@@ -3820,7 +3823,7 @@ c_omp_address_inspector::expand_component_selector (tree c,
 
     default:
       *idx = i + 2;
-      return error_mark_node;
+      return NULL;
     }
 
   if (c3)
@@ -3828,29 +3831,29 @@ c_omp_address_inspector::expand_component_selector (tree c,
       OMP_CLAUSE_CHAIN (c3) = OMP_CLAUSE_CHAIN (c);
       OMP_CLAUSE_CHAIN (c2) = c3;
       OMP_CLAUSE_CHAIN (c) = c2;
-      c = c3;
+      pc = &OMP_CLAUSE_CHAIN (c2);
     }
   else if (c2)
     {
       OMP_CLAUSE_CHAIN (c2) = OMP_CLAUSE_CHAIN (c);
       OMP_CLAUSE_CHAIN (c) = c2;
-      c = c2;
+      pc = &OMP_CLAUSE_CHAIN (c);
     }
 
   i += 2;
   *idx = i;
 
   if (chain_p && map_p)
-    return omp_expand_access_chain (c, expr, addr_tokens, idx);
+    return omp_expand_access_chain (pc, expr, addr_tokens, idx);
 
-  return c;
+  return pc;
 }
 
 /* Expand a map clause into a group of mapping clauses, creating nodes to
    attach/detach pointers and so forth as necessary.  */
 
-tree
-c_omp_address_inspector::expand_map_clause (tree c, tree expr,
+tree *
+c_omp_address_inspector::expand_map_clause (tree *pc, tree expr,
 					    vec<omp_addr_token *> &addr_tokens,
 					    c_omp_region_type ort)
 {
@@ -3866,18 +3869,18 @@ c_omp_address_inspector::expand_map_clause (tree c, tree expr,
 	  && addr_tokens[i]->u.structure_base_kind == BASE_DECL
 	  && addr_tokens[i + 1]->type == ACCESS_METHOD)
 	{
-	  c = expand_array_base (c, addr_tokens, expr, &i, ort, true);
-	  if (c == error_mark_node)
-	    return error_mark_node;
+	  pc = expand_array_base (pc, addr_tokens, expr, &i, ort, true);
+	  if (pc == NULL)
+	    return NULL;
 	}
       else if (remaining >= 2
 	       && addr_tokens[i]->type == ARRAY_BASE
 	       && addr_tokens[i]->u.structure_base_kind == BASE_ARBITRARY_EXPR
 	       && addr_tokens[i + 1]->type == ACCESS_METHOD)
 	{
-	  c = expand_array_base (c, addr_tokens, expr, &i, ort, false);
-	  if (c == error_mark_node)
-	    return error_mark_node;
+	  pc = expand_array_base (pc, addr_tokens, expr, &i, ort, false);
+	  if (pc == NULL)
+	    return NULL;
 	}
       else if (remaining >= 2
 	       && addr_tokens[i]->type == STRUCTURE_BASE
@@ -3904,18 +3907,18 @@ c_omp_address_inspector::expand_map_clause (tree c, tree expr,
 		i++;
 	      break;
 	    default:
-	      return error_mark_node;
+	      return NULL;
 	    }
 	}
       else if (remaining >= 2
 	       && addr_tokens[i]->type == COMPONENT_SELECTOR
 	       && addr_tokens[i + 1]->type == ACCESS_METHOD)
 	{
-	  c = expand_component_selector (c, addr_tokens, expr, &i);
+	  pc = expand_component_selector (pc, addr_tokens, expr, &i);
 	  /* We used 'expr', so these must have been the last tokens.  */
 	  gcc_assert (i == length);
-	  if (c == error_mark_node)
-	    return error_mark_node;
+	  if (pc == NULL)
+	    return NULL;
 	}
       else if (remaining >= 3
 	       && addr_tokens[i]->type == COMPONENT_SELECTOR
@@ -3933,9 +3936,9 @@ c_omp_address_inspector::expand_map_clause (tree c, tree expr,
     }
 
   if (i == length)
-    return c;
+    return pc;
 
-  return error_mark_node;
+  return NULL;
 }
 
 /* Given a mapper function MAPPER_FN, recursively scan through the map clauses
diff --git a/gcc/c/c-typeck.cc b/gcc/c/c-typeck.cc
index 2696e681be4f..fb480994520e 100644
--- a/gcc/c/c-typeck.cc
+++ b/gcc/c/c-typeck.cc
@@ -14043,8 +14043,9 @@ handle_omp_array_sections_1 (tree c, tree t, vec<tree> &types,
 /* Handle array sections for clause C.  */
 
 static bool
-handle_omp_array_sections (tree c, enum c_omp_region_type ort)
+handle_omp_array_sections (tree *pc, enum c_omp_region_type ort)
 {
+  tree c = *pc;
   bool maybe_zero_len = false;
   unsigned int first_non_one = 0;
   auto_vec<tree, 10> types;
@@ -14266,8 +14267,8 @@ handle_omp_array_sections (tree c, enum c_omp_region_type ort)
 
       c_omp_address_inspector ai (OMP_CLAUSE_LOCATION (c), t);
 
-      tree nc = ai.expand_map_clause (c, first, addr_tokens, ort);
-      if (nc != error_mark_node)
+      tree *npc = ai.expand_map_clause (pc, first, addr_tokens, ort);
+      if (npc != NULL)
 	{
 	  if (ai.maybe_zero_length_array_section (c))
 	    OMP_CLAUSE_MAP_MAYBE_ZERO_LENGTH_ARRAY_SECTION (c) = 1;
@@ -14616,12 +14617,13 @@ c_finish_omp_clauses (tree clauses, enum c_omp_region_type ort)
 	  t = OMP_CLAUSE_DECL (c);
 	  if (TREE_CODE (t) == OMP_ARRAY_SECTION)
 	    {
-	      if (handle_omp_array_sections (c, ort))
+	      if (handle_omp_array_sections (pc, ort))
 		{
 		  remove = true;
 		  break;
 		}
 
+	      c = *pc;
 	      t = OMP_CLAUSE_DECL (c);
 	      if (OMP_CLAUSE_CODE (c) == OMP_CLAUSE_REDUCTION
 		  && OMP_CLAUSE_REDUCTION_INSCAN (c))
@@ -15237,10 +15239,12 @@ c_finish_omp_clauses (tree clauses, enum c_omp_region_type ort)
 	    last_iterators = NULL_TREE;
 	  if (TREE_CODE (t) == OMP_ARRAY_SECTION)
 	    {
-	      if (handle_omp_array_sections (c, ort))
+	      if (handle_omp_array_sections (pc, ort))
 		remove = true;
-	      else if (OMP_CLAUSE_CODE (c) == OMP_CLAUSE_DEPEND
-		       && OMP_CLAUSE_DEPEND_KIND (c) == OMP_CLAUSE_DEPEND_DEPOBJ)
+	      else if ((c = *pc)
+		       && OMP_CLAUSE_CODE (c) == OMP_CLAUSE_DEPEND
+		       && (OMP_CLAUSE_DEPEND_KIND (c)
+			   == OMP_CLAUSE_DEPEND_DEPOBJ))
 		{
 		  error_at (OMP_CLAUSE_LOCATION (c),
 			    "%<depend%> clause with %<depobj%> dependence "
@@ -15356,10 +15360,11 @@ c_finish_omp_clauses (tree clauses, enum c_omp_region_type ort)
 		grp_start_p = pc;
 		grp_sentinel = OMP_CLAUSE_CHAIN (c);
 
-		if (handle_omp_array_sections (c, ort))
+		if (handle_omp_array_sections (pc, ort))
 		  remove = true;
 		else
 		  {
+		    c = *pc;
 		    t = OMP_CLAUSE_DECL (c);
 		    if (!omp_mappable_type (TREE_TYPE (t)))
 		      {
@@ -15649,10 +15654,10 @@ c_finish_omp_clauses (tree clauses, enum c_omp_region_type ort)
 	      {
 		grp_start_p = pc;
 		grp_sentinel = OMP_CLAUSE_CHAIN (c);
-		tree nc = ai.expand_map_clause (c, OMP_CLAUSE_DECL (c),
-						addr_tokens, ort);
-		if (nc != error_mark_node)
-		  c = nc;
+		tree *npc = ai.expand_map_clause (pc, OMP_CLAUSE_DECL (c),
+						  addr_tokens, ort);
+		if (npc != NULL)
+		  c = *npc;
 	      }
 	  }
 	  break;
@@ -15752,10 +15757,11 @@ c_finish_omp_clauses (tree clauses, enum c_omp_region_type ort)
 	  t = OMP_CLAUSE_DECL (c);
 	  if (TREE_CODE (t) == OMP_ARRAY_SECTION)
 	    {
-	      if (handle_omp_array_sections (c, ort))
+	      if (handle_omp_array_sections (pc, ort))
 		remove = true;
 	      else
 		{
+		  c = *pc;
 		  t = OMP_CLAUSE_DECL (c);
 		  while (TREE_CODE (t) == ARRAY_REF)
 		    t = TREE_OPERAND (t, 0);
diff --git a/gcc/cp/semantics.cc b/gcc/cp/semantics.cc
index d6f2da65410f..aa1fa15548cf 100644
--- a/gcc/cp/semantics.cc
+++ b/gcc/cp/semantics.cc
@@ -5630,8 +5630,9 @@ handle_omp_array_sections_1 (tree c, tree t, vec<tree> &types,
 /* Handle array sections for clause C.  */
 
 static bool
-handle_omp_array_sections (tree &c, enum c_omp_region_type ort)
+handle_omp_array_sections (tree *pc, tree **pnext, enum c_omp_region_type ort)
 {
+  tree c = *pc;
   bool maybe_zero_len = false;
   unsigned int first_non_one = 0;
   auto_vec<tree, 10> types;
@@ -5884,23 +5885,27 @@ handle_omp_array_sections (tree &c, enum c_omp_region_type ort)
 
 	  cp_omp_address_inspector ai (OMP_CLAUSE_LOCATION (c), t);
 
-	  tree nc = ai.expand_map_clause (c, first, addr_tokens, ort);
-	  if (nc != error_mark_node)
+	  tree* npc = ai.expand_map_clause (pc, first, addr_tokens, ort);
+	  if (npc != NULL)
 	    {
 	      using namespace omp_addr_tokenizer;
 
+	      c = *pc;
+
 	      if (ai.maybe_zero_length_array_section (c))
 		OMP_CLAUSE_MAP_MAYBE_ZERO_LENGTH_ARRAY_SECTION (c) = 1;
 
 	      /* !!! If we're accessing a base decl via chained access
 		 methods (e.g. multiple indirections), duplicate clause
 		 detection won't work properly.  Skip it in that case.  */
-	      if ((addr_tokens[0]->type == STRUCTURE_BASE
+	      if (pnext
+		  && (addr_tokens[0]->type == STRUCTURE_BASE
 		   || addr_tokens[0]->type == ARRAY_BASE)
 		  && addr_tokens[0]->u.structure_base_kind == BASE_DECL
 		  && addr_tokens[1]->type == ACCESS_METHOD
 		  && omp_access_chain_p (addr_tokens, 1))
-		c = nc;
+		/* NPC points to the last node in the new sequence.  */
+		*pnext = npc;
 
 	      return false;
 	    }
@@ -7028,7 +7033,7 @@ finish_omp_clauses (tree clauses, enum c_omp_region_type ort)
 	  t = OMP_CLAUSE_DECL (c);
 	  if (TREE_CODE (t) == OMP_ARRAY_SECTION)
 	    {
-	      if (handle_omp_array_sections (c, ort))
+	      if (handle_omp_array_sections (pc, NULL, ort))
 		{
 		  remove = true;
 		  break;
@@ -8068,7 +8073,7 @@ finish_omp_clauses (tree clauses, enum c_omp_region_type ort)
 
 	  if (TREE_CODE (t) == OMP_ARRAY_SECTION)
 	    {
-	      if (handle_omp_array_sections (c, ort))
+	      if (handle_omp_array_sections (pc, NULL, ort))
 		remove = true;
 	      else if (OMP_CLAUSE_CODE (c) == OMP_CLAUSE_DEPEND
 		       && (OMP_CLAUSE_DEPEND_KIND (c)
@@ -8237,10 +8242,13 @@ finish_omp_clauses (tree clauses, enum c_omp_region_type ort)
 		grp_start_p = pc;
 		grp_sentinel = OMP_CLAUSE_CHAIN (c);
 
-		if (handle_omp_array_sections (c, ort))
+		tree *pnext = NULL;
+		if (handle_omp_array_sections (pc, &pnext, ort))
 		  remove = true;
 		else
 		  {
+		    /* We might have replaced the clause, so refresh C.  */
+		    c = *pc;
 		    t = OMP_CLAUSE_DECL (c);
 		    if (TREE_CODE (t) != OMP_ARRAY_SECTION
 			&& !type_dependent_expression_p (t)
@@ -8338,6 +8346,8 @@ finish_omp_clauses (tree clauses, enum c_omp_region_type ort)
 		     clauses, reset the OMP_CLAUSE_SIZE (representing a bias)
 		     to zero here.  */
 		  OMP_CLAUSE_SIZE (c) = size_zero_node;
+		if (pnext)
+		  c = *pnext;
 		break;
 	      }
 	    else if (type_dependent_expression_p (t))
@@ -8583,10 +8593,10 @@ finish_omp_clauses (tree clauses, enum c_omp_region_type ort)
 	      {
 		grp_start_p = pc;
 		grp_sentinel = OMP_CLAUSE_CHAIN (c);
-		tree nc = ai.expand_map_clause (c, OMP_CLAUSE_DECL (c),
-						addr_tokens, ort);
-		if (nc != error_mark_node)
-		  c = nc;
+		tree *npc = ai.expand_map_clause (pc, OMP_CLAUSE_DECL (c),
+						  addr_tokens, ort);
+		if (npc != NULL)
+		  c = *npc;
 	      }
 	  }
 	  break;
@@ -8824,10 +8834,11 @@ finish_omp_clauses (tree clauses, enum c_omp_region_type ort)
 	  t = OMP_CLAUSE_DECL (c);
 	  if (TREE_CODE (t) == OMP_ARRAY_SECTION)
 	    {
-	      if (handle_omp_array_sections (c, ort))
+	      if (handle_omp_array_sections (pc, NULL, ort))
 		remove = true;
 	      else
 		{
+		  c = *pc;
 		  t = OMP_CLAUSE_DECL (c);
 		  while (TREE_CODE (t) == OMP_ARRAY_SECTION)
 		    t = TREE_OPERAND (t, 0);
-- 
2.41.0


^ permalink raw reply	[flat|nested] 10+ messages in thread

* [PATCH 3/5] OpenMP: Support strided and shaped-array updates for C++
  2023-09-06  9:34 [PATCH 0/5] OpenMP: Array-shaping operator and strided/rectangular 'target update' support Julian Brown
  2023-09-06  9:34 ` [PATCH 1/5] OpenMP, NVPTX: memcpy[23]D bias correction Julian Brown
  2023-09-06  9:34 ` [PATCH 2/5] OpenMP: Allow complete replacement of clause during map/to/from expansion Julian Brown
@ 2023-09-06  9:34 ` Julian Brown
  2023-09-06  9:34 ` [PATCH 4/5] OpenMP: Array shaping operator and strided "target update" for C Julian Brown
  2023-09-06  9:34 ` [PATCH 5/5] OpenMP: Noncontiguous "target update" for Fortran Julian Brown
  4 siblings, 0 replies; 10+ messages in thread
From: Julian Brown @ 2023-09-06  9:34 UTC (permalink / raw)
  To: gcc-patches; +Cc: fortran, tobias, jakub

This patch adds support for OpenMP 5.0 strided updates and the
array-shaping operator ("([x][y][z]) foo[0:n]...").  This patch concerns
C++, though necessary changes have been made to the C FE also to adjust
for changes to shared data structures.

In terms of the implementation of various bits:

 - The OMP_ARRAY_SECTION tree code has been extended to take a 'stride'
   argument, and changes have been made throughout semantics.cc, etc. to
   take the new field into account -- including bounds checking.

 - A new type of cast operator has been added to represent the OpenMP
   array-shaping operator: OMP_ARRAYSHAPE_CAST_EXPR (1).

 - The address tokenization mechanism from previous patches has been
   extended with two new access kinds to represent noncontiguous array
   updates.

 - New mapping kinds have been added to represent noncontiguous updates:
   those which may be subject to array shaping, or have non-unit strides.
   These are processed by omp-low.cc into a kind of descriptor that is
   passed to the libgomp runtime (2).

The current patch reuses an extended version of the helper code for
omp_target_memcpy_rect, which may generate very many small host-device or
device-host copies.  (The "descriptor" has also been designed so reusing
that functionality is relatively straightforward.)  Depending on the
target, some of these may use 2D/3D rectangular copies when available.

This version of the patch incorporates the following fixes from the og13
branch relative to the version previously posted for upstream:

"OpenMP: Dimension ordering for array-shaping operator for C and C++":

  https://gcc.gnu.org/pipermail/gcc-patches/2023-July/624542.html

"OpenMP: Strided/rectangular 'target update' out-of-bounds array lookup
fix":

  https://gcc.gnu.org/pipermail/gcc-patches/2023-July/624265.html

Notes:

(1) In a bit more detail: the array-shaping operator has the same
precedence as a C-style cast, but applies to the whole expression,
including array-section specifiers. We parse it initially as if it
applies to the "value" of the whole expression:

  ([x][y]) ptr[0:10:2][1:5:2]

i.e., something like:

  ([x][y]) (ptr[0:10:2][1:5:2])

or as if the cast applies to the innermost/right-hand side array
section. Then, a little later in parsing (cp_parser_omp_var_list_no_open),
we rewrite it to apply to the inner pointer instead:

  (([x][y]) ptr)[0:10:2][1:5:2]

and that means a genuine multi-dimensional array or an array-shaped
pointer can be handled pretty much the same for the rest of
compilation. We use VIEW_CONVERT_EXPR for the "cast", unless we're
processing a template definition, where we use a new tree code instead.

(2) The new map kinds work like this. An update directive starts
out with OMP_CLAUSE_TO or OMP_CLAUSE_FROM clauses representing the
block in question and the direction of the needed transfer. If we
detect a noncontiguous update, we emit a list of mapping nodes (type
OMP_CLAUSE_MAP, with new kinds, so the "mapping group" machinery in
gimplify.cc can be reused):

  OMP_CLAUSE_TO -->

  GOMP_MAP_TO_GRID (VIEW_CONVERT_EXPR<int[x][y]>(ptr) [len: <element-size>])
  GOMP_MAP_GRID_DIM 0 [len: 10]   (i.e. [0:10:2])
  GOMP_MAP_GRID_STRIDE 2
  GOMP_MAP_GRID_DIM 1 [len: 5]    (i.e. [1:5:2])
  GOMP_MAP_GRID_STRIDE 2

During omp-low.cc, this sequence is reformulated into:

  GOMP_MAP_TO_GRID (ptr) [len: <whole array size>]
  GOMP_MAP_TO_PSET (&ptr_desc [len: <desc size>])

"ptr_desc" is a struct, stored statically or constructed on the (host)
stack, containing arrays representing the size of the whole array, the
rectangular subregion to transfer, and the stride with which to walk
over elements in each dimension.

2023-09-05  Julian Brown  <julian@codesourcery.com>

gcc/c-family/
	* c-common.h (expand_array_base): Update prototype.
	* c-omp.cc (c_omp_address_inspector::map_supported_p): Support
	VIEW_CONVERT_EXPR and ADDR_EXPR codes.
	(omp_expand_grid_dim): New function.
	(omp_handle_noncontig_array): New function.
	(c_omp_address_inspector:expand_array_base): Remove DECL_P parameter.
	Support noncontiguous array updates.
	(c_omp_address_inspector::expand_component_selector): Support
	noncontiguous array updates.
	(c_omp_address_inspector::expand_map_clause): Update calls to
	expand_array_base.
	* c-pretty-print.cc (c_pretty_printer::postfix_expression): Add
	OMP_ARRAY_SECTION stride support.

gcc/c/
	* c-parser.cc (c_parser_postfix_expression_after_primary): Dummy stride
	support (for now).
	(struct omp_dim): Add stride support.
	(c_parser_omp_variable_list): Likewise.
	* c-tree.h (build_omp_array_section): Update prototype.
	* c-typeck.cc (mark_exp_read): Add stride support for
	OMP_ARRAY_SECTION.
	(build_omp_array_section): Add stride support.
	(handle_omp_array_sections_1): Add minimal stride support.

gcc/cp/
	* cp-objcp-common.cc (cp_common_init_ts): Add array-shape cast
	support.
	* cp-tree.def (OMP_ARRAYSHAPE_CAST_EXPR): Add tree code.
	* cp-tree.h (DECLTYPE_FOR_OMP_ARRAYSHAPE_CAST): Add flag.
	(cp_omp_create_arrayshape_type, cp_build_omp_arrayshape_cast): Add
	prototypes.
	(grok_omp_array_section, build_omp_array_section): Add stride
	parameters.
	* decl.cc (create_anon_array_type): New function.
	(cp_omp_create_arrayshape_type): New function.
	* decl2.cc (grok_omp_array_section): Add stride parameter.
	(min_vis_expr_r): Add OMP_ARRAYSHAPE_CAST_EXPR support.
	* error.cc (dump_expr): Add stride support for OMP_ARRAY_SECTION.
	* mangle.cc (write_expression): Add OMP_ARRAYSHAPE_CAST_EXPR support.
	* operators.def (OMP_ARRAYSHAPE_CAST_EXPR): Add.
	* parser.cc (cp_parser_new): Initialise omp_array_shaping_op_p and
	omp_has_array_shape_p fields.
	(cp_parser_statement_expr): Don't allow array shaping op in statement
	exprs.
	(cp_parser_postfix_open_square_expression): Add stride parsing for
	array sections.  Use array section code to represent array refs if we
	have an array-shaping operator.
	(cp_parser_parenthesized_expression_list): Don't allow array-shaping
	op here.
	(cp_parser_cast_expression): Add array-shaping operator parsing.
	(cp_parser_lambda_expression): Don't allow array-shaping op in lambda
	body.
	(cp_parser_braced_list): Don't allow array-shaping op in braced list.
	(struct omp_dim): Add stride field.
	(cp_parser_var_list_no_open): Add stride/array shape support.
	(cp_parser_omp_target_update): Handle noncontiguous updates.
	* parser.h (cp_parser): Add omp_array_shaping_op_p and
	omp_has_array_shape_p fields.
	* pt.cc (tsubst): Add array-shape cast support.
	(tsubst_copy, tsubst_copy_and_build): Likewise. Add stride support for
	OMP_ARRAY_SECTION.
	(tsubst_omp_clause_decl): Add stride support for OMP_ARRAY_SECTION.
	* semantics.cc (handle_omp_array_sections_1): Add DISCONTIGUOUS
	parameter and stride support.
	(omp_array_section_low_bound): New function.
	(handle_omp_array_sections): Add DISCONTIGUOUS parameter and stride
	support.
	(finish_omp_clauses): Update calls to handle_omp_array_sections, and
	add noncontiguous array update support.
	(cp_build_omp_arrayshape_cast): New function.
	* typeck.cc (structural_comptypes): Add array-shape cast support.
	(build_omp_array_section): Add stride parameter.
	(check_for_casting_away_constness): Add OMP_ARRAYSHAPE_CAST_EXPR
	support.

gcc/
	* gimplify.cc (omp_group_last, omp_group_base): Add GOMP_MAP_TO_GRID,
	GOMP_MAP_FROM_GRID support.
	(gimplify_adjust_omp_clauses): Support new GOMP_MAP_GRID_DIM,
	GOMP_MAP_GRID_STRIDE mapping nodes.  Don't crash on e.g. misuse of
	ADDR_EXPR in mapping clauses.
	* omp-general.cc (omp_parse_noncontiguous_array): New function.
	(omp_parse_access_method): Add noncontiguous array support.
	(omp_parse_structure_base): Add array-shaping support.
	(debug_omp_tokenized_addr): Add ACCESS_NONCONTIG_ARRAY,
	ACCESS_NONCONTIG_REF_TO_ARRAY token support.
	* omp-general.h (access_method_kinds): Add ACCESS_NONCONTIG_ARRAY and
	ACCESS_NONCONTIG_REF_TO_ARRAY access kinds.
	* omp-low.cc (omp_noncontig_descriptor_type): New function.
	(scan_sharing_clauses): Support noncontiguous array updates.
	(lower_omp_target): Likewise.
	* tree-pretty-print.cc (dump_omp_clause): Add GOMP_MAP_TO_GRID,
	GOMP_MAP_FROM_GRID, GOMP_MAP_GRID_DIM, GOMP_MAP_GRID_STRIDE map kinds.
	(dump_generic_node): Add stride support for OMP_ARRAY_SECTION.
	* tree.def (OMP_ARRAY_SECTION): Add stride argument.

include/
	* gomp-constants.h (gomp_map_kind): Add GOMP_MAP_TO_GRID,
	GOMP_MAP_FROM_GRID, GOMP_MAP_GRID_DIM, GOMP_MAP_GRID_STRIDE map kinds.

gcc/testsuite/
	* g++.dg/gomp/array-shaping-1.C: New test.
	* g++.dg/gomp/array-shaping-2.C: New test.
	* g++.dg/gomp/bad-array-shaping-1.C: New test.
	* g++.dg/gomp/bad-array-shaping-2.C: New test.
	* g++.dg/gomp/bad-array-shaping-3.C: New test.
	* g++.dg/gomp/bad-array-shaping-4.C: New test.
	* g++.dg/gomp/bad-array-shaping-5.C: New test.
	* g++.dg/gomp/bad-array-shaping-6.C: New test.
	* g++.dg/gomp/bad-array-shaping-7.C: New test.
	* g++.dg/gomp/bad-array-shaping-8.C: New test.

libgomp/
	* libgomp.h (omp_noncontig_array_desc): New struct.
	* target.c (omp_target_memcpy_rect_worker): Add stride array
	parameter.  Forward declare.  Add STRIDES parameter and strided
	update support.
	(gomp_update): Add noncontiguous (strided/shaped) update support.
	* testsuite/libgomp.c++/array-shaping-1.C: New test.
	* testsuite/libgomp.c++/array-shaping-2.C: New test.
	* testsuite/libgomp.c++/array-shaping-3.C: New test.
	* testsuite/libgomp.c++/array-shaping-4.C: New test.
	* testsuite/libgomp.c++/array-shaping-5.C: New test.
	* testsuite/libgomp.c++/array-shaping-6.C: New test.
	* testsuite/libgomp.c++/array-shaping-7.C: New test.
	* testsuite/libgomp.c++/array-shaping-8.C: New test.
	* testsuite/libgomp.c++/array-shaping-9.C: New test.
	* testsuite/libgomp.c++/array-shaping-10.C: New test.
	* testsuite/libgomp.c++/array-shaping-11.C: New test.
	* testsuite/libgomp.c++/array-shaping-12.C: New test.
	* testsuite/libgomp.c++/array-shaping-13.C: New test.
	* testsuite/libgomp.c-c++-common/array-shaping-14.c: New test.
---
 gcc/c-family/c-common.h                       |   2 +-
 gcc/c-family/c-omp.cc                         | 206 +++++++-
 gcc/c-family/c-pretty-print.cc                |   5 +
 gcc/c/c-parser.cc                             |  32 +-
 gcc/c/c-tree.h                                |   2 +-
 gcc/c/c-typeck.cc                             |  26 +-
 gcc/cp/cp-objcp-common.cc                     |   1 +
 gcc/cp/cp-tree.def                            |   1 +
 gcc/cp/cp-tree.h                              |  13 +-
 gcc/cp/decl.cc                                |  75 +++
 gcc/cp/decl2.cc                               |  19 +-
 gcc/cp/error.cc                               |   5 +
 gcc/cp/mangle.cc                              |   1 +
 gcc/cp/operators.def                          |   1 +
 gcc/cp/parser.cc                              | 303 ++++++++++-
 gcc/cp/parser.h                               |   7 +
 gcc/cp/pt.cc                                  |  39 +-
 gcc/cp/semantics.cc                           | 262 ++++++++--
 gcc/cp/typeck.cc                              |  12 +-
 gcc/gimplify.cc                               |  44 +-
 gcc/omp-general.cc                            |  47 ++
 gcc/omp-general.h                             |   4 +-
 gcc/omp-low.cc                                | 447 ++++++++++++++++-
 gcc/testsuite/g++.dg/gomp/array-shaping-1.C   |  22 +
 gcc/testsuite/g++.dg/gomp/array-shaping-2.C   | 134 +++++
 .../g++.dg/gomp/bad-array-shaping-1.C         |  47 ++
 .../g++.dg/gomp/bad-array-shaping-2.C         |  52 ++
 .../g++.dg/gomp/bad-array-shaping-3.C         |  53 ++
 .../g++.dg/gomp/bad-array-shaping-4.C         |  60 +++
 .../g++.dg/gomp/bad-array-shaping-5.C         |  55 ++
 .../g++.dg/gomp/bad-array-shaping-6.C         |  59 +++
 .../g++.dg/gomp/bad-array-shaping-7.C         |  48 ++
 .../g++.dg/gomp/bad-array-shaping-8.C         |  50 ++
 gcc/tree-pretty-print.cc                      |  17 +
 gcc/tree.def                                  |   2 +-
 include/gomp-constants.h                      |   7 +-
 libgomp/libgomp.h                             |  14 +
 libgomp/target.c                              | 260 ++++++----
 .../testsuite/libgomp.c++/array-shaping-1.C   | 469 ++++++++++++++++++
 .../testsuite/libgomp.c++/array-shaping-10.C  |  61 +++
 .../testsuite/libgomp.c++/array-shaping-11.C  |  63 +++
 .../testsuite/libgomp.c++/array-shaping-12.C  |  65 +++
 .../testsuite/libgomp.c++/array-shaping-13.C  |  89 ++++
 .../testsuite/libgomp.c++/array-shaping-2.C   |  38 ++
 .../testsuite/libgomp.c++/array-shaping-3.C   |  38 ++
 .../testsuite/libgomp.c++/array-shaping-4.C   |  38 ++
 .../testsuite/libgomp.c++/array-shaping-5.C   |  38 ++
 .../testsuite/libgomp.c++/array-shaping-6.C   |  54 ++
 .../testsuite/libgomp.c++/array-shaping-7.C   |  54 ++
 .../testsuite/libgomp.c++/array-shaping-8.C   |  65 +++
 .../testsuite/libgomp.c++/array-shaping-9.C   |  95 ++++
 .../libgomp.c-c++-common/array-shaping-14.c   |  34 ++
 52 files changed, 3420 insertions(+), 215 deletions(-)
 create mode 100644 gcc/testsuite/g++.dg/gomp/array-shaping-1.C
 create mode 100644 gcc/testsuite/g++.dg/gomp/array-shaping-2.C
 create mode 100644 gcc/testsuite/g++.dg/gomp/bad-array-shaping-1.C
 create mode 100644 gcc/testsuite/g++.dg/gomp/bad-array-shaping-2.C
 create mode 100644 gcc/testsuite/g++.dg/gomp/bad-array-shaping-3.C
 create mode 100644 gcc/testsuite/g++.dg/gomp/bad-array-shaping-4.C
 create mode 100644 gcc/testsuite/g++.dg/gomp/bad-array-shaping-5.C
 create mode 100644 gcc/testsuite/g++.dg/gomp/bad-array-shaping-6.C
 create mode 100644 gcc/testsuite/g++.dg/gomp/bad-array-shaping-7.C
 create mode 100644 gcc/testsuite/g++.dg/gomp/bad-array-shaping-8.C
 create mode 100644 libgomp/testsuite/libgomp.c++/array-shaping-1.C
 create mode 100644 libgomp/testsuite/libgomp.c++/array-shaping-10.C
 create mode 100644 libgomp/testsuite/libgomp.c++/array-shaping-11.C
 create mode 100644 libgomp/testsuite/libgomp.c++/array-shaping-12.C
 create mode 100644 libgomp/testsuite/libgomp.c++/array-shaping-13.C
 create mode 100644 libgomp/testsuite/libgomp.c++/array-shaping-2.C
 create mode 100644 libgomp/testsuite/libgomp.c++/array-shaping-3.C
 create mode 100644 libgomp/testsuite/libgomp.c++/array-shaping-4.C
 create mode 100644 libgomp/testsuite/libgomp.c++/array-shaping-5.C
 create mode 100644 libgomp/testsuite/libgomp.c++/array-shaping-6.C
 create mode 100644 libgomp/testsuite/libgomp.c++/array-shaping-7.C
 create mode 100644 libgomp/testsuite/libgomp.c++/array-shaping-8.C
 create mode 100644 libgomp/testsuite/libgomp.c++/array-shaping-9.C
 create mode 100644 libgomp/testsuite/libgomp.c-c++-common/array-shaping-14.c

diff --git a/gcc/c-family/c-common.h b/gcc/c-family/c-common.h
index e2b85d394d32..47de4571d9fc 100644
--- a/gcc/c-family/c-common.h
+++ b/gcc/c-family/c-common.h
@@ -1384,7 +1384,7 @@ public:
   bool maybe_zero_length_array_section (tree);
 
   tree * expand_array_base (tree *, vec<omp_addr_token *> &, tree, unsigned *,
-			    c_omp_region_type, bool);
+			    c_omp_region_type);
   tree * expand_component_selector (tree *, vec<omp_addr_token *> &, tree,
 				    unsigned *);
   tree * expand_map_clause (tree *, tree, vec<omp_addr_token *> &,
diff --git a/gcc/c-family/c-omp.cc b/gcc/c-family/c-omp.cc
index 3c6b79107edc..0eb9868a33bb 100644
--- a/gcc/c-family/c-omp.cc
+++ b/gcc/c-family/c-omp.cc
@@ -3243,7 +3243,9 @@ c_omp_address_inspector::map_supported_p ()
 	 || TREE_CODE (t) == POINTER_PLUS_EXPR
 	 || TREE_CODE (t) == NON_LVALUE_EXPR
 	 || TREE_CODE (t) == OMP_ARRAY_SECTION
-	 || TREE_CODE (t) == NOP_EXPR)
+	 || TREE_CODE (t) == NOP_EXPR
+	 || TREE_CODE (t) == VIEW_CONVERT_EXPR
+	 || TREE_CODE (t) == ADDR_EXPR)
     if (TREE_CODE (t) == COMPOUND_EXPR)
       t = TREE_OPERAND (t, 1);
     else
@@ -3393,21 +3395,95 @@ omp_expand_access_chain (tree *pc, tree expr,
   return pc;
 }
 
+static tree *
+omp_expand_grid_dim (location_t loc, tree *pc, tree decl)
+{
+  if (TREE_CODE (decl) == OMP_ARRAY_SECTION)
+    pc = omp_expand_grid_dim (loc, pc, TREE_OPERAND (decl, 0));
+  else
+    return pc;
+
+  tree c = *pc;
+  tree low_bound = TREE_OPERAND (decl, 1);
+  tree length = TREE_OPERAND (decl, 2);
+  tree stride = TREE_OPERAND (decl, 3);
+
+  tree cd = build_omp_clause (loc, OMP_CLAUSE_MAP);
+  OMP_CLAUSE_SET_MAP_KIND (cd, GOMP_MAP_GRID_DIM);
+  OMP_CLAUSE_DECL (cd) = unshare_expr (low_bound);
+  OMP_CLAUSE_SIZE (cd) = unshare_expr (length);
+
+  if (stride && !integer_onep (stride))
+    {
+      tree cs = build_omp_clause (loc, OMP_CLAUSE_MAP);
+      OMP_CLAUSE_SET_MAP_KIND (cs, GOMP_MAP_GRID_STRIDE);
+      OMP_CLAUSE_DECL (cs) = unshare_expr (stride);
+
+      OMP_CLAUSE_CHAIN (cs) = OMP_CLAUSE_CHAIN (c);
+      OMP_CLAUSE_CHAIN (cd) = cs;
+      OMP_CLAUSE_CHAIN (c) = cd;
+      pc = &OMP_CLAUSE_CHAIN (cd);
+    }
+  else
+    {
+      OMP_CLAUSE_CHAIN (cd) = OMP_CLAUSE_CHAIN (c);
+      OMP_CLAUSE_CHAIN (c) = cd;
+      pc = &OMP_CLAUSE_CHAIN (c);
+    }
+
+  return pc;
+}
+
+tree *
+omp_handle_noncontig_array (location_t loc, tree *pc, tree c, tree base)
+{
+  tree type;
+
+  if (POINTER_TYPE_P (TREE_TYPE (base)))
+    type = TREE_TYPE (TREE_TYPE (base));
+  else
+    type = strip_array_types (TREE_TYPE (base));
+
+  tree c_map = build_omp_clause (loc, OMP_CLAUSE_MAP);
+
+  OMP_CLAUSE_DECL (c_map) = unshare_expr (base);
+  /* Use the element size (or pointed-to type size) here.  */
+  OMP_CLAUSE_SIZE (c_map) = TYPE_SIZE_UNIT (type);
+
+  switch (OMP_CLAUSE_CODE (c))
+    {
+    case OMP_CLAUSE_TO:
+      OMP_CLAUSE_SET_MAP_KIND (c_map, GOMP_MAP_TO_GRID);
+      break;
+    case OMP_CLAUSE_FROM:
+      OMP_CLAUSE_SET_MAP_KIND (c_map, GOMP_MAP_FROM_GRID);
+      break;
+    default:
+      gcc_unreachable ();
+    }
+
+  OMP_CLAUSE_CHAIN (c_map) = OMP_CLAUSE_CHAIN (c);
+
+  *pc = c_map;
+
+  return omp_expand_grid_dim (loc, pc, OMP_CLAUSE_DECL (c));
+}
+
 /* Translate "array_base_decl access_method" to OMP mapping clauses.  */
 
 tree *
 c_omp_address_inspector::expand_array_base (tree *pc,
 					    vec<omp_addr_token *> &addr_tokens,
 					    tree expr, unsigned *idx,
-					    c_omp_region_type ort,
-					    bool decl_p)
+					    c_omp_region_type ort)
 {
   using namespace omp_addr_tokenizer;
   tree c = *pc;
   location_t loc = OMP_CLAUSE_LOCATION (c);
   int i = *idx;
   tree decl = addr_tokens[i + 1]->expr;
-  bool declare_target_p = (decl_p
+  bool decl_p = DECL_P (decl);
+  bool declare_target_p = (DECL_P (decl)
 			   && is_global_var (decl)
 			   && lookup_attribute ("omp declare target",
 						DECL_ATTRIBUTES (decl)));
@@ -3419,6 +3495,7 @@ c_omp_address_inspector::expand_array_base (tree *pc,
   unsigned consume_tokens = 2;
   bool target = (ort & C_ORT_TARGET) != 0;
   bool openmp = (ort & C_ORT_OMP) != 0;
+  unsigned acc = i + 1;
 
   gcc_assert (i == 0);
 
@@ -3431,7 +3508,15 @@ c_omp_address_inspector::expand_array_base (tree *pc,
       return pc;
     }
 
-  switch (addr_tokens[i + 1]->u.access_kind)
+  if (!map_p && chain_p)
+    {
+      /* See comment in c_omp_address_inspector::expand_component_selector.  */
+      while (acc + 1 < addr_tokens.length ()
+	     && addr_tokens[acc + 1]->type == ACCESS_METHOD)
+	acc++;
+    }
+
+  switch (addr_tokens[acc]->u.access_kind)
     {
     case ACCESS_DIRECT:
       if (decl_p && !target)
@@ -3672,6 +3757,40 @@ c_omp_address_inspector::expand_array_base (tree *pc,
       }
       break;
 
+    case ACCESS_NONCONTIG_ARRAY:
+      {
+	gcc_assert (!map_p);
+
+	tree base = addr_tokens[acc]->expr;
+
+	if (decl_p)
+	  c_common_mark_addressable_vec (base);
+
+	pc = omp_handle_noncontig_array (loc, pc, c, base);
+	consume_tokens = (acc + 1) - i;
+	chain_p = false;
+      }
+      break;
+
+    case ACCESS_NONCONTIG_REF_TO_ARRAY:
+      {
+	gcc_assert (!map_p);
+
+	if (decl_p)
+	  c_common_mark_addressable_vec (addr_tokens[acc]->expr);
+
+	/* Or here.  */
+	gcc_assert (!chain_p);
+
+	tree base = addr_tokens[i + 1]->expr;
+	base = convert_from_reference (base);
+
+	pc = omp_handle_noncontig_array (loc, pc, c, base);
+	consume_tokens = (acc + 1) - i;
+	chain_p = false;
+      }
+      break;
+
     default:
       *idx = i + consume_tokens;
       return NULL;
@@ -3722,8 +3841,27 @@ c_omp_address_inspector::expand_component_selector (tree *pc,
   tree c2 = NULL_TREE, c3 = NULL_TREE;
   bool chain_p = omp_access_chain_p (addr_tokens, i + 1);
   bool map_p = OMP_CLAUSE_CODE (c) == OMP_CLAUSE_MAP;
+  unsigned acc = i + 1;
 
-  switch (addr_tokens[i + 1]->u.access_kind)
+  if (!map_p && chain_p)
+    {
+      /* We have a non-map clause (i.e. to/from for an "update" directive),
+	 and we might have a noncontiguous array section at the end of a
+	 chain of other accesses, e.g. pointer indirections like this:
+
+	   struct_base_decl access_pointer access_pointer component_selector
+	     access_pointer access_pointer access_noncontig_array
+
+	 We only need to process the last access in this case, so skip
+	 over previous accesses.  */
+
+      while (acc + 1 < addr_tokens.length ()
+	     && addr_tokens[acc + 1]->type == ACCESS_METHOD)
+	acc++;
+      chain_p = false;
+    }
+
+  switch (addr_tokens[acc]->u.access_kind)
     {
     case ACCESS_DIRECT:
     case ACCESS_INDEXED_ARRAY:
@@ -3733,7 +3871,7 @@ c_omp_address_inspector::expand_component_selector (tree *pc,
       {
 	/* Copy the referenced object.  Note that we also do this for !MAP_P
 	   clauses.  */
-	tree obj = convert_from_reference (addr_tokens[i + 1]->expr);
+	tree obj = convert_from_reference (addr_tokens[acc]->expr);
 	OMP_CLAUSE_DECL (c) = obj;
 	OMP_CLAUSE_SIZE (c) = TYPE_SIZE_UNIT (TREE_TYPE (obj));
 
@@ -3742,7 +3880,7 @@ c_omp_address_inspector::expand_component_selector (tree *pc,
 
 	c2 = build_omp_clause (loc, OMP_CLAUSE_MAP);
 	OMP_CLAUSE_SET_MAP_KIND (c2, GOMP_MAP_ATTACH_DETACH);
-	OMP_CLAUSE_DECL (c2) = addr_tokens[i + 1]->expr;
+	OMP_CLAUSE_DECL (c2) = addr_tokens[acc]->expr;
 	OMP_CLAUSE_SIZE (c2) = size_zero_node;
       }
       break;
@@ -3753,15 +3891,15 @@ c_omp_address_inspector::expand_component_selector (tree *pc,
 	  break;
 
 	tree virtual_origin
-	  = convert_from_reference (addr_tokens[i + 1]->expr);
+	  = convert_from_reference (addr_tokens[acc]->expr);
 	virtual_origin = build_fold_addr_expr (virtual_origin);
 	virtual_origin = fold_convert_loc (loc, ptrdiff_type_node,
 					   virtual_origin);
-	tree data_addr = omp_accessed_addr (addr_tokens, i + 1, expr);
+	tree data_addr = omp_accessed_addr (addr_tokens, acc, expr);
 
 	c2 = build_omp_clause (loc, OMP_CLAUSE_MAP);
 	OMP_CLAUSE_SET_MAP_KIND (c2, GOMP_MAP_ATTACH_DETACH);
-	OMP_CLAUSE_DECL (c2) = addr_tokens[i + 1]->expr;
+	OMP_CLAUSE_DECL (c2) = addr_tokens[acc]->expr;
 	OMP_CLAUSE_SIZE (c2)
 	  = fold_build2_loc (loc, MINUS_EXPR, ptrdiff_type_node,
 			     fold_convert_loc (loc, ptrdiff_type_node,
@@ -3778,12 +3916,12 @@ c_omp_address_inspector::expand_component_selector (tree *pc,
 
 	tree virtual_origin
 	  = fold_convert_loc (loc, ptrdiff_type_node,
-			      addr_tokens[i + 1]->expr);
-	tree data_addr = omp_accessed_addr (addr_tokens, i + 1, expr);
+			      addr_tokens[acc]->expr);
+	tree data_addr = omp_accessed_addr (addr_tokens, acc, expr);
 
 	c2 = build_omp_clause (loc, OMP_CLAUSE_MAP);
 	OMP_CLAUSE_SET_MAP_KIND (c2, GOMP_MAP_ATTACH_DETACH);
-	OMP_CLAUSE_DECL (c2) = addr_tokens[i + 1]->expr;
+	OMP_CLAUSE_DECL (c2) = addr_tokens[acc]->expr;
 	OMP_CLAUSE_SIZE (c2)
 	  = fold_build2_loc (loc, MINUS_EXPR, ptrdiff_type_node,
 			     fold_convert_loc (loc, ptrdiff_type_node,
@@ -3798,10 +3936,10 @@ c_omp_address_inspector::expand_component_selector (tree *pc,
 	if (!map_p)
 	  break;
 
-	tree ptr = convert_from_reference (addr_tokens[i + 1]->expr);
+	tree ptr = convert_from_reference (addr_tokens[acc]->expr);
 	tree virtual_origin = fold_convert_loc (loc, ptrdiff_type_node,
 						ptr);
-	tree data_addr = omp_accessed_addr (addr_tokens, i + 1, expr);
+	tree data_addr = omp_accessed_addr (addr_tokens, acc, expr);
 
 	/* Attach the pointer...  */
 	c2 = build_omp_clause (OMP_CLAUSE_LOCATION (c), OMP_CLAUSE_MAP);
@@ -3816,13 +3954,38 @@ c_omp_address_inspector::expand_component_selector (tree *pc,
 	/* ...and also the reference.  */
 	c3 = build_omp_clause (OMP_CLAUSE_LOCATION (c), OMP_CLAUSE_MAP);
 	OMP_CLAUSE_SET_MAP_KIND (c3, GOMP_MAP_ATTACH_DETACH);
-	OMP_CLAUSE_DECL (c3) = addr_tokens[i + 1]->expr;
+	OMP_CLAUSE_DECL (c3) = addr_tokens[acc]->expr;
 	OMP_CLAUSE_SIZE (c3) = size_zero_node;
       }
       break;
 
+    case ACCESS_NONCONTIG_ARRAY:
+      {
+	gcc_assert (!map_p);
+
+	/* We don't expect to see further accesses here.  */
+	gcc_assert (!chain_p);
+
+	pc = omp_handle_noncontig_array (loc, pc, c, addr_tokens[acc]->expr);
+      }
+      break;
+
+    case ACCESS_NONCONTIG_REF_TO_ARRAY:
+      {
+	gcc_assert (!map_p);
+
+	/* Or here.  */
+	gcc_assert (!chain_p);
+
+	tree base = addr_tokens[acc]->expr;
+	base = convert_from_reference (base);
+
+	pc = omp_handle_noncontig_array (loc, pc, c, base);
+      }
+      break;
+
     default:
-      *idx = i + 2;
+      *idx = acc + 1;
       return NULL;
     }
 
@@ -3840,8 +4003,7 @@ c_omp_address_inspector::expand_component_selector (tree *pc,
       pc = &OMP_CLAUSE_CHAIN (c);
     }
 
-  i += 2;
-  *idx = i;
+  *idx = acc + 1;
 
   if (chain_p && map_p)
     return omp_expand_access_chain (pc, expr, addr_tokens, idx);
@@ -3869,7 +4031,7 @@ c_omp_address_inspector::expand_map_clause (tree *pc, tree expr,
 	  && addr_tokens[i]->u.structure_base_kind == BASE_DECL
 	  && addr_tokens[i + 1]->type == ACCESS_METHOD)
 	{
-	  pc = expand_array_base (pc, addr_tokens, expr, &i, ort, true);
+	  pc = expand_array_base (pc, addr_tokens, expr, &i, ort);
 	  if (pc == NULL)
 	    return NULL;
 	}
@@ -3878,7 +4040,7 @@ c_omp_address_inspector::expand_map_clause (tree *pc, tree expr,
 	       && addr_tokens[i]->u.structure_base_kind == BASE_ARBITRARY_EXPR
 	       && addr_tokens[i + 1]->type == ACCESS_METHOD)
 	{
-	  pc = expand_array_base (pc, addr_tokens, expr, &i, ort, false);
+	  pc = expand_array_base (pc, addr_tokens, expr, &i, ort);
 	  if (pc == NULL)
 	    return NULL;
 	}
diff --git a/gcc/c-family/c-pretty-print.cc b/gcc/c-family/c-pretty-print.cc
index 225ac7ef2851..fc99b951f988 100644
--- a/gcc/c-family/c-pretty-print.cc
+++ b/gcc/c-family/c-pretty-print.cc
@@ -1623,6 +1623,11 @@ c_pretty_printer::postfix_expression (tree e)
       pp_colon (this);
       if (TREE_OPERAND (e, 2))
 	expression (TREE_OPERAND (e, 2));
+      if (TREE_OPERAND (e, 3))
+	{
+	  pp_colon (this);
+	  expression (TREE_OPERAND (e, 3));
+	}
       pp_c_right_bracket (this);
       break;
 
diff --git a/gcc/c/c-parser.cc b/gcc/c/c-parser.cc
index 35b9ba6f2f0c..be80c2723ef0 100644
--- a/gcc/c/c-parser.cc
+++ b/gcc/c/c-parser.cc
@@ -11281,7 +11281,7 @@ c_parser_postfix_expression_after_primary (c_parser *parser,
 	      start = expr.get_start ();
 	      finish = parser->tokens_buf[0].location;
 	      expr.value = build_omp_array_section (op_loc, expr.value, idx,
-						    len);
+						    len, NULL_TREE /* fixme */);
 	      set_c_expr_source_range (&expr, start, finish);
 	      expr.original_code = ERROR_MARK;
 	      expr.original_type = NULL;
@@ -13852,11 +13852,11 @@ c_parser_oacc_wait_list (c_parser *parser, location_t clause_loc, tree list)
 
 struct omp_dim
 {
-  tree low_bound, length;
+  tree low_bound, length, stride;
   location_t loc;
   bool no_colon;
-  omp_dim (tree lb, tree len, location_t lo, bool nc)
-  : low_bound (lb), length (len), loc (lo), no_colon (nc) {}
+  omp_dim (tree lb, tree len, tree str, location_t lo, bool nc)
+  : low_bound (lb), length (len), stride (str), loc (lo), no_colon (nc) {}
 };
 
 static tree
@@ -13984,7 +13984,9 @@ c_parser_omp_variable_list (c_parser *parser,
 		{
 		  tree low_bound = TREE_OPERAND (decl, 1);
 		  tree length = TREE_OPERAND (decl, 2);
-		  dims.safe_push (omp_dim (low_bound, length, loc, false));
+		  tree stride = TREE_OPERAND (decl, 3);
+		  dims.safe_push (omp_dim (low_bound, length, stride, loc,
+					   false));
 		  decl = TREE_OPERAND (decl, 0);
 		}
 
@@ -14000,21 +14002,22 @@ c_parser_omp_variable_list (c_parser *parser,
 		  else if (TREE_CODE (decl) == INDIRECT_REF)
 		    {
 		      dims.safe_push (omp_dim (integer_zero_node,
-					       integer_one_node, loc, true));
+					       integer_one_node, NULL_TREE, loc,
+					       true));
 		      decl = TREE_OPERAND (decl, 0);
 		    }
 		  else  /* ARRAY_REF. */
 		    {
 		      tree index = TREE_OPERAND (decl, 1);
-		      dims.safe_push (omp_dim (index, integer_one_node, loc,
-					       true));
+		      dims.safe_push (omp_dim (index, integer_one_node,
+					       NULL_TREE, loc, true));
 		      decl = TREE_OPERAND (decl, 0);
 		    }
 		}
 
 	      for (int i = dims.length () - 1; i >= 0; i--)
 		decl = build_omp_array_section (loc,  decl, dims[i].low_bound,
-						dims[i].length);
+						dims[i].length, dims[i].stride);
 	    }
 	  else if (TREE_CODE (decl) == INDIRECT_REF)
 	    {
@@ -14024,7 +14027,7 @@ c_parser_omp_variable_list (c_parser *parser,
 	      STRIP_NOPS (decl);
 
 	      decl = build_omp_array_section (loc, decl, integer_zero_node,
-					      integer_one_node);
+					      integer_one_node, NULL_TREE);
 	    }
 	  else if (TREE_CODE (decl) == ARRAY_REF)
 	    {
@@ -14033,7 +14036,8 @@ c_parser_omp_variable_list (c_parser *parser,
 	      decl = TREE_OPERAND (decl, 0);
 	      STRIP_NOPS (decl);
 
-	      decl = build_omp_array_section (loc, decl, idx, integer_one_node);
+	      decl = build_omp_array_section (loc, decl, idx, integer_one_node,
+					      NULL_TREE);
 	    }
 	  else if (TREE_CODE (decl) == NON_LVALUE_EXPR
 		   || CONVERT_EXPR_P (decl))
@@ -14187,7 +14191,8 @@ c_parser_omp_variable_list (c_parser *parser,
 		      break;
 		    }
 
-		  dims.safe_push (omp_dim (low_bound, length, loc, no_colon));
+		  dims.safe_push (omp_dim (low_bound, length, NULL_TREE, loc,
+				  no_colon));
 		}
 
 	      if (t != error_mark_node)
@@ -14211,7 +14216,8 @@ c_parser_omp_variable_list (c_parser *parser,
 		    for (unsigned i = 0; i < dims.length (); i++)
 		      t = build_omp_array_section (clause_loc, t,
 						   dims[i].low_bound,
-						   dims[i].length);
+						   dims[i].length,
+						   dims[i].stride);
 		}
 
 	      if ((kind == OMP_CLAUSE_DEPEND || kind == OMP_CLAUSE_AFFINITY)
diff --git a/gcc/c/c-tree.h b/gcc/c/c-tree.h
index d2cc975a5ba3..10f1cf26dd28 100644
--- a/gcc/c/c-tree.h
+++ b/gcc/c/c-tree.h
@@ -765,7 +765,7 @@ extern tree composite_type (tree, tree);
 extern tree build_component_ref (location_t, tree, tree, location_t,
 				 location_t);
 extern tree build_array_ref (location_t, tree, tree);
-extern tree build_omp_array_section (location_t, tree, tree, tree);
+extern tree build_omp_array_section (location_t, tree, tree, tree, tree);
 extern tree build_external_ref (location_t, tree, bool, tree *);
 extern void pop_maybe_used (bool);
 extern struct c_expr c_expr_sizeof_expr (location_t, struct c_expr);
diff --git a/gcc/c/c-typeck.cc b/gcc/c/c-typeck.cc
index fb480994520e..c7409045abf3 100644
--- a/gcc/c/c-typeck.cc
+++ b/gcc/c/c-typeck.cc
@@ -2010,6 +2010,8 @@ mark_exp_read (tree exp)
 	mark_exp_read (TREE_OPERAND (exp, 1));
       if (TREE_OPERAND (exp, 2))
 	mark_exp_read (TREE_OPERAND (exp, 2));
+      if (TREE_OPERAND (exp, 3))
+	mark_exp_read (TREE_OPERAND (exp, 3));
       break;
     default:
       break;
@@ -2895,7 +2897,8 @@ build_array_ref (location_t loc, tree array, tree index)
    instead.  */
 
 tree
-build_omp_array_section (location_t loc, tree array, tree index, tree length)
+build_omp_array_section (location_t loc, tree array, tree index, tree length,
+			 tree stride)
 {
   tree idxtype;
 
@@ -2932,7 +2935,8 @@ build_omp_array_section (location_t loc, tree array, tree index, tree length)
   else
     sectype = build_array_type (eltype, idxtype);
 
-  return build3_loc (loc, OMP_ARRAY_SECTION, sectype, array, index, length);
+  return build4_loc (loc, OMP_ARRAY_SECTION, sectype, array, index, length,
+		     stride);
 }
 
 \f
@@ -13694,7 +13698,7 @@ handle_omp_array_sections_1 (tree c, tree t, vec<tree> &types,
 			     bool &maybe_zero_len, unsigned int &first_non_one,
 			     enum c_omp_region_type ort)
 {
-  tree ret, low_bound, length, type;
+  tree ret, low_bound, length, stride, type;
   bool openacc = (ort & C_ORT_ACC) != 0;
   if (TREE_CODE (t) != OMP_ARRAY_SECTION)
     {
@@ -13779,8 +13783,11 @@ handle_omp_array_sections_1 (tree c, tree t, vec<tree> &types,
   type = TREE_TYPE (ret);
   low_bound = TREE_OPERAND (t, 1);
   length = TREE_OPERAND (t, 2);
+  stride = TREE_OPERAND (t, 3);
 
-  if (low_bound == error_mark_node || length == error_mark_node)
+  if (low_bound == error_mark_node
+      || length == error_mark_node
+      || stride == error_mark_node)
     return error_mark_node;
 
   if (low_bound && !INTEGRAL_TYPE_P (TREE_TYPE (low_bound)))
@@ -13797,6 +13804,13 @@ handle_omp_array_sections_1 (tree c, tree t, vec<tree> &types,
 		length);
       return error_mark_node;
     }
+  if (stride && !INTEGRAL_TYPE_P (TREE_TYPE (stride)))
+    {
+      error_at (OMP_CLAUSE_LOCATION (c),
+		"stride %qE of array section does not have integral type",
+		stride);
+      return error_mark_node;
+    }
   if (low_bound
       && TREE_CODE (low_bound) == INTEGER_CST
       && TYPE_PRECISION (TREE_TYPE (low_bound))
@@ -14011,7 +14025,9 @@ handle_omp_array_sections_1 (tree c, tree t, vec<tree> &types,
 	       d = TREE_OPERAND (d, 0))
 	    {
 	      tree d_length = TREE_OPERAND (d, 2);
-	      if (d_length == NULL_TREE || !integer_onep (d_length))
+	      tree d_stride = TREE_OPERAND (d, 3);
+	      if (d_length == NULL_TREE || !integer_onep (d_length)
+		  || (d_stride && !integer_onep (d_stride)))
 		{
 		  error_at (OMP_CLAUSE_LOCATION (c),
 			    "array section is not contiguous in %qs clause",
diff --git a/gcc/cp/cp-objcp-common.cc b/gcc/cp/cp-objcp-common.cc
index 93b027b80ce8..77dbac93f3f0 100644
--- a/gcc/cp/cp-objcp-common.cc
+++ b/gcc/cp/cp-objcp-common.cc
@@ -530,6 +530,7 @@ cp_common_init_ts (void)
   MARK_TS_EXP (OFFSET_REF);
   MARK_TS_EXP (PSEUDO_DTOR_EXPR);
   MARK_TS_EXP (REINTERPRET_CAST_EXPR);
+  MARK_TS_EXP (OMP_ARRAYSHAPE_CAST_EXPR);
   MARK_TS_EXP (SCOPE_REF);
   MARK_TS_EXP (STATIC_CAST_EXPR);
   MARK_TS_EXP (STMT_EXPR);
diff --git a/gcc/cp/cp-tree.def b/gcc/cp/cp-tree.def
index 0e66ca70e00c..08d66c25a891 100644
--- a/gcc/cp/cp-tree.def
+++ b/gcc/cp/cp-tree.def
@@ -256,6 +256,7 @@ DEFTREECODE (REINTERPRET_CAST_EXPR, "reinterpret_cast_expr", tcc_unary, 1)
 DEFTREECODE (CONST_CAST_EXPR, "const_cast_expr", tcc_unary, 1)
 DEFTREECODE (STATIC_CAST_EXPR, "static_cast_expr", tcc_unary, 1)
 DEFTREECODE (DYNAMIC_CAST_EXPR, "dynamic_cast_expr", tcc_unary, 1)
+DEFTREECODE (OMP_ARRAYSHAPE_CAST_EXPR, "omp_arrayshape_cast_expr", tcc_unary, 1)
 DEFTREECODE (IMPLICIT_CONV_EXPR, "implicit_conv_expr", tcc_unary, 1)
 DEFTREECODE (DOTSTAR_EXPR, "dotstar_expr", tcc_expression, 2)
 DEFTREECODE (TYPEID_EXPR, "typeid_expr", tcc_expression, 1)
diff --git a/gcc/cp/cp-tree.h b/gcc/cp/cp-tree.h
index d2561fee8092..2f9a369de56f 100644
--- a/gcc/cp/cp-tree.h
+++ b/gcc/cp/cp-tree.h
@@ -508,6 +508,7 @@ extern GTY(()) tree cp_global_trees[CPTI_MAX];
       OVL_LOOKUP_P (in OVERLOAD)
       LOOKUP_FOUND_P (in RECORD_TYPE, UNION_TYPE, ENUMERAL_TYPE, NAMESPACE_DECL)
       FNDECL_MANIFESTLY_CONST_EVALUATED (in FUNCTION_DECL)
+      DECLTYPE_FOR_OMP_ARRAYSHAPE_CAST (in DECLTYPE_TYPE)
    5: IDENTIFIER_VIRTUAL_P (in IDENTIFIER_NODE)
       FUNCTION_RVALUE_QUALIFIED (in FUNCTION_TYPE, METHOD_TYPE)
       CALL_EXPR_REVERSE_ARGS (in CALL_EXPR, AGGR_INIT_EXPR)
@@ -4876,6 +4877,8 @@ get_vec_init_expr (tree t)
   TREE_LANG_FLAG_2 (DECLTYPE_TYPE_CHECK (NODE))
 #define DECLTYPE_FOR_REF_CAPTURE(NODE) \
   TREE_LANG_FLAG_3 (DECLTYPE_TYPE_CHECK (NODE))
+#define DECLTYPE_FOR_OMP_ARRAYSHAPE_CAST(NODE) \
+  TREE_LANG_FLAG_4 (DECLTYPE_TYPE_CHECK (NODE))
 
 /* Nonzero for VAR_DECL and FUNCTION_DECL node means that `extern' was
    specified in its declaration.  This can also be set for an
@@ -6956,6 +6959,8 @@ extern tree cxx_comdat_group			(tree);
 extern bool cp_missing_noreturn_ok_p		(tree);
 extern bool is_direct_enum_init			(tree, tree);
 extern void initialize_artificial_var		(tree, vec<constructor_elt, va_gc> *);
+extern tree cp_omp_create_arrayshape_type	(location_t, tree,
+						 vec<cp_expr> *);
 extern tree check_var_type			(tree, tree, location_t);
 extern tree reshape_init                        (tree, tree, tsubst_flags_t);
 extern tree next_aggregate_field		(tree);
@@ -6989,7 +6994,8 @@ extern void grokclassfn				(tree, tree,
 						 enum overload_flags);
 extern tree grok_array_decl			(location_t, tree, tree,
 						 vec<tree, va_gc> **, tsubst_flags_t);
-extern tree grok_omp_array_section		(location_t, tree, tree, tree);
+extern tree grok_omp_array_section		(location_t, tree, tree, tree,
+						 tree);
 extern tree delete_sanity			(location_t, tree, tree, bool,
 						 int, tsubst_flags_t);
 extern tree check_classfn			(tree, tree, tree);
@@ -7845,6 +7851,8 @@ extern tree cp_build_vec_convert		(tree, location_t, tree,
 						 tsubst_flags_t);
 extern tree cp_build_bit_cast			(location_t, tree, tree,
 						 tsubst_flags_t);
+extern tree cp_build_omp_arrayshape_cast	(location_t, tree, tree,
+						 tsubst_flags_t);
 extern void start_lambda_scope			(tree decl);
 extern void finish_lambda_scope			(void);
 extern void record_lambda_scope			(tree lambda);
@@ -8097,7 +8105,8 @@ inline tree build_x_binary_op (const op_location_t &loc,
 }
 extern tree build_x_array_ref			(location_t, tree, tree,
 						 tsubst_flags_t);
-extern tree build_omp_array_section		(location_t, tree, tree, tree);
+extern tree build_omp_array_section		(location_t, tree, tree, tree,
+						 tree);
 extern tree build_x_unary_op			(location_t,
 						 enum tree_code, cp_expr,
 						 tree, tsubst_flags_t);
diff --git a/gcc/cp/decl.cc b/gcc/cp/decl.cc
index 6779de0fb7a6..f6eb1305d6bc 100644
--- a/gcc/cp/decl.cc
+++ b/gcc/cp/decl.cc
@@ -11584,6 +11584,81 @@ create_array_type_for_decl (tree name, tree type, tree size, location_t loc)
   return build_cplus_array_type (type, itype);
 }
 
+/* Build an anonymous array of SIZE elements of ELTYPE.  */
+
+static tree
+create_anon_array_type (location_t loc, tree eltype, tree size)
+{
+  if (eltype == error_mark_node || size == error_mark_node)
+    return error_mark_node;
+
+  tree itype = compute_array_index_type_loc (loc, NULL_TREE, size,
+					     tf_warning_or_error);
+
+  if (type_uses_auto (eltype)
+      && variably_modified_type_p (itype, /*fn=*/NULL_TREE))
+    {
+      sorry_at (loc, "variable-length array of %<auto%>");
+      return error_mark_node;
+    }
+
+  return build_cplus_array_type (eltype, itype);
+}
+
+/* Derive an array type for an OpenMP array-shaping operator given EXPR, which
+   is an expression that might have array refs or array sections postfixed
+   (e.g. "ptr[0:3:2][3:4]"), and OMP_SHAPE_DIMS, a vector of dimensions.  */
+
+tree
+cp_omp_create_arrayshape_type (location_t loc, tree expr,
+			       vec<cp_expr> *omp_shape_dims)
+{
+  tree type, strip_sections = expr;
+
+  while (TREE_CODE (strip_sections) == OMP_ARRAY_SECTION
+	 || TREE_CODE (strip_sections) == ARRAY_REF)
+    strip_sections = TREE_OPERAND (strip_sections, 0);
+
+  /* Determine the element type, either directly or by using
+     "decltype" of an expression representing an element to
+     figure it out later during template instantiation.  */
+  if (type_dependent_expression_p (expr))
+    {
+      type = cxx_make_type (DECLTYPE_TYPE);
+
+      DECLTYPE_TYPE_EXPR (type)
+	= build_min_nt_loc (loc, INDIRECT_REF, strip_sections);
+      DECLTYPE_FOR_OMP_ARRAYSHAPE_CAST (type) = true;
+      SET_TYPE_STRUCTURAL_EQUALITY (type);
+    }
+  else
+    {
+      type = TREE_TYPE (strip_sections);
+
+      if (TREE_CODE (type) == REFERENCE_TYPE)
+	type = TREE_TYPE (type);
+
+      if (TREE_CODE (type) != POINTER_TYPE)
+	{
+	  error ("OpenMP array shaping operator with non-pointer argument");
+	  return error_mark_node;
+	}
+
+      type = TREE_TYPE (type);
+    }
+
+  int i;
+  cp_expr dim;
+  FOR_EACH_VEC_ELT_REVERSE (*omp_shape_dims, i, dim)
+    {
+      if (!type_dependent_expression_p (dim))
+	dim = fold_convert (sizetype, dim);
+      type = create_anon_array_type (loc, type, dim);
+    }
+
+  return type;
+}
+
 /* Returns the smallest location that is not UNKNOWN_LOCATION.  */
 
 static location_t
diff --git a/gcc/cp/decl2.cc b/gcc/cp/decl2.cc
index aec54702f06a..20693798dc58 100644
--- a/gcc/cp/decl2.cc
+++ b/gcc/cp/decl2.cc
@@ -617,43 +617,49 @@ grok_array_decl (location_t loc, tree array_expr, tree index_exp,
 
 tree
 grok_omp_array_section (location_t loc, tree array_expr, tree index,
-			tree length)
+			tree length, tree stride)
 {
   tree orig_array_expr = array_expr;
   tree orig_index = index;
   tree orig_length = length;
+  tree orig_stride = stride;
 
   if (error_operand_p (array_expr)
       || error_operand_p (index)
-      || error_operand_p (length))
+      || error_operand_p (length)
+      || error_operand_p (stride))
     return error_mark_node;
 
   if (processing_template_decl)
     {
       if (type_dependent_expression_p (array_expr)
 	  || type_dependent_expression_p (index)
-	  || type_dependent_expression_p (length))
+	  || type_dependent_expression_p (length)
+	  || type_dependent_expression_p (stride))
 	return build_min_nt_loc (loc, OMP_ARRAY_SECTION, array_expr, index,
-				 length);
+				 length, stride);
       array_expr = build_non_dependent_expr (array_expr);
       if (index)
 	index = build_non_dependent_expr (index);
       if (length)
 	length = build_non_dependent_expr (length);
+      if (stride)
+	stride = build_non_dependent_expr (stride);
     }
 
   index = fold_non_dependent_expr (index);
   length = fold_non_dependent_expr (length);
+  stride = fold_non_dependent_expr (stride);
 
   /* NOTE: We can pass through invalidly-typed index/length fields
      here (e.g. if the user tries to use a floating-point index/length).
      This is diagnosed later in semantics.cc:handle_omp_array_sections_1.  */
 
-  tree expr = build_omp_array_section (loc, array_expr, index, length);
+  tree expr = build_omp_array_section (loc, array_expr, index, length, stride);
 
   if (processing_template_decl)
     expr = build_min_non_dep (OMP_ARRAY_SECTION, expr, orig_array_expr,
-			      orig_index, orig_length);
+			      orig_index, orig_length, orig_stride);
   return expr;
 }
 
@@ -2635,6 +2641,7 @@ min_vis_expr_r (tree *tp, int */*walk_subtrees*/, void *data)
     case REINTERPRET_CAST_EXPR:
     case CONST_CAST_EXPR:
     case DYNAMIC_CAST_EXPR:
+    case OMP_ARRAYSHAPE_CAST_EXPR:
     case NEW_EXPR:
     case CONSTRUCTOR:
     case LAMBDA_EXPR:
diff --git a/gcc/cp/error.cc b/gcc/cp/error.cc
index 68692a0b80f3..b5af1d5bcf85 100644
--- a/gcc/cp/error.cc
+++ b/gcc/cp/error.cc
@@ -2530,6 +2530,11 @@ dump_expr (cxx_pretty_printer *pp, tree t, int flags)
       dump_expr (pp, TREE_OPERAND (t, 1), flags);
       pp_colon (pp);
       dump_expr (pp, TREE_OPERAND (t, 2), flags);
+      if (TREE_OPERAND (t, 3))
+	{
+	  pp_colon (pp);
+	  dump_expr (pp, TREE_OPERAND (t, 3), flags);
+	}
       pp_cxx_right_bracket (pp);
       break;
 
diff --git a/gcc/cp/mangle.cc b/gcc/cp/mangle.cc
index bef0fda6d229..458de0903a7d 100644
--- a/gcc/cp/mangle.cc
+++ b/gcc/cp/mangle.cc
@@ -3617,6 +3617,7 @@ write_expression (tree expr)
 	case REINTERPRET_CAST_EXPR:
 	case STATIC_CAST_EXPR:
 	case CONST_CAST_EXPR:
+	case OMP_ARRAYSHAPE_CAST_EXPR:
 	  write_type (TREE_TYPE (expr));
 	  write_expression (TREE_OPERAND (expr, 0));
 	  break;
diff --git a/gcc/cp/operators.def b/gcc/cp/operators.def
index e9481752f930..3aca8e087623 100644
--- a/gcc/cp/operators.def
+++ b/gcc/cp/operators.def
@@ -134,6 +134,7 @@ DEF_OPERATOR (NULL, DYNAMIC_CAST_EXPR, "dc", OVL_OP_FLAG_UNARY)
 DEF_OPERATOR (NULL, REINTERPRET_CAST_EXPR, "rc", OVL_OP_FLAG_UNARY)
 DEF_OPERATOR (NULL, CONST_CAST_EXPR, "cc", OVL_OP_FLAG_UNARY)
 DEF_OPERATOR (NULL, STATIC_CAST_EXPR, "sc", OVL_OP_FLAG_UNARY)
+DEF_OPERATOR (NULL, OMP_ARRAYSHAPE_CAST_EXPR, "oc", OVL_OP_FLAG_UNARY)
 DEF_OPERATOR (NULL, SCOPE_REF, "sr", OVL_OP_FLAG_NONE)
 DEF_OPERATOR (NULL, EXPR_PACK_EXPANSION, "sp", OVL_OP_FLAG_NONE)
 DEF_OPERATOR (NULL, UNARY_LEFT_FOLD_EXPR, "fl", OVL_OP_FLAG_NONE)
diff --git a/gcc/cp/parser.cc b/gcc/cp/parser.cc
index d2de6d480f1f..b961dfec3d32 100644
--- a/gcc/cp/parser.cc
+++ b/gcc/cp/parser.cc
@@ -4348,6 +4348,12 @@ cp_parser_new (cp_lexer *lexer)
   /* Disallow OpenMP array sections in expressions.  */
   parser->omp_array_section_p = false;
 
+  /* Disallow OpenMP array-shaping operator in expressions.  */
+  parser->omp_array_shaping_op_p = false;
+
+  /* We don't have an OpenMP array shape here.  */
+  parser->omp_has_array_shape_p = false;
+
   /* Not declaring an implicit function template.  */
   parser->auto_is_implicit_function_template_parm_p = false;
   parser->fully_implicit_function_template_p = false;
@@ -5333,6 +5339,7 @@ cp_parser_statement_expr (cp_parser *parser)
 {
   cp_token_position start = cp_parser_start_tentative_firewall (parser);
   auto oas = make_temp_override (parser->omp_array_section_p, false);
+  auto aso = make_temp_override (parser->omp_array_shaping_op_p, false);
 
   /* Consume the '('.  */
   location_t start_loc = cp_lexer_peek_token (parser->lexer)->location;
@@ -8243,7 +8250,7 @@ cp_parser_postfix_open_square_expression (cp_parser *parser,
       && cp_lexer_next_token_is (parser->lexer, CPP_COLON))
     {
       cp_lexer_consume_token (parser->lexer);
-      tree length = NULL_TREE;
+      tree length = NULL_TREE, stride = NULL_TREE;
       if (cp_lexer_next_token_is_not (parser->lexer, CPP_CLOSE_SQUARE))
 	{
 	  if (cxx_dialect >= cxx23)
@@ -8276,9 +8283,23 @@ cp_parser_postfix_open_square_expression (cp_parser *parser,
 				      /*warn_comma_p=*/warn_comma_subscript);
 	}
 
+      if (cp_lexer_next_token_is (parser->lexer, CPP_COLON))
+	{
+	  cp_lexer_consume_token (parser->lexer);
+	  /* We could check for C++-23 multidimensional/comma-separated
+	     subscripts here, or not bother.  */
+	  if (cp_lexer_next_token_is_not (parser->lexer, CPP_CLOSE_SQUARE))
+	    stride
+	      = cp_parser_expression (parser, NULL, /*cast_p=*/false,
+				      /*decltype_p=*/false,
+				      /*warn_comma_p=*/warn_comma_subscript);
+	}
+
       parser->colon_corrects_to_scope_p = saved_colon_corrects_to_scope_p;
 
-      if (index == error_mark_node || length == error_mark_node)
+      if (index == error_mark_node
+	  || length == error_mark_node
+	  || stride == error_mark_node)
 	{
 	  cp_parser_skip_to_closing_square_bracket (parser);
 	  return error_mark_node;
@@ -8287,7 +8308,7 @@ cp_parser_postfix_open_square_expression (cp_parser *parser,
 	cp_parser_require (parser, CPP_CLOSE_SQUARE, RT_CLOSE_SQUARE);
 
       return grok_omp_array_section (input_location, postfix_expression, index,
-				     length);
+				     length, stride);
     }
 
   parser->colon_corrects_to_scope_p = saved_colon_corrects_to_scope_p;
@@ -8295,11 +8316,23 @@ cp_parser_postfix_open_square_expression (cp_parser *parser,
   /* Look for the closing `]'.  */
   cp_parser_require (parser, CPP_CLOSE_SQUARE, RT_CLOSE_SQUARE);
 
-  /* Build the ARRAY_REF.  */
-  postfix_expression = grok_array_decl (loc, postfix_expression,
-					index, &expression_list,
-					tf_warning_or_error
-					| (decltype_p ? tf_decltype : 0));
+  if (parser->omp_has_array_shape_p
+      && (expression_list.get () == NULL
+	  || vec_safe_length (expression_list) == 1))
+    /* If we have an array-shaping operator, we may not be able to represent
+       a well-formed ARRAY_REF here, because we are coercing the type of the
+       innermost array base and the original type may not be compatible.  Use
+       the OMP_ARRAY_SECTION code instead.  We also want to explicitly avoid
+       creating INDIRECT_REFs for pointer bases, because that can lead to
+       parsing ambiguities (see cp_parser_omp_var_list_no_open).  */
+    return grok_omp_array_section (loc, postfix_expression, index,
+				   size_one_node, NULL_TREE);
+  else
+    /* Build the ARRAY_REF.  */
+    postfix_expression = grok_array_decl (loc, postfix_expression,
+					  index, &expression_list,
+					  tf_warning_or_error
+					  | (decltype_p ? tf_decltype : 0));
 
   /* When not doing offsetof, array references are not permitted in
      constant-expressions.  */
@@ -8621,6 +8654,7 @@ cp_parser_parenthesized_expression_list (cp_parser* parser,
   vec<tree, va_gc> *expression_list;
   bool saved_greater_than_is_operator_p;
   bool saved_omp_array_section_p;
+  bool saved_omp_array_shaping_op_p;
 
   /* Assume all the expressions will be constant.  */
   if (non_constant_p)
@@ -8639,7 +8673,9 @@ cp_parser_parenthesized_expression_list (cp_parser* parser,
   parser->greater_than_is_operator_p = true;
 
   saved_omp_array_section_p = parser->omp_array_section_p;
+  saved_omp_array_shaping_op_p = parser->omp_array_shaping_op_p;
   parser->omp_array_section_p = false;
+  parser->omp_array_shaping_op_p = false;
 
   cp_expr expr (NULL_TREE);
 
@@ -8706,6 +8742,7 @@ cp_parser_parenthesized_expression_list (cp_parser* parser,
 	  parser->greater_than_is_operator_p
 	    = saved_greater_than_is_operator_p;
 	  parser->omp_array_section_p = saved_omp_array_section_p;
+	  parser->omp_array_shaping_op_p = saved_omp_array_shaping_op_p;
 	  return NULL;
 	}
     }
@@ -8713,6 +8750,7 @@ cp_parser_parenthesized_expression_list (cp_parser* parser,
   parser->greater_than_is_operator_p
     = saved_greater_than_is_operator_p;
   parser->omp_array_section_p = saved_omp_array_section_p;
+  parser->omp_array_shaping_op_p = saved_omp_array_shaping_op_p;
 
   return expression_list;
 }
@@ -9979,6 +10017,8 @@ cp_parser_cast_expression (cp_parser *parser, bool address_p, bool cast_p,
       cp_expr expr (NULL_TREE);
       int cast_expression = 0;
       const char *saved_message;
+      auto_vec<cp_expr, 4> omp_shape_dims;
+      bool omp_array_shape_p = false;
 
       /* There's no way to know yet whether or not this is a cast.
 	 For example, `(int (3))' is a unary-expression, while `(int)
@@ -10048,6 +10088,28 @@ cp_parser_cast_expression (cp_parser *parser, bool address_p, bool cast_p,
 	 that the call to cp_parser_error_occurred below returns true.  */
       if (!cast_expression)
 	cp_parser_simulate_error (parser);
+      else if (parser->omp_array_shaping_op_p
+	       && cp_lexer_next_token_is (parser->lexer, CPP_OPEN_SQUARE))
+	{
+	  auto oas = make_temp_override (parser->omp_array_section_p, false);
+	  auto aso = make_temp_override (parser->omp_array_shaping_op_p, false);
+
+	  while (cp_lexer_next_token_is (parser->lexer, CPP_OPEN_SQUARE))
+	    {
+	      cp_lexer_consume_token (parser->lexer);
+	      cp_expr e = cp_parser_expression (parser);
+	      if (e.get_value () == error_mark_node)
+		break;
+	      omp_shape_dims.safe_push (e);
+	      if (!cp_parser_require (parser, CPP_CLOSE_SQUARE,
+				      RT_CLOSE_SQUARE))
+		break;
+	    }
+	  cp_token *close_paren = parens.require_close (parser);
+	  if (close_paren)
+	    close_paren_loc = close_paren->location;
+	  omp_array_shape_p = true;
+	}
       else
 	{
 	  bool saved_in_type_id_in_expr_p = parser->in_type_id_in_expr_p;
@@ -10069,6 +10131,10 @@ cp_parser_cast_expression (cp_parser *parser, bool address_p, bool cast_p,
 	 function returning T.  */
       if (!cp_parser_error_occurred (parser))
 	{
+	  auto aso = make_temp_override (parser->omp_array_shaping_op_p, false);
+	  auto as = make_temp_override (parser->omp_has_array_shape_p,
+					omp_array_shape_p);
+
 	  /* Only commit if the cast-expression doesn't start with
 	     '++', '--', or '[' in C++11.  */
 	  if (cast_expression > 0)
@@ -10082,6 +10148,24 @@ cp_parser_cast_expression (cp_parser *parser, bool address_p, bool cast_p,
 
 	  if (cp_parser_parse_definitely (parser))
 	    {
+	      if (omp_array_shape_p)
+		{
+		  location_t cast_loc = make_location (open_paren_loc,
+						       open_paren_loc,
+						       expr.get_finish ());
+
+		  type = cp_omp_create_arrayshape_type (cast_loc, expr,
+							&omp_shape_dims);
+
+		  /* Things rapidly get worse below if we carry on from here
+		     with an erroneous type...  */
+		  if (error_operand_p (type))
+		    return error_mark_node;
+
+		  return cp_build_omp_arrayshape_cast (cast_loc, type, expr,
+						       tf_warning_or_error);
+		}
+
 	      /* Warn about old-style casts, if so requested.  */
 	      if (warn_old_style_cast
 		  && !in_system_header_at (input_location)
@@ -11237,6 +11321,7 @@ cp_parser_lambda_expression (cp_parser* parser)
     bool auto_is_implicit_function_template_parm_p
         = parser->auto_is_implicit_function_template_parm_p;
     bool saved_omp_array_section_p = parser->omp_array_section_p;
+    bool saved_omp_array_shaping_op_p = parser->omp_array_shaping_op_p;
 
     parser->num_template_parameter_lists = 0;
     parser->in_statement = 0;
@@ -11246,6 +11331,7 @@ cp_parser_lambda_expression (cp_parser* parser)
     parser->implicit_template_scope = 0;
     parser->auto_is_implicit_function_template_parm_p = false;
     parser->omp_array_section_p = false;
+    parser->omp_array_shaping_op_p = false;
 
     /* The body of a lambda in a discarded statement is not discarded.  */
     bool discarded = in_discarded_stmt;
@@ -11297,6 +11383,7 @@ cp_parser_lambda_expression (cp_parser* parser)
     parser->auto_is_implicit_function_template_parm_p
 	= auto_is_implicit_function_template_parm_p;
     parser->omp_array_section_p = saved_omp_array_section_p;
+    parser->omp_array_shaping_op_p = saved_omp_array_shaping_op_p;
   }
 
   /* This field is only used during parsing of the lambda.  */
@@ -25630,6 +25717,7 @@ cp_parser_braced_list (cp_parser *parser, bool *non_constant_p /*=nullptr*/)
   tree initializer;
   location_t start_loc = cp_lexer_peek_token (parser->lexer)->location;
   auto oas = make_temp_override (parser->omp_array_section_p, false);
+  auto aso = make_temp_override (parser->omp_array_shaping_op_p, false);
 
   /* Consume the `{' token.  */
   matching_braces braces;
@@ -37529,11 +37617,11 @@ check_no_duplicate_clause (tree clauses, enum omp_clause_code code,
 
 struct omp_dim
 {
-  tree low_bound, length;
+  tree low_bound, length, stride;
   location_t loc;
   bool no_colon;
-  omp_dim (tree lb, tree len, location_t lo, bool nc)
-    : low_bound (lb), length (len), loc (lo), no_colon (nc) {}
+  omp_dim (tree lb, tree len, tree str, location_t lo, bool nc)
+    : low_bound (lb), length (len), stride (str), loc (lo), no_colon (nc) {}
 };
 
 static tree
@@ -37565,10 +37653,22 @@ cp_parser_omp_var_list_no_open (cp_parser *parser, enum omp_clause_code kind,
 		   || kind == OMP_CLAUSE_FROM))
 	{
 	  auto s = make_temp_override (parser->omp_array_section_p, true);
+	  auto o = make_temp_override (parser->omp_array_shaping_op_p,
+				       (kind == OMP_CLAUSE_TO
+					|| kind == OMP_CLAUSE_FROM));
+	  tree reshaped_to = NULL_TREE;
 	  token = cp_lexer_peek_token (parser->lexer);
 	  location_t loc = token->location;
 	  decl = cp_parser_assignment_expression (parser);
 
+	  if ((TREE_CODE (decl) == VIEW_CONVERT_EXPR
+	       && TREE_CODE (TREE_TYPE (decl)) == ARRAY_TYPE)
+	      || TREE_CODE (decl) == OMP_ARRAYSHAPE_CAST_EXPR)
+	    {
+	      reshaped_to = TREE_TYPE (decl);
+	      decl = TREE_OPERAND (decl, 0);
+	    }
+
 	  /* This code rewrites a parsed expression containing various tree
 	     codes used to represent array accesses into a more uniform nest of
 	     OMP_ARRAY_SECTION nodes before it is processed by
@@ -37579,49 +37679,159 @@ cp_parser_omp_var_list_no_open (cp_parser *parser, enum omp_clause_code kind,
 	  dims.truncate (0);
 	  if (TREE_CODE (decl) == OMP_ARRAY_SECTION)
 	    {
+	      size_t sections = 0;
+	      tree orig_decl = decl;
+	      bool update_p = (kind == OMP_CLAUSE_TO
+			       || kind == OMP_CLAUSE_FROM);
+	      bool maybe_ptr_based_noncontig_update = false;
+
+	      while (update_p
+		     && !reshaped_to
+		     && (TREE_CODE (decl) == OMP_ARRAY_SECTION
+			 || TREE_CODE (decl) == ARRAY_REF
+			 || TREE_CODE (decl) == COMPOUND_EXPR))
+		{
+		  if (TREE_CODE (decl) == COMPOUND_EXPR)
+		    decl = TREE_OPERAND (decl, 1);
+		  else
+		    {
+		      if (TREE_CODE (decl) == OMP_ARRAY_SECTION)
+			maybe_ptr_based_noncontig_update = true;
+		      decl = TREE_OPERAND (decl, 0);
+		      sections++;
+		    }
+		}
+
+	      decl = orig_decl;
+
 	      while (TREE_CODE (decl) == OMP_ARRAY_SECTION)
 		{
 		  tree low_bound = TREE_OPERAND (decl, 1);
 		  tree length = TREE_OPERAND (decl, 2);
-		  dims.safe_push (omp_dim (low_bound, length, loc, false));
+		  tree stride = TREE_OPERAND (decl, 3);
+		  dims.safe_push (omp_dim (low_bound, length, stride, loc,
+					   false));
 		  decl = TREE_OPERAND (decl, 0);
+		  if (sections > 0)
+		    sections--;
 		}
 
+	      /* The handling of INDIRECT_REF here in the presence of
+		 array-shaping operations is a little tricky.  We need to
+		 avoid treating a pointer dereference as a unit-sized array
+		 section when we have an array shaping operation, because we
+		 don't want an indirection to consume one of the user's
+		 requested array dimensions.  E.g. if we have a
+		 double-indirect pointer like:
+
+		   int **foopp;
+		   #pragma omp target update from(([N][N]) (*foopp)[0:X][0:Y])
+
+		 We don't want to interpret this as:
+
+		   foopp[0:1][0:X][0:Y]
+
+		 else the array shape [N][N] won't match.  Also we can't match
+		 the array sections right-to-left instead, else this:
+
+		   #pragma omp target update from(([N][N]) (*foopp)[0:X])
+
+		 would not copy the dimensions:
+
+		   (*foopp)[0:X][0:N]
+
+		 as required.  So, avoid descending through INDIRECT_REFs if
+		 we have an array-shaping op.
+
+		 If we *don't* have an array-shaping op, but we have a
+		 multiply-indirected pointer and an array section like this:
+
+		   int ***fooppp;
+		   #pragma omp target update from((**fooppp)[0:X:S]
+
+		 also avoid descending through more indirections than we have
+		 array sections, since the noncontiguous update processing code
+		 won't understand them (and doesn't need to traverse them
+		 anyway).  */
+
 	      while (TREE_CODE (decl) == ARRAY_REF
-		     || TREE_CODE (decl) == INDIRECT_REF
+		     || (TREE_CODE (decl) == INDIRECT_REF
+			 && !reshaped_to)
 		     || TREE_CODE (decl) == COMPOUND_EXPR)
 		{
 		  if (REFERENCE_REF_P (decl))
 		    break;
 
+		  if (maybe_ptr_based_noncontig_update && sections == 0)
+		    break;
+
 		  if (TREE_CODE (decl) == COMPOUND_EXPR)
 		    {
 		      decl = TREE_OPERAND (decl, 1);
 		      STRIP_NOPS (decl);
+		      continue;
 		    }
-		  else if (TREE_CODE (decl) == INDIRECT_REF)
+		  else if (TREE_CODE (decl) == INDIRECT_REF
+			   && !reshaped_to)
 		    {
 		      dims.safe_push (omp_dim (integer_zero_node,
-					       integer_one_node, loc, true));
+					       integer_one_node, NULL_TREE, loc,
+					       true));
 		      decl = TREE_OPERAND (decl, 0);
 		    }
 		  else  /* ARRAY_REF. */
 		    {
 		      tree index = TREE_OPERAND (decl, 1);
-		      dims.safe_push (omp_dim (index, integer_one_node, loc,
-					       true));
+		      dims.safe_push (omp_dim (index, integer_one_node,
+					       NULL_TREE, loc, true));
 		      decl = TREE_OPERAND (decl, 0);
+		      if (sections > 0)
+			sections--;
 		    }
 		}
 
+	      if (reshaped_to)
+		{
+		  unsigned reshaped_dims = 0;
+
+		  for (tree t = reshaped_to;
+		       TREE_CODE (t) == ARRAY_TYPE;
+		       t = TREE_TYPE (t))
+		    reshaped_dims++;
+
+		  if (dims.length () > reshaped_dims)
+		    {
+		      error_at (loc, "too many array section specifiers "
+				"for %qT", reshaped_to);
+		      decl = error_mark_node;
+		    }
+		  else
+		    {
+		      /* We have a pointer DECL whose target should be
+			 interpreted as an array with particular dimensions,
+			 not "the pointer itself".  So, add an indirection
+			 here.  */
+		      if (type_dependent_expression_p (decl))
+			decl = build_min_nt_loc (loc, INDIRECT_REF, decl);
+		      else
+			{
+			  /* We're interested in the reference target.  */
+			  decl = convert_from_reference (decl);
+			  decl = cp_build_fold_indirect_ref (decl);
+			}
+		      decl
+			= cp_build_omp_arrayshape_cast (loc, reshaped_to, decl,
+							tf_warning_or_error);
+		    }
+		}
 	      /* Bare references have their own special handling, so remove
 		 the explicit dereference added by convert_from_reference.  */
-	      if (REFERENCE_REF_P (decl))
+	      else if (REFERENCE_REF_P (decl))
 		decl = TREE_OPERAND (decl, 0);
 
 	      for (int i = dims.length () - 1; i >= 0; i--)
 		decl = grok_omp_array_section (loc, decl, dims[i].low_bound,
-					       dims[i].length);
+					       dims[i].length, dims[i].stride);
 	    }
 	  else if (TREE_CODE (decl) == INDIRECT_REF)
 	    {
@@ -37638,7 +37848,7 @@ cp_parser_omp_var_list_no_open (cp_parser *parser, enum omp_clause_code kind,
 		   "foo[0:1]".  */
 	      if (!ref_p)
 		decl = grok_omp_array_section (loc, decl, integer_zero_node,
-					       integer_one_node);
+					       integer_one_node, NULL_TREE);
 	    }
 	  else if (TREE_CODE (decl) == ARRAY_REF)
 	    {
@@ -37647,7 +37857,16 @@ cp_parser_omp_var_list_no_open (cp_parser *parser, enum omp_clause_code kind,
 	      decl = TREE_OPERAND (decl, 0);
 	      STRIP_NOPS (decl);
 
-	      decl = grok_omp_array_section (loc, decl, idx, integer_one_node);
+	      decl = grok_omp_array_section (loc, decl, idx, integer_one_node,
+					     NULL_TREE);
+	    }
+	  else if (reshaped_to)
+	    {
+	      /* We're copying the whole of a reshaped array, originally a
+		 base pointer.  Rewrite as an array section.  */
+	      tree elems = array_type_nelts_total (reshaped_to);
+	      decl = grok_omp_array_section (loc, decl, size_zero_node, elems,
+					     NULL_TREE);
 	    }
 	  else if (TREE_CODE (decl) == NON_LVALUE_EXPR
 		   || CONVERT_EXPR_P (decl))
@@ -37811,7 +38030,8 @@ cp_parser_omp_var_list_no_open (cp_parser *parser, enum omp_clause_code kind,
 		      goto skip_comma;
 		    }
 
-		  dims.safe_push (omp_dim (low_bound, length, loc, no_colon));
+		  dims.safe_push (omp_dim (low_bound, length, NULL_TREE, loc,
+					   no_colon));
 		}
 
 	      if ((kind == OMP_CLAUSE_MAP
@@ -37833,7 +38053,8 @@ cp_parser_omp_var_list_no_open (cp_parser *parser, enum omp_clause_code kind,
 		for (unsigned i = 0; i < dims.length (); i++)
 		  decl = build_omp_array_section (input_location, decl,
 						  dims[i].low_bound,
-						  dims[i].length);
+						  dims[i].length,
+						  dims[i].stride);
 	      break;
 	    default:
 	      break;
@@ -37846,6 +38067,8 @@ cp_parser_omp_var_list_no_open (cp_parser *parser, enum omp_clause_code kind,
 		  && cp_parser_simulate_error (parser))
 		{
 		depend_lvalue:
+		  auto o = make_temp_override (parser->omp_array_shaping_op_p,
+					       true);
 		  cp_parser_abort_tentative_parse (parser);
 		  decl = cp_parser_assignment_expression (parser, NULL,
 							  false, false);
@@ -45898,8 +46121,38 @@ cp_parser_omp_target_update (cp_parser *parser, cp_token *pragma_tok,
   tree clauses
     = cp_parser_omp_all_clauses (parser, OMP_TARGET_UPDATE_CLAUSE_MASK,
 				 "#pragma omp target update", pragma_tok);
-  if (omp_find_clause (clauses, OMP_CLAUSE_TO) == NULL_TREE
-      && omp_find_clause (clauses, OMP_CLAUSE_FROM) == NULL_TREE)
+  bool to_clause = false, from_clause = false;
+  for (tree c = clauses;
+       c && !to_clause && !from_clause;
+       c = OMP_CLAUSE_CHAIN (c))
+    {
+      switch (OMP_CLAUSE_CODE (c))
+	{
+	case OMP_CLAUSE_TO:
+	  to_clause = true;
+	  break;
+	case OMP_CLAUSE_FROM:
+	  from_clause = true;
+	  break;
+	case OMP_CLAUSE_MAP:
+	  switch (OMP_CLAUSE_MAP_KIND (c))
+	    {
+	    case GOMP_MAP_TO_GRID:
+	      to_clause = true;
+	      break;
+	    case GOMP_MAP_FROM_GRID:
+	      from_clause = true;
+	      break;
+	    default:
+	      ;
+	    }
+	  break;
+	default:
+	  ;
+	}
+    }
+
+  if (!to_clause && !from_clause)
     {
       error_at (pragma_tok->location,
 		"%<#pragma omp target update%> must contain at least one "
diff --git a/gcc/cp/parser.h b/gcc/cp/parser.h
index 574a83f38340..9c5633c90834 100644
--- a/gcc/cp/parser.h
+++ b/gcc/cp/parser.h
@@ -410,6 +410,13 @@ struct GTY(()) cp_parser {
   /* TRUE if an OpenMP array section is allowed.  */
   bool omp_array_section_p;
 
+  /* TRUE if an OpenMP array-shaping operator is allowed.  */
+  bool omp_array_shaping_op_p;
+
+  /* TRUE if we are parsing an expression with an OpenMP array-shaping
+     operator.  */
+  bool omp_has_array_shape_p;
+
   /* Tracks the function's template parameter list when declaring a function
      using generic type parameters.  This is either a new chain in the case of a
      fully implicit function template or an extension of the function's existing
diff --git a/gcc/cp/pt.cc b/gcc/cp/pt.cc
index a1dadf4dd461..b1ca7a47c81b 100644
--- a/gcc/cp/pt.cc
+++ b/gcc/cp/pt.cc
@@ -16720,6 +16720,10 @@ tsubst (tree t, tree args, tsubst_flags_t complain, tree in_decl)
 		 member access.  */
 	      id = false;
 	    type = finish_decltype_type (type, id, complain);
+
+	    if (DECLTYPE_FOR_OMP_ARRAYSHAPE_CAST (t)
+		&& TYPE_REF_P (type))
+	      type = TREE_TYPE (type);
 	  }
 	return cp_build_qualified_type (type,
 					cp_type_quals (t)
@@ -17555,6 +17559,7 @@ tsubst_copy (tree t, tree args, tsubst_flags_t complain, tree in_decl)
     case STATIC_CAST_EXPR:
     case DYNAMIC_CAST_EXPR:
     case IMPLICIT_CONV_EXPR:
+    case OMP_ARRAYSHAPE_CAST_EXPR:
     CASE_CONVERT:
       {
 	tsubst_flags_t tcomplain = complain;
@@ -17780,12 +17785,14 @@ tsubst_copy (tree t, tree args, tsubst_flags_t complain, tree in_decl)
     case OMP_ARRAY_SECTION:
       {
 	tree op0 = tsubst_copy (TREE_OPERAND (t, 0), args, complain, in_decl);
-	tree op1 = NULL_TREE, op2 = NULL_TREE;
+	tree op1 = NULL_TREE, op2 = NULL_TREE, op3 = NULL_TREE;
 	if (TREE_OPERAND (t, 1))
 	  op1 = tsubst_copy (TREE_OPERAND (t, 1), args, complain, in_decl);
 	if (TREE_OPERAND (t, 2))
 	  op2 = tsubst_copy (TREE_OPERAND (t, 2), args, complain, in_decl);
-	return build_nt (OMP_ARRAY_SECTION, op0, op1, op2);
+	if (TREE_OPERAND (t, 3))
+	  op3 = tsubst_copy (TREE_OPERAND (t, 3), args, complain, in_decl);
+	return build_nt (OMP_ARRAY_SECTION, op0, op1, op2, op3);
       }
 
     case CALL_EXPR:
@@ -18062,14 +18069,17 @@ tsubst_omp_clause_decl (tree decl, tree args, tsubst_flags_t complain,
 	= tsubst_expr (TREE_OPERAND (decl, 1), args, complain, in_decl);
       tree length = tsubst_expr (TREE_OPERAND (decl, 2), args, complain,
 				 in_decl);
+      tree stride = tsubst_expr (TREE_OPERAND (decl, 3), args, complain,
+				 in_decl);
       tree base = tsubst_omp_clause_decl (TREE_OPERAND (decl, 0), args,
 					   complain, in_decl, NULL);
       if (TREE_OPERAND (decl, 0) == base
 	  && TREE_OPERAND (decl, 1) == low_bound
-	  && TREE_OPERAND (decl, 2) == length)
+	  && TREE_OPERAND (decl, 2) == length
+	  && TREE_OPERAND (decl, 3) == stride)
 	return decl;
-      tree ret = build3 (OMP_ARRAY_SECTION, TREE_TYPE (base), base, low_bound,
-			 length);
+      tree ret = build4 (OMP_ARRAY_SECTION, TREE_TYPE (base), base, low_bound,
+			 length, stride);
       return ret;
     }
   tree ret = tsubst_expr (decl, args, complain, in_decl);
@@ -20703,6 +20713,14 @@ tsubst_copy_and_build (tree t,
 	RETURN (cp_build_bit_cast (EXPR_LOCATION (t), type, op0, complain));
       }
 
+    case OMP_ARRAYSHAPE_CAST_EXPR:
+      {
+	tree type = tsubst (TREE_TYPE (t), args, complain, in_decl);
+	tree op0 = RECUR (TREE_OPERAND (t, 0));
+	RETURN (cp_build_omp_arrayshape_cast (EXPR_LOCATION (t), type, op0,
+					      complain));
+      }
+
     case POSTDECREMENT_EXPR:
     case POSTINCREMENT_EXPR:
       op1 = tsubst_non_call_postfix_expression (TREE_OPERAND (t, 0),
@@ -20869,7 +20887,7 @@ tsubst_copy_and_build (tree t,
     case OMP_ARRAY_SECTION:
       {
 	tree op0 = RECUR (TREE_OPERAND (t, 0));
-	tree op1 = NULL_TREE, op2 = NULL_TREE;
+	tree op1 = NULL_TREE, op2 = NULL_TREE, op3 = NULL_TREE;
 	if (op0 == error_mark_node)
 	  RETURN (error_mark_node);
 	if (TREE_OPERAND (t, 1))
@@ -20884,7 +20902,14 @@ tsubst_copy_and_build (tree t,
 	    if (op2 == error_mark_node)
 	      RETURN (error_mark_node);
 	  }
-	RETURN (build_omp_array_section (EXPR_LOCATION (t), op0, op1, op2));
+	if (TREE_OPERAND (t, 3))
+	  {
+	    op3 = RECUR (TREE_OPERAND (t, 3));
+	    if (op3 == error_mark_node)
+	      RETURN (error_mark_node);
+	  }
+	RETURN (build_omp_array_section (EXPR_LOCATION (t), op0, op1, op2,
+					 op3));
       }
 
     case SIZEOF_EXPR:
diff --git a/gcc/cp/semantics.cc b/gcc/cp/semantics.cc
index aa1fa15548cf..53e97ca716b8 100644
--- a/gcc/cp/semantics.cc
+++ b/gcc/cp/semantics.cc
@@ -5271,9 +5271,9 @@ public:
 static tree
 handle_omp_array_sections_1 (tree c, tree t, vec<tree> &types,
 			     bool &maybe_zero_len, unsigned int &first_non_one,
-			     enum c_omp_region_type ort)
+			     enum c_omp_region_type ort, int *discontiguous)
 {
-  tree ret, low_bound, length, type;
+  tree ret, low_bound, length, stride, type;
   bool openacc = (ort & C_ORT_ACC) != 0;
   if (TREE_CODE (t) != OMP_ARRAY_SECTION)
     {
@@ -5335,18 +5335,26 @@ handle_omp_array_sections_1 (tree c, tree t, vec<tree> &types,
       && TREE_CODE (TREE_OPERAND (t, 0)) == FIELD_DECL)
     TREE_OPERAND (t, 0) = omp_privatize_field (TREE_OPERAND (t, 0), false);
   ret = handle_omp_array_sections_1 (c, TREE_OPERAND (t, 0), types,
-				     maybe_zero_len, first_non_one, ort);
+				     maybe_zero_len, first_non_one, ort,
+				     discontiguous);
   if (ret == error_mark_node || ret == NULL_TREE)
     return ret;
 
-  type = TREE_TYPE (ret);
+  if (TREE_CODE (ret) == OMP_ARRAY_SECTION)
+    type = TREE_TYPE (TREE_TYPE (TREE_OPERAND (ret, 0)));
+  else
+    type = TREE_TYPE (ret);
   low_bound = TREE_OPERAND (t, 1);
   length = TREE_OPERAND (t, 2);
+  stride = TREE_OPERAND (t, 3);
   if ((low_bound && type_dependent_expression_p (low_bound))
-      || (length && type_dependent_expression_p (length)))
+      || (length && type_dependent_expression_p (length))
+      || (stride && type_dependent_expression_p (stride)))
     return NULL_TREE;
 
-  if (low_bound == error_mark_node || length == error_mark_node)
+  if (low_bound == error_mark_node
+      || length == error_mark_node
+      || stride == error_mark_node)
     return error_mark_node;
 
   if (low_bound && !INTEGRAL_TYPE_P (TREE_TYPE (low_bound)))
@@ -5363,15 +5371,26 @@ handle_omp_array_sections_1 (tree c, tree t, vec<tree> &types,
 		length);
       return error_mark_node;
     }
+  if (stride && !INTEGRAL_TYPE_P (TREE_TYPE (stride)))
+    {
+      error_at (OMP_CLAUSE_LOCATION (c),
+		"stride %qE of array section does not have integral type",
+		stride);
+      return error_mark_node;
+    }
   if (low_bound)
     low_bound = mark_rvalue_use (low_bound);
   if (length)
     length = mark_rvalue_use (length);
+  if (stride)
+    stride = mark_rvalue_use (stride);
   /* We need to reduce to real constant-values for checks below.  */
   if (length)
     length = fold_simple (length);
   if (low_bound)
     low_bound = fold_simple (low_bound);
+  if (stride)
+    stride = fold_simple (stride);
   if (low_bound
       && TREE_CODE (low_bound) == INTEGER_CST
       && TYPE_PRECISION (TREE_TYPE (low_bound))
@@ -5382,9 +5401,15 @@ handle_omp_array_sections_1 (tree c, tree t, vec<tree> &types,
       && TYPE_PRECISION (TREE_TYPE (length))
 	 > TYPE_PRECISION (sizetype))
     length = fold_convert (sizetype, length);
+  if (stride
+      && TREE_CODE (stride) == INTEGER_CST
+      && TYPE_PRECISION (TREE_TYPE (stride))
+	 > TYPE_PRECISION (sizetype))
+    stride = fold_convert (sizetype, stride);
   if (low_bound == NULL_TREE)
     low_bound = integer_zero_node;
-
+  if (stride == NULL_TREE)
+    stride = size_one_node;
   if (OMP_CLAUSE_CODE (c) == OMP_CLAUSE_MAP
       && (OMP_CLAUSE_MAP_KIND (c) == GOMP_MAP_ATTACH
 	  || OMP_CLAUSE_MAP_KIND (c) == GOMP_MAP_DETACH))
@@ -5503,12 +5528,29 @@ handle_omp_array_sections_1 (tree c, tree t, vec<tree> &types,
 	    }
 	  if (length && TREE_CODE (length) == INTEGER_CST)
 	    {
-	      if (tree_int_cst_lt (size, length))
+	      tree slength = length;
+	      if (stride && TREE_CODE (stride) == INTEGER_CST)
 		{
-		  error_at (OMP_CLAUSE_LOCATION (c),
-			    "length %qE above array section size "
-			    "in %qs clause", length,
-			    omp_clause_code_name[OMP_CLAUSE_CODE (c)]);
+		  slength = size_binop (MULT_EXPR,
+					fold_convert (sizetype, length),
+					fold_convert (sizetype, stride));
+		  slength = size_binop (MINUS_EXPR,
+					  slength,
+					  fold_convert (sizetype, stride));
+		  slength = size_binop (PLUS_EXPR, slength, size_one_node);
+		}
+	      if (tree_int_cst_lt (size, slength))
+		{
+		  if (stride)
+		    error_at (OMP_CLAUSE_LOCATION (c),
+			      "length %qE with stride %qE above array "
+			      "section size in %qs clause", length, stride,
+			      omp_clause_code_name[OMP_CLAUSE_CODE (c)]);
+		  else
+		    error_at (OMP_CLAUSE_LOCATION (c),
+			      "length %qE above array section size "
+			      "in %qs clause", length,
+			      omp_clause_code_name[OMP_CLAUSE_CODE (c)]);
 		  return error_mark_node;
 		}
 	      if (TREE_CODE (low_bound) == INTEGER_CST)
@@ -5516,7 +5558,7 @@ handle_omp_array_sections_1 (tree c, tree t, vec<tree> &types,
 		  tree lbpluslen
 		    = size_binop (PLUS_EXPR,
 				  fold_convert (sizetype, low_bound),
-				  fold_convert (sizetype, length));
+				  fold_convert (sizetype, slength));
 		  if (TREE_CODE (lbpluslen) == INTEGER_CST
 		      && tree_int_cst_lt (size, lbpluslen))
 		    {
@@ -5586,12 +5628,20 @@ handle_omp_array_sections_1 (tree c, tree t, vec<tree> &types,
 	       d = TREE_OPERAND (d, 0))
 	    {
 	      tree d_length = TREE_OPERAND (d, 2);
-	      if (d_length == NULL_TREE || !integer_onep (d_length))
+	      tree d_stride = TREE_OPERAND (d, 3);
+	      if (d_length == NULL_TREE
+		  || !integer_onep (d_length)
+		  || (d_stride && !integer_onep (d_stride)))
 		{
-		  error_at (OMP_CLAUSE_LOCATION (c),
-			    "array section is not contiguous in %qs clause",
-			    omp_clause_code_name[OMP_CLAUSE_CODE (c)]);
-		  return error_mark_node;
+		  if (discontiguous && *discontiguous)
+		    *discontiguous = 2;
+		  else
+		    {
+		      error_at (OMP_CLAUSE_LOCATION (c),
+				"array section is not contiguous in %qs clause",
+				omp_clause_code_name[OMP_CLAUSE_CODE (c)]);
+		      return error_mark_node;
+		    }
 		}
 	    }
 	}
@@ -5603,7 +5653,7 @@ handle_omp_array_sections_1 (tree c, tree t, vec<tree> &types,
       return error_mark_node;
     }
   if (OMP_CLAUSE_CODE (c) != OMP_CLAUSE_DEPEND)
-    types.safe_push (TREE_TYPE (ret));
+    types.safe_push (type);
   /* We will need to evaluate lb more than once.  */
   tree lb = cp_save_expr (low_bound);
   if (lb != low_bound)
@@ -5622,15 +5672,45 @@ handle_omp_array_sections_1 (tree c, tree t, vec<tree> &types,
 		      OMP_CLAUSE_CODE (c) == OMP_CLAUSE_REDUCTION
 		      || OMP_CLAUSE_CODE (c) == OMP_CLAUSE_IN_REDUCTION
 		      || OMP_CLAUSE_CODE (c) == OMP_CLAUSE_TASK_REDUCTION);
-  ret = grok_array_decl (OMP_CLAUSE_LOCATION (c), ret, low_bound, NULL,
-			 tf_warning_or_error);
+  /* NOTE: Stride/length are discarded for affinity/depend here.  */
+  if (discontiguous
+      && *discontiguous
+      && OMP_CLAUSE_CODE (c) != OMP_CLAUSE_AFFINITY
+      && OMP_CLAUSE_CODE (c) != OMP_CLAUSE_DEPEND)
+    ret = grok_omp_array_section (OMP_CLAUSE_LOCATION (c), ret, low_bound,
+				  length, stride);
+  else
+    ret = grok_array_decl (OMP_CLAUSE_LOCATION (c), ret, low_bound, NULL,
+			   tf_warning_or_error);
   return ret;
 }
 
-/* Handle array sections for clause C.  */
+/* We built a reference to an array section, but it turns out we only need a
+   set of ARRAY_REFs to the lower bound.  Rewrite the node.  */
+
+static tree
+omp_array_section_low_bound (location_t loc, tree node)
+{
+  if (TREE_CODE (node) == OMP_ARRAY_SECTION)
+    {
+      tree low_bound = TREE_OPERAND (node, 1);
+      tree ret
+	= omp_array_section_low_bound (loc, TREE_OPERAND (node, 0));
+      return grok_array_decl (loc, ret, low_bound, NULL, tf_warning_or_error);
+    }
+
+  return node;
+}
+
+/* Handle array sections for clause C.  On entry *DISCONTIGUOUS is 0 if array
+   section must be contiguous, 1 if it can be discontiguous, and in the latter
+   case it is set to 2 on exit if it is determined to be discontiguous during
+   the function's execution.  PC points to the clause to be processed, and
+   *PNEXT to the last mapping node created, if passed as non-NULL.  */
 
 static bool
-handle_omp_array_sections (tree *pc, tree **pnext, enum c_omp_region_type ort)
+handle_omp_array_sections (tree *pc, tree **pnext, enum c_omp_region_type ort,
+			   int *discontiguous)
 {
   tree c = *pc;
   bool maybe_zero_len = false;
@@ -5645,7 +5725,7 @@ handle_omp_array_sections (tree *pc, tree **pnext, enum c_omp_region_type ort)
     tp = &TREE_VALUE (*tp);
   tree first = handle_omp_array_sections_1 (c, *tp, types,
 					    maybe_zero_len, first_non_one,
-					    ort);
+					    ort, discontiguous);
   if (first == error_mark_node)
     return true;
   if (first == NULL_TREE)
@@ -5686,6 +5766,8 @@ handle_omp_array_sections (tree *pc, tree **pnext, enum c_omp_region_type ort)
       if (processing_template_decl && maybe_zero_len)
 	return false;
 
+      bool higher_discontiguous = false;
+
       for (i = num, t = OMP_CLAUSE_DECL (c); i > 0;
 	   t = TREE_OPERAND (t, 0))
 	{
@@ -5693,6 +5775,7 @@ handle_omp_array_sections (tree *pc, tree **pnext, enum c_omp_region_type ort)
 
 	  tree low_bound = TREE_OPERAND (t, 1);
 	  tree length = TREE_OPERAND (t, 2);
+	  tree stride = TREE_OPERAND (t, 3);
 
 	  i--;
 	  if (low_bound
@@ -5705,12 +5788,57 @@ handle_omp_array_sections (tree *pc, tree **pnext, enum c_omp_region_type ort)
 	      && TYPE_PRECISION (TREE_TYPE (length))
 		 > TYPE_PRECISION (sizetype))
 	    length = fold_convert (sizetype, length);
+	  if (stride
+	      && TREE_CODE (stride) == INTEGER_CST
+	      && TYPE_PRECISION (TREE_TYPE (stride))
+		 > TYPE_PRECISION (sizetype))
+	    stride = fold_convert (sizetype, stride);
 	  if (low_bound == NULL_TREE)
 	    low_bound = integer_zero_node;
+	  if (stride == NULL_TREE)
+	    stride = size_one_node;
+	  if (discontiguous && *discontiguous)
+	    {
+	      /* This condition is similar to the error check below, but
+		 whereas that checks for a definitely-discontiguous array
+		 section in order to report an error (where such a section is
+		 illegal), here we instead need to know if the array section
+		 *may be* discontiguous so we can handle that case
+		 appropriately (i.e. for rectangular "target update"
+		 operations).  */
+	      bool full_span = false;
+	      if (length != NULL_TREE
+		  && TREE_CODE (length) == INTEGER_CST
+		  && TREE_CODE (types[i]) == ARRAY_TYPE
+		  && TYPE_DOMAIN (types[i])
+		  && TYPE_MAX_VALUE (TYPE_DOMAIN (types[i]))
+		  && TREE_CODE (TYPE_MAX_VALUE (TYPE_DOMAIN (types[i])))
+		     == INTEGER_CST)
+		{
+		  tree size;
+		  size = size_binop (PLUS_EXPR,
+				     TYPE_MAX_VALUE (TYPE_DOMAIN (types[i])),
+				     size_one_node);
+		  if (tree_int_cst_equal (length, size))
+		    full_span = true;
+		}
+
+	      if (!integer_onep (stride)
+		  || (higher_discontiguous
+		      && (!integer_zerop (low_bound)
+			  || !full_span)))
+		*discontiguous = 2;
+
+	      if (!integer_onep (stride)
+		  || !integer_zerop (low_bound)
+		  || !full_span)
+		higher_discontiguous = true;
+	    }
+
 	  if (!maybe_zero_len && i > first_non_one)
 	    {
 	      if (integer_nonzerop (low_bound))
-		goto do_warn_noncontiguous;
+		goto is_noncontiguous;
 	      if (length != NULL_TREE
 		  && TREE_CODE (length) == INTEGER_CST
 		  && TYPE_DOMAIN (types[i])
@@ -5724,12 +5852,17 @@ handle_omp_array_sections (tree *pc, tree **pnext, enum c_omp_region_type ort)
 				     size_one_node);
 		  if (!tree_int_cst_equal (length, size))
 		    {
-		     do_warn_noncontiguous:
-		      error_at (OMP_CLAUSE_LOCATION (c),
-				"array section is not contiguous in %qs "
-				"clause",
-				omp_clause_code_name[OMP_CLAUSE_CODE (c)]);
-		      return true;
+		     is_noncontiguous:
+		      if (discontiguous && *discontiguous)
+			*discontiguous = 2;
+		      else
+			{
+			  error_at (OMP_CLAUSE_LOCATION (c),
+				    "array section is not contiguous in %qs "
+				    "clause",
+				    omp_clause_code_name[OMP_CLAUSE_CODE (c)]);
+			  return true;
+			}
 		    }
 		}
 	      if (!processing_template_decl
@@ -5838,6 +5971,9 @@ handle_omp_array_sections (tree *pc, tree **pnext, enum c_omp_region_type ort)
 	      OMP_CLAUSE_DECL (c) = t;
 	      return false;
 	    }
+	  if (discontiguous && *discontiguous != 2)
+	    first = omp_array_section_low_bound (OMP_CLAUSE_LOCATION (c),
+						 first);
 	  OMP_CLAUSE_DECL (c) = first;
 	  if (OMP_CLAUSE_CODE (c) == OMP_CLAUSE_HAS_DEVICE_ADDR)
 	    return false;
@@ -5845,9 +5981,6 @@ handle_omp_array_sections (tree *pc, tree **pnext, enum c_omp_region_type ort)
 	  if (TREE_CODE (t) == FIELD_DECL)
 	    t = finish_non_static_data_member (t, NULL_TREE, NULL_TREE);
 
-	  if (OMP_CLAUSE_CODE (c) != OMP_CLAUSE_MAP)
-	    return false;
-
 	  if (TREE_CODE (first) == INDIRECT_REF)
 	    {
 	      /* Detect and skip adding extra nodes for pointer-to-member
@@ -5874,6 +6007,10 @@ handle_omp_array_sections (tree *pc, tree **pnext, enum c_omp_region_type ort)
 		}
 	    }
 
+	  if (OMP_CLAUSE_CODE (c) != OMP_CLAUSE_MAP
+	      && !(discontiguous && *discontiguous == 2))
+	    return false;
+
 	  /* FIRST represents the first item of data that we are mapping.
 	     E.g. if we're mapping an array, FIRST might resemble
 	     "foo.bar.myarray[0]".  */
@@ -5892,7 +6029,8 @@ handle_omp_array_sections (tree *pc, tree **pnext, enum c_omp_region_type ort)
 
 	      c = *pc;
 
-	      if (ai.maybe_zero_length_array_section (c))
+	      if (OMP_CLAUSE_CODE (c) == OMP_CLAUSE_MAP
+		  && ai.maybe_zero_length_array_section (c))
 		OMP_CLAUSE_MAP_MAYBE_ZERO_LENGTH_ARRAY_SECTION (c) = 1;
 
 	      /* !!! If we're accessing a base decl via chained access
@@ -7033,7 +7171,7 @@ finish_omp_clauses (tree clauses, enum c_omp_region_type ort)
 	  t = OMP_CLAUSE_DECL (c);
 	  if (TREE_CODE (t) == OMP_ARRAY_SECTION)
 	    {
-	      if (handle_omp_array_sections (pc, NULL, ort))
+	      if (handle_omp_array_sections (pc, NULL, ort, NULL))
 		{
 		  remove = true;
 		  break;
@@ -8073,7 +8211,8 @@ finish_omp_clauses (tree clauses, enum c_omp_region_type ort)
 
 	  if (TREE_CODE (t) == OMP_ARRAY_SECTION)
 	    {
-	      if (handle_omp_array_sections (pc, NULL, ort))
+	      int discontiguous = 1;
+	      if (handle_omp_array_sections (pc, NULL, ort, &discontiguous))
 		remove = true;
 	      else if (OMP_CLAUSE_CODE (c) == OMP_CLAUSE_DEPEND
 		       && (OMP_CLAUSE_DEPEND_KIND (c)
@@ -8228,6 +8367,9 @@ finish_omp_clauses (tree clauses, enum c_omp_region_type ort)
 	      remove = true;
 	      break;
 	    }
+	  if (OMP_CLAUSE_MAP_KIND (c) == GOMP_MAP_GRID_DIM
+	      || OMP_CLAUSE_MAP_KIND (c) == GOMP_MAP_GRID_STRIDE)
+	    break;
 	  /* FALLTHRU */
 	case OMP_CLAUSE_TO:
 	case OMP_CLAUSE_FROM:
@@ -8242,8 +8384,11 @@ finish_omp_clauses (tree clauses, enum c_omp_region_type ort)
 		grp_start_p = pc;
 		grp_sentinel = OMP_CLAUSE_CHAIN (c);
 
+		int discontiguous
+		  = (OMP_CLAUSE_CODE (c) == OMP_CLAUSE_TO
+		     || OMP_CLAUSE_CODE (c) == OMP_CLAUSE_FROM);
 		tree *pnext = NULL;
-		if (handle_omp_array_sections (pc, &pnext, ort))
+		if (handle_omp_array_sections (pc, &pnext, ort, &discontiguous))
 		  remove = true;
 		else
 		  {
@@ -8834,7 +8979,7 @@ finish_omp_clauses (tree clauses, enum c_omp_region_type ort)
 	  t = OMP_CLAUSE_DECL (c);
 	  if (TREE_CODE (t) == OMP_ARRAY_SECTION)
 	    {
-	      if (handle_omp_array_sections (pc, NULL, ort))
+	      if (handle_omp_array_sections (pc, NULL, ort, NULL))
 		remove = true;
 	      else
 		{
@@ -12867,4 +13012,43 @@ cp_build_bit_cast (location_t loc, tree type, tree arg,
   return ret;
 }
 
+/* Build an OpenMP array-shape cast of ARG to TYPE.  */
+
+tree
+cp_build_omp_arrayshape_cast (location_t loc, tree type, tree arg,
+			      tsubst_flags_t complain)
+{
+  if (error_operand_p (type))
+    return error_mark_node;
+
+  if (!dependent_type_p (type)
+      && !complete_type_or_maybe_complain (type, NULL_TREE, complain))
+    return error_mark_node;
+
+  if (error_operand_p (arg))
+    return error_mark_node;
+
+  if (!type_dependent_expression_p (arg) && !dependent_type_p (type))
+    {
+      if (!trivially_copyable_p (TREE_TYPE (arg)))
+	{
+	  error_at (cp_expr_loc_or_loc (arg, loc),
+		    "OpenMP array shape source type %qT "
+		    "is not trivially copyable", TREE_TYPE (arg));
+	  return error_mark_node;
+	}
+
+      /* A pointer to multi-dimensional array conversion isn't normally
+	 allowed, but we force it here for array shape operators by creating
+	 the node directly.  We also want to avoid any overloaded conversions
+	 the user might have defined, not that there are likely to be any.  */
+      return build1_loc (loc, VIEW_CONVERT_EXPR, type, arg);
+    }
+
+  tree ret = build_min (OMP_ARRAYSHAPE_CAST_EXPR, type, arg);
+  SET_EXPR_LOCATION (ret, loc);
+
+  return ret;
+}
+
 #include "gt-cp-semantics.h"
diff --git a/gcc/cp/typeck.cc b/gcc/cp/typeck.cc
index 33c789ebc332..69c3f0659054 100644
--- a/gcc/cp/typeck.cc
+++ b/gcc/cp/typeck.cc
@@ -1625,6 +1625,9 @@ structural_comptypes (tree t1, tree t2, int strict)
 	return false;
       if (DECLTYPE_FOR_LAMBDA_PROXY (t1) != DECLTYPE_FOR_LAMBDA_PROXY (t2))
 	return false;
+      if (DECLTYPE_FOR_OMP_ARRAYSHAPE_CAST (t1)
+	  != DECLTYPE_FOR_OMP_ARRAYSHAPE_CAST (t2))
+	return false;
       if (!cp_tree_equal (DECLTYPE_TYPE_EXPR (t1), DECLTYPE_TYPE_EXPR (t2)))
         return false;
       break;
@@ -4793,7 +4796,7 @@ build_x_array_ref (location_t loc, tree arg1, tree arg2,
 
 tree
 build_omp_array_section (location_t loc, tree array_expr, tree index,
-			 tree length)
+			 tree length, tree stride)
 {
   tree idxtype;
 
@@ -4832,8 +4835,8 @@ build_omp_array_section (location_t loc, tree array_expr, tree index,
   else
     sectype = build_array_type (eltype, idxtype);
 
-  return build3_loc (loc, OMP_ARRAY_SECTION, sectype, array_expr, index,
-		     length);
+  return build4_loc (loc, OMP_ARRAY_SECTION, sectype, array_expr, index,
+		     length, stride);
 }
 
 /* Return whether OP is an expression of enum type cast to integer
@@ -8152,6 +8155,9 @@ check_for_casting_away_constness (location_t loc, tree src_type,
 		  src_type, dest_type);
       return true;
 
+    case OMP_ARRAYSHAPE_CAST_EXPR:
+      return true;
+
     default:
       gcc_unreachable();
     }
diff --git a/gcc/gimplify.cc b/gcc/gimplify.cc
index 8214efeed275..c74e7696da42 100644
--- a/gcc/gimplify.cc
+++ b/gcc/gimplify.cc
@@ -9307,6 +9307,19 @@ omp_group_last (tree *start_p)
 	grp_last_p = &OMP_CLAUSE_CHAIN (c);
       break;
 
+    case GOMP_MAP_TO_GRID:
+    case GOMP_MAP_FROM_GRID:
+      while (nc
+	     && OMP_CLAUSE_CODE (nc) == OMP_CLAUSE_MAP
+	     && (OMP_CLAUSE_MAP_KIND (nc) == GOMP_MAP_GRID_DIM
+		 || OMP_CLAUSE_MAP_KIND (nc) == GOMP_MAP_GRID_STRIDE))
+	{
+	  grp_last_p = &OMP_CLAUSE_CHAIN (c);
+	  c = nc;
+	    nc = OMP_CLAUSE_CHAIN (c);
+	}
+      break;
+
     case GOMP_MAP_STRUCT:
     case GOMP_MAP_STRUCT_UNORD:
       {
@@ -9456,6 +9469,10 @@ omp_group_base (omp_mapping_group *grp, unsigned int *chained,
 	internal_error ("unexpected mapping node");
       return error_mark_node;
 
+    case GOMP_MAP_TO_GRID:
+    case GOMP_MAP_FROM_GRID:
+      return *grp->grp_start;
+
     case GOMP_MAP_ATTACH:
     case GOMP_MAP_DETACH:
       node = OMP_CLAUSE_CHAIN (node);
@@ -14229,7 +14246,9 @@ gimplify_adjust_omp_clauses (gimple_seq *pre_p, gimple_seq body, tree *list_p,
 	    }
 	  if (remove)
 	    break;
-	  if (OMP_CLAUSE_SIZE (c) == NULL_TREE)
+	  if (OMP_CLAUSE_SIZE (c) == NULL_TREE
+	      && OMP_CLAUSE_MAP_KIND (c) != GOMP_MAP_GRID_DIM
+	      && OMP_CLAUSE_MAP_KIND (c) != GOMP_MAP_GRID_STRIDE)
 	    OMP_CLAUSE_SIZE (c) = DECL_P (decl) ? DECL_SIZE_UNIT (decl)
 				  : TYPE_SIZE_UNIT (TREE_TYPE (decl));
 	  gimplify_omp_ctxp = ctx->outer_context;
@@ -14308,6 +14327,20 @@ gimplify_adjust_omp_clauses (gimple_seq *pre_p, gimple_seq body, tree *list_p,
 				 is_gimple_lvalue, fb_lvalue) == GS_ERROR)
 		remove = true;
 	    }
+	  else if (OMP_CLAUSE_MAP_KIND (c) == GOMP_MAP_GRID_DIM
+		   || OMP_CLAUSE_MAP_KIND (c) == GOMP_MAP_GRID_STRIDE)
+	    {
+	      /* The OMP_CLAUSE_DECL for GRID_DIM/GRID_STRIDE isn't necessarily
+		 an lvalue -- e.g. it might be a constant.  So handle it
+		 specially here.  */
+	      if (gimplify_expr (&OMP_CLAUSE_DECL (c), pre_p, NULL,
+				 is_gimple_val, fb_rvalue) == GS_ERROR)
+		{
+		  gimplify_omp_ctxp = ctx;
+		  remove = true;
+		}
+	      break;
+	    }
 	  else if (!DECL_P (decl))
 	    {
 	      if ((ctx->region_type & ORT_TARGET) != 0
@@ -14400,8 +14433,13 @@ gimplify_adjust_omp_clauses (gimple_seq *pre_p, gimple_seq body, tree *list_p,
 
 	      gimplify_omp_ctxp = ctx->outer_context;
 	      if (gimplify_expr (pd, pre_p, NULL, is_gimple_lvalue,
-				 fb_lvalue) == GS_ERROR)
-		remove = true;
+				 fb_lvalue | fb_mayfail) == GS_ERROR)
+		{
+		  sorry_at (OMP_CLAUSE_LOCATION (c),
+			    "unsupported map expression %qE",
+			    OMP_CLAUSE_DECL (c));
+		  remove = true;
+		}
 	      gimplify_omp_ctxp = ctx;
 	      break;
 	    }
diff --git a/gcc/omp-general.cc b/gcc/omp-general.cc
index caddac939cd9..1f6a4b23baa7 100644
--- a/gcc/omp-general.cc
+++ b/gcc/omp-general.cc
@@ -3174,6 +3174,32 @@ omp_parse_pointer (tree *expr0, bool *has_offset)
   return false;
 }
 
+static bool
+omp_parse_noncontiguous_array (tree *expr0)
+{
+  tree expr = *expr0;
+  bool noncontig = false;
+
+  while (TREE_CODE (expr) == OMP_ARRAY_SECTION
+	 || TREE_CODE (expr) == ARRAY_REF)
+    {
+      /* Contiguous arrays use ARRAY_REF.  By the time we reach here,
+	 OMP_ARRAY_SECTION is only used for noncontiguous arrays.  */
+      if (TREE_CODE (expr) == OMP_ARRAY_SECTION)
+	noncontig = true;
+
+      expr = TREE_OPERAND (expr, 0);
+    }
+
+  if (noncontig)
+    {
+      *expr0 = expr;
+      return true;
+    }
+
+  return false;
+}
+
 static bool
 omp_parse_access_method (tree *expr0, enum access_method_kinds *kind)
 {
@@ -3182,6 +3208,13 @@ omp_parse_access_method (tree *expr0, enum access_method_kinds *kind)
 
   if (omp_parse_ref (&expr))
     *kind = ACCESS_REF;
+  else if (omp_parse_noncontiguous_array (&expr))
+    {
+      if (omp_parse_ref (&expr))
+	*kind = ACCESS_NONCONTIG_REF_TO_ARRAY;
+      else
+	*kind = ACCESS_NONCONTIG_ARRAY;
+    }
   else if (omp_parse_pointer (&expr, &has_offset))
     {
       if (omp_parse_ref (&expr))
@@ -3255,6 +3288,14 @@ omp_parse_structure_base (vec<omp_addr_token *> &addr_tokens,
       return true;
     }
 
+  if (TREE_CODE (expr) == VIEW_CONVERT_EXPR
+      && TREE_CODE (TREE_TYPE (expr)) == ARRAY_TYPE)
+    {
+      *kind = BASE_DECL;
+      *expr0 = TREE_OPERAND (expr, 0);
+      return true;
+    }
+
   *kind = BASE_ARBITRARY_EXPR;
   *expr0 = expr;
   return true;
@@ -3404,6 +3445,12 @@ debug_omp_tokenized_addr (vec<omp_addr_token *> &addr_tokens,
 	    case ACCESS_INDEXED_REF_TO_ARRAY:
 	      fputs ("access_indexed_ref_to_array", stderr);
 	      break;
+	    case ACCESS_NONCONTIG_ARRAY:
+	      fputs ("access_noncontig_array", stderr);
+	      break;
+	    case ACCESS_NONCONTIG_REF_TO_ARRAY:
+	      fputs ("access_noncontig_ref_to_array", stderr);
+	      break;
 	    }
 	  break;
 	case ARRAY_BASE:
diff --git a/gcc/omp-general.h b/gcc/omp-general.h
index a993b1294157..0164d5b1a245 100644
--- a/gcc/omp-general.h
+++ b/gcc/omp-general.h
@@ -254,7 +254,9 @@ enum access_method_kinds
   ACCESS_POINTER_OFFSET,
   ACCESS_REF_TO_POINTER_OFFSET,
   ACCESS_INDEXED_ARRAY,
-  ACCESS_INDEXED_REF_TO_ARRAY
+  ACCESS_INDEXED_REF_TO_ARRAY,
+  ACCESS_NONCONTIG_ARRAY,
+  ACCESS_NONCONTIG_REF_TO_ARRAY
 };
 
 /* These are the kinds that a STRUCTURE_BASE or ARRAY_BASE (except
diff --git a/gcc/omp-low.cc b/gcc/omp-low.cc
index 3e2c984f8815..bce74707fe89 100644
--- a/gcc/omp-low.cc
+++ b/gcc/omp-low.cc
@@ -1141,6 +1141,55 @@ fixup_child_record_type (omp_context *ctx)
     = build_qualified_type (build_reference_type (type), TYPE_QUAL_RESTRICT);
 }
 
+/* Build record type for noncontiguous target update operations.  Must be kept
+   in sync with libgomp/libgomp.h omp_noncontig_array_desc.  */
+
+static tree
+omp_noncontig_descriptor_type (location_t loc)
+{
+  static tree cached = NULL_TREE;
+
+  if (cached)
+    return cached;
+
+  tree t = make_node (RECORD_TYPE);
+
+  tree fields = build_decl (loc, FIELD_DECL, get_identifier ("__ndims"),
+			    size_type_node);
+
+  tree field = build_decl (loc, FIELD_DECL, get_identifier ("__elemsize"),
+			   size_type_node);
+  TREE_CHAIN (field) = fields;
+  fields = field;
+
+  tree ptr_size_type = build_pointer_type (size_type_node);
+
+  field = build_decl (loc, FIELD_DECL, get_identifier ("__dim"), ptr_size_type);
+  TREE_CHAIN (field) = fields;
+  fields = field;
+
+  field = build_decl (loc, FIELD_DECL, get_identifier ("__index"),
+		      ptr_size_type);
+  TREE_CHAIN (field) = fields;
+  fields = field;
+
+  field = build_decl (loc, FIELD_DECL, get_identifier ("__length"),
+		      ptr_size_type);
+  TREE_CHAIN (field) = fields;
+  fields = field;
+
+  field = build_decl (loc, FIELD_DECL, get_identifier ("__stride"),
+		      ptr_size_type);
+  TREE_CHAIN (field) = fields;
+  fields = field;
+
+  finish_builtin_struct (t, "__omp_noncontig_desc_type", fields, ptr_type_node);
+
+  cached = t;
+
+  return t;
+}
+
 /* Instantiate decls as necessary in CTX to satisfy the data sharing
    specified by CLAUSES.  */
 
@@ -1660,7 +1709,75 @@ scan_sharing_clauses (tree clauses, omp_context *ctx)
 	      install_var_local (decl, ctx);
 	      break;
 	    }
-	  if (DECL_P (decl))
+
+	  if (OMP_CLAUSE_CODE (c) == OMP_CLAUSE_MAP
+	      && (OMP_CLAUSE_MAP_KIND (c) == GOMP_MAP_TO_GRID
+		  || OMP_CLAUSE_MAP_KIND (c) == GOMP_MAP_FROM_GRID))
+	    {
+	      tree desc_type = omp_noncontig_descriptor_type (UNKNOWN_LOCATION);
+
+	      tree bare = decl;
+	      if (TREE_CODE (bare) == VIEW_CONVERT_EXPR)
+		bare = TREE_OPERAND (bare, 0);
+
+	      const char *desc_name = ".omp_noncontig_desc";
+	      /* Try (but not too hard) to make a friendly name for the
+		 descriptor.  */
+	      if (DECL_P (bare))
+		desc_name = ACONCAT ((".omp_nc_desc_",
+				      IDENTIFIER_POINTER (DECL_NAME (bare)),
+				      NULL));
+	      tree desc = create_tmp_var (desc_type, desc_name);
+	      DECL_NAMELESS (desc) = 1;
+	      TREE_ADDRESSABLE (desc) = 1;
+
+	      /* Adjust DECL so it refers to the first element of the array:
+		 either by indirecting a pointer, or by selecting the zero'th
+		 index of each dimension of an array.  (We don't have a "bias"
+		 as such for this type of noncontiguous update operation, just
+		 the volume specified in the descriptor we build in
+		 lower_omp_target.)  */
+
+	      if (TREE_CODE (TREE_TYPE (decl)) == POINTER_TYPE)
+		{
+		  decl = build_fold_indirect_ref (decl);
+		  OMP_CLAUSE_DECL (c) = decl;
+		}
+
+	      tree field
+		= build_decl (OMP_CLAUSE_LOCATION (c), 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_value) field);
+
+	      tree dn = build_omp_clause (OMP_CLAUSE_LOCATION (c),
+					  OMP_CLAUSE_MAP);
+	      OMP_CLAUSE_SET_MAP_KIND (dn, GOMP_MAP_TO_PSET);
+	      OMP_CLAUSE_DECL (dn) = desc;
+	      OMP_CLAUSE_SIZE (dn) = TYPE_SIZE_UNIT (desc_type);
+
+	      OMP_CLAUSE_CHAIN (dn) = OMP_CLAUSE_CHAIN (c);
+	      OMP_CLAUSE_CHAIN (c) = dn;
+
+	      field = build_decl (OMP_CLAUSE_LOCATION (c), 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) desc,
+				 (splay_tree_value) field);
+
+	      c = dn;
+	      tree nc;
+
+	      while ((nc = OMP_CLAUSE_CHAIN (c))
+		     && OMP_CLAUSE_CODE (nc) == OMP_CLAUSE_MAP
+		     && (OMP_CLAUSE_MAP_KIND (nc) == GOMP_MAP_GRID_DIM
+			 || OMP_CLAUSE_MAP_KIND (nc) == GOMP_MAP_GRID_STRIDE))
+		c = nc;
+	    }
+	  else if (DECL_P (decl))
 	    {
 	      if (DECL_SIZE (decl)
 		  && TREE_CODE (DECL_SIZE (decl)) != INTEGER_CST)
@@ -1893,6 +2010,11 @@ scan_sharing_clauses (tree clauses, omp_context *ctx)
 	      && is_omp_target (ctx->stmt)
 	      && !is_gimple_omp_offloaded (ctx->stmt))
 	    break;
+	  if (OMP_CLAUSE_MAP_KIND (c) == GOMP_MAP_TO_GRID
+	      || OMP_CLAUSE_MAP_KIND (c) == GOMP_MAP_FROM_GRID
+	      || OMP_CLAUSE_MAP_KIND (c) == GOMP_MAP_GRID_DIM
+	      || OMP_CLAUSE_MAP_KIND (c) == GOMP_MAP_GRID_STRIDE)
+	    break;
 	  if (DECL_P (decl))
 	    {
 	      if ((OMP_CLAUSE_MAP_KIND (c) == GOMP_MAP_POINTER
@@ -12817,6 +12939,10 @@ lower_omp_target (gimple_stmt_iterator *gsi_p, omp_context *ctx)
 	  case GOMP_MAP_DETACH:
 	  case GOMP_MAP_ATTACH_ZERO_LENGTH_ARRAY_SECTION:
 	  case GOMP_MAP_POINTER_TO_ZERO_LENGTH_ARRAY_SECTION:
+	  case GOMP_MAP_TO_GRID:
+	  case GOMP_MAP_FROM_GRID:
+	  case GOMP_MAP_GRID_DIM:
+	  case GOMP_MAP_GRID_STRIDE:
 	    break;
 	  case GOMP_MAP_IF_PRESENT:
 	  case GOMP_MAP_FORCE_ALLOC:
@@ -12833,6 +12959,20 @@ lower_omp_target (gimple_stmt_iterator *gsi_p, omp_context *ctx)
 	    gcc_unreachable ();
 	  }
 #endif
+	if (OMP_CLAUSE_MAP_KIND (c) == GOMP_MAP_TO_GRID
+	    || OMP_CLAUSE_MAP_KIND (c) == GOMP_MAP_FROM_GRID)
+	  {
+	    tree nc = OMP_CLAUSE_CHAIN (c);
+	    gcc_assert (OMP_CLAUSE_CODE (nc) == OMP_CLAUSE_MAP
+			&& OMP_CLAUSE_MAP_KIND (nc) == GOMP_MAP_TO_PSET);
+	    c = nc;
+	    while ((nc = OMP_CLAUSE_CHAIN (c))
+		   && (OMP_CLAUSE_MAP_KIND (nc) == GOMP_MAP_GRID_DIM
+		       || OMP_CLAUSE_MAP_KIND (nc) == GOMP_MAP_GRID_STRIDE))
+	      c = nc;
+	    map_cnt += 2;
+	    continue;
+	  }
 	  /* FALLTHRU */
       case OMP_CLAUSE_TO:
       case OMP_CLAUSE_FROM:
@@ -13124,7 +13264,310 @@ lower_omp_target (gimple_stmt_iterator *gsi_p, omp_context *ctx)
 		    || (OMP_CLAUSE_MAP_KIND (c)
 			== GOMP_MAP_FIRSTPRIVATE_REFERENCE)))
 	      break;
-	    if (!DECL_P (ovar))
+	    if (OMP_CLAUSE_CODE (c) == OMP_CLAUSE_MAP
+		&& (OMP_CLAUSE_MAP_KIND (c) == GOMP_MAP_TO_GRID
+		    || OMP_CLAUSE_MAP_KIND (c) == GOMP_MAP_FROM_GRID))
+	      {
+		tree decl = OMP_CLAUSE_DECL (c);
+		tree dn = OMP_CLAUSE_CHAIN (c);
+		gcc_assert (OMP_CLAUSE_CODE (dn) == OMP_CLAUSE_MAP
+			    && OMP_CLAUSE_MAP_KIND (dn) == GOMP_MAP_TO_PSET);
+		tree desc = OMP_CLAUSE_DECL (dn);
+
+		tree oc, elsize = OMP_CLAUSE_SIZE (c);
+		tree type = TREE_TYPE (decl);
+		int i, dims = 0;
+		auto_vec<tree> tdims;
+		bool pointer_based = false, handled_pointer_section = false;
+		tree arrsize = fold_convert (sizetype, elsize);
+
+		/* Allow a single (maybe strided) array section if we have a
+		   pointer base.  */
+		if (TREE_CODE (decl) == INDIRECT_REF
+		    && (TREE_CODE (TREE_TYPE (TREE_OPERAND (decl, 0)))
+			== POINTER_TYPE))
+		  {
+		    pointer_based = true;
+		    dims = 1;
+		  }
+		else
+		  for (tree itype = type;
+		       TREE_CODE (itype) == ARRAY_TYPE;
+		       itype = TREE_TYPE (itype))
+		    {
+		      tdims.safe_push (itype);
+		      dims++;
+		    }
+
+		unsigned tdim = 0;
+
+		vec<constructor_elt, va_gc> *vdim;
+		vec<constructor_elt, va_gc> *vindex;
+		vec<constructor_elt, va_gc> *vlen;
+		vec<constructor_elt, va_gc> *vstride;
+		vec_alloc (vdim, dims);
+		vec_alloc (vindex, dims);
+		vec_alloc (vlen, dims);
+		vec_alloc (vstride, dims);
+
+		tree size_arr_type
+		  = build_array_type_nelts (size_type_node, dims);
+
+		tree dim_tmp = create_tmp_var (size_arr_type, ".omp_dim");
+		DECL_NAMELESS (dim_tmp) = 1;
+		TREE_ADDRESSABLE (dim_tmp) = 1;
+		TREE_STATIC (dim_tmp) = 1;
+		tree index_tmp = create_tmp_var (size_arr_type, ".omp_index");
+		DECL_NAMELESS (index_tmp) = 1;
+		TREE_ADDRESSABLE (index_tmp) = 1;
+		TREE_STATIC (index_tmp) = 1;
+		tree len_tmp = create_tmp_var (size_arr_type, ".omp_len");
+		DECL_NAMELESS (len_tmp) = 1;
+		TREE_ADDRESSABLE (len_tmp) = 1;
+		TREE_STATIC (len_tmp) = 1;
+		tree stride_tmp = create_tmp_var (size_arr_type, ".omp_stride");
+		DECL_NAMELESS (stride_tmp) = 1;
+		TREE_ADDRESSABLE (stride_tmp) = 1;
+		TREE_STATIC (stride_tmp) = 1;
+
+		oc = c;
+		c = dn;
+
+		for (i = 0; i < dims; i++)
+		  {
+		    nc = OMP_CLAUSE_CHAIN (c);
+		    tree dim = NULL_TREE, index = NULL_TREE, len = NULL_TREE,
+			 stride = size_one_node;
+
+		    if (OMP_CLAUSE_CODE (nc) == OMP_CLAUSE_MAP
+			&& OMP_CLAUSE_MAP_KIND (nc) == GOMP_MAP_GRID_DIM)
+		      {
+			index = OMP_CLAUSE_DECL (nc);
+			len = OMP_CLAUSE_SIZE (nc);
+
+			index = fold_convert (sizetype, index);
+			len = fold_convert (sizetype, len);
+
+			tree nc2 = OMP_CLAUSE_CHAIN (nc);
+			if (nc2
+			    && OMP_CLAUSE_CODE (nc2) == OMP_CLAUSE_MAP
+			    && (OMP_CLAUSE_MAP_KIND (nc2)
+				== GOMP_MAP_GRID_STRIDE))
+			  {
+			    stride = OMP_CLAUSE_DECL (nc2);
+			    stride = fold_convert (sizetype, stride);
+			    nc = nc2;
+			  }
+
+			if (tdim < tdims.length ())
+			  {
+			    /* We have an array shape -- use that to find the
+			       total size of the data on the target to look up
+			       in libgomp.  */
+			    tree dtype = TYPE_DOMAIN (tdims[tdim]);
+			    tree minval = TYPE_MIN_VALUE (dtype);
+			    tree maxval = TYPE_MAX_VALUE (dtype);
+			    minval = fold_convert (sizetype, minval);
+			    maxval = fold_convert (sizetype, maxval);
+			    dim = size_binop (MINUS_EXPR, maxval, minval);
+			    dim = size_binop (PLUS_EXPR, dim,
+					      size_one_node);
+			    arrsize = size_binop (MULT_EXPR, arrsize, dim);
+			  }
+			else if (pointer_based && !handled_pointer_section)
+			  {
+			    /* Use the selected array section to determine the
+			       size of the array.  */
+			    tree tmp = size_binop (MULT_EXPR, len, stride);
+			    tmp = size_binop (MINUS_EXPR, tmp, stride);
+			    tmp = size_binop (PLUS_EXPR, tmp, size_one_node);
+			    dim = size_binop (PLUS_EXPR, index, tmp);
+			    arrsize = size_binop (MULT_EXPR, arrsize, dim);
+			    handled_pointer_section = true;
+			  }
+			else
+			  {
+			    if (pointer_based)
+			      error_at (OMP_CLAUSE_LOCATION (c),
+					"too many array section specifiers "
+					"for pointer-based array");
+			    else
+			      error_at (OMP_CLAUSE_LOCATION (c),
+					"too many array section specifiers "
+					"for array");
+			    dim = index = len = stride = error_mark_node;
+			  }
+			tdim++;
+
+			c = nc;
+		      }
+		    else
+		      {
+			/* We have more array dimensions than array section
+			   specifiers.  Copy the whole span.  */
+			tree dtype = TYPE_DOMAIN (tdims[tdim]);
+			tree minval = TYPE_MIN_VALUE (dtype);
+			tree maxval = TYPE_MAX_VALUE (dtype);
+			minval = fold_convert (sizetype, minval);
+			maxval = fold_convert (sizetype, maxval);
+			dim = size_binop (MINUS_EXPR, maxval, minval);
+			dim = size_binop (PLUS_EXPR, dim, size_one_node);
+			len = dim;
+			index = size_zero_node;
+		      }
+
+		    if (TREE_CODE (dim) != INTEGER_CST)
+		      TREE_STATIC (dim_tmp) = 0;
+
+		    if (TREE_CODE (index) != INTEGER_CST)
+		      TREE_STATIC (index_tmp) = 0;
+
+		    if (TREE_CODE (len) != INTEGER_CST)
+		      TREE_STATIC (len_tmp) = 0;
+
+		    if (TREE_CODE (stride) != INTEGER_CST)
+		      TREE_STATIC (stride_tmp) = 0;
+
+		    tree cidx = size_int (i);
+		    CONSTRUCTOR_APPEND_ELT (vdim, cidx, dim);
+		    CONSTRUCTOR_APPEND_ELT (vindex, cidx, index);
+		    CONSTRUCTOR_APPEND_ELT (vlen, cidx, len);
+		    CONSTRUCTOR_APPEND_ELT (vstride, cidx, stride);
+		  }
+
+		tree bias = size_zero_node;
+		tree volume = size_one_node;
+		tree enclosure = size_one_node;
+		for (i = dims - 1; i >= 0; i--)
+		  {
+		    tree dim = (*vdim)[i].value;
+		    tree index = (*vindex)[i].value;
+		    tree stride = (*vstride)[i].value;
+		    tree len = (*vlen)[i].value;
+
+		    /* For the bias we want, e.g.:
+
+			   index[0] * stride[0] * dim[1] * dim[2]
+			 + index[1] * stride[1] * dim[2]
+			 + index[2] * stride[2]
+
+		       All multiplied by "elsize".  */
+
+		    tree index_stride = size_binop (MULT_EXPR, index, stride);
+		    bias = size_binop (PLUS_EXPR, bias,
+				       size_binop (MULT_EXPR, volume,
+						   index_stride));
+		    volume = size_binop (MULT_EXPR, volume, dim);
+
+		    if (i == 0)
+		      {
+			tree elems_covered = size_binop (MINUS_EXPR, len,
+							 size_one_node);
+			elems_covered = size_binop (MULT_EXPR, elems_covered,
+						    stride);
+			elems_covered = size_binop (PLUS_EXPR, elems_covered,
+						    size_one_node);
+			enclosure = size_binop (MULT_EXPR, enclosure,
+						elems_covered);
+		      }
+		    else
+		      enclosure = volume;
+		  }
+
+		elsize = fold_convert (sizetype, elsize);
+
+		/* The size of a volume enclosing the elements to be
+		   transferred.  */
+		OMP_CLAUSE_SIZE (oc)
+		  = size_binop (MULT_EXPR, enclosure, elsize);
+		/* And the bias of the first element we will update.  */
+		OMP_CLAUSE_SIZE (dn) = size_binop (MULT_EXPR, bias, elsize);
+
+		tree cdim = build_constructor (size_arr_type, vdim);
+		tree cindex = build_constructor (size_arr_type, vindex);
+		tree clen = build_constructor (size_arr_type, vlen);
+		tree cstride = build_constructor (size_arr_type, vstride);
+
+		if (TREE_STATIC (dim_tmp))
+		  DECL_INITIAL (dim_tmp) = cdim;
+		else
+		  gimplify_assign (dim_tmp, cdim, &ilist);
+
+		if (TREE_STATIC (index_tmp))
+		  DECL_INITIAL (index_tmp) = cindex;
+		else
+		  gimplify_assign (index_tmp, cindex, &ilist);
+
+		if (TREE_STATIC (len_tmp))
+		  DECL_INITIAL (len_tmp) = clen;
+		else
+		  gimplify_assign (len_tmp, clen, &ilist);
+
+		if (TREE_STATIC (stride_tmp))
+		  DECL_INITIAL (stride_tmp) = cstride;
+		else
+		  gimplify_assign (stride_tmp, cstride, &ilist);
+
+		tree desc_type = TREE_TYPE (desc);
+
+		tree ndims_field = TYPE_FIELDS (desc_type);
+		tree elemsize_field = DECL_CHAIN (ndims_field);
+		tree dim_field = DECL_CHAIN (elemsize_field);
+		tree index_field = DECL_CHAIN (dim_field);
+		tree len_field = DECL_CHAIN (index_field);
+		tree stride_field = DECL_CHAIN (len_field);
+
+		vec<constructor_elt, va_gc> *v;
+		vec_alloc (v, 6);
+
+		bool all_static = (TREE_STATIC (dim_tmp)
+				   && TREE_STATIC (index_tmp)
+				   && TREE_STATIC (len_tmp)
+				   && TREE_STATIC (stride_tmp));
+
+		dim_tmp = build4 (ARRAY_REF, sizetype, dim_tmp, size_zero_node,
+				  NULL_TREE, NULL_TREE);
+		dim_tmp = build_fold_addr_expr (dim_tmp);
+
+		/* TODO: we could skip all-zeros index.  */
+		index_tmp = build4 (ARRAY_REF, sizetype, index_tmp,
+				    size_zero_node, NULL_TREE, NULL_TREE);
+		index_tmp = build_fold_addr_expr (index_tmp);
+
+		len_tmp = build4 (ARRAY_REF, sizetype, len_tmp, size_zero_node,
+				  NULL_TREE, NULL_TREE);
+		len_tmp = build_fold_addr_expr (len_tmp);
+
+		/* TODO: we could skip all-ones stride.  */
+		stride_tmp = build4 (ARRAY_REF, sizetype, stride_tmp,
+				     size_zero_node, NULL_TREE, NULL_TREE);
+		stride_tmp = build_fold_addr_expr (stride_tmp);
+
+		elsize = fold_convert (sizetype, elsize);
+		tree ndims = size_int (dims);
+
+		CONSTRUCTOR_APPEND_ELT (v, ndims_field, ndims);
+		CONSTRUCTOR_APPEND_ELT (v, elemsize_field, elsize);
+		CONSTRUCTOR_APPEND_ELT (v, dim_field, dim_tmp);
+		CONSTRUCTOR_APPEND_ELT (v, index_field, index_tmp);
+		CONSTRUCTOR_APPEND_ELT (v, len_field, len_tmp);
+		CONSTRUCTOR_APPEND_ELT (v, stride_field, stride_tmp);
+
+		tree desc_ctor = build_constructor (desc_type, v);
+
+		if (all_static)
+		  {
+		    TREE_STATIC (desc) = 1;
+		    DECL_INITIAL (desc) = desc_ctor;
+		  }
+		else
+		  gimplify_assign (desc, desc_ctor, &ilist);
+
+		OMP_CLAUSE_CHAIN (dn) = OMP_CLAUSE_CHAIN (nc);
+		c = oc;
+		nc = c;
+	      }
+	    else if (!DECL_P (ovar))
 	      {
 		if (OMP_CLAUSE_CODE (c) == OMP_CLAUSE_MAP
 		    && OMP_CLAUSE_MAP_ZERO_BIAS_ARRAY_SECTION (c))
diff --git a/gcc/testsuite/g++.dg/gomp/array-shaping-1.C b/gcc/testsuite/g++.dg/gomp/array-shaping-1.C
new file mode 100644
index 000000000000..8627aa7ffb35
--- /dev/null
+++ b/gcc/testsuite/g++.dg/gomp/array-shaping-1.C
@@ -0,0 +1,22 @@
+// { dg-do compile }
+// { dg-additional-options "-fdump-tree-original" }
+
+template<typename T, typename E, int A, int B, int C, int D>
+void foo ()
+{
+  T *ptr;
+  E a = A, b = B, c = C, d = D;
+
+  /* Dependent types for indices.  */
+#pragma omp target update from(([a][b+1][c][d]) ptr[1:a-2][1:b][1:c-2][1:d-2])
+// { dg-final { scan-tree-dump {map\(from_grid:VIEW_CONVERT_EXPR.*\(\*ptr\) \[len: 1\]\) map\(grid_dim:1 \[len: [^\]]+\]\) map\(grid_dim:1 \[len: [^\]]+\]\) map\(grid_dim:1 \[len: [^\]]+\]\) map\(grid_dim:1 \[len: [^]]+\]\)} "original" } }
+}
+
+int main()
+{
+  char *ptr;
+
+  foo<char, short, 3, 4, 5, 6> ();
+
+  return 0;
+}
diff --git a/gcc/testsuite/g++.dg/gomp/array-shaping-2.C b/gcc/testsuite/g++.dg/gomp/array-shaping-2.C
new file mode 100644
index 000000000000..861d66261a14
--- /dev/null
+++ b/gcc/testsuite/g++.dg/gomp/array-shaping-2.C
@@ -0,0 +1,134 @@
+// { dg-do compile }
+// { dg-additional-options "-fdump-tree-original" }
+
+template<typename T>
+struct St
+{
+  T ***ppptr;
+  T ***&rppptr;
+
+  St(T ***p, T ***&rp) : ppptr(p), rppptr(rp) { }
+};
+
+template<typename A, typename B>
+void foo()
+{
+  A *ptr;
+  A **pptr = &ptr;
+  A ***ppptr = &pptr;
+  A ***&rppptr = ppptr;
+
+#pragma omp target update to(([10]) (**ppptr)[3:4:2])
+// { dg-final { scan-tree-dump {map\(to_grid:VIEW_CONVERT_EXPR<int\[10\]>\(\*\*\*ppptr\) \[len: [0-9]+\]\) map\(grid_dim:3 \[len: 4\]\) map\(grid_stride:2\)} "original" } }
+
+#pragma omp target update to(([10]) (**rppptr)[3:4:2])
+// { dg-final { scan-tree-dump {map\(to_grid:VIEW_CONVERT_EXPR<int\[10\]>\(\*\*\*\*rppptr\) \[len: [0-9]+\]\) map\(grid_dim:3 \[len: 4\]\) map\(grid_stride:2\)} "original" } }
+
+#pragma omp target update to((**ppptr)[3:4:2])
+// { dg-final { scan-tree-dump {map\(to_grid:\*\*ppptr \[len: [0-9]+\]\) map\(grid_dim:3 \[len: 4\]\) map\(grid_stride:2\)} "original" } }
+
+#pragma omp target update to((**rppptr)[3:4:2])
+// { dg-final { scan-tree-dump {map\(to_grid:\*\*\*rppptr \[len: [0-9]+\]\) map\(grid_dim:3 \[len: 4\]\) map\(grid_stride:2\)} "original" } }
+
+  B *ptr2;
+  B **pptr2 = &ptr2;
+  B ***ppptr2 = &pptr2;
+  St<B> *s = new St<B>(ppptr2, ppptr2);
+  St<B> **ps = &s;
+  St<B> **&rps = ps;
+
+#pragma omp target update from(([10]) (**(*ps)->ppptr)[3:4:2])
+// { dg-final { scan-tree-dump {map\(from_grid:VIEW_CONVERT_EXPR<long int\[10\]>\(\*\*\*\(\*ps\)->ppptr\) \[len: [0-9]+\]\) map\(grid_dim:3 \[len: 4\]\) map\(grid_stride:2\)} "original" } }
+
+#pragma omp target update from(([10]) (**(*rps)->rppptr)[3:4:2])
+// { dg-final { scan-tree-dump {map\(from_grid:VIEW_CONVERT_EXPR<long int\[10\]>\(\*\*\*\*\(\*\*rps\)->rppptr\) \[len: [0-9]+\]\) map\(grid_dim:3 \[len: 4\]\) map\(grid_stride:2\)} "original" } }
+
+#pragma omp target update from((**(*ps)->ppptr)[3:4:2])
+// { dg-final { scan-tree-dump {map\(from_grid:\*\*\(\*ps\)->ppptr \[len: [0-9]+\]\) map\(grid_dim:3 \[len: 4\]\) map\(grid_stride:2\)} "original" } }
+
+#pragma omp target update from((**(*rps)->rppptr)[3:4:2])
+// { dg-final { scan-tree-dump {map\(from_grid:\*\*\*\(\*\*rps\)->rppptr \[len: [0-9]+\]\) map\(grid_dim:3 \[len: 4\]\) map\(grid_stride:2\)} "original" } }
+
+  B arr[10][10];
+  B (*parr)[10][10] = &arr;
+  B (**pparr2)[10][10] = &parr;
+  B (**&rpparr2)[10][10] = pparr2;
+
+#pragma omp target update from(**pparr2)
+// { dg-final { scan-tree-dump {from\(\*NON_LVALUE_EXPR <\*pparr2> \[len: [0-9]+\]\)} "original" } }
+
+#pragma omp target update to((**pparr2)[1:5:2][3:4:2])
+// { dg-final { scan-tree-dump {map\(to_grid:\*\*pparr2 \[len: [0-9]+\]\) map\(grid_dim:1 \[len: 5\]\) map\(grid_stride:2\) map\(grid_dim:3 \[len: 4\]\) map\(grid_stride:2\)} "original" } }
+
+#pragma omp target update from((**rpparr2)[1:5:2][3:4:2])
+// { dg-final { scan-tree-dump {map\(from_grid:\*\*\*rpparr2 \[len: [0-9]+\]\) map\(grid_dim:1 \[len: 5\]\) map\(grid_stride:2\) map\(grid_dim:3 \[len: 4\]\) map\(grid_stride:2\)} "original" } }
+
+  delete s;
+}
+
+struct S
+{
+  short ***ppptr;
+  short ***&rppptr;
+
+  S(short ***p, short ***&rp) : ppptr(p), rppptr(rp) { }
+};
+
+int main()
+{
+  char *ptr;
+  char **pptr = &ptr;
+  char ***ppptr = &pptr;
+  char ***&rppptr = ppptr;
+
+#pragma omp target update to(([10]) (**ppptr)[1:5:2])
+// { dg-final { scan-tree-dump {map\(to_grid:VIEW_CONVERT_EXPR<char\[10\]>\(\*\*\*ppptr\) \[len: 1\]\) map\(grid_dim:1 \[len: 5\]\) map\(grid_stride:2\)} "original" } }
+
+#pragma omp target update to(([10]) (**rppptr)[1:5:2])
+// { dg-final { scan-tree-dump {map\(to_grid:VIEW_CONVERT_EXPR<char\[10\]>\(\*\*\*\*rppptr\) \[len: 1\]\) map\(grid_dim:1 \[len: 5\]\) map\(grid_stride:2\)} "original" } }
+
+#pragma omp target update to((**ppptr)[1:5:2])
+// { dg-final { scan-tree-dump {map\(to_grid:\*\*ppptr \[len: 1\]\) map\(grid_dim:1 \[len: 5\]\) map\(grid_stride:2\)} "original" } }
+
+#pragma omp target update to((**rppptr)[1:5:2])
+// { dg-final { scan-tree-dump {map\(to_grid:\*\*\*rppptr \[len: 1\]\) map\(grid_dim:1 \[len: 5\]\) map\(grid_stride:2\)} "original" } }
+
+  short *ptr2;
+  short **pptr2 = &ptr2;
+  short ***ppptr2 = &pptr2;
+  S *s = new S(ppptr2, ppptr2);
+  S **ps = &s;
+  S **&rps = ps;
+
+#pragma omp target update from(([10]) (**(*ps)->ppptr)[1:5:2])
+// { dg-final { scan-tree-dump {map\(from_grid:VIEW_CONVERT_EXPR<short int\[10\]>\(\*\*\*\(\*ps\)->ppptr\) \[len: [0-9]+\]\) map\(grid_dim:1 \[len: 5\]\) map\(grid_stride:2\)} "original" } }
+
+#pragma omp target update from(([10]) (**(*rps)->rppptr)[1:5:2])
+// { dg-final { scan-tree-dump {map\(from_grid:VIEW_CONVERT_EXPR<short int\[10\]>\(\*\*\*\*\(\*\*rps\)->rppptr\) \[len: [0-9]+\]\) map\(grid_dim:1 \[len: 5\]\) map\(grid_stride:2\)} "original" } }
+
+#pragma omp target update from((**(*ps)->ppptr)[1:5:2])
+// { dg-final { scan-tree-dump {map\(from_grid:\*\*\(\*ps\)->ppptr \[len: [0-9]+\]\) map\(grid_dim:1 \[len: 5\]\) map\(grid_stride:2\)} "original" } }
+
+#pragma omp target update from((**(*rps)->rppptr)[1:5:2])
+// { dg-final { scan-tree-dump {map\(from_grid:\*\*\*\(\*\*rps\)->rppptr \[len: [0-9]+\]\) map\(grid_dim:1 \[len: 5\]\) map\(grid_stride:2\)} "original" } }
+
+  delete s;
+
+  short arr[10][10];
+  short (*parr)[10][10] = &arr;
+  short (**pparr)[10][10] = &parr;
+  short (**&rpparr)[10][10] = pparr;
+
+#pragma omp target update from(**pparr)
+// { dg-final { scan-tree-dump {from\(\*NON_LVALUE_EXPR <\*pparr> \[len: [0-9]+\]\)} "original" } }
+
+#pragma omp target update to((**pparr)[1:5:2][1:5:2])
+// { dg-final { scan-tree-dump {map\(to_grid:\*\*pparr \[len: [0-9]+\]\) map\(grid_dim:1 \[len: 5\]\) map\(grid_stride:2\) map\(grid_dim:1 \[len: 5\]\) map\(grid_stride:2\)} "original" } }
+
+#pragma omp target update from((**rpparr)[1:5:2][1:5:2])
+// { dg-final { scan-tree-dump {map\(from_grid:\*\*\*rpparr \[len: [0-9]+\]\) map\(grid_dim:1 \[len: 5\]\) map\(grid_stride:2\) map\(grid_dim:1 \[len: 5\]\) map\(grid_stride:2\)} "original" } }
+
+  foo<int, long> ();
+
+  return 0;
+}
diff --git a/gcc/testsuite/g++.dg/gomp/bad-array-shaping-1.C b/gcc/testsuite/g++.dg/gomp/bad-array-shaping-1.C
new file mode 100644
index 000000000000..1f4e68bc065a
--- /dev/null
+++ b/gcc/testsuite/g++.dg/gomp/bad-array-shaping-1.C
@@ -0,0 +1,47 @@
+// { dg-do compile }
+
+#include <string.h>
+#include <assert.h>
+
+template<typename T, int C, int D>
+void foo (T *w)
+{
+  memset (w, 0, sizeof (T) * 100);
+
+#pragma omp target enter data map(to: w[:100])
+
+  for (int j = 0; j < 10; j++)
+    for (int i = 0; i < 10; i++)
+      w[j * 10 + i] = i + j * 3;
+
+#pragma omp target update to(([C][D]) w[3:2][1:8][0:5])
+// { dg-error "too many array section specifiers for" "" { target *-*-* } .-1 }
+// { dg-error "'#pragma omp target update' must contain at least one 'from' or 'to' clauses" "" { target *-*-* } .-2 }
+
+#pragma omp target exit data map(from: w[:100])
+}
+
+int main()
+{
+  float *arr = new float[100];
+
+  memset (arr, 0, sizeof (float) * 100);
+
+#pragma omp target enter data map(to: arr[:100])
+
+  for (int j = 0; j < 10; j++)
+    for (int i = 0; i < 10; i++)
+      arr[j * 10 + i] = i + j * 3;
+
+#pragma omp target update to(([10][10]) arr[3:2][1:8][0:5])
+// { dg-error "too many array section specifiers for" "" { target *-*-* } .-1 }
+// { dg-error "'#pragma omp target update' must contain at least one 'from' or 'to' clauses" "" { target *-*-* } .-2 }
+
+#pragma omp target exit data map(from: arr[:100])
+
+  foo<float, 5, 20> (arr);
+
+  delete[] arr;
+
+  return 0;
+}
diff --git a/gcc/testsuite/g++.dg/gomp/bad-array-shaping-2.C b/gcc/testsuite/g++.dg/gomp/bad-array-shaping-2.C
new file mode 100644
index 000000000000..d32092925464
--- /dev/null
+++ b/gcc/testsuite/g++.dg/gomp/bad-array-shaping-2.C
@@ -0,0 +1,52 @@
+// { dg-do compile }
+
+#include <string.h>
+#include <assert.h>
+
+template<typename T, int C, int D>
+void foo (T *w)
+{
+  /* This isn't allowed. We get a cascade of errors because it looks a bit
+     like lambda-definition syntax  */
+#pragma omp target enter data map(to: ([C][D]) w[:100])
+  // { dg-error {capture of non-variable 'C'} "" { target *-*-* } .-1 }
+  // { dg-error {expected '\{' before '\[' token} "" { target *-*-* } .-2 }
+  // { dg-warning {lambda expressions only available with} "" { target c++98_only } .-3 }
+  // { dg-error {expected '\)' before 'w'} "" { target *-*-* } .-4 }
+  // { dg-error {does not have pointer or array type} "" { target *-*-* } .-5 }
+
+#pragma omp target exit data map(from: ([C][D]) w[:100])
+  // { dg-error {capture of non-variable 'C'} "" { target *-*-* } .-1 }
+  // { dg-error {expected '\{' before '\[' token} "" { target *-*-* } .-2 }
+  // { dg-warning {lambda expressions only available with} "" { target c++98_only } .-3 }
+  // { dg-error {expected '\)' before 'w'} "" { target *-*-* } .-4 }
+  // { dg-error {does not have pointer or array type} "" { target *-*-* } .-5 }
+}
+
+int main()
+{
+  float *arr = new float[100];
+
+  /* This isn't allowed (as above).  */
+#pragma omp target enter data map(to: ([10][10]) arr[:100])
+  // { dg-error {expected identifier before numeric constant} "" { target *-*-* } .-1 }
+  // { dg-error {expected '\{' before '\[' token} "" { target *-*-* } .-2 }
+  // { dg-warning {lambda expressions only available with} "" { target c++98_only } .-3 }
+  // { dg-error {expected '\)' before 'arr'} "" { target *-*-* } .-4 }
+  // { dg-error {no match for 'operator\[\]' in} "" { target *-*-* } .-5 }
+  // { dg-error {'#pragma omp target enter data' must contain at least one 'map' clause} "" { target *-*-*} .-6 }
+
+#pragma omp target exit data map(from: ([10][10]) arr[:100])
+  // { dg-error {expected identifier before numeric constant} "" { target *-*-* } .-1 }
+  // { dg-error {expected '\{' before '\[' token} "" { target *-*-* } .-2 }
+  // { dg-warning {lambda expressions only available with} "" { target c++98_only } .-3 }
+  // { dg-error {no match for 'operator\[\]' in} "" { target *-*-* } .-4 }
+  // { dg-error {expected '\)' before 'arr'} "" { target *-*-* } .-5 }
+  // { dg-error {'#pragma omp target exit data' must contain at least one 'map' clause} "" { target *-*-* } .-6 }
+
+  foo<float, 5, 20> (arr);
+
+  delete[] arr;
+
+  return 0;
+}
diff --git a/gcc/testsuite/g++.dg/gomp/bad-array-shaping-3.C b/gcc/testsuite/g++.dg/gomp/bad-array-shaping-3.C
new file mode 100644
index 000000000000..90d0a5a80c52
--- /dev/null
+++ b/gcc/testsuite/g++.dg/gomp/bad-array-shaping-3.C
@@ -0,0 +1,53 @@
+// { dg-do compile }
+
+#include <string.h>
+#include <assert.h>
+
+template<typename T>
+void foo (T *w)
+{
+  memset (w, 0, sizeof (T) * 100);
+  int c = 50;
+
+#pragma omp target enter data map(to: w[:100])
+
+  for (int j = 0; j < 10; j++)
+    for (int i = 0; i < 10; i++)
+      w[j * 10 + i] = i + j * 3;
+
+  /* This starts out looking like an array-shape cast.  Make sure it's still
+     parsed as a lambda.  */
+#pragma omp target update to(([c] (T *v) -> T { return v[c]; } (w)))
+  // { dg-message {sorry, unimplemented: unsupported map expression} "" { target *-*-* } .-1 }
+  // { dg-warning {lambda expressions only available with} "" { target c++98_only } .-2 }
+
+#pragma omp target exit data map(from: w[:100])
+}
+
+int main()
+{
+  float *arr = new float[100];
+  int c = 50;
+
+  memset (arr, 0, sizeof (float) * 100);
+
+#pragma omp target enter data map(to: arr[:100])
+
+  for (int j = 0; j < 10; j++)
+    for (int i = 0; i < 10; i++)
+      arr[j * 10 + i] = i + j * 3;
+
+  /* As above.  */
+#pragma omp target update to(([c] (float *v) -> float { return v[c]; } (arr)))
+  // { dg-message {sorry, unimplemented: unsupported map expression} "" { target *-*-* } .-1 }
+  // { dg-error {'#pragma omp target update' must contain at least one 'from' or 'to' clauses} "" { target *-*-* } .-2 }
+  // { dg-warning {lambda expressions only available with} "" { target c++98_only } .-3 }
+
+#pragma omp target exit data map(from: arr[:100])
+
+  foo<float> (arr);
+
+  delete[] arr;
+
+  return 0;
+}
diff --git a/gcc/testsuite/g++.dg/gomp/bad-array-shaping-4.C b/gcc/testsuite/g++.dg/gomp/bad-array-shaping-4.C
new file mode 100644
index 000000000000..4518f03e9a0c
--- /dev/null
+++ b/gcc/testsuite/g++.dg/gomp/bad-array-shaping-4.C
@@ -0,0 +1,60 @@
+// { dg-do compile }
+
+#include <string.h>
+#include <assert.h>
+
+template<typename T>
+extern T* baz(T*);
+
+template<typename T>
+void foo (T *w)
+{
+  memset (w, 0, sizeof (T) * 100);
+  int c = 50;
+
+#pragma omp target enter data map(to: w[:100])
+
+  for (int j = 0; j < 10; j++)
+    for (int i = 0; i < 10; i++)
+      w[j * 10 + i] = i + j * 3;
+
+  /* No array-shaping inside a function call.  */
+#pragma omp target update to(baz(([10][10]) w))
+  // { dg-error {expected identifier before numeric constant} "" { target *-*-* } .-1 }
+  // { dg-error {expected '\{' before '\[' token} "" { target *-*-* } .-2 }
+  // { dg-warning {lambda expressions only available with} "" { target c++98_only } .-3 }
+  // { dg-error {expected '\)' before 'w'} "" { target *-*-* } .-4 }
+  // { dg-error {no match for 'operator\[\]' in} "" { target *-*-* } .-5 }
+
+#pragma omp target exit data map(from: w[:100])
+}
+
+int main()
+{
+  float *arr = new float[100];
+  int c = 50;
+
+  memset (arr, 0, sizeof (float) * 100);
+
+#pragma omp target enter data map(to: arr[:100])
+
+  for (int j = 0; j < 10; j++)
+    for (int i = 0; i < 10; i++)
+      arr[j * 10 + i] = i + j * 3;
+
+  /* As above.  */
+#pragma omp target update to(baz(([10][10]) arr))
+  // { dg-error {expected identifier before numeric constant} "" { target *-*-* } .-1 }
+  // { dg-error {expected '\{' before '\[' token} "" { target *-*-* } .-2 }
+  // { dg-warning {lambda expressions only available with} "" { target c++98_only } .-3 }
+  // { dg-error {no match for 'operator\[\]' in} "" { target *-*-* } .-4 }
+  // { dg-error {'#pragma omp target update' must contain at least one 'from' or 'to' clauses} "" { target *-*-* } .-5 }
+
+#pragma omp target exit data map(from: arr[:100])
+
+  foo<float> (arr);
+
+  delete[] arr;
+
+  return 0;
+}
diff --git a/gcc/testsuite/g++.dg/gomp/bad-array-shaping-5.C b/gcc/testsuite/g++.dg/gomp/bad-array-shaping-5.C
new file mode 100644
index 000000000000..25edb9d1d9d3
--- /dev/null
+++ b/gcc/testsuite/g++.dg/gomp/bad-array-shaping-5.C
@@ -0,0 +1,55 @@
+// { dg-do compile }
+// { dg-additional-options "-std=c++14" }
+
+#include <string.h>
+#include <assert.h>
+
+template<typename T>
+void foo (T *w)
+{
+  memset (w, 0, sizeof (T) * 100);
+  int c = 50;
+
+#pragma omp target enter data map(to: w[:100])
+
+  for (int j = 0; j < 10; j++)
+    for (int i = 0; i < 10; i++)
+      w[j * 10 + i] = i + j * 3;
+
+  /* No array-shaping inside a lambda body.  */
+#pragma omp target update to([&](const int d) -> auto& { return ([d][d]) w; } (10))
+// { dg-error {expected '\{' before '\[' token} "" { target *-*-* } .-1 }
+// { dg-error {expected ';' before 'w'} "" { target *-*-* } .-2 }
+// { dg-error {no match for 'operator\[\]' in} "" { target *-*-* } .-3 }
+
+#pragma omp target exit data map(from: w[:100])
+}
+
+int main()
+{
+  float *arr = new float[100];
+  int c = 50;
+
+  memset (arr, 0, sizeof (float) * 100);
+
+#pragma omp target enter data map(to: arr[:100])
+
+  for (int j = 0; j < 10; j++)
+    for (int i = 0; i < 10; i++)
+      arr[j * 10 + i] = i + j * 3;
+
+  /* As above.  */
+#pragma omp target update to([&](const int d) -> auto& { return ([d][d]) arr; } (10))
+// { dg-error {expected '\{' before '\[' token} "" { target *-*-* } .-1 }
+// { dg-error {no match for 'operator\[\]' in} "" { target *-*-* } .-2 }
+// { dg-error {expected ';' before 'arr'} "" { target *-*-* } .-3 }
+// { dg-error {'#pragma omp target update' must contain at least one 'from' or 'to' clauses} "" { target *-*-* } .-4 }
+
+#pragma omp target exit data map(from: arr[:100])
+
+  foo<float> (arr);
+
+  delete[] arr;
+
+  return 0;
+}
diff --git a/gcc/testsuite/g++.dg/gomp/bad-array-shaping-6.C b/gcc/testsuite/g++.dg/gomp/bad-array-shaping-6.C
new file mode 100644
index 000000000000..e796eaa39a3d
--- /dev/null
+++ b/gcc/testsuite/g++.dg/gomp/bad-array-shaping-6.C
@@ -0,0 +1,59 @@
+// { dg-do compile }
+
+#include <string.h>
+#include <assert.h>
+
+template<typename T>
+void foo (T *w)
+{
+  memset (w, 0, sizeof (T) * 100);
+
+#pragma omp target enter data map(to: w[:100])
+
+  for (int j = 0; j < 10; j++)
+    for (int i = 0; i < 10; i++)
+      w[j * 10 + i] = i + j * 3;
+
+  /* No array-shaping inside a statement expression.  */
+#pragma omp target update to( ({ int d = 10; ([d][d]) w; )} )
+// { dg-error {expected '\{' before '\[' token} "" { target *-*-* } .-1 }
+// { dg-warning {lambda expressions only available with} "" { target c++98_only } .-2 }
+// { dg-error {no match for 'operator\[\]'} "" { target *-*-* } .-3 }
+// { dg-error {expected ';' before 'w'} "" { target *-*-* } .-4 }
+// { dg-error {expected primary-expression before '\)' token} "" { target *-*-* } .-5 }
+// { dg-error {expected '\)' before end of line} "" { target *-*-* } .-6 }
+// { dg-message {sorry, unimplemented: unsupported map expression} "" { target *-*-* } .-7 }
+
+#pragma omp target exit data map(from: w[:100])
+}
+
+int main()
+{
+  float *arr = new float[100];
+
+  memset (arr, 0, sizeof (float) * 100);
+
+#pragma omp target enter data map(to: arr[:100])
+
+  for (int j = 0; j < 10; j++)
+    for (int i = 0; i < 10; i++)
+      arr[j * 10 + i] = i + j * 3;
+
+  /* As above.  */
+#pragma omp target update to( ({ int d = 10; ([d][d]) arr; )} )
+// { dg-error {expected '\{' before '\[' token} "" { target *-*-* } .-1 }
+// { dg-warning {lambda expressions only available with} "" { target c++98_only } .-2 }
+// { dg-error {no match for 'operator\[\]'} "" { target *-*-* } .-3 }
+// { dg-error {expected primary-expression before '\)' token} "" { target *-*-* } .-4 }
+// { dg-error {expected '\)' before end of line} "" { target *-*-* } .-5 }
+// { dg-message {sorry, unimplemented: unsupported map expression} "" { target *-*-* } .-6 }
+// { dg-error {'#pragma omp target update' must contain at least one 'from' or 'to' clauses} "" { target *-*-* } .-7 }
+
+#pragma omp target exit data map(from: arr[:100])
+
+  foo<float> (arr);
+
+  delete[] arr;
+
+  return 0;
+}
diff --git a/gcc/testsuite/g++.dg/gomp/bad-array-shaping-7.C b/gcc/testsuite/g++.dg/gomp/bad-array-shaping-7.C
new file mode 100644
index 000000000000..62463b2f6c67
--- /dev/null
+++ b/gcc/testsuite/g++.dg/gomp/bad-array-shaping-7.C
@@ -0,0 +1,48 @@
+// { dg-do compile }
+// { dg-additional-options "-std=c++11" }
+
+#include <new>
+
+template<typename T>
+struct St {
+  T *pp;
+};
+
+template<typename T>
+void foo (T *w)
+{
+  alignas (St<T>) unsigned char buf[sizeof (St<T>)];
+  T *sub1;
+
+  /* No array shaping op in brace initialiser (nonsensical anyway, but make
+     sure it doesn't parse).  */
+#pragma omp target update to( new (buf) St<T> { ([10][10]) sub1 } )
+// { dg-error {expected identifier before numeric constant} "" { target *-*-* } .-1 }
+// { dg-error {expected '\{' before '\[' token} "" { target *-*-* } .-2 }
+// { dg-error {expected '\}' before 'sub1'} "" { target *-*-* } .-3 }
+// { dg-error {expected '\)' before 'sub1'} "" { target *-*-* } .-4 }
+// { dg-error {expected an OpenMP clause before '\}' token} "" { target *-*-* } .-5 }
+}
+
+struct S {
+  int *pp;
+};
+
+int main()
+{
+  alignas (S) unsigned char buf[sizeof (S)];
+  int *sub1;
+
+  // As above.
+#pragma omp target update to( new (buf) S { ([10][10]) sub1 } )
+// { dg-error {expected identifier before numeric constant} "" { target *-*-* } .-1 }
+// { dg-error {expected '\{' before '\[' token} "" { target *-*-* } .-2 }
+// { dg-error {expected '\}' before 'sub1'} "" { target *-*-* } .-3 }
+// { dg-error {expected '\)' before 'sub1'} "" { target *-*-* } .-4 }
+// { dg-error {expected an OpenMP clause before '\}' token} "" { target *-*-* } .-5 }
+// { dg-error {no match for 'operator\[\]'} "" { target *-*-* } .-6 }
+// { dg-error {could not convert} "" { target *-*-* } .-7 }
+// { dg-error {'#pragma omp target update' must contain at least one 'from' or 'to' clauses} "" { target *-*-* } .-8 }
+
+  return 0;
+}
diff --git a/gcc/testsuite/g++.dg/gomp/bad-array-shaping-8.C b/gcc/testsuite/g++.dg/gomp/bad-array-shaping-8.C
new file mode 100644
index 000000000000..02d7de6088e0
--- /dev/null
+++ b/gcc/testsuite/g++.dg/gomp/bad-array-shaping-8.C
@@ -0,0 +1,50 @@
+// { dg-do compile }
+
+template<typename T>
+void foo ()
+{
+  T *ptr;
+
+#pragma omp target update to(([5][6][7]) ptr[0:4][0:7][0:7])
+// { dg-error {length '7' with stride '1' above array section size in 'to' clause} "" { target *-*-* } .-1 }
+
+#pragma omp target update to(([5][6][7]) ptr[1:5][0:6][0:7])
+// { dg-error {high bound '6' above array section size in 'to' clause} "" { target *-*-* } .-1 }
+
+  // This one's OK...
+#pragma omp target update from(([100]) ptr[3:33:3])
+
+  // But this is one element out of bounds.
+#pragma omp target update from(([100]) ptr[4:33:3])
+// { dg-error {high bound '101' above array section size in 'from' clause} "" { target *-*-* } .-1 }
+
+#pragma omp target update to(([10][10]) ptr[0:9:-1][0:9])
+// { dg-error {length '9' with stride '-1' above array section size in 'to' clause} "" { target *-*-* } .-1 }
+}
+
+int main()
+{
+  char *ptr;
+
+#pragma omp target update to(([5][6][7]) ptr[0:4][0:7][0:7])
+// { dg-error {length '7' with stride '1' above array section size in 'to' clause} "" { target *-*-* } .-1 }
+// { dg-error {'#pragma omp target update' must contain at least one 'from' or 'to' clauses} "" { target *-*-* } .-2 }
+
+#pragma omp target update to(([5][6][7]) ptr[1:5][0:6][0:7])
+// { dg-error {high bound '6' above array section size in 'to' clause} "" { target *-*-* } .-1 }
+// { dg-error {'#pragma omp target update' must contain at least one 'from' or 'to' clauses} "" { target *-*-* } .-2 }
+
+#pragma omp target update from(([100]) ptr[3:33:3])
+
+#pragma omp target update from(([100]) ptr[4:33:3])
+// { dg-error {high bound '101' above array section size in 'from' clause} "" { target *-*-* } .-1 }
+// { dg-error {'#pragma omp target update' must contain at least one 'from' or 'to' clauses} "" { target *-*-* } .-2 }
+
+#pragma omp target update to(([10][10]) ptr[0:9:-1][0:9])
+// { dg-error {length '9' with stride '-1' above array section size in 'to' clause} "" { target *-*-* } .-1 }
+// { dg-error {'#pragma omp target update' must contain at least one 'from' or 'to' clauses} "" { target *-*-* } .-2 }
+
+  foo<char> ();
+
+  return 0;
+}
diff --git a/gcc/tree-pretty-print.cc b/gcc/tree-pretty-print.cc
index b3c8aa49537c..a459b23d0731 100644
--- a/gcc/tree-pretty-print.cc
+++ b/gcc/tree-pretty-print.cc
@@ -1015,6 +1015,18 @@ dump_omp_clause (pretty_printer *pp, tree clause, int spc, dump_flags_t flags)
 	case GOMP_MAP_ALWAYS_PRESENT_TOFROM:
 	  pp_string (pp, "always,present,tofrom");
 	  break;
+	case GOMP_MAP_TO_GRID:
+	  pp_string (pp, "to_grid");
+	  break;
+	case GOMP_MAP_FROM_GRID:
+	  pp_string (pp, "from_grid");
+	  break;
+	case GOMP_MAP_GRID_DIM:
+	  pp_string (pp, "grid_dim");
+	  break;
+	case GOMP_MAP_GRID_STRIDE:
+	  pp_string (pp, "grid_stride");
+	  break;
 	case GOMP_MAP_UNSET:
 	  pp_string (pp, "unset");
 	  break;
@@ -2617,6 +2629,11 @@ dump_generic_node (pretty_printer *pp, tree node, int spc, dump_flags_t flags,
       dump_generic_node (pp, TREE_OPERAND (node, 1), spc, flags, false);
       pp_colon (pp);
       dump_generic_node (pp, TREE_OPERAND (node, 2), spc, flags, false);
+      if (TREE_OPERAND (node, 3))
+	{
+	  pp_colon (pp);
+	  dump_generic_node (pp,  TREE_OPERAND (node, 3), spc, flags, false);
+	}
       pp_right_bracket (pp);
       break;
 
diff --git a/gcc/tree.def b/gcc/tree.def
index 3eaf79193516..2eabc0506cd6 100644
--- a/gcc/tree.def
+++ b/gcc/tree.def
@@ -1362,7 +1362,7 @@ DEFTREECODE (OMP_ATOMIC_CAPTURE_NEW, "omp_atomic_capture_new", tcc_statement, 2)
 DEFTREECODE (OMP_CLAUSE, "omp_clause", tcc_exceptional, 0)
 
 /* An OpenMP array section.  */
-DEFTREECODE (OMP_ARRAY_SECTION, "omp_array_section", tcc_expression, 3)
+DEFTREECODE (OMP_ARRAY_SECTION, "omp_array_section", tcc_expression, 4)
 
 /* TRANSACTION_EXPR tree code.
    Operand 0: BODY: contains body of the transaction.  */
diff --git a/include/gomp-constants.h b/include/gomp-constants.h
index 7903edf42985..cb779069c138 100644
--- a/include/gomp-constants.h
+++ b/include/gomp-constants.h
@@ -195,6 +195,9 @@ enum gomp_map_kind
     GOMP_MAP_ATTACH_ZERO_LENGTH_ARRAY_SECTION
       =					(GOMP_MAP_DEEP_COPY | 2),
 
+    GOMP_MAP_TO_GRID =			(GOMP_MAP_DEEP_COPY | 4),
+    GOMP_MAP_FROM_GRID =		(GOMP_MAP_DEEP_COPY | 5),
+
     /* Internal to GCC, not used in libgomp.  */
     /* Do not map, but pointer assign a pointer instead.  */
     GOMP_MAP_FIRSTPRIVATE_POINTER =	(GOMP_MAP_LAST | 1),
@@ -218,7 +221,9 @@ enum gomp_map_kind
     GOMP_MAP_POP_MAPPER_NAME =		(GOMP_MAP_LAST | 10),
     /* Used to hold a TREE_LIST of grouped nodes in an 'omp declare mapper'
        definition (only for Fortran at present).  */
-    GOMP_MAP_MAPPING_GROUP =		(GOMP_MAP_LAST | 11)
+    GOMP_MAP_MAPPING_GROUP =		(GOMP_MAP_LAST | 11),
+    GOMP_MAP_GRID_DIM =			(GOMP_MAP_LAST | 12),
+    GOMP_MAP_GRID_STRIDE =		(GOMP_MAP_LAST | 13)
   };
 
 #define GOMP_MAP_COPY_TO_P(X) \
diff --git a/libgomp/libgomp.h b/libgomp/libgomp.h
index 68f20651fbf9..a930a8243eae 100644
--- a/libgomp/libgomp.h
+++ b/libgomp/libgomp.h
@@ -1301,6 +1301,20 @@ struct target_mem_desc {
 };
 
 
+/* A rectangular section of an array, for noncontiguous target update
+   operations.  Must be kept in sync with
+   omp-low.cc:omp_noncontig_descriptor_type.  */
+
+typedef struct {
+  size_t ndims;
+  size_t elemsize;
+  size_t *dim;
+  size_t *index;
+  size_t *length;
+  size_t *stride;
+} omp_noncontig_array_desc;
+
+
 typedef struct acc_dispatch_t
 {
   /* Execute.  */
diff --git a/libgomp/target.c b/libgomp/target.c
index a94fcea154f9..d11e222d6fa8 100644
--- a/libgomp/target.c
+++ b/libgomp/target.c
@@ -2126,6 +2126,14 @@ goacc_unmap_vars (struct target_mem_desc *tgt, bool do_copyfrom,
   gomp_unmap_vars_internal (tgt, do_copyfrom, NULL, aq);
 }
 
+static int omp_target_memcpy_rect_worker (void *, const void *, size_t, int,
+					  const size_t *, const size_t *,
+					  const size_t *, const size_t *,
+					  const size_t *, const size_t *,
+					  struct gomp_device_descr *,
+					  struct gomp_device_descr *,
+					  size_t *, void **);
+
 static void
 gomp_update (struct gomp_device_descr *devicep, size_t mapnum, void **hostaddrs,
 	     size_t *sizes, void *kinds, bool short_mapkind)
@@ -2133,6 +2141,8 @@ gomp_update (struct gomp_device_descr *devicep, size_t mapnum, void **hostaddrs,
   size_t i;
   struct splay_tree_key_s cur_node;
   const int typemask = short_mapkind ? 0xff : 0x7;
+  size_t tmp_size = 0;
+  void *tmp = NULL;
 
   if (!devicep)
     return;
@@ -2148,91 +2158,131 @@ gomp_update (struct gomp_device_descr *devicep, size_t mapnum, void **hostaddrs,
     }
 
   for (i = 0; i < mapnum; i++)
-    if (sizes[i])
-      {
-	cur_node.host_start = (uintptr_t) hostaddrs[i];
-	cur_node.host_end = cur_node.host_start + sizes[i];
-	splay_tree_key n = splay_tree_lookup (&devicep->mem_map, &cur_node);
-	if (n)
-	  {
-	    int kind = get_kind (short_mapkind, kinds, i);
-	    if (n->host_start > cur_node.host_start
-		|| n->host_end < cur_node.host_end)
-	      {
-		gomp_mutex_unlock (&devicep->lock);
-		gomp_fatal ("Trying to update [%p..%p) object when "
-			    "only [%p..%p) is mapped",
-			    (void *) cur_node.host_start,
-			    (void *) cur_node.host_end,
-			    (void *) n->host_start,
-			    (void *) n->host_end);
-	      }
+    {
+      int kind = get_kind (short_mapkind, kinds, i);
+      if ((kind & typemask) == GOMP_MAP_TO_GRID
+	  || (kind & typemask) == GOMP_MAP_FROM_GRID)
+	{
+	  omp_noncontig_array_desc *desc
+	    = (omp_noncontig_array_desc *) hostaddrs[i + 1];
+	  cur_node.host_start = (uintptr_t) hostaddrs[i];
+	  cur_node.host_end = cur_node.host_start + sizes[i];
+	  assert (sizes[i + 1] == sizeof (omp_noncontig_array_desc));
+	  splay_tree_key n = splay_tree_lookup (&devicep->mem_map, &cur_node);
+	  if (n)
+	    {
+	      if (n->aux && n->aux->attach_count)
+		{
+		  gomp_mutex_unlock (&devicep->lock);
+		  gomp_error ("noncontiguous update with attached pointers");
+		  return;
+		}
+	      void *devaddr = (void *) (n->tgt->tgt_start + n->tgt_offset
+					+ cur_node.host_start
+					- n->host_start);
+	      if ((kind & typemask) == GOMP_MAP_TO_GRID)
+		omp_target_memcpy_rect_worker (devaddr, hostaddrs[i],
+					       desc->elemsize, desc->ndims,
+					       desc->length, desc->stride,
+					       desc->index, desc->index,
+					       desc->dim, desc->dim, devicep,
+					       NULL, &tmp_size, &tmp);
+	      else
+		omp_target_memcpy_rect_worker (hostaddrs[i], devaddr,
+					       desc->elemsize, desc->ndims,
+					       desc->length, desc->stride,
+					       desc->index, desc->index,
+					       desc->dim, desc->dim, NULL,
+					       devicep, &tmp_size, &tmp);
+	    }
+	  i++;
+	}
+      else if (sizes[i])
+	{
+	  cur_node.host_start = (uintptr_t) hostaddrs[i];
+	  cur_node.host_end = cur_node.host_start + sizes[i];
+	  splay_tree_key n = splay_tree_lookup (&devicep->mem_map, &cur_node);
+	  if (n)
+	    {
+	      if (n->host_start > cur_node.host_start
+		  || n->host_end < cur_node.host_end)
+		{
+		  gomp_mutex_unlock (&devicep->lock);
+		  gomp_fatal ("Trying to update [%p..%p) object when "
+			      "only [%p..%p) is mapped",
+			      (void *) cur_node.host_start,
+			      (void *) cur_node.host_end,
+			      (void *) n->host_start,
+			      (void *) n->host_end);
+		}
 
-	    if (n->aux && n->aux->attach_count)
-	      {
-		uintptr_t addr = cur_node.host_start;
-		while (addr < cur_node.host_end)
-		  {
-		    /* We have to be careful not to overwrite still attached
-		       pointers during host<->device updates.  */
-		    size_t i = (addr - cur_node.host_start) / sizeof (void *);
-		    if (n->aux->attach_count[i] == 0)
-		      {
-			void *devaddr = (void *) (n->tgt->tgt_start
-						  + n->tgt_offset
-						  + addr - n->host_start);
-			if (GOMP_MAP_COPY_TO_P (kind & typemask))
-			  gomp_copy_host2dev (devicep, NULL,
-					      devaddr, (void *) addr,
-					      sizeof (void *), false, NULL);
-			if (GOMP_MAP_COPY_FROM_P (kind & typemask))
-			  gomp_copy_dev2host (devicep, NULL,
-					      (void *) addr, devaddr,
-					      sizeof (void *));
-		      }
-		    addr += sizeof (void *);
-		  }
-	      }
-	    else
-	      {
-		void *hostaddr = (void *) cur_node.host_start;
-		void *devaddr = (void *) (n->tgt->tgt_start + n->tgt_offset
-					  + cur_node.host_start
-					  - n->host_start);
-		size_t size = cur_node.host_end - cur_node.host_start;
+	      if (n->aux && n->aux->attach_count)
+		{
+		  uintptr_t addr = cur_node.host_start;
+		  while (addr < cur_node.host_end)
+		    {
+		      /* We have to be careful not to overwrite still attached
+			 pointers during host<->device updates.  */
+		      size_t i = (addr - cur_node.host_start) / sizeof (void *);
+		      if (n->aux->attach_count[i] == 0)
+			{
+			  void *devaddr = (void *) (n->tgt->tgt_start
+						    + n->tgt_offset
+						    + addr - n->host_start);
+			  if (GOMP_MAP_COPY_TO_P (kind & typemask))
+			    gomp_copy_host2dev (devicep, NULL,
+						devaddr, (void *) addr,
+						sizeof (void *), false, NULL);
+			  if (GOMP_MAP_COPY_FROM_P (kind & typemask))
+			    gomp_copy_dev2host (devicep, NULL,
+						(void *) addr, devaddr,
+						sizeof (void *));
+			}
+		      addr += sizeof (void *);
+		    }
+		}
+	      else
+		{
+		  void *hostaddr = (void *) cur_node.host_start;
+		  void *devaddr = (void *) (n->tgt->tgt_start + n->tgt_offset
+					    + cur_node.host_start
+					    - n->host_start);
+		  size_t size = cur_node.host_end - cur_node.host_start;
 
-		if (GOMP_MAP_COPY_TO_P (kind & typemask))
-		  gomp_copy_host2dev (devicep, NULL, devaddr, hostaddr, size,
-				      false, NULL);
-		if (GOMP_MAP_COPY_FROM_P (kind & typemask))
-		  gomp_copy_dev2host (devicep, NULL, hostaddr, devaddr, size);
-	      }
-	  }
-	else
-	  {
-	    int kind = get_kind (short_mapkind, kinds, i);
+		  if (GOMP_MAP_COPY_TO_P (kind & typemask))
+		    gomp_copy_host2dev (devicep, NULL, devaddr, hostaddr, size,
+					false, NULL);
+		  if (GOMP_MAP_COPY_FROM_P (kind & typemask))
+		    gomp_copy_dev2host (devicep, NULL, hostaddr, devaddr, size);
+		}
+	    }
+	  else
+	    {
+	      int kind = get_kind (short_mapkind, kinds, i);
 
-	    if (GOMP_MAP_PRESENT_P (kind))
-	      {
-		/* We already looked up the memory region above and it
-		   was missing.  */
-		gomp_mutex_unlock (&devicep->lock);
+	      if (GOMP_MAP_PRESENT_P (kind))
+		{
+		  /* We already looked up the memory region above and it
+		     was missing.  */
+		  gomp_mutex_unlock (&devicep->lock);
 #ifdef HAVE_INTTYPES_H
-		gomp_fatal ("present clause: not present on the device "
-			    "(addr: %p, size: %"PRIu64" (0x%"PRIx64"), "
-			    "dev: %d)", (void *) hostaddrs[i],
-			    (uint64_t) sizes[i], (uint64_t) sizes[i],
-			    devicep->target_id);
+		  gomp_fatal ("present clause: not present on the device "
+			      "(addr: %p, size: %"PRIu64" (0x%"PRIx64"), "
+			      "dev: %d)", (void *) hostaddrs[i],
+			      (uint64_t) sizes[i], (uint64_t) sizes[i],
+			      devicep->target_id);
 #else
-		gomp_fatal ("present clause: not present on the device "
-			    "(addr: %p, size: %lu (0x%lx), dev: %d)",
-			    (void *) hostaddrs[i], (unsigned long) sizes[i],
-			    (unsigned long) sizes[i], devicep->target_id);
+		  gomp_fatal ("present clause: not present on the device "
+			      "(addr: %p, size: %lu (0x%lx), dev: %d)",
+			      (void *) hostaddrs[i], (unsigned long) sizes[i],
+			      (unsigned long) sizes[i], devicep->target_id);
 #endif
-	      }
-	  }
-      }
+		}
+	    }
+	}
+    }
   gomp_mutex_unlock (&devicep->lock);
+  assert (tmp == NULL);
 }
 
 static struct gomp_offload_icv_list *
@@ -4591,6 +4641,7 @@ omp_target_memcpy_async (void *dst, const void *src, size_t length,
 static int
 omp_target_memcpy_rect_worker (void *dst, const void *src, size_t element_size,
 			       int num_dims, const size_t *volume,
+			       const size_t *strides,
 			       const size_t *dst_offsets,
 			       const size_t *src_offsets,
 			       const size_t *dst_dimensions,
@@ -4604,7 +4655,7 @@ omp_target_memcpy_rect_worker (void *dst, const void *src, size_t element_size,
   size_t j, dst_off, src_off, length;
   int i, ret;
 
-  if (num_dims == 1)
+  if (num_dims == 1 && (!strides || strides[0] == 1))
     {
       if (__builtin_mul_overflow (element_size, volume[0], &length)
 	  || __builtin_mul_overflow (element_size, dst_offsets[0], &dst_off)
@@ -4658,9 +4709,42 @@ omp_target_memcpy_rect_worker (void *dst, const void *src, size_t element_size,
 	}
       return ret ? 0 : EINVAL;
     }
+  else if (num_dims == 1 && strides)
+    {
+      size_t stride;
+
+      assert ((src_devicep == NULL || dst_devicep == NULL)
+	      && (src_devicep != NULL || dst_devicep != NULL));
+
+      if (__builtin_mul_overflow (element_size, dst_offsets[0], &dst_off)
+	  || __builtin_mul_overflow (element_size, src_offsets[0], &src_off))
+	return EINVAL;
+
+      if (strides
+	  && __builtin_mul_overflow (element_size, strides[0], &stride))
+	return EINVAL;
+
+      for (i = 0, ret = 1; i < volume[0] && ret; i++)
+	{
+	  if (src_devicep == NULL)
+	    ret = dst_devicep->host2dev_func (dst_devicep->target_id,
+					      (char *) dst + dst_off,
+					      (const char *) src + src_off,
+					      element_size);
+	  else if (dst_devicep == NULL)
+	    ret = src_devicep->dev2host_func (src_devicep->target_id,
+					      (char *) dst + dst_off,
+					      (const char *) src + src_off,
+					      element_size);
+	  dst_off += stride;
+	  src_off += stride;
+	}
+      return ret ? 0 : EINVAL;
+    }
 
   /* host->device, device->host and intra device.  */
   if (num_dims == 2
+      && (!strides || (strides[0] == 1 && strides[1] == 1))
       && ((src_devicep
 	   && src_devicep == dst_devicep
 	   && src_devicep->memcpy2d_func)
@@ -4687,6 +4771,9 @@ omp_target_memcpy_rect_worker (void *dst, const void *src, size_t element_size,
 	return ret ? 0 : EINVAL;
     }
   else if (num_dims == 3
+	   && (!strides || (strides[0] == 1
+			    && strides[1] == 1
+			    && strides[2] == 1))
 	   && ((src_devicep
 		&& src_devicep == dst_devicep
 		&& src_devicep->memcpy3d_func)
@@ -4722,13 +4809,19 @@ omp_target_memcpy_rect_worker (void *dst, const void *src, size_t element_size,
   if (__builtin_mul_overflow (dst_slice, dst_offsets[0], &dst_off)
       || __builtin_mul_overflow (src_slice, src_offsets[0], &src_off))
     return EINVAL;
+  if (strides
+      && (__builtin_mul_overflow (dst_slice, strides[0], &dst_slice)
+	  || __builtin_mul_overflow (src_slice, strides[0], &src_slice)))
+    return EINVAL;
   for (j = 0; j < volume[0]; j++)
     {
       ret = omp_target_memcpy_rect_worker ((char *) dst + dst_off,
 					   (const char *) src + src_off,
 					   element_size, num_dims - 1,
-					   volume + 1, dst_offsets + 1,
-					   src_offsets + 1, dst_dimensions + 1,
+					   volume + 1,
+					   strides ? strides + 1 : NULL,
+					   dst_offsets + 1, src_offsets + 1,
+					   dst_dimensions + 1,
 					   src_dimensions + 1, dst_devicep,
 					   src_devicep, tmp_size, tmp);
       if (ret)
@@ -4778,8 +4871,9 @@ omp_target_memcpy_rect_copy (void *dst, const void *src,
   if (lock_dst)
     gomp_mutex_lock (&dst_devicep->lock);
   int ret = omp_target_memcpy_rect_worker (dst, src, element_size, num_dims,
-					   volume, dst_offsets, src_offsets,
-					   dst_dimensions, src_dimensions,
+					   volume, NULL, dst_offsets,
+					   src_offsets, dst_dimensions,
+					   src_dimensions,
 					   dst_devicep, src_devicep,
 					   &tmp_size, &tmp);
   if (lock_src)
diff --git a/libgomp/testsuite/libgomp.c++/array-shaping-1.C b/libgomp/testsuite/libgomp.c++/array-shaping-1.C
new file mode 100644
index 000000000000..6ff5f9475f6b
--- /dev/null
+++ b/libgomp/testsuite/libgomp.c++/array-shaping-1.C
@@ -0,0 +1,469 @@
+// { dg-do run { target offload_device_nonshared_as } }
+
+#include <string.h>
+#include <assert.h>
+
+volatile int yy = 4, zz = 2, str_str = 2;
+
+template<typename T>
+void foo()
+{
+  T *arr;
+  int x = 5;
+  T arr2d[10][10];
+
+  arr = new T[100];
+
+  /* Update whole reshaped array.  */
+
+  memset (arr, 0, 100 * sizeof (T));
+
+#pragma omp target enter data map(to: arr[:100])
+
+  for (int j = 0; j < x; j++)
+    for (int i = 0; i < 10; i++)
+      arr[j * 10 + i] = i ^ j;
+
+#pragma omp target update to(([10][x]) arr)
+
+#pragma omp target exit data map(from: arr[:100])
+
+  for (int j = 0; j < 10; j++)
+    for (int i = 0; i < 10; i++)
+      if (j < x)
+	assert (arr[j * 10 + i] == i ^ j);
+      else
+	assert (arr[j * 10 + i] == 0);
+
+
+  /* Strided update.  */
+
+  memset (arr, 0, 100 * sizeof (T));
+
+#pragma omp target enter data map(to: arr[:100])
+
+  for (int j = 0; j < 20; j++)
+    for (int i = 0; i < 5; i++)
+      arr[j * 5 + i] = i + j;
+
+#pragma omp target update to(([5][5]) arr[0:3][0:3:2])
+
+#pragma omp target exit data map(from: arr[:100])
+
+  for (int j = 0; j < 20; j++)
+    for (int i = 0; i < 5; i++)
+      if (j < 3 && (i & 1) == 0 && i < 6)
+	assert (arr[j * 5 + i] == i + j);
+      else
+	assert (arr[j * 5 + i] == 0);
+
+
+  /* Reshaped update, contiguous.  */
+
+  memset (arr, 0, 100 * sizeof (T));
+
+#pragma omp target enter data map(to: arr[:100])
+
+  for (int j = 0; j < 20; j++)
+    for (int i = 0; i < 5; i++)
+      arr[j * 5 + i] = 2 * j + i;
+
+#pragma omp target update to(([5][5]) arr[0:5][0:5])
+
+#pragma omp target exit data map(from: arr[:100])
+
+  for (int j = 0; j < 20; j++)
+    for (int i = 0; i < 5; i++)
+      if (j < 5 && i < 5)
+	assert (arr[j * 5 + i] == 2 * j + i);
+      else
+	assert (arr[j * 5 + i] == 0);
+
+
+  /* Strided update on actual array.  */
+
+  memset (arr2d, 0, 100 * sizeof (T));
+
+#pragma omp target enter data map(to: arr2d)
+
+  for (int j = 0; j < 10; j++)
+    for (int i = 0; i < 10; i++)
+      arr2d[j][i] = j + 2 * i;
+
+#pragma omp target update to(arr2d[0:5:2][5:2])
+
+#pragma omp target exit data map(from: arr2d)
+
+  for (int j = 0; j < 10; j++)
+    for (int i = 0; i < 10; i++)
+      if ((j & 1) == 0 && i >= 5 && i < 7)
+	assert (arr2d[j][i] == j + 2 * i);
+      else
+	assert (arr2d[j][i] == 0);
+
+
+  /* Update with non-constant bounds.  */
+
+  memset (arr, 0, 100 * sizeof (T));
+
+#pragma omp target enter data map(to: arr[:100])
+
+  for (int j = 0; j < 10; j++)
+    for (int i = 0; i < 10; i++)
+      arr[j * 10 + i] = (2 * j) ^ i;
+
+  x = 3;
+  int y = yy, z = zz, str = str_str;
+  /* This is actually [0:3:2] [4:2:2].  */
+#pragma omp target update to(([10][10]) arr[0:x:2][y:z:str])
+
+#pragma omp target exit data map(from: arr[:100])
+
+  for (int j = 0; j < 10; j++)
+    for (int i = 0; i < 10; i++)
+      if ((j & 1) == 0 && j < 6 && (i & 1) == 0 && i >= 4 && i < 8)
+	assert (arr[j * 10 + i] == (2 * j) ^ i);
+      else
+	assert (arr[j * 10 + i] == 0);
+
+
+  /* Update with full "major" dimension.  */
+
+  memset (arr, 0, 100 * sizeof (T));
+
+#pragma omp target enter data map(to: arr[:100])
+
+  for (int j = 0; j < 10; j++)
+    for (int i = 0; i < 10; i++)
+      arr[j * 10 + i] = i + j;
+
+#pragma omp target update to(([10][10]) arr[0:10][3:1])
+
+#pragma omp target exit data map(from: arr[:100])
+
+  for (int j = 0; j < 10; j++)
+    for (int i = 0; i < 10; i++)
+      if (i == 3)
+	assert (arr[j * 10 + i] == i + j);
+      else
+	assert (arr[j * 10 + i] == 0);
+
+
+  /* Update with full "minor" dimension.  */
+
+  memset (arr, 0, 100 * sizeof (T));
+
+#pragma omp target enter data map(to: arr[:100])
+
+  for (int j = 0; j < 10; j++)
+    for (int i = 0; i < 10; i++)
+      arr[j * 10 + i] = 3 * (i + j);
+
+#pragma omp target update to(([10][10]) arr[3:2][0:10])
+
+#pragma omp target exit data map(from: arr[:100])
+
+  for (int j = 0; j < 10; j++)
+    for (int i = 0; i < 10; i++)
+      if (j >= 3 && j < 5)
+	assert (arr[j * 10 + i] == 3 * (i + j));
+      else
+	assert (arr[j * 10 + i] == 0);
+
+
+  /* Rectangle update.  */
+
+  memset (arr, 0, 100 * sizeof (T));
+
+#pragma omp target enter data map(to: arr[:100])
+
+  for (int j = 0; j < 10; j++)
+    for (int i = 0; i < 10; i++)
+      arr[j * 10 + i] = 5 * (i + j);
+
+#pragma omp target update to(([10][10]) arr[3:2][0:9])
+
+#pragma omp target exit data map(from: arr[:100])
+
+  for (int j = 0; j < 10; j++)
+    for (int i = 0; i < 10; i++)
+      if (j >= 3 && j < 5 && i < 9)
+	assert (arr[j * 10 + i] == 5 * (i + j));
+      else
+	assert (arr[j * 10 + i] == 0);
+
+
+  /* One-dimensional strided update.  */
+
+  memset (arr, 0, 100 * sizeof (T));
+
+#pragma omp target enter data map(to: arr[:100])
+
+  for (int i = 0; i < 100; i++)
+    arr[i] = i + 99;
+
+#pragma omp target update to(([100]) arr[3:33:3])
+
+#pragma omp target exit data map(from: arr[:100])
+
+  for (int i = 0; i < 100; i++)
+    if (i >= 3 && ((i - 3) % 3) == 0)
+      assert (arr[i] == i + 99);
+    else
+      assert (arr[i] == 0);
+
+
+  /* One-dimensional strided update without explicit array shape.  */
+
+  memset (arr, 0, 100 * sizeof (T));
+
+#pragma omp target enter data map(to: arr[:100])
+
+  for (int i = 0; i < 100; i++)
+    arr[i] = i + 121;
+
+#pragma omp target update to(arr[3:33:3])
+
+#pragma omp target exit data map(from: arr[:100])
+
+  for (int i = 0; i < 100; i++)
+    if (i >= 3 && ((i - 3) % 3) == 0)
+      assert (arr[i] == i + 121);
+    else
+      assert (arr[i] == 0);
+
+  delete[] arr;
+}
+
+int main()
+{
+  int *arr;
+  int x = 5;
+  int arr2d[10][10];
+
+  arr = new int[100];
+
+  /* Update whole reshaped array.  */
+
+  memset (arr, 0, 100 * sizeof (int));
+
+#pragma omp target enter data map(to: arr[:100])
+
+  for (int j = 0; j < x; j++)
+    for (int i = 0; i < 10; i++)
+      arr[j * 10 + i] = i ^ j;
+
+#pragma omp target update to(([10][x]) arr)
+
+#pragma omp target exit data map(from: arr[:100])
+
+  for (int j = 0; j < 10; j++)
+    for (int i = 0; i < 10; i++)
+      if (j < x)
+	assert (arr[j * 10 + i] == i ^ j);
+      else
+	assert (arr[j * 10 + i] == 0);
+
+
+  /* Strided update.  */
+
+  memset (arr, 0, 100 * sizeof (int));
+
+#pragma omp target enter data map(to: arr[:100])
+
+  for (int j = 0; j < 20; j++)
+    for (int i = 0; i < 5; i++)
+      arr[j * 5 + i] = i + j;
+
+#pragma omp target update to(([5][5]) arr[0:3][0:3:2])
+
+#pragma omp target exit data map(from: arr[:100])
+
+  for (int j = 0; j < 20; j++)
+    for (int i = 0; i < 5; i++)
+      if (j < 3 && (i & 1) == 0 && i < 6)
+	assert (arr[j * 5 + i] == i + j);
+      else
+	assert (arr[j * 5 + i] == 0);
+
+
+  /* Reshaped update, contiguous.  */
+
+  memset (arr, 0, 100 * sizeof (int));
+
+#pragma omp target enter data map(to: arr[:100])
+
+  for (int j = 0; j < 20; j++)
+    for (int i = 0; i < 5; i++)
+      arr[j * 5 + i] = 2 * j + i;
+
+#pragma omp target update to(([5][5]) arr[0:5][0:5])
+
+#pragma omp target exit data map(from: arr[:100])
+
+  for (int j = 0; j < 20; j++)
+    for (int i = 0; i < 5; i++)
+      if (j < 5 && i < 5)
+	assert (arr[j * 5 + i] == 2 * j + i);
+      else
+	assert (arr[j * 5 + i] == 0);
+
+
+  /* Strided update on actual array.  */
+
+  memset (arr2d, 0, 100 * sizeof (int));
+
+#pragma omp target enter data map(to: arr2d)
+
+  for (int j = 0; j < 10; j++)
+    for (int i = 0; i < 10; i++)
+      arr2d[j][i] = j + 2 * i;
+
+#pragma omp target update to(arr2d[0:5:2][5:2])
+
+#pragma omp target exit data map(from: arr2d)
+
+  for (int j = 0; j < 10; j++)
+    for (int i = 0; i < 10; i++)
+      if ((j & 1) == 0 && i >= 5 && i < 7)
+	assert (arr2d[j][i] == j + 2 * i);
+      else
+	assert (arr2d[j][i] == 0);
+
+
+  /* Update with non-constant bounds.  */
+
+  memset (arr, 0, 100 * sizeof (int));
+
+#pragma omp target enter data map(to: arr[:100])
+
+  for (int j = 0; j < 10; j++)
+    for (int i = 0; i < 10; i++)
+      arr[j * 10 + i] = (2 * j) ^ i;
+
+  x = 3;
+  int y = yy, z = zz, str = str_str;
+  /* This is actually [0:3:2] [4:2:2].  */
+#pragma omp target update to(([10][10]) arr[0:x:2][y:z:str])
+
+#pragma omp target exit data map(from: arr[:100])
+
+  for (int j = 0; j < 10; j++)
+    for (int i = 0; i < 10; i++)
+      if ((j & 1) == 0 && j < 6 && (i & 1) == 0 && i >= 4 && i < 8)
+	assert (arr[j * 10 + i] == (2 * j) ^ i);
+      else
+	assert (arr[j * 10 + i] == 0);
+
+
+  /* Update with full "major" dimension.  */
+
+  memset (arr, 0, 100 * sizeof (int));
+
+#pragma omp target enter data map(to: arr[:100])
+
+  for (int j = 0; j < 10; j++)
+    for (int i = 0; i < 10; i++)
+      arr[j * 10 + i] = i + j;
+
+#pragma omp target update to(([10][10]) arr[0:10][3:1])
+
+#pragma omp target exit data map(from: arr[:100])
+
+  for (int j = 0; j < 10; j++)
+    for (int i = 0; i < 10; i++)
+      if (i == 3)
+	assert (arr[j * 10 + i] == i + j);
+      else
+	assert (arr[j * 10 + i] == 0);
+
+
+  /* Update with full "minor" dimension.  */
+
+  memset (arr, 0, 100 * sizeof (int));
+
+#pragma omp target enter data map(to: arr[:100])
+
+  for (int j = 0; j < 10; j++)
+    for (int i = 0; i < 10; i++)
+      arr[j * 10 + i] = 3 * (i + j);
+
+#pragma omp target update to(([10][10]) arr[3:2][0:10])
+
+#pragma omp target exit data map(from: arr[:100])
+
+  for (int j = 0; j < 10; j++)
+    for (int i = 0; i < 10; i++)
+      if (j >= 3 && j < 5)
+	assert (arr[j * 10 + i] == 3 * (i + j));
+      else
+	assert (arr[j * 10 + i] == 0);
+
+
+  /* Rectangle update.  */
+
+  memset (arr, 0, 100 * sizeof (int));
+
+#pragma omp target enter data map(to: arr[:100])
+
+  for (int j = 0; j < 10; j++)
+    for (int i = 0; i < 10; i++)
+      arr[j * 10 + i] = 5 * (i + j);
+
+#pragma omp target update to(([10][10]) arr[3:2][0:9])
+
+#pragma omp target exit data map(from: arr[:100])
+
+  for (int j = 0; j < 10; j++)
+    for (int i = 0; i < 10; i++)
+      if (j >= 3 && j < 5 && i < 9)
+	assert (arr[j * 10 + i] == 5 * (i + j));
+      else
+	assert (arr[j * 10 + i] == 0);
+
+
+  /* One-dimensional strided update.  */
+
+  memset (arr, 0, 100 * sizeof (int));
+
+#pragma omp target enter data map(to: arr[:100])
+
+  for (int i = 0; i < 100; i++)
+    arr[i] = i + 99;
+
+#pragma omp target update to(([100]) arr[3:33:3])
+
+#pragma omp target exit data map(from: arr[:100])
+
+  for (int i = 0; i < 100; i++)
+    if (i >= 3 && ((i - 3) % 3) == 0)
+      assert (arr[i] == i + 99);
+    else
+      assert (arr[i] == 0);
+
+
+  /* One-dimensional strided update without explicit array shape.  */
+
+  memset (arr, 0, 100 * sizeof (int));
+
+#pragma omp target enter data map(to: arr[:100])
+
+  for (int i = 0; i < 100; i++)
+    arr[i] = i + 121;
+
+#pragma omp target update to(arr[3:33:3])
+
+#pragma omp target exit data map(from: arr[:100])
+
+  for (int i = 0; i < 100; i++)
+    if (i >= 3 && ((i - 3) % 3) == 0)
+      assert (arr[i] == i + 121);
+    else
+      assert (arr[i] == 0);
+
+  delete[] arr;
+
+  foo<long> ();
+
+  return 0;
+}
diff --git a/libgomp/testsuite/libgomp.c++/array-shaping-10.C b/libgomp/testsuite/libgomp.c++/array-shaping-10.C
new file mode 100644
index 000000000000..648f02d34798
--- /dev/null
+++ b/libgomp/testsuite/libgomp.c++/array-shaping-10.C
@@ -0,0 +1,61 @@
+// { dg-do run { target offload_device_nonshared_as } }
+
+#include <assert.h>
+#include <string.h>
+
+#define N 10
+
+template<typename T>
+void foo ()
+{
+  T tarr[N * N];
+
+  memset (tarr, 0, N * N * sizeof (T));
+
+#pragma omp target enter data map(to: tarr)
+
+#pragma omp target
+  {
+    for (int i = 0; i < N; i++)
+      for (int j = 0; j < N; j++)
+	tarr[i * N + j] = 2 * (i + j);
+  }
+
+  /* An array, but cast to a pointer, then reshaped.  */
+#pragma omp target update from(([N][N]) ((T *) &tarr[0])[4:3][5:3])
+
+  for (int i = 4; i < 7; i++)
+    for (int j = 5; j < 8; j++)
+      assert (tarr[i * N + j] == 2 * (i + j));
+
+#pragma omp target exit data map(delete: tarr)
+}
+
+int main ()
+{
+  int iarr[N * N];
+
+  memset (iarr, 0, N * N * sizeof (int));
+
+#pragma omp target enter data map(to: iarr)
+
+#pragma omp target
+  {
+    for (int i = 0; i < 10; i++)
+      for (int j = 0; j < 10; j++)
+	iarr[i * 10 + j] = i + j;
+  }
+
+  /* An array, but cast to a pointer, then reshaped.  */
+#pragma omp target update from(([10][10]) ((int *) &iarr[0])[4:3][4:3])
+
+  for (int i = 4; i < 7; i++)
+    for (int j = 4; j < 7; j++)
+      assert (iarr[i * 10 + j] == i + j);
+
+#pragma omp target exit data map(delete: iarr)
+
+  foo<unsigned short> ();
+
+  return 0;
+}
diff --git a/libgomp/testsuite/libgomp.c++/array-shaping-11.C b/libgomp/testsuite/libgomp.c++/array-shaping-11.C
new file mode 100644
index 000000000000..6b15bd62fb1f
--- /dev/null
+++ b/libgomp/testsuite/libgomp.c++/array-shaping-11.C
@@ -0,0 +1,63 @@
+// { dg-do run { target offload_device_nonshared_as } }
+
+#include <assert.h>
+#include <string.h>
+
+#define N 10
+
+template<typename T>
+void foo ()
+{
+  T tarr_real[N * N];
+  T (&tarr)[N * N] = tarr_real;
+
+  memset (tarr, 0, N * N * sizeof (T));
+
+#pragma omp target enter data map(to: tarr)
+
+#pragma omp target
+  {
+    for (int i = 0; i < N; i++)
+      for (int j = 0; j < N; j++)
+	tarr[i * N + j] = 2 * (i + j);
+  }
+
+  /* A ref to an array, but cast to a pointer, then reshaped.  */
+#pragma omp target update from(([N][N]) ((T *) &tarr[0])[4:3][5:3])
+
+  for (int i = 4; i < 7; i++)
+    for (int j = 5; j < 8; j++)
+      assert (tarr[i * N + j] == 2 * (i + j));
+
+#pragma omp target exit data map(delete: tarr)
+}
+
+int main ()
+{
+  int iarr_real[N * N];
+  int (&iarr)[N * N] = iarr_real;
+
+  memset (iarr, 0, N * N * sizeof (int));
+
+#pragma omp target enter data map(to: iarr)
+
+#pragma omp target
+  {
+    for (int i = 0; i < 10; i++)
+      for (int j = 0; j < 10; j++)
+	iarr[i * 10 + j] = i + j;
+  }
+
+  /* A ref to an array, but cast to a pointer, then reshaped.  */
+#pragma omp target update from(([10][10]) ((int *) &iarr[0])[4:3][4:3])
+
+  for (int i = 4; i < 7; i++)
+    for (int j = 4; j < 7; j++)
+      assert (iarr[i * 10 + j] == i + j);
+
+#pragma omp target exit data map(delete: iarr)
+
+  foo<unsigned short> ();
+
+  return 0;
+}
diff --git a/libgomp/testsuite/libgomp.c++/array-shaping-12.C b/libgomp/testsuite/libgomp.c++/array-shaping-12.C
new file mode 100644
index 000000000000..103c99aa847e
--- /dev/null
+++ b/libgomp/testsuite/libgomp.c++/array-shaping-12.C
@@ -0,0 +1,65 @@
+// { dg-do run { target offload_device_nonshared_as } }
+
+#include <assert.h>
+#include <string.h>
+
+#define N 10
+
+template<typename T>
+void foo ()
+{
+  T tarr_real[N * N];
+  T *tarrp = &tarr_real[0];
+  T **tarrpp = &tarrp;
+
+  memset (tarrp, 0, N * N * sizeof (T));
+
+#pragma omp target enter data map(to: tarr_real)
+
+#pragma omp target
+  {
+    for (int i = 0; i < N; i++)
+      for (int j = 0; j < N; j++)
+	tarrp[i * N + j] = 2 * (i + j);
+  }
+
+  /* A pointer with an extra indirection.  */
+#pragma omp target update from(([N][N]) (*tarrpp)[4:3][5:3])
+
+  for (int i = 4; i < 7; i++)
+    for (int j = 5; j < 8; j++)
+      assert (tarrp[i * N + j] == 2 * (i + j));
+
+#pragma omp target exit data map(delete: tarr_real)
+}
+
+int main ()
+{
+  int iarr_real[N * N];
+  int *iarrp = &iarr_real[0];
+  int **iarrpp = &iarrp;
+
+  memset (iarrp, 0, N * N * sizeof (int));
+
+#pragma omp target enter data map(to: iarr_real)
+
+#pragma omp target
+  {
+    for (int i = 0; i < 10; i++)
+      for (int j = 0; j < 10; j++)
+	iarrp[i * 10 + j] = i + j;
+  }
+
+  /* A pointer with an extra indirection.  */
+#pragma omp target update from(([10][10]) (*iarrpp)[4:3][4:3])
+
+  for (int i = 4; i < 7; i++)
+    for (int j = 4; j < 7; j++)
+      assert (iarrp[i * 10 + j] == i + j);
+
+#pragma omp target exit data map(delete: iarr_real)
+
+  foo<unsigned short> ();
+
+  return 0;
+}
diff --git a/libgomp/testsuite/libgomp.c++/array-shaping-13.C b/libgomp/testsuite/libgomp.c++/array-shaping-13.C
new file mode 100644
index 000000000000..29345ca4264c
--- /dev/null
+++ b/libgomp/testsuite/libgomp.c++/array-shaping-13.C
@@ -0,0 +1,89 @@
+// { dg-do run { target offload_device_nonshared_as } }
+
+#include <assert.h>
+#include <string.h>
+
+#define N 10
+
+template<typename T>
+void foo ()
+{
+  T *tptr = new T[N * N * N];
+
+  memset (tptr, 0, N * N * N * sizeof (T));
+
+#pragma omp target enter data map(to: tptr[0:N*N*N])
+
+#pragma omp target
+  {
+    for (int i = 0; i < N; i++)
+      for (int j = 0; j < N; j++)
+	tptr[i * N * N + 4 * N + j] = 2 * (i + j);
+  }
+
+  /* An array ref between two array sections.  */
+#pragma omp target update from(([N][N][N]) tptr[4:3][4][5:3])
+
+  for (int i = 4; i < 7; i++)
+    for (int j = 5; j < 8; j++)
+      assert (tptr[i * N * N + 4 * N + j] == 2 * (i + j));
+
+  memset (tptr, 0, N * N * N * sizeof (T));
+
+  for (int i = 0; i < N; i++)
+    tptr[2 * N * N + i * N + 4] = 4 * i;
+
+  /* Array section between two array refs.  */
+#pragma omp target update to(([N][N][N]) tptr[2][3:6][4])
+
+#pragma omp target exit data map(from: tptr[0:N*N*N])
+
+  for (int i = 3; i < 9; i++)
+    assert (tptr[2 * N * N + i * N + 4] == 4 * i);
+
+#pragma omp target exit data map(delete: tptr[0:N*N*N])
+
+  delete[] tptr;
+}
+
+int main ()
+{
+  int *iptr = new int[N * N * N];
+
+  memset (iptr, 0, N * N * N * sizeof (int));
+
+#pragma omp target enter data map(to: iptr[0:N*N*N])
+
+#pragma omp target
+  {
+    for (int i = 0; i < N; i++)
+      for (int j = 0; j < N; j++)
+	iptr[i * N * N + 4 * N + j] = i + j;
+  }
+
+  /* An array ref between two array sections.  */
+#pragma omp target update from(([N][N][N]) iptr[2:3][4][6:3])
+
+  for (int i = 2; i < 5; i++)
+    for (int j = 6; j < 9; j++)
+      assert (iptr[i * N * N + 4 * N + j] == i + j);
+
+  memset (iptr, 0, N * N * N * sizeof (int));
+
+  for (int i = 0; i < N; i++)
+    iptr[2 * N * N + i * N + 4] = 3 * i;
+
+  /* Array section between two array refs.  */
+#pragma omp target update to(([N][N][N]) iptr[2][3:6][4])
+
+#pragma omp target exit data map(from: iptr[0:N*N*N])
+
+  for (int i = 3; i < 9; i++)
+    assert (iptr[2 * N * N + i * N + 4] == 3 * i);
+
+  delete[] iptr;
+
+  foo<unsigned long> ();
+
+  return 0;
+}
diff --git a/libgomp/testsuite/libgomp.c++/array-shaping-2.C b/libgomp/testsuite/libgomp.c++/array-shaping-2.C
new file mode 100644
index 000000000000..027543e8d297
--- /dev/null
+++ b/libgomp/testsuite/libgomp.c++/array-shaping-2.C
@@ -0,0 +1,38 @@
+// { dg-do run { target offload_device_nonshared_as } }
+
+#include <string.h>
+#include <assert.h>
+
+template<typename T>
+void foo (T *w)
+{
+  memset (w, 0, sizeof (T) * 100);
+
+#pragma omp target enter data map(to: w[:100])
+
+  for (int j = 0; j < 10; j++)
+    for (int i = 0; i < 10; i++)
+      w[j * 10 + i] = i + j;
+
+#pragma omp target update to(([10][10]) w[3:2][1:8])
+
+#pragma omp target exit data map(from: w[:100])
+
+  for (int j = 0; j < 10; j++)
+    for (int i = 0; i < 10; i++)
+      if (j >= 3 && j < 5 && i >= 1 && i < 9)
+	assert (w[j * 10 + i] == i + j);
+      else
+	assert (w[j * 10 + i] == 0);
+}
+
+int main()
+{
+  int *arr = new int[100];
+
+  foo<int> (arr);
+
+  delete[] arr;
+
+  return 0;
+}
diff --git a/libgomp/testsuite/libgomp.c++/array-shaping-3.C b/libgomp/testsuite/libgomp.c++/array-shaping-3.C
new file mode 100644
index 000000000000..09ff04bc1145
--- /dev/null
+++ b/libgomp/testsuite/libgomp.c++/array-shaping-3.C
@@ -0,0 +1,38 @@
+// { dg-do run { target offload_device_nonshared_as } }
+
+#include <string.h>
+#include <assert.h>
+
+template<int C, int D>
+void foo (double *w)
+{
+  memset (w, 0, sizeof (double) * 100);
+
+#pragma omp target enter data map(to: w[:100])
+
+  for (int j = 0; j < 10; j++)
+    for (int i = 0; i < 10; i++)
+      w[j * 10 + i] = i * 3 + j * 2;
+
+#pragma omp target update to(([C][D]) w[3:2][1:8])
+
+#pragma omp target exit data map(from: w[:100])
+
+  for (int j = 0; j < 10; j++)
+    for (int i = 0; i < 10; i++)
+      if (j >= 3 && j < 5 && i >= 1 && i < 9)
+	assert (w[j * 10 + i] == i * 3 + j * 2);
+      else
+	assert (w[j * 10 + i] == 0.0f);
+}
+
+int main()
+{
+  double *arr = new double[100];
+
+  foo<10, 10> (arr);
+
+  delete[] arr;
+
+  return 0;
+}
diff --git a/libgomp/testsuite/libgomp.c++/array-shaping-4.C b/libgomp/testsuite/libgomp.c++/array-shaping-4.C
new file mode 100644
index 000000000000..efa115e8be6b
--- /dev/null
+++ b/libgomp/testsuite/libgomp.c++/array-shaping-4.C
@@ -0,0 +1,38 @@
+// { dg-do run { target offload_device_nonshared_as } }
+
+#include <string.h>
+#include <assert.h>
+
+template<auto C, auto D>
+void foo (double *w)
+{
+  memset (w, 0, sizeof (double) * 100);
+
+#pragma omp target enter data map(to: w[:100])
+
+  for (int j = 0; j < 10; j++)
+    for (int i = 0; i < 10; i++)
+      w[j * 10 + i] = i * 2 + j * 3;
+
+#pragma omp target update to(([C][D]) w[3:2][1:8])
+
+#pragma omp target exit data map(from: w[:100])
+
+  for (int j = 0; j < 10; j++)
+    for (int i = 0; i < 10; i++)
+      if (j >= 3 && j < 5 && i >= 1 && i < 9)
+	assert (w[j * 10 + i] == i * 2 + j * 3);
+      else
+	assert (w[j * 10 + i] == 0.0f);
+}
+
+int main()
+{
+  double *arr = new double[100];
+
+  foo<10, 10> (arr);
+
+  delete[] arr;
+
+  return 0;
+}
diff --git a/libgomp/testsuite/libgomp.c++/array-shaping-5.C b/libgomp/testsuite/libgomp.c++/array-shaping-5.C
new file mode 100644
index 000000000000..7046a13c106f
--- /dev/null
+++ b/libgomp/testsuite/libgomp.c++/array-shaping-5.C
@@ -0,0 +1,38 @@
+// { dg-do run { target offload_device_nonshared_as } }
+
+#include <string.h>
+#include <assert.h>
+
+template<typename T, auto C>
+void foo (T *w, int e, int f, int g)
+{
+  memset (w, 0, sizeof (T) * 100);
+
+#pragma omp target enter data map(to: w[:100])
+
+  for (int j = 0; j < e; j++)
+    for (int i = 0; i < C; i++)
+      w[j * C + i] = i + j;
+
+#pragma omp target update to(([e][C]) w[3:2][f:g])
+
+#pragma omp target exit data map(from: w[:100])
+
+  for (int j = 0; j < e; j++)
+    for (int i = 0; i < C; i++)
+      if (j >= 3 && j < 5 && i >= f && i < f + g)
+	assert (w[j * C + i] == i + j);
+      else
+	assert (w[j * C + i] == 0.0f);
+}
+
+int main()
+{
+  float *arr = new float[100];
+
+  foo<float, 10> (arr, 10, 1, 8);
+
+  delete[] arr;
+
+  return 0;
+}
diff --git a/libgomp/testsuite/libgomp.c++/array-shaping-6.C b/libgomp/testsuite/libgomp.c++/array-shaping-6.C
new file mode 100644
index 000000000000..b960b5e58e14
--- /dev/null
+++ b/libgomp/testsuite/libgomp.c++/array-shaping-6.C
@@ -0,0 +1,54 @@
+// { dg-do run { target offload_device_nonshared_as } }
+
+#include <assert.h>
+#include <string.h>
+
+template<typename T>
+void foo (T *&aref)
+{
+#pragma omp target enter data map(to: aref[:100])
+
+#pragma omp target
+  {
+    for (int i = 0; i < 10; i++)
+      for (int j = 0; j < 10; j++)
+	aref[i * 10 + j] = i + j;
+  }
+
+#pragma omp target update from(([10][10]) aref[2:3:2][7:3])
+
+  for (int i = 2; i < 8; i += 2)
+    for (int j = 7; j < 10; j++)
+      assert (aref[i * 10 + j] == i + j);
+
+#pragma omp target exit data map(delete: aref[:100])
+}
+
+int main()
+{
+  float *arr = new float[100];
+  float *&w = arr;
+
+  memset (arr, 0, 100 * sizeof (float));
+
+#pragma omp target enter data map(to: w[:100])
+
+#pragma omp target
+  {
+    for (int i = 0; i < 10; i++)
+      for (int j = 0; j < 10; j++)
+	w[i * 10 + j] = i + j;
+  }
+
+#pragma omp target update from(([10][10]) w[4:3][4:3])
+
+  for (int i = 4; i < 7; i++)
+    for (int j = 4; j < 7; j++)
+      assert (w[i * 10 + j] == i + j);
+
+#pragma omp target exit data map(delete: w[:100])
+
+  foo<float> (arr);
+
+  delete[] arr;
+}
diff --git a/libgomp/testsuite/libgomp.c++/array-shaping-7.C b/libgomp/testsuite/libgomp.c++/array-shaping-7.C
new file mode 100644
index 000000000000..b6193f8d619e
--- /dev/null
+++ b/libgomp/testsuite/libgomp.c++/array-shaping-7.C
@@ -0,0 +1,54 @@
+// { dg-do run { target offload_device_nonshared_as } }
+
+#include <assert.h>
+#include <string.h>
+
+template<typename T>
+void foo (T (&aref)[10][10])
+{
+#pragma omp target enter data map(to: aref)
+
+#pragma omp target
+  {
+    for (int i = 0; i < 10; i++)
+      for (int j = 0; j < 10; j++)
+	aref[i][j] = i + j;
+  }
+
+#pragma omp target update from(aref[2:3:2][7:3])
+
+  for (int i = 2; i < 8; i += 2)
+    for (int j = 7; j < 10; j++)
+      assert (aref[i][j] == i + j);
+
+#pragma omp target exit data map(delete: aref)
+}
+
+int main()
+{
+  float arr2d[10][10];
+  float (&w)[10][10] = arr2d;
+
+  memset (&arr2d, 0, 100 * sizeof (float));
+
+#pragma omp target enter data map(to: w)
+
+#pragma omp target
+  {
+    for (int i = 0; i < 10; i++)
+      for (int j = 0; j < 10; j++)
+	w[i][j] = i + j;
+  }
+
+#pragma omp target update from(w[4:3][4:3])
+
+  for (int i = 4; i < 7; i++)
+    for (int j = 4; j < 7; j++)
+      assert (w[i][j] == i + j);
+
+#pragma omp target exit data map(delete: w)
+
+  foo<float> (arr2d);
+
+  return 0;
+}
diff --git a/libgomp/testsuite/libgomp.c++/array-shaping-8.C b/libgomp/testsuite/libgomp.c++/array-shaping-8.C
new file mode 100644
index 000000000000..a96cf3cffb80
--- /dev/null
+++ b/libgomp/testsuite/libgomp.c++/array-shaping-8.C
@@ -0,0 +1,65 @@
+// { dg-do run { target offload_device_nonshared_as } }
+
+#include <assert.h>
+#include <string.h>
+
+template<typename T>
+struct C {
+  T *&aptr;
+
+  C(T *&aptr_1) : aptr(aptr_1)
+  {
+  }
+};
+
+template<typename T>
+void foo (T *c)
+{
+#pragma omp target enter data map(to: c->aptr, c->aptr[:100])
+
+#pragma omp target
+  {
+    for (int i = 0; i < 10; i++)
+      for (int j = 0; j < 10; j++)
+	c->aptr[i * 10 + j] = i + j;
+  }
+
+#pragma omp target update from(([10][10]) c->aptr[2:3:2][7:3])
+
+  for (int i = 2; i < 8; i += 2)
+    for (int j = 7; j < 10; j++)
+      assert (c->aptr[i * 10 + j] == i + j);
+
+#pragma omp target exit data map(delete: c->aptr, c->aptr[:100])
+}
+
+int main()
+{
+  float *arr = new float[100];
+  C<float> cvar(arr);
+
+  memset (arr, 0, 100 * sizeof (float));
+
+#pragma omp target enter data map(to: cvar.aptr, cvar.aptr[:100])
+
+#pragma omp target
+  {
+    for (int i = 0; i < 10; i++)
+      for (int j = 0; j < 10; j++)
+	cvar.aptr[i * 10 + j] = i + j;
+  }
+
+#pragma omp target update from(([10][10]) cvar.aptr[4:3][4:3])
+
+  for (int i = 4; i < 7; i++)
+    for (int j = 4; j < 7; j++)
+      assert (cvar.aptr[i * 10 + j] == i + j);
+
+#pragma omp target exit data map(delete: cvar.aptr, cvar.aptr[:100])
+
+  foo<C<float> > (&cvar);
+
+  delete[] arr;
+
+  return 0;
+}
diff --git a/libgomp/testsuite/libgomp.c++/array-shaping-9.C b/libgomp/testsuite/libgomp.c++/array-shaping-9.C
new file mode 100644
index 000000000000..786fe9d11edb
--- /dev/null
+++ b/libgomp/testsuite/libgomp.c++/array-shaping-9.C
@@ -0,0 +1,95 @@
+// { dg-do run { target offload_device_nonshared_as } }
+
+#include <assert.h>
+#include <string.h>
+
+#define N 10
+
+struct B {
+  int (&aref)[N][N];
+
+  B(int (&aref1)[N][N]) : aref(aref1)
+  {
+  }
+};
+
+template<typename T, int S>
+struct C {
+  T (&aref)[S][S];
+
+  C(T (&aref1)[S][S]) : aref(aref1)
+  {
+  }
+};
+
+template<typename T>
+void foo (T *c)
+{
+#pragma omp target enter data map(to: c->aref)
+
+#pragma omp target
+  {
+    for (int i = 0; i < 10; i++)
+      for (int j = 0; j < 10; j++)
+	c->aref[i][j] = 2 * (i + j);
+  }
+
+#pragma omp target update from(c->aref[2:3:2][7:3])
+
+  for (int i = 2; i < 8; i += 2)
+    for (int j = 7; j < 10; j++)
+      assert (c->aref[i][j] == 2 * (i + j));
+
+#pragma omp target exit data map(delete: c->aref)
+}
+
+int main()
+{
+  int iarr[N][N];
+  float farr[N][N];
+  B bvar(iarr);
+  C<float, N> cvar(farr);
+
+  memset (iarr, 0, N * N * sizeof (int));
+  memset (farr, 0, N * N * sizeof (float));
+
+#pragma omp target enter data map(to: bvar.aref)
+
+#pragma omp target
+  {
+    for (int i = 0; i < 10; i++)
+      for (int j = 0; j < 10; j++)
+	bvar.aref[i][j] = i + j;
+  }
+
+#pragma omp target update from(bvar.aref[4:3][4:3])
+
+  for (int i = 4; i < 7; i++)
+    for (int j = 4; j < 7; j++)
+      assert (bvar.aref[i][j] == i + j);
+
+#pragma omp target exit data map(delete: bvar.aref)
+
+#pragma omp target enter data map(to: cvar.aref)
+
+#pragma omp target
+  {
+    for (int i = 0; i < 10; i++)
+      for (int j = 0; j < 10; j++)
+	cvar.aref[i][j] = i + j;
+  }
+
+#pragma omp target update from(cvar.aref[4:3][4:3])
+
+  for (int i = 4; i < 7; i++)
+    for (int j = 4; j < 7; j++)
+      assert (cvar.aref[i][j] == i + j);
+
+#pragma omp target exit data map(delete: cvar.aref)
+
+  memset (farr, 0, N * N * sizeof (float));
+
+  foo<C<float, N> > (&cvar);
+
+  return 0;
+}
diff --git a/libgomp/testsuite/libgomp.c-c++-common/array-shaping-14.c b/libgomp/testsuite/libgomp.c-c++-common/array-shaping-14.c
new file mode 100644
index 000000000000..4ca6f794f931
--- /dev/null
+++ b/libgomp/testsuite/libgomp.c-c++-common/array-shaping-14.c
@@ -0,0 +1,34 @@
+/* { dg-do run { target offload_device_nonshared_as } } */
+
+#include <stdlib.h>
+#include <stdio.h>
+#include <assert.h>
+
+typedef struct {
+  int *ptr;
+} S;
+
+int main(void)
+{
+  S q;
+  q.ptr = (int *) calloc (9 * 11, sizeof (int));
+
+#pragma omp target enter data map(to: q.ptr, q.ptr[0:9*11])
+
+#pragma omp target
+  for (int i = 0; i < 9*11; i++)
+    q.ptr[i] = i;
+
+#pragma omp target update from(([9][11]) q.ptr[3:3:2][1:4:3])
+
+  for (int j = 0; j < 9; j++)
+    for (int i = 0; i < 11; i++)
+      if (j >= 3 && j <= 7 && ((j - 3) % 2) == 0
+	  && i >= 1 && i <= 10 && ((i - 1) % 3) == 0)
+	assert (q.ptr[j * 11 + i] == j * 11 + i);
+      else
+	assert (q.ptr[j * 11 + i] == 0);
+
+#pragma omp target exit data map(release: q.ptr, q.ptr[0:9*11])
+  return 0;
+}
-- 
2.41.0


^ permalink raw reply	[flat|nested] 10+ messages in thread

* [PATCH 4/5] OpenMP: Array shaping operator and strided "target update" for C
  2023-09-06  9:34 [PATCH 0/5] OpenMP: Array-shaping operator and strided/rectangular 'target update' support Julian Brown
                   ` (2 preceding siblings ...)
  2023-09-06  9:34 ` [PATCH 3/5] OpenMP: Support strided and shaped-array updates for C++ Julian Brown
@ 2023-09-06  9:34 ` Julian Brown
  2023-09-06  9:34 ` [PATCH 5/5] OpenMP: Noncontiguous "target update" for Fortran Julian Brown
  4 siblings, 0 replies; 10+ messages in thread
From: Julian Brown @ 2023-09-06  9:34 UTC (permalink / raw)
  To: gcc-patches; +Cc: fortran, tobias, jakub

Following the similar support for C++, here is the C implementation for
the OpenMP 5.0 array-shaping operator, and for strided and rectangular
updates for "target update".

Much of the implementation is shared with the C++ support added by the
previous patch.  Some details of parsing necessarily differ for C,
but the general ideas are the same.

This version of the patch has been rebased and contains a couple of
minor fixes relative to versions posted previously.

2023-09-05  Julian Brown  <julian@codesourcery.com>

gcc/c/
	* c-parser.cc (c_parser_braced_init): Disallow array-shaping operator
	in braced init.
	(c_parser_conditional_expression): Disallow array-shaping operator in
	conditional expression.
	(c_parser_cast_expression): Add array-shaping operator support.
	(c_parser_postfix_expression): Disallow array-shaping operator in
	statement expressions.
	(c_parser_postfix_expression_after_primary): Add OpenMP array section
	stride support.
	(c_parser_expr_list): Disallow array-shaping operator in expression
	lists.
	(c_array_type_nelts_top, c_array_type_nelts_total): New functions.
	(c_parser_omp_variable_list): Support array-shaping operator.
	(c_parser_omp_target_update): Recognize GOMP_MAP_TO_GRID and
	GOMP_MAP_FROM_GRID map kinds as well as OMP_CLAUSE_TO/OMP_CLAUSE_FROM.
	* c-tree.h (c_omp_array_shaping_op_p, c_omp_has_array_shape_p): New
	extern declarations.
	(create_omp_arrayshape_type): Add prototype.
	* c-typeck.cc (c_omp_array_shaping_op_p, c_omp_has_array_shape_p): New
	globals.
	(build_omp_array_section): Permit integral types, not just integer
	constants, when creating array types for array sections.
	(create_omp_arrayshape_type): New function.
	(handle_omp_array_sections_1): Add DISCONTIGUOUS parameter.  Add
	strided/rectangular array section support.
	(omp_array_section_low_bound): New function.
	(handle_omp_array_sections): Add DISCONTIGUOUS parameter.  Add
	strided/rectangular array section support.
	(c_finish_omp_clauses): Update calls to handle_omp_array_sections.
	Handle discontiguous updates.

gcc/testsuite/
	* gcc.dg/gomp/bad-array-shaping-c-1.c: New test.
	* gcc.dg/gomp/bad-array-shaping-c-2.c: New test.
	* gcc.dg/gomp/bad-array-shaping-c-3.c: New test.
	* gcc.dg/gomp/bad-array-shaping-c-4.c: New test.
	* gcc.dg/gomp/bad-array-shaping-c-5.c: New test.
	* gcc.dg/gomp/bad-array-shaping-c-6.c: New test.
	* gcc.dg/gomp/bad-array-shaping-c-7.c: New test.

libgomp/
	* testsuite/libgomp.c/array-shaping-1.c: New test.
	* testsuite/libgomp.c/array-shaping-2.c: New test.
	* testsuite/libgomp.c/array-shaping-3.c: New test.
	* testsuite/libgomp.c/array-shaping-4.c: New test.
	* testsuite/libgomp.c/array-shaping-5.c: New test.
	* testsuite/libgomp.c/array-shaping-6.c: New test.
---
 gcc/c/c-parser.cc                             | 300 +++++++++++++++++-
 gcc/c/c-tree.h                                |   4 +
 gcc/c/c-typeck.cc                             | 235 ++++++++++++--
 .../gcc.dg/gomp/bad-array-shaping-c-1.c       |  26 ++
 .../gcc.dg/gomp/bad-array-shaping-c-2.c       |  24 ++
 .../gcc.dg/gomp/bad-array-shaping-c-3.c       |  30 ++
 .../gcc.dg/gomp/bad-array-shaping-c-4.c       |  27 ++
 .../gcc.dg/gomp/bad-array-shaping-c-5.c       |  17 +
 .../gcc.dg/gomp/bad-array-shaping-c-6.c       |  26 ++
 .../gcc.dg/gomp/bad-array-shaping-c-7.c       |  15 +
 libgomp/testsuite/libgomp.c/array-shaping-1.c | 236 ++++++++++++++
 libgomp/testsuite/libgomp.c/array-shaping-2.c |  39 +++
 libgomp/testsuite/libgomp.c/array-shaping-3.c |  42 +++
 libgomp/testsuite/libgomp.c/array-shaping-4.c |  36 +++
 libgomp/testsuite/libgomp.c/array-shaping-5.c |  38 +++
 libgomp/testsuite/libgomp.c/array-shaping-6.c |  45 +++
 16 files changed, 1097 insertions(+), 43 deletions(-)
 create mode 100644 gcc/testsuite/gcc.dg/gomp/bad-array-shaping-c-1.c
 create mode 100644 gcc/testsuite/gcc.dg/gomp/bad-array-shaping-c-2.c
 create mode 100644 gcc/testsuite/gcc.dg/gomp/bad-array-shaping-c-3.c
 create mode 100644 gcc/testsuite/gcc.dg/gomp/bad-array-shaping-c-4.c
 create mode 100644 gcc/testsuite/gcc.dg/gomp/bad-array-shaping-c-5.c
 create mode 100644 gcc/testsuite/gcc.dg/gomp/bad-array-shaping-c-6.c
 create mode 100644 gcc/testsuite/gcc.dg/gomp/bad-array-shaping-c-7.c
 create mode 100644 libgomp/testsuite/libgomp.c/array-shaping-1.c
 create mode 100644 libgomp/testsuite/libgomp.c/array-shaping-2.c
 create mode 100644 libgomp/testsuite/libgomp.c/array-shaping-3.c
 create mode 100644 libgomp/testsuite/libgomp.c/array-shaping-4.c
 create mode 100644 libgomp/testsuite/libgomp.c/array-shaping-5.c
 create mode 100644 libgomp/testsuite/libgomp.c/array-shaping-6.c

diff --git a/gcc/c/c-parser.cc b/gcc/c/c-parser.cc
index be80c2723ef0..1c28f763c8a0 100644
--- a/gcc/c/c-parser.cc
+++ b/gcc/c/c-parser.cc
@@ -5768,7 +5768,9 @@ c_parser_braced_init (c_parser *parser, tree type, bool nested_p,
   gcc_obstack_init (&braced_init_obstack);
   gcc_assert (c_parser_next_token_is (parser, CPP_OPEN_BRACE));
   bool save_c_omp_array_section_p = c_omp_array_section_p;
+  bool save_c_omp_array_shaping_op_p = c_omp_array_shaping_op_p;
   c_omp_array_section_p = false;
+  c_omp_array_shaping_op_p = false;
   matching_braces braces;
   braces.consume_open (parser);
   if (nested_p)
@@ -5808,6 +5810,7 @@ c_parser_braced_init (c_parser *parser, tree type, bool nested_p,
 	}
     }
   c_omp_array_section_p = save_c_omp_array_section_p;
+  c_omp_array_shaping_op_p = save_c_omp_array_shaping_op_p;
   c_token *next_tok = c_parser_peek_token (parser);
   if (next_tok->type != CPP_CLOSE_BRACE)
     {
@@ -8198,6 +8201,7 @@ c_parser_conditional_expression (c_parser *parser, struct c_expr *after,
   struct c_expr cond, exp1, exp2, ret;
   location_t start, cond_loc, colon_loc;
   bool save_c_omp_array_section_p = c_omp_array_section_p;
+  bool save_c_omp_array_shaping_op_p = c_omp_array_shaping_op_p;
 
   gcc_assert (!after || c_dialect_objc ());
 
@@ -8206,6 +8210,7 @@ c_parser_conditional_expression (c_parser *parser, struct c_expr *after,
   if (c_parser_next_token_is_not (parser, CPP_QUERY))
     return cond;
   c_omp_array_section_p = false;
+  c_omp_array_shaping_op_p = false;
   if (cond.value != error_mark_node)
     start = cond.get_start ();
   else
@@ -8259,6 +8264,7 @@ c_parser_conditional_expression (c_parser *parser, struct c_expr *after,
       ret.original_code = ERROR_MARK;
       ret.original_type = NULL;
       c_omp_array_section_p = save_c_omp_array_section_p;
+      c_omp_array_shaping_op_p = save_c_omp_array_shaping_op_p;
       return ret;
     }
   {
@@ -8306,6 +8312,7 @@ c_parser_conditional_expression (c_parser *parser, struct c_expr *after,
   set_c_expr_source_range (&ret, start, exp2.get_finish ());
   ret.m_decimal = 0;
   c_omp_array_section_p = save_c_omp_array_section_p;
+  c_omp_array_shaping_op_p = save_c_omp_array_shaping_op_p;
   return ret;
 }
 
@@ -8687,6 +8694,8 @@ c_parser_cast_expression (c_parser *parser, struct c_expr *after)
   if (after)
     return c_parser_postfix_expression_after_primary (parser,
 						      cast_loc, *after);
+  bool save_c_omp_has_array_shape_p = c_omp_has_array_shape_p;
+  c_omp_has_array_shape_p = false;
   /* If the expression begins with a parenthesized type name, it may
      be either a cast or a compound literal; we need to see whether
      the next character is '{' to tell the difference.  If not, it is
@@ -8695,6 +8704,10 @@ c_parser_cast_expression (c_parser *parser, struct c_expr *after)
   if (c_parser_next_token_is (parser, CPP_OPEN_PAREN)
       && c_token_starts_compound_literal (c_parser_peek_2nd_token (parser)))
     {
+      bool save_c_omp_array_section_p = c_omp_array_section_p;
+      bool save_c_omp_array_shaping_op_p = c_omp_array_shaping_op_p;
+      c_omp_array_section_p = false;
+      c_omp_array_shaping_op_p = false;
       struct c_declspecs *scspecs;
       struct c_type_name *type_name;
       struct c_expr ret;
@@ -8706,6 +8719,8 @@ c_parser_cast_expression (c_parser *parser, struct c_expr *after)
       parens.skip_until_found_close (parser);
       if (type_name == NULL)
 	{
+	  c_omp_array_section_p = save_c_omp_array_section_p;
+	  c_omp_array_shaping_op_p = save_c_omp_array_shaping_op_p;
 	  ret.set_error ();
 	  ret.original_code = ERROR_MARK;
 	  ret.original_type = NULL;
@@ -8716,9 +8731,15 @@ c_parser_cast_expression (c_parser *parser, struct c_expr *after)
       used_types_insert (type_name->specs->type);
 
       if (c_parser_next_token_is (parser, CPP_OPEN_BRACE))
-	return c_parser_postfix_expression_after_paren_type (parser, scspecs,
-							     type_name,
-							     cast_loc);
+	{
+	  c_expr r = c_parser_postfix_expression_after_paren_type (parser,
+								   scspecs,
+								   type_name,
+								   cast_loc);
+	  c_omp_array_section_p = save_c_omp_array_section_p;
+	  c_omp_array_shaping_op_p = save_c_omp_array_shaping_op_p;
+	  return r;
+	}
       if (scspecs)
 	error_at (cast_loc, "storage class specifier in cast");
       if (type_name->specs->alignas_p)
@@ -8735,10 +8756,61 @@ c_parser_cast_expression (c_parser *parser, struct c_expr *after)
       ret.original_code = ERROR_MARK;
       ret.original_type = NULL;
       ret.m_decimal = 0;
+      c_omp_array_section_p = save_c_omp_array_section_p;
+      c_omp_array_shaping_op_p = save_c_omp_array_shaping_op_p;
+      return ret;
+    }
+  else if (c_omp_array_shaping_op_p
+	   && c_parser_next_token_is (parser, CPP_OPEN_PAREN)
+	   && c_parser_peek_2nd_token (parser)->type == CPP_OPEN_SQUARE)
+    {
+      bool save_c_omp_array_section_p = c_omp_array_section_p;
+      bool save_c_omp_array_shaping_op_p = c_omp_array_shaping_op_p;
+      c_omp_array_section_p = false;
+      c_omp_array_shaping_op_p = false;
+      auto_vec<tree, 4> omp_shape_dims;
+      struct c_expr expr, ret;
+      matching_parens parens;
+      parens.consume_open (parser);
+      while (c_parser_next_token_is (parser, CPP_OPEN_SQUARE))
+	{
+	  c_parser_consume_token (parser);
+	  c_expr e = c_parser_expression (parser);
+	  if (e.value == error_mark_node)
+	    break;
+	  omp_shape_dims.safe_push (e.value);
+	  if (!c_parser_require (parser, CPP_CLOSE_SQUARE,
+				 "expected %<]%>"))
+	    break;
+	}
+      parens.require_close (parser);
+      c_omp_array_section_p = save_c_omp_array_section_p;
+      c_omp_array_shaping_op_p = save_c_omp_array_shaping_op_p;
+      {
+	location_t expr_loc = c_parser_peek_token (parser)->location;
+	bool save_c_omp_has_array_shape_p = c_omp_has_array_shape_p;
+	c_omp_has_array_shape_p = true;
+	expr = c_parser_cast_expression (parser, NULL);
+	c_omp_has_array_shape_p = save_c_omp_has_array_shape_p;
+	/* NOTE: We don't want to introduce conversions here.  */
+	expr = convert_lvalue_to_rvalue (expr_loc, expr, false, true);
+      }
+      tree arrtype
+	= create_omp_arrayshape_type (expr.value, &omp_shape_dims);
+      ret.value = build1_loc (cast_loc, VIEW_CONVERT_EXPR, arrtype,
+			      expr.value);
+      if (ret.value && expr.value)
+	set_c_expr_source_range (&ret, cast_loc, expr.get_finish ());
+      ret.original_code = ERROR_MARK;
+      ret.original_type = NULL;
+      ret.m_decimal = 0;
       return ret;
     }
   else
-    return c_parser_unary_expression (parser);
+    {
+      c_omp_has_array_shape_p = save_c_omp_has_array_shape_p;
+      return c_parser_unary_expression (parser);
+    }
 }
 
 /* Parse an unary expression (C90 6.3.3, C99 6.5.3, C11 6.5.3).
@@ -9758,6 +9830,7 @@ c_parser_postfix_expression (c_parser *parser)
 	  tree stmt;
 	  location_t brace_loc;
 	  bool save_c_omp_array_section_p = c_omp_array_section_p;
+	  bool save_c_omp_array_shaping_op_p = c_omp_array_shaping_op_p;
 	  c_parser_consume_token (parser);
 	  brace_loc = c_parser_peek_token (parser)->location;
 	  c_parser_consume_token (parser);
@@ -9775,6 +9848,7 @@ c_parser_postfix_expression (c_parser *parser)
 	      break;
 	    }
 	  c_omp_array_section_p = false;
+	  c_omp_array_shaping_op_p = false;
 	  stmt = c_begin_stmt_expr ();
 	  c_parser_compound_statement_nostart (parser);
 	  location_t close_loc = c_parser_peek_token (parser)->location;
@@ -9786,6 +9860,7 @@ c_parser_postfix_expression (c_parser *parser)
 	  set_c_expr_source_range (&expr, loc, close_loc);
 	  mark_exp_read (expr.value);
 	  c_omp_array_section_p = save_c_omp_array_section_p;
+	  c_omp_array_shaping_op_p = save_c_omp_array_shaping_op_p;
 	}
       else
 	{
@@ -11271,17 +11346,26 @@ c_parser_postfix_expression_after_primary (c_parser *parser,
 	  if (c_omp_array_section_p
 	      && c_parser_next_token_is (parser, CPP_COLON))
 	    {
+	      tree stride = NULL_TREE;
+
 	      c_parser_consume_token (parser);
 	      if (c_parser_next_token_is_not (parser, CPP_CLOSE_SQUARE))
 		len = c_parser_expression (parser).value;
 
+	      if (c_parser_next_token_is (parser, CPP_COLON))
+		{
+		  c_parser_consume_token (parser);
+		  if (c_parser_next_token_is_not (parser, CPP_CLOSE_SQUARE))
+		    stride = c_parser_expression (parser).value;
+		}
+
 	      c_parser_skip_until_found (parser, CPP_CLOSE_SQUARE,
 					 "expected %<]%>");
 
 	      start = expr.get_start ();
 	      finish = parser->tokens_buf[0].location;
 	      expr.value = build_omp_array_section (op_loc, expr.value, idx,
-						    len, NULL_TREE /* fixme */);
+						    len, stride);
 	      set_c_expr_source_range (&expr, start, finish);
 	      expr.original_code = ERROR_MARK;
 	      expr.original_type = NULL;
@@ -11292,7 +11376,20 @@ c_parser_postfix_expression_after_primary (c_parser *parser,
 					 "expected %<]%>");
 	      start = expr.get_start ();
 	      finish = parser->tokens_buf[0].location;
-	      expr.value = build_array_ref (op_loc, expr.value, idx);
+	      if (c_omp_has_array_shape_p)
+		/* If we have an array-shaping operator, we may not be able to
+		   represent a well-formed ARRAY_REF here, because we are
+		   coercing the type of the innermost array base and the
+		   original type may not be compatible.  Use the
+		   OMP_ARRAY_SECTION code instead.  We also want to explicitly
+		   avoid creating INDIRECT_REFs for pointer bases, because
+		   that can lead to parsing ambiguities (see
+		   c_parser_omp_variable_list).  */
+		expr.value
+		  = build_omp_array_section (op_loc, expr.value, idx,
+					     size_one_node, NULL_TREE);
+	      else
+		expr.value = build_array_ref (op_loc, expr.value, idx);
 	      set_c_expr_source_range (&expr, start, finish);
 	      expr.original_code = ERROR_MARK;
 	      expr.original_type = NULL;
@@ -11581,7 +11678,9 @@ c_parser_expr_list (c_parser *parser, bool convert_p, bool fold_p,
   struct c_expr expr;
   unsigned int idx = 0;
   bool save_c_omp_array_section_p = c_omp_array_section_p;
+  bool save_c_omp_array_shaping_op_p = c_omp_array_shaping_op_p;
   c_omp_array_section_p = false;
+  c_omp_array_shaping_op_p = false;
 
   ret = make_tree_vector ();
   if (p_orig_types == NULL)
@@ -11636,6 +11735,7 @@ c_parser_expr_list (c_parser *parser, bool convert_p, bool fold_p,
   if (orig_types)
     *p_orig_types = orig_types;
   c_omp_array_section_p = save_c_omp_array_section_p;
+  c_omp_array_shaping_op_p = save_c_omp_array_shaping_op_p;
   return ret;
 }
 \f
@@ -13835,6 +13935,35 @@ c_parser_oacc_wait_list (c_parser *parser, location_t clause_loc, tree list)
   return list;
 }
 
+/* Return, as an INTEGER_CST node, the number of elements for TYPE
+   (which is an ARRAY_TYPE).  This counts only elements of the top
+   array.  (From cp/tree.cc).  */
+
+static tree
+c_array_type_nelts_top (tree type)
+{
+  return fold_build2_loc (input_location, PLUS_EXPR, sizetype,
+			  array_type_nelts (type), size_one_node);
+}
+
+/* Return, as an INTEGER_CST node, the number of elements for TYPE
+   (which is an ARRAY_TYPE).  This one is a recursive count of all
+   ARRAY_TYPEs that are clumped together.  (From cp/tree.cc).  */
+
+static tree
+c_array_type_nelts_total (tree type)
+{
+  tree sz = c_array_type_nelts_top (type);
+  type = TREE_TYPE (type);
+  while (TREE_CODE (type) == ARRAY_TYPE)
+    {
+      tree n = c_array_type_nelts_top (type);
+      sz = fold_build2_loc (input_location, MULT_EXPR, sizetype, sz, n);
+      type = TREE_TYPE (type);
+    }
+  return sz;
+}
+
 /* OpenACC 2.0, OpenMP 2.5:
    variable-list:
      identifier
@@ -13962,12 +14091,24 @@ c_parser_omp_variable_list (c_parser *parser,
 	{
 	  location_t loc = c_parser_peek_token (parser)->location;
 	  bool save_c_omp_array_section_p = c_omp_array_section_p;
+	  bool save_c_omp_array_shaping_op_p = c_omp_array_shaping_op_p;
 	  c_omp_array_section_p = true;
+	  c_omp_array_shaping_op_p
+	    = (kind == OMP_CLAUSE_TO || kind == OMP_CLAUSE_FROM);
 	  c_expr expr = c_parser_expr_no_commas (parser, NULL);
 	  if (expr.value != error_mark_node)
 	    mark_exp_read (expr.value);
 	  c_omp_array_section_p = save_c_omp_array_section_p;
+	  c_omp_array_shaping_op_p = save_c_omp_array_shaping_op_p;
 	  tree decl = expr.value;
+	  tree reshaped_to = NULL_TREE;
+
+	  if (TREE_CODE (decl) == VIEW_CONVERT_EXPR
+	      && TREE_CODE (TREE_TYPE (decl)) == ARRAY_TYPE)
+	    {
+	      reshaped_to = TREE_TYPE (decl);
+	      decl = TREE_OPERAND (decl, 0);
+	    }
 
 	 /* This code rewrites a parsed expression containing various tree
 	    codes used to represent array accesses into a more uniform nest of
@@ -13980,6 +14121,31 @@ c_parser_omp_variable_list (c_parser *parser,
 	  dims.truncate (0);
 	  if (TREE_CODE (decl) == OMP_ARRAY_SECTION)
 	    {
+	      size_t sections = 0;
+	      tree orig_decl = decl;
+	      bool update_p = (kind == OMP_CLAUSE_TO
+			       || kind == OMP_CLAUSE_FROM);
+	      bool maybe_ptr_based_noncontig_update = false;
+
+	      while (update_p
+		     && !reshaped_to
+		     && (TREE_CODE (decl) == OMP_ARRAY_SECTION
+			 || TREE_CODE (decl) == ARRAY_REF
+			 || TREE_CODE (decl) == COMPOUND_EXPR))
+		{
+		  if (TREE_CODE (decl) == COMPOUND_EXPR)
+		    decl = TREE_OPERAND (decl, 1);
+		  else
+		    {
+		      if (TREE_CODE (decl) == OMP_ARRAY_SECTION)
+			maybe_ptr_based_noncontig_update = true;
+		      decl = TREE_OPERAND (decl, 0);
+		      sections++;
+		    }
+		}
+
+	      decl = orig_decl;
+
 	      while (TREE_CODE (decl) == OMP_ARRAY_SECTION)
 		{
 		  tree low_bound = TREE_OPERAND (decl, 1);
@@ -13988,18 +14154,63 @@ c_parser_omp_variable_list (c_parser *parser,
 		  dims.safe_push (omp_dim (low_bound, length, stride, loc,
 					   false));
 		  decl = TREE_OPERAND (decl, 0);
+		  if (sections > 0)
+		    sections--;
 		}
 
+	      /* The handling of INDIRECT_REF here in the presence of
+		 array-shaping operations is a little tricky.  We need to
+		 avoid treating a pointer dereference as a unit-sized array
+		 section when we have an array shaping operation, because we
+		 don't want an indirection to consume one of the user's
+		 requested array dimensions.  E.g. if we have a
+		 double-indirect pointer like:
+
+		   int **foopp;
+		   #pragma omp target update from(([N][N]) (*foopp)[0:X][0:Y])
+
+		 We don't want to interpret this as:
+
+		   foopp[0:1][0:X][0:Y]
+
+		 else the array shape [N][N] won't match.  Also we can't match
+		 the array sections right-to-left instead, else this:
+
+		   #pragma omp target update from(([N][N]) (*foopp)[0:X])
+
+		 would not copy the dimensions:
+
+		   (*foopp)[0:X][0:N]
+
+		 as required.  So, avoid descending through INDIRECT_REFs if
+		 we have an array-shaping op.
+
+		 If we *don't* have an array-shaping op, but we have a
+		 multiply-indirected pointer and an array section like this:
+
+		   int ***fooppp;
+		   #pragma omp target update from((**fooppp)[0:X:S]
+
+		 also avoid descending through more indirections than we have
+		 array sections, since the noncontiguous update processing code
+		 won't understand them (and doesn't need to traverse them
+		 anyway).  */
+
 	      while (TREE_CODE (decl) == ARRAY_REF
-		     || TREE_CODE (decl) == INDIRECT_REF
+		     || (TREE_CODE (decl) == INDIRECT_REF
+			 && !reshaped_to)
 		     || TREE_CODE (decl) == COMPOUND_EXPR)
 		{
+		  if (maybe_ptr_based_noncontig_update && sections == 0)
+		    break;
+
 		  if (TREE_CODE (decl) == COMPOUND_EXPR)
 		    {
 		      decl = TREE_OPERAND (decl, 1);
 		      STRIP_NOPS (decl);
 		    }
-		  else if (TREE_CODE (decl) == INDIRECT_REF)
+		  else if (TREE_CODE (decl) == INDIRECT_REF
+			   && !reshaped_to)
 		    {
 		      dims.safe_push (omp_dim (integer_zero_node,
 					       integer_one_node, NULL_TREE, loc,
@@ -14012,6 +14223,35 @@ c_parser_omp_variable_list (c_parser *parser,
 		      dims.safe_push (omp_dim (index, integer_one_node,
 					       NULL_TREE, loc, true));
 		      decl = TREE_OPERAND (decl, 0);
+		      if (sections > 0)
+			sections--;
+		    }
+		}
+
+	      if (reshaped_to)
+		{
+		  unsigned reshaped_dims = 0;
+
+		  for (tree t = reshaped_to;
+		       TREE_CODE (t) == ARRAY_TYPE;
+		       t = TREE_TYPE (t))
+		    reshaped_dims++;
+
+		  if (dims.length () > reshaped_dims)
+		    {
+		      error_at (loc, "too many array section specifiers "
+				"for %qT", reshaped_to);
+		      decl = error_mark_node;
+		    }
+		  else
+		    {
+		      /* We have a pointer DECL whose target should be
+			 interpreted as an array with particular dimensions,
+			 not "the pointer itself".  So, add an indirection
+			 here.  */
+		      decl = build_indirect_ref (loc, decl, RO_UNARY_STAR);
+		      decl = build1_loc (loc, VIEW_CONVERT_EXPR, reshaped_to,
+					 decl);
 		    }
 		}
 
@@ -14039,6 +14279,14 @@ c_parser_omp_variable_list (c_parser *parser,
 	      decl = build_omp_array_section (loc, decl, idx, integer_one_node,
 					      NULL_TREE);
 	    }
+	  else if (reshaped_to)
+	    {
+	      /* We're copying the whole of a reshaped array, originally a
+		 base pointer.  Rewrite as an array section.  */
+	      tree elems = c_array_type_nelts_total (reshaped_to);
+	      decl = build_omp_array_section (loc, decl, size_zero_node, elems,
+					      NULL_TREE);
+	    }
 	  else if (TREE_CODE (decl) == NON_LVALUE_EXPR
 		   || CONVERT_EXPR_P (decl))
 	    decl = TREE_OPERAND (decl, 0);
@@ -17803,7 +18051,7 @@ c_parser_omp_clause_from_to (c_parser *parser, enum omp_clause_code kind,
       c_parser_consume_token (parser);
     }
 
-  tree nl = c_parser_omp_variable_list (parser, loc, kind, list);
+  tree nl = c_parser_omp_variable_list (parser, loc, kind, list, true);
   parens.skip_until_found_close (parser);
 
   if (present)
@@ -22240,8 +22488,38 @@ c_parser_omp_target_update (location_t loc, c_parser *parser,
   tree clauses
     = c_parser_omp_all_clauses (parser, OMP_TARGET_UPDATE_CLAUSE_MASK,
 				"#pragma omp target update");
-  if (omp_find_clause (clauses, OMP_CLAUSE_TO) == NULL_TREE
-      && omp_find_clause (clauses, OMP_CLAUSE_FROM) == NULL_TREE)
+  bool to_clause = false, from_clause = false;
+  for (tree c = clauses;
+       c && !to_clause && !from_clause;
+       c = OMP_CLAUSE_CHAIN (c))
+    {
+      switch (OMP_CLAUSE_CODE (c))
+       {
+       case OMP_CLAUSE_TO:
+	 to_clause = true;
+	 break;
+       case OMP_CLAUSE_FROM:
+	 from_clause = true;
+	 break;
+       case OMP_CLAUSE_MAP:
+	 switch (OMP_CLAUSE_MAP_KIND (c))
+	   {
+	   case GOMP_MAP_TO_GRID:
+	     to_clause = true;
+	     break;
+	   case GOMP_MAP_FROM_GRID:
+	     from_clause = true;
+	     break;
+	   default:
+	     ;
+	   }
+	 break;
+       default:
+	 ;
+       }
+    }
+
+  if (!to_clause && !from_clause)
     {
       error_at (loc,
 		"%<#pragma omp target update%> must contain at least one "
diff --git a/gcc/c/c-tree.h b/gcc/c/c-tree.h
index 10f1cf26dd28..25830711e55a 100644
--- a/gcc/c/c-tree.h
+++ b/gcc/c/c-tree.h
@@ -727,6 +727,8 @@ extern int in_sizeof;
 extern int in_typeof;
 extern bool c_in_omp_for;
 extern bool c_omp_array_section_p;
+extern bool c_omp_array_shaping_op_p;
+extern bool c_omp_has_array_shape_p;
 
 extern tree c_last_sizeof_arg;
 extern location_t c_last_sizeof_loc;
@@ -766,6 +768,8 @@ extern tree build_component_ref (location_t, tree, tree, location_t,
 				 location_t);
 extern tree build_array_ref (location_t, tree, tree);
 extern tree build_omp_array_section (location_t, tree, tree, tree, tree);
+extern tree create_omp_arrayshape_type (tree expr,
+					vec<tree> *omp_shape_dims);
 extern tree build_external_ref (location_t, tree, bool, tree *);
 extern void pop_maybe_used (bool);
 extern struct c_expr c_expr_sizeof_expr (location_t, struct c_expr);
diff --git a/gcc/c/c-typeck.cc b/gcc/c/c-typeck.cc
index c7409045abf3..79ca45401606 100644
--- a/gcc/c/c-typeck.cc
+++ b/gcc/c/c-typeck.cc
@@ -79,6 +79,13 @@ bool c_in_omp_for;
 /* True when parsing OpenMP map clause.  */
 bool c_omp_array_section_p;
 
+/* True when parsing OpenMP to/from clause.  */
+bool c_omp_array_shaping_op_p;
+
+/* True if we have an OpenMP array-shaping "cast" expression.  This adjusts
+   the parsed representation for e.g. array refs.  */
+bool c_omp_has_array_shape_p;
+
 /* The argument of last parsed sizeof expression, only to be tested
    if expr.original_code == SIZEOF_EXPR.  */
 tree c_last_sizeof_arg;
@@ -2939,6 +2946,46 @@ build_omp_array_section (location_t loc, tree array, tree index, tree length,
 		     stride);
 }
 
+/* Build an array type whose dimensions are given by OMP_SHAPE_DIMS and whose
+   elements are of the type pointed to by the "base" node of EXPR with outer
+   OMP_ARRAY_SECTIONs and ARRAY_REFs stripped off, e.g. the type of "*myptr"
+   in "myptr[0:2:3][4][5:6]".  */
+
+tree
+create_omp_arrayshape_type (tree expr, vec<tree> *omp_shape_dims)
+{
+  tree strip_sections = expr;
+
+  while (TREE_CODE (strip_sections) == OMP_ARRAY_SECTION
+	 || TREE_CODE (strip_sections) == ARRAY_REF)
+    strip_sections = TREE_OPERAND (strip_sections, 0);
+
+  tree type = TREE_TYPE (strip_sections);
+
+  if (TREE_CODE (type) == REFERENCE_TYPE)
+    type = TREE_TYPE (type);
+
+  if (TREE_CODE (type) != POINTER_TYPE)
+    {
+      error ("OpenMP array shaping operator with non-pointer argument");
+      return error_mark_node;
+    }
+
+  type = TREE_TYPE (type);
+
+  int i;
+  tree dim;
+  FOR_EACH_VEC_ELT_REVERSE (*omp_shape_dims, i, dim)
+    {
+      tree maxidx = fold_convert (sizetype, dim);
+      maxidx = size_binop (MINUS_EXPR, maxidx, size_one_node);
+      tree index = build_index_type (maxidx);
+      type = build_array_type (type, index);
+    }
+
+  return type;
+}
+
 \f
 /* Build an external reference to identifier ID.  FUN indicates
    whether this will be used for a function call.  LOC is the source
@@ -13696,7 +13743,7 @@ c_finish_omp_cancellation_point (location_t loc, tree clauses)
 static tree
 handle_omp_array_sections_1 (tree c, tree t, vec<tree> &types,
 			     bool &maybe_zero_len, unsigned int &first_non_one,
-			     enum c_omp_region_type ort)
+			     enum c_omp_region_type ort, int *discontiguous)
 {
   tree ret, low_bound, length, stride, type;
   bool openacc = (ort & C_ORT_ACC) != 0;
@@ -13776,11 +13823,15 @@ handle_omp_array_sections_1 (tree c, tree t, vec<tree> &types,
     }
 
   ret = handle_omp_array_sections_1 (c, TREE_OPERAND (t, 0), types,
-				     maybe_zero_len, first_non_one, ort);
+				     maybe_zero_len, first_non_one, ort,
+				     discontiguous);
   if (ret == error_mark_node || ret == NULL_TREE)
     return ret;
 
-  type = TREE_TYPE (ret);
+  if (TREE_CODE (ret) == OMP_ARRAY_SECTION)
+    type = TREE_TYPE (TREE_TYPE (TREE_OPERAND (ret, 0)));
+  else
+    type = TREE_TYPE (ret);
   low_bound = TREE_OPERAND (t, 1);
   length = TREE_OPERAND (t, 2);
   stride = TREE_OPERAND (t, 3);
@@ -13821,8 +13872,15 @@ handle_omp_array_sections_1 (tree c, tree t, vec<tree> &types,
       && TYPE_PRECISION (TREE_TYPE (length))
 	 > TYPE_PRECISION (sizetype))
     length = fold_convert (sizetype, length);
+  if (stride
+      && TREE_CODE (stride) == INTEGER_CST
+      && TYPE_PRECISION (TREE_TYPE (stride))
+	 > TYPE_PRECISION (sizetype))
+    stride = fold_convert (sizetype, stride);
   if (low_bound == NULL_TREE)
     low_bound = integer_zero_node;
+  if (stride == NULL_TREE)
+    stride = size_one_node;
   if (OMP_CLAUSE_CODE (c) == OMP_CLAUSE_MAP
       && (OMP_CLAUSE_MAP_KIND (c) == GOMP_MAP_ATTACH
 	  || OMP_CLAUSE_MAP_KIND (c) == GOMP_MAP_DETACH))
@@ -13941,12 +13999,29 @@ handle_omp_array_sections_1 (tree c, tree t, vec<tree> &types,
 	    }
 	  if (length && TREE_CODE (length) == INTEGER_CST)
 	    {
-	      if (tree_int_cst_lt (size, length))
+	      tree slength = length;
+	      if (stride && TREE_CODE (stride) == INTEGER_CST)
 		{
-		  error_at (OMP_CLAUSE_LOCATION (c),
-			    "length %qE above array section size "
-			    "in %qs clause", length,
-			    omp_clause_code_name[OMP_CLAUSE_CODE (c)]);
+		  slength = size_binop (MULT_EXPR,
+					fold_convert (sizetype, length),
+					fold_convert (sizetype, stride));
+		  slength = size_binop (MINUS_EXPR,
+					slength,
+					fold_convert (sizetype, stride));
+		  slength = size_binop (PLUS_EXPR, slength, size_one_node);
+		}
+	      if (tree_int_cst_lt (size, slength))
+		{
+		  if (stride && !integer_onep (stride))
+		    error_at (OMP_CLAUSE_LOCATION (c),
+			      "length %qE with stride %qE above array "
+			      "section size in %qs clause", length, stride,
+			      omp_clause_code_name[OMP_CLAUSE_CODE (c)]);
+		  else
+		    error_at (OMP_CLAUSE_LOCATION (c),
+			      "length %qE above array section size "
+			      "in %qs clause", length,
+			      omp_clause_code_name[OMP_CLAUSE_CODE (c)]);
 		  return error_mark_node;
 		}
 	      if (TREE_CODE (low_bound) == INTEGER_CST)
@@ -13954,7 +14029,7 @@ handle_omp_array_sections_1 (tree c, tree t, vec<tree> &types,
 		  tree lbpluslen
 		    = size_binop (PLUS_EXPR,
 				  fold_convert (sizetype, low_bound),
-				  fold_convert (sizetype, length));
+				  fold_convert (sizetype, slength));
 		  if (TREE_CODE (lbpluslen) == INTEGER_CST
 		      && tree_int_cst_lt (size, lbpluslen))
 		    {
@@ -14026,13 +14101,19 @@ handle_omp_array_sections_1 (tree c, tree t, vec<tree> &types,
 	    {
 	      tree d_length = TREE_OPERAND (d, 2);
 	      tree d_stride = TREE_OPERAND (d, 3);
-	      if (d_length == NULL_TREE || !integer_onep (d_length)
+	      if (d_length == NULL_TREE
+		  || !integer_onep (d_length)
 		  || (d_stride && !integer_onep (d_stride)))
 		{
-		  error_at (OMP_CLAUSE_LOCATION (c),
-			    "array section is not contiguous in %qs clause",
-			    omp_clause_code_name[OMP_CLAUSE_CODE (c)]);
-		  return error_mark_node;
+		  if (discontiguous && *discontiguous)
+		    *discontiguous = 2;
+		  else
+		    {
+		      error_at (OMP_CLAUSE_LOCATION (c),
+				"array section is not contiguous in %qs clause",
+				omp_clause_code_name[OMP_CLAUSE_CODE (c)]);
+		      return error_mark_node;
+		    }
 		}
 	    }
 	}
@@ -14044,7 +14125,7 @@ handle_omp_array_sections_1 (tree c, tree t, vec<tree> &types,
       return error_mark_node;
     }
   if (OMP_CLAUSE_CODE (c) != OMP_CLAUSE_DEPEND)
-    types.safe_push (TREE_TYPE (ret));
+    types.safe_push (type);
   /* We will need to evaluate lb more than once.  */
   tree lb = save_expr (low_bound);
   if (lb != low_bound)
@@ -14052,14 +14133,42 @@ handle_omp_array_sections_1 (tree c, tree t, vec<tree> &types,
       TREE_OPERAND (t, 1) = lb;
       low_bound = lb;
     }
-  ret = build_array_ref (OMP_CLAUSE_LOCATION (c), ret, low_bound);
+  /* NOTE: Stride/length are discarded for affinity/depend here.  */
+  if (discontiguous
+      && *discontiguous
+      && OMP_CLAUSE_CODE (c) != OMP_CLAUSE_AFFINITY
+      && OMP_CLAUSE_CODE (c) != OMP_CLAUSE_DEPEND)
+    ret = build_omp_array_section (OMP_CLAUSE_LOCATION (c), ret, low_bound,
+				   length, stride);
+  else
+    ret = build_array_ref (OMP_CLAUSE_LOCATION (c), ret, low_bound);
   return ret;
 }
 
-/* Handle array sections for clause C.  */
+/* We built a reference to an array section, but it turns out we only need a
+   set of ARRAY_REFs to the lower bound.  Rewrite the node.  */
+
+static tree
+omp_array_section_low_bound (location_t loc, tree node)
+{
+  if (TREE_CODE (node) == OMP_ARRAY_SECTION)
+    {
+      tree low_bound = TREE_OPERAND (node, 1);
+      tree ret = omp_array_section_low_bound (loc, TREE_OPERAND (node, 0));
+      return build_array_ref (loc, ret, low_bound);
+    }
+
+  return node;
+}
+
+/* Handle array sections for clause C.  On entry *DISCONTIGUOUS is 0 if array
+   section must be contiguous, 1 if it can be discontiguous, and in the latter
+   case it is set to 2 on exit if it is determined to be discontiguous during
+   the function's execution.  */
 
 static bool
-handle_omp_array_sections (tree *pc, enum c_omp_region_type ort)
+handle_omp_array_sections (tree *pc, enum c_omp_region_type ort,
+			   int *discontiguous)
 {
   tree c = *pc;
   bool maybe_zero_len = false;
@@ -14074,7 +14183,7 @@ handle_omp_array_sections (tree *pc, enum c_omp_region_type ort)
     tp = &TREE_VALUE (*tp);
   tree first = handle_omp_array_sections_1 (c, *tp, types,
 					    maybe_zero_len, first_non_one,
-					    ort);
+					    ort, discontiguous);
   if (first == error_mark_node)
     return true;
   if (first == NULL_TREE)
@@ -14112,11 +14221,14 @@ handle_omp_array_sections (tree *pc, enum c_omp_region_type ort)
       if (int_size_in_bytes (TREE_TYPE (first)) <= 0)
 	maybe_zero_len = true;
 
+      bool higher_discontiguous = false;
+
       for (i = num, t = OMP_CLAUSE_DECL (c); i > 0;
 	   t = TREE_OPERAND (t, 0))
 	{
 	  tree low_bound = TREE_OPERAND (t, 1);
 	  tree length = TREE_OPERAND (t, 2);
+	  tree stride = TREE_OPERAND (t, 3);
 
 	  i--;
 	  if (low_bound
@@ -14129,12 +14241,56 @@ handle_omp_array_sections (tree *pc, enum c_omp_region_type ort)
 	      && TYPE_PRECISION (TREE_TYPE (length))
 		 > TYPE_PRECISION (sizetype))
 	    length = fold_convert (sizetype, length);
+	  if (stride
+	      && TREE_CODE (stride) == INTEGER_CST
+	      && TYPE_PRECISION (TREE_TYPE (stride))
+		 > TYPE_PRECISION (sizetype))
+	    stride = fold_convert (sizetype, stride);
 	  if (low_bound == NULL_TREE)
 	    low_bound = integer_zero_node;
+	  if (stride == NULL_TREE)
+	    stride = size_one_node;
+	  if (discontiguous && *discontiguous)
+	    {
+	      /* This condition is similar to the error check below, but
+		 whereas that checks for a definitely-discontiguous array
+		 section in order to report an error (where such a section is
+		 illegal), here we instead need to know if the array section
+		 *may be* discontiguous so we can handle that case
+		 appropriately (i.e. for rectangular "target update"
+		 operations).  */
+	      bool full_span = false;
+	      if (length != NULL_TREE
+		  && TREE_CODE (length) == INTEGER_CST
+		  && TREE_CODE (types[i]) == ARRAY_TYPE
+		  && TYPE_DOMAIN (types[i])
+		  && TYPE_MAX_VALUE (TYPE_DOMAIN (types[i]))
+		  && TREE_CODE (TYPE_MAX_VALUE (TYPE_DOMAIN (types[i])))
+		     == INTEGER_CST)
+		{
+		  tree size;
+		  size = size_binop (PLUS_EXPR,
+				     TYPE_MAX_VALUE (TYPE_DOMAIN (types[i])),
+				     size_one_node);
+		  if (tree_int_cst_equal (length, size))
+		    full_span = true;
+		}
+
+	      if (!integer_onep (stride)
+		  || (higher_discontiguous
+		      && (!integer_zerop (low_bound)
+			  || !full_span)))
+		*discontiguous = 2;
+
+	      if (!integer_onep (stride)
+		  || !integer_zerop (low_bound)
+		  || !full_span)
+		higher_discontiguous = true;
+	    }
 	  if (!maybe_zero_len && i > first_non_one)
 	    {
 	      if (integer_nonzerop (low_bound))
-		goto do_warn_noncontiguous;
+		goto is_noncontiguous;
 	      if (length != NULL_TREE
 		  && TREE_CODE (length) == INTEGER_CST
 		  && TYPE_DOMAIN (types[i])
@@ -14148,12 +14304,17 @@ handle_omp_array_sections (tree *pc, enum c_omp_region_type ort)
 				     size_one_node);
 		  if (!tree_int_cst_equal (length, size))
 		    {
-		     do_warn_noncontiguous:
-		      error_at (OMP_CLAUSE_LOCATION (c),
-				"array section is not contiguous in %qs "
-				"clause",
-				omp_clause_code_name[OMP_CLAUSE_CODE (c)]);
-		      return true;
+		     is_noncontiguous:
+		      if (discontiguous && *discontiguous)
+			*discontiguous = 2;
+		      else
+			{
+			  error_at (OMP_CLAUSE_LOCATION (c),
+				    "array section is not contiguous in %qs "
+				    "clause",
+				    omp_clause_code_name[OMP_CLAUSE_CODE (c)]);
+			  return true;
+			}
 		    }
 		}
 	      if (length != NULL_TREE
@@ -14265,6 +14426,8 @@ handle_omp_array_sections (tree *pc, enum c_omp_region_type ort)
 	  OMP_CLAUSE_DECL (c) = t;
 	  return false;
 	}
+      if (discontiguous && *discontiguous != 2)
+	first = omp_array_section_low_bound (OMP_CLAUSE_LOCATION (c), first);
       first = c_fully_fold (first, false, NULL);
       OMP_CLAUSE_DECL (c) = first;
       if (OMP_CLAUSE_CODE (c) == OMP_CLAUSE_HAS_DEVICE_ADDR)
@@ -14273,7 +14436,8 @@ handle_omp_array_sections (tree *pc, enum c_omp_region_type ort)
 	size = c_fully_fold (size, false, NULL);
       OMP_CLAUSE_SIZE (c) = size;
 
-      if (OMP_CLAUSE_CODE (c) != OMP_CLAUSE_MAP)
+      if (OMP_CLAUSE_CODE (c) != OMP_CLAUSE_MAP
+	  && !(discontiguous && *discontiguous == 2))
 	return false;
 
       auto_vec<omp_addr_token *, 10> addr_tokens;
@@ -14286,7 +14450,8 @@ handle_omp_array_sections (tree *pc, enum c_omp_region_type ort)
       tree *npc = ai.expand_map_clause (pc, first, addr_tokens, ort);
       if (npc != NULL)
 	{
-	  if (ai.maybe_zero_length_array_section (c))
+	  if (OMP_CLAUSE_CODE (c) == OMP_CLAUSE_MAP
+	      && ai.maybe_zero_length_array_section (c))
 	    OMP_CLAUSE_MAP_MAYBE_ZERO_LENGTH_ARRAY_SECTION (c) = 1;
 
 	  return false;
@@ -14633,7 +14798,7 @@ c_finish_omp_clauses (tree clauses, enum c_omp_region_type ort)
 	  t = OMP_CLAUSE_DECL (c);
 	  if (TREE_CODE (t) == OMP_ARRAY_SECTION)
 	    {
-	      if (handle_omp_array_sections (pc, ort))
+	      if (handle_omp_array_sections (pc, ort, NULL))
 		{
 		  remove = true;
 		  break;
@@ -15255,7 +15420,7 @@ c_finish_omp_clauses (tree clauses, enum c_omp_region_type ort)
 	    last_iterators = NULL_TREE;
 	  if (TREE_CODE (t) == OMP_ARRAY_SECTION)
 	    {
-	      if (handle_omp_array_sections (pc, ort))
+	      if (handle_omp_array_sections (pc, ort, NULL))
 		remove = true;
 	      else if ((c = *pc)
 		       && OMP_CLAUSE_CODE (c) == OMP_CLAUSE_DEPEND
@@ -15362,6 +15527,9 @@ c_finish_omp_clauses (tree clauses, enum c_omp_region_type ort)
 	      remove = true;
 	      break;
 	    }
+	  if (OMP_CLAUSE_MAP_KIND (c) == GOMP_MAP_GRID_DIM
+	      || OMP_CLAUSE_MAP_KIND (c) == GOMP_MAP_GRID_STRIDE)
+	    break;
 	  /* FALLTHRU */
 	case OMP_CLAUSE_TO:
 	case OMP_CLAUSE_FROM:
@@ -15376,7 +15544,10 @@ c_finish_omp_clauses (tree clauses, enum c_omp_region_type ort)
 		grp_start_p = pc;
 		grp_sentinel = OMP_CLAUSE_CHAIN (c);
 
-		if (handle_omp_array_sections (pc, ort))
+		int discontiguous
+		  = (OMP_CLAUSE_CODE (c) == OMP_CLAUSE_TO
+		     || OMP_CLAUSE_CODE (c) == OMP_CLAUSE_FROM);
+		if (handle_omp_array_sections (pc, ort, &discontiguous))
 		  remove = true;
 		else
 		  {
@@ -15773,7 +15944,7 @@ c_finish_omp_clauses (tree clauses, enum c_omp_region_type ort)
 	  t = OMP_CLAUSE_DECL (c);
 	  if (TREE_CODE (t) == OMP_ARRAY_SECTION)
 	    {
-	      if (handle_omp_array_sections (pc, ort))
+	      if (handle_omp_array_sections (pc, ort, NULL))
 		remove = true;
 	      else
 		{
diff --git a/gcc/testsuite/gcc.dg/gomp/bad-array-shaping-c-1.c b/gcc/testsuite/gcc.dg/gomp/bad-array-shaping-c-1.c
new file mode 100644
index 000000000000..42d584fa6240
--- /dev/null
+++ b/gcc/testsuite/gcc.dg/gomp/bad-array-shaping-c-1.c
@@ -0,0 +1,26 @@
+// { dg-do compile }
+
+#include <string.h>
+#include <assert.h>
+#include <stdlib.h>
+
+int main (void)
+{
+  float *arr = calloc (100, sizeof (float));
+
+#pragma omp target enter data map(to: arr[:100])
+
+  for (int j = 0; j < 10; j++)
+    for (int i = 0; i < 10; i++)
+      arr[j * 10 + i] = i + j * 3;
+
+#pragma omp target update to(([10][10]) arr[3:2][1:8][0:5])
+// { dg-error "too many array section specifiers for" "" { target *-*-* } .-1 }
+// { dg-error "'#pragma omp target update' must contain at least one 'from' or 'to' clauses" "" { target *-*-* } .-2 }
+
+#pragma omp target exit data map(from: arr[:100])
+
+  free (arr);
+
+  return 0;
+}
diff --git a/gcc/testsuite/gcc.dg/gomp/bad-array-shaping-c-2.c b/gcc/testsuite/gcc.dg/gomp/bad-array-shaping-c-2.c
new file mode 100644
index 000000000000..6be3e009ecb1
--- /dev/null
+++ b/gcc/testsuite/gcc.dg/gomp/bad-array-shaping-c-2.c
@@ -0,0 +1,24 @@
+// { dg-do compile }
+
+#include <string.h>
+#include <assert.h>
+#include <stdlib.h>
+
+int main (void)
+{
+  float *arr = calloc (100, sizeof (float));
+
+  /* This isn't allowed.  */
+#pragma omp target enter data map(to: ([10][10]) arr[:100])
+/* { dg-error {expected expression before '\[' token} "" { target *-*-* } .-1 } */
+/* { dg-error {'#pragma omp target enter data' must contain at least one 'map' clause} "" { target *-*-* } .-2 } */
+
+  /* Nor this.  */
+#pragma omp target exit data map(from: ([10][10]) arr[:100])
+/* { dg-error {expected expression before '\[' token} "" { target *-*-* } .-1 } */
+/* { dg-error {'#pragma omp target exit data' must contain at least one 'map' clause} "" { target *-*-* } .-2 } */
+
+  free (arr);
+
+  return 0;
+}
diff --git a/gcc/testsuite/gcc.dg/gomp/bad-array-shaping-c-3.c b/gcc/testsuite/gcc.dg/gomp/bad-array-shaping-c-3.c
new file mode 100644
index 000000000000..1715b8ff9edd
--- /dev/null
+++ b/gcc/testsuite/gcc.dg/gomp/bad-array-shaping-c-3.c
@@ -0,0 +1,30 @@
+// { dg-do compile }
+
+#include <string.h>
+#include <assert.h>
+#include <stdlib.h>
+
+extern float* baz(void*);
+
+int main (void)
+{
+  float *arr = calloc (100, sizeof (float));
+  int c = 50;
+
+#pragma omp target enter data map(to: arr[:100])
+
+  for (int j = 0; j < 10; j++)
+    for (int i = 0; i < 10; i++)
+      arr[j * 10 + i] = i + j * 3;
+
+  /* No array shaping inside a function call.  */
+#pragma omp target update to(baz(([10][10]) arr))
+/* { dg-error {expected expression before '\[' token} "" { target *-*-* } .-1 } */
+/* { dg-error {'#pragma omp target update' must contain at least one 'from' or 'to' clauses} "" { target *-*-* } .-2 } */
+
+#pragma omp target exit data map(from: arr[:100])
+
+  free (arr);
+
+  return 0;
+}
diff --git a/gcc/testsuite/gcc.dg/gomp/bad-array-shaping-c-4.c b/gcc/testsuite/gcc.dg/gomp/bad-array-shaping-c-4.c
new file mode 100644
index 000000000000..cebefd36d189
--- /dev/null
+++ b/gcc/testsuite/gcc.dg/gomp/bad-array-shaping-c-4.c
@@ -0,0 +1,27 @@
+// { dg-do compile }
+
+#include <string.h>
+#include <assert.h>
+#include <stdlib.h>
+
+int main (void)
+{
+  float *arr = calloc (100, sizeof (float));
+
+#pragma omp target enter data map(to: arr[:100])
+
+  for (int j = 0; j < 10; j++)
+    for (int i = 0; i < 10; i++)
+      arr[j * 10 + i] = i + j * 3;
+
+  /* No array shaping inside a statement expression.  */
+#pragma omp target update to( ({ int d = 10; ([d][d]) arr; }) )
+/* { dg-error {expected expression before '\[' token} "" { target *-*-* } .-1 } */
+/* { dg-error {'#pragma omp target update' must contain at least one 'from' or 'to' clauses} "" { target *-*-* } .-2 } */
+
+#pragma omp target exit data map(from: arr[:100])
+
+  free (arr);
+
+  return 0;
+}
diff --git a/gcc/testsuite/gcc.dg/gomp/bad-array-shaping-c-5.c b/gcc/testsuite/gcc.dg/gomp/bad-array-shaping-c-5.c
new file mode 100644
index 000000000000..e1c4991f5c34
--- /dev/null
+++ b/gcc/testsuite/gcc.dg/gomp/bad-array-shaping-c-5.c
@@ -0,0 +1,17 @@
+// { dg-do compile }
+
+struct S {
+  void *pp;
+};
+
+int main()
+{
+  int *sub1;
+
+  /* No array section inside compound literal.  */
+#pragma omp target update to( (struct S) { .pp = ([10][10]) sub1 } )
+/* { dg-error {expected expression before '\[' token} "" { target *-*-* } .-1 } */
+/* { dg-error {'#pragma omp target update' must contain at least one 'from' or 'to' clauses} "" { target *-*-* } .-2 } */
+
+  return 0;
+}
diff --git a/gcc/testsuite/gcc.dg/gomp/bad-array-shaping-c-6.c b/gcc/testsuite/gcc.dg/gomp/bad-array-shaping-c-6.c
new file mode 100644
index 000000000000..d282d8598b22
--- /dev/null
+++ b/gcc/testsuite/gcc.dg/gomp/bad-array-shaping-c-6.c
@@ -0,0 +1,26 @@
+// { dg-do compile }
+
+int main (void)
+{
+  char *ptr;
+
+#pragma omp target update to(([5][6][7]) ptr[0:4][0:7][0:7])
+/* { dg-error {length '7' above array section size in 'to' clause} "" { target *-*-* } .-1 } */
+/* { dg-error {'#pragma omp target update' must contain at least one 'from' or 'to' clauses} "" { target *-*-* } .-2 } */
+
+#pragma omp target update to(([5][6][7]) ptr[1:5][0:6][0:7])
+/* { dg-error {high bound '6' above array section size in 'to' clause} "" { target *-*-* } .-1 } */
+/* { dg-error {'#pragma omp target update' must contain at least one 'from' or 'to' clauses} "" { target *-*-* } .-2 } */
+
+#pragma omp target update from(([100]) ptr[3:33:3])
+
+#pragma omp target update from(([100]) ptr[4:33:3])
+/* { dg-error {high bound '101' above array section size in 'from' clause} "" { target *-*-* } .-1 } */
+/* { dg-error {'#pragma omp target update' must contain at least one 'from' or 'to' clauses} "" { target *-*-* } .-2 } */
+
+#pragma omp target update to(([10][10]) ptr[0:9:-1][0:9])
+/* { dg-error {length '9' with stride '-1' above array section size in 'to' clause} "" { target *-*-* } .-1 } */
+/* { dg-error {'#pragma omp target update' must contain at least one 'from' or 'to' clauses} "" { target *-*-* } .-2 } */
+
+  return 0;
+}
diff --git a/gcc/testsuite/gcc.dg/gomp/bad-array-shaping-c-7.c b/gcc/testsuite/gcc.dg/gomp/bad-array-shaping-c-7.c
new file mode 100644
index 000000000000..233d8da6f445
--- /dev/null
+++ b/gcc/testsuite/gcc.dg/gomp/bad-array-shaping-c-7.c
@@ -0,0 +1,15 @@
+/* { dg-do compile } */
+
+int cond;
+
+int main (void)
+{
+  int *arr;
+
+  /* No array shaping inside conditional operator.  */
+#pragma omp target update to(cond ? ([3][9]) arr : ([2][7]) arr)
+/* { dg-error {expected expression before '\[' token} "" { target *-*-* } .-1 } */
+/* { dg-error {'#pragma omp target update' must contain at least one 'from' or 'to' clauses} "" { target *-*-* } .-2 } */
+
+  return 0;
+}
diff --git a/libgomp/testsuite/libgomp.c/array-shaping-1.c b/libgomp/testsuite/libgomp.c/array-shaping-1.c
new file mode 100644
index 000000000000..808c5f9ceae6
--- /dev/null
+++ b/libgomp/testsuite/libgomp.c/array-shaping-1.c
@@ -0,0 +1,236 @@
+// { dg-do run { target offload_device_nonshared_as } }
+
+#include <string.h>
+#include <assert.h>
+#include <stdlib.h>
+
+volatile int yy = 4, zz = 2, str_str = 2;
+
+int main()
+{
+  int *arr;
+  int x = 5;
+  int arr2d[10][10];
+
+  arr = calloc (100, sizeof (int));
+
+  /* Update whole reshaped array.  */
+
+#pragma omp target enter data map(to: arr[:100])
+
+  for (int j = 0; j < x; j++)
+    for (int i = 0; i < 10; i++)
+      arr[j * 10 + i] = i ^ j;
+
+#pragma omp target update to(([10][x]) arr)
+
+#pragma omp target exit data map(from: arr[:100])
+
+  for (int j = 0; j < 10; j++)
+    for (int i = 0; i < 10; i++)
+      if (j < x)
+	assert (arr[j * 10 + i] == i ^ j);
+      else
+	assert (arr[j * 10 + i] == 0);
+
+
+  /* Strided update.  */
+
+  memset (arr, 0, 100 * sizeof (int));
+
+#pragma omp target enter data map(to: arr[:100])
+
+  for (int j = 0; j < 20; j++)
+    for (int i = 0; i < 5; i++)
+      arr[j * 5 + i] = i + j;
+
+#pragma omp target update to(([5][5]) arr[0:3][0:3:2])
+
+#pragma omp target exit data map(from: arr[:100])
+
+  for (int j = 0; j < 20; j++)
+    for (int i = 0; i < 5; i++)
+      if (j < 3 && (i & 1) == 0 && i < 6)
+	assert (arr[j * 5 + i] == i + j);
+      else
+	assert (arr[j * 5 + i] == 0);
+
+
+  /* Reshaped update, contiguous.  */
+
+  memset (arr, 0, 100 * sizeof (int));
+
+#pragma omp target enter data map(to: arr[:100])
+
+  for (int j = 0; j < 20; j++)
+    for (int i = 0; i < 5; i++)
+      arr[j * 5 + i] = 2 * j + i;
+
+#pragma omp target update to(([5][5]) arr[0:5][0:5])
+
+#pragma omp target exit data map(from: arr[:100])
+
+  for (int j = 0; j < 20; j++)
+    for (int i = 0; i < 5; i++)
+      if (j < 5 && i < 5)
+	assert (arr[j * 5 + i] == 2 * j + i);
+      else
+	assert (arr[j * 5 + i] == 0);
+
+
+  /* Strided update on actual array.  */
+
+  memset (arr2d, 0, 100 * sizeof (int));
+
+#pragma omp target enter data map(to: arr2d)
+
+  for (int j = 0; j < 10; j++)
+    for (int i = 0; i < 10; i++)
+      arr2d[j][i] = j + 2 * i;
+
+#pragma omp target update to(arr2d[0:5:2][5:2])
+
+#pragma omp target exit data map(from: arr2d)
+
+  for (int j = 0; j < 10; j++)
+    for (int i = 0; i < 10; i++)
+      if ((j & 1) == 0 && i >= 5 && i < 7)
+	assert (arr2d[j][i] == j + 2 * i);
+      else
+	assert (arr2d[j][i] == 0);
+
+
+  /* Update with non-constant bounds.  */
+
+  memset (arr, 0, 100 * sizeof (int));
+
+#pragma omp target enter data map(to: arr[:100])
+
+  for (int j = 0; j < 10; j++)
+    for (int i = 0; i < 10; i++)
+      arr[j * 10 + i] = (2 * j) ^ i;
+
+  x = 3;
+  int y = yy, z = zz, str = str_str;
+  /* This is actually [0:3:2] [4:2:2].  */
+#pragma omp target update to(([10][10]) arr[0:x:2][y:z:str])
+
+#pragma omp target exit data map(from: arr[:100])
+
+  for (int j = 0; j < 10; j++)
+    for (int i = 0; i < 10; i++)
+      if ((j & 1) == 0 && j < 6 && (i & 1) == 0 && i >= 4 && i < 8)
+	assert (arr[j * 10 + i] == (2 * j) ^ i);
+      else
+	assert (arr[j * 10 + i] == 0);
+
+
+  /* Update with full "major" dimension.  */
+
+  memset (arr, 0, 100 * sizeof (int));
+
+#pragma omp target enter data map(to: arr[:100])
+
+  for (int j = 0; j < 10; j++)
+    for (int i = 0; i < 10; i++)
+      arr[j * 10 + i] = i + j;
+
+#pragma omp target update to(([10][10]) arr[0:10][3:1])
+
+#pragma omp target exit data map(from: arr[:100])
+
+  for (int j = 0; j < 10; j++)
+    for (int i = 0; i < 10; i++)
+      if (i == 3)
+	assert (arr[j * 10 + i] == i + j);
+      else
+	assert (arr[j * 10 + i] == 0);
+
+
+  /* Update with full "minor" dimension.  */
+
+  memset (arr, 0, 100 * sizeof (int));
+
+#pragma omp target enter data map(to: arr[:100])
+
+  for (int j = 0; j < 10; j++)
+    for (int i = 0; i < 10; i++)
+      arr[j * 10 + i] = 3 * (i + j);
+
+#pragma omp target update to(([10][10]) arr[3:2][0:10])
+
+#pragma omp target exit data map(from: arr[:100])
+
+  for (int j = 0; j < 10; j++)
+    for (int i = 0; i < 10; i++)
+      if (j >= 3 && j < 5)
+	assert (arr[j * 10 + i] == 3 * (i + j));
+      else
+	assert (arr[j * 10 + i] == 0);
+
+
+  /* Rectangle update.  */
+
+  memset (arr, 0, 100 * sizeof (int));
+
+#pragma omp target enter data map(to: arr[:100])
+
+  for (int j = 0; j < 10; j++)
+    for (int i = 0; i < 10; i++)
+      arr[j * 10 + i] = 5 * (i + j);
+
+#pragma omp target update to(([10][10]) arr[3:2][0:9])
+
+#pragma omp target exit data map(from: arr[:100])
+
+  for (int j = 0; j < 10; j++)
+    for (int i = 0; i < 10; i++)
+      if (j >= 3 && j < 5 && i < 9)
+	assert (arr[j * 10 + i] == 5 * (i + j));
+      else
+	assert (arr[j * 10 + i] == 0);
+
+
+  /* One-dimensional strided update.  */
+
+  memset (arr, 0, 100 * sizeof (int));
+
+#pragma omp target enter data map(to: arr[:100])
+
+  for (int i = 0; i < 100; i++)
+    arr[i] = i + 99;
+
+#pragma omp target update to(([100]) arr[3:33:3])
+
+#pragma omp target exit data map(from: arr[:100])
+
+  for (int i = 0; i < 100; i++)
+    if (i >= 3 && ((i - 3) % 3) == 0)
+      assert (arr[i] == i + 99);
+    else
+      assert (arr[i] == 0);
+
+
+  /* One-dimensional strided update without explicit array shape.  */
+
+  memset (arr, 0, 100 * sizeof (int));
+
+#pragma omp target enter data map(to: arr[:100])
+
+  for (int i = 0; i < 100; i++)
+    arr[i] = i + 121;
+
+#pragma omp target update to(arr[3:33:3])
+
+#pragma omp target exit data map(from: arr[:100])
+
+  for (int i = 0; i < 100; i++)
+    if (i >= 3 && ((i - 3) % 3) == 0)
+      assert (arr[i] == i + 121);
+    else
+      assert (arr[i] == 0);
+
+  free (arr);
+
+  return 0;
+}
diff --git a/libgomp/testsuite/libgomp.c/array-shaping-2.c b/libgomp/testsuite/libgomp.c/array-shaping-2.c
new file mode 100644
index 000000000000..42a6e0ca7d82
--- /dev/null
+++ b/libgomp/testsuite/libgomp.c/array-shaping-2.c
@@ -0,0 +1,39 @@
+// { dg-do run { target offload_device_nonshared_as } }
+
+#include <assert.h>
+#include <stdlib.h>
+
+typedef struct {
+  int *aptr;
+} C;
+
+int main()
+{
+  C cvar;
+
+  cvar.aptr = calloc (100, sizeof (float));
+
+#pragma omp target enter data map(to: cvar.aptr, cvar.aptr[:100])
+
+#pragma omp target
+  {
+    for (int i = 0; i < 10; i++)
+      for (int j = 0; j < 10; j++)
+	cvar.aptr[i * 10 + j] = i + j;
+  }
+
+#pragma omp target update from(([10][10]) cvar.aptr[4:3][4:3])
+
+  for (int i = 0; i < 10; i++)
+    for (int j = 0; j < 10; j++)
+      if (i >= 4 && i < 7 && j >= 4 && j < 7)
+	assert (cvar.aptr[i * 10 + j] == i + j);
+      else
+	assert (cvar.aptr[i * 10 + j] == 0);
+
+#pragma omp target exit data map(delete: cvar.aptr, cvar.aptr[:100])
+
+  free (cvar.aptr);
+
+  return 0;
+}
diff --git a/libgomp/testsuite/libgomp.c/array-shaping-3.c b/libgomp/testsuite/libgomp.c/array-shaping-3.c
new file mode 100644
index 000000000000..5dda2e328328
--- /dev/null
+++ b/libgomp/testsuite/libgomp.c/array-shaping-3.c
@@ -0,0 +1,42 @@
+// { dg-do run { target offload_device_nonshared_as } }
+
+#include <assert.h>
+#include <stdlib.h>
+#include <string.h>
+
+#define N 10
+
+typedef struct {
+  int arr[N][N];
+} B;
+
+int main()
+{
+  B *bvar = malloc (sizeof (B));
+
+  memset (bvar, 0, sizeof (B));
+
+#pragma omp target enter data map(to: bvar->arr)
+
+#pragma omp target
+  {
+    for (int i = 0; i < 10; i++)
+      for (int j = 0; j < 10; j++)
+	bvar->arr[i][j] = i + j;
+  }
+
+#pragma omp target update from(bvar->arr[4:3][4:3])
+
+  for (int i = 0; i < 10; i++)
+    for (int j = 0; j < 10; j++)
+      if (i >= 4 && i < 7 && j >= 4 && j < 7)
+	assert (bvar->arr[i][j] == i + j);
+      else
+	assert (bvar->arr[i][j] == 0);
+
+#pragma omp target exit data map(delete: bvar->arr)
+
+  free (bvar);
+
+  return 0;
+}
diff --git a/libgomp/testsuite/libgomp.c/array-shaping-4.c b/libgomp/testsuite/libgomp.c/array-shaping-4.c
new file mode 100644
index 000000000000..2b9e6949b602
--- /dev/null
+++ b/libgomp/testsuite/libgomp.c/array-shaping-4.c
@@ -0,0 +1,36 @@
+// { dg-do run { target offload_device_nonshared_as } }
+
+#include <assert.h>
+#include <string.h>
+
+#define N 10
+
+int main ()
+{
+  int iarr[N * N];
+
+  memset (iarr, 0, N * N * sizeof (int));
+
+#pragma omp target enter data map(to: iarr)
+
+#pragma omp target
+  {
+    for (int i = 0; i < 10; i++)
+      for (int j = 0; j < 10; j++)
+	iarr[i * 10 + j] = i + j;
+  }
+
+  /* An array, but cast to a pointer, then reshaped.  */
+#pragma omp target update from(([10][10]) ((int *) &iarr[0])[4:3][4:3])
+
+  for (int i = 0; i < 10; i++)
+    for (int j = 0; j < 10; j++)
+      if (i >= 4 && i < 7 && j >= 4 && j < 7)
+	assert (iarr[i * 10 + j] == i + j);
+      else
+	assert (iarr[i * 10 + j] == 0);
+
+#pragma omp target exit data map(delete: iarr)
+
+  return 0;
+}
diff --git a/libgomp/testsuite/libgomp.c/array-shaping-5.c b/libgomp/testsuite/libgomp.c/array-shaping-5.c
new file mode 100644
index 000000000000..1034682e4ca2
--- /dev/null
+++ b/libgomp/testsuite/libgomp.c/array-shaping-5.c
@@ -0,0 +1,38 @@
+// { dg-do run { target offload_device_nonshared_as } }
+
+#include <assert.h>
+#include <string.h>
+
+#define N 10
+
+int main ()
+{
+  int iarr_real[N * N];
+  int *iarrp = &iarr_real[0];
+  int **iarrpp = &iarrp;
+
+  memset (iarrp, 0, N * N * sizeof (int));
+
+#pragma omp target enter data map(to: iarr_real)
+
+#pragma omp target
+  {
+    for (int i = 0; i < 10; i++)
+      for (int j = 0; j < 10; j++)
+	iarrp[i * 10 + j] = i + j;
+  }
+
+  /* A pointer with an extra indirection.  */
+#pragma omp target update from(([10][10]) (*iarrpp)[4:3][4:3])
+
+  for (int i = 0; i < 10; i++)
+    for (int j = 0; j < 10; j++)
+      if (i >= 4 && i < 7 && j >= 4 && j < 7)
+	assert (iarrp[i * 10 + j] == i + j);
+      else
+	assert (iarrp[i * 10 + j] == 0);
+
+#pragma omp target exit data map(delete: iarr_real)
+
+  return 0;
+}
diff --git a/libgomp/testsuite/libgomp.c/array-shaping-6.c b/libgomp/testsuite/libgomp.c/array-shaping-6.c
new file mode 100644
index 000000000000..593882322443
--- /dev/null
+++ b/libgomp/testsuite/libgomp.c/array-shaping-6.c
@@ -0,0 +1,45 @@
+// { dg-do run { target offload_device_nonshared_as } }
+
+#include <assert.h>
+#include <stdlib.h>
+#include <string.h>
+
+#define N 10
+
+int main ()
+{
+  int *iptr = calloc (N * N * N, sizeof (int));
+
+#pragma omp target enter data map(to: iptr[0:N*N*N])
+
+#pragma omp target
+  {
+    for (int i = 0; i < N; i++)
+      for (int j = 0; j < N; j++)
+	iptr[i * N * N + 4 * N + j] = i + j;
+  }
+
+  /* An array ref between two array sections.  */
+#pragma omp target update from(([N][N][N]) iptr[2:3][4][6:3])
+
+  for (int i = 2; i < 5; i++)
+    for (int j = 6; j < 9; j++)
+      assert (iptr[i * N * N + 4 * N + j] == i + j);
+
+  memset (iptr, 0, N * N * N * sizeof (int));
+
+  for (int i = 0; i < N; i++)
+    iptr[2 * N * N + i * N + 4] = 3 * i;
+
+  /* Array section between two array refs.  */
+#pragma omp target update to(([N][N][N]) iptr[2][3:6][4])
+
+#pragma omp target exit data map(from: iptr[0:N*N*N])
+
+  for (int i = 3; i < 9; i++)
+    assert (iptr[2 * N * N + i * N + 4] == 3 * i);
+
+  free (iptr);
+
+  return 0;
+}
-- 
2.41.0


^ permalink raw reply	[flat|nested] 10+ messages in thread

* [PATCH 5/5] OpenMP: Noncontiguous "target update" for Fortran
  2023-09-06  9:34 [PATCH 0/5] OpenMP: Array-shaping operator and strided/rectangular 'target update' support Julian Brown
                   ` (3 preceding siblings ...)
  2023-09-06  9:34 ` [PATCH 4/5] OpenMP: Array shaping operator and strided "target update" for C Julian Brown
@ 2023-09-06  9:34 ` Julian Brown
  4 siblings, 0 replies; 10+ messages in thread
From: Julian Brown @ 2023-09-06  9:34 UTC (permalink / raw)
  To: gcc-patches; +Cc: fortran, tobias, jakub

This patch implements noncontiguous "target update" for Fortran.
The existing middle end/runtime bits relating to C and C++ support are
reused, with some small adjustments, e.g.:

  1. The node used to map the OMP "array descriptor" (from omp-low.cc
     onwards) now uses the OMP_CLAUSE_SIZE field as a bias (the difference
     between the "virtual origin" element with zero indices in each
     dimension and the first element actually stored in memory).

  2. The OMP_CLAUSE_SIZE field of a GOMP_MAP_DIM_STRIDE node may now be
     used to store a "span", which is the distance in bytes between
     two adjacent elements in an array (with unit stride) when that is
     different from the element size, as it can be in Fortran.

The implementation goes to some effort to massage Fortran array metadata
(array descriptors) into a form that can ultimately be consumed by
omp_target_memcpy_rect_worker. The method for doing this is described
in comments in the patch body.

This version of the patch has been rebased and contains some additional
minor fixes relative to the version posted for the og13 branch.

2023-09-05  Julian Brown  <julian@codesourcery.com>

gcc/fortran/
	* openmp.cc (omp_verify_map_motion_clauses): Allow strided array
	sections with 'omp target update'.
	* trans-openmp.cc (gfc_trans_omp_arrayshape_type, gfc_omp_calculate_gcd,
	gfc_desc_to_omp_noncontig_array, gfc_omp_contiguous_update_p): New
	functions.
	(gfc_trans_omp_clauses): Handle noncontiguous to/from clauses for OMP
	"target update" directives.

gcc/
	* gimplify.cc (gimplify_adjust_omp_clauses): Don't gimplify
	VIEW_CONVERT_EXPR away in GOMP_MAP_TO_GRID/GOMP_MAP_FROM_GRID clauses.
	* omp-low.cc (omp_noncontig_descriptor_type): Add SPAN field.
	(scan_sharing_clauses): Don't store descriptor size in its
	OMP_CLAUSE_SIZE field.
	(lower_omp_target): Add missing OMP_CLAUSE_MAP check.  Add special-case
	string handling.  Handle span and bias.  Use low bound instead of zero
	as index for trailing full dimensions.

gcc/testsuite/
	* gfortran.dg/gomp/noncontig-updates-1.f90: New test.
	* gfortran.dg/gomp/noncontig-updates-2.f90: New test.
	* gfortran.dg/gomp/noncontig-updates-3.f90: New test.
	* gfortran.dg/gomp/noncontig-updates-4.f90: New test.

libgomp/
	* libgomp.h (omp_noncontig_array_desc): Add span field.
	* target.c (omp_target_memcpy_rect_worker): Add span parameter. Update
	forward declaration. Handle span != element_size.
	(gomp_update): Handle bias in descriptor's size slot.  Update calls to
	omp_target_memcpy_rect_worker.
	(omp_target_memcpy_rect_worker): Add element_size == span checks (to
	existing stride == 1 check) to guard use of target plugin's 2D/3D
	memcpy hooks.
	* testsuite/libgomp.fortran/noncontig-updates-1.f90: New test.
	* testsuite/libgomp.fortran/noncontig-updates-2.f90: New test.
	* testsuite/libgomp.fortran/noncontig-updates-3.f90: New test.
	* testsuite/libgomp.fortran/noncontig-updates-4.f90: New test.
	* testsuite/libgomp.fortran/noncontig-updates-5.f90: New test.
	* testsuite/libgomp.fortran/noncontig-updates-6.f90: New test.
	* testsuite/libgomp.fortran/noncontig-updates-7.f90: New test.
	* testsuite/libgomp.fortran/noncontig-updates-8.f90: New test.
	* testsuite/libgomp.fortran/noncontig-updates-9.f90: New test.
	* testsuite/libgomp.fortran/noncontig-updates-10.f90: New test.
	* testsuite/libgomp.fortran/noncontig-updates-11.f90: New test.
	* testsuite/libgomp.fortran/noncontig-updates-12.f90: New test.
	* testsuite/libgomp.fortran/noncontig-updates-13.f90: New test.
---
 gcc/fortran/openmp.cc                         |   5 +-
 gcc/fortran/trans-openmp.cc                   | 499 ++++++++++++++++++
 gcc/gimplify.cc                               |  10 +
 gcc/omp-low.cc                                |  53 +-
 .../gfortran.dg/gomp/noncontig-updates-1.f90  |  19 +
 .../gfortran.dg/gomp/noncontig-updates-2.f90  |  16 +
 .../gfortran.dg/gomp/noncontig-updates-3.f90  |  16 +
 .../gfortran.dg/gomp/noncontig-updates-4.f90  |  15 +
 libgomp/libgomp.h                             |   1 +
 libgomp/target.c                              |  57 +-
 .../libgomp.fortran/noncontig-updates-1.f90   |  54 ++
 .../libgomp.fortran/noncontig-updates-10.f90  |  29 +
 .../libgomp.fortran/noncontig-updates-11.f90  |  51 ++
 .../libgomp.fortran/noncontig-updates-12.f90  |  59 +++
 .../libgomp.fortran/noncontig-updates-13.f90  |  42 ++
 .../libgomp.fortran/noncontig-updates-2.f90   | 101 ++++
 .../libgomp.fortran/noncontig-updates-3.f90   |  47 ++
 .../libgomp.fortran/noncontig-updates-4.f90   |  78 +++
 .../libgomp.fortran/noncontig-updates-5.f90   |  55 ++
 .../libgomp.fortran/noncontig-updates-6.f90   |  34 ++
 .../libgomp.fortran/noncontig-updates-7.f90   |  36 ++
 .../libgomp.fortran/noncontig-updates-8.f90   |  39 ++
 .../libgomp.fortran/noncontig-updates-9.f90   |  34 ++
 23 files changed, 1311 insertions(+), 39 deletions(-)
 create mode 100644 gcc/testsuite/gfortran.dg/gomp/noncontig-updates-1.f90
 create mode 100644 gcc/testsuite/gfortran.dg/gomp/noncontig-updates-2.f90
 create mode 100644 gcc/testsuite/gfortran.dg/gomp/noncontig-updates-3.f90
 create mode 100644 gcc/testsuite/gfortran.dg/gomp/noncontig-updates-4.f90
 create mode 100644 libgomp/testsuite/libgomp.fortran/noncontig-updates-1.f90
 create mode 100644 libgomp/testsuite/libgomp.fortran/noncontig-updates-10.f90
 create mode 100644 libgomp/testsuite/libgomp.fortran/noncontig-updates-11.f90
 create mode 100644 libgomp/testsuite/libgomp.fortran/noncontig-updates-12.f90
 create mode 100644 libgomp/testsuite/libgomp.fortran/noncontig-updates-13.f90
 create mode 100644 libgomp/testsuite/libgomp.fortran/noncontig-updates-2.f90
 create mode 100644 libgomp/testsuite/libgomp.fortran/noncontig-updates-3.f90
 create mode 100644 libgomp/testsuite/libgomp.fortran/noncontig-updates-4.f90
 create mode 100644 libgomp/testsuite/libgomp.fortran/noncontig-updates-5.f90
 create mode 100644 libgomp/testsuite/libgomp.fortran/noncontig-updates-6.f90
 create mode 100644 libgomp/testsuite/libgomp.fortran/noncontig-updates-7.f90
 create mode 100644 libgomp/testsuite/libgomp.fortran/noncontig-updates-8.f90
 create mode 100644 libgomp/testsuite/libgomp.fortran/noncontig-updates-9.f90

diff --git a/gcc/fortran/openmp.cc b/gcc/fortran/openmp.cc
index 585ffe035236..42d003776ee0 100644
--- a/gcc/fortran/openmp.cc
+++ b/gcc/fortran/openmp.cc
@@ -8026,7 +8026,10 @@ omp_verify_map_motion_clauses (gfc_code *code, int list, const char *name,
 	  int i;
 	  gfc_array_ref *ar = &lastslice->u.ar;
 	  for (i = 0; i < ar->dimen; i++)
-	    if (ar->stride[i] && code && code->op != EXEC_OACC_UPDATE)
+	    if (ar->stride[i]
+		&& code
+		&& code->op != EXEC_OACC_UPDATE
+		&& code->op != EXEC_OMP_TARGET_UPDATE)
 	      {
 		gfc_error ("Stride should not be specified for array section "
 			   "in %s clause at %L", name, &n->where);
diff --git a/gcc/fortran/trans-openmp.cc b/gcc/fortran/trans-openmp.cc
index 95ce33797d62..3c4d9ccc5432 100644
--- a/gcc/fortran/trans-openmp.cc
+++ b/gcc/fortran/trans-openmp.cc
@@ -2781,6 +2781,346 @@ get_symbol_rooted_namelist (hash_map<gfc_symbol *,
   return NULL;
 }
 
+/* We build an "un-Fortrannish" array-of-arrays here to pass our calculated
+   array bounds to the middle end for strided/rectangular OpenMP
+   "target update" operations.  */
+
+static tree
+gfc_trans_omp_arrayshape_type (tree type, vec<tree> *dims)
+{
+  gcc_assert (dims->length () > 0);
+
+  for (unsigned i = 0; i < dims->length (); i++)
+    {
+      tree dim = fold_convert (sizetype, (*dims)[i]);
+      /* We need the index of the last element, not the array size.  */
+      dim = size_binop (MINUS_EXPR, dim, size_one_node);
+      tree idxtype = build_index_type (dim);
+      type = build_array_type (type, idxtype);
+    }
+
+  return type;
+}
+
+/* Emit code to find the greatest common divisor of two (gfc_array_index_type)
+   trees to BLOCK.  This is Euclid's algorithm:
+
+     int
+     gcd (int a, int b)
+     {
+       int tmp;
+       while (b != 0)
+	 {
+	   tmp = b;
+	   b = a % b;
+	   a = tmp;
+	 }
+       return a;
+     }
+*/
+
+static void
+gfc_omp_calculate_gcd (stmtblock_t *block, tree dst, tree a, tree b)
+{
+  tree tmp = gfc_create_var (gfc_array_index_type, "tmp");
+  tree avar = gfc_create_var (gfc_array_index_type, "a");
+  tree bvar = gfc_create_var (gfc_array_index_type, "b");
+
+  /* Avoid clobbering the inputs.  */
+  gfc_add_modify (block, avar, a);
+  gfc_add_modify (block, bvar, b);
+
+  tree label_cond = gfc_build_label_decl (NULL_TREE);
+  tree label_loop = gfc_build_label_decl (NULL_TREE);
+  TREE_USED (label_cond) = 1;
+  TREE_USED (label_loop) = 1;
+
+  gfc_add_expr_to_block (block, build1_v (GOTO_EXPR, label_cond));
+  gfc_add_expr_to_block (block, build1_v (LABEL_EXPR, label_loop));
+
+  gfc_add_modify (block, tmp, bvar);
+  gfc_add_modify (block, bvar,
+		  fold_build2_loc (input_location, TRUNC_MOD_EXPR,
+				   gfc_array_index_type, avar, bvar));
+  gfc_add_modify (block, avar, tmp);
+
+  gfc_add_expr_to_block (block, build1_v (LABEL_EXPR, label_cond));
+
+  tmp = fold_build2_loc (input_location, NE_EXPR, boolean_type_node, bvar,
+			 gfc_index_zero_node);
+  tmp = build3_v (COND_EXPR, tmp, build1_v (GOTO_EXPR, label_loop),
+		  build_empty_stmt (input_location));
+  gfc_add_expr_to_block (block, tmp);
+
+  gfc_add_modify (block, dst, avar);
+}
+
+/* Convert a gfortran array descriptor -- specifically the per-dimension
+   strides -- into a form that can be easily translated to a noncontiguous
+   OpenMP "target update" operation.  We emit a specialized version of a
+   function like this inline:
+
+     void
+     gfc_desc_to_omp_noncontig_array (int *dims, int *strides, int ndims,
+				      int *fstrides, int *flo, int *fhi)
+     {
+       dims[ndims - 1] = (fhi[ndims - 1] - flo[ndims - 1] + 1);
+       strides[0] = fstrides[0];
+       if (ndims > 1)
+	 strides[ndims - 1] = 1;
+       if (ndims == 2)
+	 dims[0] = fstrides[1];
+       else if (ndims > 2)
+	 {
+	   int grains[ndims - 2];
+
+	   int bigger_grain = fstrides[ndims - 1];
+	   for (int i = ndims - 2; i > 0; i--)
+	     {
+	       grains[i - 1] = gcd (fstrides[i], bigger_grain);
+	       bigger_grain = grains[i - 1];
+	     }
+
+	   int volume = 1;
+	   for (int i = 0; i < ndims - 2; i++)
+	     {
+	       int g = grains[i];
+	       dims[i] = g / volume;
+	       strides[i + 1] = fstrides[i + 1] / g;
+	       volume = volume * dims[i];
+	     }
+	   dims[ndims - 2] = fstrides[ndims - 1] / volume;
+	 }
+     }
+
+   where "fstrides", "flo" and "fhi" represent the stride, low bound and upper
+   bound of each dimension in the Fortran array descriptor.
+
+   (Note that most of the complexity only applies to arrays with more than two
+   dimensions, and the final stanza won't be emitted at all for lower-ranked
+   arrays.)
+
+   The output of the algorithm is a set of dimensions dims[] = { D, C, B, A }
+   "as if" the array was declared like this (in C):
+
+     type arr[A][B][C][D];
+
+   i.e. with the innermost dimension first, and a set of strides (in terms of
+   the step size along each dimension, without previous dimensions multiplied
+   in).
+
+   As an example, if we have an array:
+
+     allocate (arr(18,19,20,21,22))
+
+   and an update operation:
+
+     !$omp target update to(arr(1:3:2,1:4:3,1:5:4,1:6:5,1:7:6))
+
+   the strides we see in the Fortran array descriptor will be:
+
+     2 54 1368 34200 861840
+
+   as given by:
+
+     2 = stride0
+     54 = dim0 * stride1
+     1368 = dim0 * dim1 * stride2
+     34200 = dim0 * dim1 * dim2 * stride3
+     861840 = dim0 * dim1 * dim2 * dim3 * stride4
+
+   where "dimN" are the extents of each dimension (18,19,20,21,22), and
+   "strideN" are the strides in terms of step length along each dimension
+   (2,3,4,5,6).
+
+   We'd like to figure out what the original dimN, strideN were from the
+   Fortran array descriptor, but that's in general impossible.  Furthermore,
+   if we naively divide a stride by the preceding stride, the result isn't
+   necessarily an integer, as for e.g.:
+
+     861840/34200 = 25.2
+
+   What we can do though is figure out the greatest common divisor of
+   each stride and the preceding one, from the largest down, and use those as
+   units of granularity, i.e. the size of the corresponding dimension we pass
+   to the middle-end/runtime.  The stepwise stride is then the number of
+   times each "grain" fits into the Fortran array descriptor stride.
+
+   The output of the algorithm will be:
+
+     dims  strides
+     18    2
+     76    3
+     5     1
+     126   5
+     9     1
+
+   These numbers work fine for libgomp target.c:omp_target_memcpy_rect_worker.
+   Multiplying them through also gives the same numbers as the source Fortran
+   array strides, i.e. dim0*dim1*dim2*stride3 (18*76*5*5) = 34200.  */
+
+static void
+gfc_desc_to_omp_noncontig_array (stmtblock_t *block, tree *ompdimsp,
+				 tree *ompstridesp, tree desc, int ndims)
+{
+  tree lastdim = build_int_cst (gfc_array_index_type, ndims - 1);
+  tree dimrange = build_index_type (lastdim);
+  tree ndimarrtype = build_array_type (gfc_array_index_type, dimrange);
+  tree ompdims = gfc_create_var (ndimarrtype, "dims");
+  tree ompstrides = gfc_create_var (ndimarrtype, "strides");
+
+  *ompdimsp = ompdims;
+  *ompstridesp = ompstrides;
+
+  /* dims[ndims - 1] = (fhi[ndims - 1] - flo[ndims - 1] + 1);  */
+  tree lastlbound = gfc_conv_array_lbound (desc, ndims - 1);
+  tree lastubound = gfc_conv_array_ubound (desc, ndims - 1);
+  tree lastrange = fold_build2_loc (input_location, MINUS_EXPR,
+				    gfc_array_index_type, lastubound,
+				    lastlbound);
+  lastrange = fold_build2_loc (input_location, PLUS_EXPR, gfc_array_index_type,
+			       lastrange, gfc_index_one_node);
+
+  gfc_add_modify (block,
+		  gfc_build_array_ref (ompdims, lastdim, NULL_TREE, true),
+		  lastrange);
+
+  /* strides[0] = fstrides[0];  */
+  tree stride0 = gfc_conv_array_stride (desc, 0);
+  gfc_add_modify (block,
+		  gfc_build_array_ref (ompstrides, gfc_index_zero_node,
+				       NULL_TREE, true),
+		  stride0);
+
+  if (ndims > 1)
+    /* strides[ndims - 1] = 1;  */
+    gfc_add_modify (block,
+		    gfc_build_array_ref (ompstrides, lastdim, NULL_TREE, true),
+		    gfc_index_one_node);
+
+  if (ndims == 2)
+    /* dims[0] = fstrides[1];  */
+    gfc_add_modify (block,
+		    gfc_build_array_ref (ompdims, gfc_index_zero_node,
+					 NULL_TREE, true),
+		    gfc_conv_array_stride (desc, 1));
+  else if (ndims > 2)
+    {
+      /* int grains[ndims - 2];  */
+      tree lastgrain = build_int_cst (gfc_array_index_type, ndims - 3);
+      tree grainrange = build_index_type (lastgrain);
+      tree grainarrtype = build_array_type (gfc_array_index_type, grainrange);
+      tree grains = gfc_create_var (grainarrtype, "grains");
+
+      /* int bigger_grain = fstrides[ndims - 1];  */
+      tree bigger_grain = gfc_create_var (gfc_array_index_type, "bigger_grain");
+      tree fstridem1 = gfc_conv_array_stride (desc, ndims - 1);
+      gfc_add_modify (block, bigger_grain, fstridem1);
+
+      /*
+	for (int i = ndims - 2; i > 0; i--)
+	  {
+	    grains[i - 1] = gcd (fstrides[i], bigger_grain);
+	    bigger_grain = grains[i - 1];
+	  }
+      */
+      stmtblock_t loop_body;
+      gfc_init_block (&loop_body);
+
+      tree idx = gfc_create_var (gfc_array_index_type, "idx");
+
+      tree gcdtmp = gfc_create_var (gfc_array_index_type, "tmp");
+      gfc_omp_calculate_gcd (&loop_body, gcdtmp,
+			     gfc_conv_descriptor_stride_get (desc, idx),
+			     bigger_grain);
+      tree idxm1 = fold_build2_loc (input_location, MINUS_EXPR,
+				    gfc_array_index_type, idx,
+				    gfc_index_one_node);
+      gfc_add_modify (&loop_body,
+		      gfc_build_array_ref (grains, idxm1, NULL_TREE, true),
+		      gcdtmp);
+      gfc_add_modify (&loop_body, bigger_grain, gcdtmp);
+
+      gfc_simple_for_loop (block, idx,
+			   build_int_cst (gfc_array_index_type, ndims - 2),
+			   gfc_index_zero_node, GT_EXPR,
+			   build_int_cst (gfc_array_index_type, -1),
+			   gfc_finish_block (&loop_body));
+      /*
+	 int volume = 1;
+	 for (int i = 0; i < ndims - 2; i++)
+	   {
+	     int g = grains[i];
+	     dims[i] = g / volume;
+	     strides[i + 1] = fstrides[i + 1] / g;
+	     volume = volume * dims[i];
+	   }
+      */
+      tree volume = gfc_create_var (gfc_array_index_type, "volume");
+      gfc_add_modify (block, volume, gfc_index_one_node);
+
+      gfc_init_block (&loop_body);
+      tree grain = gfc_create_var (gfc_array_index_type, "grain");
+      gfc_add_modify (&loop_body, grain,
+		      gfc_build_array_ref (grains, idx, NULL_TREE, true));
+      tree dims_i = gfc_build_array_ref (ompdims, idx, NULL_TREE, true);
+      gfc_add_modify (&loop_body, dims_i,
+		      fold_build2_loc (input_location, TRUNC_DIV_EXPR,
+				       gfc_array_index_type, grain, volume));
+      tree nidx = fold_build2_loc (input_location, PLUS_EXPR,
+				   gfc_array_index_type, idx,
+				   gfc_index_one_node);
+      tree strides_ni = gfc_build_array_ref (ompstrides, nidx, NULL_TREE, true);
+      tree fstrides_ni = gfc_conv_descriptor_stride_get (desc, nidx);
+      gfc_add_modify (&loop_body, strides_ni,
+		      fold_build2_loc (input_location, TRUNC_DIV_EXPR,
+				       gfc_array_index_type, fstrides_ni,
+				       grain));
+      gfc_add_modify (&loop_body, volume,
+		      fold_build2_loc (input_location, MULT_EXPR,
+				       gfc_array_index_type, volume, dims_i));
+
+      gfc_simple_for_loop (block, idx, gfc_index_zero_node,
+			   build_int_cst (gfc_array_index_type, ndims - 2),
+			   LT_EXPR, gfc_index_one_node,
+			   gfc_finish_block (&loop_body));
+
+      /* dims[ndims - 2] = fstrides[ndims - 1] / volume;  */
+      tree dimsm2
+	= gfc_build_array_ref (ompdims,
+			       build_int_cst (gfc_array_index_type, ndims - 2),
+			       NULL_TREE, true);
+      gfc_add_modify (block, dimsm2,
+		      fold_build2_loc (input_location, TRUNC_DIV_EXPR,
+				       gfc_array_index_type, fstridem1,
+				       volume));
+    }
+}
+
+/* Return TRUE if update for N can definitely be done with a single contiguous
+   transfer.  If no or if we can't tell, return FALSE.  */
+
+static bool
+gfc_omp_contiguous_update_p (gfc_omp_namelist *n)
+{
+  gfc_expr *contig_expr = n->expr;
+
+  if (!n->expr)
+    {
+      if (n->sym->attr.contiguous)
+	return true;
+
+      tree desc = gfc_trans_omp_variable (n->sym, false);
+      tree type = TREE_TYPE (desc);
+      if (!GFC_ARRAY_TYPE_P (type) && !GFC_DESCRIPTOR_TYPE_P (type))
+	return true;
+
+      contig_expr = gfc_lval_expr_from_sym (n->sym);
+    }
+
+  return gfc_is_simply_contiguous (contig_expr, false, true);
+}
+
 static tree
 gfc_trans_omp_clauses (stmtblock_t *block, gfc_omp_clauses *clauses,
 		       locus where, toc_directive cd = TOC_OPENMP)
@@ -4218,6 +4558,165 @@ gfc_trans_omp_clauses (stmtblock_t *block, gfc_omp_clauses *clauses,
 		default:
 		  gcc_unreachable ();
 		}
+
+	      gfc_ref *lastref = NULL;
+	      if (n->expr)
+		for (gfc_ref *ref = n->expr->ref; ref; ref = ref->next)
+		  if (ref->type == REF_COMPONENT || ref->type == REF_ARRAY)
+		    lastref = ref;
+
+	      if ((list == OMP_LIST_TO || list == OMP_LIST_FROM)
+		  && (!n->expr || (lastref && lastref->type == REF_ARRAY))
+		  && !gfc_omp_contiguous_update_p (n))
+		{
+		  int ndims;
+		  gfc_se se;
+		  gfc_init_se (&se, NULL);
+
+		  tree desc, span = NULL_TREE;
+
+		  if (n->expr)
+		    {
+		      if (n->expr->rank)
+			gfc_conv_expr_descriptor (&se, n->expr);
+		      else
+			gfc_conv_expr (&se, n->expr);
+
+		      desc = se.expr;
+		      /* The span is the distance between two array elements
+			 along the innermost dimension (there may be padding
+			 or other data between elements, e.g. of a derived-type
+			 array).  */
+		      span = gfc_get_array_span (desc, n->expr);
+		      ndims = lastref->u.ar.dimen;
+		    }
+		  else
+		    {
+		      desc = gfc_trans_omp_variable (n->sym, false);
+		      tree type = TREE_TYPE (desc);
+		      if (GFC_DESCRIPTOR_TYPE_P (type))
+			span = gfc_conv_descriptor_span_get (desc);
+		      ndims = GFC_TYPE_ARRAY_RANK (type);
+		    }
+
+		  gfc_add_block_to_block (block, &se.pre);
+
+		  tree ompdims, ompstrides;
+
+		  gfc_desc_to_omp_noncontig_array (block, &ompdims,
+						   &ompstrides, desc, ndims);
+
+		  tree type = TREE_TYPE (desc);
+		  tree etype = gfc_get_element_type (type);
+		  tree elsize = fold_convert (gfc_array_index_type,
+					      size_in_bytes (etype));
+
+		  tree ptr = gfc_conv_array_data (desc);
+		  tree offset = gfc_conv_array_offset (desc);
+
+		  if (!span)
+		    /* The span is the element size.  */
+		    span = elsize;
+
+		  tree node = build_omp_clause (input_location, OMP_CLAUSE_MAP);
+
+		  switch (list)
+		    {
+		    case OMP_LIST_TO:
+		      OMP_CLAUSE_SET_MAP_KIND (node, GOMP_MAP_TO_GRID);
+		      break;
+		    case OMP_LIST_FROM:
+		      OMP_CLAUSE_SET_MAP_KIND (node, GOMP_MAP_FROM_GRID);
+		      break;
+		    default:
+		      gcc_unreachable ();
+		    }
+
+		  gcc_assert (POINTER_TYPE_P (TREE_TYPE (ptr)));
+		  tree byte_offset = fold_convert (sizetype, offset);
+		  byte_offset = size_binop (MULT_EXPR, byte_offset,
+					    fold_convert (sizetype, span));
+		  tree origin
+		    = fold_build2_loc (input_location, POINTER_PLUS_EXPR,
+				       TREE_TYPE (ptr), ptr, byte_offset);
+
+		  OMP_CLAUSE_SIZE (node) = elsize;
+
+		  omp_clauses = gfc_trans_add_clause (node, omp_clauses);
+
+		  auto_vec<tree, 5> dims;
+
+		  for (int r = 0; r < ndims; r++)
+		    {
+		      tree d
+			= gfc_build_array_ref (ompdims,
+					       build_int_cst
+						 (gfc_array_index_type, r),
+					       NULL_TREE, true);
+		      d = gfc_evaluate_now (d, block);
+		      dims.safe_push (d);
+		    }
+
+		  for (int r = ndims - 1; r >= 0; r--)
+		    {
+		      tree stride_r, len_r, lowbound_r;
+
+		      tree rcst = build_int_cst (gfc_array_index_type, r);
+
+		      stride_r = gfc_build_array_ref (ompstrides, rcst,
+						      NULL_TREE, true);
+		      lowbound_r = gfc_conv_array_lbound (desc, r);
+		      len_r
+			= fold_build2_loc (input_location, MINUS_EXPR,
+					   gfc_array_index_type,
+					   gfc_conv_array_ubound (desc, r),
+					   lowbound_r);
+		      len_r
+			= fold_build2_loc (input_location, PLUS_EXPR,
+					   gfc_array_index_type,
+					   len_r, gfc_index_one_node);
+
+		      lowbound_r
+			= fold_build2_loc (input_location, MULT_EXPR,
+					   gfc_array_index_type, lowbound_r,
+					   stride_r);
+
+		      stride_r = gfc_evaluate_now (stride_r, block);
+		      lowbound_r = gfc_evaluate_now (lowbound_r, block);
+		      len_r = gfc_evaluate_now (len_r, block);
+
+		      tree dim = build_omp_clause (input_location,
+						   OMP_CLAUSE_MAP);
+		      OMP_CLAUSE_SET_MAP_KIND (dim, GOMP_MAP_GRID_DIM);
+		      OMP_CLAUSE_DECL (dim) = lowbound_r;
+		      OMP_CLAUSE_SIZE (dim) = len_r;
+
+		      omp_clauses = gfc_trans_add_clause (dim, omp_clauses);
+
+		      if (!integer_onep (stride_r)
+			  || (r == 0 && !operand_equal_p (span, elsize)))
+			{
+			  tree snode = build_omp_clause (input_location,
+							 OMP_CLAUSE_MAP);
+			  OMP_CLAUSE_SET_MAP_KIND (snode,
+						   GOMP_MAP_GRID_STRIDE);
+			  OMP_CLAUSE_DECL (snode) = stride_r;
+			  if (r == 0 && !operand_equal_p (span, elsize))
+			    OMP_CLAUSE_SIZE (snode) = span;
+			  omp_clauses = gfc_trans_add_clause (snode,
+							      omp_clauses);
+			}
+		    }
+		  origin = build_fold_indirect_ref (origin);
+		  tree eltype = gfc_get_element_type (TREE_TYPE (desc));
+		  tree arrtype
+		    = gfc_trans_omp_arrayshape_type (eltype, &dims);
+		  OMP_CLAUSE_DECL (node)
+		    = build1_loc (input_location, VIEW_CONVERT_EXPR,
+				  arrtype, origin);
+		  continue;
+		}
+
 	      tree node = build_omp_clause (input_location, clause_code);
 	      if (n->expr == NULL
 		  || (n->expr->ref->type == REF_ARRAY
diff --git a/gcc/gimplify.cc b/gcc/gimplify.cc
index c74e7696da42..695373992462 100644
--- a/gcc/gimplify.cc
+++ b/gcc/gimplify.cc
@@ -14426,6 +14426,16 @@ gimplify_adjust_omp_clauses (gimple_seq *pre_p, gimple_seq body, tree *list_p,
 	      if (OMP_CLAUSE_MAP_KIND (c) == GOMP_MAP_STRUCT)
 		break;
 
+	      /* If we have a non-contiguous (strided/rectangular) update
+		 operation with a VIEW_CONVERT_EXPR, we need to be careful not
+		 to gimplify the conversion away, because we need it during
+		 omp-low.cc in order to retrieve the array's dimensions.  Just
+		 gimplify partially instead.  */
+	      if ((OMP_CLAUSE_MAP_KIND (c) == GOMP_MAP_TO_GRID
+		   || OMP_CLAUSE_MAP_KIND (c) == GOMP_MAP_FROM_GRID)
+		  && TREE_CODE (*pd) == VIEW_CONVERT_EXPR)
+		pd = &TREE_OPERAND (*pd, 0);
+
 	      /* We've already partly gimplified this in
 		 gimplify_scan_omp_clauses.  Don't do any more.  */
 	      if (code == OMP_TARGET && OMP_CLAUSE_MAP_IN_REDUCTION (c))
diff --git a/gcc/omp-low.cc b/gcc/omp-low.cc
index bce74707fe89..f0885df05b84 100644
--- a/gcc/omp-low.cc
+++ b/gcc/omp-low.cc
@@ -1162,6 +1162,11 @@ omp_noncontig_descriptor_type (location_t loc)
   TREE_CHAIN (field) = fields;
   fields = field;
 
+  field = build_decl (loc, FIELD_DECL, get_identifier ("__span"),
+		      size_type_node);
+  TREE_CHAIN (field) = fields;
+  fields = field;
+
   tree ptr_size_type = build_pointer_type (size_type_node);
 
   field = build_decl (loc, FIELD_DECL, get_identifier ("__dim"), ptr_size_type);
@@ -1756,7 +1761,6 @@ scan_sharing_clauses (tree clauses, omp_context *ctx)
 					  OMP_CLAUSE_MAP);
 	      OMP_CLAUSE_SET_MAP_KIND (dn, GOMP_MAP_TO_PSET);
 	      OMP_CLAUSE_DECL (dn) = desc;
-	      OMP_CLAUSE_SIZE (dn) = TYPE_SIZE_UNIT (desc_type);
 
 	      OMP_CLAUSE_CHAIN (dn) = OMP_CLAUSE_CHAIN (c);
 	      OMP_CLAUSE_CHAIN (c) = dn;
@@ -12967,6 +12971,7 @@ lower_omp_target (gimple_stmt_iterator *gsi_p, omp_context *ctx)
 			&& OMP_CLAUSE_MAP_KIND (nc) == GOMP_MAP_TO_PSET);
 	    c = nc;
 	    while ((nc = OMP_CLAUSE_CHAIN (c))
+		   && OMP_CLAUSE_CODE (nc) == OMP_CLAUSE_MAP
 		   && (OMP_CLAUSE_MAP_KIND (nc) == GOMP_MAP_GRID_DIM
 		       || OMP_CLAUSE_MAP_KIND (nc) == GOMP_MAP_GRID_STRIDE))
 	      c = nc;
@@ -13279,7 +13284,7 @@ lower_omp_target (gimple_stmt_iterator *gsi_p, omp_context *ctx)
 		int i, dims = 0;
 		auto_vec<tree> tdims;
 		bool pointer_based = false, handled_pointer_section = false;
-		tree arrsize = fold_convert (sizetype, elsize);
+		tree arrsize = size_one_node;
 
 		/* Allow a single (maybe strided) array section if we have a
 		   pointer base.  */
@@ -13291,8 +13296,12 @@ lower_omp_target (gimple_stmt_iterator *gsi_p, omp_context *ctx)
 		    dims = 1;
 		  }
 		else
+		  /* NOTE: Don't treat (e.g. Fortran, fixed-length) strings as
+		     array types here; array section syntax isn't applicable to
+		     strings.  */
 		  for (tree itype = type;
-		       TREE_CODE (itype) == ARRAY_TYPE;
+		       TREE_CODE (itype) == ARRAY_TYPE
+		       && !TYPE_STRING_FLAG (itype);
 		       itype = TREE_TYPE (itype))
 		    {
 		      tdims.safe_push (itype);
@@ -13333,13 +13342,16 @@ lower_omp_target (gimple_stmt_iterator *gsi_p, omp_context *ctx)
 		oc = c;
 		c = dn;
 
+		tree span = NULL_TREE;
+
 		for (i = 0; i < dims; i++)
 		  {
 		    nc = OMP_CLAUSE_CHAIN (c);
 		    tree dim = NULL_TREE, index = NULL_TREE, len = NULL_TREE,
 			 stride = size_one_node;
 
-		    if (OMP_CLAUSE_CODE (nc) == OMP_CLAUSE_MAP
+		    if (nc
+			&& OMP_CLAUSE_CODE (nc) == OMP_CLAUSE_MAP
 			&& OMP_CLAUSE_MAP_KIND (nc) == GOMP_MAP_GRID_DIM)
 		      {
 			index = OMP_CLAUSE_DECL (nc);
@@ -13356,6 +13368,18 @@ lower_omp_target (gimple_stmt_iterator *gsi_p, omp_context *ctx)
 			  {
 			    stride = OMP_CLAUSE_DECL (nc2);
 			    stride = fold_convert (sizetype, stride);
+			    if (OMP_CLAUSE_SIZE (nc2))
+			      {
+				/* If the element size is not the same as the
+				   distance between two adjacent array
+				   elements (in the innermost dimension),
+				   retrieve the latter value ("span") from the
+				   size field of the stride.  We only expect to
+				   see one such field per array.  */
+				gcc_assert (!span);
+				span = OMP_CLAUSE_SIZE (nc2);
+				span = fold_convert (sizetype, span);
+			      }
 			    nc = nc2;
 			  }
 
@@ -13413,7 +13437,8 @@ lower_omp_target (gimple_stmt_iterator *gsi_p, omp_context *ctx)
 			dim = size_binop (MINUS_EXPR, maxval, minval);
 			dim = size_binop (PLUS_EXPR, dim, size_one_node);
 			len = dim;
-			index = size_zero_node;
+			index = minval;
+			nc = c;
 		      }
 
 		    if (TREE_CODE (dim) != INTEGER_CST)
@@ -13451,7 +13476,7 @@ lower_omp_target (gimple_stmt_iterator *gsi_p, omp_context *ctx)
 			 + index[1] * stride[1] * dim[2]
 			 + index[2] * stride[2]
 
-		       All multiplied by "elsize".  */
+		       All multiplied by "span" (or "elsize").  */
 
 		    tree index_stride = size_binop (MULT_EXPR, index, stride);
 		    bias = size_binop (PLUS_EXPR, bias,
@@ -13474,14 +13499,16 @@ lower_omp_target (gimple_stmt_iterator *gsi_p, omp_context *ctx)
 		      enclosure = volume;
 		  }
 
-		elsize = fold_convert (sizetype, elsize);
+		/* If we don't have a separate span size, use the element size
+		   instead.  */
+		if (!span)
+		  span = fold_convert (sizetype, elsize);
 
 		/* The size of a volume enclosing the elements to be
 		   transferred.  */
-		OMP_CLAUSE_SIZE (oc)
-		  = size_binop (MULT_EXPR, enclosure, elsize);
+		OMP_CLAUSE_SIZE (oc) = size_binop (MULT_EXPR, enclosure, span);
 		/* And the bias of the first element we will update.  */
-		OMP_CLAUSE_SIZE (dn) = size_binop (MULT_EXPR, bias, elsize);
+		OMP_CLAUSE_SIZE (dn) = size_binop (MULT_EXPR, bias, span);
 
 		tree cdim = build_constructor (size_arr_type, vdim);
 		tree cindex = build_constructor (size_arr_type, vindex);
@@ -13512,13 +13539,14 @@ lower_omp_target (gimple_stmt_iterator *gsi_p, omp_context *ctx)
 
 		tree ndims_field = TYPE_FIELDS (desc_type);
 		tree elemsize_field = DECL_CHAIN (ndims_field);
-		tree dim_field = DECL_CHAIN (elemsize_field);
+		tree span_field = DECL_CHAIN (elemsize_field);
+		tree dim_field = DECL_CHAIN (span_field);
 		tree index_field = DECL_CHAIN (dim_field);
 		tree len_field = DECL_CHAIN (index_field);
 		tree stride_field = DECL_CHAIN (len_field);
 
 		vec<constructor_elt, va_gc> *v;
-		vec_alloc (v, 6);
+		vec_alloc (v, 7);
 
 		bool all_static = (TREE_STATIC (dim_tmp)
 				   && TREE_STATIC (index_tmp)
@@ -13548,6 +13576,7 @@ lower_omp_target (gimple_stmt_iterator *gsi_p, omp_context *ctx)
 
 		CONSTRUCTOR_APPEND_ELT (v, ndims_field, ndims);
 		CONSTRUCTOR_APPEND_ELT (v, elemsize_field, elsize);
+		CONSTRUCTOR_APPEND_ELT (v, span_field, span);
 		CONSTRUCTOR_APPEND_ELT (v, dim_field, dim_tmp);
 		CONSTRUCTOR_APPEND_ELT (v, index_field, index_tmp);
 		CONSTRUCTOR_APPEND_ELT (v, len_field, len_tmp);
diff --git a/gcc/testsuite/gfortran.dg/gomp/noncontig-updates-1.f90 b/gcc/testsuite/gfortran.dg/gomp/noncontig-updates-1.f90
new file mode 100644
index 000000000000..5c60f5cac620
--- /dev/null
+++ b/gcc/testsuite/gfortran.dg/gomp/noncontig-updates-1.f90
@@ -0,0 +1,19 @@
+! { dg-additional-options "-fdump-tree-original" }
+
+integer :: basicarray(100)
+integer, allocatable :: allocarray(:)
+
+allocate(allocarray(1:20))
+
+!$omp target update to(basicarray)
+
+!$omp target update from(basicarray(:))
+
+!$omp target update to(allocarray)
+
+!$omp target update from(allocarray(:))
+
+end
+
+! { dg-final { scan-tree-dump-times {omp target update from\(} 2 "original" } }
+! { dg-final { scan-tree-dump-times {omp target update to\(} 2 "original" } }
diff --git a/gcc/testsuite/gfortran.dg/gomp/noncontig-updates-2.f90 b/gcc/testsuite/gfortran.dg/gomp/noncontig-updates-2.f90
new file mode 100644
index 000000000000..f5a52736b0cc
--- /dev/null
+++ b/gcc/testsuite/gfortran.dg/gomp/noncontig-updates-2.f90
@@ -0,0 +1,16 @@
+! { dg-additional-options "-fdump-tree-original" }
+
+integer, allocatable :: allocarray(:)
+integer, allocatable :: allocarray2(:,:)
+
+allocate(allocarray(1:20))
+allocate(allocarray2(1:20,1:20))
+
+! This one must be noncontiguous
+!$omp target update to(allocarray(::2))
+! { dg-final { scan-tree-dump {omp target update map\(to_grid:} "original" } }
+
+!$omp target update from(allocarray2(:,5:15))
+! { dg-final { scan-tree-dump {omp target update from\(} "original" } }
+
+end
diff --git a/gcc/testsuite/gfortran.dg/gomp/noncontig-updates-3.f90 b/gcc/testsuite/gfortran.dg/gomp/noncontig-updates-3.f90
new file mode 100644
index 000000000000..5cbfe7c7be54
--- /dev/null
+++ b/gcc/testsuite/gfortran.dg/gomp/noncontig-updates-3.f90
@@ -0,0 +1,16 @@
+! { dg-additional-options "-fdump-tree-original" }
+
+integer, allocatable :: allocarray(:,:)
+
+allocate(allocarray(1:20,1:20))
+
+! This one could possibly be handled as a contiguous update - but isn't,
+! for now.
+!$omp target update to(allocarray(1:20,5:15))
+! { dg-final { scan-tree-dump {omp target update map\(to_grid:} "original" } }
+
+!$omp target update from(allocarray(:,5:15:2))
+! { dg-final { scan-tree-dump {omp target update map\(from_grid:} "original" } }
+
+end
+
diff --git a/gcc/testsuite/gfortran.dg/gomp/noncontig-updates-4.f90 b/gcc/testsuite/gfortran.dg/gomp/noncontig-updates-4.f90
new file mode 100644
index 000000000000..53152aacbb41
--- /dev/null
+++ b/gcc/testsuite/gfortran.dg/gomp/noncontig-updates-4.f90
@@ -0,0 +1,15 @@
+! { dg-additional-options "-fdump-tree-original" }
+
+integer, target :: tgtarray(20)
+integer, pointer, contiguous :: arrayptr(:)
+
+arrayptr => tgtarray
+
+!$omp target update from(arrayptr)
+! { dg-final { scan-tree-dump {omp target update from\(} "original" } }
+
+!$omp target update to(arrayptr(::2))
+! { dg-final { scan-tree-dump {omp target update map\(to_grid:} "original" } }
+
+end
+
diff --git a/libgomp/libgomp.h b/libgomp/libgomp.h
index a930a8243eae..5a2f768c29b8 100644
--- a/libgomp/libgomp.h
+++ b/libgomp/libgomp.h
@@ -1308,6 +1308,7 @@ struct target_mem_desc {
 typedef struct {
   size_t ndims;
   size_t elemsize;
+  size_t span;
   size_t *dim;
   size_t *index;
   size_t *length;
diff --git a/libgomp/target.c b/libgomp/target.c
index d11e222d6fa8..9b9b08db8be0 100644
--- a/libgomp/target.c
+++ b/libgomp/target.c
@@ -2126,8 +2126,8 @@ goacc_unmap_vars (struct target_mem_desc *tgt, bool do_copyfrom,
   gomp_unmap_vars_internal (tgt, do_copyfrom, NULL, aq);
 }
 
-static int omp_target_memcpy_rect_worker (void *, const void *, size_t, int,
-					  const size_t *, const size_t *,
+static int omp_target_memcpy_rect_worker (void *, const void *, size_t, size_t,
+					  int, const size_t *, const size_t *,
 					  const size_t *, const size_t *,
 					  const size_t *, const size_t *,
 					  struct gomp_device_descr *,
@@ -2165,9 +2165,9 @@ gomp_update (struct gomp_device_descr *devicep, size_t mapnum, void **hostaddrs,
 	{
 	  omp_noncontig_array_desc *desc
 	    = (omp_noncontig_array_desc *) hostaddrs[i + 1];
-	  cur_node.host_start = (uintptr_t) hostaddrs[i];
+	  size_t bias = sizes[i + 1];
+	  cur_node.host_start = (uintptr_t) hostaddrs[i] + bias;
 	  cur_node.host_end = cur_node.host_start + sizes[i];
-	  assert (sizes[i + 1] == sizeof (omp_noncontig_array_desc));
 	  splay_tree_key n = splay_tree_lookup (&devicep->mem_map, &cur_node);
 	  if (n)
 	    {
@@ -2179,21 +2179,24 @@ gomp_update (struct gomp_device_descr *devicep, size_t mapnum, void **hostaddrs,
 		}
 	      void *devaddr = (void *) (n->tgt->tgt_start + n->tgt_offset
 					+ cur_node.host_start
-					- n->host_start);
+					- n->host_start
+					- bias);
 	      if ((kind & typemask) == GOMP_MAP_TO_GRID)
 		omp_target_memcpy_rect_worker (devaddr, hostaddrs[i],
-					       desc->elemsize, desc->ndims,
-					       desc->length, desc->stride,
-					       desc->index, desc->index,
-					       desc->dim, desc->dim, devicep,
-					       NULL, &tmp_size, &tmp);
+					       desc->elemsize, desc->span,
+					       desc->ndims, desc->length,
+					       desc->stride, desc->index,
+					       desc->index, desc->dim,
+					       desc->dim, devicep, NULL,
+					       &tmp_size, &tmp);
 	      else
 		omp_target_memcpy_rect_worker (hostaddrs[i], devaddr,
-					       desc->elemsize, desc->ndims,
-					       desc->length, desc->stride,
-					       desc->index, desc->index,
-					       desc->dim, desc->dim, NULL,
-					       devicep, &tmp_size, &tmp);
+					       desc->elemsize, desc->span,
+					       desc->ndims, desc->length,
+					       desc->stride, desc->index,
+					       desc->index, desc->dim,
+					       desc->dim, NULL, devicep,
+					       &tmp_size, &tmp);
 	    }
 	  i++;
 	}
@@ -4640,7 +4643,7 @@ omp_target_memcpy_async (void *dst, const void *src, size_t length,
 
 static int
 omp_target_memcpy_rect_worker (void *dst, const void *src, size_t element_size,
-			       int num_dims, const size_t *volume,
+			       size_t span, int num_dims, const size_t *volume,
 			       const size_t *strides,
 			       const size_t *dst_offsets,
 			       const size_t *src_offsets,
@@ -4655,7 +4658,7 @@ omp_target_memcpy_rect_worker (void *dst, const void *src, size_t element_size,
   size_t j, dst_off, src_off, length;
   int i, ret;
 
-  if (num_dims == 1 && (!strides || strides[0] == 1))
+  if (num_dims == 1 && (!strides || (strides[0] == 1 && element_size == span)))
     {
       if (__builtin_mul_overflow (element_size, volume[0], &length)
 	  || __builtin_mul_overflow (element_size, dst_offsets[0], &dst_off)
@@ -4716,12 +4719,11 @@ omp_target_memcpy_rect_worker (void *dst, const void *src, size_t element_size,
       assert ((src_devicep == NULL || dst_devicep == NULL)
 	      && (src_devicep != NULL || dst_devicep != NULL));
 
-      if (__builtin_mul_overflow (element_size, dst_offsets[0], &dst_off)
-	  || __builtin_mul_overflow (element_size, src_offsets[0], &src_off))
+      if (__builtin_mul_overflow (span, dst_offsets[0], &dst_off)
+	  || __builtin_mul_overflow (span, src_offsets[0], &src_off))
 	return EINVAL;
 
-      if (strides
-	  && __builtin_mul_overflow (element_size, strides[0], &stride))
+      if (__builtin_mul_overflow (span, strides[0], &stride))
 	return EINVAL;
 
       for (i = 0, ret = 1; i < volume[0] && ret; i++)
@@ -4744,7 +4746,9 @@ omp_target_memcpy_rect_worker (void *dst, const void *src, size_t element_size,
 
   /* host->device, device->host and intra device.  */
   if (num_dims == 2
-      && (!strides || (strides[0] == 1 && strides[1] == 1))
+      && (!strides || (strides[0] == 1
+		       && strides[1] == 1
+		       && element_size == span))
       && ((src_devicep
 	   && src_devicep == dst_devicep
 	   && src_devicep->memcpy2d_func)
@@ -4773,7 +4777,8 @@ omp_target_memcpy_rect_worker (void *dst, const void *src, size_t element_size,
   else if (num_dims == 3
 	   && (!strides || (strides[0] == 1
 			    && strides[1] == 1
-			    && strides[2] == 1))
+			    && strides[2] == 1
+			    && element_size == span))
 	   && ((src_devicep
 		&& src_devicep == dst_devicep
 		&& src_devicep->memcpy3d_func)
@@ -4817,7 +4822,7 @@ omp_target_memcpy_rect_worker (void *dst, const void *src, size_t element_size,
     {
       ret = omp_target_memcpy_rect_worker ((char *) dst + dst_off,
 					   (const char *) src + src_off,
-					   element_size, num_dims - 1,
+					   element_size, span, num_dims - 1,
 					   volume + 1,
 					   strides ? strides + 1 : NULL,
 					   dst_offsets + 1, src_offsets + 1,
@@ -4870,8 +4875,8 @@ omp_target_memcpy_rect_copy (void *dst, const void *src,
     gomp_mutex_lock (&src_devicep->lock);
   if (lock_dst)
     gomp_mutex_lock (&dst_devicep->lock);
-  int ret = omp_target_memcpy_rect_worker (dst, src, element_size, num_dims,
-					   volume, NULL, dst_offsets,
+  int ret = omp_target_memcpy_rect_worker (dst, src, element_size, element_size,
+					   num_dims, volume, NULL, dst_offsets,
 					   src_offsets, dst_dimensions,
 					   src_dimensions,
 					   dst_devicep, src_devicep,
diff --git a/libgomp/testsuite/libgomp.fortran/noncontig-updates-1.f90 b/libgomp/testsuite/libgomp.fortran/noncontig-updates-1.f90
new file mode 100644
index 000000000000..6ee87e8043b3
--- /dev/null
+++ b/libgomp/testsuite/libgomp.fortran/noncontig-updates-1.f90
@@ -0,0 +1,54 @@
+! { dg-do run }
+! { dg-require-effective-target offload_device_nonshared_as }
+
+implicit none
+integer, allocatable, target :: arr(:), arr2(:,:)
+integer, pointer :: ap(:), ap2(:,:)
+integer :: i, j
+
+allocate(arr(1:20))
+
+arr = 0
+
+!$omp target enter data map(to: arr)
+
+ap => arr(1:20:2)
+ap = 5
+
+!$omp target update to(ap)
+
+!$omp target exit data map(from: arr)
+
+do i=1,20
+  if (mod(i,2).eq.1.and.arr(i).ne.5) stop 1
+  if (mod(i,2).eq.0.and.arr(i).ne.0) stop 2
+end do
+
+allocate(arr2(1:20,1:20))
+
+ap2 => arr2(2:10:2,3:12:3)
+
+arr2 = 1
+
+!$omp target enter data map(to: arr2)
+
+!$omp target
+ap2 = 5
+!$omp end target
+
+!$omp target update from(ap2)
+
+do i=1,20
+  do j=1,20
+    if (i.ge.2.and.i.le.10.and.mod(i-2,2).eq.0.and.&
+        &j.ge.3.and.j.le.12.and.mod(j-3,3).eq.0) then
+      if (arr2(i,j).ne.5) stop 3
+    else
+      if (arr2(i,j).ne.1) stop 4
+    end if
+  end do
+end do
+
+!$omp target exit data map(delete: arr2)
+
+end
diff --git a/libgomp/testsuite/libgomp.fortran/noncontig-updates-10.f90 b/libgomp/testsuite/libgomp.fortran/noncontig-updates-10.f90
new file mode 100644
index 000000000000..c47ce38918d6
--- /dev/null
+++ b/libgomp/testsuite/libgomp.fortran/noncontig-updates-10.f90
@@ -0,0 +1,29 @@
+! { dg-do run }
+! { dg-require-effective-target offload_device_nonshared_as }
+
+character(len=8), allocatable, dimension(:) :: lines
+integer :: i
+
+allocate(lines(10))
+
+lines = "OMPHELLO"
+
+!$omp target enter data map(to: lines)
+
+!$omp target
+lines = "NEWVALUE"
+!$omp end target
+
+!$omp target update from(lines(5:7:2))
+
+do i=1,10
+  if (i.eq.5.or.i.eq.7) then
+    if (lines(i).ne."NEWVALUE") stop 1
+  else
+    if (lines(i).ne."OMPHELLO") stop 2
+  end if
+end do
+
+!$omp target exit data map(delete: lines)
+
+end
diff --git a/libgomp/testsuite/libgomp.fortran/noncontig-updates-11.f90 b/libgomp/testsuite/libgomp.fortran/noncontig-updates-11.f90
new file mode 100644
index 000000000000..a93acf21d770
--- /dev/null
+++ b/libgomp/testsuite/libgomp.fortran/noncontig-updates-11.f90
@@ -0,0 +1,51 @@
+! { dg-do run }
+! { dg-require-effective-target offload_device_nonshared_as }
+
+program p
+implicit none
+real(kind=4) :: arr(10,10,10,10)
+
+call s(arr,9,9,9,9)
+
+contains
+
+subroutine s(arr,m,n,o,p)
+implicit none
+integer :: i,m,n,o,p
+integer :: a,b,c,d
+real(kind=4) :: arr(0:m,0:n,0:o,0:p)
+
+arr = 0
+
+!$omp target enter data map(to: arr)
+
+!$omp target
+do i=0,9
+  arr(i,i,i,i) = i
+end do
+!$omp end target
+
+!$omp target update from(arr(0:2,0:2,0:2,0:2))
+
+do a=0,9
+  do b=0,9
+    do c=0,9
+      do d=0,9
+        if (a.le.2.and.b.le.2.and.c.le.2.and.d.le.2) then
+          if (a.eq.b.and.b.eq.c.and.c.eq.d) then
+            if (arr(a,b,c,d).ne.a) stop 1
+          else
+            if (arr(a,b,c,d).ne.0) stop 2
+          end if
+        else
+          if (arr(a,b,c,d).ne.0) stop 3
+        end if
+      end do
+    end do
+  end do
+end do
+
+!$omp target exit data map(delete: arr)
+
+end subroutine s
+end program p
diff --git a/libgomp/testsuite/libgomp.fortran/noncontig-updates-12.f90 b/libgomp/testsuite/libgomp.fortran/noncontig-updates-12.f90
new file mode 100644
index 000000000000..c47fbdb0d112
--- /dev/null
+++ b/libgomp/testsuite/libgomp.fortran/noncontig-updates-12.f90
@@ -0,0 +1,59 @@
+! { dg-do run }
+! { dg-require-effective-target offload_device_nonshared_as }
+
+! Test plain, fixed-size arrays, and also pointers to same.
+
+implicit none
+integer(kind=8) :: arr(10,30)
+integer, target :: arr2(9,11,13)
+integer, pointer :: parr(:,:,:)
+integer :: i, j, k
+
+arr = 0
+!$omp target enter data map(to: arr)
+
+!$omp target
+arr = 99
+!$omp end target
+
+!$omp target update from(arr(1:10:3,5:30:7))
+
+do i=1,10
+  do j=1,30
+    if (mod(i-1,3).eq.0.and.mod(j-5,7).eq.0) then
+      if (arr(i,j).ne.99) stop 1
+    else
+      if (arr(i,j).ne.0) stop 2
+    endif
+  end do
+end do
+
+!$omp target exit data map(delete: arr)
+
+arr2 = 0
+parr => arr2
+!$omp target enter data map(to: parr)
+
+!$omp target
+parr = 99
+!$omp end target
+
+!$omp target update from(parr(7:9:2,5:7:2,3:6:3))
+
+do i=1,9
+  do j=1,11
+    do k=1,13
+      if (i.ge.7.and.j.ge.5.and.k.ge.3.and.&
+          &i.le.9.and.j.le.7.and.k.le.6.and.&
+          &mod(i-7,2).eq.0.and.mod(j-5,2).eq.0.and.mod(k-3,3).eq.0) then
+        if (parr(i,j,k).ne.99) stop 3
+      else
+        if (parr(i,j,k).ne.0) stop 4
+      end if
+    end do
+  end do
+end do
+
+!$omp target exit data map(delete: parr)
+
+end
diff --git a/libgomp/testsuite/libgomp.fortran/noncontig-updates-13.f90 b/libgomp/testsuite/libgomp.fortran/noncontig-updates-13.f90
new file mode 100644
index 000000000000..42f867efefc1
--- /dev/null
+++ b/libgomp/testsuite/libgomp.fortran/noncontig-updates-13.f90
@@ -0,0 +1,42 @@
+! { dg-do run }
+! { dg-require-effective-target offload_device_nonshared_as }
+
+implicit none
+integer, allocatable :: arr(:,:,:,:,:)
+integer :: i, j, k, l, m
+
+allocate (arr(18,19,20,21,22))
+
+arr = 0
+
+!$omp target enter data map(to: arr)
+
+arr = 10
+
+!$omp target update to(arr(1:3:2,1:4:3,1:5:4,1:6:5,1:7:6))
+
+!$omp target
+do i=1,18
+  do j=1,19
+    do k=1,20
+      do l=1,21
+        do m=1,22
+          if ((i.eq.1.or.i.eq.3).and.&
+              &(j.eq.1.or.j.eq.4).and.&
+              &(k.eq.1.or.k.eq.5).and.&
+              &(l.eq.1.or.l.eq.6).and.&
+              &(m.eq.1.or.m.eq.7)) then
+            if (arr(i,j,k,l,m).ne.10) stop 1
+          else
+            if (arr(i,j,k,l,m).ne.0) stop 2
+          end if
+        end do
+      end do
+    end do
+  end do
+end do
+!$omp end target
+
+!$omp target exit data map(delete: arr)
+
+end
diff --git a/libgomp/testsuite/libgomp.fortran/noncontig-updates-2.f90 b/libgomp/testsuite/libgomp.fortran/noncontig-updates-2.f90
new file mode 100644
index 000000000000..2d3efb8bfccc
--- /dev/null
+++ b/libgomp/testsuite/libgomp.fortran/noncontig-updates-2.f90
@@ -0,0 +1,101 @@
+! { dg-do run }
+! { dg-require-effective-target offload_device_nonshared_as }
+
+program p
+implicit none
+integer, allocatable, target :: arr3(:,:,:)
+integer, pointer :: ap3(:,:,:)
+integer :: i, j, k
+
+allocate(arr3(1:10,1:10,1:10))
+
+! CHECK 1
+
+arr3 = 0
+ap3 => arr3(1:10,1:10,1:10:2)
+
+!$omp target enter data map(to: arr3)
+
+!$omp target
+ap3 = 5
+!$omp end target
+
+!$omp target update from(ap3)
+
+call check(arr3, 0, 1, 1, 2)
+
+!$omp target exit data map(delete: arr3)
+
+! CHECK 2
+
+arr3 = 0
+ap3 => arr3(1:10,1:10:2,1:10)
+
+!$omp target enter data map(to: arr3)
+
+!$omp target
+ap3 = 5
+!$omp end target
+
+!$omp target update from(ap3)
+
+call check(arr3, 2, 1, 2, 1)
+
+!$omp target exit data map(delete: arr3)
+
+! CHECK 3
+
+arr3 = 0
+ap3 => arr3(1:10:2,1:10,1:10)
+
+!$omp target enter data map(to: arr3)
+
+!$omp target
+ap3 = 5
+!$omp end target
+
+!$omp target update from(ap3)
+
+call check(arr3, 4, 2, 1, 1)
+
+!$omp target exit data map(delete: arr3)
+
+! CHECK 4
+
+arr3 = 0
+ap3 => arr3(1:10:2,1:10:2,1:10:2)
+
+!$omp target enter data map(to: arr3)
+
+!$omp target
+ap3 = 5
+!$omp end target
+
+!$omp target update from(ap3)
+
+call check(arr3, 6, 2, 2, 2)
+
+!$omp target exit data map(delete: arr3)
+
+contains
+
+subroutine check(arr,cb,s1,s2,s3)
+implicit none
+integer :: arr(:,:,:)
+integer :: cb, s1, s2, s3
+
+do i=1,10
+  do j=1,10
+    do k=1,10
+      if (mod(k-1,s1).eq.0.and.mod(j-1,s2).eq.0.and.mod(i-1,s3).eq.0) then
+        if (arr(k,j,i).ne.5) stop cb+1
+      else
+        if (arr(k,j,i).ne.0) stop cb+2
+      end if
+    end do
+  end do
+end do
+
+end subroutine check
+
+end program p
diff --git a/libgomp/testsuite/libgomp.fortran/noncontig-updates-3.f90 b/libgomp/testsuite/libgomp.fortran/noncontig-updates-3.f90
new file mode 100644
index 000000000000..14f1288a6970
--- /dev/null
+++ b/libgomp/testsuite/libgomp.fortran/noncontig-updates-3.f90
@@ -0,0 +1,47 @@
+program p
+! { dg-do run }
+! { dg-require-effective-target offload_device_nonshared_as }
+
+integer :: A(200)
+A = [(i, i=1,200)]
+!$omp target enter data map(to: A(40:200))
+call foo(A(101:))
+
+contains
+
+subroutine foo(x)
+integer, target :: x(100)
+integer, pointer :: p(:,:)
+integer :: i, j
+
+p(0:5,-5:-1) => x(::2)
+
+!$omp target
+x = x * 2
+!$omp end target
+
+!$omp target update from(x(1:20:2))
+
+do i=1,20
+if (mod(i,2).eq.1 .and. x(i).ne.(100+i)*2) stop 1
+if (mod(i,2).eq.0 .and. x(i).ne.100+i) stop 2
+end do
+
+!$omp target
+p = 0
+!$omp end target
+
+!$omp target update from(p(::3,::2))
+
+do i=0,5
+  do j=-5,-1
+    if (mod(i,3).eq.0 .and. mod(j+5,2).eq.0) then
+      if (p(i,j).ne.0) stop 3
+    else
+      if (p(i,j).eq.0) stop 4
+    end if
+  end do
+end do
+
+end subroutine foo
+end program p
diff --git a/libgomp/testsuite/libgomp.fortran/noncontig-updates-4.f90 b/libgomp/testsuite/libgomp.fortran/noncontig-updates-4.f90
new file mode 100644
index 000000000000..46e8c23d2856
--- /dev/null
+++ b/libgomp/testsuite/libgomp.fortran/noncontig-updates-4.f90
@@ -0,0 +1,78 @@
+! { dg-do run }
+! { dg-require-effective-target offload_device_nonshared_as }
+
+type t
+  complex(kind=8) :: c
+  integer :: i
+end type t
+
+type u
+  integer :: i, j
+  complex(kind=8) :: c
+  integer :: k
+end type u
+
+type(t), target :: var(10)
+type(u), target :: var2(10)
+complex(kind=8), pointer :: ptr(:)
+integer :: i
+
+do i=1,10
+  var(i)%c = dcmplx(i,0)
+  var(i)%i = i
+end do
+
+ptr => var(:)%c
+
+!$omp target enter data map(to: var)
+
+!$omp target
+var(:)%c = dcmplx(0,0)
+var(:)%i = 0
+!$omp end target
+
+!$omp target update from(ptr)
+
+do i=1,10
+  if (var(i)%c.ne.dcmplx(0,0)) stop 1
+  if (var(i)%i.ne.i) stop 2
+end do
+
+!$omp target exit data map(delete: var)
+
+! Now do it again with a differently-ordered derived type.
+
+do i=1,10
+  var2(i)%c = dcmplx(0,i)
+  var2(i)%i = i
+  var2(i)%j = i * 2
+  var2(i)%k = i * 3
+end do
+
+ptr => var2(::2)%c
+
+!$omp target enter data map(to: var2)
+
+!$omp target
+var2(:)%c = dcmplx(0,0)
+var2(:)%i = 0
+var2(:)%j = 0
+var2(:)%k = 0
+!$omp end target
+
+!$omp target update from(ptr)
+
+do i=1,10
+  if (mod(i,2).eq.1) then
+    if (var2(i)%c.ne.dcmplx(0,0)) stop 3
+  else
+    if (var2(i)%c.ne.dcmplx(0,i)) stop 4
+  end if
+  if (var2(i)%i.ne.i) stop 5
+  if (var2(i)%j.ne.i * 2) stop 6
+  if (var2(i)%k.ne.i * 3) stop 7
+end do
+
+!$omp target exit data map(delete: var2)
+
+end
diff --git a/libgomp/testsuite/libgomp.fortran/noncontig-updates-5.f90 b/libgomp/testsuite/libgomp.fortran/noncontig-updates-5.f90
new file mode 100644
index 000000000000..9cc20fa321eb
--- /dev/null
+++ b/libgomp/testsuite/libgomp.fortran/noncontig-updates-5.f90
@@ -0,0 +1,55 @@
+! { dg-do run }
+! { dg-require-effective-target offload_device_nonshared_as }
+
+! Only some of an array mapped on the target
+
+integer, target :: arr(100)
+integer, pointer :: ptr(:)
+
+arr = [(i * 2, i=1,100)]
+
+!$omp target enter data map(to: arr(51:100))
+
+!$omp target
+arr(51:100) = arr(51:100) + 1
+!$omp end target
+
+!$omp target update from(arr(51:100:2))
+
+do i=1,100
+  if (i.le.50) then
+    if (arr(i).ne.i*2) stop 1
+  else
+    if (mod(i,2).eq.1 .and. arr(i).ne.i*2+1) stop 2
+    if (mod(i,2).eq.0 .and. arr(i).ne.i*2) stop 3
+  end if
+end do
+
+!$omp target exit data map(delete: arr)
+
+arr = [(i * 2, i=1,100)]
+
+! Similar, but update via pointer.
+
+ptr => arr(51:100)
+
+!$omp target enter data map(to: ptr(1:50))
+
+!$omp target
+ptr = ptr + 1
+!$omp end target
+
+!$omp target update from(ptr(::2))
+
+do i=1,100
+  if (i.le.50) then
+    if (arr(i).ne.i*2) stop 1
+  else
+    if (mod(i,2).eq.1 .and. arr(i).ne.i*2+1) stop 2
+    if (mod(i,2).eq.0 .and. arr(i).ne.i*2) stop 3
+  end if
+end do
+
+!$omp target exit data map(delete: ptr)
+
+end
diff --git a/libgomp/testsuite/libgomp.fortran/noncontig-updates-6.f90 b/libgomp/testsuite/libgomp.fortran/noncontig-updates-6.f90
new file mode 100644
index 000000000000..5c42b9077b38
--- /dev/null
+++ b/libgomp/testsuite/libgomp.fortran/noncontig-updates-6.f90
@@ -0,0 +1,34 @@
+! { dg-do run }
+! { dg-require-effective-target offload_device_nonshared_as }
+
+program p
+implicit none
+integer, dimension(100) :: parr
+integer :: i
+
+parr = [(i, i=1,100)]
+
+!$omp target enter data map(to: parr)
+
+call s(parr)
+
+do i=1,100
+  if (mod(i,3).eq.1 .and. parr(i).ne.999) stop 1
+  if (mod(i,3).ne.1 .and. parr(i).ne.i) stop 2
+end do
+
+!$omp target exit data map(delete: parr)
+
+contains
+subroutine s(arr)
+implicit none
+integer, intent(inout) :: arr(*)
+
+!$omp target map(alloc: arr(1:100))
+arr(1:100) = 999
+!$omp end target
+
+!$omp target update from(arr(1:100:3))
+
+end subroutine s
+end program p
diff --git a/libgomp/testsuite/libgomp.fortran/noncontig-updates-7.f90 b/libgomp/testsuite/libgomp.fortran/noncontig-updates-7.f90
new file mode 100644
index 000000000000..120fd9c90ed5
--- /dev/null
+++ b/libgomp/testsuite/libgomp.fortran/noncontig-updates-7.f90
@@ -0,0 +1,36 @@
+! { dg-do run }
+! { dg-require-effective-target offload_device_nonshared_as }
+
+! Assumed-shape arrays
+
+program p
+implicit none
+integer, dimension(100) :: parr
+integer :: i
+
+parr = [(i, i=1,100)]
+
+!$omp target enter data map(to: parr)
+
+call s(parr)
+
+do i=1,100
+  if (mod(i,3).eq.1 .and. parr(i).ne.999) stop 1
+  if (mod(i,3).ne.1 .and. parr(i).ne.i) stop 2
+end do
+
+!$omp target exit data map(delete: parr)
+
+contains
+subroutine s(arr)
+implicit none
+integer, intent(inout) :: arr(:)
+
+!$omp target
+arr = 999
+!$omp end target
+
+!$omp target update from(arr(1:100:3))
+
+end subroutine s
+end program p
diff --git a/libgomp/testsuite/libgomp.fortran/noncontig-updates-8.f90 b/libgomp/testsuite/libgomp.fortran/noncontig-updates-8.f90
new file mode 100644
index 000000000000..d9b3c9ca8966
--- /dev/null
+++ b/libgomp/testsuite/libgomp.fortran/noncontig-updates-8.f90
@@ -0,0 +1,39 @@
+! { dg-do run }
+! { dg-require-effective-target offload_device_nonshared_as }
+
+! Test biasing for target-region lookup.
+
+implicit none
+integer, allocatable, target :: var(:,:,:)
+integer, pointer :: p(:,:,:)
+integer :: i, j, k
+
+allocate(var(1:20,5:25,10:30))
+
+var = 0
+
+!$omp target enter data map(to: var)
+
+!$omp target
+var = 99
+!$omp end target
+
+p => var(1:3:2,5:5,10:10)
+
+!$omp target update from(p)
+
+do i=1,20
+  do j=5,25
+    do k=10,30
+      if ((i.eq.1.or.i.eq.3).and.j.eq.5.and.k.eq.10) then
+        if (var(i,j,k).ne.99) stop 1
+      else
+        if (var(i,j,k).ne.0) stop 2
+      end if
+    end do
+  end do
+end do
+
+!$omp target exit data map(delete: var)
+
+end
diff --git a/libgomp/testsuite/libgomp.fortran/noncontig-updates-9.f90 b/libgomp/testsuite/libgomp.fortran/noncontig-updates-9.f90
new file mode 100644
index 000000000000..689a46a91f0e
--- /dev/null
+++ b/libgomp/testsuite/libgomp.fortran/noncontig-updates-9.f90
@@ -0,0 +1,34 @@
+! { dg-do run }
+! { dg-require-effective-target offload_device_nonshared_as }
+
+! This test case hits the problem described in:
+! https://gcc.gnu.org/pipermail/gcc-patches/2023-February/612219.html
+
+! { dg-xfail-run-if "'enter data' bug" { offload_device_nonshared_as } }
+
+character(len=:), allocatable, dimension(:) :: lines
+integer :: i
+
+allocate(character(len=8) :: lines(10))
+
+lines = "OMPHELLO"
+
+!$omp target enter data map(to: lines)
+
+!$omp target
+lines = "NEWVALUE"
+!$omp end target
+
+!$omp target update from(lines(5:7:2))
+
+do i=1,10
+  if (i.eq.5.or.i.eq.7) then
+    if (lines(i).ne."NEWVALUE") stop 1
+  else
+    if (lines(i).ne."OMPHELLO") stop 2
+  end if
+end do
+
+!$omp target exit data map(delete: lines)
+
+end
-- 
2.41.0


^ permalink raw reply	[flat|nested] 10+ messages in thread

* Re: [PATCH 1/5] OpenMP, NVPTX: memcpy[23]D bias correction
  2023-09-06  9:34 ` [PATCH 1/5] OpenMP, NVPTX: memcpy[23]D bias correction Julian Brown
@ 2023-09-26 22:57   ` Thomas Schwinge
  2023-10-02 14:53     ` Julian Brown
  0 siblings, 1 reply; 10+ messages in thread
From: Thomas Schwinge @ 2023-09-26 22:57 UTC (permalink / raw)
  To: Julian Brown; +Cc: gcc-patches, fortran, tobias, jakub, Tom de Vries

Hi Julian!

On 2023-09-06T02:34:30-0700, Julian Brown <julian@codesourcery.com> wrote:
> This patch works around behaviour of the 2D and 3D memcpy operations in
> the CUDA driver runtime.  Particularly in Fortran, the "base pointer"
> of an array (used for either source or destination of a host/device copy)
> may lie outside of data that is actually stored on the device.  The fix
> is to make sure that we use the first element of data to be transferred
> instead, and adjust parameters accordingly.

Do you (a) have a stand-alone test case for this (that is, not depending
on your other pending patches, so that this could go in directly --
together with the before-FAIL test case).  Do you (b) know if is this a
bug in our use of the CUDA Driver API or rather in CUDA itself?  If the
latter, have you reported this to Nvidia?

(I didn't quickly understand
<https://docs.nvidia.com/cuda/cuda-driver-api/group__CUDA__MEM.html#group__CUDA__MEM_1g27f885b30c34cc20a663a671dbf6fc27>
"cuMemcpy2D" etc.)


Grüße
 Thomas


> 2023-09-05  Julian Brown  <julian@codesourcery.com>
>
> libgomp/
>       * plugin/plugin-nvptx.c (GOMP_OFFLOAD_memcpy2d): Adjust parameters to
>       avoid out-of-bounds array checks in CUDA runtime.
>       (GOMP_OFFLOAD_memcpy3d): Likewise.
> ---
>  libgomp/plugin/plugin-nvptx.c | 67 +++++++++++++++++++++++++++++++++++
>  1 file changed, 67 insertions(+)
>
> diff --git a/libgomp/plugin/plugin-nvptx.c b/libgomp/plugin/plugin-nvptx.c
> index 00d4241ae02b..cefe288a8aab 100644
> --- a/libgomp/plugin/plugin-nvptx.c
> +++ b/libgomp/plugin/plugin-nvptx.c
> @@ -1827,6 +1827,35 @@ GOMP_OFFLOAD_memcpy2d (int dst_ord, int src_ord, size_t dim1_size,
>    data.srcXInBytes = src_offset1_size;
>    data.srcY = src_offset0_len;
>
> +  if (data.srcXInBytes != 0 || data.srcY != 0)
> +    {
> +      /* Adjust origin to the actual array data, else the CUDA 2D memory
> +      copy API calls below may fail to validate source/dest pointers
> +      correctly (especially for Fortran where the "virtual origin" of an
> +      array is often outside the stored data).  */
> +      if (src_ord == -1)
> +     data.srcHost = (const void *) ((const char *) data.srcHost
> +                                   + data.srcY * data.srcPitch
> +                                   + data.srcXInBytes);
> +      else
> +     data.srcDevice += data.srcY * data.srcPitch + data.srcXInBytes;
> +      data.srcXInBytes = 0;
> +      data.srcY = 0;
> +    }
> +
> +  if (data.dstXInBytes != 0 || data.dstY != 0)
> +    {
> +      /* As above.  */
> +      if (dst_ord == -1)
> +     data.dstHost = (void *) ((char *) data.dstHost
> +                              + data.dstY * data.dstPitch
> +                              + data.dstXInBytes);
> +      else
> +     data.dstDevice += data.dstY * data.dstPitch + data.dstXInBytes;
> +      data.dstXInBytes = 0;
> +      data.dstY = 0;
> +    }
> +
>    CUresult res = CUDA_CALL_NOCHECK (cuMemcpy2D, &data);
>    if (res == CUDA_ERROR_INVALID_VALUE)
>      /* If pitch > CU_DEVICE_ATTRIBUTE_MAX_PITCH or for device-to-device
> @@ -1895,6 +1924,44 @@ GOMP_OFFLOAD_memcpy3d (int dst_ord, int src_ord, size_t dim2_size,
>    data.srcY = src_offset1_len;
>    data.srcZ = src_offset0_len;
>
> +  if (data.srcXInBytes != 0 || data.srcY != 0 || data.srcZ != 0)
> +    {
> +      /* Adjust origin to the actual array data, else the CUDA 3D memory
> +      copy API call below may fail to validate source/dest pointers
> +      correctly (especially for Fortran where the "virtual origin" of an
> +      array is often outside the stored data).  */
> +      if (src_ord == -1)
> +     data.srcHost
> +       = (const void *) ((const char *) data.srcHost
> +                         + (data.srcZ * data.srcHeight + data.srcY)
> +                           * data.srcPitch
> +                         + data.srcXInBytes);
> +      else
> +     data.srcDevice
> +       += (data.srcZ * data.srcHeight + data.srcY) * data.srcPitch
> +          + data.srcXInBytes;
> +      data.srcXInBytes = 0;
> +      data.srcY = 0;
> +      data.srcZ = 0;
> +    }
> +
> +  if (data.dstXInBytes != 0 || data.dstY != 0 || data.dstZ != 0)
> +    {
> +      /* As above.  */
> +      if (dst_ord == -1)
> +     data.dstHost = (void *) ((char *) data.dstHost
> +                              + (data.dstZ * data.dstHeight + data.dstY)
> +                                * data.dstPitch
> +                              + data.dstXInBytes);
> +      else
> +     data.dstDevice
> +       += (data.dstZ * data.dstHeight + data.dstY) * data.dstPitch
> +          + data.dstXInBytes;
> +      data.dstXInBytes = 0;
> +      data.dstY = 0;
> +      data.dstZ = 0;
> +    }
> +
>    CUDA_CALL (cuMemcpy3D, &data);
>    return true;
>  }
> --
> 2.41.0
-----------------
Siemens Electronic Design Automation GmbH; Anschrift: Arnulfstraße 201, 80634 München; Gesellschaft mit beschränkter Haftung; Geschäftsführer: Thomas Heurung, Frank Thürauf; Sitz der Gesellschaft: München; Registergericht München, HRB 106955

^ permalink raw reply	[flat|nested] 10+ messages in thread

* Re: [PATCH 1/5] OpenMP, NVPTX: memcpy[23]D bias correction
  2023-09-26 22:57   ` Thomas Schwinge
@ 2023-10-02 14:53     ` Julian Brown
  2023-12-19 20:45       ` Tobias Burnus
  0 siblings, 1 reply; 10+ messages in thread
From: Julian Brown @ 2023-10-02 14:53 UTC (permalink / raw)
  To: Thomas Schwinge; +Cc: gcc-patches, fortran, tobias, jakub, Tom de Vries

[-- Attachment #1: Type: text/plain, Size: 1903 bytes --]

On Wed, 27 Sep 2023 00:57:58 +0200
Thomas Schwinge <thomas@codesourcery.com> wrote:

> On 2023-09-06T02:34:30-0700, Julian Brown <julian@codesourcery.com>
> wrote:
> > This patch works around behaviour of the 2D and 3D memcpy
> > operations in the CUDA driver runtime.  Particularly in Fortran,
> > the "base pointer" of an array (used for either source or
> > destination of a host/device copy) may lie outside of data that is
> > actually stored on the device.  The fix is to make sure that we use
> > the first element of data to be transferred instead, and adjust
> > parameters accordingly.  
> 
> Do you (a) have a stand-alone test case for this (that is, not
> depending on your other pending patches, so that this could go in
> directly -- together with the before-FAIL test case).

Thanks for the reply! Here's a version with a stand-alone test case.

> Do you (b)
> know if is this a bug in our use of the CUDA Driver API or rather in
> CUDA itself?  If the latter, have you reported this to Nvidia?

I don't think the CUDA behaviour is *wrong*, as such -- at least to the
C/C++ way of thinking (or indeed a graphics-oriented way of thinking),
one would normally think of an array as having a zero-based origin, and
these 2D/3D memory copies would be intended as a way of updating just a
part of an array (or texture) that has full duplicate copies on both
the host and device.  Our use-case just happens to be a bit different,
both because Fortran (internally) represents an array by a zero-based
origin but may use 1-based (or whatever-based) indices, and because we
support partial mappings of host arrays on the device in all three
supported languages -- which amounts to much the same thing, actually.

That said, it *could* be fixed in CUDA, though probably not in all the
versions currently deployed out there in the world.  So I guess we'd
still need a patch like this anyway.

Julian

[-- Attachment #2: cuda-memcpyxd-bias-2.diff --]
[-- Type: text/x-patch, Size: 5759 bytes --]

commit f6fd3ad060bbe5c57661cd861d009dbc2b415778
Author: Julian Brown <julian@codesourcery.com>
Date:   Wed Aug 23 23:46:29 2023 +0000

    OpenMP, NVPTX: memcpy[23]D bias correction
    
    This patch works around behaviour of the 2D and 3D memcpy operations in
    the CUDA driver runtime.  Particularly in Fortran, the "base pointer"
    of an array (used for either source or destination of a host/device copy)
    may lie outside of data that is actually stored on the device.  The fix
    is to make sure that we use the first element of data to be transferred
    instead, and adjust parameters accordingly.
    
    2023-10-02  Julian Brown  <julian@codesourcery.com>
    
    libgomp/
            * plugin/plugin-nvptx.c (GOMP_OFFLOAD_memcpy2d): Adjust parameters to
            avoid out-of-bounds array checks in CUDA runtime.
            (GOMP_OFFLOAD_memcpy3d): Likewise.
            * testsuite/libgomp.c-c++-common/memcpyxd-bias-1.c: New test.

diff --git a/libgomp/plugin/plugin-nvptx.c b/libgomp/plugin/plugin-nvptx.c
index 00d4241ae02..cefe288a8aa 100644
--- a/libgomp/plugin/plugin-nvptx.c
+++ b/libgomp/plugin/plugin-nvptx.c
@@ -1827,6 +1827,35 @@ GOMP_OFFLOAD_memcpy2d (int dst_ord, int src_ord, size_t dim1_size,
   data.srcXInBytes = src_offset1_size;
   data.srcY = src_offset0_len;
 
+  if (data.srcXInBytes != 0 || data.srcY != 0)
+    {
+      /* Adjust origin to the actual array data, else the CUDA 2D memory
+	 copy API calls below may fail to validate source/dest pointers
+	 correctly (especially for Fortran where the "virtual origin" of an
+	 array is often outside the stored data).  */
+      if (src_ord == -1)
+	data.srcHost = (const void *) ((const char *) data.srcHost
+				      + data.srcY * data.srcPitch
+				      + data.srcXInBytes);
+      else
+	data.srcDevice += data.srcY * data.srcPitch + data.srcXInBytes;
+      data.srcXInBytes = 0;
+      data.srcY = 0;
+    }
+
+  if (data.dstXInBytes != 0 || data.dstY != 0)
+    {
+      /* As above.  */
+      if (dst_ord == -1)
+	data.dstHost = (void *) ((char *) data.dstHost
+				 + data.dstY * data.dstPitch
+				 + data.dstXInBytes);
+      else
+	data.dstDevice += data.dstY * data.dstPitch + data.dstXInBytes;
+      data.dstXInBytes = 0;
+      data.dstY = 0;
+    }
+
   CUresult res = CUDA_CALL_NOCHECK (cuMemcpy2D, &data);
   if (res == CUDA_ERROR_INVALID_VALUE)
     /* If pitch > CU_DEVICE_ATTRIBUTE_MAX_PITCH or for device-to-device
@@ -1895,6 +1924,44 @@ GOMP_OFFLOAD_memcpy3d (int dst_ord, int src_ord, size_t dim2_size,
   data.srcY = src_offset1_len;
   data.srcZ = src_offset0_len;
 
+  if (data.srcXInBytes != 0 || data.srcY != 0 || data.srcZ != 0)
+    {
+      /* Adjust origin to the actual array data, else the CUDA 3D memory
+	 copy API call below may fail to validate source/dest pointers
+	 correctly (especially for Fortran where the "virtual origin" of an
+	 array is often outside the stored data).  */
+      if (src_ord == -1)
+	data.srcHost
+	  = (const void *) ((const char *) data.srcHost
+			    + (data.srcZ * data.srcHeight + data.srcY)
+			      * data.srcPitch
+			    + data.srcXInBytes);
+      else
+	data.srcDevice
+	  += (data.srcZ * data.srcHeight + data.srcY) * data.srcPitch
+	     + data.srcXInBytes;
+      data.srcXInBytes = 0;
+      data.srcY = 0;
+      data.srcZ = 0;
+    }
+
+  if (data.dstXInBytes != 0 || data.dstY != 0 || data.dstZ != 0)
+    {
+      /* As above.  */
+      if (dst_ord == -1)
+	data.dstHost = (void *) ((char *) data.dstHost
+				 + (data.dstZ * data.dstHeight + data.dstY)
+				   * data.dstPitch
+				 + data.dstXInBytes);
+      else
+	data.dstDevice
+	  += (data.dstZ * data.dstHeight + data.dstY) * data.dstPitch
+	     + data.dstXInBytes;
+      data.dstXInBytes = 0;
+      data.dstY = 0;
+      data.dstZ = 0;
+    }
+
   CUDA_CALL (cuMemcpy3D, &data);
   return true;
 }
diff --git a/libgomp/testsuite/libgomp.c-c++-common/memcpyxd-bias-1.c b/libgomp/testsuite/libgomp.c-c++-common/memcpyxd-bias-1.c
new file mode 100644
index 00000000000..6aa7b3d614f
--- /dev/null
+++ b/libgomp/testsuite/libgomp.c-c++-common/memcpyxd-bias-1.c
@@ -0,0 +1,61 @@
+/* { dg-do run } */
+
+#include <stdlib.h>
+#include <stdint.h>
+#include <assert.h>
+#include <omp.h>
+
+/* Say this is N rows and M columns.  */
+#define N 1024
+#define M 2048
+
+#define row_offset 256
+#define row_length 512
+#define col_offset 128
+#define col_length 384
+
+int
+main ()
+{
+  int *arr2d = (int *) calloc (N * M, sizeof (int));
+  uintptr_t dstptr;
+  int hostdev = omp_get_initial_device ();
+  int targdev;
+
+#pragma omp target enter data map(to: arr2d[col_offset*M:col_length*M])
+
+#pragma omp target map(from: targdev, dstptr) \
+		   map(present, tofrom: arr2d[col_offset*M:col_length*M])
+  {
+    for (int j = col_offset; j < col_offset + col_length; j++)
+      for (int i = row_offset; i < row_offset + row_length; i++)
+	arr2d[j * M + i]++;
+    targdev = omp_get_device_num ();
+    dstptr = (uintptr_t) arr2d;
+  }
+
+  /* Copy rectangular block back to the host.  */
+  {
+    size_t volume[2] = { col_length, row_length };
+    size_t offsets[2] = { col_offset, row_offset };
+    size_t dimensions[2] = { N, M };
+    omp_target_memcpy_rect ((void *) arr2d, (const void *) dstptr,
+			    sizeof (int), 2, &volume[0], &offsets[0],
+			    &offsets[0], &dimensions[0], &dimensions[0],
+			    hostdev, targdev);
+  }
+
+#pragma omp target exit data map(release: arr2d[col_offset*M:col_length*M])
+
+  for (int j = 0; j < N; j++)
+    for (int i = 0; i < M; i++)
+      if (i >= row_offset && i < row_offset + row_length
+	  && j >= col_offset && j < col_offset + col_length)
+	assert (arr2d[j * M + i] == 1);
+      else
+	assert (arr2d[j * M + i] == 0);
+
+  free (arr2d);
+
+  return 0;
+}

^ permalink raw reply	[flat|nested] 10+ messages in thread

* Re: [PATCH 1/5] OpenMP, NVPTX: memcpy[23]D bias correction
  2023-10-02 14:53     ` Julian Brown
@ 2023-12-19 20:45       ` Tobias Burnus
  0 siblings, 0 replies; 10+ messages in thread
From: Tobias Burnus @ 2023-12-19 20:45 UTC (permalink / raw)
  To: Julian Brown, Thomas Schwinge; +Cc: gcc-patches, fortran, jakub, Tom de Vries

Hi Julian & Thomas,

the patch LGTM - and seemingly also Thomas is somewhat fine with it -
and it includes the stand-alone testcase.

* * *

I guess, you don't know the answer to Thomas question, i.e. whether
that's a bug in CUDA or in our use of the CUDA API?

CUDA's spec itself,
https://docs.nvidia.com/cuda/cuda-driver-api/group__CUDA__MEM.html has
for cuMemcpy2D

‎  void* Start = (void*)((char*)srcHost+srcY*srcPitch + srcXInBytes);

and for cuMemcpy3D

‎  void* Start = (void*)((char*)srcHost+(srcZ*srcHeight+srcY)*srcPitch + srcXInBytes);

Thus, I assume we use it "properly", except that the CUDA writers
probably assumed that one allocates a big chunk of memory and work with
that memory and not just maps a subset.

This might or might not be stated in the manual in the following:
"Memory regions spanning over allocations that are both registered and
not registered with CUDA are not supported and will return
CUDA_ERROR_INVALID_VALUE." – where the question is whether everything
until 'start' really counts as "spanning".

Tobias


On 02.10.23 16:53, Julian Brown wrote:
> On Wed, 27 Sep 2023 00:57:58 +0200
> Thomas Schwinge <thomas@codesourcery.com> wrote:
>
>> On 2023-09-06T02:34:30-0700, Julian Brown <julian@codesourcery.com>
>> wrote:
>>> This patch works around behaviour of the 2D and 3D memcpy
>>> operations in the CUDA driver runtime.  Particularly in Fortran,
>>> the "base pointer" of an array (used for either source or
>>> destination of a host/device copy) may lie outside of data that is
>>> actually stored on the device.  The fix is to make sure that we use
>>> the first element of data to be transferred instead, and adjust
>>> parameters accordingly.
>> Do you (a) have a stand-alone test case for this (that is, not
>> depending on your other pending patches, so that this could go in
>> directly -- together with the before-FAIL test case).
> Thanks for the reply! Here's a version with a stand-alone test case.
>
>> Do you (b)
>> know if is this a bug in our use of the CUDA Driver API or rather in
>> CUDA itself?  If the latter, have you reported this to Nvidia?
> I don't think the CUDA behaviour is *wrong*, as such -- at least to the
> C/C++ way of thinking (or indeed a graphics-oriented way of thinking),
> one would normally think of an array as having a zero-based origin, and
> these 2D/3D memory copies would be intended as a way of updating just a
> part of an array (or texture) that has full duplicate copies on both
> the host and device.  Our use-case just happens to be a bit different,
> both because Fortran (internally) represents an array by a zero-based
> origin but may use 1-based (or whatever-based) indices, and because we
> support partial mappings of host arrays on the device in all three
> supported languages -- which amounts to much the same thing, actually.
>
> That said, it *could* be fixed in CUDA, though probably not in all the
> versions currently deployed out there in the world.  So I guess we'd
> still need a patch like this anyway.
>
> Julian
-----------------
Siemens Electronic Design Automation GmbH; Anschrift: Arnulfstraße 201, 80634 München; Gesellschaft mit beschränkter Haftung; Geschäftsführer: Thomas Heurung, Frank Thürauf; Sitz der Gesellschaft: München; Registergericht München, HRB 106955

^ permalink raw reply	[flat|nested] 10+ messages in thread

* [PATCH 3/5] OpenMP: Support strided and shaped-array updates for C++
  2023-07-03 21:33 [PATCH 0/5] [og13] OpenMP: strides, rectangular updates and array-shaping operator for "target update" Julian Brown
@ 2023-07-03 21:33 ` Julian Brown
  0 siblings, 0 replies; 10+ messages in thread
From: Julian Brown @ 2023-07-03 21:33 UTC (permalink / raw)
  To: gcc-patches; +Cc: fortran, jakub, tobias

This patch adds support for OpenMP 5.0 strided updates and the
array-shaping operator ("([x][y][z]) foo[0:n]...").  This is mostly for
C++ only so far, though necessary changes have been made to the C FE to
adjust for changes to shared data structures.

In terms of the implementation of various bits:

 - The OMP_ARRAY_SECTION tree code has been extended to take a 'stride'
   argument, and changes have been made throughout semantics.cc, etc. to
   take the new field into account -- including bounds checking.

 - A new type of cast operator has been added to represent the OpenMP
   array-shaping operator: OMP_ARRAYSHAPE_CAST_EXPR (1).

 - The address tokenization mechanism from previous patches has been
   extended with two new access kinds to represent noncontiguous array
   updates.

 - New mapping kinds have been added to represent noncontiguous updates:
   those which may be subject to array shaping, or have non-unit strides.
   These are processed by omp-low.cc into a kind of descriptor that is
   passed to the libgomp runtime (2).

The current patch reuses an extended version of the helper code for
omp_target_memcpy_rect, which may generate very many small host-device or
device-host copies.  (The "descriptor" has also been designed so reusing
that functionality is relatively straightforward.)  Optimising those
multiple copies, e.g. by packing them into a single transfer when it
would be beneficial, is left as the subject of a future patch.

This patch has some adjustments to the omp-low.cc code after Chung-Lin's
patch "OpenMP 5.0: Allow multiple clauses mapping same variable"
(325f085897efca59879a64704ab15f1763ecb807), relative to the version last
posted for mainline.

Notes:

(1) In a bit more detail: the array-shaping operator has the same
precedence as a C-style cast, but applies to the whole expression,
including array-section specifiers. We parse it initially as if it
applies to the "value" of the whole expression:

  ([x][y]) ptr[0:10:2][1:5:2]

i.e., something like:

  ([x][y]) (ptr[0:10:2][1:5:2])

or as if the cast applies to the innermost/right-hand side array
section. Then, a little later in parsing (cp_parser_omp_var_list_no_open),
we rewrite it to apply to the inner pointer instead:

  (([x][y]) ptr)[0:10:2][1:5:2]

and that means a genuine multi-dimensional array or an array-shaped
pointer can be handled pretty much the same for the rest of
compilation. We use VIEW_CONVERT_EXPR for the "cast", unless we're
processing a template definition, where we use a new tree code instead.

(2) The new map kinds work like this. An update directive starts
out with OMP_CLAUSE_TO or OMP_CLAUSE_FROM clauses representing the
block in question and the direction of the needed transfer. If we
detect a noncontiguous update, we emit a list of mapping nodes (type
OMP_CLAUSE_MAP, with new kinds, so the "mapping group" machinery in
gimplify.cc can be reused):

  OMP_CLAUSE_TO -->

  GOMP_MAP_TO_GRID (VIEW_CONVERT_EXPR<int[x][y]>(ptr) [len: <element-size>])
  GOMP_MAP_GRID_DIM 0 [len: 10]   (i.e. [0:10:2])
  GOMP_MAP_GRID_STRIDE 2
  GOMP_MAP_GRID_DIM 1 [len: 5]    (i.e. [1:5:2])
  GOMP_MAP_GRID_STRIDE 2

During omp-low.cc, this sequence is reformulated into:

  GOMP_MAP_TO_GRID (ptr) [len: <whole array size>]
  GOMP_MAP_TO_PSET (&ptr_desc [len: <desc size>])

"ptr_desc" is a struct, stored statically or constructed on the (host)
stack, containing arrays representing the size of the whole array, the
rectangular subregion to transfer, and the stride with which to walk
over elements in each dimension.

2023-07-03  Julian Brown  <julian@codesourcery.com>

gcc/c-family/
	* c-common.h (expand_array_base): Update prototype.
	* c-omp.cc (c_omp_address_inspector::map_supported_p): Support
	VIEW_CONVERT_EXPR and ADDR_EXPR codes.
	(omp_expand_grid_dim): New function.
	(omp_handle_noncontig_array): New function.
	(c_omp_address_inspector:expand_array_base): Remove DECL_P parameter.
	Support noncontiguous array updates.
	(c_omp_address_inspector::expand_component_selector): Support
	noncontiguous array updates.
	(c_omp_address_inspector::expand_map_clause): Update calls to
	expand_array_base.
	* c-pretty-print.cc (c_pretty_printer::postfix_expression): Add
	OMP_ARRAY_SECTION stride support.

gcc/c/
	* c-parser.cc (c_parser_postfix_expression_after_primary): Dummy stride
	support (for now).
	(struct omp_dim): Add stride support.
	(c_parser_omp_variable_list): Likewise.
	* c-tree.h (build_omp_array_section): Update prototype.
	* c-typeck.cc (mark_exp_read): Add stride support for
	OMP_ARRAY_SECTION.
	(build_omp_array_section): Add stride support.
	(handle_omp_array_sections_1): Add minimal stride support.

gcc/cp/
	* cp-objcp-common.cc (cp_common_init_ts): Add array-shape cast
	support.
	* cp-tree.def (OMP_ARRAYSHAPE_CAST_EXPR): Add tree code.
	* cp-tree.h (DECLTYPE_FOR_OMP_ARRAYSHAPE_CAST): Add flag.
	(cp_omp_create_arrayshape_type, cp_build_omp_arrayshape_cast): Add
	prototypes.
	(grok_omp_array_section, build_omp_array_section): Add stride
	parameters.
	* decl.cc (create_anon_array_type): New function.
	(cp_omp_create_arrayshape_type): New function.
	* decl2.cc (grok_omp_array_section): Add stride parameter.
	(min_vis_expr_r): Add OMP_ARRAYSHAPE_CAST_EXPR support.
	* error.cc (dump_expr): Add stride support for OMP_ARRAY_SECTION.
	* mangle.cc (write_expression): Add OMP_ARRAYSHAPE_CAST_EXPR support.
	* operators.def (OMP_ARRAYSHAPE_CAST_EXPR): Add.
	* parser.cc (cp_parser_new): Initialise omp_array_shaping_op_p and
	omp_has_array_shape_p fields.
	(cp_parser_statement_expr): Don't allow array shaping op in statement
	exprs.
	(cp_parser_postfix_open_square_expression): Add stride parsing for
	array sections.  Use array section code to represent array refs if we
	have an array-shaping operator.
	(cp_parser_parenthesized_expression_list): Don't allow array-shaping
	op here.
	(cp_parser_cast_expression): Add array-shaping operator parsing.
	(cp_parser_lambda_expression): Don't allow array-shaping op in lambda
	body.
	(cp_parser_braced_list): Don't allow array-shaping op in braced list.
	(struct omp_dim): Add stride field.
	(cp_parser_var_list_no_open): Add stride/array shape support.
	(cp_parser_omp_target_update): Handle noncontiguous updates.
	* parser.h (cp_parser): Add omp_array_shaping_op_p and
	omp_has_array_shape_p fields.
	* pt.cc (tsubst): Add array-shape cast support.
	(tsubst_copy, tsubst_copy_and_build): Likewise. Add stride support for
	OMP_ARRAY_SECTION.
	(tsubst_omp_clause_decl): Add stride support for OMP_ARRAY_SECTION.
	* semantics.cc (handle_omp_array_sections_1): Add DISCONTIGUOUS
	parameter and stride support.
	(omp_array_section_low_bound): New function.
	(handle_omp_array_sections): Add DISCONTIGUOUS parameter and stride
	support.
	(finish_omp_clauses): Update calls to handle_omp_array_sections, and
	add noncontiguous array update support.
	(cp_build_omp_arrayshape_cast): New function.
	* typeck.cc (structural_comptypes): Add array-shape cast support.
	(build_omp_array_section): Add stride parameter.
	(check_for_casting_away_constness): Add OMP_ARRAYSHAPE_CAST_EXPR
	support.

gcc/
	* gimplify.cc (omp_group_last, omp_group_base): Add GOMP_MAP_TO_GRID,
	GOMP_MAP_FROM_GRID support.
	(gimplify_adjust_omp_clauses): Support new GOMP_MAP_GRID_DIM,
	GOMP_MAP_GRID_STRIDE mapping nodes.  Don't crash on e.g. misuse of
	ADDR_EXPR in mapping clauses.
	* omp-general.cc (omp_parse_noncontiguous_array): New function.
	(omp_parse_access_method): Add noncontiguous array support.
	(omp_parse_structure_base): Add array-shaping support.
	(debug_omp_tokenized_addr): Add ACCESS_NONCONTIG_ARRAY,
	ACCESS_NONCONTIG_REF_TO_ARRAY token support.
	* omp-general.h (access_method_kinds): Add ACCESS_NONCONTIG_ARRAY and
	ACCESS_NONCONTIG_REF_TO_ARRAY access kinds.
	* omp-low.cc (omp_noncontig_descriptor_type): New function.
	(scan_sharing_clauses): Support noncontiguous array updates.
	(lower_omp_target): Likewise.
	* tree-pretty-print.cc (dump_omp_clause): Add GOMP_MAP_TO_GRID,
	GOMP_MAP_FROM_GRID, GOMP_MAP_GRID_DIM, GOMP_MAP_GRID_STRIDE map kinds.
	(dump_generic_node): Add stride support for OMP_ARRAY_SECTION.
	* tree.def (OMP_ARRAY_SECTION): Add stride argument.

include/
	* gomp-constants.h (gomp_map_kind): Add GOMP_MAP_TO_GRID,
	GOMP_MAP_FROM_GRID, GOMP_MAP_GRID_DIM, GOMP_MAP_GRID_STRIDE map kinds.

gcc/testsuite/
	* g++.dg/gomp/array-shaping-1.C: New test.
	* g++.dg/gomp/array-shaping-2.C: New test.
	* g++.dg/gomp/bad-array-shaping-1.C: New test.
	* g++.dg/gomp/bad-array-shaping-2.C: New test.
	* g++.dg/gomp/bad-array-shaping-3.C: New test.
	* g++.dg/gomp/bad-array-shaping-4.C: New test.
	* g++.dg/gomp/bad-array-shaping-5.C: New test.
	* g++.dg/gomp/bad-array-shaping-6.C: New test.
	* g++.dg/gomp/bad-array-shaping-7.C: New test.
	* g++.dg/gomp/bad-array-shaping-8.C: New test.

libgomp/
	* libgomp.h (omp_noncontig_array_desc): New struct.
	* target.c (omp_target_memcpy_rect_worker): Add stride array
	parameter.  Forward declare.  Add STRIDES parameter and strided
	update support.
	(gomp_update): Add noncontiguous (strided/shaped) update support.
	* testsuite/libgomp.c++/array-shaping-1.C: New test.
	* testsuite/libgomp.c++/array-shaping-2.C: New test.
	* testsuite/libgomp.c++/array-shaping-3.C: New test.
	* testsuite/libgomp.c++/array-shaping-4.C: New test.
	* testsuite/libgomp.c++/array-shaping-5.C: New test.
	* testsuite/libgomp.c++/array-shaping-6.C: New test.
	* testsuite/libgomp.c++/array-shaping-7.C: New test.
	* testsuite/libgomp.c++/array-shaping-8.C: New test.
	* testsuite/libgomp.c++/array-shaping-9.C: New test.
	* testsuite/libgomp.c++/array-shaping-10.C: New test.
	* testsuite/libgomp.c++/array-shaping-11.C: New test.
	* testsuite/libgomp.c++/array-shaping-12.C: New test.
	* testsuite/libgomp.c++/array-shaping-13.C: New test.
---
 gcc/c-family/c-common.h                       |   2 +-
 gcc/c-family/c-omp.cc                         | 206 +++++++-
 gcc/c-family/c-pretty-print.cc                |   5 +
 gcc/c/c-parser.cc                             |  32 +-
 gcc/c/c-tree.h                                |   2 +-
 gcc/c/c-typeck.cc                             |  26 +-
 gcc/cp/cp-objcp-common.cc                     |   1 +
 gcc/cp/cp-tree.def                            |   1 +
 gcc/cp/cp-tree.h                              |  13 +-
 gcc/cp/decl.cc                                |  75 +++
 gcc/cp/decl2.cc                               |  19 +-
 gcc/cp/error.cc                               |   5 +
 gcc/cp/mangle.cc                              |   1 +
 gcc/cp/operators.def                          |   1 +
 gcc/cp/parser.cc                              | 303 ++++++++++-
 gcc/cp/parser.h                               |   7 +
 gcc/cp/pt.cc                                  |  39 +-
 gcc/cp/semantics.cc                           | 262 ++++++++--
 gcc/cp/typeck.cc                              |  12 +-
 gcc/gimplify.cc                               |  44 +-
 gcc/omp-general.cc                            |  47 ++
 gcc/omp-general.h                             |   4 +-
 gcc/omp-low.cc                                | 402 ++++++++++++++-
 gcc/testsuite/g++.dg/gomp/array-shaping-1.C   |  22 +
 gcc/testsuite/g++.dg/gomp/array-shaping-2.C   | 134 +++++
 .../g++.dg/gomp/bad-array-shaping-1.C         |  47 ++
 .../g++.dg/gomp/bad-array-shaping-2.C         |  52 ++
 .../g++.dg/gomp/bad-array-shaping-3.C         |  53 ++
 .../g++.dg/gomp/bad-array-shaping-4.C         |  60 +++
 .../g++.dg/gomp/bad-array-shaping-5.C         |  55 ++
 .../g++.dg/gomp/bad-array-shaping-6.C         |  59 +++
 .../g++.dg/gomp/bad-array-shaping-7.C         |  48 ++
 .../g++.dg/gomp/bad-array-shaping-8.C         |  50 ++
 gcc/tree-pretty-print.cc                      |  17 +
 gcc/tree.def                                  |   2 +-
 include/gomp-constants.h                      |   7 +-
 libgomp/libgomp.h                             |  14 +
 libgomp/target.c                              | 254 ++++++----
 .../testsuite/libgomp.c++/array-shaping-1.C   | 469 ++++++++++++++++++
 .../testsuite/libgomp.c++/array-shaping-10.C  |  61 +++
 .../testsuite/libgomp.c++/array-shaping-11.C  |  63 +++
 .../testsuite/libgomp.c++/array-shaping-12.C  |  65 +++
 .../testsuite/libgomp.c++/array-shaping-13.C  |  89 ++++
 .../testsuite/libgomp.c++/array-shaping-2.C   |  38 ++
 .../testsuite/libgomp.c++/array-shaping-3.C   |  38 ++
 .../testsuite/libgomp.c++/array-shaping-4.C   |  38 ++
 .../testsuite/libgomp.c++/array-shaping-5.C   |  38 ++
 .../testsuite/libgomp.c++/array-shaping-6.C   |  54 ++
 .../testsuite/libgomp.c++/array-shaping-7.C   |  54 ++
 .../testsuite/libgomp.c++/array-shaping-8.C   |  65 +++
 .../testsuite/libgomp.c++/array-shaping-9.C   |  95 ++++
 51 files changed, 3334 insertions(+), 216 deletions(-)
 create mode 100644 gcc/testsuite/g++.dg/gomp/array-shaping-1.C
 create mode 100644 gcc/testsuite/g++.dg/gomp/array-shaping-2.C
 create mode 100644 gcc/testsuite/g++.dg/gomp/bad-array-shaping-1.C
 create mode 100644 gcc/testsuite/g++.dg/gomp/bad-array-shaping-2.C
 create mode 100644 gcc/testsuite/g++.dg/gomp/bad-array-shaping-3.C
 create mode 100644 gcc/testsuite/g++.dg/gomp/bad-array-shaping-4.C
 create mode 100644 gcc/testsuite/g++.dg/gomp/bad-array-shaping-5.C
 create mode 100644 gcc/testsuite/g++.dg/gomp/bad-array-shaping-6.C
 create mode 100644 gcc/testsuite/g++.dg/gomp/bad-array-shaping-7.C
 create mode 100644 gcc/testsuite/g++.dg/gomp/bad-array-shaping-8.C
 create mode 100644 libgomp/testsuite/libgomp.c++/array-shaping-1.C
 create mode 100644 libgomp/testsuite/libgomp.c++/array-shaping-10.C
 create mode 100644 libgomp/testsuite/libgomp.c++/array-shaping-11.C
 create mode 100644 libgomp/testsuite/libgomp.c++/array-shaping-12.C
 create mode 100644 libgomp/testsuite/libgomp.c++/array-shaping-13.C
 create mode 100644 libgomp/testsuite/libgomp.c++/array-shaping-2.C
 create mode 100644 libgomp/testsuite/libgomp.c++/array-shaping-3.C
 create mode 100644 libgomp/testsuite/libgomp.c++/array-shaping-4.C
 create mode 100644 libgomp/testsuite/libgomp.c++/array-shaping-5.C
 create mode 100644 libgomp/testsuite/libgomp.c++/array-shaping-6.C
 create mode 100644 libgomp/testsuite/libgomp.c++/array-shaping-7.C
 create mode 100644 libgomp/testsuite/libgomp.c++/array-shaping-8.C
 create mode 100644 libgomp/testsuite/libgomp.c++/array-shaping-9.C

diff --git a/gcc/c-family/c-common.h b/gcc/c-family/c-common.h
index 756358f3fd8..ea6c479cd62 100644
--- a/gcc/c-family/c-common.h
+++ b/gcc/c-family/c-common.h
@@ -1376,7 +1376,7 @@ public:
   bool maybe_zero_length_array_section (tree);
 
   tree * expand_array_base (tree *, vec<omp_addr_token *> &, tree, unsigned *,
-			    c_omp_region_type, bool);
+			    c_omp_region_type);
   tree * expand_component_selector (tree *, vec<omp_addr_token *> &, tree,
 				    unsigned *);
   tree * expand_map_clause (tree *, tree, vec<omp_addr_token *> &,
diff --git a/gcc/c-family/c-omp.cc b/gcc/c-family/c-omp.cc
index 17f3d71c655..9e307411f76 100644
--- a/gcc/c-family/c-omp.cc
+++ b/gcc/c-family/c-omp.cc
@@ -4042,7 +4042,9 @@ c_omp_address_inspector::map_supported_p ()
 	 || TREE_CODE (t) == POINTER_PLUS_EXPR
 	 || TREE_CODE (t) == NON_LVALUE_EXPR
 	 || TREE_CODE (t) == OMP_ARRAY_SECTION
-	 || TREE_CODE (t) == NOP_EXPR)
+	 || TREE_CODE (t) == NOP_EXPR
+	 || TREE_CODE (t) == VIEW_CONVERT_EXPR
+	 || TREE_CODE (t) == ADDR_EXPR)
     if (TREE_CODE (t) == COMPOUND_EXPR)
       t = TREE_OPERAND (t, 1);
     else
@@ -4192,21 +4194,95 @@ omp_expand_access_chain (tree *pc, tree expr,
   return pc;
 }
 
+static tree *
+omp_expand_grid_dim (location_t loc, tree *pc, tree decl)
+{
+  if (TREE_CODE (decl) == OMP_ARRAY_SECTION)
+    pc = omp_expand_grid_dim (loc, pc, TREE_OPERAND (decl, 0));
+  else
+    return pc;
+
+  tree c = *pc;
+  tree low_bound = TREE_OPERAND (decl, 1);
+  tree length = TREE_OPERAND (decl, 2);
+  tree stride = TREE_OPERAND (decl, 3);
+
+  tree cd = build_omp_clause (loc, OMP_CLAUSE_MAP);
+  OMP_CLAUSE_SET_MAP_KIND (cd, GOMP_MAP_GRID_DIM);
+  OMP_CLAUSE_DECL (cd) = unshare_expr (low_bound);
+  OMP_CLAUSE_SIZE (cd) = unshare_expr (length);
+
+  if (stride && !integer_onep (stride))
+    {
+      tree cs = build_omp_clause (loc, OMP_CLAUSE_MAP);
+      OMP_CLAUSE_SET_MAP_KIND (cs, GOMP_MAP_GRID_STRIDE);
+      OMP_CLAUSE_DECL (cs) = unshare_expr (stride);
+
+      OMP_CLAUSE_CHAIN (cs) = OMP_CLAUSE_CHAIN (c);
+      OMP_CLAUSE_CHAIN (cd) = cs;
+      OMP_CLAUSE_CHAIN (c) = cd;
+      pc = &OMP_CLAUSE_CHAIN (cd);
+    }
+  else
+    {
+      OMP_CLAUSE_CHAIN (cd) = OMP_CLAUSE_CHAIN (c);
+      OMP_CLAUSE_CHAIN (c) = cd;
+      pc = &OMP_CLAUSE_CHAIN (c);
+    }
+
+  return pc;
+}
+
+tree *
+omp_handle_noncontig_array (location_t loc, tree *pc, tree c, tree base)
+{
+  tree type;
+
+  if (POINTER_TYPE_P (TREE_TYPE (base)))
+    type = TREE_TYPE (TREE_TYPE (base));
+  else
+    type = strip_array_types (TREE_TYPE (base));
+
+  tree c_map = build_omp_clause (loc, OMP_CLAUSE_MAP);
+
+  OMP_CLAUSE_DECL (c_map) = unshare_expr (base);
+  /* Use the element size (or pointed-to type size) here.  */
+  OMP_CLAUSE_SIZE (c_map) = TYPE_SIZE_UNIT (type);
+
+  switch (OMP_CLAUSE_CODE (c))
+    {
+    case OMP_CLAUSE_TO:
+      OMP_CLAUSE_SET_MAP_KIND (c_map, GOMP_MAP_TO_GRID);
+      break;
+    case OMP_CLAUSE_FROM:
+      OMP_CLAUSE_SET_MAP_KIND (c_map, GOMP_MAP_FROM_GRID);
+      break;
+    default:
+      gcc_unreachable ();
+    }
+
+  OMP_CLAUSE_CHAIN (c_map) = OMP_CLAUSE_CHAIN (c);
+
+  *pc = c_map;
+
+  return omp_expand_grid_dim (loc, pc, OMP_CLAUSE_DECL (c));
+}
+
 /* Translate "array_base_decl access_method" to OMP mapping clauses.  */
 
 tree *
 c_omp_address_inspector::expand_array_base (tree *pc,
 					    vec<omp_addr_token *> &addr_tokens,
 					    tree expr, unsigned *idx,
-					    c_omp_region_type ort,
-					    bool decl_p)
+					    c_omp_region_type ort)
 {
   using namespace omp_addr_tokenizer;
   tree c = *pc;
   location_t loc = OMP_CLAUSE_LOCATION (c);
   int i = *idx;
   tree decl = addr_tokens[i + 1]->expr;
-  bool declare_target_p = (decl_p
+  bool decl_p = DECL_P (decl);
+  bool declare_target_p = (DECL_P (decl)
 			   && is_global_var (decl)
 			   && lookup_attribute ("omp declare target",
 						DECL_ATTRIBUTES (decl)));
@@ -4218,6 +4294,7 @@ c_omp_address_inspector::expand_array_base (tree *pc,
   unsigned consume_tokens = 2;
   bool target = (ort & C_ORT_TARGET) != 0;
   bool openmp = (ort & C_ORT_OMP) != 0;
+  unsigned acc = i + 1;
 
   gcc_assert (i == 0);
 
@@ -4230,7 +4307,15 @@ c_omp_address_inspector::expand_array_base (tree *pc,
       return pc;
     }
 
-  switch (addr_tokens[i + 1]->u.access_kind)
+  if (!map_p && chain_p)
+    {
+      /* See comment in c_omp_address_inspector::expand_component_selector.  */
+      while (acc + 1 < addr_tokens.length ()
+	     && addr_tokens[acc + 1]->type == ACCESS_METHOD)
+	acc++;
+    }
+
+  switch (addr_tokens[acc]->u.access_kind)
     {
     case ACCESS_DIRECT:
       if (decl_p && !target)
@@ -4474,6 +4559,40 @@ c_omp_address_inspector::expand_array_base (tree *pc,
       }
       break;
 
+    case ACCESS_NONCONTIG_ARRAY:
+      {
+	gcc_assert (!map_p);
+
+	tree base = addr_tokens[acc]->expr;
+
+	if (decl_p)
+	  c_common_mark_addressable_vec (base);
+
+	pc = omp_handle_noncontig_array (loc, pc, c, base);
+	consume_tokens = (acc + 1) - i;
+	chain_p = false;
+      }
+      break;
+
+    case ACCESS_NONCONTIG_REF_TO_ARRAY:
+      {
+	gcc_assert (!map_p);
+
+	if (decl_p)
+	  c_common_mark_addressable_vec (addr_tokens[acc]->expr);
+
+	/* Or here.  */
+	gcc_assert (!chain_p);
+
+	tree base = addr_tokens[i + 1]->expr;
+	base = convert_from_reference (base);
+
+	pc = omp_handle_noncontig_array (loc, pc, c, base);
+	consume_tokens = (acc + 1) - i;
+	chain_p = false;
+      }
+      break;
+
     default:
       *idx = i + consume_tokens;
       return NULL;
@@ -4524,8 +4643,27 @@ c_omp_address_inspector::expand_component_selector (tree *pc,
   tree c2 = NULL_TREE, c3 = NULL_TREE;
   bool chain_p = omp_access_chain_p (addr_tokens, i + 1);
   bool map_p = OMP_CLAUSE_CODE (c) == OMP_CLAUSE_MAP;
+  unsigned acc = i + 1;
 
-  switch (addr_tokens[i + 1]->u.access_kind)
+  if (!map_p && chain_p)
+    {
+      /* We have a non-map clause (i.e. to/from for an "update" directive),
+	 and we might have a noncontiguous array section at the end of a
+	 chain of other accesses, e.g. pointer indirections like this:
+
+	   struct_base_decl access_pointer access_pointer component_selector
+	     access_pointer access_pointer access_noncontig_array
+
+	 We only need to process the last access in this case, so skip
+	 over previous accesses.  */
+
+      while (acc + 1 < addr_tokens.length ()
+	     && addr_tokens[acc + 1]->type == ACCESS_METHOD)
+	acc++;
+      chain_p = false;
+    }
+
+  switch (addr_tokens[acc]->u.access_kind)
     {
     case ACCESS_DIRECT:
     case ACCESS_INDEXED_ARRAY:
@@ -4535,7 +4673,7 @@ c_omp_address_inspector::expand_component_selector (tree *pc,
       {
 	/* Copy the referenced object.  Note that we also do this for !MAP_P
 	   clauses.  */
-	tree obj = convert_from_reference (addr_tokens[i + 1]->expr);
+	tree obj = convert_from_reference (addr_tokens[acc]->expr);
 	OMP_CLAUSE_DECL (c) = obj;
 	OMP_CLAUSE_SIZE (c) = TYPE_SIZE_UNIT (TREE_TYPE (obj));
 
@@ -4544,7 +4682,7 @@ c_omp_address_inspector::expand_component_selector (tree *pc,
 
 	c2 = build_omp_clause (loc, OMP_CLAUSE_MAP);
 	OMP_CLAUSE_SET_MAP_KIND (c2, GOMP_MAP_ATTACH_DETACH);
-	OMP_CLAUSE_DECL (c2) = addr_tokens[i + 1]->expr;
+	OMP_CLAUSE_DECL (c2) = addr_tokens[acc]->expr;
 	OMP_CLAUSE_SIZE (c2) = size_zero_node;
       }
       break;
@@ -4555,15 +4693,15 @@ c_omp_address_inspector::expand_component_selector (tree *pc,
 	  break;
 
 	tree virtual_origin
-	  = convert_from_reference (addr_tokens[i + 1]->expr);
+	  = convert_from_reference (addr_tokens[acc]->expr);
 	virtual_origin = build_fold_addr_expr (virtual_origin);
 	virtual_origin = fold_convert_loc (loc, ptrdiff_type_node,
 					   virtual_origin);
-	tree data_addr = omp_accessed_addr (addr_tokens, i + 1, expr);
+	tree data_addr = omp_accessed_addr (addr_tokens, acc, expr);
 
 	c2 = build_omp_clause (loc, OMP_CLAUSE_MAP);
 	OMP_CLAUSE_SET_MAP_KIND (c2, GOMP_MAP_ATTACH_DETACH);
-	OMP_CLAUSE_DECL (c2) = addr_tokens[i + 1]->expr;
+	OMP_CLAUSE_DECL (c2) = addr_tokens[acc]->expr;
 	OMP_CLAUSE_SIZE (c2)
 	  = fold_build2_loc (loc, MINUS_EXPR, ptrdiff_type_node,
 			     fold_convert_loc (loc, ptrdiff_type_node,
@@ -4580,12 +4718,12 @@ c_omp_address_inspector::expand_component_selector (tree *pc,
 
 	tree virtual_origin
 	  = fold_convert_loc (loc, ptrdiff_type_node,
-			      addr_tokens[i + 1]->expr);
-	tree data_addr = omp_accessed_addr (addr_tokens, i + 1, expr);
+			      addr_tokens[acc]->expr);
+	tree data_addr = omp_accessed_addr (addr_tokens, acc, expr);
 
 	c2 = build_omp_clause (loc, OMP_CLAUSE_MAP);
 	OMP_CLAUSE_SET_MAP_KIND (c2, GOMP_MAP_ATTACH_DETACH);
-	OMP_CLAUSE_DECL (c2) = addr_tokens[i + 1]->expr;
+	OMP_CLAUSE_DECL (c2) = addr_tokens[acc]->expr;
 	OMP_CLAUSE_SIZE (c2)
 	  = fold_build2_loc (loc, MINUS_EXPR, ptrdiff_type_node,
 			     fold_convert_loc (loc, ptrdiff_type_node,
@@ -4600,10 +4738,10 @@ c_omp_address_inspector::expand_component_selector (tree *pc,
 	if (!map_p)
 	  break;
 
-	tree ptr = convert_from_reference (addr_tokens[i + 1]->expr);
+	tree ptr = convert_from_reference (addr_tokens[acc]->expr);
 	tree virtual_origin = fold_convert_loc (loc, ptrdiff_type_node,
 						ptr);
-	tree data_addr = omp_accessed_addr (addr_tokens, i + 1, expr);
+	tree data_addr = omp_accessed_addr (addr_tokens, acc, expr);
 
 	/* Attach the pointer...  */
 	c2 = build_omp_clause (OMP_CLAUSE_LOCATION (c), OMP_CLAUSE_MAP);
@@ -4618,13 +4756,38 @@ c_omp_address_inspector::expand_component_selector (tree *pc,
 	/* ...and also the reference.  */
 	c3 = build_omp_clause (OMP_CLAUSE_LOCATION (c), OMP_CLAUSE_MAP);
 	OMP_CLAUSE_SET_MAP_KIND (c3, GOMP_MAP_ATTACH_DETACH);
-	OMP_CLAUSE_DECL (c3) = addr_tokens[i + 1]->expr;
+	OMP_CLAUSE_DECL (c3) = addr_tokens[acc]->expr;
 	OMP_CLAUSE_SIZE (c3) = size_zero_node;
       }
       break;
 
+    case ACCESS_NONCONTIG_ARRAY:
+      {
+	gcc_assert (!map_p);
+
+	/* We don't expect to see further accesses here.  */
+	gcc_assert (!chain_p);
+
+	pc = omp_handle_noncontig_array (loc, pc, c, addr_tokens[acc]->expr);
+      }
+      break;
+
+    case ACCESS_NONCONTIG_REF_TO_ARRAY:
+      {
+	gcc_assert (!map_p);
+
+	/* Or here.  */
+	gcc_assert (!chain_p);
+
+	tree base = addr_tokens[acc]->expr;
+	base = convert_from_reference (base);
+
+	pc = omp_handle_noncontig_array (loc, pc, c, base);
+      }
+      break;
+
     default:
-      *idx = i + 2;
+      *idx = acc + 1;
       return NULL;
     }
 
@@ -4642,8 +4805,7 @@ c_omp_address_inspector::expand_component_selector (tree *pc,
       pc = &OMP_CLAUSE_CHAIN (c);
     }
 
-  i += 2;
-  *idx = i;
+  *idx = acc + 1;
 
   if (chain_p && map_p)
     return omp_expand_access_chain (pc, expr, addr_tokens, idx);
@@ -4671,7 +4833,7 @@ c_omp_address_inspector::expand_map_clause (tree *pc, tree expr,
 	  && addr_tokens[i]->u.structure_base_kind == BASE_DECL
 	  && addr_tokens[i + 1]->type == ACCESS_METHOD)
 	{
-	  pc = expand_array_base (pc, addr_tokens, expr, &i, ort, true);
+	  pc = expand_array_base (pc, addr_tokens, expr, &i, ort);
 	  if (pc == NULL)
 	    return NULL;
 	}
@@ -4680,7 +4842,7 @@ c_omp_address_inspector::expand_map_clause (tree *pc, tree expr,
 	       && addr_tokens[i]->u.structure_base_kind == BASE_ARBITRARY_EXPR
 	       && addr_tokens[i + 1]->type == ACCESS_METHOD)
 	{
-	  pc = expand_array_base (pc, addr_tokens, expr, &i, ort, false);
+	  pc = expand_array_base (pc, addr_tokens, expr, &i, ort);
 	  if (pc == NULL)
 	    return NULL;
 	}
diff --git a/gcc/c-family/c-pretty-print.cc b/gcc/c-family/c-pretty-print.cc
index 225ac7ef285..fc99b951f98 100644
--- a/gcc/c-family/c-pretty-print.cc
+++ b/gcc/c-family/c-pretty-print.cc
@@ -1623,6 +1623,11 @@ c_pretty_printer::postfix_expression (tree e)
       pp_colon (this);
       if (TREE_OPERAND (e, 2))
 	expression (TREE_OPERAND (e, 2));
+      if (TREE_OPERAND (e, 3))
+	{
+	  pp_colon (this);
+	  expression (TREE_OPERAND (e, 3));
+	}
       pp_c_right_bracket (this);
       break;
 
diff --git a/gcc/c/c-parser.cc b/gcc/c/c-parser.cc
index 1b9417ac289..280426ddf10 100644
--- a/gcc/c/c-parser.cc
+++ b/gcc/c/c-parser.cc
@@ -11386,7 +11386,7 @@ c_parser_postfix_expression_after_primary (c_parser *parser,
 	      start = expr.get_start ();
 	      finish = parser->tokens_buf[0].location;
 	      expr.value = build_omp_array_section (op_loc, expr.value, idx,
-						    len);
+						    len, NULL_TREE /* fixme */);
 	      set_c_expr_source_range (&expr, start, finish);
 	      expr.original_code = ERROR_MARK;
 	      expr.original_type = NULL;
@@ -13956,11 +13956,11 @@ c_parser_oacc_wait_list (c_parser *parser, location_t clause_loc, tree list)
 
 struct omp_dim
 {
-  tree low_bound, length;
+  tree low_bound, length, stride;
   location_t loc;
   bool no_colon;
-  omp_dim (tree lb, tree len, location_t lo, bool nc)
-  : low_bound (lb), length (len), loc (lo), no_colon (nc) {}
+  omp_dim (tree lb, tree len, tree str, location_t lo, bool nc)
+  : low_bound (lb), length (len), stride (str), loc (lo), no_colon (nc) {}
 };
 
 static tree
@@ -14089,7 +14089,9 @@ c_parser_omp_variable_list (c_parser *parser,
 		{
 		  tree low_bound = TREE_OPERAND (decl, 1);
 		  tree length = TREE_OPERAND (decl, 2);
-		  dims.safe_push (omp_dim (low_bound, length, loc, false));
+		  tree stride = TREE_OPERAND (decl, 3);
+		  dims.safe_push (omp_dim (low_bound, length, stride, loc,
+					   false));
 		  decl = TREE_OPERAND (decl, 0);
 		}
 
@@ -14105,21 +14107,22 @@ c_parser_omp_variable_list (c_parser *parser,
 		  else if (TREE_CODE (decl) == INDIRECT_REF)
 		    {
 		      dims.safe_push (omp_dim (integer_zero_node,
-					       integer_one_node, loc, true));
+					       integer_one_node, NULL_TREE, loc,
+					       true));
 		      decl = TREE_OPERAND (decl, 0);
 		    }
 		  else  /* ARRAY_REF. */
 		    {
 		      tree index = TREE_OPERAND (decl, 1);
-		      dims.safe_push (omp_dim (index, integer_one_node, loc,
-					       true));
+		      dims.safe_push (omp_dim (index, integer_one_node,
+					       NULL_TREE, loc, true));
 		      decl = TREE_OPERAND (decl, 0);
 		    }
 		}
 
 	      for (int i = dims.length () - 1; i >= 0; i--)
 		decl = build_omp_array_section (loc,  decl, dims[i].low_bound,
-						dims[i].length);
+						dims[i].length, dims[i].stride);
 	    }
 	  else if (TREE_CODE (decl) == INDIRECT_REF)
 	    {
@@ -14129,7 +14132,7 @@ c_parser_omp_variable_list (c_parser *parser,
 	      STRIP_NOPS (decl);
 
 	      decl = build_omp_array_section (loc, decl, integer_zero_node,
-					      integer_one_node);
+					      integer_one_node, NULL_TREE);
 	    }
 	  else if (TREE_CODE (decl) == ARRAY_REF)
 	    {
@@ -14138,7 +14141,8 @@ c_parser_omp_variable_list (c_parser *parser,
 	      decl = TREE_OPERAND (decl, 0);
 	      STRIP_NOPS (decl);
 
-	      decl = build_omp_array_section (loc, decl, idx, integer_one_node);
+	      decl = build_omp_array_section (loc, decl, idx, integer_one_node,
+					      NULL_TREE);
 	    }
 	  else if (TREE_CODE (decl) == NON_LVALUE_EXPR
 		   || CONVERT_EXPR_P (decl))
@@ -14293,7 +14297,8 @@ c_parser_omp_variable_list (c_parser *parser,
 		      break;
 		    }
 
-		  dims.safe_push (omp_dim (low_bound, length, loc, no_colon));
+		  dims.safe_push (omp_dim (low_bound, length, NULL_TREE, loc,
+				  no_colon));
 		}
 
 	      if (t != error_mark_node)
@@ -14317,7 +14322,8 @@ c_parser_omp_variable_list (c_parser *parser,
 		    for (unsigned i = 0; i < dims.length (); i++)
 		      t = build_omp_array_section (clause_loc, t,
 						   dims[i].low_bound,
-						   dims[i].length);
+						   dims[i].length,
+						   dims[i].stride);
 		}
 
 	      if ((kind == OMP_CLAUSE_DEPEND || kind == OMP_CLAUSE_AFFINITY)
diff --git a/gcc/c/c-tree.h b/gcc/c/c-tree.h
index 00aef7381d7..37790bab640 100644
--- a/gcc/c/c-tree.h
+++ b/gcc/c/c-tree.h
@@ -765,7 +765,7 @@ extern tree composite_type (tree, tree);
 extern tree build_component_ref (location_t, tree, tree, location_t,
 				 location_t);
 extern tree build_array_ref (location_t, tree, tree);
-extern tree build_omp_array_section (location_t, tree, tree, tree);
+extern tree build_omp_array_section (location_t, tree, tree, tree, tree);
 extern tree build_external_ref (location_t, tree, bool, tree *);
 extern void pop_maybe_used (bool);
 extern struct c_expr c_expr_sizeof_expr (location_t, struct c_expr);
diff --git a/gcc/c/c-typeck.cc b/gcc/c/c-typeck.cc
index a54d310403a..0eff41e7567 100644
--- a/gcc/c/c-typeck.cc
+++ b/gcc/c/c-typeck.cc
@@ -2036,6 +2036,8 @@ mark_exp_read (tree exp)
 	mark_exp_read (TREE_OPERAND (exp, 1));
       if (TREE_OPERAND (exp, 2))
 	mark_exp_read (TREE_OPERAND (exp, 2));
+      if (TREE_OPERAND (exp, 3))
+	mark_exp_read (TREE_OPERAND (exp, 3));
       break;
     default:
       break;
@@ -2921,7 +2923,8 @@ build_array_ref (location_t loc, tree array, tree index)
    instead.  */
 
 tree
-build_omp_array_section (location_t loc, tree array, tree index, tree length)
+build_omp_array_section (location_t loc, tree array, tree index, tree length,
+			 tree stride)
 {
   tree idxtype;
 
@@ -2958,7 +2961,8 @@ build_omp_array_section (location_t loc, tree array, tree index, tree length)
   else
     sectype = build_array_type (eltype, idxtype);
 
-  return build3_loc (loc, OMP_ARRAY_SECTION, sectype, array, index, length);
+  return build4_loc (loc, OMP_ARRAY_SECTION, sectype, array, index, length,
+		     stride);
 }
 
 \f
@@ -13712,7 +13716,7 @@ handle_omp_array_sections_1 (tree c, tree t, vec<tree> &types,
 			     bool &maybe_zero_len, unsigned int &first_non_one,
 			     bool &non_contiguous, enum c_omp_region_type ort)
 {
-  tree ret, low_bound, length, type;
+  tree ret, low_bound, length, stride, type;
   bool openacc = (ort & C_ORT_ACC) != 0;
   if (TREE_CODE (t) != OMP_ARRAY_SECTION)
     {
@@ -13798,8 +13802,11 @@ handle_omp_array_sections_1 (tree c, tree t, vec<tree> &types,
   type = TREE_TYPE (ret);
   low_bound = TREE_OPERAND (t, 1);
   length = TREE_OPERAND (t, 2);
+  stride = TREE_OPERAND (t, 3);
 
-  if (low_bound == error_mark_node || length == error_mark_node)
+  if (low_bound == error_mark_node
+      || length == error_mark_node
+      || stride == error_mark_node)
     return error_mark_node;
 
   if (low_bound && !INTEGRAL_TYPE_P (TREE_TYPE (low_bound)))
@@ -13816,6 +13823,13 @@ handle_omp_array_sections_1 (tree c, tree t, vec<tree> &types,
 		length);
       return error_mark_node;
     }
+  if (stride && !INTEGRAL_TYPE_P (TREE_TYPE (stride)))
+    {
+      error_at (OMP_CLAUSE_LOCATION (c),
+		"stride %qE of array section does not have integral type",
+		stride);
+      return error_mark_node;
+    }
   if (low_bound
       && TREE_CODE (low_bound) == INTEGER_CST
       && TYPE_PRECISION (TREE_TYPE (low_bound))
@@ -14032,7 +14046,9 @@ handle_omp_array_sections_1 (tree c, tree t, vec<tree> &types,
 	       d = TREE_OPERAND (d, 0))
 	    {
 	      tree d_length = TREE_OPERAND (d, 2);
-	      if (d_length == NULL_TREE || !integer_onep (d_length))
+	      tree d_stride = TREE_OPERAND (d, 3);
+	      if (d_length == NULL_TREE || !integer_onep (d_length)
+		  || (d_stride && !integer_onep (d_stride)))
 		{
 		  if (openacc && OMP_CLAUSE_CODE (c) == OMP_CLAUSE_MAP)
 		    {
diff --git a/gcc/cp/cp-objcp-common.cc b/gcc/cp/cp-objcp-common.cc
index 93b027b80ce..77dbac93f3f 100644
--- a/gcc/cp/cp-objcp-common.cc
+++ b/gcc/cp/cp-objcp-common.cc
@@ -530,6 +530,7 @@ cp_common_init_ts (void)
   MARK_TS_EXP (OFFSET_REF);
   MARK_TS_EXP (PSEUDO_DTOR_EXPR);
   MARK_TS_EXP (REINTERPRET_CAST_EXPR);
+  MARK_TS_EXP (OMP_ARRAYSHAPE_CAST_EXPR);
   MARK_TS_EXP (SCOPE_REF);
   MARK_TS_EXP (STATIC_CAST_EXPR);
   MARK_TS_EXP (STMT_EXPR);
diff --git a/gcc/cp/cp-tree.def b/gcc/cp/cp-tree.def
index 0e66ca70e00..08d66c25a89 100644
--- a/gcc/cp/cp-tree.def
+++ b/gcc/cp/cp-tree.def
@@ -256,6 +256,7 @@ DEFTREECODE (REINTERPRET_CAST_EXPR, "reinterpret_cast_expr", tcc_unary, 1)
 DEFTREECODE (CONST_CAST_EXPR, "const_cast_expr", tcc_unary, 1)
 DEFTREECODE (STATIC_CAST_EXPR, "static_cast_expr", tcc_unary, 1)
 DEFTREECODE (DYNAMIC_CAST_EXPR, "dynamic_cast_expr", tcc_unary, 1)
+DEFTREECODE (OMP_ARRAYSHAPE_CAST_EXPR, "omp_arrayshape_cast_expr", tcc_unary, 1)
 DEFTREECODE (IMPLICIT_CONV_EXPR, "implicit_conv_expr", tcc_unary, 1)
 DEFTREECODE (DOTSTAR_EXPR, "dotstar_expr", tcc_expression, 2)
 DEFTREECODE (TYPEID_EXPR, "typeid_expr", tcc_expression, 1)
diff --git a/gcc/cp/cp-tree.h b/gcc/cp/cp-tree.h
index 6227a6b61a8..97d88e935b9 100644
--- a/gcc/cp/cp-tree.h
+++ b/gcc/cp/cp-tree.h
@@ -504,6 +504,7 @@ extern GTY(()) tree cp_global_trees[CPTI_MAX];
       OVL_LOOKUP_P (in OVERLOAD)
       LOOKUP_FOUND_P (in RECORD_TYPE, UNION_TYPE, ENUMERAL_TYPE, NAMESPACE_DECL)
       FNDECL_MANIFESTLY_CONST_EVALUATED (in FUNCTION_DECL)
+      DECLTYPE_FOR_OMP_ARRAYSHAPE_CAST (in DECLTYPE_TYPE)
    5: IDENTIFIER_VIRTUAL_P (in IDENTIFIER_NODE)
       FUNCTION_RVALUE_QUALIFIED (in FUNCTION_TYPE, METHOD_TYPE)
       CALL_EXPR_REVERSE_ARGS (in CALL_EXPR, AGGR_INIT_EXPR)
@@ -4852,6 +4853,8 @@ get_vec_init_expr (tree t)
   TREE_LANG_FLAG_2 (DECLTYPE_TYPE_CHECK (NODE))
 #define DECLTYPE_FOR_REF_CAPTURE(NODE) \
   TREE_LANG_FLAG_3 (DECLTYPE_TYPE_CHECK (NODE))
+#define DECLTYPE_FOR_OMP_ARRAYSHAPE_CAST(NODE) \
+  TREE_LANG_FLAG_4 (DECLTYPE_TYPE_CHECK (NODE))
 
 /* Nonzero for VAR_DECL and FUNCTION_DECL node means that `extern' was
    specified in its declaration.  This can also be set for an
@@ -6924,6 +6927,8 @@ extern tree cxx_comdat_group			(tree);
 extern bool cp_missing_noreturn_ok_p		(tree);
 extern bool is_direct_enum_init			(tree, tree);
 extern void initialize_artificial_var		(tree, vec<constructor_elt, va_gc> *);
+extern tree cp_omp_create_arrayshape_type	(location_t, tree,
+						 vec<cp_expr> *);
 extern tree check_var_type			(tree, tree, location_t);
 extern tree reshape_init                        (tree, tree, tsubst_flags_t);
 extern tree next_aggregate_field		(tree);
@@ -6957,7 +6962,8 @@ extern void grokclassfn				(tree, tree,
 						 enum overload_flags);
 extern tree grok_array_decl			(location_t, tree, tree,
 						 vec<tree, va_gc> **, tsubst_flags_t);
-extern tree grok_omp_array_section		(location_t, tree, tree, tree);
+extern tree grok_omp_array_section		(location_t, tree, tree, tree,
+						 tree);
 extern tree delete_sanity			(location_t, tree, tree, bool,
 						 int, tsubst_flags_t);
 extern tree check_classfn			(tree, tree, tree);
@@ -7809,6 +7815,8 @@ extern tree cp_build_vec_convert		(tree, location_t, tree,
 						 tsubst_flags_t);
 extern tree cp_build_bit_cast			(location_t, tree, tree,
 						 tsubst_flags_t);
+extern tree cp_build_omp_arrayshape_cast	(location_t, tree, tree,
+						 tsubst_flags_t);
 extern void start_lambda_scope			(tree decl);
 extern void finish_lambda_scope			(void);
 extern void record_lambda_scope			(tree lambda);
@@ -8061,7 +8069,8 @@ inline tree build_x_binary_op (const op_location_t &loc,
 }
 extern tree build_x_array_ref			(location_t, tree, tree,
 						 tsubst_flags_t);
-extern tree build_omp_array_section		(location_t, tree, tree, tree);
+extern tree build_omp_array_section		(location_t, tree, tree, tree,
+						 tree);
 extern tree build_x_unary_op			(location_t,
 						 enum tree_code, cp_expr,
 						 tree, tsubst_flags_t);
diff --git a/gcc/cp/decl.cc b/gcc/cp/decl.cc
index b81e420a248..201e8c8ae69 100644
--- a/gcc/cp/decl.cc
+++ b/gcc/cp/decl.cc
@@ -11591,6 +11591,81 @@ create_array_type_for_decl (tree name, tree type, tree size, location_t loc)
   return build_cplus_array_type (type, itype);
 }
 
+/* Build an anonymous array of SIZE elements of ELTYPE.  */
+
+static tree
+create_anon_array_type (location_t loc, tree eltype, tree size)
+{
+  if (eltype == error_mark_node || size == error_mark_node)
+    return error_mark_node;
+
+  tree itype = compute_array_index_type_loc (loc, NULL_TREE, size,
+					     tf_warning_or_error);
+
+  if (type_uses_auto (eltype)
+      && variably_modified_type_p (itype, /*fn=*/NULL_TREE))
+    {
+      sorry_at (loc, "variable-length array of %<auto%>");
+      return error_mark_node;
+    }
+
+  return build_cplus_array_type (eltype, itype);
+}
+
+/* Derive an array type for an OpenMP array-shaping operator given EXPR, which
+   is an expression that might have array refs or array sections postfixed
+   (e.g. "ptr[0:3:2][3:4]"), and OMP_SHAPE_DIMS, a vector of dimensions.  */
+
+tree
+cp_omp_create_arrayshape_type (location_t loc, tree expr,
+			       vec<cp_expr> *omp_shape_dims)
+{
+  tree type, strip_sections = expr;
+
+  while (TREE_CODE (strip_sections) == OMP_ARRAY_SECTION
+	 || TREE_CODE (strip_sections) == ARRAY_REF)
+    strip_sections = TREE_OPERAND (strip_sections, 0);
+
+  /* Determine the element type, either directly or by using
+     "decltype" of an expression representing an element to
+     figure it out later during template instantiation.  */
+  if (type_dependent_expression_p (expr))
+    {
+      type = cxx_make_type (DECLTYPE_TYPE);
+
+      DECLTYPE_TYPE_EXPR (type)
+	= build_min_nt_loc (loc, INDIRECT_REF, strip_sections);
+      DECLTYPE_FOR_OMP_ARRAYSHAPE_CAST (type) = true;
+      SET_TYPE_STRUCTURAL_EQUALITY (type);
+    }
+  else
+    {
+      type = TREE_TYPE (strip_sections);
+
+      if (TREE_CODE (type) == REFERENCE_TYPE)
+	type = TREE_TYPE (type);
+
+      if (TREE_CODE (type) != POINTER_TYPE)
+	{
+	  error ("OpenMP array shaping operator with non-pointer argument");
+	  return error_mark_node;
+	}
+
+      type = TREE_TYPE (type);
+    }
+
+  int i;
+  cp_expr dim;
+  FOR_EACH_VEC_ELT_REVERSE (*omp_shape_dims, i, dim)
+    {
+      if (!type_dependent_expression_p (dim))
+	dim = fold_convert (sizetype, dim);
+      type = create_anon_array_type (loc, type, dim);
+    }
+
+  return type;
+}
+
 /* Returns the smallest location that is not UNKNOWN_LOCATION.  */
 
 static location_t
diff --git a/gcc/cp/decl2.cc b/gcc/cp/decl2.cc
index 64c812766ab..5686d310dbe 100644
--- a/gcc/cp/decl2.cc
+++ b/gcc/cp/decl2.cc
@@ -620,43 +620,49 @@ grok_array_decl (location_t loc, tree array_expr, tree index_exp,
 
 tree
 grok_omp_array_section (location_t loc, tree array_expr, tree index,
-			tree length)
+			tree length, tree stride)
 {
   tree orig_array_expr = array_expr;
   tree orig_index = index;
   tree orig_length = length;
+  tree orig_stride = stride;
 
   if (error_operand_p (array_expr)
       || error_operand_p (index)
-      || error_operand_p (length))
+      || error_operand_p (length)
+      || error_operand_p (stride))
     return error_mark_node;
 
   if (processing_template_decl)
     {
       if (type_dependent_expression_p (array_expr)
 	  || type_dependent_expression_p (index)
-	  || type_dependent_expression_p (length))
+	  || type_dependent_expression_p (length)
+	  || type_dependent_expression_p (stride))
 	return build_min_nt_loc (loc, OMP_ARRAY_SECTION, array_expr, index,
-				 length);
+				 length, stride);
       array_expr = build_non_dependent_expr (array_expr);
       if (index)
 	index = build_non_dependent_expr (index);
       if (length)
 	length = build_non_dependent_expr (length);
+      if (stride)
+	stride = build_non_dependent_expr (stride);
     }
 
   index = fold_non_dependent_expr (index);
   length = fold_non_dependent_expr (length);
+  stride = fold_non_dependent_expr (stride);
 
   /* NOTE: We can pass through invalidly-typed index/length fields
      here (e.g. if the user tries to use a floating-point index/length).
      This is diagnosed later in semantics.cc:handle_omp_array_sections_1.  */
 
-  tree expr = build_omp_array_section (loc, array_expr, index, length);
+  tree expr = build_omp_array_section (loc, array_expr, index, length, stride);
 
   if (processing_template_decl)
     expr = build_min_non_dep (OMP_ARRAY_SECTION, expr, orig_array_expr,
-			      orig_index, orig_length);
+			      orig_index, orig_length, orig_stride);
   return expr;
 }
 
@@ -2642,6 +2648,7 @@ min_vis_expr_r (tree *tp, int */*walk_subtrees*/, void *data)
     case REINTERPRET_CAST_EXPR:
     case CONST_CAST_EXPR:
     case DYNAMIC_CAST_EXPR:
+    case OMP_ARRAYSHAPE_CAST_EXPR:
     case NEW_EXPR:
     case CONSTRUCTOR:
     case LAMBDA_EXPR:
diff --git a/gcc/cp/error.cc b/gcc/cp/error.cc
index 30b5179768e..96c7bf82a41 100644
--- a/gcc/cp/error.cc
+++ b/gcc/cp/error.cc
@@ -2528,6 +2528,11 @@ dump_expr (cxx_pretty_printer *pp, tree t, int flags)
       dump_expr (pp, TREE_OPERAND (t, 1), flags);
       pp_colon (pp);
       dump_expr (pp, TREE_OPERAND (t, 2), flags);
+      if (TREE_OPERAND (t, 3))
+	{
+	  pp_colon (pp);
+	  dump_expr (pp, TREE_OPERAND (t, 3), flags);
+	}
       pp_cxx_right_bracket (pp);
       break;
 
diff --git a/gcc/cp/mangle.cc b/gcc/cp/mangle.cc
index a235f23459d..3bea004d02b 100644
--- a/gcc/cp/mangle.cc
+++ b/gcc/cp/mangle.cc
@@ -3611,6 +3611,7 @@ write_expression (tree expr)
 	case REINTERPRET_CAST_EXPR:
 	case STATIC_CAST_EXPR:
 	case CONST_CAST_EXPR:
+	case OMP_ARRAYSHAPE_CAST_EXPR:
 	  write_type (TREE_TYPE (expr));
 	  write_expression (TREE_OPERAND (expr, 0));
 	  break;
diff --git a/gcc/cp/operators.def b/gcc/cp/operators.def
index e9481752f93..3aca8e08762 100644
--- a/gcc/cp/operators.def
+++ b/gcc/cp/operators.def
@@ -134,6 +134,7 @@ DEF_OPERATOR (NULL, DYNAMIC_CAST_EXPR, "dc", OVL_OP_FLAG_UNARY)
 DEF_OPERATOR (NULL, REINTERPRET_CAST_EXPR, "rc", OVL_OP_FLAG_UNARY)
 DEF_OPERATOR (NULL, CONST_CAST_EXPR, "cc", OVL_OP_FLAG_UNARY)
 DEF_OPERATOR (NULL, STATIC_CAST_EXPR, "sc", OVL_OP_FLAG_UNARY)
+DEF_OPERATOR (NULL, OMP_ARRAYSHAPE_CAST_EXPR, "oc", OVL_OP_FLAG_UNARY)
 DEF_OPERATOR (NULL, SCOPE_REF, "sr", OVL_OP_FLAG_NONE)
 DEF_OPERATOR (NULL, EXPR_PACK_EXPANSION, "sp", OVL_OP_FLAG_NONE)
 DEF_OPERATOR (NULL, UNARY_LEFT_FOLD_EXPR, "fl", OVL_OP_FLAG_NONE)
diff --git a/gcc/cp/parser.cc b/gcc/cp/parser.cc
index c6aaf3877da..92495c1049a 100644
--- a/gcc/cp/parser.cc
+++ b/gcc/cp/parser.cc
@@ -4380,6 +4380,12 @@ cp_parser_new (cp_lexer *lexer)
   /* Disallow OpenMP array sections in expressions.  */
   parser->omp_array_section_p = false;
 
+  /* Disallow OpenMP array-shaping operator in expressions.  */
+  parser->omp_array_shaping_op_p = false;
+
+  /* We don't have an OpenMP array shape here.  */
+  parser->omp_has_array_shape_p = false;
+
   /* Not declaring an implicit function template.  */
   parser->auto_is_implicit_function_template_parm_p = false;
   parser->fully_implicit_function_template_p = false;
@@ -5365,6 +5371,7 @@ cp_parser_statement_expr (cp_parser *parser)
 {
   cp_token_position start = cp_parser_start_tentative_firewall (parser);
   auto oas = make_temp_override (parser->omp_array_section_p, false);
+  auto aso = make_temp_override (parser->omp_array_shaping_op_p, false);
 
   /* Consume the '('.  */
   location_t start_loc = cp_lexer_peek_token (parser->lexer)->location;
@@ -8290,7 +8297,7 @@ cp_parser_postfix_open_square_expression (cp_parser *parser,
       && cp_lexer_next_token_is (parser->lexer, CPP_COLON))
     {
       cp_lexer_consume_token (parser->lexer);
-      tree length = NULL_TREE;
+      tree length = NULL_TREE, stride = NULL_TREE;
       if (cp_lexer_next_token_is_not (parser->lexer, CPP_CLOSE_SQUARE))
 	{
 	  if (cxx_dialect >= cxx23)
@@ -8323,9 +8330,23 @@ cp_parser_postfix_open_square_expression (cp_parser *parser,
 				      /*warn_comma_p=*/warn_comma_subscript);
 	}
 
+      if (cp_lexer_next_token_is (parser->lexer, CPP_COLON))
+	{
+	  cp_lexer_consume_token (parser->lexer);
+	  /* We could check for C++-23 multidimensional/comma-separated
+	     subscripts here, or not bother.  */
+	  if (cp_lexer_next_token_is_not (parser->lexer, CPP_CLOSE_SQUARE))
+	    stride
+	      = cp_parser_expression (parser, NULL, /*cast_p=*/false,
+				      /*decltype_p=*/false,
+				      /*warn_comma_p=*/warn_comma_subscript);
+	}
+
       parser->colon_corrects_to_scope_p = saved_colon_corrects_to_scope_p;
 
-      if (index == error_mark_node || length == error_mark_node)
+      if (index == error_mark_node
+	  || length == error_mark_node
+	  || stride == error_mark_node)
 	{
 	  cp_parser_skip_to_closing_square_bracket (parser);
 	  return error_mark_node;
@@ -8334,7 +8355,7 @@ cp_parser_postfix_open_square_expression (cp_parser *parser,
 	cp_parser_require (parser, CPP_CLOSE_SQUARE, RT_CLOSE_SQUARE);
 
       return grok_omp_array_section (input_location, postfix_expression, index,
-				     length);
+				     length, stride);
     }
 
   parser->colon_corrects_to_scope_p = saved_colon_corrects_to_scope_p;
@@ -8342,11 +8363,23 @@ cp_parser_postfix_open_square_expression (cp_parser *parser,
   /* Look for the closing `]'.  */
   cp_parser_require (parser, CPP_CLOSE_SQUARE, RT_CLOSE_SQUARE);
 
-  /* Build the ARRAY_REF.  */
-  postfix_expression = grok_array_decl (loc, postfix_expression,
-					index, &expression_list,
-					tf_warning_or_error
-					| (decltype_p ? tf_decltype : 0));
+  if (parser->omp_has_array_shape_p
+      && (expression_list.get () == NULL
+	  || vec_safe_length (expression_list) == 1))
+    /* If we have an array-shaping operator, we may not be able to represent
+       a well-formed ARRAY_REF here, because we are coercing the type of the
+       innermost array base and the original type may not be compatible.  Use
+       the OMP_ARRAY_SECTION code instead.  We also want to explicitly avoid
+       creating INDIRECT_REFs for pointer bases, because that can lead to
+       parsing ambiguities (see cp_parser_omp_var_list_no_open).  */
+    return grok_omp_array_section (loc, postfix_expression, index,
+				   size_one_node, NULL_TREE);
+  else
+    /* Build the ARRAY_REF.  */
+    postfix_expression = grok_array_decl (loc, postfix_expression,
+					  index, &expression_list,
+					  tf_warning_or_error
+					  | (decltype_p ? tf_decltype : 0));
 
   /* When not doing offsetof, array references are not permitted in
      constant-expressions.  */
@@ -8668,6 +8701,7 @@ cp_parser_parenthesized_expression_list (cp_parser* parser,
   vec<tree, va_gc> *expression_list;
   bool saved_greater_than_is_operator_p;
   bool saved_omp_array_section_p;
+  bool saved_omp_array_shaping_op_p;
 
   /* Assume all the expressions will be constant.  */
   if (non_constant_p)
@@ -8686,7 +8720,9 @@ cp_parser_parenthesized_expression_list (cp_parser* parser,
   parser->greater_than_is_operator_p = true;
 
   saved_omp_array_section_p = parser->omp_array_section_p;
+  saved_omp_array_shaping_op_p = parser->omp_array_shaping_op_p;
   parser->omp_array_section_p = false;
+  parser->omp_array_shaping_op_p = false;
 
   cp_expr expr (NULL_TREE);
 
@@ -8753,6 +8789,7 @@ cp_parser_parenthesized_expression_list (cp_parser* parser,
 	  parser->greater_than_is_operator_p
 	    = saved_greater_than_is_operator_p;
 	  parser->omp_array_section_p = saved_omp_array_section_p;
+	  parser->omp_array_shaping_op_p = saved_omp_array_shaping_op_p;
 	  return NULL;
 	}
     }
@@ -8760,6 +8797,7 @@ cp_parser_parenthesized_expression_list (cp_parser* parser,
   parser->greater_than_is_operator_p
     = saved_greater_than_is_operator_p;
   parser->omp_array_section_p = saved_omp_array_section_p;
+  parser->omp_array_shaping_op_p = saved_omp_array_shaping_op_p;
 
   return expression_list;
 }
@@ -10028,6 +10066,8 @@ cp_parser_cast_expression (cp_parser *parser, bool address_p, bool cast_p,
       cp_expr expr (NULL_TREE);
       int cast_expression = 0;
       const char *saved_message;
+      auto_vec<cp_expr, 4> omp_shape_dims;
+      bool omp_array_shape_p = false;
 
       /* There's no way to know yet whether or not this is a cast.
 	 For example, `(int (3))' is a unary-expression, while `(int)
@@ -10097,6 +10137,28 @@ cp_parser_cast_expression (cp_parser *parser, bool address_p, bool cast_p,
 	 that the call to cp_parser_error_occurred below returns true.  */
       if (!cast_expression)
 	cp_parser_simulate_error (parser);
+      else if (parser->omp_array_shaping_op_p
+	       && cp_lexer_next_token_is (parser->lexer, CPP_OPEN_SQUARE))
+	{
+	  auto oas = make_temp_override (parser->omp_array_section_p, false);
+	  auto aso = make_temp_override (parser->omp_array_shaping_op_p, false);
+
+	  while (cp_lexer_next_token_is (parser->lexer, CPP_OPEN_SQUARE))
+	    {
+	      cp_lexer_consume_token (parser->lexer);
+	      cp_expr e = cp_parser_expression (parser);
+	      if (e.get_value () == error_mark_node)
+		break;
+	      omp_shape_dims.safe_push (e);
+	      if (!cp_parser_require (parser, CPP_CLOSE_SQUARE,
+				      RT_CLOSE_SQUARE))
+		break;
+	    }
+	  cp_token *close_paren = parens.require_close (parser);
+	  if (close_paren)
+	    close_paren_loc = close_paren->location;
+	  omp_array_shape_p = true;
+	}
       else
 	{
 	  bool saved_in_type_id_in_expr_p = parser->in_type_id_in_expr_p;
@@ -10118,6 +10180,10 @@ cp_parser_cast_expression (cp_parser *parser, bool address_p, bool cast_p,
 	 function returning T.  */
       if (!cp_parser_error_occurred (parser))
 	{
+	  auto aso = make_temp_override (parser->omp_array_shaping_op_p, false);
+	  auto as = make_temp_override (parser->omp_has_array_shape_p,
+					omp_array_shape_p);
+
 	  /* Only commit if the cast-expression doesn't start with
 	     '++', '--', or '[' in C++11.  */
 	  if (cast_expression > 0)
@@ -10131,6 +10197,24 @@ cp_parser_cast_expression (cp_parser *parser, bool address_p, bool cast_p,
 
 	  if (cp_parser_parse_definitely (parser))
 	    {
+	      if (omp_array_shape_p)
+		{
+		  location_t cast_loc = make_location (open_paren_loc,
+						       open_paren_loc,
+						       expr.get_finish ());
+
+		  type = cp_omp_create_arrayshape_type (cast_loc, expr,
+							&omp_shape_dims);
+
+		  /* Things rapidly get worse below if we carry on from here
+		     with an erroneous type...  */
+		  if (error_operand_p (type))
+		    return error_mark_node;
+
+		  return cp_build_omp_arrayshape_cast (cast_loc, type, expr,
+						       tf_warning_or_error);
+		}
+
 	      /* Warn about old-style casts, if so requested.  */
 	      if (warn_old_style_cast
 		  && !in_system_header_at (input_location)
@@ -11258,6 +11342,7 @@ cp_parser_lambda_expression (cp_parser* parser)
     bool auto_is_implicit_function_template_parm_p
         = parser->auto_is_implicit_function_template_parm_p;
     bool saved_omp_array_section_p = parser->omp_array_section_p;
+    bool saved_omp_array_shaping_op_p = parser->omp_array_shaping_op_p;
 
     parser->num_template_parameter_lists = 0;
     parser->in_statement = 0;
@@ -11267,6 +11352,7 @@ cp_parser_lambda_expression (cp_parser* parser)
     parser->implicit_template_scope = 0;
     parser->auto_is_implicit_function_template_parm_p = false;
     parser->omp_array_section_p = false;
+    parser->omp_array_shaping_op_p = false;
 
     /* The body of a lambda in a discarded statement is not discarded.  */
     bool discarded = in_discarded_stmt;
@@ -11318,6 +11404,7 @@ cp_parser_lambda_expression (cp_parser* parser)
     parser->auto_is_implicit_function_template_parm_p
 	= auto_is_implicit_function_template_parm_p;
     parser->omp_array_section_p = saved_omp_array_section_p;
+    parser->omp_array_shaping_op_p = saved_omp_array_shaping_op_p;
   }
 
   /* This field is only used during parsing of the lambda.  */
@@ -25745,6 +25832,7 @@ cp_parser_braced_list (cp_parser* parser, bool* non_constant_p)
   tree initializer;
   location_t start_loc = cp_lexer_peek_token (parser->lexer)->location;
   auto oas = make_temp_override (parser->omp_array_section_p, false);
+  auto aso = make_temp_override (parser->omp_array_shaping_op_p, false);
 
   /* Consume the `{' token.  */
   matching_braces braces;
@@ -37643,11 +37731,11 @@ check_no_duplicate_clause (tree clauses, enum omp_clause_code code,
 
 struct omp_dim
 {
-  tree low_bound, length;
+  tree low_bound, length, stride;
   location_t loc;
   bool no_colon;
-  omp_dim (tree lb, tree len, location_t lo, bool nc)
-    : low_bound (lb), length (len), loc (lo), no_colon (nc) {}
+  omp_dim (tree lb, tree len, tree str, location_t lo, bool nc)
+    : low_bound (lb), length (len), stride (str), loc (lo), no_colon (nc) {}
 };
 
 static tree
@@ -37680,10 +37768,22 @@ cp_parser_omp_var_list_no_open (cp_parser *parser, enum omp_clause_code kind,
 		   || kind == OMP_CLAUSE_FROM))
 	{
 	  auto s = make_temp_override (parser->omp_array_section_p, true);
+	  auto o = make_temp_override (parser->omp_array_shaping_op_p,
+				       (kind == OMP_CLAUSE_TO
+					|| kind == OMP_CLAUSE_FROM));
+	  tree reshaped_to = NULL_TREE;
 	  token = cp_lexer_peek_token (parser->lexer);
 	  location_t loc = token->location;
 	  decl = cp_parser_assignment_expression (parser);
 
+	  if ((TREE_CODE (decl) == VIEW_CONVERT_EXPR
+	       && TREE_CODE (TREE_TYPE (decl)) == ARRAY_TYPE)
+	      || TREE_CODE (decl) == OMP_ARRAYSHAPE_CAST_EXPR)
+	    {
+	      reshaped_to = TREE_TYPE (decl);
+	      decl = TREE_OPERAND (decl, 0);
+	    }
+
 	  /* This code rewrites a parsed expression containing various tree
 	     codes used to represent array accesses into a more uniform nest of
 	     OMP_ARRAY_SECTION nodes before it is processed by
@@ -37694,49 +37794,159 @@ cp_parser_omp_var_list_no_open (cp_parser *parser, enum omp_clause_code kind,
 	  dims.truncate (0);
 	  if (TREE_CODE (decl) == OMP_ARRAY_SECTION)
 	    {
+	      size_t sections = 0;
+	      tree orig_decl = decl;
+	      bool update_p = (kind == OMP_CLAUSE_TO
+			       || kind == OMP_CLAUSE_FROM);
+	      bool maybe_ptr_based_noncontig_update = false;
+
+	      while (update_p
+		     && !reshaped_to
+		     && (TREE_CODE (decl) == OMP_ARRAY_SECTION
+			 || TREE_CODE (decl) == ARRAY_REF
+			 || TREE_CODE (decl) == COMPOUND_EXPR))
+		{
+		  if (TREE_CODE (decl) == COMPOUND_EXPR)
+		    decl = TREE_OPERAND (decl, 1);
+		  else
+		    {
+		      if (TREE_CODE (decl) == OMP_ARRAY_SECTION)
+			maybe_ptr_based_noncontig_update = true;
+		      decl = TREE_OPERAND (decl, 0);
+		      sections++;
+		    }
+		}
+
+	      decl = orig_decl;
+
 	      while (TREE_CODE (decl) == OMP_ARRAY_SECTION)
 		{
 		  tree low_bound = TREE_OPERAND (decl, 1);
 		  tree length = TREE_OPERAND (decl, 2);
-		  dims.safe_push (omp_dim (low_bound, length, loc, false));
+		  tree stride = TREE_OPERAND (decl, 3);
+		  dims.safe_push (omp_dim (low_bound, length, stride, loc,
+					   false));
 		  decl = TREE_OPERAND (decl, 0);
+		  if (sections > 0)
+		    sections--;
 		}
 
+	      /* The handling of INDIRECT_REF here in the presence of
+		 array-shaping operations is a little tricky.  We need to
+		 avoid treating a pointer dereference as a unit-sized array
+		 section when we have an array shaping operation, because we
+		 don't want an indirection to consume one of the user's
+		 requested array dimensions.  E.g. if we have a
+		 double-indirect pointer like:
+
+		   int **foopp;
+		   #pragma omp target update from(([N][N]) (*foopp)[0:X][0:Y])
+
+		 We don't want to interpret this as:
+
+		   foopp[0:1][0:X][0:Y]
+
+		 else the array shape [N][N] won't match.  Also we can't match
+		 the array sections right-to-left instead, else this:
+
+		   #pragma omp target update from(([N][N]) (*foopp)[0:X])
+
+		 would not copy the dimensions:
+
+		   (*foopp)[0:X][0:N]
+
+		 as required.  So, avoid descending through INDIRECT_REFs if
+		 we have an array-shaping op.
+
+		 If we *don't* have an array-shaping op, but we have a
+		 multiply-indirected pointer and an array section like this:
+
+		   int ***fooppp;
+		   #pragma omp target update from((**fooppp)[0:X:S]
+
+		 also avoid descending through more indirections than we have
+		 array sections, since the noncontiguous update processing code
+		 won't understand them (and doesn't need to traverse them
+		 anyway).  */
+
 	      while (TREE_CODE (decl) == ARRAY_REF
-		     || TREE_CODE (decl) == INDIRECT_REF
+		     || (TREE_CODE (decl) == INDIRECT_REF
+			 && !reshaped_to)
 		     || TREE_CODE (decl) == COMPOUND_EXPR)
 		{
 		  if (REFERENCE_REF_P (decl))
 		    break;
 
+		  if (maybe_ptr_based_noncontig_update && sections == 0)
+		    break;
+
 		  if (TREE_CODE (decl) == COMPOUND_EXPR)
 		    {
 		      decl = TREE_OPERAND (decl, 1);
 		      STRIP_NOPS (decl);
+		      continue;
 		    }
-		  else if (TREE_CODE (decl) == INDIRECT_REF)
+		  else if (TREE_CODE (decl) == INDIRECT_REF
+			   && !reshaped_to)
 		    {
 		      dims.safe_push (omp_dim (integer_zero_node,
-					       integer_one_node, loc, true));
+					       integer_one_node, NULL_TREE, loc,
+					       true));
 		      decl = TREE_OPERAND (decl, 0);
 		    }
 		  else  /* ARRAY_REF. */
 		    {
 		      tree index = TREE_OPERAND (decl, 1);
-		      dims.safe_push (omp_dim (index, integer_one_node, loc,
-					       true));
+		      dims.safe_push (omp_dim (index, integer_one_node,
+					       NULL_TREE, loc, true));
 		      decl = TREE_OPERAND (decl, 0);
+		      if (sections > 0)
+			sections--;
 		    }
 		}
 
+	      if (reshaped_to)
+		{
+		  unsigned reshaped_dims = 0;
+
+		  for (tree t = reshaped_to;
+		       TREE_CODE (t) == ARRAY_TYPE;
+		       t = TREE_TYPE (t))
+		    reshaped_dims++;
+
+		  if (dims.length () > reshaped_dims)
+		    {
+		      error_at (loc, "too many array section specifiers "
+				"for %qT", reshaped_to);
+		      decl = error_mark_node;
+		    }
+		  else
+		    {
+		      /* We have a pointer DECL whose target should be
+			 interpreted as an array with particular dimensions,
+			 not "the pointer itself".  So, add an indirection
+			 here.  */
+		      if (type_dependent_expression_p (decl))
+			decl = build_min_nt_loc (loc, INDIRECT_REF, decl);
+		      else
+			{
+			  /* We're interested in the reference target.  */
+			  decl = convert_from_reference (decl);
+			  decl = cp_build_fold_indirect_ref (decl);
+			}
+		      decl
+			= cp_build_omp_arrayshape_cast (loc, reshaped_to, decl,
+							tf_warning_or_error);
+		    }
+		}
 	      /* Bare references have their own special handling, so remove
 		 the explicit dereference added by convert_from_reference.  */
-	      if (REFERENCE_REF_P (decl))
+	      else if (REFERENCE_REF_P (decl))
 		decl = TREE_OPERAND (decl, 0);
 
 	      for (int i = dims.length () - 1; i >= 0; i--)
 		decl = grok_omp_array_section (loc, decl, dims[i].low_bound,
-					       dims[i].length);
+					       dims[i].length, dims[i].stride);
 	    }
 	  else if (TREE_CODE (decl) == INDIRECT_REF)
 	    {
@@ -37753,7 +37963,7 @@ cp_parser_omp_var_list_no_open (cp_parser *parser, enum omp_clause_code kind,
 		   "foo[0:1]".  */
 	      if (!ref_p)
 		decl = grok_omp_array_section (loc, decl, integer_zero_node,
-					       integer_one_node);
+					       integer_one_node, NULL_TREE);
 	    }
 	  else if (TREE_CODE (decl) == ARRAY_REF)
 	    {
@@ -37762,7 +37972,16 @@ cp_parser_omp_var_list_no_open (cp_parser *parser, enum omp_clause_code kind,
 	      decl = TREE_OPERAND (decl, 0);
 	      STRIP_NOPS (decl);
 
-	      decl = grok_omp_array_section (loc, decl, idx, integer_one_node);
+	      decl = grok_omp_array_section (loc, decl, idx, integer_one_node,
+					     NULL_TREE);
+	    }
+	  else if (reshaped_to)
+	    {
+	      /* We're copying the whole of a reshaped array, originally a
+		 base pointer.  Rewrite as an array section.  */
+	      tree elems = array_type_nelts_total (reshaped_to);
+	      decl = grok_omp_array_section (loc, decl, size_zero_node, elems,
+					     NULL_TREE);
 	    }
 	  else if (TREE_CODE (decl) == NON_LVALUE_EXPR
 		   || CONVERT_EXPR_P (decl))
@@ -37927,7 +38146,8 @@ cp_parser_omp_var_list_no_open (cp_parser *parser, enum omp_clause_code kind,
 		      goto skip_comma;
 		    }
 
-		  dims.safe_push (omp_dim (low_bound, length, loc, no_colon));
+		  dims.safe_push (omp_dim (low_bound, length, NULL_TREE, loc,
+					   no_colon));
 		}
 
 	      if ((kind == OMP_CLAUSE_MAP
@@ -37949,7 +38169,8 @@ cp_parser_omp_var_list_no_open (cp_parser *parser, enum omp_clause_code kind,
 		for (unsigned i = 0; i < dims.length (); i++)
 		  decl = build_omp_array_section (input_location, decl,
 						  dims[i].low_bound,
-						  dims[i].length);
+						  dims[i].length,
+						  dims[i].stride);
 	      break;
 	    default:
 	      break;
@@ -37962,6 +38183,8 @@ cp_parser_omp_var_list_no_open (cp_parser *parser, enum omp_clause_code kind,
 		  && cp_parser_simulate_error (parser))
 		{
 		depend_lvalue:
+		  auto o = make_temp_override (parser->omp_array_shaping_op_p,
+					       true);
 		  cp_parser_abort_tentative_parse (parser);
 		  decl = cp_parser_assignment_expression (parser, NULL,
 							  false, false);
@@ -46698,8 +46921,38 @@ cp_parser_omp_target_update (cp_parser *parser, cp_token *pragma_tok,
   tree clauses
     = cp_parser_omp_all_clauses (parser, OMP_TARGET_UPDATE_CLAUSE_MASK,
 				 "#pragma omp target update", pragma_tok);
-  if (omp_find_clause (clauses, OMP_CLAUSE_TO) == NULL_TREE
-      && omp_find_clause (clauses, OMP_CLAUSE_FROM) == NULL_TREE)
+  bool to_clause = false, from_clause = false;
+  for (tree c = clauses;
+       c && !to_clause && !from_clause;
+       c = OMP_CLAUSE_CHAIN (c))
+    {
+      switch (OMP_CLAUSE_CODE (c))
+	{
+	case OMP_CLAUSE_TO:
+	  to_clause = true;
+	  break;
+	case OMP_CLAUSE_FROM:
+	  from_clause = true;
+	  break;
+	case OMP_CLAUSE_MAP:
+	  switch (OMP_CLAUSE_MAP_KIND (c))
+	    {
+	    case GOMP_MAP_TO_GRID:
+	      to_clause = true;
+	      break;
+	    case GOMP_MAP_FROM_GRID:
+	      from_clause = true;
+	      break;
+	    default:
+	      ;
+	    }
+	  break;
+	default:
+	  ;
+	}
+    }
+
+  if (!to_clause && !from_clause)
     {
       error_at (pragma_tok->location,
 		"%<#pragma omp target update%> must contain at least one "
diff --git a/gcc/cp/parser.h b/gcc/cp/parser.h
index 40009a42e96..350dd7bf026 100644
--- a/gcc/cp/parser.h
+++ b/gcc/cp/parser.h
@@ -410,6 +410,13 @@ struct GTY(()) cp_parser {
   /* TRUE if an OpenMP array section is allowed.  */
   bool omp_array_section_p;
 
+  /* TRUE if an OpenMP array-shaping operator is allowed.  */
+  bool omp_array_shaping_op_p;
+
+  /* TRUE if we are parsing an expression with an OpenMP array-shaping
+     operator.  */
+  bool omp_has_array_shape_p;
+
   /* Tracks the function's template parameter list when declaring a function
      using generic type parameters.  This is either a new chain in the case of a
      fully implicit function template or an extension of the function's existing
diff --git a/gcc/cp/pt.cc b/gcc/cp/pt.cc
index bbbe365bf38..83c25811222 100644
--- a/gcc/cp/pt.cc
+++ b/gcc/cp/pt.cc
@@ -16700,6 +16700,10 @@ tsubst (tree t, tree args, tsubst_flags_t complain, tree in_decl)
 		 member access.  */
 	      id = false;
 	    type = finish_decltype_type (type, id, complain);
+
+	    if (DECLTYPE_FOR_OMP_ARRAYSHAPE_CAST (t)
+		&& TYPE_REF_P (type))
+	      type = TREE_TYPE (type);
 	  }
 	return cp_build_qualified_type (type,
 					cp_type_quals (t)
@@ -17531,6 +17535,7 @@ tsubst_copy (tree t, tree args, tsubst_flags_t complain, tree in_decl)
     case STATIC_CAST_EXPR:
     case DYNAMIC_CAST_EXPR:
     case IMPLICIT_CONV_EXPR:
+    case OMP_ARRAYSHAPE_CAST_EXPR:
     CASE_CONVERT:
       {
 	tsubst_flags_t tcomplain = complain;
@@ -17756,12 +17761,14 @@ tsubst_copy (tree t, tree args, tsubst_flags_t complain, tree in_decl)
     case OMP_ARRAY_SECTION:
       {
 	tree op0 = tsubst_copy (TREE_OPERAND (t, 0), args, complain, in_decl);
-	tree op1 = NULL_TREE, op2 = NULL_TREE;
+	tree op1 = NULL_TREE, op2 = NULL_TREE, op3 = NULL_TREE;
 	if (TREE_OPERAND (t, 1))
 	  op1 = tsubst_copy (TREE_OPERAND (t, 1), args, complain, in_decl);
 	if (TREE_OPERAND (t, 2))
 	  op2 = tsubst_copy (TREE_OPERAND (t, 2), args, complain, in_decl);
-	return build_nt (OMP_ARRAY_SECTION, op0, op1, op2);
+	if (TREE_OPERAND (t, 3))
+	  op3 = tsubst_copy (TREE_OPERAND (t, 3), args, complain, in_decl);
+	return build_nt (OMP_ARRAY_SECTION, op0, op1, op2, op3);
       }
 
     case CALL_EXPR:
@@ -18038,14 +18045,17 @@ tsubst_omp_clause_decl (tree decl, tree args, tsubst_flags_t complain,
 	= tsubst_expr (TREE_OPERAND (decl, 1), args, complain, in_decl);
       tree length = tsubst_expr (TREE_OPERAND (decl, 2), args, complain,
 				 in_decl);
+      tree stride = tsubst_expr (TREE_OPERAND (decl, 3), args, complain,
+				 in_decl);
       tree base = tsubst_omp_clause_decl (TREE_OPERAND (decl, 0), args,
 					   complain, in_decl, NULL);
       if (TREE_OPERAND (decl, 0) == base
 	  && TREE_OPERAND (decl, 1) == low_bound
-	  && TREE_OPERAND (decl, 2) == length)
+	  && TREE_OPERAND (decl, 2) == length
+	  && TREE_OPERAND (decl, 3) == stride)
 	return decl;
-      tree ret = build3 (OMP_ARRAY_SECTION, TREE_TYPE (base), base, low_bound,
-			 length);
+      tree ret = build4 (OMP_ARRAY_SECTION, TREE_TYPE (base), base, low_bound,
+			 length, stride);
       return ret;
     }
   tree ret = tsubst_expr (decl, args, complain, in_decl);
@@ -20685,6 +20695,14 @@ tsubst_copy_and_build (tree t,
 	RETURN (cp_build_bit_cast (EXPR_LOCATION (t), type, op0, complain));
       }
 
+    case OMP_ARRAYSHAPE_CAST_EXPR:
+      {
+	tree type = tsubst (TREE_TYPE (t), args, complain, in_decl);
+	tree op0 = RECUR (TREE_OPERAND (t, 0));
+	RETURN (cp_build_omp_arrayshape_cast (EXPR_LOCATION (t), type, op0,
+					      complain));
+      }
+
     case POSTDECREMENT_EXPR:
     case POSTINCREMENT_EXPR:
       op1 = tsubst_non_call_postfix_expression (TREE_OPERAND (t, 0),
@@ -20851,7 +20869,7 @@ tsubst_copy_and_build (tree t,
     case OMP_ARRAY_SECTION:
       {
 	tree op0 = RECUR (TREE_OPERAND (t, 0));
-	tree op1 = NULL_TREE, op2 = NULL_TREE;
+	tree op1 = NULL_TREE, op2 = NULL_TREE, op3 = NULL_TREE;
 	if (op0 == error_mark_node)
 	  RETURN (error_mark_node);
 	if (TREE_OPERAND (t, 1))
@@ -20866,7 +20884,14 @@ tsubst_copy_and_build (tree t,
 	    if (op2 == error_mark_node)
 	      RETURN (error_mark_node);
 	  }
-	RETURN (build_omp_array_section (EXPR_LOCATION (t), op0, op1, op2));
+	if (TREE_OPERAND (t, 3))
+	  {
+	    op3 = RECUR (TREE_OPERAND (t, 3));
+	    if (op3 == error_mark_node)
+	      RETURN (error_mark_node);
+	  }
+	RETURN (build_omp_array_section (EXPR_LOCATION (t), op0, op1, op2,
+					 op3));
       }
 
     case SIZEOF_EXPR:
diff --git a/gcc/cp/semantics.cc b/gcc/cp/semantics.cc
index 4505f0b49f6..e55ff6d5a2d 100644
--- a/gcc/cp/semantics.cc
+++ b/gcc/cp/semantics.cc
@@ -5180,9 +5180,10 @@ public:
 static tree
 handle_omp_array_sections_1 (tree c, tree t, vec<tree> &types,
 			     bool &maybe_zero_len, unsigned int &first_non_one,
-			     bool &non_contiguous, enum c_omp_region_type ort)
+			     bool &non_contiguous, enum c_omp_region_type ort,
+			     int *discontiguous)
 {
-  tree ret, low_bound, length, type;
+  tree ret, low_bound, length, stride, type;
   bool openacc = (ort & C_ORT_ACC) != 0;
   if (TREE_CODE (t) != OMP_ARRAY_SECTION)
     {
@@ -5245,18 +5246,25 @@ handle_omp_array_sections_1 (tree c, tree t, vec<tree> &types,
     TREE_OPERAND (t, 0) = omp_privatize_field (TREE_OPERAND (t, 0), false);
   ret = handle_omp_array_sections_1 (c, TREE_OPERAND (t, 0), types,
 				     maybe_zero_len, first_non_one,
-				     non_contiguous, ort);
+				     non_contiguous, ort, discontiguous);
   if (ret == error_mark_node || ret == NULL_TREE)
     return ret;
 
-  type = TREE_TYPE (ret);
+  if (TREE_CODE (ret) == OMP_ARRAY_SECTION)
+    type = TREE_TYPE (TREE_TYPE (TREE_OPERAND (ret, 0)));
+  else
+    type = TREE_TYPE (ret);
   low_bound = TREE_OPERAND (t, 1);
   length = TREE_OPERAND (t, 2);
+  stride = TREE_OPERAND (t, 3);
   if ((low_bound && type_dependent_expression_p (low_bound))
-      || (length && type_dependent_expression_p (length)))
+      || (length && type_dependent_expression_p (length))
+      || (stride && type_dependent_expression_p (stride)))
     return NULL_TREE;
 
-  if (low_bound == error_mark_node || length == error_mark_node)
+  if (low_bound == error_mark_node
+      || length == error_mark_node
+      || stride == error_mark_node)
     return error_mark_node;
 
   if (low_bound && !INTEGRAL_TYPE_P (TREE_TYPE (low_bound)))
@@ -5273,10 +5281,19 @@ handle_omp_array_sections_1 (tree c, tree t, vec<tree> &types,
 		length);
       return error_mark_node;
     }
+  if (stride && !INTEGRAL_TYPE_P (TREE_TYPE (stride)))
+    {
+      error_at (OMP_CLAUSE_LOCATION (c),
+		"stride %qE of array section does not have integral type",
+		stride);
+      return error_mark_node;
+    }
   if (low_bound)
     low_bound = mark_rvalue_use (low_bound);
   if (length)
     length = mark_rvalue_use (length);
+  if (stride)
+    stride = mark_rvalue_use (stride);
   /* We need to reduce to real constant-values for checks below.  */
   if (length)
     STRIP_NOPS (length);
@@ -5286,6 +5303,8 @@ handle_omp_array_sections_1 (tree c, tree t, vec<tree> &types,
     length = fold_simple (length);
   if (low_bound)
     low_bound = fold_simple (low_bound);
+  if (stride)
+    stride = fold_simple (stride);
   if (low_bound
       && TREE_CODE (low_bound) == INTEGER_CST
       && TYPE_PRECISION (TREE_TYPE (low_bound))
@@ -5296,9 +5315,15 @@ handle_omp_array_sections_1 (tree c, tree t, vec<tree> &types,
       && TYPE_PRECISION (TREE_TYPE (length))
 	 > TYPE_PRECISION (sizetype))
     length = fold_convert (sizetype, length);
+  if (stride
+      && TREE_CODE (stride) == INTEGER_CST
+      && TYPE_PRECISION (TREE_TYPE (stride))
+	 > TYPE_PRECISION (sizetype))
+    stride = fold_convert (sizetype, stride);
   if (low_bound == NULL_TREE)
     low_bound = integer_zero_node;
-
+  if (stride == NULL_TREE)
+    stride = size_one_node;
   if (OMP_CLAUSE_CODE (c) == OMP_CLAUSE_MAP
       && (OMP_CLAUSE_MAP_KIND (c) == GOMP_MAP_ATTACH
 	  || OMP_CLAUSE_MAP_KIND (c) == GOMP_MAP_DETACH))
@@ -5417,12 +5442,29 @@ handle_omp_array_sections_1 (tree c, tree t, vec<tree> &types,
 	    }
 	  if (length && TREE_CODE (length) == INTEGER_CST)
 	    {
-	      if (tree_int_cst_lt (size, length))
+	      tree slength = length;
+	      if (stride && TREE_CODE (stride) == INTEGER_CST)
 		{
-		  error_at (OMP_CLAUSE_LOCATION (c),
-			    "length %qE above array section size "
-			    "in %qs clause", length,
-			    omp_clause_code_name[OMP_CLAUSE_CODE (c)]);
+		  slength = size_binop (MULT_EXPR,
+					fold_convert (sizetype, length),
+					fold_convert (sizetype, stride));
+		  slength = size_binop (MINUS_EXPR,
+					  slength,
+					  fold_convert (sizetype, stride));
+		  slength = size_binop (PLUS_EXPR, slength, size_one_node);
+		}
+	      if (tree_int_cst_lt (size, slength))
+		{
+		  if (stride)
+		    error_at (OMP_CLAUSE_LOCATION (c),
+			      "length %qE with stride %qE above array "
+			      "section size in %qs clause", length, stride,
+			      omp_clause_code_name[OMP_CLAUSE_CODE (c)]);
+		  else
+		    error_at (OMP_CLAUSE_LOCATION (c),
+			      "length %qE above array section size "
+			      "in %qs clause", length,
+			      omp_clause_code_name[OMP_CLAUSE_CODE (c)]);
 		  return error_mark_node;
 		}
 	      if (TREE_CODE (low_bound) == INTEGER_CST)
@@ -5430,7 +5472,7 @@ handle_omp_array_sections_1 (tree c, tree t, vec<tree> &types,
 		  tree lbpluslen
 		    = size_binop (PLUS_EXPR,
 				  fold_convert (sizetype, low_bound),
-				  fold_convert (sizetype, length));
+				  fold_convert (sizetype, slength));
 		  if (TREE_CODE (lbpluslen) == INTEGER_CST
 		      && tree_int_cst_lt (size, lbpluslen))
 		    {
@@ -5500,7 +5542,10 @@ handle_omp_array_sections_1 (tree c, tree t, vec<tree> &types,
 	       d = TREE_OPERAND (d, 0))
 	    {
 	      tree d_length = TREE_OPERAND (d, 2);
-	      if (d_length == NULL_TREE || !integer_onep (d_length))
+	      tree d_stride = TREE_OPERAND (d, 3);
+	      if (d_length == NULL_TREE
+		  || !integer_onep (d_length)
+		  || (d_stride && !integer_onep (d_stride)))
 		{
 		  if (openacc && OMP_CLAUSE_CODE (c) == OMP_CLAUSE_MAP)
 		    {
@@ -5520,10 +5565,15 @@ handle_omp_array_sections_1 (tree c, tree t, vec<tree> &types,
 		      return error_mark_node;
 		    }
 
-		  error_at (OMP_CLAUSE_LOCATION (c),
-			    "array section is not contiguous in %qs clause",
-			    omp_clause_code_name[OMP_CLAUSE_CODE (c)]);
-		  return error_mark_node;
+		  if (discontiguous && *discontiguous)
+		    *discontiguous = 2;
+		  else
+		    {
+		      error_at (OMP_CLAUSE_LOCATION (c),
+				"array section is not contiguous in %qs clause",
+				omp_clause_code_name[OMP_CLAUSE_CODE (c)]);
+		      return error_mark_node;
+		    }
 		}
 	    }
 	}
@@ -5535,7 +5585,7 @@ handle_omp_array_sections_1 (tree c, tree t, vec<tree> &types,
       return error_mark_node;
     }
   if (OMP_CLAUSE_CODE (c) != OMP_CLAUSE_DEPEND)
-    types.safe_push (TREE_TYPE (ret));
+    types.safe_push (type);
   /* We will need to evaluate lb more than once.  */
   tree lb = cp_save_expr (low_bound);
   if (lb != low_bound)
@@ -5554,15 +5604,45 @@ handle_omp_array_sections_1 (tree c, tree t, vec<tree> &types,
 		      OMP_CLAUSE_CODE (c) == OMP_CLAUSE_REDUCTION
 		      || OMP_CLAUSE_CODE (c) == OMP_CLAUSE_IN_REDUCTION
 		      || OMP_CLAUSE_CODE (c) == OMP_CLAUSE_TASK_REDUCTION);
-  ret = grok_array_decl (OMP_CLAUSE_LOCATION (c), ret, low_bound, NULL,
-			 tf_warning_or_error);
+  /* NOTE: Stride/length are discarded for affinity/depend here.  */
+  if (discontiguous
+      && *discontiguous
+      && OMP_CLAUSE_CODE (c) != OMP_CLAUSE_AFFINITY
+      && OMP_CLAUSE_CODE (c) != OMP_CLAUSE_DEPEND)
+    ret = grok_omp_array_section (OMP_CLAUSE_LOCATION (c), ret, low_bound,
+				  length, stride);
+  else
+    ret = grok_array_decl (OMP_CLAUSE_LOCATION (c), ret, low_bound, NULL,
+			   tf_warning_or_error);
   return ret;
 }
 
-/* Handle array sections for clause C.  */
+/* We built a reference to an array section, but it turns out we only need a
+   set of ARRAY_REFs to the lower bound.  Rewrite the node.  */
+
+static tree
+omp_array_section_low_bound (location_t loc, tree node)
+{
+  if (TREE_CODE (node) == OMP_ARRAY_SECTION)
+    {
+      tree low_bound = TREE_OPERAND (node, 1);
+      tree ret
+	= omp_array_section_low_bound (loc, TREE_OPERAND (node, 0));
+      return grok_array_decl (loc, ret, low_bound, NULL, tf_warning_or_error);
+    }
+
+  return node;
+}
+
+/* Handle array sections for clause C.  On entry *DISCONTIGUOUS is 0 if array
+   section must be contiguous, 1 if it can be discontiguous, and in the latter
+   case it is set to 2 on exit if it is determined to be discontiguous during
+   the function's execution.  PC points to the clause to be processed, and
+   *PNEXT to the last mapping node created, if passed as non-NULL.  */
 
 static bool
-handle_omp_array_sections (tree *pc, tree **pnext, enum c_omp_region_type ort)
+handle_omp_array_sections (tree *pc, tree **pnext, enum c_omp_region_type ort,
+			   int *discontiguous)
 {
   tree c = *pc;
   bool maybe_zero_len = false;
@@ -5578,7 +5658,7 @@ handle_omp_array_sections (tree *pc, tree **pnext, enum c_omp_region_type ort)
     tp = &TREE_VALUE (*tp);
   tree first = handle_omp_array_sections_1 (c, *tp, types,
 					    maybe_zero_len, first_non_one,
-					    non_contiguous, ort);
+					    non_contiguous, ort, discontiguous);
   if (first == error_mark_node)
     return true;
   if (first == NULL_TREE)
@@ -5620,6 +5700,8 @@ handle_omp_array_sections (tree *pc, tree **pnext, enum c_omp_region_type ort)
       if (processing_template_decl && maybe_zero_len)
 	return false;
 
+      bool higher_discontiguous = false;
+
       for (i = num, t = OMP_CLAUSE_DECL (c); i > 0;
 	   t = TREE_OPERAND (t, 0))
 	{
@@ -5627,6 +5709,7 @@ handle_omp_array_sections (tree *pc, tree **pnext, enum c_omp_region_type ort)
 
 	  tree low_bound = TREE_OPERAND (t, 1);
 	  tree length = TREE_OPERAND (t, 2);
+	  tree stride = TREE_OPERAND (t, 3);
 
 	  if (length)
 	    STRIP_NOPS (length);
@@ -5644,6 +5727,11 @@ handle_omp_array_sections (tree *pc, tree **pnext, enum c_omp_region_type ort)
 	      && TYPE_PRECISION (TREE_TYPE (length))
 		 > TYPE_PRECISION (sizetype))
 	    length = fold_convert (sizetype, length);
+	  if (stride
+	      && TREE_CODE (stride) == INTEGER_CST
+	      && TYPE_PRECISION (TREE_TYPE (stride))
+		 > TYPE_PRECISION (sizetype))
+	    stride = fold_convert (sizetype, stride);
 	  if (low_bound == NULL_TREE)
 	    low_bound = integer_zero_node;
 
@@ -5653,10 +5741,50 @@ handle_omp_array_sections (tree *pc, tree **pnext, enum c_omp_region_type ort)
 	      continue;
 	    }
 
+	  if (stride == NULL_TREE)
+	    stride = size_one_node;
+	  if (discontiguous && *discontiguous)
+	    {
+	      /* This condition is similar to the error check below, but
+		 whereas that checks for a definitely-discontiguous array
+		 section in order to report an error (where such a section is
+		 illegal), here we instead need to know if the array section
+		 *may be* discontiguous so we can handle that case
+		 appropriately (i.e. for rectangular "target update"
+		 operations).  */
+	      bool full_span = false;
+	      if (length != NULL_TREE
+		  && TREE_CODE (length) == INTEGER_CST
+		  && TREE_CODE (types[i]) == ARRAY_TYPE
+		  && TYPE_DOMAIN (types[i])
+		  && TYPE_MAX_VALUE (TYPE_DOMAIN (types[i]))
+		  && TREE_CODE (TYPE_MAX_VALUE (TYPE_DOMAIN (types[i])))
+		     == INTEGER_CST)
+		{
+		  tree size;
+		  size = size_binop (PLUS_EXPR,
+				     TYPE_MAX_VALUE (TYPE_DOMAIN (types[i])),
+				     size_one_node);
+		  if (tree_int_cst_equal (length, size))
+		    full_span = true;
+		}
+
+	      if (!integer_onep (stride)
+		  || (higher_discontiguous
+		      && (!integer_zerop (low_bound)
+			  || !full_span)))
+		*discontiguous = 2;
+
+	      if (!integer_onep (stride)
+		  || !integer_zerop (low_bound)
+		  || !full_span)
+		higher_discontiguous = true;
+	    }
+
 	  if (!maybe_zero_len && i > first_non_one)
 	    {
 	      if (integer_nonzerop (low_bound))
-		goto do_warn_noncontiguous;
+		goto is_noncontiguous;
 	      if (length != NULL_TREE
 		  && TREE_CODE (length) == INTEGER_CST
 		  && TYPE_DOMAIN (types[i])
@@ -5670,12 +5798,17 @@ handle_omp_array_sections (tree *pc, tree **pnext, enum c_omp_region_type ort)
 				     size_one_node);
 		  if (!tree_int_cst_equal (length, size))
 		    {
-		     do_warn_noncontiguous:
-		      error_at (OMP_CLAUSE_LOCATION (c),
-				"array section is not contiguous in %qs "
-				"clause",
-				omp_clause_code_name[OMP_CLAUSE_CODE (c)]);
-		      return true;
+		     is_noncontiguous:
+		      if (discontiguous && *discontiguous)
+			*discontiguous = 2;
+		      else
+			{
+			  error_at (OMP_CLAUSE_LOCATION (c),
+				    "array section is not contiguous in %qs "
+				    "clause",
+				    omp_clause_code_name[OMP_CLAUSE_CODE (c)]);
+			  return true;
+			}
 		    }
 		}
 	      if (!processing_template_decl
@@ -5792,6 +5925,9 @@ handle_omp_array_sections (tree *pc, tree **pnext, enum c_omp_region_type ort)
 	      OMP_CLAUSE_DECL (c) = t;
 	      return false;
 	    }
+	  if (discontiguous && *discontiguous != 2)
+	    first = omp_array_section_low_bound (OMP_CLAUSE_LOCATION (c),
+						 first);
 	  OMP_CLAUSE_DECL (c) = first;
 	  if (OMP_CLAUSE_CODE (c) == OMP_CLAUSE_HAS_DEVICE_ADDR)
 	    return false;
@@ -5799,9 +5935,6 @@ handle_omp_array_sections (tree *pc, tree **pnext, enum c_omp_region_type ort)
 	  if (TREE_CODE (t) == FIELD_DECL)
 	    t = finish_non_static_data_member (t, NULL_TREE, NULL_TREE);
 
-	  if (OMP_CLAUSE_CODE (c) != OMP_CLAUSE_MAP)
-	    return false;
-
 	  if (TREE_CODE (first) == INDIRECT_REF)
 	    {
 	      /* Detect and skip adding extra nodes for pointer-to-member
@@ -5828,6 +5961,10 @@ handle_omp_array_sections (tree *pc, tree **pnext, enum c_omp_region_type ort)
 		}
 	    }
 
+	  if (OMP_CLAUSE_CODE (c) != OMP_CLAUSE_MAP
+	      && !(discontiguous && *discontiguous == 2))
+	    return false;
+
 	  /* FIRST represents the first item of data that we are mapping.
 	     E.g. if we're mapping an array, FIRST might resemble
 	     "foo.bar.myarray[0]".  */
@@ -5846,7 +5983,8 @@ handle_omp_array_sections (tree *pc, tree **pnext, enum c_omp_region_type ort)
 
 	      c = *pc;
 
-	      if (ai.maybe_zero_length_array_section (c))
+	      if (OMP_CLAUSE_CODE (c) == OMP_CLAUSE_MAP
+		  && ai.maybe_zero_length_array_section (c))
 		OMP_CLAUSE_MAP_MAYBE_ZERO_LENGTH_ARRAY_SECTION (c) = 1;
 
 	      /* !!! If we're accessing a base decl via chained access
@@ -6988,7 +7126,7 @@ finish_omp_clauses (tree clauses, enum c_omp_region_type ort)
 	  t = OMP_CLAUSE_DECL (c);
 	  if (TREE_CODE (t) == OMP_ARRAY_SECTION)
 	    {
-	      if (handle_omp_array_sections (pc, NULL, ort))
+	      if (handle_omp_array_sections (pc, NULL, ort, NULL))
 		{
 		  remove = true;
 		  break;
@@ -8136,7 +8274,8 @@ finish_omp_clauses (tree clauses, enum c_omp_region_type ort)
 
 	  if (TREE_CODE (t) == OMP_ARRAY_SECTION)
 	    {
-	      if (handle_omp_array_sections (pc, NULL, ort))
+	      int discontiguous = 1;
+	      if (handle_omp_array_sections (pc, NULL, ort, &discontiguous))
 		remove = true;
 	      else if (OMP_CLAUSE_CODE (c) == OMP_CLAUSE_DEPEND
 		       && (OMP_CLAUSE_DEPEND_KIND (c)
@@ -8291,6 +8430,9 @@ finish_omp_clauses (tree clauses, enum c_omp_region_type ort)
 	      remove = true;
 	      break;
 	    }
+	  if (OMP_CLAUSE_MAP_KIND (c) == GOMP_MAP_GRID_DIM
+	      || OMP_CLAUSE_MAP_KIND (c) == GOMP_MAP_GRID_STRIDE)
+	    break;
 	  /* FALLTHRU */
 	case OMP_CLAUSE_TO:
 	case OMP_CLAUSE_FROM:
@@ -8305,8 +8447,11 @@ finish_omp_clauses (tree clauses, enum c_omp_region_type ort)
 		grp_start_p = pc;
 		grp_sentinel = OMP_CLAUSE_CHAIN (c);
 
+		int discontiguous
+		  = (OMP_CLAUSE_CODE (c) == OMP_CLAUSE_TO
+		     || OMP_CLAUSE_CODE (c) == OMP_CLAUSE_FROM);
 		tree *pnext = NULL;
-		if (handle_omp_array_sections (pc, &pnext, ort))
+		if (handle_omp_array_sections (pc, &pnext, ort, &discontiguous))
 		  remove = true;
 		else
 		  {
@@ -8897,7 +9042,7 @@ finish_omp_clauses (tree clauses, enum c_omp_region_type ort)
 	  t = OMP_CLAUSE_DECL (c);
 	  if (TREE_CODE (t) == OMP_ARRAY_SECTION)
 	    {
-	      if (handle_omp_array_sections (pc, NULL, ort))
+	      if (handle_omp_array_sections (pc, NULL, ort, NULL))
 		remove = true;
 	      else
 		{
@@ -13037,4 +13182,43 @@ cp_build_bit_cast (location_t loc, tree type, tree arg,
   return ret;
 }
 
+/* Build an OpenMP array-shape cast of ARG to TYPE.  */
+
+tree
+cp_build_omp_arrayshape_cast (location_t loc, tree type, tree arg,
+			      tsubst_flags_t complain)
+{
+  if (error_operand_p (type))
+    return error_mark_node;
+
+  if (!dependent_type_p (type)
+      && !complete_type_or_maybe_complain (type, NULL_TREE, complain))
+    return error_mark_node;
+
+  if (error_operand_p (arg))
+    return error_mark_node;
+
+  if (!type_dependent_expression_p (arg) && !dependent_type_p (type))
+    {
+      if (!trivially_copyable_p (TREE_TYPE (arg)))
+	{
+	  error_at (cp_expr_loc_or_loc (arg, loc),
+		    "OpenMP array shape source type %qT "
+		    "is not trivially copyable", TREE_TYPE (arg));
+	  return error_mark_node;
+	}
+
+      /* A pointer to multi-dimensional array conversion isn't normally
+	 allowed, but we force it here for array shape operators by creating
+	 the node directly.  We also want to avoid any overloaded conversions
+	 the user might have defined, not that there are likely to be any.  */
+      return build1_loc (loc, VIEW_CONVERT_EXPR, type, arg);
+    }
+
+  tree ret = build_min (OMP_ARRAYSHAPE_CAST_EXPR, type, arg);
+  SET_EXPR_LOCATION (ret, loc);
+
+  return ret;
+}
+
 #include "gt-cp-semantics.h"
diff --git a/gcc/cp/typeck.cc b/gcc/cp/typeck.cc
index 6dcbfedf03e..4e31da7c592 100644
--- a/gcc/cp/typeck.cc
+++ b/gcc/cp/typeck.cc
@@ -1625,6 +1625,9 @@ structural_comptypes (tree t1, tree t2, int strict)
 	return false;
       if (DECLTYPE_FOR_LAMBDA_PROXY (t1) != DECLTYPE_FOR_LAMBDA_PROXY (t2))
 	return false;
+      if (DECLTYPE_FOR_OMP_ARRAYSHAPE_CAST (t1)
+	  != DECLTYPE_FOR_OMP_ARRAYSHAPE_CAST (t2))
+	return false;
       if (!cp_tree_equal (DECLTYPE_TYPE_EXPR (t1), DECLTYPE_TYPE_EXPR (t2)))
         return false;
       break;
@@ -4793,7 +4796,7 @@ build_x_array_ref (location_t loc, tree arg1, tree arg2,
 
 tree
 build_omp_array_section (location_t loc, tree array_expr, tree index,
-			 tree length)
+			 tree length, tree stride)
 {
   tree idxtype;
 
@@ -4832,8 +4835,8 @@ build_omp_array_section (location_t loc, tree array_expr, tree index,
   else
     sectype = build_array_type (eltype, idxtype);
 
-  return build3_loc (loc, OMP_ARRAY_SECTION, sectype, array_expr, index,
-		     length);
+  return build4_loc (loc, OMP_ARRAY_SECTION, sectype, array_expr, index,
+		     length, stride);
 }
 
 /* Return whether OP is an expression of enum type cast to integer
@@ -8151,6 +8154,9 @@ check_for_casting_away_constness (location_t loc, tree src_type,
 		  src_type, dest_type);
       return true;
 
+    case OMP_ARRAYSHAPE_CAST_EXPR:
+      return true;
+
     default:
       gcc_unreachable();
     }
diff --git a/gcc/gimplify.cc b/gcc/gimplify.cc
index 6280eb7e028..21206e31114 100644
--- a/gcc/gimplify.cc
+++ b/gcc/gimplify.cc
@@ -9307,6 +9307,19 @@ omp_group_last (tree *start_p)
 	grp_last_p = &OMP_CLAUSE_CHAIN (c);
       break;
 
+    case GOMP_MAP_TO_GRID:
+    case GOMP_MAP_FROM_GRID:
+      while (nc
+	     && OMP_CLAUSE_CODE (nc) == OMP_CLAUSE_MAP
+	     && (OMP_CLAUSE_MAP_KIND (nc) == GOMP_MAP_GRID_DIM
+		 || OMP_CLAUSE_MAP_KIND (nc) == GOMP_MAP_GRID_STRIDE))
+	{
+	  grp_last_p = &OMP_CLAUSE_CHAIN (c);
+	  c = nc;
+	    nc = OMP_CLAUSE_CHAIN (c);
+	}
+      break;
+
     case GOMP_MAP_STRUCT:
     case GOMP_MAP_STRUCT_UNORD:
       {
@@ -9455,6 +9468,10 @@ omp_group_base (omp_mapping_group *grp, unsigned int *chained,
 	internal_error ("unexpected mapping node");
       return error_mark_node;
 
+    case GOMP_MAP_TO_GRID:
+    case GOMP_MAP_FROM_GRID:
+      return *grp->grp_start;
+
     case GOMP_MAP_ATTACH:
     case GOMP_MAP_DETACH:
       node = OMP_CLAUSE_CHAIN (node);
@@ -14396,7 +14413,9 @@ gimplify_adjust_omp_clauses (gimple_seq *pre_p, gimple_seq body, tree *list_p,
 	    }
 	  if (remove)
 	    break;
-	  if (OMP_CLAUSE_SIZE (c) == NULL_TREE)
+	  if (OMP_CLAUSE_SIZE (c) == NULL_TREE
+	      && OMP_CLAUSE_MAP_KIND (c) != GOMP_MAP_GRID_DIM
+	      && OMP_CLAUSE_MAP_KIND (c) != GOMP_MAP_GRID_STRIDE)
 	    OMP_CLAUSE_SIZE (c) = DECL_P (decl) ? DECL_SIZE_UNIT (decl)
 				  : TYPE_SIZE_UNIT (TREE_TYPE (decl));
 	  gimplify_omp_ctxp = ctx->outer_context;
@@ -14483,6 +14502,20 @@ gimplify_adjust_omp_clauses (gimple_seq *pre_p, gimple_seq body, tree *list_p,
 				 is_gimple_lvalue, fb_lvalue) == GS_ERROR)
 		remove = true;
 	    }
+	  else if (OMP_CLAUSE_MAP_KIND (c) == GOMP_MAP_GRID_DIM
+		   || OMP_CLAUSE_MAP_KIND (c) == GOMP_MAP_GRID_STRIDE)
+	    {
+	      /* The OMP_CLAUSE_DECL for GRID_DIM/GRID_STRIDE isn't necessarily
+		 an lvalue -- e.g. it might be a constant.  So handle it
+		 specially here.  */
+	      if (gimplify_expr (&OMP_CLAUSE_DECL (c), pre_p, NULL,
+				 is_gimple_val, fb_rvalue) == GS_ERROR)
+		{
+		  gimplify_omp_ctxp = ctx;
+		  remove = true;
+		}
+	      break;
+	    }
 	  else if (!DECL_P (decl))
 	    {
 	      if ((ctx->region_type & ORT_TARGET) != 0
@@ -14575,8 +14608,13 @@ gimplify_adjust_omp_clauses (gimple_seq *pre_p, gimple_seq body, tree *list_p,
 
 	      gimplify_omp_ctxp = ctx->outer_context;
 	      if (gimplify_expr (pd, pre_p, NULL, is_gimple_lvalue,
-				 fb_lvalue) == GS_ERROR)
-		remove = true;
+				 fb_lvalue | fb_mayfail) == GS_ERROR)
+		{
+		  sorry_at (OMP_CLAUSE_LOCATION (c),
+			    "unsupported map expression %qE",
+			    OMP_CLAUSE_DECL (c));
+		  remove = true;
+		}
 	      gimplify_omp_ctxp = ctx;
 	      break;
 	    }
diff --git a/gcc/omp-general.cc b/gcc/omp-general.cc
index d05a1d78ab7..9615a4426f3 100644
--- a/gcc/omp-general.cc
+++ b/gcc/omp-general.cc
@@ -3702,6 +3702,32 @@ omp_parse_pointer (tree *expr0, bool *has_offset)
   return false;
 }
 
+static bool
+omp_parse_noncontiguous_array (tree *expr0)
+{
+  tree expr = *expr0;
+  bool noncontig = false;
+
+  while (TREE_CODE (expr) == OMP_ARRAY_SECTION
+	 || TREE_CODE (expr) == ARRAY_REF)
+    {
+      /* Contiguous arrays use ARRAY_REF.  By the time we reach here,
+	 OMP_ARRAY_SECTION is only used for noncontiguous arrays.  */
+      if (TREE_CODE (expr) == OMP_ARRAY_SECTION)
+	noncontig = true;
+
+      expr = TREE_OPERAND (expr, 0);
+    }
+
+  if (noncontig)
+    {
+      *expr0 = expr;
+      return true;
+    }
+
+  return false;
+}
+
 static bool
 omp_parse_access_method (tree *expr0, enum access_method_kinds *kind)
 {
@@ -3710,6 +3736,13 @@ omp_parse_access_method (tree *expr0, enum access_method_kinds *kind)
 
   if (omp_parse_ref (&expr))
     *kind = ACCESS_REF;
+  else if (omp_parse_noncontiguous_array (&expr))
+    {
+      if (omp_parse_ref (&expr))
+	*kind = ACCESS_NONCONTIG_REF_TO_ARRAY;
+      else
+	*kind = ACCESS_NONCONTIG_ARRAY;
+    }
   else if (omp_parse_pointer (&expr, &has_offset))
     {
       if (omp_parse_ref (&expr))
@@ -3783,6 +3816,14 @@ omp_parse_structure_base (vec<omp_addr_token *> &addr_tokens,
       return true;
     }
 
+  if (TREE_CODE (expr) == VIEW_CONVERT_EXPR
+      && TREE_CODE (TREE_TYPE (expr)) == ARRAY_TYPE)
+    {
+      *kind = BASE_DECL;
+      *expr0 = TREE_OPERAND (expr, 0);
+      return true;
+    }
+
   *kind = BASE_ARBITRARY_EXPR;
   *expr0 = expr;
   return true;
@@ -3932,6 +3973,12 @@ debug_omp_tokenized_addr (vec<omp_addr_token *> &addr_tokens,
 	    case ACCESS_INDEXED_REF_TO_ARRAY:
 	      fputs ("access_indexed_ref_to_array", stderr);
 	      break;
+	    case ACCESS_NONCONTIG_ARRAY:
+	      fputs ("access_noncontig_array", stderr);
+	      break;
+	    case ACCESS_NONCONTIG_REF_TO_ARRAY:
+	      fputs ("access_noncontig_ref_to_array", stderr);
+	      break;
 	    }
 	  break;
 	case ARRAY_BASE:
diff --git a/gcc/omp-general.h b/gcc/omp-general.h
index 75362f80fb7..f68b960ede6 100644
--- a/gcc/omp-general.h
+++ b/gcc/omp-general.h
@@ -273,7 +273,9 @@ enum access_method_kinds
   ACCESS_POINTER_OFFSET,
   ACCESS_REF_TO_POINTER_OFFSET,
   ACCESS_INDEXED_ARRAY,
-  ACCESS_INDEXED_REF_TO_ARRAY
+  ACCESS_INDEXED_REF_TO_ARRAY,
+  ACCESS_NONCONTIG_ARRAY,
+  ACCESS_NONCONTIG_REF_TO_ARRAY
 };
 
 /* These are the kinds that a STRUCTURE_BASE or ARRAY_BASE (except
diff --git a/gcc/omp-low.cc b/gcc/omp-low.cc
index 9e4fcbe72ab..bfd41f71100 100644
--- a/gcc/omp-low.cc
+++ b/gcc/omp-low.cc
@@ -1388,6 +1388,55 @@ oacc_record_private_scalars (omp_context *ctx, tree clauses)
       }
 }
 
+/* Build record type for noncontiguous target update operations.  Must be kept
+   in sync with libgomp/libgomp.h omp_noncontig_array_desc.  */
+
+static tree
+omp_noncontig_descriptor_type (location_t loc)
+{
+  static tree cached = NULL_TREE;
+
+  if (cached)
+    return cached;
+
+  tree t = make_node (RECORD_TYPE);
+
+  tree fields = build_decl (loc, FIELD_DECL, get_identifier ("__ndims"),
+			    size_type_node);
+
+  tree field = build_decl (loc, FIELD_DECL, get_identifier ("__elemsize"),
+			   size_type_node);
+  TREE_CHAIN (field) = fields;
+  fields = field;
+
+  tree ptr_size_type = build_pointer_type (size_type_node);
+
+  field = build_decl (loc, FIELD_DECL, get_identifier ("__dim"), ptr_size_type);
+  TREE_CHAIN (field) = fields;
+  fields = field;
+
+  field = build_decl (loc, FIELD_DECL, get_identifier ("__index"),
+		      ptr_size_type);
+  TREE_CHAIN (field) = fields;
+  fields = field;
+
+  field = build_decl (loc, FIELD_DECL, get_identifier ("__length"),
+		      ptr_size_type);
+  TREE_CHAIN (field) = fields;
+  fields = field;
+
+  field = build_decl (loc, FIELD_DECL, get_identifier ("__stride"),
+		      ptr_size_type);
+  TREE_CHAIN (field) = fields;
+  fields = field;
+
+  finish_builtin_struct (t, "__omp_noncontig_desc_type", fields, ptr_type_node);
+
+  cached = t;
+
+  return t;
+}
+
 /* Instantiate decls as necessary in CTX to satisfy the data sharing
    specified by CLAUSES.  If BASE_POINTERS_RESTRICT, install var field with
    restrict.  */
@@ -1949,8 +1998,74 @@ scan_sharing_clauses (tree clauses, omp_context *ctx,
 	      install_var_local (array_decl, ctx);
 	      break;
 	    }
+	  else if (OMP_CLAUSE_CODE (c) == OMP_CLAUSE_MAP
+		   && (OMP_CLAUSE_MAP_KIND (c) == GOMP_MAP_TO_GRID
+		       || OMP_CLAUSE_MAP_KIND (c) == GOMP_MAP_FROM_GRID))
+	    {
+	      tree desc_type = omp_noncontig_descriptor_type (UNKNOWN_LOCATION);
 
-	  if (DECL_P (decl))
+	      tree bare = decl;
+	      if (TREE_CODE (bare) == VIEW_CONVERT_EXPR)
+		bare = TREE_OPERAND (bare, 0);
+
+	      const char *desc_name = ".omp_noncontig_desc";
+	      /* Try (but not too hard) to make a friendly name for the
+		 descriptor.  */
+	      if (DECL_P (bare))
+		desc_name = ACONCAT ((".omp_nc_desc_",
+				      IDENTIFIER_POINTER (DECL_NAME (bare)),
+				      NULL));
+	      tree desc = create_tmp_var (desc_type, desc_name);
+	      DECL_NAMELESS (desc) = 1;
+	      TREE_ADDRESSABLE (desc) = 1;
+
+	      /* Adjust DECL so it refers to the first element of the array:
+		 either by indirecting a pointer, or by selecting the zero'th
+		 index of each dimension of an array.  (We don't have a "bias"
+		 as such for this type of noncontiguous update operation, just
+		 the volume specified in the descriptor we build in
+		 lower_omp_target.)  */
+
+	      if (TREE_CODE (TREE_TYPE (decl)) == POINTER_TYPE)
+		{
+		  decl = build_fold_indirect_ref (decl);
+		  OMP_CLAUSE_DECL (c) = decl;
+		}
+
+	      tree field
+		= build_decl (OMP_CLAUSE_LOCATION (c), 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) c,
+				 (splay_tree_value) field);
+
+	      tree dn = build_omp_clause (OMP_CLAUSE_LOCATION (c),
+					  OMP_CLAUSE_MAP);
+	      OMP_CLAUSE_SET_MAP_KIND (dn, GOMP_MAP_TO_PSET);
+	      OMP_CLAUSE_DECL (dn) = desc;
+	      OMP_CLAUSE_SIZE (dn) = TYPE_SIZE_UNIT (desc_type);
+
+	      OMP_CLAUSE_CHAIN (dn) = OMP_CLAUSE_CHAIN (c);
+	      OMP_CLAUSE_CHAIN (c) = dn;
+
+	      field = build_decl (OMP_CLAUSE_LOCATION (c), 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) dn,
+				 (splay_tree_value) field);
+
+	      c = dn;
+	      tree nc;
+
+	      while ((nc = OMP_CLAUSE_CHAIN (c))
+		     && OMP_CLAUSE_CODE (nc) == OMP_CLAUSE_MAP
+		     && (OMP_CLAUSE_MAP_KIND (nc) == GOMP_MAP_GRID_DIM
+			 || OMP_CLAUSE_MAP_KIND (nc) == GOMP_MAP_GRID_STRIDE))
+		c = nc;
+	    }
+	  else if (DECL_P (decl))
 	    {
 	      if (DECL_SIZE (decl)
 		  && TREE_CODE (DECL_SIZE (decl)) != INTEGER_CST)
@@ -2192,6 +2307,11 @@ scan_sharing_clauses (tree clauses, omp_context *ctx,
 	      && is_omp_target (ctx->stmt)
 	      && !is_gimple_omp_offloaded (ctx->stmt))
 	    break;
+	  if (OMP_CLAUSE_MAP_KIND (c) == GOMP_MAP_TO_GRID
+	      || OMP_CLAUSE_MAP_KIND (c) == GOMP_MAP_FROM_GRID
+	      || OMP_CLAUSE_MAP_KIND (c) == GOMP_MAP_GRID_DIM
+	      || OMP_CLAUSE_MAP_KIND (c) == GOMP_MAP_GRID_STRIDE)
+	    break;
 	  if (DECL_P (decl))
 	    {
 	      if ((OMP_CLAUSE_MAP_KIND (c) == GOMP_MAP_POINTER
@@ -13666,6 +13786,10 @@ lower_omp_target (gimple_stmt_iterator *gsi_p, omp_context *ctx)
 	  case GOMP_MAP_DETACH:
 	  case GOMP_MAP_ATTACH_ZERO_LENGTH_ARRAY_SECTION:
 	  case GOMP_MAP_POINTER_TO_ZERO_LENGTH_ARRAY_SECTION:
+	  case GOMP_MAP_TO_GRID:
+	  case GOMP_MAP_FROM_GRID:
+	  case GOMP_MAP_GRID_DIM:
+	  case GOMP_MAP_GRID_STRIDE:
 	    break;
 	  case GOMP_MAP_IF_PRESENT:
 	  case GOMP_MAP_FORCE_ALLOC:
@@ -13693,6 +13817,20 @@ lower_omp_target (gimple_stmt_iterator *gsi_p, omp_context *ctx)
 	    gcc_unreachable ();
 	  }
 #endif
+	if (OMP_CLAUSE_MAP_KIND (c) == GOMP_MAP_TO_GRID
+	    || OMP_CLAUSE_MAP_KIND (c) == GOMP_MAP_FROM_GRID)
+	  {
+	    tree nc = OMP_CLAUSE_CHAIN (c);
+	    gcc_assert (OMP_CLAUSE_CODE (nc) == OMP_CLAUSE_MAP
+			&& OMP_CLAUSE_MAP_KIND (nc) == GOMP_MAP_TO_PSET);
+	    c = nc;
+	    while ((nc = OMP_CLAUSE_CHAIN (c))
+		   && (OMP_CLAUSE_MAP_KIND (nc) == GOMP_MAP_GRID_DIM
+		       || OMP_CLAUSE_MAP_KIND (nc) == GOMP_MAP_GRID_STRIDE))
+	      c = nc;
+	    map_cnt += 2;
+	    continue;
+	  }
 	  /* FALLTHRU */
       case OMP_CLAUSE_TO:
       case OMP_CLAUSE_FROM:
@@ -14108,7 +14246,267 @@ lower_omp_target (gimple_stmt_iterator *gsi_p, omp_context *ctx)
 						   deep_map_offset_data,
 						   deep_map_offset, &ilist);
 	      }
-	    if (!DECL_P (ovar))
+	    if (OMP_CLAUSE_CODE (c) == OMP_CLAUSE_MAP
+		&& (OMP_CLAUSE_MAP_KIND (c) == GOMP_MAP_TO_GRID
+		    || OMP_CLAUSE_MAP_KIND (c) == GOMP_MAP_FROM_GRID))
+	      {
+		tree decl = OMP_CLAUSE_DECL (c);
+		tree dn = OMP_CLAUSE_CHAIN (c);
+		gcc_assert (OMP_CLAUSE_CODE (dn) == OMP_CLAUSE_MAP
+			    && OMP_CLAUSE_MAP_KIND (dn) == GOMP_MAP_TO_PSET);
+		tree desc = OMP_CLAUSE_DECL (dn);
+
+		tree oc, elsize = OMP_CLAUSE_SIZE (c);
+		tree type = TREE_TYPE (decl);
+		int i, dims = 0;
+		auto_vec<tree> tdims;
+		bool pointer_based = false, handled_pointer_section = false;
+		tree arrsize = fold_convert (sizetype, elsize);
+
+		/* Allow a single (maybe strided) array section if we have a
+		   pointer base.  */
+		if (TREE_CODE (decl) == INDIRECT_REF
+		    && (TREE_CODE (TREE_TYPE (TREE_OPERAND (decl, 0)))
+			== POINTER_TYPE))
+		  {
+		    pointer_based = true;
+		    dims = 1;
+		  }
+		else
+		  for (tree itype = type;
+		       TREE_CODE (itype) == ARRAY_TYPE;
+		       itype = TREE_TYPE (itype))
+		    {
+		      tdims.safe_push (itype);
+		      dims++;
+		    }
+
+		int tdim = tdims.length () - 1;
+
+		vec<constructor_elt, va_gc> *vdim;
+		vec<constructor_elt, va_gc> *vindex;
+		vec<constructor_elt, va_gc> *vlen;
+		vec<constructor_elt, va_gc> *vstride;
+		vec_alloc (vdim, dims);
+		vec_alloc (vindex, dims);
+		vec_alloc (vlen, dims);
+		vec_alloc (vstride, dims);
+
+		tree size_arr_type
+		  = build_array_type_nelts (size_type_node, dims);
+
+		tree dim_tmp = create_tmp_var (size_arr_type, ".omp_dim");
+		DECL_NAMELESS (dim_tmp) = 1;
+		TREE_ADDRESSABLE (dim_tmp) = 1;
+		TREE_STATIC (dim_tmp) = 1;
+		tree index_tmp = create_tmp_var (size_arr_type, ".omp_index");
+		DECL_NAMELESS (index_tmp) = 1;
+		TREE_ADDRESSABLE (index_tmp) = 1;
+		TREE_STATIC (index_tmp) = 1;
+		tree len_tmp = create_tmp_var (size_arr_type, ".omp_len");
+		DECL_NAMELESS (len_tmp) = 1;
+		TREE_ADDRESSABLE (len_tmp) = 1;
+		TREE_STATIC (len_tmp) = 1;
+		tree stride_tmp = create_tmp_var (size_arr_type, ".omp_stride");
+		DECL_NAMELESS (stride_tmp) = 1;
+		TREE_ADDRESSABLE (stride_tmp) = 1;
+		TREE_STATIC (stride_tmp) = 1;
+
+		oc = c;
+		c = dn;
+
+		for (i = 0; i < dims; i++)
+		  {
+		    nc = OMP_CLAUSE_CHAIN (c);
+		    tree dim = NULL_TREE, index = NULL_TREE, len = NULL_TREE,
+			 stride = size_one_node;
+
+		    if (OMP_CLAUSE_CODE (nc) == OMP_CLAUSE_MAP
+			&& OMP_CLAUSE_MAP_KIND (nc) == GOMP_MAP_GRID_DIM)
+		      {
+			index = OMP_CLAUSE_DECL (nc);
+			len = OMP_CLAUSE_SIZE (nc);
+
+			index = fold_convert (sizetype, index);
+			len = fold_convert (sizetype, len);
+
+			tree nc2 = OMP_CLAUSE_CHAIN (nc);
+			if (nc2
+			    && OMP_CLAUSE_CODE (nc2) == OMP_CLAUSE_MAP
+			    && (OMP_CLAUSE_MAP_KIND (nc2)
+				== GOMP_MAP_GRID_STRIDE))
+			  {
+			    stride = OMP_CLAUSE_DECL (nc2);
+			    stride = fold_convert (sizetype, stride);
+			    nc = nc2;
+			  }
+
+			if (tdim >= 0)
+			  {
+			    /* We have an array shape -- use that to find the
+			       total size of the data on the target to look up
+			       in libgomp.  */
+			    tree dtype = TYPE_DOMAIN (tdims[tdim]);
+			    tree minval = TYPE_MIN_VALUE (dtype);
+			    tree maxval = TYPE_MAX_VALUE (dtype);
+			    minval = fold_convert (sizetype, minval);
+			    maxval = fold_convert (sizetype, maxval);
+			    dim = size_binop (MINUS_EXPR, maxval, minval);
+			    dim = size_binop (PLUS_EXPR, dim,
+					      size_one_node);
+			    arrsize = size_binop (MULT_EXPR, arrsize, dim);
+			  }
+			else if (pointer_based && !handled_pointer_section)
+			  {
+			    /* Use the selected array section to determine the
+			       size of the array.  */
+			    tree tmp = size_binop (MULT_EXPR, len, stride);
+			    tmp = size_binop (MINUS_EXPR, tmp, stride);
+			    tmp = size_binop (PLUS_EXPR, tmp, size_one_node);
+			    dim = size_binop (PLUS_EXPR, index, tmp);
+			    arrsize = size_binop (MULT_EXPR, arrsize, dim);
+			    handled_pointer_section = true;
+			  }
+			else
+			  {
+			    if (pointer_based)
+			      error_at (OMP_CLAUSE_LOCATION (c),
+					"too many array section specifiers "
+					"for pointer-based array");
+			    else
+			      error_at (OMP_CLAUSE_LOCATION (c),
+					"too many array section specifiers "
+					"for array");
+			    dim = index = len = stride = error_mark_node;
+			  }
+			tdim--;
+
+			c = nc;
+		      }
+		    else
+		      {
+			/* We have more array dimensions than array section
+			   specifiers.  Copy the whole span.  */
+			tree dtype = TYPE_DOMAIN (tdims[tdim]);
+			tree minval = TYPE_MIN_VALUE (dtype);
+			tree maxval = TYPE_MAX_VALUE (dtype);
+			minval = fold_convert (sizetype, minval);
+			maxval = fold_convert (sizetype, maxval);
+			dim = size_binop (MINUS_EXPR, maxval, minval);
+			dim = size_binop (PLUS_EXPR, dim, size_one_node);
+			len = dim;
+			index = size_zero_node;
+		      }
+
+		    if (TREE_CODE (dim) != INTEGER_CST)
+		      TREE_STATIC (dim_tmp) = 0;
+
+		    if (TREE_CODE (index) != INTEGER_CST)
+		      TREE_STATIC (index_tmp) = 0;
+
+		    if (TREE_CODE (len) != INTEGER_CST)
+		      TREE_STATIC (len_tmp) = 0;
+
+		    if (TREE_CODE (stride) != INTEGER_CST)
+		      TREE_STATIC (stride_tmp) = 0;
+
+		    tree cidx = size_int (i);
+		    CONSTRUCTOR_APPEND_ELT (vdim, cidx, dim);
+		    CONSTRUCTOR_APPEND_ELT (vindex, cidx, index);
+		    CONSTRUCTOR_APPEND_ELT (vlen, cidx, len);
+		    CONSTRUCTOR_APPEND_ELT (vstride, cidx, stride);
+		  }
+
+		/* The size of the whole array -- to make sure we find any
+		   part of the array via splay-tree lookup that might be
+		   mapped on the target at runtime.  */
+		OMP_CLAUSE_SIZE (oc) = arrsize;
+
+		tree cdim = build_constructor (size_arr_type, vdim);
+		tree cindex = build_constructor (size_arr_type, vindex);
+		tree clen = build_constructor (size_arr_type, vlen);
+		tree cstride = build_constructor (size_arr_type, vstride);
+
+		if (TREE_STATIC (dim_tmp))
+		  DECL_INITIAL (dim_tmp) = cdim;
+		else
+		  gimplify_assign (dim_tmp, cdim, &ilist);
+
+		if (TREE_STATIC (index_tmp))
+		  DECL_INITIAL (index_tmp) = cindex;
+		else
+		  gimplify_assign (index_tmp, cindex, &ilist);
+
+		if (TREE_STATIC (len_tmp))
+		  DECL_INITIAL (len_tmp) = clen;
+		else
+		  gimplify_assign (len_tmp, clen, &ilist);
+
+		if (TREE_STATIC (stride_tmp))
+		  DECL_INITIAL (stride_tmp) = cstride;
+		else
+		  gimplify_assign (stride_tmp, cstride, &ilist);
+
+		tree desc_type = TREE_TYPE (desc);
+
+		tree ndims_field = TYPE_FIELDS (desc_type);
+		tree elemsize_field = DECL_CHAIN (ndims_field);
+		tree dim_field = DECL_CHAIN (elemsize_field);
+		tree index_field = DECL_CHAIN (dim_field);
+		tree len_field = DECL_CHAIN (index_field);
+		tree stride_field = DECL_CHAIN (len_field);
+
+		vec<constructor_elt, va_gc> *v;
+		vec_alloc (v, 6);
+
+		bool all_static = (TREE_STATIC (dim_tmp)
+				   && TREE_STATIC (index_tmp)
+				   && TREE_STATIC (len_tmp)
+				   && TREE_STATIC (stride_tmp));
+
+		dim_tmp = build4 (ARRAY_REF, sizetype, dim_tmp, size_zero_node,
+				  NULL_TREE, NULL_TREE);
+		dim_tmp = build_fold_addr_expr (dim_tmp);
+
+		/* TODO: we could skip all-zeros index.  */
+		index_tmp = build4 (ARRAY_REF, sizetype, index_tmp,
+				    size_zero_node, NULL_TREE, NULL_TREE);
+		index_tmp = build_fold_addr_expr (index_tmp);
+
+		len_tmp = build4 (ARRAY_REF, sizetype, len_tmp, size_zero_node,
+				  NULL_TREE, NULL_TREE);
+		len_tmp = build_fold_addr_expr (len_tmp);
+
+		/* TODO: we could skip all-ones stride.  */
+		stride_tmp = build4 (ARRAY_REF, sizetype, stride_tmp,
+				     size_zero_node, NULL_TREE, NULL_TREE);
+		stride_tmp = build_fold_addr_expr (stride_tmp);
+
+		elsize = fold_convert (sizetype, elsize);
+		tree ndims = size_int (dims);
+
+		CONSTRUCTOR_APPEND_ELT (v, ndims_field, ndims);
+		CONSTRUCTOR_APPEND_ELT (v, elemsize_field, elsize);
+		CONSTRUCTOR_APPEND_ELT (v, dim_field, dim_tmp);
+		CONSTRUCTOR_APPEND_ELT (v, index_field, index_tmp);
+		CONSTRUCTOR_APPEND_ELT (v, len_field, len_tmp);
+		CONSTRUCTOR_APPEND_ELT (v, stride_field, stride_tmp);
+
+		tree desc_ctor = build_constructor (desc_type, v);
+
+		if (all_static)
+		  {
+		    TREE_STATIC (desc) = 1;
+		    DECL_INITIAL (desc) = desc_ctor;
+		  }
+		else
+		  gimplify_assign (desc, desc_ctor, &ilist);
+
+		OMP_CLAUSE_CHAIN (dn) = OMP_CLAUSE_CHAIN (nc);
+		c = oc;
+		nc = c;
+	      }
+	    else if (!DECL_P (ovar))
 	      {
 		if (OMP_CLAUSE_CODE (c) == OMP_CLAUSE_MAP
 		    && OMP_CLAUSE_MAP_ZERO_BIAS_ARRAY_SECTION (c))
diff --git a/gcc/testsuite/g++.dg/gomp/array-shaping-1.C b/gcc/testsuite/g++.dg/gomp/array-shaping-1.C
new file mode 100644
index 00000000000..8627aa7ffb3
--- /dev/null
+++ b/gcc/testsuite/g++.dg/gomp/array-shaping-1.C
@@ -0,0 +1,22 @@
+// { dg-do compile }
+// { dg-additional-options "-fdump-tree-original" }
+
+template<typename T, typename E, int A, int B, int C, int D>
+void foo ()
+{
+  T *ptr;
+  E a = A, b = B, c = C, d = D;
+
+  /* Dependent types for indices.  */
+#pragma omp target update from(([a][b+1][c][d]) ptr[1:a-2][1:b][1:c-2][1:d-2])
+// { dg-final { scan-tree-dump {map\(from_grid:VIEW_CONVERT_EXPR.*\(\*ptr\) \[len: 1\]\) map\(grid_dim:1 \[len: [^\]]+\]\) map\(grid_dim:1 \[len: [^\]]+\]\) map\(grid_dim:1 \[len: [^\]]+\]\) map\(grid_dim:1 \[len: [^]]+\]\)} "original" } }
+}
+
+int main()
+{
+  char *ptr;
+
+  foo<char, short, 3, 4, 5, 6> ();
+
+  return 0;
+}
diff --git a/gcc/testsuite/g++.dg/gomp/array-shaping-2.C b/gcc/testsuite/g++.dg/gomp/array-shaping-2.C
new file mode 100644
index 00000000000..861d66261a1
--- /dev/null
+++ b/gcc/testsuite/g++.dg/gomp/array-shaping-2.C
@@ -0,0 +1,134 @@
+// { dg-do compile }
+// { dg-additional-options "-fdump-tree-original" }
+
+template<typename T>
+struct St
+{
+  T ***ppptr;
+  T ***&rppptr;
+
+  St(T ***p, T ***&rp) : ppptr(p), rppptr(rp) { }
+};
+
+template<typename A, typename B>
+void foo()
+{
+  A *ptr;
+  A **pptr = &ptr;
+  A ***ppptr = &pptr;
+  A ***&rppptr = ppptr;
+
+#pragma omp target update to(([10]) (**ppptr)[3:4:2])
+// { dg-final { scan-tree-dump {map\(to_grid:VIEW_CONVERT_EXPR<int\[10\]>\(\*\*\*ppptr\) \[len: [0-9]+\]\) map\(grid_dim:3 \[len: 4\]\) map\(grid_stride:2\)} "original" } }
+
+#pragma omp target update to(([10]) (**rppptr)[3:4:2])
+// { dg-final { scan-tree-dump {map\(to_grid:VIEW_CONVERT_EXPR<int\[10\]>\(\*\*\*\*rppptr\) \[len: [0-9]+\]\) map\(grid_dim:3 \[len: 4\]\) map\(grid_stride:2\)} "original" } }
+
+#pragma omp target update to((**ppptr)[3:4:2])
+// { dg-final { scan-tree-dump {map\(to_grid:\*\*ppptr \[len: [0-9]+\]\) map\(grid_dim:3 \[len: 4\]\) map\(grid_stride:2\)} "original" } }
+
+#pragma omp target update to((**rppptr)[3:4:2])
+// { dg-final { scan-tree-dump {map\(to_grid:\*\*\*rppptr \[len: [0-9]+\]\) map\(grid_dim:3 \[len: 4\]\) map\(grid_stride:2\)} "original" } }
+
+  B *ptr2;
+  B **pptr2 = &ptr2;
+  B ***ppptr2 = &pptr2;
+  St<B> *s = new St<B>(ppptr2, ppptr2);
+  St<B> **ps = &s;
+  St<B> **&rps = ps;
+
+#pragma omp target update from(([10]) (**(*ps)->ppptr)[3:4:2])
+// { dg-final { scan-tree-dump {map\(from_grid:VIEW_CONVERT_EXPR<long int\[10\]>\(\*\*\*\(\*ps\)->ppptr\) \[len: [0-9]+\]\) map\(grid_dim:3 \[len: 4\]\) map\(grid_stride:2\)} "original" } }
+
+#pragma omp target update from(([10]) (**(*rps)->rppptr)[3:4:2])
+// { dg-final { scan-tree-dump {map\(from_grid:VIEW_CONVERT_EXPR<long int\[10\]>\(\*\*\*\*\(\*\*rps\)->rppptr\) \[len: [0-9]+\]\) map\(grid_dim:3 \[len: 4\]\) map\(grid_stride:2\)} "original" } }
+
+#pragma omp target update from((**(*ps)->ppptr)[3:4:2])
+// { dg-final { scan-tree-dump {map\(from_grid:\*\*\(\*ps\)->ppptr \[len: [0-9]+\]\) map\(grid_dim:3 \[len: 4\]\) map\(grid_stride:2\)} "original" } }
+
+#pragma omp target update from((**(*rps)->rppptr)[3:4:2])
+// { dg-final { scan-tree-dump {map\(from_grid:\*\*\*\(\*\*rps\)->rppptr \[len: [0-9]+\]\) map\(grid_dim:3 \[len: 4\]\) map\(grid_stride:2\)} "original" } }
+
+  B arr[10][10];
+  B (*parr)[10][10] = &arr;
+  B (**pparr2)[10][10] = &parr;
+  B (**&rpparr2)[10][10] = pparr2;
+
+#pragma omp target update from(**pparr2)
+// { dg-final { scan-tree-dump {from\(\*NON_LVALUE_EXPR <\*pparr2> \[len: [0-9]+\]\)} "original" } }
+
+#pragma omp target update to((**pparr2)[1:5:2][3:4:2])
+// { dg-final { scan-tree-dump {map\(to_grid:\*\*pparr2 \[len: [0-9]+\]\) map\(grid_dim:1 \[len: 5\]\) map\(grid_stride:2\) map\(grid_dim:3 \[len: 4\]\) map\(grid_stride:2\)} "original" } }
+
+#pragma omp target update from((**rpparr2)[1:5:2][3:4:2])
+// { dg-final { scan-tree-dump {map\(from_grid:\*\*\*rpparr2 \[len: [0-9]+\]\) map\(grid_dim:1 \[len: 5\]\) map\(grid_stride:2\) map\(grid_dim:3 \[len: 4\]\) map\(grid_stride:2\)} "original" } }
+
+  delete s;
+}
+
+struct S
+{
+  short ***ppptr;
+  short ***&rppptr;
+
+  S(short ***p, short ***&rp) : ppptr(p), rppptr(rp) { }
+};
+
+int main()
+{
+  char *ptr;
+  char **pptr = &ptr;
+  char ***ppptr = &pptr;
+  char ***&rppptr = ppptr;
+
+#pragma omp target update to(([10]) (**ppptr)[1:5:2])
+// { dg-final { scan-tree-dump {map\(to_grid:VIEW_CONVERT_EXPR<char\[10\]>\(\*\*\*ppptr\) \[len: 1\]\) map\(grid_dim:1 \[len: 5\]\) map\(grid_stride:2\)} "original" } }
+
+#pragma omp target update to(([10]) (**rppptr)[1:5:2])
+// { dg-final { scan-tree-dump {map\(to_grid:VIEW_CONVERT_EXPR<char\[10\]>\(\*\*\*\*rppptr\) \[len: 1\]\) map\(grid_dim:1 \[len: 5\]\) map\(grid_stride:2\)} "original" } }
+
+#pragma omp target update to((**ppptr)[1:5:2])
+// { dg-final { scan-tree-dump {map\(to_grid:\*\*ppptr \[len: 1\]\) map\(grid_dim:1 \[len: 5\]\) map\(grid_stride:2\)} "original" } }
+
+#pragma omp target update to((**rppptr)[1:5:2])
+// { dg-final { scan-tree-dump {map\(to_grid:\*\*\*rppptr \[len: 1\]\) map\(grid_dim:1 \[len: 5\]\) map\(grid_stride:2\)} "original" } }
+
+  short *ptr2;
+  short **pptr2 = &ptr2;
+  short ***ppptr2 = &pptr2;
+  S *s = new S(ppptr2, ppptr2);
+  S **ps = &s;
+  S **&rps = ps;
+
+#pragma omp target update from(([10]) (**(*ps)->ppptr)[1:5:2])
+// { dg-final { scan-tree-dump {map\(from_grid:VIEW_CONVERT_EXPR<short int\[10\]>\(\*\*\*\(\*ps\)->ppptr\) \[len: [0-9]+\]\) map\(grid_dim:1 \[len: 5\]\) map\(grid_stride:2\)} "original" } }
+
+#pragma omp target update from(([10]) (**(*rps)->rppptr)[1:5:2])
+// { dg-final { scan-tree-dump {map\(from_grid:VIEW_CONVERT_EXPR<short int\[10\]>\(\*\*\*\*\(\*\*rps\)->rppptr\) \[len: [0-9]+\]\) map\(grid_dim:1 \[len: 5\]\) map\(grid_stride:2\)} "original" } }
+
+#pragma omp target update from((**(*ps)->ppptr)[1:5:2])
+// { dg-final { scan-tree-dump {map\(from_grid:\*\*\(\*ps\)->ppptr \[len: [0-9]+\]\) map\(grid_dim:1 \[len: 5\]\) map\(grid_stride:2\)} "original" } }
+
+#pragma omp target update from((**(*rps)->rppptr)[1:5:2])
+// { dg-final { scan-tree-dump {map\(from_grid:\*\*\*\(\*\*rps\)->rppptr \[len: [0-9]+\]\) map\(grid_dim:1 \[len: 5\]\) map\(grid_stride:2\)} "original" } }
+
+  delete s;
+
+  short arr[10][10];
+  short (*parr)[10][10] = &arr;
+  short (**pparr)[10][10] = &parr;
+  short (**&rpparr)[10][10] = pparr;
+
+#pragma omp target update from(**pparr)
+// { dg-final { scan-tree-dump {from\(\*NON_LVALUE_EXPR <\*pparr> \[len: [0-9]+\]\)} "original" } }
+
+#pragma omp target update to((**pparr)[1:5:2][1:5:2])
+// { dg-final { scan-tree-dump {map\(to_grid:\*\*pparr \[len: [0-9]+\]\) map\(grid_dim:1 \[len: 5\]\) map\(grid_stride:2\) map\(grid_dim:1 \[len: 5\]\) map\(grid_stride:2\)} "original" } }
+
+#pragma omp target update from((**rpparr)[1:5:2][1:5:2])
+// { dg-final { scan-tree-dump {map\(from_grid:\*\*\*rpparr \[len: [0-9]+\]\) map\(grid_dim:1 \[len: 5\]\) map\(grid_stride:2\) map\(grid_dim:1 \[len: 5\]\) map\(grid_stride:2\)} "original" } }
+
+  foo<int, long> ();
+
+  return 0;
+}
diff --git a/gcc/testsuite/g++.dg/gomp/bad-array-shaping-1.C b/gcc/testsuite/g++.dg/gomp/bad-array-shaping-1.C
new file mode 100644
index 00000000000..1f4e68bc065
--- /dev/null
+++ b/gcc/testsuite/g++.dg/gomp/bad-array-shaping-1.C
@@ -0,0 +1,47 @@
+// { dg-do compile }
+
+#include <string.h>
+#include <assert.h>
+
+template<typename T, int C, int D>
+void foo (T *w)
+{
+  memset (w, 0, sizeof (T) * 100);
+
+#pragma omp target enter data map(to: w[:100])
+
+  for (int j = 0; j < 10; j++)
+    for (int i = 0; i < 10; i++)
+      w[j * 10 + i] = i + j * 3;
+
+#pragma omp target update to(([C][D]) w[3:2][1:8][0:5])
+// { dg-error "too many array section specifiers for" "" { target *-*-* } .-1 }
+// { dg-error "'#pragma omp target update' must contain at least one 'from' or 'to' clauses" "" { target *-*-* } .-2 }
+
+#pragma omp target exit data map(from: w[:100])
+}
+
+int main()
+{
+  float *arr = new float[100];
+
+  memset (arr, 0, sizeof (float) * 100);
+
+#pragma omp target enter data map(to: arr[:100])
+
+  for (int j = 0; j < 10; j++)
+    for (int i = 0; i < 10; i++)
+      arr[j * 10 + i] = i + j * 3;
+
+#pragma omp target update to(([10][10]) arr[3:2][1:8][0:5])
+// { dg-error "too many array section specifiers for" "" { target *-*-* } .-1 }
+// { dg-error "'#pragma omp target update' must contain at least one 'from' or 'to' clauses" "" { target *-*-* } .-2 }
+
+#pragma omp target exit data map(from: arr[:100])
+
+  foo<float, 5, 20> (arr);
+
+  delete[] arr;
+
+  return 0;
+}
diff --git a/gcc/testsuite/g++.dg/gomp/bad-array-shaping-2.C b/gcc/testsuite/g++.dg/gomp/bad-array-shaping-2.C
new file mode 100644
index 00000000000..d3209292546
--- /dev/null
+++ b/gcc/testsuite/g++.dg/gomp/bad-array-shaping-2.C
@@ -0,0 +1,52 @@
+// { dg-do compile }
+
+#include <string.h>
+#include <assert.h>
+
+template<typename T, int C, int D>
+void foo (T *w)
+{
+  /* This isn't allowed. We get a cascade of errors because it looks a bit
+     like lambda-definition syntax  */
+#pragma omp target enter data map(to: ([C][D]) w[:100])
+  // { dg-error {capture of non-variable 'C'} "" { target *-*-* } .-1 }
+  // { dg-error {expected '\{' before '\[' token} "" { target *-*-* } .-2 }
+  // { dg-warning {lambda expressions only available with} "" { target c++98_only } .-3 }
+  // { dg-error {expected '\)' before 'w'} "" { target *-*-* } .-4 }
+  // { dg-error {does not have pointer or array type} "" { target *-*-* } .-5 }
+
+#pragma omp target exit data map(from: ([C][D]) w[:100])
+  // { dg-error {capture of non-variable 'C'} "" { target *-*-* } .-1 }
+  // { dg-error {expected '\{' before '\[' token} "" { target *-*-* } .-2 }
+  // { dg-warning {lambda expressions only available with} "" { target c++98_only } .-3 }
+  // { dg-error {expected '\)' before 'w'} "" { target *-*-* } .-4 }
+  // { dg-error {does not have pointer or array type} "" { target *-*-* } .-5 }
+}
+
+int main()
+{
+  float *arr = new float[100];
+
+  /* This isn't allowed (as above).  */
+#pragma omp target enter data map(to: ([10][10]) arr[:100])
+  // { dg-error {expected identifier before numeric constant} "" { target *-*-* } .-1 }
+  // { dg-error {expected '\{' before '\[' token} "" { target *-*-* } .-2 }
+  // { dg-warning {lambda expressions only available with} "" { target c++98_only } .-3 }
+  // { dg-error {expected '\)' before 'arr'} "" { target *-*-* } .-4 }
+  // { dg-error {no match for 'operator\[\]' in} "" { target *-*-* } .-5 }
+  // { dg-error {'#pragma omp target enter data' must contain at least one 'map' clause} "" { target *-*-*} .-6 }
+
+#pragma omp target exit data map(from: ([10][10]) arr[:100])
+  // { dg-error {expected identifier before numeric constant} "" { target *-*-* } .-1 }
+  // { dg-error {expected '\{' before '\[' token} "" { target *-*-* } .-2 }
+  // { dg-warning {lambda expressions only available with} "" { target c++98_only } .-3 }
+  // { dg-error {no match for 'operator\[\]' in} "" { target *-*-* } .-4 }
+  // { dg-error {expected '\)' before 'arr'} "" { target *-*-* } .-5 }
+  // { dg-error {'#pragma omp target exit data' must contain at least one 'map' clause} "" { target *-*-* } .-6 }
+
+  foo<float, 5, 20> (arr);
+
+  delete[] arr;
+
+  return 0;
+}
diff --git a/gcc/testsuite/g++.dg/gomp/bad-array-shaping-3.C b/gcc/testsuite/g++.dg/gomp/bad-array-shaping-3.C
new file mode 100644
index 00000000000..90d0a5a80c5
--- /dev/null
+++ b/gcc/testsuite/g++.dg/gomp/bad-array-shaping-3.C
@@ -0,0 +1,53 @@
+// { dg-do compile }
+
+#include <string.h>
+#include <assert.h>
+
+template<typename T>
+void foo (T *w)
+{
+  memset (w, 0, sizeof (T) * 100);
+  int c = 50;
+
+#pragma omp target enter data map(to: w[:100])
+
+  for (int j = 0; j < 10; j++)
+    for (int i = 0; i < 10; i++)
+      w[j * 10 + i] = i + j * 3;
+
+  /* This starts out looking like an array-shape cast.  Make sure it's still
+     parsed as a lambda.  */
+#pragma omp target update to(([c] (T *v) -> T { return v[c]; } (w)))
+  // { dg-message {sorry, unimplemented: unsupported map expression} "" { target *-*-* } .-1 }
+  // { dg-warning {lambda expressions only available with} "" { target c++98_only } .-2 }
+
+#pragma omp target exit data map(from: w[:100])
+}
+
+int main()
+{
+  float *arr = new float[100];
+  int c = 50;
+
+  memset (arr, 0, sizeof (float) * 100);
+
+#pragma omp target enter data map(to: arr[:100])
+
+  for (int j = 0; j < 10; j++)
+    for (int i = 0; i < 10; i++)
+      arr[j * 10 + i] = i + j * 3;
+
+  /* As above.  */
+#pragma omp target update to(([c] (float *v) -> float { return v[c]; } (arr)))
+  // { dg-message {sorry, unimplemented: unsupported map expression} "" { target *-*-* } .-1 }
+  // { dg-error {'#pragma omp target update' must contain at least one 'from' or 'to' clauses} "" { target *-*-* } .-2 }
+  // { dg-warning {lambda expressions only available with} "" { target c++98_only } .-3 }
+
+#pragma omp target exit data map(from: arr[:100])
+
+  foo<float> (arr);
+
+  delete[] arr;
+
+  return 0;
+}
diff --git a/gcc/testsuite/g++.dg/gomp/bad-array-shaping-4.C b/gcc/testsuite/g++.dg/gomp/bad-array-shaping-4.C
new file mode 100644
index 00000000000..4518f03e9a0
--- /dev/null
+++ b/gcc/testsuite/g++.dg/gomp/bad-array-shaping-4.C
@@ -0,0 +1,60 @@
+// { dg-do compile }
+
+#include <string.h>
+#include <assert.h>
+
+template<typename T>
+extern T* baz(T*);
+
+template<typename T>
+void foo (T *w)
+{
+  memset (w, 0, sizeof (T) * 100);
+  int c = 50;
+
+#pragma omp target enter data map(to: w[:100])
+
+  for (int j = 0; j < 10; j++)
+    for (int i = 0; i < 10; i++)
+      w[j * 10 + i] = i + j * 3;
+
+  /* No array-shaping inside a function call.  */
+#pragma omp target update to(baz(([10][10]) w))
+  // { dg-error {expected identifier before numeric constant} "" { target *-*-* } .-1 }
+  // { dg-error {expected '\{' before '\[' token} "" { target *-*-* } .-2 }
+  // { dg-warning {lambda expressions only available with} "" { target c++98_only } .-3 }
+  // { dg-error {expected '\)' before 'w'} "" { target *-*-* } .-4 }
+  // { dg-error {no match for 'operator\[\]' in} "" { target *-*-* } .-5 }
+
+#pragma omp target exit data map(from: w[:100])
+}
+
+int main()
+{
+  float *arr = new float[100];
+  int c = 50;
+
+  memset (arr, 0, sizeof (float) * 100);
+
+#pragma omp target enter data map(to: arr[:100])
+
+  for (int j = 0; j < 10; j++)
+    for (int i = 0; i < 10; i++)
+      arr[j * 10 + i] = i + j * 3;
+
+  /* As above.  */
+#pragma omp target update to(baz(([10][10]) arr))
+  // { dg-error {expected identifier before numeric constant} "" { target *-*-* } .-1 }
+  // { dg-error {expected '\{' before '\[' token} "" { target *-*-* } .-2 }
+  // { dg-warning {lambda expressions only available with} "" { target c++98_only } .-3 }
+  // { dg-error {no match for 'operator\[\]' in} "" { target *-*-* } .-4 }
+  // { dg-error {'#pragma omp target update' must contain at least one 'from' or 'to' clauses} "" { target *-*-* } .-5 }
+
+#pragma omp target exit data map(from: arr[:100])
+
+  foo<float> (arr);
+
+  delete[] arr;
+
+  return 0;
+}
diff --git a/gcc/testsuite/g++.dg/gomp/bad-array-shaping-5.C b/gcc/testsuite/g++.dg/gomp/bad-array-shaping-5.C
new file mode 100644
index 00000000000..25edb9d1d9d
--- /dev/null
+++ b/gcc/testsuite/g++.dg/gomp/bad-array-shaping-5.C
@@ -0,0 +1,55 @@
+// { dg-do compile }
+// { dg-additional-options "-std=c++14" }
+
+#include <string.h>
+#include <assert.h>
+
+template<typename T>
+void foo (T *w)
+{
+  memset (w, 0, sizeof (T) * 100);
+  int c = 50;
+
+#pragma omp target enter data map(to: w[:100])
+
+  for (int j = 0; j < 10; j++)
+    for (int i = 0; i < 10; i++)
+      w[j * 10 + i] = i + j * 3;
+
+  /* No array-shaping inside a lambda body.  */
+#pragma omp target update to([&](const int d) -> auto& { return ([d][d]) w; } (10))
+// { dg-error {expected '\{' before '\[' token} "" { target *-*-* } .-1 }
+// { dg-error {expected ';' before 'w'} "" { target *-*-* } .-2 }
+// { dg-error {no match for 'operator\[\]' in} "" { target *-*-* } .-3 }
+
+#pragma omp target exit data map(from: w[:100])
+}
+
+int main()
+{
+  float *arr = new float[100];
+  int c = 50;
+
+  memset (arr, 0, sizeof (float) * 100);
+
+#pragma omp target enter data map(to: arr[:100])
+
+  for (int j = 0; j < 10; j++)
+    for (int i = 0; i < 10; i++)
+      arr[j * 10 + i] = i + j * 3;
+
+  /* As above.  */
+#pragma omp target update to([&](const int d) -> auto& { return ([d][d]) arr; } (10))
+// { dg-error {expected '\{' before '\[' token} "" { target *-*-* } .-1 }
+// { dg-error {no match for 'operator\[\]' in} "" { target *-*-* } .-2 }
+// { dg-error {expected ';' before 'arr'} "" { target *-*-* } .-3 }
+// { dg-error {'#pragma omp target update' must contain at least one 'from' or 'to' clauses} "" { target *-*-* } .-4 }
+
+#pragma omp target exit data map(from: arr[:100])
+
+  foo<float> (arr);
+
+  delete[] arr;
+
+  return 0;
+}
diff --git a/gcc/testsuite/g++.dg/gomp/bad-array-shaping-6.C b/gcc/testsuite/g++.dg/gomp/bad-array-shaping-6.C
new file mode 100644
index 00000000000..e796eaa39a3
--- /dev/null
+++ b/gcc/testsuite/g++.dg/gomp/bad-array-shaping-6.C
@@ -0,0 +1,59 @@
+// { dg-do compile }
+
+#include <string.h>
+#include <assert.h>
+
+template<typename T>
+void foo (T *w)
+{
+  memset (w, 0, sizeof (T) * 100);
+
+#pragma omp target enter data map(to: w[:100])
+
+  for (int j = 0; j < 10; j++)
+    for (int i = 0; i < 10; i++)
+      w[j * 10 + i] = i + j * 3;
+
+  /* No array-shaping inside a statement expression.  */
+#pragma omp target update to( ({ int d = 10; ([d][d]) w; )} )
+// { dg-error {expected '\{' before '\[' token} "" { target *-*-* } .-1 }
+// { dg-warning {lambda expressions only available with} "" { target c++98_only } .-2 }
+// { dg-error {no match for 'operator\[\]'} "" { target *-*-* } .-3 }
+// { dg-error {expected ';' before 'w'} "" { target *-*-* } .-4 }
+// { dg-error {expected primary-expression before '\)' token} "" { target *-*-* } .-5 }
+// { dg-error {expected '\)' before end of line} "" { target *-*-* } .-6 }
+// { dg-message {sorry, unimplemented: unsupported map expression} "" { target *-*-* } .-7 }
+
+#pragma omp target exit data map(from: w[:100])
+}
+
+int main()
+{
+  float *arr = new float[100];
+
+  memset (arr, 0, sizeof (float) * 100);
+
+#pragma omp target enter data map(to: arr[:100])
+
+  for (int j = 0; j < 10; j++)
+    for (int i = 0; i < 10; i++)
+      arr[j * 10 + i] = i + j * 3;
+
+  /* As above.  */
+#pragma omp target update to( ({ int d = 10; ([d][d]) arr; )} )
+// { dg-error {expected '\{' before '\[' token} "" { target *-*-* } .-1 }
+// { dg-warning {lambda expressions only available with} "" { target c++98_only } .-2 }
+// { dg-error {no match for 'operator\[\]'} "" { target *-*-* } .-3 }
+// { dg-error {expected primary-expression before '\)' token} "" { target *-*-* } .-4 }
+// { dg-error {expected '\)' before end of line} "" { target *-*-* } .-5 }
+// { dg-message {sorry, unimplemented: unsupported map expression} "" { target *-*-* } .-6 }
+// { dg-error {'#pragma omp target update' must contain at least one 'from' or 'to' clauses} "" { target *-*-* } .-7 }
+
+#pragma omp target exit data map(from: arr[:100])
+
+  foo<float> (arr);
+
+  delete[] arr;
+
+  return 0;
+}
diff --git a/gcc/testsuite/g++.dg/gomp/bad-array-shaping-7.C b/gcc/testsuite/g++.dg/gomp/bad-array-shaping-7.C
new file mode 100644
index 00000000000..62463b2f6c6
--- /dev/null
+++ b/gcc/testsuite/g++.dg/gomp/bad-array-shaping-7.C
@@ -0,0 +1,48 @@
+// { dg-do compile }
+// { dg-additional-options "-std=c++11" }
+
+#include <new>
+
+template<typename T>
+struct St {
+  T *pp;
+};
+
+template<typename T>
+void foo (T *w)
+{
+  alignas (St<T>) unsigned char buf[sizeof (St<T>)];
+  T *sub1;
+
+  /* No array shaping op in brace initialiser (nonsensical anyway, but make
+     sure it doesn't parse).  */
+#pragma omp target update to( new (buf) St<T> { ([10][10]) sub1 } )
+// { dg-error {expected identifier before numeric constant} "" { target *-*-* } .-1 }
+// { dg-error {expected '\{' before '\[' token} "" { target *-*-* } .-2 }
+// { dg-error {expected '\}' before 'sub1'} "" { target *-*-* } .-3 }
+// { dg-error {expected '\)' before 'sub1'} "" { target *-*-* } .-4 }
+// { dg-error {expected an OpenMP clause before '\}' token} "" { target *-*-* } .-5 }
+}
+
+struct S {
+  int *pp;
+};
+
+int main()
+{
+  alignas (S) unsigned char buf[sizeof (S)];
+  int *sub1;
+
+  // As above.
+#pragma omp target update to( new (buf) S { ([10][10]) sub1 } )
+// { dg-error {expected identifier before numeric constant} "" { target *-*-* } .-1 }
+// { dg-error {expected '\{' before '\[' token} "" { target *-*-* } .-2 }
+// { dg-error {expected '\}' before 'sub1'} "" { target *-*-* } .-3 }
+// { dg-error {expected '\)' before 'sub1'} "" { target *-*-* } .-4 }
+// { dg-error {expected an OpenMP clause before '\}' token} "" { target *-*-* } .-5 }
+// { dg-error {no match for 'operator\[\]'} "" { target *-*-* } .-6 }
+// { dg-error {could not convert} "" { target *-*-* } .-7 }
+// { dg-error {'#pragma omp target update' must contain at least one 'from' or 'to' clauses} "" { target *-*-* } .-8 }
+
+  return 0;
+}
diff --git a/gcc/testsuite/g++.dg/gomp/bad-array-shaping-8.C b/gcc/testsuite/g++.dg/gomp/bad-array-shaping-8.C
new file mode 100644
index 00000000000..02d7de6088e
--- /dev/null
+++ b/gcc/testsuite/g++.dg/gomp/bad-array-shaping-8.C
@@ -0,0 +1,50 @@
+// { dg-do compile }
+
+template<typename T>
+void foo ()
+{
+  T *ptr;
+
+#pragma omp target update to(([5][6][7]) ptr[0:4][0:7][0:7])
+// { dg-error {length '7' with stride '1' above array section size in 'to' clause} "" { target *-*-* } .-1 }
+
+#pragma omp target update to(([5][6][7]) ptr[1:5][0:6][0:7])
+// { dg-error {high bound '6' above array section size in 'to' clause} "" { target *-*-* } .-1 }
+
+  // This one's OK...
+#pragma omp target update from(([100]) ptr[3:33:3])
+
+  // But this is one element out of bounds.
+#pragma omp target update from(([100]) ptr[4:33:3])
+// { dg-error {high bound '101' above array section size in 'from' clause} "" { target *-*-* } .-1 }
+
+#pragma omp target update to(([10][10]) ptr[0:9:-1][0:9])
+// { dg-error {length '9' with stride '-1' above array section size in 'to' clause} "" { target *-*-* } .-1 }
+}
+
+int main()
+{
+  char *ptr;
+
+#pragma omp target update to(([5][6][7]) ptr[0:4][0:7][0:7])
+// { dg-error {length '7' with stride '1' above array section size in 'to' clause} "" { target *-*-* } .-1 }
+// { dg-error {'#pragma omp target update' must contain at least one 'from' or 'to' clauses} "" { target *-*-* } .-2 }
+
+#pragma omp target update to(([5][6][7]) ptr[1:5][0:6][0:7])
+// { dg-error {high bound '6' above array section size in 'to' clause} "" { target *-*-* } .-1 }
+// { dg-error {'#pragma omp target update' must contain at least one 'from' or 'to' clauses} "" { target *-*-* } .-2 }
+
+#pragma omp target update from(([100]) ptr[3:33:3])
+
+#pragma omp target update from(([100]) ptr[4:33:3])
+// { dg-error {high bound '101' above array section size in 'from' clause} "" { target *-*-* } .-1 }
+// { dg-error {'#pragma omp target update' must contain at least one 'from' or 'to' clauses} "" { target *-*-* } .-2 }
+
+#pragma omp target update to(([10][10]) ptr[0:9:-1][0:9])
+// { dg-error {length '9' with stride '-1' above array section size in 'to' clause} "" { target *-*-* } .-1 }
+// { dg-error {'#pragma omp target update' must contain at least one 'from' or 'to' clauses} "" { target *-*-* } .-2 }
+
+  foo<char> ();
+
+  return 0;
+}
diff --git a/gcc/tree-pretty-print.cc b/gcc/tree-pretty-print.cc
index 42dc0f12e11..2bcb9a2cf42 100644
--- a/gcc/tree-pretty-print.cc
+++ b/gcc/tree-pretty-print.cc
@@ -1124,6 +1124,18 @@ dump_omp_clause (pretty_printer *pp, tree clause, int spc, dump_flags_t flags)
 	case GOMP_MAP_NONCONTIG_ARRAY_FORCE_PRESENT:
 	  pp_string (pp, "force_present,noncontig_array");
 	  break;
+	case GOMP_MAP_TO_GRID:
+	  pp_string (pp, "to_grid");
+	  break;
+	case GOMP_MAP_FROM_GRID:
+	  pp_string (pp, "from_grid");
+	  break;
+	case GOMP_MAP_GRID_DIM:
+	  pp_string (pp, "grid_dim");
+	  break;
+	case GOMP_MAP_GRID_STRIDE:
+	  pp_string (pp, "grid_stride");
+	  break;
 	case GOMP_MAP_UNSET:
 	  pp_string (pp, "unset");
 	  break;
@@ -2750,6 +2762,11 @@ dump_generic_node (pretty_printer *pp, tree node, int spc, dump_flags_t flags,
       dump_generic_node (pp, TREE_OPERAND (node, 1), spc, flags, false);
       pp_colon (pp);
       dump_generic_node (pp, TREE_OPERAND (node, 2), spc, flags, false);
+      if (TREE_OPERAND (node, 3))
+	{
+	  pp_colon (pp);
+	  dump_generic_node (pp,  TREE_OPERAND (node, 3), spc, flags, false);
+	}
       pp_right_bracket (pp);
       break;
 
diff --git a/gcc/tree.def b/gcc/tree.def
index b9454e7c783..c5e99441571 100644
--- a/gcc/tree.def
+++ b/gcc/tree.def
@@ -1372,7 +1372,7 @@ DEFTREECODE (OMP_ALLOCATE, "omp allocate", tcc_statement, 1)
 DEFTREECODE (OMP_CLAUSE, "omp_clause", tcc_exceptional, 0)
 
 /* An OpenMP array section.  */
-DEFTREECODE (OMP_ARRAY_SECTION, "omp_array_section", tcc_expression, 3)
+DEFTREECODE (OMP_ARRAY_SECTION, "omp_array_section", tcc_expression, 4)
 
 /* TRANSACTION_EXPR tree code.
    Operand 0: BODY: contains body of the transaction.  */
diff --git a/include/gomp-constants.h b/include/gomp-constants.h
index 8aa28421b60..1c7508bedce 100644
--- a/include/gomp-constants.h
+++ b/include/gomp-constants.h
@@ -220,6 +220,9 @@ enum gomp_map_kind
     GOMP_MAP_ATTACH_ZERO_LENGTH_ARRAY_SECTION
       =					(GOMP_MAP_DEEP_COPY | 2),
 
+    GOMP_MAP_TO_GRID =			(GOMP_MAP_DEEP_COPY | 4),
+    GOMP_MAP_FROM_GRID =		(GOMP_MAP_DEEP_COPY | 5),
+
     /* Internal to GCC, not used in libgomp.  */
     /* Do not map, but pointer assign a pointer instead.  */
     GOMP_MAP_FIRSTPRIVATE_POINTER =	(GOMP_MAP_LAST | 1),
@@ -243,7 +246,9 @@ enum gomp_map_kind
     GOMP_MAP_POP_MAPPER_NAME =		(GOMP_MAP_LAST | 10),
     /* Used to hold a TREE_LIST of grouped nodes in an 'omp declare mapper'
        definition (only for Fortran at present).  */
-    GOMP_MAP_MAPPING_GROUP =		(GOMP_MAP_LAST | 11)
+    GOMP_MAP_MAPPING_GROUP =		(GOMP_MAP_LAST | 11),
+    GOMP_MAP_GRID_DIM =			(GOMP_MAP_LAST | 12),
+    GOMP_MAP_GRID_STRIDE =		(GOMP_MAP_LAST | 13)
   };
 
 #define GOMP_MAP_COPY_TO_P(X) \
diff --git a/libgomp/libgomp.h b/libgomp/libgomp.h
index 1a9468fbd14..f884617c9ae 100644
--- a/libgomp/libgomp.h
+++ b/libgomp/libgomp.h
@@ -1313,6 +1313,20 @@ struct target_mem_desc {
 };
 
 
+/* A rectangular section of an array, for noncontiguous target update
+   operations.  Must be kept in sync with
+   omp-low.cc:omp_noncontig_descriptor_type.  */
+
+typedef struct {
+  size_t ndims;
+  size_t elemsize;
+  size_t *dim;
+  size_t *index;
+  size_t *length;
+  size_t *stride;
+} omp_noncontig_array_desc;
+
+
 typedef struct acc_dispatch_t
 {
   /* Execute.  */
diff --git a/libgomp/target.c b/libgomp/target.c
index 4447675cd16..a060f850c70 100644
--- a/libgomp/target.c
+++ b/libgomp/target.c
@@ -2604,6 +2604,13 @@ goacc_unmap_vars (struct target_mem_desc *tgt, bool do_copyfrom,
   gomp_unmap_vars_internal (tgt, do_copyfrom, NULL, aq);
 }
 
+static int omp_target_memcpy_rect_worker (void *, const void *, size_t, int,
+					  const size_t *, const size_t *,
+					  const size_t *, const size_t *,
+					  const size_t *, const size_t *,
+					  struct gomp_device_descr *,
+					  struct gomp_device_descr *);
+
 static void
 gomp_update (struct gomp_device_descr *devicep, size_t mapnum, void **hostaddrs,
 	     size_t *sizes, void *kinds, bool short_mapkind)
@@ -2626,90 +2633,129 @@ gomp_update (struct gomp_device_descr *devicep, size_t mapnum, void **hostaddrs,
     }
 
   for (i = 0; i < mapnum; i++)
-    if (sizes[i])
-      {
-	cur_node.host_start = (uintptr_t) hostaddrs[i];
-	cur_node.host_end = cur_node.host_start + sizes[i];
-	splay_tree_key n = splay_tree_lookup (&devicep->mem_map, &cur_node);
-	if (n)
-	  {
-	    int kind = get_kind (short_mapkind, kinds, i);
-	    if (n->host_start > cur_node.host_start
-		|| n->host_end < cur_node.host_end)
-	      {
-		gomp_mutex_unlock (&devicep->lock);
-		gomp_fatal ("Trying to update [%p..%p) object when "
-			    "only [%p..%p) is mapped",
-			    (void *) cur_node.host_start,
-			    (void *) cur_node.host_end,
-			    (void *) n->host_start,
-			    (void *) n->host_end);
-	      }
+    {
+      int kind = get_kind (short_mapkind, kinds, i);
+      if ((kind & typemask) == GOMP_MAP_TO_GRID
+	  || (kind & typemask) == GOMP_MAP_FROM_GRID)
+	{
+	  omp_noncontig_array_desc *desc
+	    = (omp_noncontig_array_desc *) hostaddrs[i + 1];
+	  cur_node.host_start = (uintptr_t) hostaddrs[i];
+	  cur_node.host_end = cur_node.host_start + sizes[i];
+	  assert (sizes[i + 1] == sizeof (omp_noncontig_array_desc));
+	  splay_tree_key n = splay_tree_lookup (&devicep->mem_map, &cur_node);
+	  if (n)
+	    {
+	      if (n->aux && n->aux->attach_count)
+		{
+		  gomp_mutex_unlock (&devicep->lock);
+		  gomp_error ("noncontiguous update with attached pointers");
+		  return;
+		}
+	      void *devaddr = (void *) (n->tgt->tgt_start + n->tgt_offset
+					+ cur_node.host_start
+					- n->host_start);
+	      if ((kind & typemask) == GOMP_MAP_TO_GRID)
+		omp_target_memcpy_rect_worker (devaddr, hostaddrs[i],
+					       desc->elemsize, desc->ndims,
+					       desc->length, desc->stride,
+					       desc->index, desc->index,
+					       desc->dim, desc->dim, devicep,
+					       NULL);
+	      else
+		omp_target_memcpy_rect_worker (hostaddrs[i], devaddr,
+					       desc->elemsize, desc->ndims,
+					       desc->length, desc->stride,
+					       desc->index, desc->index,
+					       desc->dim, desc->dim, NULL,
+					       devicep);
+	    }
+	  i++;
+	}
+      else if (sizes[i])
+	{
+	  cur_node.host_start = (uintptr_t) hostaddrs[i];
+	  cur_node.host_end = cur_node.host_start + sizes[i];
+	  splay_tree_key n = splay_tree_lookup (&devicep->mem_map, &cur_node);
+	  if (n)
+	    {
+	      if (n->host_start > cur_node.host_start
+		  || n->host_end < cur_node.host_end)
+		{
+		  gomp_mutex_unlock (&devicep->lock);
+		  gomp_fatal ("Trying to update [%p..%p) object when "
+			      "only [%p..%p) is mapped",
+			      (void *) cur_node.host_start,
+			      (void *) cur_node.host_end,
+			      (void *) n->host_start,
+			      (void *) n->host_end);
+		}
 
-	    if (n->aux && n->aux->attach_count)
-	      {
-		uintptr_t addr = cur_node.host_start;
-		while (addr < cur_node.host_end)
-		  {
-		    /* We have to be careful not to overwrite still attached
-		       pointers during host<->device updates.  */
-		    size_t i = (addr - cur_node.host_start) / sizeof (void *);
-		    if (n->aux->attach_count[i] == 0)
-		      {
-			void *devaddr = (void *) (n->tgt->tgt_start
-						  + n->tgt_offset
-						  + addr - n->host_start);
-			if (GOMP_MAP_COPY_TO_P (kind & typemask))
-			  gomp_copy_host2dev (devicep, NULL,
-					      devaddr, (void *) addr,
-					      sizeof (void *), false, NULL);
-			if (GOMP_MAP_COPY_FROM_P (kind & typemask))
-			  gomp_copy_dev2host (devicep, NULL,
-					      (void *) addr, devaddr,
-					      sizeof (void *));
-		      }
-		    addr += sizeof (void *);
-		  }
-	      }
-	    else
-	      {
-		void *hostaddr = (void *) cur_node.host_start;
-		void *devaddr = (void *) (n->tgt->tgt_start + n->tgt_offset
-					  + cur_node.host_start
-					  - n->host_start);
-		size_t size = cur_node.host_end - cur_node.host_start;
+	      if (n->aux && n->aux->attach_count)
+		{
+		  uintptr_t addr = cur_node.host_start;
+		  while (addr < cur_node.host_end)
+		    {
+		      /* We have to be careful not to overwrite still attached
+			 pointers during host<->device updates.  */
+		      size_t i = (addr - cur_node.host_start) / sizeof (void *);
+		      if (n->aux->attach_count[i] == 0)
+			{
+			  void *devaddr = (void *) (n->tgt->tgt_start
+						    + n->tgt_offset
+						    + addr - n->host_start);
+			  if (GOMP_MAP_COPY_TO_P (kind & typemask))
+			    gomp_copy_host2dev (devicep, NULL,
+						devaddr, (void *) addr,
+						sizeof (void *), false, NULL);
+			  if (GOMP_MAP_COPY_FROM_P (kind & typemask))
+			    gomp_copy_dev2host (devicep, NULL,
+						(void *) addr, devaddr,
+						sizeof (void *));
+			}
+		      addr += sizeof (void *);
+		    }
+		}
+	      else
+		{
+		  void *hostaddr = (void *) cur_node.host_start;
+		  void *devaddr = (void *) (n->tgt->tgt_start + n->tgt_offset
+					    + cur_node.host_start
+					    - n->host_start);
+		  size_t size = cur_node.host_end - cur_node.host_start;
 
-		if (GOMP_MAP_COPY_TO_P (kind & typemask))
-		  gomp_copy_host2dev (devicep, NULL, devaddr, hostaddr, size,
-				      false, NULL);
-		if (GOMP_MAP_COPY_FROM_P (kind & typemask))
-		  gomp_copy_dev2host (devicep, NULL, hostaddr, devaddr, size);
-	      }
-	  }
-	else
-	  {
-	    int kind = get_kind (short_mapkind, kinds, i);
+		  if (GOMP_MAP_COPY_TO_P (kind & typemask))
+		    gomp_copy_host2dev (devicep, NULL, devaddr, hostaddr, size,
+					false, NULL);
+		  if (GOMP_MAP_COPY_FROM_P (kind & typemask))
+		    gomp_copy_dev2host (devicep, NULL, hostaddr, devaddr, size);
+		}
+	    }
+	  else
+	    {
+	      int kind = get_kind (short_mapkind, kinds, i);
 
-	    if (GOMP_MAP_PRESENT_P (kind))
-	      {
-		/* We already looked up the memory region above and it
-		   was missing.  */
-		gomp_mutex_unlock (&devicep->lock);
+	      if (GOMP_MAP_PRESENT_P (kind))
+		{
+		  /* We already looked up the memory region above and it
+		     was missing.  */
+		  gomp_mutex_unlock (&devicep->lock);
 #ifdef HAVE_INTTYPES_H
-		gomp_fatal ("present clause: not present on the device "
-			    "(addr: %p, size: %"PRIu64" (0x%"PRIx64"), "
-			    "dev: %d)", (void *) hostaddrs[i],
-			    (uint64_t) sizes[i], (uint64_t) sizes[i],
-			    devicep->target_id);
+		  gomp_fatal ("present clause: not present on the device "
+			      "(addr: %p, size: %"PRIu64" (0x%"PRIx64"), "
+			      "dev: %d)", (void *) hostaddrs[i],
+			      (uint64_t) sizes[i], (uint64_t) sizes[i],
+			      devicep->target_id);
 #else
-		gomp_fatal ("present clause: not present on the device "
-			    "(addr: %p, size: %lu (0x%lx), dev: %d)",
-			    (void *) hostaddrs[i], (unsigned long) sizes[i],
-			    (unsigned long) sizes[i], devicep->target_id);
+		  gomp_fatal ("present clause: not present on the device "
+			      "(addr: %p, size: %lu (0x%lx), dev: %d)",
+			      (void *) hostaddrs[i], (unsigned long) sizes[i],
+			      (unsigned long) sizes[i], devicep->target_id);
 #endif
-	      }
-	  }
-      }
+		}
+	    }
+	}
+    }
   gomp_mutex_unlock (&devicep->lock);
 }
 
@@ -5641,6 +5687,7 @@ omp_target_memcpy_async (void *dst, const void *src, size_t length,
 static int
 omp_target_memcpy_rect_worker (void *dst, const void *src, size_t element_size,
 			       int num_dims, const size_t *volume,
+			       const size_t *strides,
 			       const size_t *dst_offsets,
 			       const size_t *src_offsets,
 			       const size_t *dst_dimensions,
@@ -5653,7 +5700,7 @@ omp_target_memcpy_rect_worker (void *dst, const void *src, size_t element_size,
   size_t j, dst_off, src_off, length;
   int i, ret;
 
-  if (num_dims == 1)
+  if (num_dims == 1 && (!strides || strides[0] == 1))
     {
       if (__builtin_mul_overflow (element_size, volume[0], &length)
 	  || __builtin_mul_overflow (element_size, dst_offsets[0], &dst_off)
@@ -5726,6 +5773,38 @@ omp_target_memcpy_rect_worker (void *dst, const void *src, size_t element_size,
 	ret = 0;
       return ret ? 0 : EINVAL;
     }
+  else if (num_dims == 1 && strides)
+    {
+      size_t stride;
+
+      assert ((src_devicep == NULL || dst_devicep == NULL)
+	      && (src_devicep != NULL || dst_devicep != NULL));
+
+      if (__builtin_mul_overflow (element_size, dst_offsets[0], &dst_off)
+	  || __builtin_mul_overflow (element_size, src_offsets[0], &src_off))
+	return EINVAL;
+
+      if (strides
+	  && __builtin_mul_overflow (element_size, strides[0], &stride))
+	return EINVAL;
+
+      for (i = 0, ret = 1; i < volume[0] && ret; i++)
+	{
+	  if (src_devicep == NULL)
+	    ret = dst_devicep->host2dev_func (dst_devicep->target_id,
+					      (char *) dst + dst_off,
+					      (const char *) src + src_off,
+					      element_size);
+	  else if (dst_devicep == NULL)
+	    ret = src_devicep->dev2host_func (src_devicep->target_id,
+					      (char *) dst + dst_off,
+					      (const char *) src + src_off,
+					      element_size);
+	  dst_off += stride;
+	  src_off += stride;
+	}
+      return ret ? 0 : EINVAL;
+    }
 
   /* FIXME: it would be nice to have some plugin function to handle
      num_dims == 2 and num_dims == 3 more efficiently.  Larger ones can
@@ -5739,13 +5818,19 @@ omp_target_memcpy_rect_worker (void *dst, const void *src, size_t element_size,
   if (__builtin_mul_overflow (dst_slice, dst_offsets[0], &dst_off)
       || __builtin_mul_overflow (src_slice, src_offsets[0], &src_off))
     return EINVAL;
+  if (strides
+      && (__builtin_mul_overflow (dst_slice, strides[0], &dst_slice)
+	  || __builtin_mul_overflow (src_slice, strides[0], &src_slice)))
+    return EINVAL;
   for (j = 0; j < volume[0]; j++)
     {
       ret = omp_target_memcpy_rect_worker ((char *) dst + dst_off,
 					   (const char *) src + src_off,
 					   element_size, num_dims - 1,
-					   volume + 1, dst_offsets + 1,
-					   src_offsets + 1, dst_dimensions + 1,
+					   volume + 1,
+					   strides ? strides + 1 : NULL,
+					   dst_offsets + 1, src_offsets + 1,
+					   dst_dimensions + 1,
 					   src_dimensions + 1, dst_devicep,
 					   src_devicep);
       if (ret)
@@ -5791,9 +5876,10 @@ omp_target_memcpy_rect_copy (void *dst, const void *src,
   else if (dst_devicep)
     gomp_mutex_lock (&dst_devicep->lock);
   int ret = omp_target_memcpy_rect_worker (dst, src, element_size, num_dims,
-					   volume, dst_offsets, src_offsets,
-					   dst_dimensions, src_dimensions,
-					   dst_devicep, src_devicep);
+					   volume, NULL, dst_offsets,
+					   src_offsets, dst_dimensions,
+					   src_dimensions, dst_devicep,
+					   src_devicep);
   if (src_devicep)
     gomp_mutex_unlock (&src_devicep->lock);
   else if (dst_devicep)
diff --git a/libgomp/testsuite/libgomp.c++/array-shaping-1.C b/libgomp/testsuite/libgomp.c++/array-shaping-1.C
new file mode 100644
index 00000000000..6ff5f9475f6
--- /dev/null
+++ b/libgomp/testsuite/libgomp.c++/array-shaping-1.C
@@ -0,0 +1,469 @@
+// { dg-do run { target offload_device_nonshared_as } }
+
+#include <string.h>
+#include <assert.h>
+
+volatile int yy = 4, zz = 2, str_str = 2;
+
+template<typename T>
+void foo()
+{
+  T *arr;
+  int x = 5;
+  T arr2d[10][10];
+
+  arr = new T[100];
+
+  /* Update whole reshaped array.  */
+
+  memset (arr, 0, 100 * sizeof (T));
+
+#pragma omp target enter data map(to: arr[:100])
+
+  for (int j = 0; j < x; j++)
+    for (int i = 0; i < 10; i++)
+      arr[j * 10 + i] = i ^ j;
+
+#pragma omp target update to(([10][x]) arr)
+
+#pragma omp target exit data map(from: arr[:100])
+
+  for (int j = 0; j < 10; j++)
+    for (int i = 0; i < 10; i++)
+      if (j < x)
+	assert (arr[j * 10 + i] == i ^ j);
+      else
+	assert (arr[j * 10 + i] == 0);
+
+
+  /* Strided update.  */
+
+  memset (arr, 0, 100 * sizeof (T));
+
+#pragma omp target enter data map(to: arr[:100])
+
+  for (int j = 0; j < 20; j++)
+    for (int i = 0; i < 5; i++)
+      arr[j * 5 + i] = i + j;
+
+#pragma omp target update to(([5][5]) arr[0:3][0:3:2])
+
+#pragma omp target exit data map(from: arr[:100])
+
+  for (int j = 0; j < 20; j++)
+    for (int i = 0; i < 5; i++)
+      if (j < 3 && (i & 1) == 0 && i < 6)
+	assert (arr[j * 5 + i] == i + j);
+      else
+	assert (arr[j * 5 + i] == 0);
+
+
+  /* Reshaped update, contiguous.  */
+
+  memset (arr, 0, 100 * sizeof (T));
+
+#pragma omp target enter data map(to: arr[:100])
+
+  for (int j = 0; j < 20; j++)
+    for (int i = 0; i < 5; i++)
+      arr[j * 5 + i] = 2 * j + i;
+
+#pragma omp target update to(([5][5]) arr[0:5][0:5])
+
+#pragma omp target exit data map(from: arr[:100])
+
+  for (int j = 0; j < 20; j++)
+    for (int i = 0; i < 5; i++)
+      if (j < 5 && i < 5)
+	assert (arr[j * 5 + i] == 2 * j + i);
+      else
+	assert (arr[j * 5 + i] == 0);
+
+
+  /* Strided update on actual array.  */
+
+  memset (arr2d, 0, 100 * sizeof (T));
+
+#pragma omp target enter data map(to: arr2d)
+
+  for (int j = 0; j < 10; j++)
+    for (int i = 0; i < 10; i++)
+      arr2d[j][i] = j + 2 * i;
+
+#pragma omp target update to(arr2d[0:5:2][5:2])
+
+#pragma omp target exit data map(from: arr2d)
+
+  for (int j = 0; j < 10; j++)
+    for (int i = 0; i < 10; i++)
+      if ((j & 1) == 0 && i >= 5 && i < 7)
+	assert (arr2d[j][i] == j + 2 * i);
+      else
+	assert (arr2d[j][i] == 0);
+
+
+  /* Update with non-constant bounds.  */
+
+  memset (arr, 0, 100 * sizeof (T));
+
+#pragma omp target enter data map(to: arr[:100])
+
+  for (int j = 0; j < 10; j++)
+    for (int i = 0; i < 10; i++)
+      arr[j * 10 + i] = (2 * j) ^ i;
+
+  x = 3;
+  int y = yy, z = zz, str = str_str;
+  /* This is actually [0:3:2] [4:2:2].  */
+#pragma omp target update to(([10][10]) arr[0:x:2][y:z:str])
+
+#pragma omp target exit data map(from: arr[:100])
+
+  for (int j = 0; j < 10; j++)
+    for (int i = 0; i < 10; i++)
+      if ((j & 1) == 0 && j < 6 && (i & 1) == 0 && i >= 4 && i < 8)
+	assert (arr[j * 10 + i] == (2 * j) ^ i);
+      else
+	assert (arr[j * 10 + i] == 0);
+
+
+  /* Update with full "major" dimension.  */
+
+  memset (arr, 0, 100 * sizeof (T));
+
+#pragma omp target enter data map(to: arr[:100])
+
+  for (int j = 0; j < 10; j++)
+    for (int i = 0; i < 10; i++)
+      arr[j * 10 + i] = i + j;
+
+#pragma omp target update to(([10][10]) arr[0:10][3:1])
+
+#pragma omp target exit data map(from: arr[:100])
+
+  for (int j = 0; j < 10; j++)
+    for (int i = 0; i < 10; i++)
+      if (i == 3)
+	assert (arr[j * 10 + i] == i + j);
+      else
+	assert (arr[j * 10 + i] == 0);
+
+
+  /* Update with full "minor" dimension.  */
+
+  memset (arr, 0, 100 * sizeof (T));
+
+#pragma omp target enter data map(to: arr[:100])
+
+  for (int j = 0; j < 10; j++)
+    for (int i = 0; i < 10; i++)
+      arr[j * 10 + i] = 3 * (i + j);
+
+#pragma omp target update to(([10][10]) arr[3:2][0:10])
+
+#pragma omp target exit data map(from: arr[:100])
+
+  for (int j = 0; j < 10; j++)
+    for (int i = 0; i < 10; i++)
+      if (j >= 3 && j < 5)
+	assert (arr[j * 10 + i] == 3 * (i + j));
+      else
+	assert (arr[j * 10 + i] == 0);
+
+
+  /* Rectangle update.  */
+
+  memset (arr, 0, 100 * sizeof (T));
+
+#pragma omp target enter data map(to: arr[:100])
+
+  for (int j = 0; j < 10; j++)
+    for (int i = 0; i < 10; i++)
+      arr[j * 10 + i] = 5 * (i + j);
+
+#pragma omp target update to(([10][10]) arr[3:2][0:9])
+
+#pragma omp target exit data map(from: arr[:100])
+
+  for (int j = 0; j < 10; j++)
+    for (int i = 0; i < 10; i++)
+      if (j >= 3 && j < 5 && i < 9)
+	assert (arr[j * 10 + i] == 5 * (i + j));
+      else
+	assert (arr[j * 10 + i] == 0);
+
+
+  /* One-dimensional strided update.  */
+
+  memset (arr, 0, 100 * sizeof (T));
+
+#pragma omp target enter data map(to: arr[:100])
+
+  for (int i = 0; i < 100; i++)
+    arr[i] = i + 99;
+
+#pragma omp target update to(([100]) arr[3:33:3])
+
+#pragma omp target exit data map(from: arr[:100])
+
+  for (int i = 0; i < 100; i++)
+    if (i >= 3 && ((i - 3) % 3) == 0)
+      assert (arr[i] == i + 99);
+    else
+      assert (arr[i] == 0);
+
+
+  /* One-dimensional strided update without explicit array shape.  */
+
+  memset (arr, 0, 100 * sizeof (T));
+
+#pragma omp target enter data map(to: arr[:100])
+
+  for (int i = 0; i < 100; i++)
+    arr[i] = i + 121;
+
+#pragma omp target update to(arr[3:33:3])
+
+#pragma omp target exit data map(from: arr[:100])
+
+  for (int i = 0; i < 100; i++)
+    if (i >= 3 && ((i - 3) % 3) == 0)
+      assert (arr[i] == i + 121);
+    else
+      assert (arr[i] == 0);
+
+  delete[] arr;
+}
+
+int main()
+{
+  int *arr;
+  int x = 5;
+  int arr2d[10][10];
+
+  arr = new int[100];
+
+  /* Update whole reshaped array.  */
+
+  memset (arr, 0, 100 * sizeof (int));
+
+#pragma omp target enter data map(to: arr[:100])
+
+  for (int j = 0; j < x; j++)
+    for (int i = 0; i < 10; i++)
+      arr[j * 10 + i] = i ^ j;
+
+#pragma omp target update to(([10][x]) arr)
+
+#pragma omp target exit data map(from: arr[:100])
+
+  for (int j = 0; j < 10; j++)
+    for (int i = 0; i < 10; i++)
+      if (j < x)
+	assert (arr[j * 10 + i] == i ^ j);
+      else
+	assert (arr[j * 10 + i] == 0);
+
+
+  /* Strided update.  */
+
+  memset (arr, 0, 100 * sizeof (int));
+
+#pragma omp target enter data map(to: arr[:100])
+
+  for (int j = 0; j < 20; j++)
+    for (int i = 0; i < 5; i++)
+      arr[j * 5 + i] = i + j;
+
+#pragma omp target update to(([5][5]) arr[0:3][0:3:2])
+
+#pragma omp target exit data map(from: arr[:100])
+
+  for (int j = 0; j < 20; j++)
+    for (int i = 0; i < 5; i++)
+      if (j < 3 && (i & 1) == 0 && i < 6)
+	assert (arr[j * 5 + i] == i + j);
+      else
+	assert (arr[j * 5 + i] == 0);
+
+
+  /* Reshaped update, contiguous.  */
+
+  memset (arr, 0, 100 * sizeof (int));
+
+#pragma omp target enter data map(to: arr[:100])
+
+  for (int j = 0; j < 20; j++)
+    for (int i = 0; i < 5; i++)
+      arr[j * 5 + i] = 2 * j + i;
+
+#pragma omp target update to(([5][5]) arr[0:5][0:5])
+
+#pragma omp target exit data map(from: arr[:100])
+
+  for (int j = 0; j < 20; j++)
+    for (int i = 0; i < 5; i++)
+      if (j < 5 && i < 5)
+	assert (arr[j * 5 + i] == 2 * j + i);
+      else
+	assert (arr[j * 5 + i] == 0);
+
+
+  /* Strided update on actual array.  */
+
+  memset (arr2d, 0, 100 * sizeof (int));
+
+#pragma omp target enter data map(to: arr2d)
+
+  for (int j = 0; j < 10; j++)
+    for (int i = 0; i < 10; i++)
+      arr2d[j][i] = j + 2 * i;
+
+#pragma omp target update to(arr2d[0:5:2][5:2])
+
+#pragma omp target exit data map(from: arr2d)
+
+  for (int j = 0; j < 10; j++)
+    for (int i = 0; i < 10; i++)
+      if ((j & 1) == 0 && i >= 5 && i < 7)
+	assert (arr2d[j][i] == j + 2 * i);
+      else
+	assert (arr2d[j][i] == 0);
+
+
+  /* Update with non-constant bounds.  */
+
+  memset (arr, 0, 100 * sizeof (int));
+
+#pragma omp target enter data map(to: arr[:100])
+
+  for (int j = 0; j < 10; j++)
+    for (int i = 0; i < 10; i++)
+      arr[j * 10 + i] = (2 * j) ^ i;
+
+  x = 3;
+  int y = yy, z = zz, str = str_str;
+  /* This is actually [0:3:2] [4:2:2].  */
+#pragma omp target update to(([10][10]) arr[0:x:2][y:z:str])
+
+#pragma omp target exit data map(from: arr[:100])
+
+  for (int j = 0; j < 10; j++)
+    for (int i = 0; i < 10; i++)
+      if ((j & 1) == 0 && j < 6 && (i & 1) == 0 && i >= 4 && i < 8)
+	assert (arr[j * 10 + i] == (2 * j) ^ i);
+      else
+	assert (arr[j * 10 + i] == 0);
+
+
+  /* Update with full "major" dimension.  */
+
+  memset (arr, 0, 100 * sizeof (int));
+
+#pragma omp target enter data map(to: arr[:100])
+
+  for (int j = 0; j < 10; j++)
+    for (int i = 0; i < 10; i++)
+      arr[j * 10 + i] = i + j;
+
+#pragma omp target update to(([10][10]) arr[0:10][3:1])
+
+#pragma omp target exit data map(from: arr[:100])
+
+  for (int j = 0; j < 10; j++)
+    for (int i = 0; i < 10; i++)
+      if (i == 3)
+	assert (arr[j * 10 + i] == i + j);
+      else
+	assert (arr[j * 10 + i] == 0);
+
+
+  /* Update with full "minor" dimension.  */
+
+  memset (arr, 0, 100 * sizeof (int));
+
+#pragma omp target enter data map(to: arr[:100])
+
+  for (int j = 0; j < 10; j++)
+    for (int i = 0; i < 10; i++)
+      arr[j * 10 + i] = 3 * (i + j);
+
+#pragma omp target update to(([10][10]) arr[3:2][0:10])
+
+#pragma omp target exit data map(from: arr[:100])
+
+  for (int j = 0; j < 10; j++)
+    for (int i = 0; i < 10; i++)
+      if (j >= 3 && j < 5)
+	assert (arr[j * 10 + i] == 3 * (i + j));
+      else
+	assert (arr[j * 10 + i] == 0);
+
+
+  /* Rectangle update.  */
+
+  memset (arr, 0, 100 * sizeof (int));
+
+#pragma omp target enter data map(to: arr[:100])
+
+  for (int j = 0; j < 10; j++)
+    for (int i = 0; i < 10; i++)
+      arr[j * 10 + i] = 5 * (i + j);
+
+#pragma omp target update to(([10][10]) arr[3:2][0:9])
+
+#pragma omp target exit data map(from: arr[:100])
+
+  for (int j = 0; j < 10; j++)
+    for (int i = 0; i < 10; i++)
+      if (j >= 3 && j < 5 && i < 9)
+	assert (arr[j * 10 + i] == 5 * (i + j));
+      else
+	assert (arr[j * 10 + i] == 0);
+
+
+  /* One-dimensional strided update.  */
+
+  memset (arr, 0, 100 * sizeof (int));
+
+#pragma omp target enter data map(to: arr[:100])
+
+  for (int i = 0; i < 100; i++)
+    arr[i] = i + 99;
+
+#pragma omp target update to(([100]) arr[3:33:3])
+
+#pragma omp target exit data map(from: arr[:100])
+
+  for (int i = 0; i < 100; i++)
+    if (i >= 3 && ((i - 3) % 3) == 0)
+      assert (arr[i] == i + 99);
+    else
+      assert (arr[i] == 0);
+
+
+  /* One-dimensional strided update without explicit array shape.  */
+
+  memset (arr, 0, 100 * sizeof (int));
+
+#pragma omp target enter data map(to: arr[:100])
+
+  for (int i = 0; i < 100; i++)
+    arr[i] = i + 121;
+
+#pragma omp target update to(arr[3:33:3])
+
+#pragma omp target exit data map(from: arr[:100])
+
+  for (int i = 0; i < 100; i++)
+    if (i >= 3 && ((i - 3) % 3) == 0)
+      assert (arr[i] == i + 121);
+    else
+      assert (arr[i] == 0);
+
+  delete[] arr;
+
+  foo<long> ();
+
+  return 0;
+}
diff --git a/libgomp/testsuite/libgomp.c++/array-shaping-10.C b/libgomp/testsuite/libgomp.c++/array-shaping-10.C
new file mode 100644
index 00000000000..648f02d3479
--- /dev/null
+++ b/libgomp/testsuite/libgomp.c++/array-shaping-10.C
@@ -0,0 +1,61 @@
+// { dg-do run { target offload_device_nonshared_as } }
+
+#include <assert.h>
+#include <string.h>
+
+#define N 10
+
+template<typename T>
+void foo ()
+{
+  T tarr[N * N];
+
+  memset (tarr, 0, N * N * sizeof (T));
+
+#pragma omp target enter data map(to: tarr)
+
+#pragma omp target
+  {
+    for (int i = 0; i < N; i++)
+      for (int j = 0; j < N; j++)
+	tarr[i * N + j] = 2 * (i + j);
+  }
+
+  /* An array, but cast to a pointer, then reshaped.  */
+#pragma omp target update from(([N][N]) ((T *) &tarr[0])[4:3][5:3])
+
+  for (int i = 4; i < 7; i++)
+    for (int j = 5; j < 8; j++)
+      assert (tarr[i * N + j] == 2 * (i + j));
+
+#pragma omp target exit data map(delete: tarr)
+}
+
+int main ()
+{
+  int iarr[N * N];
+
+  memset (iarr, 0, N * N * sizeof (int));
+
+#pragma omp target enter data map(to: iarr)
+
+#pragma omp target
+  {
+    for (int i = 0; i < 10; i++)
+      for (int j = 0; j < 10; j++)
+	iarr[i * 10 + j] = i + j;
+  }
+
+  /* An array, but cast to a pointer, then reshaped.  */
+#pragma omp target update from(([10][10]) ((int *) &iarr[0])[4:3][4:3])
+
+  for (int i = 4; i < 7; i++)
+    for (int j = 4; j < 7; j++)
+      assert (iarr[i * 10 + j] == i + j);
+
+#pragma omp target exit data map(delete: iarr)
+
+  foo<unsigned short> ();
+
+  return 0;
+}
diff --git a/libgomp/testsuite/libgomp.c++/array-shaping-11.C b/libgomp/testsuite/libgomp.c++/array-shaping-11.C
new file mode 100644
index 00000000000..6b15bd62fb1
--- /dev/null
+++ b/libgomp/testsuite/libgomp.c++/array-shaping-11.C
@@ -0,0 +1,63 @@
+// { dg-do run { target offload_device_nonshared_as } }
+
+#include <assert.h>
+#include <string.h>
+
+#define N 10
+
+template<typename T>
+void foo ()
+{
+  T tarr_real[N * N];
+  T (&tarr)[N * N] = tarr_real;
+
+  memset (tarr, 0, N * N * sizeof (T));
+
+#pragma omp target enter data map(to: tarr)
+
+#pragma omp target
+  {
+    for (int i = 0; i < N; i++)
+      for (int j = 0; j < N; j++)
+	tarr[i * N + j] = 2 * (i + j);
+  }
+
+  /* A ref to an array, but cast to a pointer, then reshaped.  */
+#pragma omp target update from(([N][N]) ((T *) &tarr[0])[4:3][5:3])
+
+  for (int i = 4; i < 7; i++)
+    for (int j = 5; j < 8; j++)
+      assert (tarr[i * N + j] == 2 * (i + j));
+
+#pragma omp target exit data map(delete: tarr)
+}
+
+int main ()
+{
+  int iarr_real[N * N];
+  int (&iarr)[N * N] = iarr_real;
+
+  memset (iarr, 0, N * N * sizeof (int));
+
+#pragma omp target enter data map(to: iarr)
+
+#pragma omp target
+  {
+    for (int i = 0; i < 10; i++)
+      for (int j = 0; j < 10; j++)
+	iarr[i * 10 + j] = i + j;
+  }
+
+  /* A ref to an array, but cast to a pointer, then reshaped.  */
+#pragma omp target update from(([10][10]) ((int *) &iarr[0])[4:3][4:3])
+
+  for (int i = 4; i < 7; i++)
+    for (int j = 4; j < 7; j++)
+      assert (iarr[i * 10 + j] == i + j);
+
+#pragma omp target exit data map(delete: iarr)
+
+  foo<unsigned short> ();
+
+  return 0;
+}
diff --git a/libgomp/testsuite/libgomp.c++/array-shaping-12.C b/libgomp/testsuite/libgomp.c++/array-shaping-12.C
new file mode 100644
index 00000000000..103c99aa847
--- /dev/null
+++ b/libgomp/testsuite/libgomp.c++/array-shaping-12.C
@@ -0,0 +1,65 @@
+// { dg-do run { target offload_device_nonshared_as } }
+
+#include <assert.h>
+#include <string.h>
+
+#define N 10
+
+template<typename T>
+void foo ()
+{
+  T tarr_real[N * N];
+  T *tarrp = &tarr_real[0];
+  T **tarrpp = &tarrp;
+
+  memset (tarrp, 0, N * N * sizeof (T));
+
+#pragma omp target enter data map(to: tarr_real)
+
+#pragma omp target
+  {
+    for (int i = 0; i < N; i++)
+      for (int j = 0; j < N; j++)
+	tarrp[i * N + j] = 2 * (i + j);
+  }
+
+  /* A pointer with an extra indirection.  */
+#pragma omp target update from(([N][N]) (*tarrpp)[4:3][5:3])
+
+  for (int i = 4; i < 7; i++)
+    for (int j = 5; j < 8; j++)
+      assert (tarrp[i * N + j] == 2 * (i + j));
+
+#pragma omp target exit data map(delete: tarr_real)
+}
+
+int main ()
+{
+  int iarr_real[N * N];
+  int *iarrp = &iarr_real[0];
+  int **iarrpp = &iarrp;
+
+  memset (iarrp, 0, N * N * sizeof (int));
+
+#pragma omp target enter data map(to: iarr_real)
+
+#pragma omp target
+  {
+    for (int i = 0; i < 10; i++)
+      for (int j = 0; j < 10; j++)
+	iarrp[i * 10 + j] = i + j;
+  }
+
+  /* A pointer with an extra indirection.  */
+#pragma omp target update from(([10][10]) (*iarrpp)[4:3][4:3])
+
+  for (int i = 4; i < 7; i++)
+    for (int j = 4; j < 7; j++)
+      assert (iarrp[i * 10 + j] == i + j);
+
+#pragma omp target exit data map(delete: iarr_real)
+
+  foo<unsigned short> ();
+
+  return 0;
+}
diff --git a/libgomp/testsuite/libgomp.c++/array-shaping-13.C b/libgomp/testsuite/libgomp.c++/array-shaping-13.C
new file mode 100644
index 00000000000..29345ca4264
--- /dev/null
+++ b/libgomp/testsuite/libgomp.c++/array-shaping-13.C
@@ -0,0 +1,89 @@
+// { dg-do run { target offload_device_nonshared_as } }
+
+#include <assert.h>
+#include <string.h>
+
+#define N 10
+
+template<typename T>
+void foo ()
+{
+  T *tptr = new T[N * N * N];
+
+  memset (tptr, 0, N * N * N * sizeof (T));
+
+#pragma omp target enter data map(to: tptr[0:N*N*N])
+
+#pragma omp target
+  {
+    for (int i = 0; i < N; i++)
+      for (int j = 0; j < N; j++)
+	tptr[i * N * N + 4 * N + j] = 2 * (i + j);
+  }
+
+  /* An array ref between two array sections.  */
+#pragma omp target update from(([N][N][N]) tptr[4:3][4][5:3])
+
+  for (int i = 4; i < 7; i++)
+    for (int j = 5; j < 8; j++)
+      assert (tptr[i * N * N + 4 * N + j] == 2 * (i + j));
+
+  memset (tptr, 0, N * N * N * sizeof (T));
+
+  for (int i = 0; i < N; i++)
+    tptr[2 * N * N + i * N + 4] = 4 * i;
+
+  /* Array section between two array refs.  */
+#pragma omp target update to(([N][N][N]) tptr[2][3:6][4])
+
+#pragma omp target exit data map(from: tptr[0:N*N*N])
+
+  for (int i = 3; i < 9; i++)
+    assert (tptr[2 * N * N + i * N + 4] == 4 * i);
+
+#pragma omp target exit data map(delete: tptr[0:N*N*N])
+
+  delete[] tptr;
+}
+
+int main ()
+{
+  int *iptr = new int[N * N * N];
+
+  memset (iptr, 0, N * N * N * sizeof (int));
+
+#pragma omp target enter data map(to: iptr[0:N*N*N])
+
+#pragma omp target
+  {
+    for (int i = 0; i < N; i++)
+      for (int j = 0; j < N; j++)
+	iptr[i * N * N + 4 * N + j] = i + j;
+  }
+
+  /* An array ref between two array sections.  */
+#pragma omp target update from(([N][N][N]) iptr[2:3][4][6:3])
+
+  for (int i = 2; i < 5; i++)
+    for (int j = 6; j < 9; j++)
+      assert (iptr[i * N * N + 4 * N + j] == i + j);
+
+  memset (iptr, 0, N * N * N * sizeof (int));
+
+  for (int i = 0; i < N; i++)
+    iptr[2 * N * N + i * N + 4] = 3 * i;
+
+  /* Array section between two array refs.  */
+#pragma omp target update to(([N][N][N]) iptr[2][3:6][4])
+
+#pragma omp target exit data map(from: iptr[0:N*N*N])
+
+  for (int i = 3; i < 9; i++)
+    assert (iptr[2 * N * N + i * N + 4] == 3 * i);
+
+  delete[] iptr;
+
+  foo<unsigned long> ();
+
+  return 0;
+}
diff --git a/libgomp/testsuite/libgomp.c++/array-shaping-2.C b/libgomp/testsuite/libgomp.c++/array-shaping-2.C
new file mode 100644
index 00000000000..027543e8d29
--- /dev/null
+++ b/libgomp/testsuite/libgomp.c++/array-shaping-2.C
@@ -0,0 +1,38 @@
+// { dg-do run { target offload_device_nonshared_as } }
+
+#include <string.h>
+#include <assert.h>
+
+template<typename T>
+void foo (T *w)
+{
+  memset (w, 0, sizeof (T) * 100);
+
+#pragma omp target enter data map(to: w[:100])
+
+  for (int j = 0; j < 10; j++)
+    for (int i = 0; i < 10; i++)
+      w[j * 10 + i] = i + j;
+
+#pragma omp target update to(([10][10]) w[3:2][1:8])
+
+#pragma omp target exit data map(from: w[:100])
+
+  for (int j = 0; j < 10; j++)
+    for (int i = 0; i < 10; i++)
+      if (j >= 3 && j < 5 && i >= 1 && i < 9)
+	assert (w[j * 10 + i] == i + j);
+      else
+	assert (w[j * 10 + i] == 0);
+}
+
+int main()
+{
+  int *arr = new int[100];
+
+  foo<int> (arr);
+
+  delete[] arr;
+
+  return 0;
+}
diff --git a/libgomp/testsuite/libgomp.c++/array-shaping-3.C b/libgomp/testsuite/libgomp.c++/array-shaping-3.C
new file mode 100644
index 00000000000..09ff04bc114
--- /dev/null
+++ b/libgomp/testsuite/libgomp.c++/array-shaping-3.C
@@ -0,0 +1,38 @@
+// { dg-do run { target offload_device_nonshared_as } }
+
+#include <string.h>
+#include <assert.h>
+
+template<int C, int D>
+void foo (double *w)
+{
+  memset (w, 0, sizeof (double) * 100);
+
+#pragma omp target enter data map(to: w[:100])
+
+  for (int j = 0; j < 10; j++)
+    for (int i = 0; i < 10; i++)
+      w[j * 10 + i] = i * 3 + j * 2;
+
+#pragma omp target update to(([C][D]) w[3:2][1:8])
+
+#pragma omp target exit data map(from: w[:100])
+
+  for (int j = 0; j < 10; j++)
+    for (int i = 0; i < 10; i++)
+      if (j >= 3 && j < 5 && i >= 1 && i < 9)
+	assert (w[j * 10 + i] == i * 3 + j * 2);
+      else
+	assert (w[j * 10 + i] == 0.0f);
+}
+
+int main()
+{
+  double *arr = new double[100];
+
+  foo<10, 10> (arr);
+
+  delete[] arr;
+
+  return 0;
+}
diff --git a/libgomp/testsuite/libgomp.c++/array-shaping-4.C b/libgomp/testsuite/libgomp.c++/array-shaping-4.C
new file mode 100644
index 00000000000..efa115e8be6
--- /dev/null
+++ b/libgomp/testsuite/libgomp.c++/array-shaping-4.C
@@ -0,0 +1,38 @@
+// { dg-do run { target offload_device_nonshared_as } }
+
+#include <string.h>
+#include <assert.h>
+
+template<auto C, auto D>
+void foo (double *w)
+{
+  memset (w, 0, sizeof (double) * 100);
+
+#pragma omp target enter data map(to: w[:100])
+
+  for (int j = 0; j < 10; j++)
+    for (int i = 0; i < 10; i++)
+      w[j * 10 + i] = i * 2 + j * 3;
+
+#pragma omp target update to(([C][D]) w[3:2][1:8])
+
+#pragma omp target exit data map(from: w[:100])
+
+  for (int j = 0; j < 10; j++)
+    for (int i = 0; i < 10; i++)
+      if (j >= 3 && j < 5 && i >= 1 && i < 9)
+	assert (w[j * 10 + i] == i * 2 + j * 3);
+      else
+	assert (w[j * 10 + i] == 0.0f);
+}
+
+int main()
+{
+  double *arr = new double[100];
+
+  foo<10, 10> (arr);
+
+  delete[] arr;
+
+  return 0;
+}
diff --git a/libgomp/testsuite/libgomp.c++/array-shaping-5.C b/libgomp/testsuite/libgomp.c++/array-shaping-5.C
new file mode 100644
index 00000000000..7046a13c106
--- /dev/null
+++ b/libgomp/testsuite/libgomp.c++/array-shaping-5.C
@@ -0,0 +1,38 @@
+// { dg-do run { target offload_device_nonshared_as } }
+
+#include <string.h>
+#include <assert.h>
+
+template<typename T, auto C>
+void foo (T *w, int e, int f, int g)
+{
+  memset (w, 0, sizeof (T) * 100);
+
+#pragma omp target enter data map(to: w[:100])
+
+  for (int j = 0; j < e; j++)
+    for (int i = 0; i < C; i++)
+      w[j * C + i] = i + j;
+
+#pragma omp target update to(([e][C]) w[3:2][f:g])
+
+#pragma omp target exit data map(from: w[:100])
+
+  for (int j = 0; j < e; j++)
+    for (int i = 0; i < C; i++)
+      if (j >= 3 && j < 5 && i >= f && i < f + g)
+	assert (w[j * C + i] == i + j);
+      else
+	assert (w[j * C + i] == 0.0f);
+}
+
+int main()
+{
+  float *arr = new float[100];
+
+  foo<float, 10> (arr, 10, 1, 8);
+
+  delete[] arr;
+
+  return 0;
+}
diff --git a/libgomp/testsuite/libgomp.c++/array-shaping-6.C b/libgomp/testsuite/libgomp.c++/array-shaping-6.C
new file mode 100644
index 00000000000..b960b5e58e1
--- /dev/null
+++ b/libgomp/testsuite/libgomp.c++/array-shaping-6.C
@@ -0,0 +1,54 @@
+// { dg-do run { target offload_device_nonshared_as } }
+
+#include <assert.h>
+#include <string.h>
+
+template<typename T>
+void foo (T *&aref)
+{
+#pragma omp target enter data map(to: aref[:100])
+
+#pragma omp target
+  {
+    for (int i = 0; i < 10; i++)
+      for (int j = 0; j < 10; j++)
+	aref[i * 10 + j] = i + j;
+  }
+
+#pragma omp target update from(([10][10]) aref[2:3:2][7:3])
+
+  for (int i = 2; i < 8; i += 2)
+    for (int j = 7; j < 10; j++)
+      assert (aref[i * 10 + j] == i + j);
+
+#pragma omp target exit data map(delete: aref[:100])
+}
+
+int main()
+{
+  float *arr = new float[100];
+  float *&w = arr;
+
+  memset (arr, 0, 100 * sizeof (float));
+
+#pragma omp target enter data map(to: w[:100])
+
+#pragma omp target
+  {
+    for (int i = 0; i < 10; i++)
+      for (int j = 0; j < 10; j++)
+	w[i * 10 + j] = i + j;
+  }
+
+#pragma omp target update from(([10][10]) w[4:3][4:3])
+
+  for (int i = 4; i < 7; i++)
+    for (int j = 4; j < 7; j++)
+      assert (w[i * 10 + j] == i + j);
+
+#pragma omp target exit data map(delete: w[:100])
+
+  foo<float> (arr);
+
+  delete[] arr;
+}
diff --git a/libgomp/testsuite/libgomp.c++/array-shaping-7.C b/libgomp/testsuite/libgomp.c++/array-shaping-7.C
new file mode 100644
index 00000000000..b6193f8d619
--- /dev/null
+++ b/libgomp/testsuite/libgomp.c++/array-shaping-7.C
@@ -0,0 +1,54 @@
+// { dg-do run { target offload_device_nonshared_as } }
+
+#include <assert.h>
+#include <string.h>
+
+template<typename T>
+void foo (T (&aref)[10][10])
+{
+#pragma omp target enter data map(to: aref)
+
+#pragma omp target
+  {
+    for (int i = 0; i < 10; i++)
+      for (int j = 0; j < 10; j++)
+	aref[i][j] = i + j;
+  }
+
+#pragma omp target update from(aref[2:3:2][7:3])
+
+  for (int i = 2; i < 8; i += 2)
+    for (int j = 7; j < 10; j++)
+      assert (aref[i][j] == i + j);
+
+#pragma omp target exit data map(delete: aref)
+}
+
+int main()
+{
+  float arr2d[10][10];
+  float (&w)[10][10] = arr2d;
+
+  memset (&arr2d, 0, 100 * sizeof (float));
+
+#pragma omp target enter data map(to: w)
+
+#pragma omp target
+  {
+    for (int i = 0; i < 10; i++)
+      for (int j = 0; j < 10; j++)
+	w[i][j] = i + j;
+  }
+
+#pragma omp target update from(w[4:3][4:3])
+
+  for (int i = 4; i < 7; i++)
+    for (int j = 4; j < 7; j++)
+      assert (w[i][j] == i + j);
+
+#pragma omp target exit data map(delete: w)
+
+  foo<float> (arr2d);
+
+  return 0;
+}
diff --git a/libgomp/testsuite/libgomp.c++/array-shaping-8.C b/libgomp/testsuite/libgomp.c++/array-shaping-8.C
new file mode 100644
index 00000000000..a96cf3cffb8
--- /dev/null
+++ b/libgomp/testsuite/libgomp.c++/array-shaping-8.C
@@ -0,0 +1,65 @@
+// { dg-do run { target offload_device_nonshared_as } }
+
+#include <assert.h>
+#include <string.h>
+
+template<typename T>
+struct C {
+  T *&aptr;
+
+  C(T *&aptr_1) : aptr(aptr_1)
+  {
+  }
+};
+
+template<typename T>
+void foo (T *c)
+{
+#pragma omp target enter data map(to: c->aptr, c->aptr[:100])
+
+#pragma omp target
+  {
+    for (int i = 0; i < 10; i++)
+      for (int j = 0; j < 10; j++)
+	c->aptr[i * 10 + j] = i + j;
+  }
+
+#pragma omp target update from(([10][10]) c->aptr[2:3:2][7:3])
+
+  for (int i = 2; i < 8; i += 2)
+    for (int j = 7; j < 10; j++)
+      assert (c->aptr[i * 10 + j] == i + j);
+
+#pragma omp target exit data map(delete: c->aptr, c->aptr[:100])
+}
+
+int main()
+{
+  float *arr = new float[100];
+  C<float> cvar(arr);
+
+  memset (arr, 0, 100 * sizeof (float));
+
+#pragma omp target enter data map(to: cvar.aptr, cvar.aptr[:100])
+
+#pragma omp target
+  {
+    for (int i = 0; i < 10; i++)
+      for (int j = 0; j < 10; j++)
+	cvar.aptr[i * 10 + j] = i + j;
+  }
+
+#pragma omp target update from(([10][10]) cvar.aptr[4:3][4:3])
+
+  for (int i = 4; i < 7; i++)
+    for (int j = 4; j < 7; j++)
+      assert (cvar.aptr[i * 10 + j] == i + j);
+
+#pragma omp target exit data map(delete: cvar.aptr, cvar.aptr[:100])
+
+  foo<C<float> > (&cvar);
+
+  delete[] arr;
+
+  return 0;
+}
diff --git a/libgomp/testsuite/libgomp.c++/array-shaping-9.C b/libgomp/testsuite/libgomp.c++/array-shaping-9.C
new file mode 100644
index 00000000000..786fe9d11ed
--- /dev/null
+++ b/libgomp/testsuite/libgomp.c++/array-shaping-9.C
@@ -0,0 +1,95 @@
+// { dg-do run { target offload_device_nonshared_as } }
+
+#include <assert.h>
+#include <string.h>
+
+#define N 10
+
+struct B {
+  int (&aref)[N][N];
+
+  B(int (&aref1)[N][N]) : aref(aref1)
+  {
+  }
+};
+
+template<typename T, int S>
+struct C {
+  T (&aref)[S][S];
+
+  C(T (&aref1)[S][S]) : aref(aref1)
+  {
+  }
+};
+
+template<typename T>
+void foo (T *c)
+{
+#pragma omp target enter data map(to: c->aref)
+
+#pragma omp target
+  {
+    for (int i = 0; i < 10; i++)
+      for (int j = 0; j < 10; j++)
+	c->aref[i][j] = 2 * (i + j);
+  }
+
+#pragma omp target update from(c->aref[2:3:2][7:3])
+
+  for (int i = 2; i < 8; i += 2)
+    for (int j = 7; j < 10; j++)
+      assert (c->aref[i][j] == 2 * (i + j));
+
+#pragma omp target exit data map(delete: c->aref)
+}
+
+int main()
+{
+  int iarr[N][N];
+  float farr[N][N];
+  B bvar(iarr);
+  C<float, N> cvar(farr);
+
+  memset (iarr, 0, N * N * sizeof (int));
+  memset (farr, 0, N * N * sizeof (float));
+
+#pragma omp target enter data map(to: bvar.aref)
+
+#pragma omp target
+  {
+    for (int i = 0; i < 10; i++)
+      for (int j = 0; j < 10; j++)
+	bvar.aref[i][j] = i + j;
+  }
+
+#pragma omp target update from(bvar.aref[4:3][4:3])
+
+  for (int i = 4; i < 7; i++)
+    for (int j = 4; j < 7; j++)
+      assert (bvar.aref[i][j] == i + j);
+
+#pragma omp target exit data map(delete: bvar.aref)
+
+#pragma omp target enter data map(to: cvar.aref)
+
+#pragma omp target
+  {
+    for (int i = 0; i < 10; i++)
+      for (int j = 0; j < 10; j++)
+	cvar.aref[i][j] = i + j;
+  }
+
+#pragma omp target update from(cvar.aref[4:3][4:3])
+
+  for (int i = 4; i < 7; i++)
+    for (int j = 4; j < 7; j++)
+      assert (cvar.aref[i][j] == i + j);
+
+#pragma omp target exit data map(delete: cvar.aref)
+
+  memset (farr, 0, N * N * sizeof (float));
+
+  foo<C<float, N> > (&cvar);
+
+  return 0;
+}
-- 
2.25.1


^ permalink raw reply	[flat|nested] 10+ messages in thread

end of thread, other threads:[~2023-12-19 20:45 UTC | newest]

Thread overview: 10+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2023-09-06  9:34 [PATCH 0/5] OpenMP: Array-shaping operator and strided/rectangular 'target update' support Julian Brown
2023-09-06  9:34 ` [PATCH 1/5] OpenMP, NVPTX: memcpy[23]D bias correction Julian Brown
2023-09-26 22:57   ` Thomas Schwinge
2023-10-02 14:53     ` Julian Brown
2023-12-19 20:45       ` Tobias Burnus
2023-09-06  9:34 ` [PATCH 2/5] OpenMP: Allow complete replacement of clause during map/to/from expansion Julian Brown
2023-09-06  9:34 ` [PATCH 3/5] OpenMP: Support strided and shaped-array updates for C++ Julian Brown
2023-09-06  9:34 ` [PATCH 4/5] OpenMP: Array shaping operator and strided "target update" for C Julian Brown
2023-09-06  9:34 ` [PATCH 5/5] OpenMP: Noncontiguous "target update" for Fortran Julian Brown
  -- strict thread matches above, loose matches on Subject: below --
2023-07-03 21:33 [PATCH 0/5] [og13] OpenMP: strides, rectangular updates and array-shaping operator for "target update" Julian Brown
2023-07-03 21:33 ` [PATCH 3/5] OpenMP: Support strided and shaped-array updates for C++ Julian Brown

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for read-only IMAP folder(s) and NNTP newsgroup(s).