public inbox for fortran@gcc.gnu.org
 help / color / mirror / Atom feed
* [PATCH v7 0/5] OpenMP/OpenACC: map clause and OMP gimplify rework
@ 2023-08-18 22:47 Julian Brown
  2023-08-18 22:47 ` [PATCH v7 1/5] OpenMP/OpenACC: Reindent TO/FROM/_CACHE_ stanza in {c_}finish_omp_clause Julian Brown
                   ` (4 more replies)
  0 siblings, 5 replies; 19+ messages in thread
From: Julian Brown @ 2023-08-18 22:47 UTC (permalink / raw)
  To: gcc-patches; +Cc: fortran, tobias, jakub

This series comprises the first few patches in support of several OpenMP
5.0 features: "lvalue" parsing for map/to/from clauses, "declare mapper"
support and array shaping/strided "target update" support.  These patches
provide the critical infrastructure changes needed to implement those
features.

Though labelled "v7", there are fewer patches here than in the
previously-posted "v6" series for mainline:

  https://gcc.gnu.org/pipermail/gcc-patches/2022-December/609031.html

Rather, this is more similar to the series posted for og13:

  https://gcc.gnu.org/pipermail/gcc-patches/2023-June/622213.html

Though rearranged somewhat and without several of the later
OpenACC-specific patches.

Further comments on individual patches.  Tested with offloading to NVPTX
and bootstrapped.  OK for mainline?

Julian Brown (5):
  OpenMP/OpenACC: Reindent TO/FROM/_CACHE_ stanza in
    {c_}finish_omp_clause
  OpenMP/OpenACC: Rework clause expansion and nested struct handling
  OpenMP: Pointers and member mappings
  OpenMP/OpenACC: Unordered/non-constant component offset runtime
    diagnostic
  OpenMP/OpenACC: Reorganise OMP map clause handling in gimplify.cc

 gcc/c-family/c-common.h                       |   74 +-
 gcc/c-family/c-omp.cc                         |  834 ++++-
 gcc/c/c-parser.cc                             |   17 +-
 gcc/c/c-typeck.cc                             |  745 ++--
 gcc/cp/parser.cc                              |   17 +-
 gcc/cp/pt.cc                                  |    4 +-
 gcc/cp/semantics.cc                           | 1035 +++---
 gcc/fortran/dependency.cc                     |  128 +
 gcc/fortran/dependency.h                      |    1 +
 gcc/fortran/gfortran.h                        |    1 +
 gcc/fortran/trans-openmp.cc                   |  306 +-
 gcc/gimplify.cc                               | 1936 +++++++---
 gcc/omp-general.cc                            |  425 +++
 gcc/omp-general.h                             |   69 +
 gcc/omp-low.cc                                |    8 +-
 gcc/testsuite/c-c++-common/gomp/clauses-2.c   |    2 +-
 gcc/testsuite/c-c++-common/gomp/target-50.c   |    2 +-
 .../c-c++-common/gomp/target-enter-data-1.c   |    3 +-
 .../c-c++-common/gomp/target-implicit-map-2.c |    3 +-
 .../g++.dg/gomp/static-component-1.C          |   23 +
 gcc/testsuite/gcc.dg/gomp/target-3.c          |    2 +-
 gcc/testsuite/gfortran.dg/gomp/map-12.f90     |    2 +-
 gcc/testsuite/gfortran.dg/gomp/map-9.f90      |    2 +-
 .../gfortran.dg/gomp/map-subarray-2.f90       |   57 +
 .../gfortran.dg/gomp/map-subarray.f90         |   40 +
 gcc/tree-pretty-print.cc                      |    3 +
 gcc/tree.h                                    |    8 +
 include/gomp-constants.h                      |    6 +
 libgomp/oacc-mem.c                            |    6 +-
 libgomp/target.c                              |   98 +-
 libgomp/testsuite/libgomp.c++/baseptrs-3.C    |  275 ++
 libgomp/testsuite/libgomp.c++/baseptrs-4.C    | 3154 +++++++++++++++++
 libgomp/testsuite/libgomp.c++/baseptrs-5.C    |   62 +
 libgomp/testsuite/libgomp.c++/class-array-1.C |   59 +
 libgomp/testsuite/libgomp.c++/target-48.C     |   32 +
 libgomp/testsuite/libgomp.c++/target-49.C     |   37 +
 .../libgomp.c++/target-exit-data-reftoptr-1.C |   34 +
 .../testsuite/libgomp.c++/target-lambda-1.C   |    5 +-
 libgomp/testsuite/libgomp.c++/target-this-3.C |   11 +-
 libgomp/testsuite/libgomp.c++/target-this-4.C |   11 +-
 .../libgomp.c-c++-common/baseptrs-1.c         |   50 +
 .../libgomp.c-c++-common/baseptrs-2.c         |   70 +
 .../map-arrayofstruct-1.c                     |   38 +
 .../map-arrayofstruct-2.c                     |   58 +
 .../map-arrayofstruct-3.c                     |   68 +
 .../libgomp.c-c++-common/ptr-attach-2.c       |   60 +
 .../target-implicit-map-2.c                   |    2 +
 .../target-implicit-map-5.c                   |   50 +
 .../libgomp.c-c++-common/target-map-zlas-1.c  |   36 +
 .../libgomp.fortran/map-subarray-2.f90        |  108 +
 .../libgomp.fortran/map-subarray-3.f90        |   62 +
 .../libgomp.fortran/map-subarray-4.f90        |   35 +
 .../libgomp.fortran/map-subarray-5.f90        |   54 +
 .../libgomp.fortran/map-subarray-6.f90        |   26 +
 .../libgomp.fortran/map-subarray-7.f90        |   29 +
 .../libgomp.fortran/map-subarray-8.f90        |   47 +
 .../libgomp.fortran/map-subarray.f90          |   33 +
 .../libgomp.fortran/map-subcomponents.f90     |   32 +
 .../libgomp.fortran/struct-elem-map-1.f90     |  180 +
 59 files changed, 9039 insertions(+), 1536 deletions(-)
 create mode 100644 gcc/testsuite/g++.dg/gomp/static-component-1.C
 create mode 100644 gcc/testsuite/gfortran.dg/gomp/map-subarray-2.f90
 create mode 100644 gcc/testsuite/gfortran.dg/gomp/map-subarray.f90
 create mode 100644 libgomp/testsuite/libgomp.c++/baseptrs-3.C
 create mode 100644 libgomp/testsuite/libgomp.c++/baseptrs-4.C
 create mode 100644 libgomp/testsuite/libgomp.c++/baseptrs-5.C
 create mode 100644 libgomp/testsuite/libgomp.c++/class-array-1.C
 create mode 100644 libgomp/testsuite/libgomp.c++/target-48.C
 create mode 100644 libgomp/testsuite/libgomp.c++/target-49.C
 create mode 100644 libgomp/testsuite/libgomp.c++/target-exit-data-reftoptr-1.C
 create mode 100644 libgomp/testsuite/libgomp.c-c++-common/baseptrs-1.c
 create mode 100644 libgomp/testsuite/libgomp.c-c++-common/baseptrs-2.c
 create mode 100644 libgomp/testsuite/libgomp.c-c++-common/map-arrayofstruct-1.c
 create mode 100644 libgomp/testsuite/libgomp.c-c++-common/map-arrayofstruct-2.c
 create mode 100644 libgomp/testsuite/libgomp.c-c++-common/map-arrayofstruct-3.c
 create mode 100644 libgomp/testsuite/libgomp.c-c++-common/ptr-attach-2.c
 create mode 100644 libgomp/testsuite/libgomp.c-c++-common/target-implicit-map-5.c
 create mode 100644 libgomp/testsuite/libgomp.c-c++-common/target-map-zlas-1.c
 create mode 100644 libgomp/testsuite/libgomp.fortran/map-subarray-2.f90
 create mode 100644 libgomp/testsuite/libgomp.fortran/map-subarray-3.f90
 create mode 100644 libgomp/testsuite/libgomp.fortran/map-subarray-4.f90
 create mode 100644 libgomp/testsuite/libgomp.fortran/map-subarray-5.f90
 create mode 100644 libgomp/testsuite/libgomp.fortran/map-subarray-6.f90
 create mode 100644 libgomp/testsuite/libgomp.fortran/map-subarray-7.f90
 create mode 100644 libgomp/testsuite/libgomp.fortran/map-subarray-8.f90
 create mode 100644 libgomp/testsuite/libgomp.fortran/map-subarray.f90
 create mode 100644 libgomp/testsuite/libgomp.fortran/map-subcomponents.f90

-- 
2.41.0


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

* [PATCH v7 1/5] OpenMP/OpenACC: Reindent TO/FROM/_CACHE_ stanza in {c_}finish_omp_clause
  2023-08-18 22:47 [PATCH v7 0/5] OpenMP/OpenACC: map clause and OMP gimplify rework Julian Brown
@ 2023-08-18 22:47 ` Julian Brown
  2023-08-18 22:47 ` [PATCH v7 2/5] OpenMP/OpenACC: Rework clause expansion and nested struct handling Julian Brown
                   ` (3 subsequent siblings)
  4 siblings, 0 replies; 19+ messages in thread
From: Julian Brown @ 2023-08-18 22:47 UTC (permalink / raw)
  To: gcc-patches; +Cc: fortran, tobias, jakub

This patch trivially adds braces and reindents the
OMP_CLAUSE_TO/OMP_CLAUSE_FROM/OMP_CLAUSE__CACHE_ stanza in
c_finish_omp_clause and finish_omp_clause, in preparation for the
following patch (to clarify the diff a little).

2023-08-18  Julian Brown  <julian@codesourcery.com>

gcc/c/
	* c-typeck.cc (c_finish_omp_clauses): Add braces and reindent
	OMP_CLAUSE_TO/OMP_CLAUSE_FROM/OMP_CLAUSE__CACHE_ stanza.

gcc/cp/
	* semantics.cc (finish_omp_clause): Add braces and reindent
	OMP_CLAUSE_TO/OMP_CLAUSE_FROM/OMP_CLAUSE__CACHE_ stanza.
---
 gcc/c/c-typeck.cc   | 615 +++++++++++++++++-----------------
 gcc/cp/semantics.cc | 786 ++++++++++++++++++++++----------------------
 2 files changed, 706 insertions(+), 695 deletions(-)

diff --git a/gcc/c/c-typeck.cc b/gcc/c/c-typeck.cc
index 6f2fff51683a..f29dbfe6526e 100644
--- a/gcc/c/c-typeck.cc
+++ b/gcc/c/c-typeck.cc
@@ -15346,321 +15346,326 @@ c_finish_omp_clauses (tree clauses, enum c_omp_region_type ort)
 	case OMP_CLAUSE_TO:
 	case OMP_CLAUSE_FROM:
 	case OMP_CLAUSE__CACHE_:
-	  t = OMP_CLAUSE_DECL (c);
-	  if (TREE_CODE (t) == TREE_LIST)
-	    {
-	      grp_start_p = pc;
-	      grp_sentinel = OMP_CLAUSE_CHAIN (c);
+	  {
+	    t = OMP_CLAUSE_DECL (c);
+	    if (TREE_CODE (t) == TREE_LIST)
+	      {
+		grp_start_p = pc;
+		grp_sentinel = OMP_CLAUSE_CHAIN (c);
 
-	      if (handle_omp_array_sections (c, ort))
-		remove = true;
-	      else
-		{
-		  t = OMP_CLAUSE_DECL (c);
-		  if (!omp_mappable_type (TREE_TYPE (t)))
-		    {
-		      error_at (OMP_CLAUSE_LOCATION (c),
-				"array section does not have mappable type "
-				"in %qs clause",
-				omp_clause_code_name[OMP_CLAUSE_CODE (c)]);
-		      remove = true;
-		    }
-		  else if (TYPE_ATOMIC (TREE_TYPE (t)))
-		    {
-		      error_at (OMP_CLAUSE_LOCATION (c),
-				"%<_Atomic%> %qE in %qs clause", t,
-				omp_clause_code_name[OMP_CLAUSE_CODE (c)]);
-		      remove = true;
-		    }
-		  while (TREE_CODE (t) == ARRAY_REF)
-		    t = TREE_OPERAND (t, 0);
-		  if (TREE_CODE (t) == COMPONENT_REF
-		      && TREE_CODE (TREE_TYPE (t)) == ARRAY_TYPE)
-		    {
-		      do
-			{
-			  t = TREE_OPERAND (t, 0);
-			  if (TREE_CODE (t) == MEM_REF
-			      || INDIRECT_REF_P (t))
-			    {
-			      t = TREE_OPERAND (t, 0);
-			      STRIP_NOPS (t);
-			      if (TREE_CODE (t) == POINTER_PLUS_EXPR)
-				t = TREE_OPERAND (t, 0);
-			    }
-			}
-		      while (TREE_CODE (t) == COMPONENT_REF
-			     || TREE_CODE (t) == ARRAY_REF);
-
-		      if (OMP_CLAUSE_CODE (c) == OMP_CLAUSE_MAP
-			  && OMP_CLAUSE_MAP_IMPLICIT (c)
-			  && (bitmap_bit_p (&map_head, DECL_UID (t))
-			      || bitmap_bit_p (&map_field_head, DECL_UID (t))
-			      || bitmap_bit_p (&map_firstprivate_head,
-					       DECL_UID (t))))
-			{
-			  remove = true;
-			  break;
-			}
-		      if (bitmap_bit_p (&map_field_head, DECL_UID (t)))
-			break;
-		      if (bitmap_bit_p (&map_head, DECL_UID (t)))
-			{
-			  if (OMP_CLAUSE_CODE (c) != OMP_CLAUSE_MAP)
-			    error_at (OMP_CLAUSE_LOCATION (c),
-				      "%qD appears more than once in motion "
-				      "clauses", t);
-			  else if (ort == C_ORT_ACC)
-			    error_at (OMP_CLAUSE_LOCATION (c),
-				      "%qD appears more than once in data "
-				      "clauses", t);
-			  else
-			    error_at (OMP_CLAUSE_LOCATION (c),
-				      "%qD appears more than once in map "
-				      "clauses", t);
-			  remove = true;
-			}
-		      else
-			{
-			  bitmap_set_bit (&map_head, DECL_UID (t));
-			  bitmap_set_bit (&map_field_head, DECL_UID (t));
-			}
-		    }
-		}
-	      if (c_oacc_check_attachments (c))
-		remove = true;
-	      if (OMP_CLAUSE_CODE (c) == OMP_CLAUSE_MAP
-		  && (OMP_CLAUSE_MAP_KIND (c) == GOMP_MAP_ATTACH
-		      || OMP_CLAUSE_MAP_KIND (c) == GOMP_MAP_DETACH))
-		/* In this case, we have a single array element which is a
-		   pointer, and we already set OMP_CLAUSE_SIZE in
-		   handle_omp_array_sections above.  For attach/detach clauses,
-		   reset the OMP_CLAUSE_SIZE (representing a bias) to zero
-		   here.  */
-		OMP_CLAUSE_SIZE (c) = size_zero_node;
-	      break;
-	    }
-	  if (t == error_mark_node)
-	    {
-	      remove = true;
-	      break;
-	    }
-	  /* OpenACC attach / detach clauses must be pointers.  */
-	  if (c_oacc_check_attachments (c))
-	    {
-	      remove = true;
-	      break;
-	    }
-	  if (OMP_CLAUSE_CODE (c) == OMP_CLAUSE_MAP
-	      && (OMP_CLAUSE_MAP_KIND (c) == GOMP_MAP_ATTACH
-		  || OMP_CLAUSE_MAP_KIND (c) == GOMP_MAP_DETACH))
-	    /* For attach/detach clauses, set OMP_CLAUSE_SIZE (representing a
-	       bias) to zero here, so it is not set erroneously to the pointer
-	       size later on in gimplify.cc.  */
-	    OMP_CLAUSE_SIZE (c) = size_zero_node;
-	  while (INDIRECT_REF_P (t)
-		 || TREE_CODE (t) == ARRAY_REF)
-	    {
-	      t = TREE_OPERAND (t, 0);
-	      STRIP_NOPS (t);
-	      if (TREE_CODE (t) == POINTER_PLUS_EXPR)
-		t = TREE_OPERAND (t, 0);
-	    }
-	  while (TREE_CODE (t) == COMPOUND_EXPR)
-	    {
-	      t = TREE_OPERAND (t, 1);
-	      STRIP_NOPS (t);
-	    }
-	  indir_component_ref_p = false;
-	  if (TREE_CODE (t) == COMPONENT_REF
-	      && (TREE_CODE (TREE_OPERAND (t, 0)) == MEM_REF
-		  || INDIRECT_REF_P (TREE_OPERAND (t, 0))
-		  || TREE_CODE (TREE_OPERAND (t, 0)) == ARRAY_REF))
-	    {
-	      t = TREE_OPERAND (TREE_OPERAND (t, 0), 0);
-	      indir_component_ref_p = true;
-	      STRIP_NOPS (t);
-	      if (TREE_CODE (t) == POINTER_PLUS_EXPR)
-		t = TREE_OPERAND (t, 0);
-	    }
-
-	  if (TREE_CODE (t) == COMPONENT_REF
-	      && OMP_CLAUSE_CODE (c) != OMP_CLAUSE__CACHE_)
-	    {
-	      if (DECL_BIT_FIELD (TREE_OPERAND (t, 1)))
-		{
-		  error_at (OMP_CLAUSE_LOCATION (c),
-			    "bit-field %qE in %qs clause",
-			    t, omp_clause_code_name[OMP_CLAUSE_CODE (c)]);
+		if (handle_omp_array_sections (c, ort))
 		  remove = true;
-		}
-	      else if (!omp_mappable_type (TREE_TYPE (t)))
-		{
-		  error_at (OMP_CLAUSE_LOCATION (c),
-			    "%qE does not have a mappable type in %qs clause",
-			    t, omp_clause_code_name[OMP_CLAUSE_CODE (c)]);
-		  remove = true;
-		}
-	      else if (TYPE_ATOMIC (TREE_TYPE (t)))
-		{
-		  error_at (OMP_CLAUSE_LOCATION (c),
-			    "%<_Atomic%> %qE in %qs clause", t,
-			    omp_clause_code_name[OMP_CLAUSE_CODE (c)]);
-		  remove = true;
-		}
-	      while (TREE_CODE (t) == COMPONENT_REF)
-		{
-		  if (TREE_CODE (TREE_TYPE (TREE_OPERAND (t, 0)))
-		      == UNION_TYPE)
-		    {
-		      error_at (OMP_CLAUSE_LOCATION (c),
-				"%qE is a member of a union", t);
-		      remove = true;
-		      break;
-		    }
-		  t = TREE_OPERAND (t, 0);
-		  if (TREE_CODE (t) == MEM_REF)
-		    {
-		      if (maybe_ne (mem_ref_offset (t), 0))
+		else
+		  {
+		    t = OMP_CLAUSE_DECL (c);
+		    if (!omp_mappable_type (TREE_TYPE (t)))
+		      {
 			error_at (OMP_CLAUSE_LOCATION (c),
-				  "cannot dereference %qE in %qs clause", t,
+				  "array section does not have mappable type "
+				  "in %qs clause",
 				  omp_clause_code_name[OMP_CLAUSE_CODE (c)]);
-		      else
-			t = TREE_OPERAND (t, 0);
-		    }
-		  while (TREE_CODE (t) == MEM_REF
-			 || INDIRECT_REF_P (t)
-			 || TREE_CODE (t) == ARRAY_REF)
-		    {
+			remove = true;
+		      }
+		    else if (TYPE_ATOMIC (TREE_TYPE (t)))
+		      {
+			error_at (OMP_CLAUSE_LOCATION (c),
+				  "%<_Atomic%> %qE in %qs clause", t,
+				  omp_clause_code_name[OMP_CLAUSE_CODE (c)]);
+			remove = true;
+		      }
+		    while (TREE_CODE (t) == ARRAY_REF)
 		      t = TREE_OPERAND (t, 0);
-		      STRIP_NOPS (t);
-		      if (TREE_CODE (t) == POINTER_PLUS_EXPR)
-			t = TREE_OPERAND (t, 0);
-		    }
-		}
-	      if (remove)
-		break;
-	      if (VAR_P (t) || TREE_CODE (t) == PARM_DECL)
-		{
-		  if (bitmap_bit_p (&map_field_head, DECL_UID (t))
-		      || (ort != C_ORT_ACC
-			  && bitmap_bit_p (&map_head, DECL_UID (t))))
-		    break;
-		}
-	    }
-	  if (!VAR_P (t) && TREE_CODE (t) != PARM_DECL)
-	    {
-	      error_at (OMP_CLAUSE_LOCATION (c),
-			"%qE is not a variable in %qs clause", t,
-			omp_clause_code_name[OMP_CLAUSE_CODE (c)]);
-	      remove = true;
-	    }
-	  else if (VAR_P (t) && DECL_THREAD_LOCAL_P (t))
-	    {
-	      error_at (OMP_CLAUSE_LOCATION (c),
-			"%qD is threadprivate variable in %qs clause", t,
-			omp_clause_code_name[OMP_CLAUSE_CODE (c)]);
-	      remove = true;
-	    }
-	  else if ((OMP_CLAUSE_CODE (c) != OMP_CLAUSE_MAP
-		    || (OMP_CLAUSE_MAP_KIND (c)
-			!= GOMP_MAP_FIRSTPRIVATE_POINTER))
-		   && !indir_component_ref_p
-		   && !c_mark_addressable (t))
-	    remove = true;
-	  else if (!(OMP_CLAUSE_CODE (c) == OMP_CLAUSE_MAP
-		     && (OMP_CLAUSE_MAP_KIND (c) == GOMP_MAP_POINTER
-			 || (OMP_CLAUSE_MAP_KIND (c)
-			     == GOMP_MAP_FIRSTPRIVATE_POINTER)
-			 || (OMP_CLAUSE_MAP_KIND (c)
-			     == GOMP_MAP_FORCE_DEVICEPTR)))
-		   && t == OMP_CLAUSE_DECL (c)
-		   && !omp_mappable_type (TREE_TYPE (t)))
-	    {
-	      error_at (OMP_CLAUSE_LOCATION (c),
-			"%qD does not have a mappable type in %qs clause", t,
-			omp_clause_code_name[OMP_CLAUSE_CODE (c)]);
-	      remove = true;
-	    }
-	  else if (TREE_TYPE (t) == error_mark_node)
-	    remove = true;
-	  else if (TYPE_ATOMIC (strip_array_types (TREE_TYPE (t))))
-	    {
-	      error_at (OMP_CLAUSE_LOCATION (c),
-			"%<_Atomic%> %qE in %qs clause", t,
-			omp_clause_code_name[OMP_CLAUSE_CODE (c)]);
-	      remove = true;
-	    }
-	  else if (OMP_CLAUSE_CODE (c) == OMP_CLAUSE_MAP
-		   && OMP_CLAUSE_MAP_IMPLICIT (c)
-		   && (bitmap_bit_p (&map_head, DECL_UID (t))
-		       || bitmap_bit_p (&map_field_head, DECL_UID (t))
-		       || bitmap_bit_p (&map_firstprivate_head, DECL_UID (t))))
-	    remove = true;
-	  else if (OMP_CLAUSE_CODE (c) == OMP_CLAUSE_MAP
-		   && OMP_CLAUSE_MAP_KIND (c) == GOMP_MAP_FIRSTPRIVATE_POINTER)
-	    {
-	      if (bitmap_bit_p (&generic_head, DECL_UID (t))
-		  || bitmap_bit_p (&firstprivate_head, DECL_UID (t))
-		  || bitmap_bit_p (&map_firstprivate_head, DECL_UID (t)))
-		{
-		  error_at (OMP_CLAUSE_LOCATION (c),
-			    "%qD appears more than once in data clauses", t);
+		    if (TREE_CODE (t) == COMPONENT_REF
+			&& TREE_CODE (TREE_TYPE (t)) == ARRAY_TYPE)
+		      {
+			do
+			  {
+			    t = TREE_OPERAND (t, 0);
+			    if (TREE_CODE (t) == MEM_REF
+			      || INDIRECT_REF_P (t))
+			      {
+				t = TREE_OPERAND (t, 0);
+				STRIP_NOPS (t);
+				if (TREE_CODE (t) == POINTER_PLUS_EXPR)
+				  t = TREE_OPERAND (t, 0);
+			      }
+			  }
+			while (TREE_CODE (t) == COMPONENT_REF
+			       || TREE_CODE (t) == ARRAY_REF);
+
+			if (OMP_CLAUSE_CODE (c) == OMP_CLAUSE_MAP
+			    && OMP_CLAUSE_MAP_IMPLICIT (c)
+			    && (bitmap_bit_p (&map_head, DECL_UID (t))
+				|| bitmap_bit_p (&map_field_head, DECL_UID (t))
+				|| bitmap_bit_p (&map_firstprivate_head,
+						 DECL_UID (t))))
+			  {
+			    remove = true;
+			    break;
+			  }
+			if (bitmap_bit_p (&map_field_head, DECL_UID (t)))
+			  break;
+			if (bitmap_bit_p (&map_head, DECL_UID (t)))
+			  {
+			    if (OMP_CLAUSE_CODE (c) != OMP_CLAUSE_MAP)
+			      error_at (OMP_CLAUSE_LOCATION (c),
+					"%qD appears more than once in motion "
+					"clauses", t);
+			    else if (ort == C_ORT_ACC)
+			      error_at (OMP_CLAUSE_LOCATION (c),
+					"%qD appears more than once in data "
+					"clauses", t);
+			    else
+			      error_at (OMP_CLAUSE_LOCATION (c),
+					"%qD appears more than once in map "
+					"clauses", t);
+			    remove = true;
+			  }
+			else
+			  {
+			    bitmap_set_bit (&map_head, DECL_UID (t));
+			    bitmap_set_bit (&map_field_head, DECL_UID (t));
+			  }
+		      }
+		  }
+		if (c_oacc_check_attachments (c))
 		  remove = true;
-		}
-	      else if (bitmap_bit_p (&map_head, DECL_UID (t))
-		       && !bitmap_bit_p (&map_field_head, DECL_UID (t)))
-		{
-		  if (ort == C_ORT_ACC)
+		if (OMP_CLAUSE_CODE (c) == OMP_CLAUSE_MAP
+		    && (OMP_CLAUSE_MAP_KIND (c) == GOMP_MAP_ATTACH
+			|| OMP_CLAUSE_MAP_KIND (c) == GOMP_MAP_DETACH))
+		  /* In this case, we have a single array element which is a
+		     pointer, and we already set OMP_CLAUSE_SIZE in
+		     handle_omp_array_sections above.  For attach/detach
+		     clauses, reset the OMP_CLAUSE_SIZE (representing a bias)
+		     to zero here.  */
+		  OMP_CLAUSE_SIZE (c) = size_zero_node;
+		break;
+	      }
+	    if (t == error_mark_node)
+	      {
+		remove = true;
+		break;
+	      }
+	    /* OpenACC attach / detach clauses must be pointers.  */
+	    if (c_oacc_check_attachments (c))
+	      {
+		remove = true;
+		break;
+	      }
+	    if (OMP_CLAUSE_CODE (c) == OMP_CLAUSE_MAP
+		&& (OMP_CLAUSE_MAP_KIND (c) == GOMP_MAP_ATTACH
+		    || OMP_CLAUSE_MAP_KIND (c) == GOMP_MAP_DETACH))
+	      /* For attach/detach clauses, set OMP_CLAUSE_SIZE (representing a
+		 bias) to zero here, so it is not set erroneously to the pointer
+		 size later on in gimplify.cc.  */
+	      OMP_CLAUSE_SIZE (c) = size_zero_node;
+	  while (INDIRECT_REF_P (t)
+		   || TREE_CODE (t) == ARRAY_REF)
+	      {
+		t = TREE_OPERAND (t, 0);
+		STRIP_NOPS (t);
+		if (TREE_CODE (t) == POINTER_PLUS_EXPR)
+		  t = TREE_OPERAND (t, 0);
+	      }
+	    while (TREE_CODE (t) == COMPOUND_EXPR)
+	      {
+		t = TREE_OPERAND (t, 1);
+		STRIP_NOPS (t);
+	      }
+	    indir_component_ref_p = false;
+	    if (TREE_CODE (t) == COMPONENT_REF
+		&& (TREE_CODE (TREE_OPERAND (t, 0)) == MEM_REF
+		  || INDIRECT_REF_P (TREE_OPERAND (t, 0))
+		    || TREE_CODE (TREE_OPERAND (t, 0)) == ARRAY_REF))
+	      {
+		t = TREE_OPERAND (TREE_OPERAND (t, 0), 0);
+		indir_component_ref_p = true;
+		STRIP_NOPS (t);
+		if (TREE_CODE (t) == POINTER_PLUS_EXPR)
+		  t = TREE_OPERAND (t, 0);
+	      }
+
+	    if (TREE_CODE (t) == COMPONENT_REF
+		&& OMP_CLAUSE_CODE (c) != OMP_CLAUSE__CACHE_)
+	      {
+		if (DECL_BIT_FIELD (TREE_OPERAND (t, 1)))
+		  {
+		    error_at (OMP_CLAUSE_LOCATION (c),
+			      "bit-field %qE in %qs clause",
+			      t, omp_clause_code_name[OMP_CLAUSE_CODE (c)]);
+		    remove = true;
+		  }
+		else if (!omp_mappable_type (TREE_TYPE (t)))
+		  {
+		    error_at (OMP_CLAUSE_LOCATION (c),
+			      "%qE does not have a mappable type in %qs clause",
+			      t, omp_clause_code_name[OMP_CLAUSE_CODE (c)]);
+		    remove = true;
+		  }
+		else if (TYPE_ATOMIC (TREE_TYPE (t)))
+		  {
+		    error_at (OMP_CLAUSE_LOCATION (c),
+			      "%<_Atomic%> %qE in %qs clause", t,
+			      omp_clause_code_name[OMP_CLAUSE_CODE (c)]);
+		    remove = true;
+		  }
+		while (TREE_CODE (t) == COMPONENT_REF)
+		  {
+		    if (TREE_CODE (TREE_TYPE (TREE_OPERAND (t, 0)))
+			== UNION_TYPE)
+		      {
+			error_at (OMP_CLAUSE_LOCATION (c),
+				  "%qE is a member of a union", t);
+			remove = true;
+			break;
+		      }
+		    t = TREE_OPERAND (t, 0);
+		    if (TREE_CODE (t) == MEM_REF)
+		      {
+			if (maybe_ne (mem_ref_offset (t), 0))
+			  error_at (OMP_CLAUSE_LOCATION (c),
+				    "cannot dereference %qE in %qs clause", t,
+				    omp_clause_code_name[OMP_CLAUSE_CODE (c)]);
+			else
+			  t = TREE_OPERAND (t, 0);
+		      }
+		    while (TREE_CODE (t) == MEM_REF
+			 || INDIRECT_REF_P (t)
+			   || TREE_CODE (t) == ARRAY_REF)
+		      {
+			t = TREE_OPERAND (t, 0);
+			STRIP_NOPS (t);
+			if (TREE_CODE (t) == POINTER_PLUS_EXPR)
+			  t = TREE_OPERAND (t, 0);
+		      }
+		  }
+		if (remove)
+		  break;
+		if (VAR_P (t) || TREE_CODE (t) == PARM_DECL)
+		  {
+		    if (bitmap_bit_p (&map_field_head, DECL_UID (t))
+			|| (ort != C_ORT_ACC
+			    && bitmap_bit_p (&map_head, DECL_UID (t))))
+		      break;
+		  }
+	      }
+	    if (!VAR_P (t) && TREE_CODE (t) != PARM_DECL)
+	      {
+		error_at (OMP_CLAUSE_LOCATION (c),
+			  "%qE is not a variable in %qs clause", t,
+			  omp_clause_code_name[OMP_CLAUSE_CODE (c)]);
+		remove = true;
+	      }
+	    else if (VAR_P (t) && DECL_THREAD_LOCAL_P (t))
+	      {
+		error_at (OMP_CLAUSE_LOCATION (c),
+			  "%qD is threadprivate variable in %qs clause", t,
+			  omp_clause_code_name[OMP_CLAUSE_CODE (c)]);
+		remove = true;
+	      }
+	    else if ((OMP_CLAUSE_CODE (c) != OMP_CLAUSE_MAP
+		      || (OMP_CLAUSE_MAP_KIND (c)
+			  != GOMP_MAP_FIRSTPRIVATE_POINTER))
+		     && !indir_component_ref_p
+		     && !c_mark_addressable (t))
+	      remove = true;
+	    else if (!(OMP_CLAUSE_CODE (c) == OMP_CLAUSE_MAP
+		       && (OMP_CLAUSE_MAP_KIND (c) == GOMP_MAP_POINTER
+			   || (OMP_CLAUSE_MAP_KIND (c)
+			       == GOMP_MAP_FIRSTPRIVATE_POINTER)
+			   || (OMP_CLAUSE_MAP_KIND (c)
+			       == GOMP_MAP_FORCE_DEVICEPTR)))
+		     && t == OMP_CLAUSE_DECL (c)
+		     && !omp_mappable_type (TREE_TYPE (t)))
+	      {
+		error_at (OMP_CLAUSE_LOCATION (c),
+			  "%qD does not have a mappable type in %qs clause", t,
+			  omp_clause_code_name[OMP_CLAUSE_CODE (c)]);
+		remove = true;
+	      }
+	    else if (TREE_TYPE (t) == error_mark_node)
+	      remove = true;
+	    else if (TYPE_ATOMIC (strip_array_types (TREE_TYPE (t))))
+	      {
+		error_at (OMP_CLAUSE_LOCATION (c),
+			  "%<_Atomic%> %qE in %qs clause", t,
+			  omp_clause_code_name[OMP_CLAUSE_CODE (c)]);
+		remove = true;
+	      }
+	    else if (OMP_CLAUSE_CODE (c) == OMP_CLAUSE_MAP
+		     && OMP_CLAUSE_MAP_IMPLICIT (c)
+		     && (bitmap_bit_p (&map_head, DECL_UID (t))
+			 || bitmap_bit_p (&map_field_head, DECL_UID (t))
+			 || bitmap_bit_p (&map_firstprivate_head,
+					  DECL_UID (t))))
+	      remove = true;
+	    else if (OMP_CLAUSE_CODE (c) == OMP_CLAUSE_MAP
+		     && (OMP_CLAUSE_MAP_KIND (c)
+			 == GOMP_MAP_FIRSTPRIVATE_POINTER))
+	      {
+		if (bitmap_bit_p (&generic_head, DECL_UID (t))
+		    || bitmap_bit_p (&firstprivate_head, DECL_UID (t))
+		    || bitmap_bit_p (&map_firstprivate_head, DECL_UID (t)))
+		  {
 		    error_at (OMP_CLAUSE_LOCATION (c),
 			      "%qD appears more than once in data clauses", t);
-		  else
-		    error_at (OMP_CLAUSE_LOCATION (c),
-			      "%qD appears both in data and map clauses", t);
-		  remove = true;
-		}
-	      else
-		bitmap_set_bit (&map_firstprivate_head, DECL_UID (t));
-	    }
-	  else if (bitmap_bit_p (&map_head, DECL_UID (t))
-		   && !bitmap_bit_p (&map_field_head, DECL_UID (t)))
-	    {
-	      if (OMP_CLAUSE_CODE (c) != OMP_CLAUSE_MAP)
-		error_at (OMP_CLAUSE_LOCATION (c),
-			  "%qD appears more than once in motion clauses", t);
-	      else if (ort == C_ORT_ACC)
+		    remove = true;
+		  }
+		else if (bitmap_bit_p (&map_head, DECL_UID (t))
+			 && !bitmap_bit_p (&map_field_head, DECL_UID (t)))
+		  {
+		    if (ort == C_ORT_ACC)
+		      error_at (OMP_CLAUSE_LOCATION (c),
+				"%qD appears more than once in data clauses",
+				t);
+		    else
+		      error_at (OMP_CLAUSE_LOCATION (c),
+				"%qD appears both in data and map clauses", t);
+		    remove = true;
+		  }
+		else
+		  bitmap_set_bit (&map_firstprivate_head, DECL_UID (t));
+	      }
+	    else if (bitmap_bit_p (&map_head, DECL_UID (t))
+		     && !bitmap_bit_p (&map_field_head, DECL_UID (t)))
+	      {
+		if (OMP_CLAUSE_CODE (c) != OMP_CLAUSE_MAP)
+		  error_at (OMP_CLAUSE_LOCATION (c),
+			    "%qD appears more than once in motion clauses", t);
+		else if (ort == C_ORT_ACC)
+		  error_at (OMP_CLAUSE_LOCATION (c),
+			    "%qD appears more than once in data clauses", t);
+		else
+		  error_at (OMP_CLAUSE_LOCATION (c),
+			    "%qD appears more than once in map clauses", t);
+		remove = true;
+	      }
+	    else if (ort == C_ORT_ACC
+		     && bitmap_bit_p (&generic_head, DECL_UID (t)))
+	      {
 		error_at (OMP_CLAUSE_LOCATION (c),
 			  "%qD appears more than once in data clauses", t);
-	      else
-		error_at (OMP_CLAUSE_LOCATION (c),
-			  "%qD appears more than once in map clauses", t);
-	      remove = true;
-	    }
-	  else if (ort == C_ORT_ACC
-		   && bitmap_bit_p (&generic_head, DECL_UID (t)))
-	    {
-	      error_at (OMP_CLAUSE_LOCATION (c),
-			"%qD appears more than once in data clauses", t);
-	      remove = true;
-	    }
-	  else if (bitmap_bit_p (&firstprivate_head, DECL_UID (t))
-		   || bitmap_bit_p (&is_on_device_head, DECL_UID (t)))
-	    {
-	      if (ort == C_ORT_ACC)
-		error_at (OMP_CLAUSE_LOCATION (c),
-			  "%qD appears more than once in data clauses", t);
-	      else
-		error_at (OMP_CLAUSE_LOCATION (c),
-			  "%qD appears both in data and map clauses", t);
-	      remove = true;
-	    }
-	  else
-	    {
-	      bitmap_set_bit (&map_head, DECL_UID (t));
-	      if (t != OMP_CLAUSE_DECL (c)
-		  && TREE_CODE (OMP_CLAUSE_DECL (c)) == COMPONENT_REF)
-		bitmap_set_bit (&map_field_head, DECL_UID (t));
-	    }
+		remove = true;
+	      }
+	    else if (bitmap_bit_p (&firstprivate_head, DECL_UID (t))
+		     || bitmap_bit_p (&is_on_device_head, DECL_UID (t)))
+	      {
+		if (ort == C_ORT_ACC)
+		  error_at (OMP_CLAUSE_LOCATION (c),
+			    "%qD appears more than once in data clauses", t);
+		else
+		  error_at (OMP_CLAUSE_LOCATION (c),
+			    "%qD appears both in data and map clauses", t);
+		remove = true;
+	      }
+	    else
+	      {
+		bitmap_set_bit (&map_head, DECL_UID (t));
+		if (t != OMP_CLAUSE_DECL (c)
+		    && TREE_CODE (OMP_CLAUSE_DECL (c)) == COMPONENT_REF)
+		  bitmap_set_bit (&map_field_head, DECL_UID (t));
+	      }
+	  }
 	  break;
 
 	case OMP_CLAUSE_ENTER:
diff --git a/gcc/cp/semantics.cc b/gcc/cp/semantics.cc
index e810bc41fc8e..c9b031082510 100644
--- a/gcc/cp/semantics.cc
+++ b/gcc/cp/semantics.cc
@@ -8133,408 +8133,414 @@ finish_omp_clauses (tree clauses, enum c_omp_region_type ort)
 	case OMP_CLAUSE_TO:
 	case OMP_CLAUSE_FROM:
 	case OMP_CLAUSE__CACHE_:
-	  t = OMP_CLAUSE_DECL (c);
-	  if (TREE_CODE (t) == TREE_LIST)
-	    {
-	      grp_start_p = pc;
-	      grp_sentinel = OMP_CLAUSE_CHAIN (c);
+	  {
+	    t = OMP_CLAUSE_DECL (c);
+	    if (TREE_CODE (t) == TREE_LIST)
+	      {
+		grp_start_p = pc;
+		grp_sentinel = OMP_CLAUSE_CHAIN (c);
 
-	      if (handle_omp_array_sections (c, ort))
-		remove = true;
-	      else
-		{
-		  t = OMP_CLAUSE_DECL (c);
-		  if (TREE_CODE (t) != TREE_LIST
-		      && !type_dependent_expression_p (t)
-		      && !omp_mappable_type (TREE_TYPE (t)))
-		    {
-		      error_at (OMP_CLAUSE_LOCATION (c),
-				"array section does not have mappable type "
-				"in %qs clause",
-				omp_clause_code_name[OMP_CLAUSE_CODE (c)]);
-		      if (TREE_TYPE (t) != error_mark_node
-			  && !COMPLETE_TYPE_P (TREE_TYPE (t)))
-			cxx_incomplete_type_inform (TREE_TYPE (t));
-		      remove = true;
-		    }
-		  while (TREE_CODE (t) == ARRAY_REF)
-		    t = TREE_OPERAND (t, 0);
-		  if (TREE_CODE (t) == COMPONENT_REF
-		      && TREE_CODE (TREE_TYPE (t)) == ARRAY_TYPE)
-		    {
-		      do
-			{
-			  t = TREE_OPERAND (t, 0);
-			  if (REFERENCE_REF_P (t))
-			    t = TREE_OPERAND (t, 0);
-			  if (TREE_CODE (t) == MEM_REF
-			      || INDIRECT_REF_P (t))
-			    {
-			      t = TREE_OPERAND (t, 0);
-			      STRIP_NOPS (t);
-			      if (TREE_CODE (t) == POINTER_PLUS_EXPR)
-				t = TREE_OPERAND (t, 0);
-			    }
-			}
-		      while (TREE_CODE (t) == COMPONENT_REF
-			     || TREE_CODE (t) == ARRAY_REF);
-
-		      if (OMP_CLAUSE_CODE (c) == OMP_CLAUSE_MAP
-			  && OMP_CLAUSE_MAP_IMPLICIT (c)
-			  && (bitmap_bit_p (&map_head, DECL_UID (t))
-			      || bitmap_bit_p (&map_field_head, DECL_UID (t))
-			      || bitmap_bit_p (&map_firstprivate_head,
-					       DECL_UID (t))))
-			{
-			  remove = true;
-			  break;
-			}
-		      if (bitmap_bit_p (&map_field_head, DECL_UID (t)))
-			break;
-		      if (bitmap_bit_p (&map_head, DECL_UID (t)))
-			{
-			  if (OMP_CLAUSE_CODE (c) != OMP_CLAUSE_MAP)
-			    error_at (OMP_CLAUSE_LOCATION (c),
-				      "%qD appears more than once in motion"
-				      " clauses", t);
-			  else if (ort == C_ORT_ACC)
-			    error_at (OMP_CLAUSE_LOCATION (c),
-				      "%qD appears more than once in data"
-				      " clauses", t);
-			  else
-			    error_at (OMP_CLAUSE_LOCATION (c),
-				      "%qD appears more than once in map"
-				      " clauses", t);
-			  remove = true;
-			}
-		      else
-			{
-			  bitmap_set_bit (&map_head, DECL_UID (t));
-			  bitmap_set_bit (&map_field_head, DECL_UID (t));
-			}
-		    }
-		}
-	      if (cp_oacc_check_attachments (c))
-		remove = true;
-	      if (OMP_CLAUSE_CODE (c) == OMP_CLAUSE_MAP
-		  && (OMP_CLAUSE_MAP_KIND (c) == GOMP_MAP_ATTACH
-		      || OMP_CLAUSE_MAP_KIND (c) == GOMP_MAP_DETACH))
-		/* In this case, we have a single array element which is a
-		   pointer, and we already set OMP_CLAUSE_SIZE in
-		   handle_omp_array_sections above.  For attach/detach clauses,
-		   reset the OMP_CLAUSE_SIZE (representing a bias) to zero
-		   here.  */
-		OMP_CLAUSE_SIZE (c) = size_zero_node;
-	      break;
-	    }
-	  if (t == error_mark_node)
-	    {
-	      remove = true;
-	      break;
-	    }
-	  /* OpenACC attach / detach clauses must be pointers.  */
-	  if (cp_oacc_check_attachments (c))
-	    {
-	      remove = true;
-	      break;
-	    }
-	  if (OMP_CLAUSE_CODE (c) == OMP_CLAUSE_MAP
-	      && (OMP_CLAUSE_MAP_KIND (c) == GOMP_MAP_ATTACH
-		  || OMP_CLAUSE_MAP_KIND (c) == GOMP_MAP_DETACH))
-	    /* For attach/detach clauses, set OMP_CLAUSE_SIZE (representing a
-	       bias) to zero here, so it is not set erroneously to the pointer
-	       size later on in gimplify.cc.  */
-	    OMP_CLAUSE_SIZE (c) = size_zero_node;
-	  if (REFERENCE_REF_P (t)
-	      && TREE_CODE (TREE_OPERAND (t, 0)) == COMPONENT_REF)
-	    {
-	      t = TREE_OPERAND (t, 0);
-	      if (OMP_CLAUSE_CODE (c) == OMP_CLAUSE_MAP
-		  && OMP_CLAUSE_MAP_KIND (c) != GOMP_MAP_ATTACH_DETACH)
-		OMP_CLAUSE_DECL (c) = t;
-	    }
-	  while (INDIRECT_REF_P (t)
-		 || TREE_CODE (t) == ARRAY_REF)
-	    {
-	      t = TREE_OPERAND (t, 0);
-	      STRIP_NOPS (t);
-	      if (TREE_CODE (t) == POINTER_PLUS_EXPR)
-		t = TREE_OPERAND (t, 0);
-	    }
-	  while (TREE_CODE (t) == COMPOUND_EXPR)
-	    {
-	      t = TREE_OPERAND (t, 1);
-	      STRIP_NOPS (t);
-	    }
-	  if (TREE_CODE (t) == COMPONENT_REF
-	      && invalid_nonstatic_memfn_p (EXPR_LOCATION (t), t,
-					    tf_warning_or_error))
-	    remove = true;
-	  indir_component_ref_p = false;
-	  if (TREE_CODE (t) == COMPONENT_REF
-	      && (INDIRECT_REF_P (TREE_OPERAND (t, 0))
-		  || TREE_CODE (TREE_OPERAND (t, 0)) == ARRAY_REF))
-	    {
-	      t = TREE_OPERAND (TREE_OPERAND (t, 0), 0);
-	      indir_component_ref_p = true;
-	      STRIP_NOPS (t);
-	      if (TREE_CODE (t) == POINTER_PLUS_EXPR)
-		t = TREE_OPERAND (t, 0);
-	    }
-	  if (TREE_CODE (t) == COMPONENT_REF
-	      && OMP_CLAUSE_CODE (c) != OMP_CLAUSE__CACHE_)
-	    {
-	      if (type_dependent_expression_p (t))
-		break;
-	      if (TREE_CODE (TREE_OPERAND (t, 1)) == FIELD_DECL
-		  && DECL_BIT_FIELD (TREE_OPERAND (t, 1)))
-		{
-		  error_at (OMP_CLAUSE_LOCATION (c),
-			    "bit-field %qE in %qs clause",
-			    t, omp_clause_code_name[OMP_CLAUSE_CODE (c)]);
+		if (handle_omp_array_sections (c, ort))
 		  remove = true;
-		}
-	      else if (!omp_mappable_type (TREE_TYPE (t)))
-		{
-		  error_at (OMP_CLAUSE_LOCATION (c),
-			    "%qE does not have a mappable type in %qs clause",
-			    t, omp_clause_code_name[OMP_CLAUSE_CODE (c)]);
-		  if (TREE_TYPE (t) != error_mark_node
-		      && !COMPLETE_TYPE_P (TREE_TYPE (t)))
-		    cxx_incomplete_type_inform (TREE_TYPE (t));
-		  remove = true;
-		}
-	      while (TREE_CODE (t) == COMPONENT_REF)
-		{
-		  if (TREE_TYPE (TREE_OPERAND (t, 0))
-		      && (TREE_CODE (TREE_TYPE (TREE_OPERAND (t, 0)))
-			  == UNION_TYPE))
-		    {
-		      error_at (OMP_CLAUSE_LOCATION (c),
-				"%qE is a member of a union", t);
-		      remove = true;
-		      break;
-		    }
-		  t = TREE_OPERAND (t, 0);
-		  if (TREE_CODE (t) == MEM_REF)
-		    {
-		      if (maybe_ne (mem_ref_offset (t), 0))
+		else
+		  {
+		    t = OMP_CLAUSE_DECL (c);
+		    if (TREE_CODE (t) != TREE_LIST
+			&& !type_dependent_expression_p (t)
+			&& !omp_mappable_type (TREE_TYPE (t)))
+		      {
 			error_at (OMP_CLAUSE_LOCATION (c),
-				  "cannot dereference %qE in %qs clause", t,
+				  "array section does not have mappable type "
+				  "in %qs clause",
 				  omp_clause_code_name[OMP_CLAUSE_CODE (c)]);
-		      else
-			t = TREE_OPERAND (t, 0);
-		    }
-		  while (TREE_CODE (t) == MEM_REF
-			 || INDIRECT_REF_P (t)
-			 || TREE_CODE (t) == ARRAY_REF)
-		    {
+			if (TREE_TYPE (t) != error_mark_node
+			    && !COMPLETE_TYPE_P (TREE_TYPE (t)))
+			  cxx_incomplete_type_inform (TREE_TYPE (t));
+			remove = true;
+		      }
+		    while (TREE_CODE (t) == ARRAY_REF)
 		      t = TREE_OPERAND (t, 0);
-		      STRIP_NOPS (t);
-		      if (TREE_CODE (t) == POINTER_PLUS_EXPR)
-			t = TREE_OPERAND (t, 0);
-		    }
-		}
-	      if (remove)
-		break;
-	      if (REFERENCE_REF_P (t))
-		t = TREE_OPERAND (t, 0);
-	      if (VAR_P (t) || TREE_CODE (t) == PARM_DECL)
-		{
-		  if (bitmap_bit_p (&map_field_head, DECL_UID (t))
-		      || (ort != C_ORT_ACC
-			  && bitmap_bit_p (&map_head, DECL_UID (t))))
-		    goto handle_map_references;
-		}
-	    }
-	  if (!processing_template_decl
-	      && TREE_CODE (t) == FIELD_DECL)
-	    {
-	      OMP_CLAUSE_DECL (c) = finish_non_static_data_member (t, NULL_TREE,
-								   NULL_TREE);
-	      break;
-	    }
-	  if (!VAR_P (t) && TREE_CODE (t) != PARM_DECL)
-	    {
-	      if (processing_template_decl && TREE_CODE (t) != OVERLOAD)
-		break;
-	      if (OMP_CLAUSE_CODE (c) == OMP_CLAUSE_MAP
-		  && (OMP_CLAUSE_MAP_KIND (c) == GOMP_MAP_POINTER
-		      || OMP_CLAUSE_MAP_KIND (c) == GOMP_MAP_ALWAYS_POINTER
-		      || OMP_CLAUSE_MAP_KIND (c) == GOMP_MAP_ATTACH_DETACH))
-		break;
-	      if (DECL_P (t))
-		error_at (OMP_CLAUSE_LOCATION (c),
-			  "%qD is not a variable in %qs clause", t,
-			  omp_clause_code_name[OMP_CLAUSE_CODE (c)]);
-	      else
-		error_at (OMP_CLAUSE_LOCATION (c),
-			  "%qE is not a variable in %qs clause", t,
-			  omp_clause_code_name[OMP_CLAUSE_CODE (c)]);
-	      remove = true;
-	    }
-	  else if (VAR_P (t) && CP_DECL_THREAD_LOCAL_P (t))
-	    {
-	      error_at (OMP_CLAUSE_LOCATION (c),
-			"%qD is threadprivate variable in %qs clause", t,
-			omp_clause_code_name[OMP_CLAUSE_CODE (c)]);
-	      remove = true;
-	    }
-	  else if (!processing_template_decl
-		   && !TYPE_REF_P (TREE_TYPE (t))
-		   && (OMP_CLAUSE_CODE (c) != OMP_CLAUSE_MAP
-		       || (OMP_CLAUSE_MAP_KIND (c)
-			   != GOMP_MAP_FIRSTPRIVATE_POINTER))
-		   && !indir_component_ref_p
-		   && !cxx_mark_addressable (t))
-	    remove = true;
-	  else if (!(OMP_CLAUSE_CODE (c) == OMP_CLAUSE_MAP
-		     && (OMP_CLAUSE_MAP_KIND (c) == GOMP_MAP_POINTER
-			 || (OMP_CLAUSE_MAP_KIND (c)
-			     == GOMP_MAP_FIRSTPRIVATE_POINTER)))
-		   && t == OMP_CLAUSE_DECL (c)
-		   && !type_dependent_expression_p (t)
-		   && !omp_mappable_type (TYPE_REF_P (TREE_TYPE (t))
-					  ? TREE_TYPE (TREE_TYPE (t))
-					  : TREE_TYPE (t)))
-	    {
-	      error_at (OMP_CLAUSE_LOCATION (c),
-			"%qD does not have a mappable type in %qs clause", t,
-			omp_clause_code_name[OMP_CLAUSE_CODE (c)]);
-	      if (TREE_TYPE (t) != error_mark_node
-		  && !COMPLETE_TYPE_P (TREE_TYPE (t)))
-		cxx_incomplete_type_inform (TREE_TYPE (t));
-	      remove = true;
-	    }
-	  else if (OMP_CLAUSE_CODE (c) == OMP_CLAUSE_MAP
-		   && OMP_CLAUSE_MAP_KIND (c) == GOMP_MAP_FORCE_DEVICEPTR
-		   && !type_dependent_expression_p (t)
-		   && !INDIRECT_TYPE_P (TREE_TYPE (t)))
-	    {
-	      error_at (OMP_CLAUSE_LOCATION (c),
-			"%qD is not a pointer variable", t);
-	      remove = true;
-	    }
-	  else if (OMP_CLAUSE_CODE (c) == OMP_CLAUSE_MAP
-		   && OMP_CLAUSE_MAP_IMPLICIT (c)
-		   && (bitmap_bit_p (&map_head, DECL_UID (t))
-		       || bitmap_bit_p (&map_field_head, DECL_UID (t))
-		       || bitmap_bit_p (&map_firstprivate_head,
-					DECL_UID (t))))
-	    remove = true;
-	  else if (OMP_CLAUSE_CODE (c) == OMP_CLAUSE_MAP
-		   && OMP_CLAUSE_MAP_KIND (c) == GOMP_MAP_FIRSTPRIVATE_POINTER)
-	    {
-	      if (bitmap_bit_p (&generic_head, DECL_UID (t))
-		  || bitmap_bit_p (&firstprivate_head, DECL_UID (t))
-		  || bitmap_bit_p (&map_firstprivate_head, DECL_UID (t)))
-		{
-		  error_at (OMP_CLAUSE_LOCATION (c),
-			    "%qD appears more than once in data clauses", t);
+		    if (TREE_CODE (t) == COMPONENT_REF
+			&& TREE_CODE (TREE_TYPE (t)) == ARRAY_TYPE)
+		      {
+			do
+			  {
+			    t = TREE_OPERAND (t, 0);
+			    if (REFERENCE_REF_P (t))
+			      t = TREE_OPERAND (t, 0);
+			    if (TREE_CODE (t) == MEM_REF
+				|| INDIRECT_REF_P (t))
+			      {
+				t = TREE_OPERAND (t, 0);
+				STRIP_NOPS (t);
+				if (TREE_CODE (t) == POINTER_PLUS_EXPR)
+				  t = TREE_OPERAND (t, 0);
+			      }
+			  }
+			while (TREE_CODE (t) == COMPONENT_REF
+			       || TREE_CODE (t) == ARRAY_REF);
+
+			if (OMP_CLAUSE_CODE (c) == OMP_CLAUSE_MAP
+			    && OMP_CLAUSE_MAP_IMPLICIT (c)
+			    && (bitmap_bit_p (&map_head, DECL_UID (t))
+				|| bitmap_bit_p (&map_field_head, DECL_UID (t))
+				|| bitmap_bit_p (&map_firstprivate_head,
+						 DECL_UID (t))))
+			  {
+			    remove = true;
+			    break;
+			  }
+			if (bitmap_bit_p (&map_field_head, DECL_UID (t)))
+			  break;
+			if (bitmap_bit_p (&map_head, DECL_UID (t)))
+			  {
+			    if (OMP_CLAUSE_CODE (c) != OMP_CLAUSE_MAP)
+			      error_at (OMP_CLAUSE_LOCATION (c),
+					"%qD appears more than once in motion"
+					" clauses", t);
+			    else if (ort == C_ORT_ACC)
+			      error_at (OMP_CLAUSE_LOCATION (c),
+					"%qD appears more than once in data"
+					" clauses", t);
+			    else
+			      error_at (OMP_CLAUSE_LOCATION (c),
+					"%qD appears more than once in map"
+					" clauses", t);
+			    remove = true;
+			  }
+			else
+			  {
+			    bitmap_set_bit (&map_head, DECL_UID (t));
+			    bitmap_set_bit (&map_field_head, DECL_UID (t));
+			  }
+		      }
+		  }
+		if (cp_oacc_check_attachments (c))
 		  remove = true;
-		}
-	      else if (bitmap_bit_p (&map_head, DECL_UID (t))
-		       && !bitmap_bit_p (&map_field_head, DECL_UID (t)))
-		{
-		  if (ort == C_ORT_ACC)
+		if (OMP_CLAUSE_CODE (c) == OMP_CLAUSE_MAP
+		    && (OMP_CLAUSE_MAP_KIND (c) == GOMP_MAP_ATTACH
+			|| OMP_CLAUSE_MAP_KIND (c) == GOMP_MAP_DETACH))
+		  /* In this case, we have a single array element which is a
+		     pointer, and we already set OMP_CLAUSE_SIZE in
+		     handle_omp_array_sections above.  For attach/detach
+		     clauses, reset the OMP_CLAUSE_SIZE (representing a bias)
+		     to zero here.  */
+		  OMP_CLAUSE_SIZE (c) = size_zero_node;
+		break;
+	      }
+	    if (t == error_mark_node)
+	      {
+		remove = true;
+		break;
+	      }
+	    /* OpenACC attach / detach clauses must be pointers.  */
+	    if (cp_oacc_check_attachments (c))
+	      {
+		remove = true;
+		break;
+	      }
+	    if (OMP_CLAUSE_CODE (c) == OMP_CLAUSE_MAP
+		&& (OMP_CLAUSE_MAP_KIND (c) == GOMP_MAP_ATTACH
+		    || OMP_CLAUSE_MAP_KIND (c) == GOMP_MAP_DETACH))
+	      /* For attach/detach clauses, set OMP_CLAUSE_SIZE (representing a
+		 bias) to zero here, so it is not set erroneously to the
+		 pointer size later on in gimplify.cc.  */
+	      OMP_CLAUSE_SIZE (c) = size_zero_node;
+	    if (REFERENCE_REF_P (t)
+		&& TREE_CODE (TREE_OPERAND (t, 0)) == COMPONENT_REF)
+	      {
+		t = TREE_OPERAND (t, 0);
+		if (OMP_CLAUSE_CODE (c) == OMP_CLAUSE_MAP
+		    && OMP_CLAUSE_MAP_KIND (c) != GOMP_MAP_ATTACH_DETACH)
+		  OMP_CLAUSE_DECL (c) = t;
+	      }
+	    while (INDIRECT_REF_P (t)
+		   || TREE_CODE (t) == ARRAY_REF)
+	      {
+		t = TREE_OPERAND (t, 0);
+		STRIP_NOPS (t);
+		if (TREE_CODE (t) == POINTER_PLUS_EXPR)
+		  t = TREE_OPERAND (t, 0);
+	      }
+	    while (TREE_CODE (t) == COMPOUND_EXPR)
+	      {
+		t = TREE_OPERAND (t, 1);
+		STRIP_NOPS (t);
+	      }
+	    if (TREE_CODE (t) == COMPONENT_REF
+		&& invalid_nonstatic_memfn_p (EXPR_LOCATION (t), t,
+					      tf_warning_or_error))
+	      remove = true;
+	    indir_component_ref_p = false;
+	    if (TREE_CODE (t) == COMPONENT_REF
+		&& (INDIRECT_REF_P (TREE_OPERAND (t, 0))
+		    || TREE_CODE (TREE_OPERAND (t, 0)) == ARRAY_REF))
+	      {
+		t = TREE_OPERAND (TREE_OPERAND (t, 0), 0);
+		indir_component_ref_p = true;
+		STRIP_NOPS (t);
+		if (TREE_CODE (t) == POINTER_PLUS_EXPR)
+		  t = TREE_OPERAND (t, 0);
+	      }
+	    if (TREE_CODE (t) == COMPONENT_REF
+		&& OMP_CLAUSE_CODE (c) != OMP_CLAUSE__CACHE_)
+	      {
+		if (type_dependent_expression_p (t))
+		  break;
+		if (TREE_CODE (TREE_OPERAND (t, 1)) == FIELD_DECL
+		    && DECL_BIT_FIELD (TREE_OPERAND (t, 1)))
+		  {
+		    error_at (OMP_CLAUSE_LOCATION (c),
+			      "bit-field %qE in %qs clause",
+			      t, omp_clause_code_name[OMP_CLAUSE_CODE (c)]);
+		    remove = true;
+		  }
+		else if (!omp_mappable_type (TREE_TYPE (t)))
+		  {
+		    error_at (OMP_CLAUSE_LOCATION (c),
+			      "%qE does not have a mappable type in %qs clause",
+			      t, omp_clause_code_name[OMP_CLAUSE_CODE (c)]);
+		    if (TREE_TYPE (t) != error_mark_node
+			&& !COMPLETE_TYPE_P (TREE_TYPE (t)))
+		      cxx_incomplete_type_inform (TREE_TYPE (t));
+		    remove = true;
+		  }
+		while (TREE_CODE (t) == COMPONENT_REF)
+		  {
+		    if (TREE_TYPE (TREE_OPERAND (t, 0))
+			&& (TREE_CODE (TREE_TYPE (TREE_OPERAND (t, 0)))
+			    == UNION_TYPE))
+		      {
+			error_at (OMP_CLAUSE_LOCATION (c),
+				  "%qE is a member of a union", t);
+			remove = true;
+			break;
+		      }
+		    t = TREE_OPERAND (t, 0);
+		    if (TREE_CODE (t) == MEM_REF)
+		      {
+			if (maybe_ne (mem_ref_offset (t), 0))
+			  error_at (OMP_CLAUSE_LOCATION (c),
+				    "cannot dereference %qE in %qs clause", t,
+				    omp_clause_code_name[OMP_CLAUSE_CODE (c)]);
+			else
+			  t = TREE_OPERAND (t, 0);
+		      }
+		    while (TREE_CODE (t) == MEM_REF
+			   || INDIRECT_REF_P (t)
+			   || TREE_CODE (t) == ARRAY_REF)
+		      {
+			t = TREE_OPERAND (t, 0);
+			STRIP_NOPS (t);
+			if (TREE_CODE (t) == POINTER_PLUS_EXPR)
+			  t = TREE_OPERAND (t, 0);
+		      }
+		  }
+		if (remove)
+		  break;
+		if (REFERENCE_REF_P (t))
+		  t = TREE_OPERAND (t, 0);
+		if (VAR_P (t) || TREE_CODE (t) == PARM_DECL)
+		  {
+		    if (bitmap_bit_p (&map_field_head, DECL_UID (t))
+			|| (ort != C_ORT_ACC
+			    && bitmap_bit_p (&map_head, DECL_UID (t))))
+		      goto handle_map_references;
+		  }
+	      }
+	    if (!processing_template_decl
+		&& TREE_CODE (t) == FIELD_DECL)
+	      {
+		OMP_CLAUSE_DECL (c)
+		  = finish_non_static_data_member (t, NULL_TREE, NULL_TREE);
+		break;
+	      }
+	    if (!VAR_P (t) && TREE_CODE (t) != PARM_DECL)
+	      {
+		if (processing_template_decl && TREE_CODE (t) != OVERLOAD)
+		  break;
+		if (OMP_CLAUSE_CODE (c) == OMP_CLAUSE_MAP
+		    && (OMP_CLAUSE_MAP_KIND (c) == GOMP_MAP_POINTER
+			|| OMP_CLAUSE_MAP_KIND (c) == GOMP_MAP_ALWAYS_POINTER
+			|| OMP_CLAUSE_MAP_KIND (c) == GOMP_MAP_ATTACH_DETACH))
+		  break;
+		if (DECL_P (t))
+		  error_at (OMP_CLAUSE_LOCATION (c),
+			    "%qD is not a variable in %qs clause", t,
+			    omp_clause_code_name[OMP_CLAUSE_CODE (c)]);
+		else
+		  error_at (OMP_CLAUSE_LOCATION (c),
+			    "%qE is not a variable in %qs clause", t,
+			    omp_clause_code_name[OMP_CLAUSE_CODE (c)]);
+		remove = true;
+	      }
+	    else if (VAR_P (t) && CP_DECL_THREAD_LOCAL_P (t))
+	      {
+		error_at (OMP_CLAUSE_LOCATION (c),
+			  "%qD is threadprivate variable in %qs clause", t,
+			  omp_clause_code_name[OMP_CLAUSE_CODE (c)]);
+		remove = true;
+	      }
+	    else if (!processing_template_decl
+		     && !TYPE_REF_P (TREE_TYPE (t))
+		     && (OMP_CLAUSE_CODE (c) != OMP_CLAUSE_MAP
+			 || (OMP_CLAUSE_MAP_KIND (c)
+			     != GOMP_MAP_FIRSTPRIVATE_POINTER))
+		     && !indir_component_ref_p
+		     && !cxx_mark_addressable (t))
+	      remove = true;
+	    else if (!(OMP_CLAUSE_CODE (c) == OMP_CLAUSE_MAP
+		       && (OMP_CLAUSE_MAP_KIND (c) == GOMP_MAP_POINTER
+			   || (OMP_CLAUSE_MAP_KIND (c)
+			       == GOMP_MAP_FIRSTPRIVATE_POINTER)))
+		     && t == OMP_CLAUSE_DECL (c)
+		     && !type_dependent_expression_p (t)
+		     && !omp_mappable_type (TYPE_REF_P (TREE_TYPE (t))
+					    ? TREE_TYPE (TREE_TYPE (t))
+					    : TREE_TYPE (t)))
+	      {
+		error_at (OMP_CLAUSE_LOCATION (c),
+			  "%qD does not have a mappable type in %qs clause", t,
+			  omp_clause_code_name[OMP_CLAUSE_CODE (c)]);
+		if (TREE_TYPE (t) != error_mark_node
+		    && !COMPLETE_TYPE_P (TREE_TYPE (t)))
+		  cxx_incomplete_type_inform (TREE_TYPE (t));
+		remove = true;
+	      }
+	    else if (OMP_CLAUSE_CODE (c) == OMP_CLAUSE_MAP
+		     && OMP_CLAUSE_MAP_KIND (c) == GOMP_MAP_FORCE_DEVICEPTR
+		     && !type_dependent_expression_p (t)
+		     && !INDIRECT_TYPE_P (TREE_TYPE (t)))
+	      {
+		error_at (OMP_CLAUSE_LOCATION (c),
+			  "%qD is not a pointer variable", t);
+		remove = true;
+	      }
+	    else if (OMP_CLAUSE_CODE (c) == OMP_CLAUSE_MAP
+		     && OMP_CLAUSE_MAP_IMPLICIT (c)
+		     && (bitmap_bit_p (&map_head, DECL_UID (t))
+			 || bitmap_bit_p (&map_field_head, DECL_UID (t))
+			 || bitmap_bit_p (&map_firstprivate_head,
+					  DECL_UID (t))))
+	      remove = true;
+	    else if (OMP_CLAUSE_CODE (c) == OMP_CLAUSE_MAP
+		     && (OMP_CLAUSE_MAP_KIND (c)
+			 == GOMP_MAP_FIRSTPRIVATE_POINTER))
+	      {
+		if (bitmap_bit_p (&generic_head, DECL_UID (t))
+		    || bitmap_bit_p (&firstprivate_head, DECL_UID (t))
+		    || bitmap_bit_p (&map_firstprivate_head, DECL_UID (t)))
+		  {
 		    error_at (OMP_CLAUSE_LOCATION (c),
 			      "%qD appears more than once in data clauses", t);
-		  else
-		    error_at (OMP_CLAUSE_LOCATION (c),
-			      "%qD appears both in data and map clauses", t);
-		  remove = true;
-		}
-	      else
-		bitmap_set_bit (&map_firstprivate_head, DECL_UID (t));
-	    }
-	  else if (bitmap_bit_p (&map_head, DECL_UID (t))
-		   && !bitmap_bit_p (&map_field_head, DECL_UID (t)))
-	    {
-	      if (OMP_CLAUSE_CODE (c) != OMP_CLAUSE_MAP)
-		error_at (OMP_CLAUSE_LOCATION (c),
-			  "%qD appears more than once in motion clauses", t);
-	      else if (ort == C_ORT_ACC)
+		    remove = true;
+		  }
+		else if (bitmap_bit_p (&map_head, DECL_UID (t))
+			 && !bitmap_bit_p (&map_field_head, DECL_UID (t)))
+		  {
+		    if (ort == C_ORT_ACC)
+		      error_at (OMP_CLAUSE_LOCATION (c),
+				"%qD appears more than once in data clauses",
+				t);
+		    else
+		      error_at (OMP_CLAUSE_LOCATION (c),
+				"%qD appears both in data and map clauses", t);
+		    remove = true;
+		  }
+		else
+		  bitmap_set_bit (&map_firstprivate_head, DECL_UID (t));
+	      }
+	    else if (bitmap_bit_p (&map_head, DECL_UID (t))
+		     && !bitmap_bit_p (&map_field_head, DECL_UID (t)))
+	      {
+		if (OMP_CLAUSE_CODE (c) != OMP_CLAUSE_MAP)
+		  error_at (OMP_CLAUSE_LOCATION (c),
+			    "%qD appears more than once in motion clauses", t);
+		else if (ort == C_ORT_ACC)
+		  error_at (OMP_CLAUSE_LOCATION (c),
+			    "%qD appears more than once in data clauses", t);
+		else
+		  error_at (OMP_CLAUSE_LOCATION (c),
+			    "%qD appears more than once in map clauses", t);
+		remove = true;
+	      }
+	    else if (ort == C_ORT_ACC
+		     && bitmap_bit_p (&generic_head, DECL_UID (t)))
+	      {
 		error_at (OMP_CLAUSE_LOCATION (c),
 			  "%qD appears more than once in data clauses", t);
-	      else
-		error_at (OMP_CLAUSE_LOCATION (c),
-			  "%qD appears more than once in map clauses", t);
-	      remove = true;
-	    }
-	  else if (ort == C_ORT_ACC
-		   && bitmap_bit_p (&generic_head, DECL_UID (t)))
-	    {
-	      error_at (OMP_CLAUSE_LOCATION (c),
-			"%qD appears more than once in data clauses", t);
-	      remove = true;
-	    }
-	  else if (bitmap_bit_p (&firstprivate_head, DECL_UID (t))
-		   || bitmap_bit_p (&is_on_device_head, DECL_UID (t)))
-	    {
-	      if (ort == C_ORT_ACC)
-		error_at (OMP_CLAUSE_LOCATION (c),
-			  "%qD appears more than once in data clauses", t);
-	      else
-		error_at (OMP_CLAUSE_LOCATION (c),
-			  "%qD appears both in data and map clauses", t);
-	      remove = true;
-	    }
-	  else
-	    {
-	      bitmap_set_bit (&map_head, DECL_UID (t));
+		remove = true;
+	      }
+	    else if (bitmap_bit_p (&firstprivate_head, DECL_UID (t))
+		     || bitmap_bit_p (&is_on_device_head, DECL_UID (t)))
+	      {
+		if (ort == C_ORT_ACC)
+		  error_at (OMP_CLAUSE_LOCATION (c),
+			    "%qD appears more than once in data clauses", t);
+		else
+		  error_at (OMP_CLAUSE_LOCATION (c),
+			    "%qD appears both in data and map clauses", t);
+		remove = true;
+	      }
+	    else
+	      {
+		bitmap_set_bit (&map_head, DECL_UID (t));
 
-	      tree decl = OMP_CLAUSE_DECL (c);
-	      if (t != decl
-		  && (TREE_CODE (decl) == COMPONENT_REF
-		      || (INDIRECT_REF_P (decl)
-			  && TREE_CODE (TREE_OPERAND (decl, 0)) == COMPONENT_REF
-			  && TYPE_REF_P (TREE_TYPE (TREE_OPERAND (decl, 0))))))
-		bitmap_set_bit (&map_field_head, DECL_UID (t));
-	    }
-	handle_map_references:
-	  if (!remove
-	      && !processing_template_decl
-	      && ort != C_ORT_DECLARE_SIMD
-	      && TYPE_REF_P (TREE_TYPE (OMP_CLAUSE_DECL (c))))
-	    {
-	      t = OMP_CLAUSE_DECL (c);
-	      if (OMP_CLAUSE_CODE (c) != OMP_CLAUSE_MAP)
-		{
-		  OMP_CLAUSE_DECL (c) = build_simple_mem_ref (t);
-		  if (OMP_CLAUSE_SIZE (c) == NULL_TREE)
-		    OMP_CLAUSE_SIZE (c)
-		      = TYPE_SIZE_UNIT (TREE_TYPE (TREE_TYPE (t)));
-		}
-	      else if (OMP_CLAUSE_MAP_KIND (c)
-		       != GOMP_MAP_FIRSTPRIVATE_POINTER
-		       && (OMP_CLAUSE_MAP_KIND (c)
-			   != GOMP_MAP_FIRSTPRIVATE_REFERENCE)
-		       && (OMP_CLAUSE_MAP_KIND (c)
-			   != GOMP_MAP_ALWAYS_POINTER)
-		       && (OMP_CLAUSE_MAP_KIND (c)
-			   != GOMP_MAP_ATTACH_DETACH))
-		{
-		  grp_start_p = pc;
-		  grp_sentinel = OMP_CLAUSE_CHAIN (c);
+		tree decl = OMP_CLAUSE_DECL (c);
+		if (t != decl
+		    && (TREE_CODE (decl) == COMPONENT_REF
+			|| (INDIRECT_REF_P (decl)
+			    && (TREE_CODE (TREE_OPERAND (decl, 0))
+				== COMPONENT_REF)
+			    && TYPE_REF_P (TREE_TYPE (TREE_OPERAND (decl,
+								    0))))))
+		  bitmap_set_bit (&map_field_head, DECL_UID (t));
+	      }
+	  handle_map_references:
+	    if (!remove
+		&& !processing_template_decl
+		&& ort != C_ORT_DECLARE_SIMD
+		&& TYPE_REF_P (TREE_TYPE (OMP_CLAUSE_DECL (c))))
+	      {
+		t = OMP_CLAUSE_DECL (c);
+		if (OMP_CLAUSE_CODE (c) != OMP_CLAUSE_MAP)
+		  {
+		    OMP_CLAUSE_DECL (c) = build_simple_mem_ref (t);
+		    if (OMP_CLAUSE_SIZE (c) == NULL_TREE)
+		      OMP_CLAUSE_SIZE (c)
+			= TYPE_SIZE_UNIT (TREE_TYPE (TREE_TYPE (t)));
+		  }
+		else if (OMP_CLAUSE_MAP_KIND (c)
+			 != GOMP_MAP_FIRSTPRIVATE_POINTER
+			 && (OMP_CLAUSE_MAP_KIND (c)
+			     != GOMP_MAP_FIRSTPRIVATE_REFERENCE)
+			 && (OMP_CLAUSE_MAP_KIND (c)
+			     != GOMP_MAP_ALWAYS_POINTER)
+			 && (OMP_CLAUSE_MAP_KIND (c)
+			     != GOMP_MAP_ATTACH_DETACH))
+		  {
+		    grp_start_p = pc;
+		    grp_sentinel = OMP_CLAUSE_CHAIN (c);
 
-		  tree c2 = build_omp_clause (OMP_CLAUSE_LOCATION (c),
-					      OMP_CLAUSE_MAP);
-		  if (TREE_CODE (t) == COMPONENT_REF)
-		    OMP_CLAUSE_SET_MAP_KIND (c2, GOMP_MAP_ALWAYS_POINTER);
-		  else
-		    OMP_CLAUSE_SET_MAP_KIND (c2,
-					     GOMP_MAP_FIRSTPRIVATE_REFERENCE);
-		  OMP_CLAUSE_DECL (c2) = t;
-		  OMP_CLAUSE_SIZE (c2) = size_zero_node;
-		  OMP_CLAUSE_CHAIN (c2) = OMP_CLAUSE_CHAIN (c);
-		  OMP_CLAUSE_CHAIN (c) = c2;
-		  OMP_CLAUSE_DECL (c) = build_simple_mem_ref (t);
-		  if (OMP_CLAUSE_SIZE (c) == NULL_TREE)
-		    OMP_CLAUSE_SIZE (c)
-		      = TYPE_SIZE_UNIT (TREE_TYPE (TREE_TYPE (t)));
-		  c = c2;
-		}
-	    }
+		    tree c2 = build_omp_clause (OMP_CLAUSE_LOCATION (c),
+						OMP_CLAUSE_MAP);
+		    if (TREE_CODE (t) == COMPONENT_REF)
+		      OMP_CLAUSE_SET_MAP_KIND (c2, GOMP_MAP_ALWAYS_POINTER);
+		    else
+		      OMP_CLAUSE_SET_MAP_KIND (c2,
+					       GOMP_MAP_FIRSTPRIVATE_REFERENCE);
+		    OMP_CLAUSE_DECL (c2) = t;
+		    OMP_CLAUSE_SIZE (c2) = size_zero_node;
+		    OMP_CLAUSE_CHAIN (c2) = OMP_CLAUSE_CHAIN (c);
+		    OMP_CLAUSE_CHAIN (c) = c2;
+		    OMP_CLAUSE_DECL (c) = build_simple_mem_ref (t);
+		    if (OMP_CLAUSE_SIZE (c) == NULL_TREE)
+		      OMP_CLAUSE_SIZE (c)
+			= TYPE_SIZE_UNIT (TREE_TYPE (TREE_TYPE (t)));
+		    c = c2;
+		  }
+	      }
+	  }
 	  break;
 
 	case OMP_CLAUSE_ENTER:
-- 
2.41.0


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

* [PATCH v7 2/5] OpenMP/OpenACC: Rework clause expansion and nested struct handling
  2023-08-18 22:47 [PATCH v7 0/5] OpenMP/OpenACC: map clause and OMP gimplify rework Julian Brown
  2023-08-18 22:47 ` [PATCH v7 1/5] OpenMP/OpenACC: Reindent TO/FROM/_CACHE_ stanza in {c_}finish_omp_clause Julian Brown
@ 2023-08-18 22:47 ` Julian Brown
  2023-11-14 10:21   ` Tobias Burnus
  2023-08-18 22:47 ` [PATCH v7 3/5] OpenMP: Pointers and member mappings Julian Brown
                   ` (2 subsequent siblings)
  4 siblings, 1 reply; 19+ messages in thread
From: Julian Brown @ 2023-08-18 22:47 UTC (permalink / raw)
  To: gcc-patches; +Cc: fortran, tobias, jakub

This patch reworks clause expansion in the C, C++ and (to a lesser
extent) Fortran front ends for OpenMP and OpenACC mapping nodes used in
GPU offloading support.

At present a single clause may be turned into several mapping nodes,
or have its mapping type changed, in several places scattered through
the front- and middle-end.  The analysis relating to which particular
transformations are needed for some given expression has become quite hard
to follow.  Briefly, we manipulate clause types in the following places:

 1. During parsing, in c_omp_adjust_map_clauses.  Depending on a set of
    rules, we may change a FIRSTPRIVATE_POINTER (etc.) mapping into
    ATTACH_DETACH, or mark the decl addressable.

 2. In semantics.cc or c-typeck.cc, clauses are expanded in
    handle_omp_array_sections (called via {c_}finish_omp_clauses, or in
    finish_omp_clauses itself.  The two cases are for processing array
    sections (the former), or non-array sections (the latter).

 3. In gimplify.cc, we build sibling lists for struct accesses, which
    groups and sorts accesses along with their struct base, creating
    new ALLOC/RELEASE nodes for pointers.

 4. In gimplify.cc:gimplify_adjust_omp_clauses, mapping nodes may be
    adjusted or created.

This patch doesn't completely disrupt this scheme, though clause
types are no longer adjusted in c_omp_adjust_map_clauses (step 1).
Clause expansion in step 2 (for C and C++) now uses a single, unified
mechanism, parts of which are also reused for analysis in step 3.

Rather than the kind-of "ad-hoc" pattern matching on addresses used to
expand clauses used at present, a new method for analysing addresses is
introduced.  This does a recursive-descent tree walk on expression nodes,
and emits a vector of tokens describing each "part" of the address.
This tokenized address can then be translated directly into mapping nodes,
with the assurance that no part of the expression has been inadvertently
skipped or misinterpreted.  In this way, all the variations of ways
pointers, arrays, references and component accesses might be combined
can be teased apart into easily-understood cases - and we know we've
"parsed" the whole address before we start analysis, so the right code
paths can easily be selected.

For example, a simple access "arr[idx]" might parse as:

  base-decl access-indexed-array

or "mystruct->foo[x]" with a pointer "foo" component might parse as:

  base-decl access-pointer component-selector access-pointer

A key observation is that support for "array" bases, e.g. accesses
whose root nodes are not structures, but describe scalars or arrays,
and also *one-level deep* structure accesses, have first-class support
in gimplify and beyond.  Expressions that use deeper struct accesses
or e.g. multiple indirections were more problematic: some cases worked,
but lots of cases didn't.  This patch reimplements the support for those
in gimplify.cc, again using the new "address tokenization" support.

An expression like "mystruct->foo->bar[0:10]" used in a mapping node will
translate the right-hand access directly in the front-end.  The base for
the access will be "mystruct->foo".  This is handled recursively in
gimplify.cc -- there may be several accesses of "mystruct"'s members
on the same directive, so the sibling-list building machinery can be
used again.  (This was already being done for OpenACC, but the new
implementation differs somewhat in details, and is more robust.)

For OpenMP, in the case where the base pointer itself,
i.e. "mystruct->foo" here, is NOT mapped on the same directive, we
create a "fragile" mapping.  This turns the "foo" component access
into a zero-length allocation (which is a new feature for the runtime,
so support has been added there too).

A couple of changes have been made to how mapping clauses are turned
into mapping nodes:

The first change is based on the observation that it is probably never
correct to use GOMP_MAP_ALWAYS_POINTER for component accesses (e.g. for
references), because if the containing struct is already mapped on the
target then the host version of the pointer in question will be corrupted
if the struct is copied back from the target.  This patch removes all
such uses, across each of C, C++ and Fortran.

The second change is to the way that GOMP_MAP_ATTACH_DETACH nodes
are processed during sibling-list creation.  For OpenMP, for pointer
components, we must map the base pointer separately from an array section
that uses the base pointer, so e.g. we must have both "map(mystruct.base)"
and "map(mystruct.base[0:10])" mappings.  These create nodes such as:

  GOMP_MAP_TOFROM mystruct.base
  G_M_TOFROM *mystruct.base [len: 10*elemsize] G_M_ATTACH_DETACH mystruct.base

Instead of using the first of these directly when building the struct
sibling list then skipping the group using GOMP_MAP_ATTACH_DETACH,
leading to:

  GOMP_MAP_STRUCT mystruct [len: 1] GOMP_MAP_TOFROM mystruct.base

we now introduce a new "mini-pass", omp_resolve_clause_dependencies, that
drops the GOMP_MAP_TOFROM for the base pointer, marks the second group
as having had a base-pointer mapping, then omp_build_struct_sibling_lists
can create:

  GOMP_MAP_STRUCT mystruct [len: 1] GOMP_MAP_ALLOC mystruct.base [len: ptrsize]

This ends up working better in many cases, particularly those involving
references.  (The "alloc" space is immediately overwritten by a pointer
attachment, so this is mildly more efficient than a redundant TO mapping
at runtime also.)

There is support in the address tokenizer for "arbitrary" base expressions
which aren't rooted at a decl, but that is not used as present because
such addresses are disallowed at parse time.

In the front-ends, the address tokenization machinery is mostly only
used for clause expansion and not for diagnostics at present.  It could
be used for those too, which would allow more of my previous "address
inspector" implementation to be removed.

The new bits in gimplify.cc work with OpenACC also.

This version of the patch is based on the version posted for the og13
branch:

https://gcc.gnu.org/pipermail/gcc-patches/2023-June/622220.html

Relative to the version last posted for mainline, it also incorporates
followup fixes previously posted as separate patches:

"OpenMP/OpenACC: Refine condition for when map clause expansion happens"
https://gcc.gnu.org/pipermail/gcc-patches/2022-December/609034.html

Parts of:
"OpenMP: OpenMP 5.2 semantics for pointers with unmapped target"
https://gcc.gnu.org/pipermail/gcc-patches/2023-June/623351.html

"OpenMP: Fix "exit data" for array sections for ref-to-ptr components"
https://gcc.gnu.org/pipermail/gcc-patches/2023-July/623501.html
(With a non-"declare mapper" test case added)

"OpenMP: implicitly map base pointer for array-section pointer components"
https://gcc.gnu.org/pipermail/gcc-patches/2023-June/622219.html

2023-08-18  Julian Brown  <julian@codesourcery.com>

gcc/c-family/
	* c-common.h (c_omp_region_type): Add C_ORT_ACC_TARGET.
	(omp_addr_token): Add forward declaration.
	(c_omp_address_inspector): New class.
	* c-omp.cc (c_omp_adjust_map_clauses): Mark decls addressable here, but
	do not change any mapping node types.
	(c_omp_address_inspector::unconverted_ref_origin,
	c_omp_address_inspector::component_access_p,
	c_omp_address_inspector::check_clause,
	c_omp_address_inspector::get_root_term,
	c_omp_address_inspector::map_supported_p,
	c_omp_address_inspector::get_origin,
	c_omp_address_inspector::maybe_unconvert_ref,
	c_omp_address_inspector::maybe_zero_length_array_section,
	c_omp_address_inspector::expand_array_base,
	c_omp_address_inspector::expand_component_selector,
	c_omp_address_inspector::expand_map_clause): New methods.
	(omp_expand_access_chain): New function.

gcc/c/
	* c-parser.cc (c_parser_oacc_all_clauses): Add TARGET parameter. Use
	to select region type for c_finish_omp_clauses call.
	(c_parser_oacc_loop): Update calls to c_parser_oacc_all_clauses.
	(c_parser_oacc_compute): Likewise.
	(c_parser_omp_target_enter_data): Support ATTACH kind.
	(c_parser_omp_target_exit_data): Support DETACH kind.
	* c-typeck.cc (handle_omp_array_sections_1,
	handle_omp_array_sections, c_finish_omp_clauses): Use
	c_omp_address_inspector class and OMP address tokenizer to analyze and
	expand map clause expressions.  Fix some diagnostics.  Fix "is OpenACC"
	condition for C_ORT_ACC_TARGET addition.

gcc/cp/
	* parser.cc (cp_parser_oacc_all_clauses): Add TARGET parameter. Use
	to select region type for finish_omp_clauses call.
	(cp_parser_omp_target_enter_data): Support GOMP_MAP_ATTACH kind.
	(cp_parser_omp_target_exit_data): Support GOMP_MAP_DETACH kind.
	(cp_parser_oacc_declare): Update call to cp_parser_oacc_all_clauses.
	(cp_parser_oacc_loop): Update calls to cp_parser_oacc_all_clauses.
	(cp_parser_oacc_compute): Likewise.
	* pt.cc (tsubst_expr): Use C_ORT_ACC_TARGET for call to
	tsubst_omp_clauses for compute regions.
	* semantics.cc (cp_omp_address_inspector): New class, derived from
	c_omp_address_inspector.
	(handle_omp_array_sections_1, handle_omp_array_sections,
	finish_omp_clauses): Use cp_omp_address_inspector class and OMP address
	tokenizer to analyze and expand OpenMP map clause expressions.  Fix
	some diagnostics.  Support C_ORT_ACC_TARGET.

gcc/fortran/
	* trans-openmp.cc (gfc_trans_omp_array_section): Add OPENMP parameter.
	Use GOMP_MAP_ATTACH_DETACH instead of GOMP_MAP_ALWAYS_POINTER for
	derived type components.
	(gfc_trans_omp_clauses): Update calls to gfc_trans_omp_array_section.

gcc/
	* gimplify.cc (build_struct_comp_nodes): Don't process
	GOMP_MAP_ATTACH_DETACH "middle" nodes here.
	(omp_mapping_group): Add REPROCESS_STRUCT and FRAGILE booleans for
	nested struct handling.
	(omp_strip_components_and_deref, omp_strip_indirections): Remove
	functions.
	(omp_get_attachment): Handle GOMP_MAP_DETACH here.
	(omp_group_last): Handle GOMP_MAP_*, GOMP_MAP_DETACH,
	GOMP_MAP_ATTACH_DETACH groups for "exit data" of reference-to-pointer
	component array sections.
	(omp_gather_mapping_groups_1): Initialise reprocess_struct and fragile
	fields.
	(omp_group_base): Handle GOMP_MAP_ATTACH_DETACH after GOMP_MAP_STRUCT.
	(omp_index_mapping_groups_1): Skip reprocess_struct groups.
	(omp_get_nonfirstprivate_group, omp_directive_maps_explicitly,
	omp_resolve_clause_dependencies, omp_expand_access_chain,
	omp_first_chained_access_token): New functions.
	(omp_check_mapping_compatibility): Adjust accepted node combinations
	for "from" clauses using release instead of alloc.
	(omp_accumulate_sibling_list): Add GROUP_MAP, ADDR_TOKENS, FRAGILE_P,
	REPROCESSING_STRUCT, ADDED_TAIL parameters.  Use OMP address tokenizer
	to analyze addresses.  Reimplement nested struct handling, and
	implement "fragile groups".
	(omp_build_struct_sibling_lists): Adjust for changes to
	omp_accumulate_sibling_list.  Recalculate bias for ATTACH_DETACH nodes
	after GOMP_MAP_STRUCT nodes.
	(gimplify_scan_omp_clauses): Call omp_resolve_clause_dependencies.  Use
	OMP address tokenizer.
	(gimplify_adjust_omp_clauses_1): Use build_fold_indirect_ref_loc
	instead of build_simple_mem_ref_loc.
	* omp-general.cc (omp-general.h, tree-pretty-print.h): Include.
	(omp_addr_tokenizer): New namespace.
	(omp_addr_tokenizer::omp_addr_token): New.
	(omp_addr_tokenizer::omp_parse_component_selector,
	omp_addr_tokenizer::omp_parse_ref,
	omp_addr_tokenizer::omp_parse_pointer,
	omp_addr_tokenizer::omp_parse_access_method,
	omp_addr_tokenizer::omp_parse_access_methods,
	omp_addr_tokenizer::omp_parse_structure_base,
	omp_addr_tokenizer::omp_parse_structured_expr,
	omp_addr_tokenizer::omp_parse_array_expr,
	omp_addr_tokenizer::omp_access_chain_p,
	omp_addr_tokenizer::omp_accessed_addr): New functions.
	(omp_parse_expr, debug_omp_tokenized_addr): New functions.
	* omp-general.h (omp_addr_tokenizer::access_method_kinds,
	omp_addr_tokenizer::structure_base_kinds,
	omp_addr_tokenizer::token_type,
	omp_addr_tokenizer::omp_addr_token,
	omp_addr_tokenizer::omp_access_chain_p,
	omp_addr_tokenizer::omp_accessed_addr): New.
	(omp_addr_token, omp_parse_expr): New.
	* omp-low.cc (scan_sharing_clauses): Skip error check for references
	to pointers.
	* tree.h (OMP_CLAUSE_ATTACHMENT_MAPPING_ERASED): New macro.

gcc/testsuite/
	* c-c++-common/gomp/clauses-2.c: Fix error output.
	* c-c++-common/gomp/target-implicit-map-2.c: Adjust scan output.
	* c-c++-common/gomp/target-50.c: Adjust scan output.
	* c-c++-common/gomp/target-enter-data-1.c: Adjust scan output.
	* g++.dg/gomp/static-component-1.C: New test.
	* gcc.dg/gomp/target-3.c: Adjust scan output.
	* gfortran.dg/gomp/map-9.c.f90: Adjust scan output.
	* c-c++-comon/gomp/target-implicit-map-2.c: Update expected scan output.

libgomp/
	* target.c (gomp_map_pointer): Modify zero-length array section
	pointer handling.
	(gomp_attach_pointer): Likewise.
	(gomp_map_fields_existing): Use gomp_map_0len_lookup.
	(gomp_attach_pointer): Allow attaching null pointers (or Fortran
	"unassociated" pointers).
	(gomp_map_vars_internal): Handle zero-sized struct members.  Add
	diagnostic for unmapped struct pointer members.
	* testsuite/libgomp.c-c++-common/baseptrs-1.c: New test.
	* testsuite/libgomp.c-c++-common/baseptrs-2.c: New test.
	* testsuite/libgomp.c-c++-common/ptr-attach-2.c: New test.
	* testsuite/libgomp.c-c++-common/target-implicit-map-2.c: Fix missing
	"free".
	* testsuite/libgomp.c-c++-common/target-implicit-map-5.c: New test.
	* testsuite/libgomp.c-c++-common/target-map-zlas-1.c: New test.
	* testsuite/libgomp.c++/class-array-1.C: New test.
	* testsuite/libgomp.c++/baseptrs-3.C: New test.
	* testsuite/libgomp.c++/baseptrs-4.C: New test.
	* testsuite/libgomp.c++/baseptrs-5.C: New test.
	* testsuite/libgomp.c++/target-48.C: New test.
	* testsuite/libgomp.c++/target-49.C: New test.
	* testsuite/libgomp.c++/target-exit-data-reftoptr-1.C: New test.
	* testsuite/libgomp.c++/target-lambda-1.C: Update for OpenMP 5.2
	semantics.
	* testsuite/libgomp.c++/target-this-3.C: Likewise.
	* testsuite/libgomp.c++/target-this-4.C: Likewise.
	* testsuite/libgomp.fortran/struct-elem-map-1.f90: Add XFAIL.
---
 gcc/c-family/c-common.h                       |   74 +-
 gcc/c-family/c-omp.cc                         |  834 ++++-
 gcc/c/c-parser.cc                             |   17 +-
 gcc/c/c-typeck.cc                             |  386 +-
 gcc/cp/parser.cc                              |   17 +-
 gcc/cp/pt.cc                                  |    4 +-
 gcc/cp/semantics.cc                           |  619 ++--
 gcc/fortran/trans-openmp.cc                   |   34 +-
 gcc/gimplify.cc                               | 1076 +++++-
 gcc/omp-general.cc                            |  425 +++
 gcc/omp-general.h                             |   69 +
 gcc/omp-low.cc                                |    7 +-
 gcc/testsuite/c-c++-common/gomp/clauses-2.c   |    2 +-
 gcc/testsuite/c-c++-common/gomp/target-50.c   |    2 +-
 .../c-c++-common/gomp/target-enter-data-1.c   |    3 +-
 .../c-c++-common/gomp/target-implicit-map-2.c |    3 +-
 .../g++.dg/gomp/static-component-1.C          |   23 +
 gcc/testsuite/gcc.dg/gomp/target-3.c          |    2 +-
 gcc/testsuite/gfortran.dg/gomp/map-9.f90      |    2 +-
 gcc/tree.h                                    |    4 +
 libgomp/target.c                              |   38 +-
 libgomp/testsuite/libgomp.c++/baseptrs-3.C    |  275 ++
 libgomp/testsuite/libgomp.c++/baseptrs-4.C    | 3154 +++++++++++++++++
 libgomp/testsuite/libgomp.c++/baseptrs-5.C    |   62 +
 libgomp/testsuite/libgomp.c++/class-array-1.C |   59 +
 libgomp/testsuite/libgomp.c++/target-48.C     |   32 +
 libgomp/testsuite/libgomp.c++/target-49.C     |   37 +
 .../libgomp.c++/target-exit-data-reftoptr-1.C |   34 +
 .../testsuite/libgomp.c++/target-lambda-1.C   |    5 +-
 libgomp/testsuite/libgomp.c++/target-this-3.C |   11 +-
 libgomp/testsuite/libgomp.c++/target-this-4.C |   11 +-
 .../libgomp.c-c++-common/baseptrs-1.c         |   50 +
 .../libgomp.c-c++-common/baseptrs-2.c         |   70 +
 .../libgomp.c-c++-common/ptr-attach-2.c       |   60 +
 .../target-implicit-map-2.c                   |    2 +
 .../target-implicit-map-5.c                   |   50 +
 .../libgomp.c-c++-common/target-map-zlas-1.c  |   36 +
 .../libgomp.fortran/struct-elem-map-1.f90     |    3 +
 38 files changed, 6786 insertions(+), 806 deletions(-)
 create mode 100644 gcc/testsuite/g++.dg/gomp/static-component-1.C
 create mode 100644 libgomp/testsuite/libgomp.c++/baseptrs-3.C
 create mode 100644 libgomp/testsuite/libgomp.c++/baseptrs-4.C
 create mode 100644 libgomp/testsuite/libgomp.c++/baseptrs-5.C
 create mode 100644 libgomp/testsuite/libgomp.c++/class-array-1.C
 create mode 100644 libgomp/testsuite/libgomp.c++/target-48.C
 create mode 100644 libgomp/testsuite/libgomp.c++/target-49.C
 create mode 100644 libgomp/testsuite/libgomp.c++/target-exit-data-reftoptr-1.C
 create mode 100644 libgomp/testsuite/libgomp.c-c++-common/baseptrs-1.c
 create mode 100644 libgomp/testsuite/libgomp.c-c++-common/baseptrs-2.c
 create mode 100644 libgomp/testsuite/libgomp.c-c++-common/ptr-attach-2.c
 create mode 100644 libgomp/testsuite/libgomp.c-c++-common/target-implicit-map-5.c
 create mode 100644 libgomp/testsuite/libgomp.c-c++-common/target-map-zlas-1.c

diff --git a/gcc/c-family/c-common.h b/gcc/c-family/c-common.h
index 78fc5248ba68..68dddfb887ca 100644
--- a/gcc/c-family/c-common.h
+++ b/gcc/c-family/c-common.h
@@ -1276,7 +1276,8 @@ enum c_omp_region_type
   C_ORT_DECLARE_SIMD		= 1 << 2,
   C_ORT_TARGET			= 1 << 3,
   C_ORT_OMP_DECLARE_SIMD	= C_ORT_OMP | C_ORT_DECLARE_SIMD,
-  C_ORT_OMP_TARGET		= C_ORT_OMP | C_ORT_TARGET
+  C_ORT_OMP_TARGET		= C_ORT_OMP | C_ORT_TARGET,
+  C_ORT_ACC_TARGET		= C_ORT_ACC | C_ORT_TARGET
 };
 
 extern tree c_finish_omp_master (location_t, tree);
@@ -1312,6 +1313,77 @@ extern tree c_omp_check_context_selector (location_t, tree);
 extern void c_omp_mark_declare_variant (location_t, tree, tree);
 extern void c_omp_adjust_map_clauses (tree, bool);
 
+namespace omp_addr_tokenizer { struct omp_addr_token; }
+typedef omp_addr_tokenizer::omp_addr_token omp_addr_token;
+
+class c_omp_address_inspector
+{
+  location_t loc;
+  tree root_term;
+  bool indirections;
+  int map_supported;
+
+protected:
+  tree orig;
+
+public:
+  c_omp_address_inspector (location_t loc, tree t)
+    : loc (loc), root_term (NULL_TREE), indirections (false),
+      map_supported (-1), orig (t)
+    {
+    }
+
+  ~c_omp_address_inspector ()
+    {
+    }
+
+  virtual bool processing_template_decl_p ()
+    {
+      return false;
+    }
+
+  virtual void emit_unmappable_type_notes (tree)
+    {
+    }
+
+  virtual tree convert_from_reference (tree)
+    {
+      gcc_unreachable ();
+    }
+
+  virtual tree build_array_ref (location_t loc, tree arr, tree idx)
+    {
+      tree eltype = TREE_TYPE (TREE_TYPE (arr));
+      return build4_loc (loc, ARRAY_REF, eltype, arr, idx, NULL_TREE,
+			 NULL_TREE);
+    }
+
+  virtual bool check_clause (tree);
+  tree get_root_term (bool);
+
+  tree get_address ()
+    {
+      return orig;
+    }
+
+  tree unconverted_ref_origin ();
+  bool component_access_p ();
+
+  bool map_supported_p ();
+
+  static tree get_origin (tree);
+  static tree maybe_unconvert_ref (tree);
+
+  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);
+};
+
 enum c_omp_directive_kind {
   C_OMP_DIR_STANDALONE,
   C_OMP_DIR_CONSTRUCT,
diff --git a/gcc/c-family/c-omp.cc b/gcc/c-family/c-omp.cc
index 4faddb00bbcb..2b173359e800 100644
--- a/gcc/c-family/c-omp.cc
+++ b/gcc/c-family/c-omp.cc
@@ -3018,8 +3018,9 @@ struct map_clause
     decl_mapped (false), omp_declare_target (false) { }
 };
 
-/* Adjust map clauses after normal clause parsing, mainly to turn specific
-   base-pointer map cases into attach/detach and mark them addressable.  */
+/* Adjust map clauses after normal clause parsing, mainly to mark specific
+   base-pointer map cases addressable that may be turned into attach/detach
+   operations during gimplification.  */
 void
 c_omp_adjust_map_clauses (tree clauses, bool is_target)
 {
@@ -3035,7 +3036,6 @@ c_omp_adjust_map_clauses (tree clauses, bool is_target)
 	    && POINTER_TYPE_P (TREE_TYPE (OMP_CLAUSE_DECL (c))))
 	  {
 	    tree ptr = OMP_CLAUSE_DECL (c);
-	    OMP_CLAUSE_SET_MAP_KIND (c, GOMP_MAP_ATTACH_DETACH);
 	    c_common_mark_addressable_vec (ptr);
 	  }
       return;
@@ -3048,7 +3048,7 @@ c_omp_adjust_map_clauses (tree clauses, bool is_target)
 	&& DECL_P (OMP_CLAUSE_DECL (c)))
       {
 	/* If this is for a target construct, the firstprivate pointer
-	   is changed to attach/detach if either is true:
+	   is marked addressable if either is true:
 	   (1) the base-pointer is mapped in this same construct, or
 	   (2) the base-pointer is a variable place on the device by
 	       "declare target" directives.
@@ -3090,11 +3090,833 @@ c_omp_adjust_map_clauses (tree clauses, bool is_target)
 
       if (mc.firstprivate_ptr_p
 	  && (mc.decl_mapped || mc.omp_declare_target))
+	c_common_mark_addressable_vec (OMP_CLAUSE_DECL (mc.clause));
+    }
+}
+
+/* Maybe strip off an indirection from a "converted" reference, then find the
+   origin of a pointer (i.e. without any offset).  */
+
+tree
+c_omp_address_inspector::unconverted_ref_origin ()
+{
+  tree t = orig;
+
+  /* We may have a reference-typed component access at the outermost level
+     that has had convert_from_reference called on it.  Get the un-dereferenced
+     reference itself.  */
+  t = maybe_unconvert_ref (t);
+
+  /* Find base pointer for POINTER_PLUS_EXPR, etc.  */
+  t = get_origin (t);
+
+  return t;
+}
+
+/* Return TRUE if the address is a component access.  */
+
+bool
+c_omp_address_inspector::component_access_p ()
+{
+  tree t = maybe_unconvert_ref (orig);
+
+  t = get_origin (t);
+
+  return TREE_CODE (t) == COMPONENT_REF;
+}
+
+/* Perform various checks on the address, as described by clause CLAUSE (we
+   only use its code and location here).  */
+
+bool
+c_omp_address_inspector::check_clause (tree clause)
+{
+  tree t = unconverted_ref_origin ();
+
+  if (TREE_CODE (t) != COMPONENT_REF)
+    return true;
+
+  if (TREE_CODE (TREE_OPERAND (t, 1)) == FIELD_DECL
+      && DECL_BIT_FIELD (TREE_OPERAND (t, 1)))
+    {
+      error_at (OMP_CLAUSE_LOCATION (clause),
+		"bit-field %qE in %qs clause",
+		t, omp_clause_code_name[OMP_CLAUSE_CODE (clause)]);
+      return false;
+    }
+  else if (!processing_template_decl_p ()
+	   && !omp_mappable_type (TREE_TYPE (t)))
+    {
+      error_at (OMP_CLAUSE_LOCATION (clause),
+		"%qE does not have a mappable type in %qs clause",
+		t, omp_clause_code_name[OMP_CLAUSE_CODE (clause)]);
+      emit_unmappable_type_notes (TREE_TYPE (t));
+      return false;
+    }
+  else if (TREE_TYPE (t) && TYPE_ATOMIC (TREE_TYPE (t)))
+    {
+      error_at (OMP_CLAUSE_LOCATION (clause),
+		"%<_Atomic%> %qE in %qs clause", t,
+		omp_clause_code_name[OMP_CLAUSE_CODE (clause)]);
+      return false;
+    }
+
+  return true;
+}
+
+/* Find the "root term" for the address.  This is the innermost decl, etc.
+   of the access.  */
+
+tree
+c_omp_address_inspector::get_root_term (bool checking)
+{
+  if (root_term && !checking)
+    return root_term;
+
+  tree t = unconverted_ref_origin ();
+
+  while (TREE_CODE (t) == COMPONENT_REF)
+    {
+      if (checking
+	  && TREE_TYPE (TREE_OPERAND (t, 0))
+	  && TREE_CODE (TREE_TYPE (TREE_OPERAND (t, 0))) == UNION_TYPE)
 	{
-	  OMP_CLAUSE_SET_MAP_KIND (mc.clause, GOMP_MAP_ATTACH_DETACH);
-	  c_common_mark_addressable_vec (OMP_CLAUSE_DECL (mc.clause));
+	  error_at (loc, "%qE is a member of a union", t);
+	  return error_mark_node;
+	}
+      t = TREE_OPERAND (t, 0);
+      while (TREE_CODE (t) == MEM_REF
+	     || TREE_CODE (t) == INDIRECT_REF
+	     || TREE_CODE (t) == ARRAY_REF)
+	{
+	  if (TREE_CODE (t) == MEM_REF
+	      || TREE_CODE (t) == INDIRECT_REF)
+	    indirections = true;
+	  t = TREE_OPERAND (t, 0);
+	  STRIP_NOPS (t);
+	  if (TREE_CODE (t) == POINTER_PLUS_EXPR)
+	    t = TREE_OPERAND (t, 0);
 	}
     }
+
+  root_term = t;
+
+  return t;
+}
+
+/* Return TRUE if the address is supported in mapping clauses.  At present,
+   this means that the innermost expression is a DECL_P, but could be extended
+   to other types of expression in the future.  */
+
+bool
+c_omp_address_inspector::map_supported_p ()
+{
+  /* If we've already decided if the mapped address is supported, return
+     that.  */
+  if (map_supported != -1)
+    return map_supported;
+
+  tree t = unconverted_ref_origin ();
+
+  STRIP_NOPS (t);
+
+  while (TREE_CODE (t) == INDIRECT_REF
+	 || TREE_CODE (t) == MEM_REF
+	 || TREE_CODE (t) == ARRAY_REF
+	 || TREE_CODE (t) == COMPONENT_REF
+	 || TREE_CODE (t) == COMPOUND_EXPR
+	 || TREE_CODE (t) == SAVE_EXPR
+	 || TREE_CODE (t) == POINTER_PLUS_EXPR
+	 || TREE_CODE (t) == NON_LVALUE_EXPR
+	 || TREE_CODE (t) == NOP_EXPR)
+    if (TREE_CODE (t) == COMPOUND_EXPR)
+      t = TREE_OPERAND (t, 1);
+    else
+      t = TREE_OPERAND (t, 0);
+
+  STRIP_NOPS (t);
+
+  map_supported = DECL_P (t);
+
+  return map_supported;
+}
+
+/* Get the origin of an address T, stripping off offsets and some other
+   bits.  */
+
+tree
+c_omp_address_inspector::get_origin (tree t)
+{
+  while (1)
+    {
+      if (TREE_CODE (t) == COMPOUND_EXPR)
+	{
+	  t = TREE_OPERAND (t, 1);
+	  STRIP_NOPS (t);
+	}
+      else if (TREE_CODE (t) == POINTER_PLUS_EXPR
+	       || TREE_CODE (t) == SAVE_EXPR)
+	t = TREE_OPERAND (t, 0);
+      else if (TREE_CODE (t) == INDIRECT_REF
+	       && TREE_CODE (TREE_TYPE (TREE_OPERAND (t, 0))) == REFERENCE_TYPE)
+	t = TREE_OPERAND (t, 0);
+      else
+	break;
+    }
+  STRIP_NOPS (t);
+  return t;
+}
+
+/* For an address T that might be a reference that has had
+   "convert_from_reference" called on it, return the actual reference without
+   any indirection.  */
+
+tree
+c_omp_address_inspector::maybe_unconvert_ref (tree t)
+{
+  if (TREE_CODE (t) == INDIRECT_REF
+      && TREE_CODE (TREE_TYPE (TREE_OPERAND (t, 0))) == REFERENCE_TYPE)
+    return TREE_OPERAND (t, 0);
+
+  return t;
+}
+
+/* Return TRUE if CLAUSE might describe a zero-length array section.  */
+
+bool
+c_omp_address_inspector::maybe_zero_length_array_section (tree clause)
+{
+  switch (OMP_CLAUSE_MAP_KIND (clause))
+    {
+    case GOMP_MAP_ALLOC:
+    case GOMP_MAP_IF_PRESENT:
+    case GOMP_MAP_TO:
+    case GOMP_MAP_FROM:
+    case GOMP_MAP_TOFROM:
+    case GOMP_MAP_ALWAYS_TO:
+    case GOMP_MAP_ALWAYS_FROM:
+    case GOMP_MAP_ALWAYS_TOFROM:
+    case GOMP_MAP_RELEASE:
+    case GOMP_MAP_DELETE:
+    case GOMP_MAP_FORCE_TO:
+    case GOMP_MAP_FORCE_FROM:
+    case GOMP_MAP_FORCE_TOFROM:
+    case GOMP_MAP_FORCE_PRESENT:
+      return true;
+    default:
+      return false;
+    }
+}
+
+/* Expand a chained access.  We only expect to see a quite limited range of
+   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)
+{
+  using namespace omp_addr_tokenizer;
+  location_t loc = OMP_CLAUSE_LOCATION (c);
+  unsigned i = *idx;
+  tree c2 = NULL_TREE;
+  gomp_map_kind kind
+    = (OMP_CLAUSE_CODE (c) == OMP_CLAUSE_FROM
+       || (OMP_CLAUSE_CODE (c) == OMP_CLAUSE_MAP
+	   && (OMP_CLAUSE_MAP_KIND (c) == GOMP_MAP_FROM
+	       || OMP_CLAUSE_MAP_KIND (c) == GOMP_MAP_DELETE
+	       || OMP_CLAUSE_MAP_KIND (c) == GOMP_MAP_RELEASE
+	       || OMP_CLAUSE_MAP_KIND (c) == GOMP_MAP_ALWAYS_FROM
+	       || OMP_CLAUSE_MAP_KIND (c) == GOMP_MAP_FORCE_FROM
+	       || OMP_CLAUSE_MAP_KIND (c) == GOMP_MAP_PRESENT_FROM)))
+      ? GOMP_MAP_DETACH : GOMP_MAP_ATTACH;
+
+  switch (addr_tokens[i]->u.access_kind)
+    {
+    case ACCESS_POINTER:
+    case ACCESS_POINTER_OFFSET:
+      {
+	tree virtual_origin
+	  = fold_convert_loc (loc, ptrdiff_type_node, addr_tokens[i]->expr);
+	tree data_addr = omp_accessed_addr (addr_tokens, i, expr);
+	c2 = build_omp_clause (loc, OMP_CLAUSE_MAP);
+	OMP_CLAUSE_SET_MAP_KIND (c2, kind);
+	OMP_CLAUSE_DECL (c2) = addr_tokens[i]->expr;
+	OMP_CLAUSE_SIZE (c2)
+	  = fold_build2_loc (loc, MINUS_EXPR, ptrdiff_type_node,
+			     fold_convert_loc (loc, ptrdiff_type_node,
+					       data_addr),
+			     virtual_origin);
+      }
+      break;
+
+    case ACCESS_INDEXED_ARRAY:
+      break;
+
+    default:
+      return error_mark_node;
+    }
+
+  if (c2)
+    {
+      OMP_CLAUSE_CHAIN (c2) = OMP_CLAUSE_CHAIN (c);
+      OMP_CLAUSE_CHAIN (c) = c2;
+      c = c2;
+    }
+
+  *idx = ++i;
+
+  if (i < addr_tokens.length ()
+      && addr_tokens[i]->type == ACCESS_METHOD)
+    return omp_expand_access_chain (c, expr, addr_tokens, idx);
+
+  return c;
+}
+
+/* Translate "array_base_decl access_method" to OMP mapping clauses.  */
+
+tree
+c_omp_address_inspector::expand_array_base (tree c,
+					    vec<omp_addr_token *> &addr_tokens,
+					    tree expr, unsigned *idx,
+					    c_omp_region_type ort,
+					    bool decl_p)
+{
+  using namespace omp_addr_tokenizer;
+  location_t loc = OMP_CLAUSE_LOCATION (c);
+  int i = *idx;
+  tree decl = addr_tokens[i + 1]->expr;
+  bool declare_target_p = (decl_p
+			   && is_global_var (decl)
+			   && lookup_attribute ("omp declare target",
+						DECL_ATTRIBUTES (decl)));
+  bool map_p = OMP_CLAUSE_CODE (c) == OMP_CLAUSE_MAP;
+  bool implicit_p = (OMP_CLAUSE_CODE (c) == OMP_CLAUSE_MAP
+		     && OMP_CLAUSE_MAP_IMPLICIT (c));
+  bool chain_p = omp_access_chain_p (addr_tokens, i + 1);
+  tree c2 = NULL_TREE, c3 = NULL_TREE;
+  unsigned consume_tokens = 2;
+  bool target = (ort & C_ORT_TARGET) != 0;
+  bool openmp = (ort & C_ORT_OMP) != 0;
+
+  gcc_assert (i == 0);
+
+  if (!openmp
+      && OMP_CLAUSE_CODE (c) == OMP_CLAUSE_MAP
+      && (OMP_CLAUSE_MAP_KIND (c) == GOMP_MAP_ATTACH
+	  || OMP_CLAUSE_MAP_KIND (c) == GOMP_MAP_DETACH))
+    {
+      *idx = ++i;
+      return c;
+    }
+
+  switch (addr_tokens[i + 1]->u.access_kind)
+    {
+    case ACCESS_DIRECT:
+      if (decl_p && !target)
+	c_common_mark_addressable_vec (addr_tokens[i + 1]->expr);
+      break;
+
+    case ACCESS_REF:
+      {
+	/* Copy the referenced object.  Note that we do this even for !MAP_P
+	   clauses.  */
+	tree obj = convert_from_reference (addr_tokens[i + 1]->expr);
+	OMP_CLAUSE_DECL (c) = obj;
+	OMP_CLAUSE_SIZE (c) = TYPE_SIZE_UNIT (TREE_TYPE (obj));
+
+	if (!map_p)
+	  {
+	    if (decl_p)
+	      c_common_mark_addressable_vec (addr_tokens[i + 1]->expr);
+	    break;
+	  }
+
+	/* If we have a reference to a pointer, avoid using
+	   FIRSTPRIVATE_REFERENCE here in case the pointer is modified in the
+	   offload region (we can only do that if the pointer does not point
+	   to a mapped block).  We could avoid doing this if we don't have a
+	   FROM mapping...  */
+	bool ref_to_ptr = TREE_CODE (TREE_TYPE (obj)) == POINTER_TYPE;
+
+	if (target)
+	  {
+	    c2 = build_omp_clause (loc, OMP_CLAUSE_MAP);
+	    if (target
+		&& !ref_to_ptr
+		&& !declare_target_p
+		&& decl_p)
+	      OMP_CLAUSE_SET_MAP_KIND (c2, GOMP_MAP_FIRSTPRIVATE_REFERENCE);
+	    else
+	      {
+		OMP_CLAUSE_SET_MAP_KIND (c2, GOMP_MAP_ATTACH_DETACH);
+		if (decl_p)
+		  c_common_mark_addressable_vec (addr_tokens[i + 1]->expr);
+	      }
+	    OMP_CLAUSE_DECL (c2) = addr_tokens[i + 1]->expr;
+	    OMP_CLAUSE_SIZE (c2) = size_zero_node;
+
+	    if (ref_to_ptr)
+	      {
+		c3 = c2;
+		c2 = build_omp_clause (loc, OMP_CLAUSE_MAP);
+		OMP_CLAUSE_SET_MAP_KIND (c2, GOMP_MAP_ALLOC);
+		OMP_CLAUSE_DECL (c2) = addr_tokens[i + 1]->expr;
+		OMP_CLAUSE_SIZE (c2)
+		  = TYPE_SIZE_UNIT (TREE_TYPE (OMP_CLAUSE_DECL (c2)));
+	      }
+	  }
+      }
+      break;
+
+    case ACCESS_INDEXED_REF_TO_ARRAY:
+      {
+	if (!map_p)
+	  {
+	    if (decl_p)
+	      c_common_mark_addressable_vec (addr_tokens[i + 1]->expr);
+	    break;
+	  }
+
+	tree virtual_origin
+	  = convert_from_reference (addr_tokens[i + 1]->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);
+	c2 = build_omp_clause (loc, OMP_CLAUSE_MAP);
+	if (decl_p && target && !declare_target_p)
+	  OMP_CLAUSE_SET_MAP_KIND (c2, GOMP_MAP_FIRSTPRIVATE_REFERENCE);
+	else
+	  {
+	    if (decl_p)
+	      c_common_mark_addressable_vec (addr_tokens[i + 1]->expr);
+	    OMP_CLAUSE_SET_MAP_KIND (c2, GOMP_MAP_ATTACH_DETACH);
+	  }
+	OMP_CLAUSE_DECL (c2) = addr_tokens[i + 1]->expr;
+	OMP_CLAUSE_SIZE (c2)
+	  = fold_build2_loc (loc, MINUS_EXPR, ptrdiff_type_node,
+			     fold_convert_loc (loc, ptrdiff_type_node,
+					       data_addr),
+			     virtual_origin);
+      }
+      break;
+
+    case ACCESS_INDEXED_ARRAY:
+      {
+	if (!map_p)
+	  {
+	    if (decl_p)
+	      c_common_mark_addressable_vec (addr_tokens[i + 1]->expr);
+	    break;
+	  }
+
+	/* The code handling "firstprivatize_array_bases" in gimplify.cc is
+	   relevant here.  What do we need to create for arrays at this
+	   stage?  (This condition doesn't feel quite right.  FIXME?)  */
+	if (!target
+	    && (TREE_CODE (TREE_TYPE (addr_tokens[i + 1]->expr))
+		== ARRAY_TYPE))
+	  break;
+
+	tree virtual_origin
+	  = build_fold_addr_expr (addr_tokens[i + 1]->expr);
+	virtual_origin = fold_convert_loc (loc, ptrdiff_type_node,
+					   virtual_origin);
+	tree data_addr = omp_accessed_addr (addr_tokens, i + 1, expr);
+	c2 = build_omp_clause (loc, OMP_CLAUSE_MAP);
+	if (decl_p && target)
+	  OMP_CLAUSE_SET_MAP_KIND (c2, GOMP_MAP_FIRSTPRIVATE_POINTER);
+	else
+	  {
+	    if (decl_p)
+	      c_common_mark_addressable_vec (addr_tokens[i + 1]->expr);
+	    OMP_CLAUSE_SET_MAP_KIND (c2, GOMP_MAP_ATTACH_DETACH);
+	  }
+	OMP_CLAUSE_DECL (c2) = addr_tokens[i + 1]->expr;
+	OMP_CLAUSE_SIZE (c2)
+	  = fold_build2_loc (loc, MINUS_EXPR, ptrdiff_type_node,
+			     fold_convert_loc (loc, ptrdiff_type_node,
+					       data_addr),
+			     virtual_origin);
+      }
+      break;
+
+    case ACCESS_POINTER:
+    case ACCESS_POINTER_OFFSET:
+      {
+	if (!map_p)
+	  {
+	    if (decl_p)
+	      c_common_mark_addressable_vec (addr_tokens[i + 1]->expr);
+	    break;
+	  }
+
+	unsigned last_access = i + 1;
+	tree virtual_origin;
+
+	if (chain_p
+	    && addr_tokens[i + 2]->type == ACCESS_METHOD
+	    && addr_tokens[i + 2]->u.access_kind == ACCESS_INDEXED_ARRAY)
+	  {
+	    /* !!! This seems wrong for ACCESS_POINTER_OFFSET.  */
+	    consume_tokens = 3;
+	    chain_p = omp_access_chain_p (addr_tokens, i + 2);
+	    last_access = i + 2;
+	    virtual_origin
+	      = build_array_ref (loc, addr_tokens[last_access]->expr,
+				 integer_zero_node);
+	    virtual_origin = build_fold_addr_expr (virtual_origin);
+	    virtual_origin = fold_convert_loc (loc, ptrdiff_type_node,
+					       virtual_origin);
+	  }
+	else
+	  virtual_origin = fold_convert_loc (loc, ptrdiff_type_node,
+					     addr_tokens[last_access]->expr);
+	tree data_addr = omp_accessed_addr (addr_tokens, last_access, expr);
+	c2 = build_omp_clause (loc, OMP_CLAUSE_MAP);
+	/* For OpenACC, use FIRSTPRIVATE_POINTER for decls even on non-compute
+	   regions (e.g. "acc data" constructs).  It'll be removed anyway in
+	   gimplify.cc, but doing it this way maintains diagnostic
+	   behaviour.  */
+	if (decl_p && (target || !openmp) && !chain_p && !declare_target_p)
+	  OMP_CLAUSE_SET_MAP_KIND (c2, GOMP_MAP_FIRSTPRIVATE_POINTER);
+	else
+	  {
+	    if (decl_p)
+	      c_common_mark_addressable_vec (addr_tokens[i + 1]->expr);
+	    OMP_CLAUSE_SET_MAP_KIND (c2, GOMP_MAP_ATTACH_DETACH);
+	  }
+	OMP_CLAUSE_DECL (c2) = addr_tokens[i + 1]->expr;
+	OMP_CLAUSE_SIZE (c2)
+	  = fold_build2_loc (loc, MINUS_EXPR, ptrdiff_type_node,
+			     fold_convert_loc (loc, ptrdiff_type_node,
+					       data_addr),
+			     virtual_origin);
+      }
+      break;
+
+    case ACCESS_REF_TO_POINTER:
+    case ACCESS_REF_TO_POINTER_OFFSET:
+      {
+	if (!map_p)
+	  {
+	    if (decl_p)
+	      c_common_mark_addressable_vec (addr_tokens[i + 1]->expr);
+	    break;
+	  }
+
+	unsigned last_access = i + 1;
+	tree virtual_origin;
+
+	if (chain_p
+	    && addr_tokens[i + 2]->type == ACCESS_METHOD
+	    && addr_tokens[i + 2]->u.access_kind == ACCESS_INDEXED_ARRAY)
+	  {
+	    /* !!! This seems wrong for ACCESS_POINTER_OFFSET.  */
+	    consume_tokens = 3;
+	    chain_p = omp_access_chain_p (addr_tokens, i + 2);
+	    last_access = i + 2;
+	    virtual_origin
+	      = build_array_ref (loc, addr_tokens[last_access]->expr,
+				 integer_zero_node);
+	    virtual_origin = build_fold_addr_expr (virtual_origin);
+	    virtual_origin = fold_convert_loc (loc, ptrdiff_type_node,
+					       virtual_origin);
+	  }
+	else
+	  {
+	    virtual_origin
+	      = convert_from_reference (addr_tokens[last_access]->expr);
+	    virtual_origin = fold_convert_loc (loc, ptrdiff_type_node,
+					       virtual_origin);
+	  }
+
+	tree data_addr = omp_accessed_addr (addr_tokens, last_access, expr);
+	c2 = build_omp_clause (loc, OMP_CLAUSE_MAP);
+	if (decl_p && target && !declare_target_p)
+	  OMP_CLAUSE_SET_MAP_KIND (c2, GOMP_MAP_FIRSTPRIVATE_REFERENCE);
+	else
+	  {
+	    if (decl_p)
+	      c_common_mark_addressable_vec (addr_tokens[i + 1]->expr);
+	    OMP_CLAUSE_SET_MAP_KIND (c2, GOMP_MAP_ATTACH_DETACH);
+	  }
+	OMP_CLAUSE_DECL (c2) = addr_tokens[i + 1]->expr;
+	OMP_CLAUSE_SIZE (c2)
+	  = fold_build2_loc (loc, MINUS_EXPR, ptrdiff_type_node,
+			     fold_convert_loc (loc, ptrdiff_type_node,
+					       data_addr),
+			     virtual_origin);
+      }
+      break;
+
+    default:
+      *idx = i + consume_tokens;
+      return error_mark_node;
+    }
+
+  if (c3)
+    {
+      OMP_CLAUSE_CHAIN (c3) = OMP_CLAUSE_CHAIN (c);
+      OMP_CLAUSE_CHAIN (c2) = c3;
+      OMP_CLAUSE_CHAIN (c) = c2;
+      if (implicit_p)
+	{
+	  OMP_CLAUSE_MAP_IMPLICIT (c2) = 1;
+	  OMP_CLAUSE_MAP_IMPLICIT (c3) = 1;
+	}
+      c = c3;
+    }
+  else if (c2)
+    {
+      OMP_CLAUSE_CHAIN (c2) = OMP_CLAUSE_CHAIN (c);
+      OMP_CLAUSE_CHAIN (c) = c2;
+      if (implicit_p)
+	OMP_CLAUSE_MAP_IMPLICIT (c2) = 1;
+      c = c2;
+    }
+
+  i += consume_tokens;
+  *idx = i;
+
+  if (chain_p && map_p)
+    return omp_expand_access_chain (c, expr, addr_tokens, idx);
+
+  return c;
+}
+
+/* Translate "component_selector access_method" to OMP mapping clauses.  */
+
+tree
+c_omp_address_inspector::expand_component_selector (tree c,
+						    vec<omp_addr_token *>
+						      &addr_tokens,
+						    tree expr, unsigned *idx)
+{
+  using namespace omp_addr_tokenizer;
+  location_t loc = OMP_CLAUSE_LOCATION (c);
+  unsigned i = *idx;
+  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;
+
+  switch (addr_tokens[i + 1]->u.access_kind)
+    {
+    case ACCESS_DIRECT:
+    case ACCESS_INDEXED_ARRAY:
+      break;
+
+    case ACCESS_REF:
+      {
+	/* Copy the referenced object.  Note that we also do this for !MAP_P
+	   clauses.  */
+	tree obj = convert_from_reference (addr_tokens[i + 1]->expr);
+	OMP_CLAUSE_DECL (c) = obj;
+	OMP_CLAUSE_SIZE (c) = TYPE_SIZE_UNIT (TREE_TYPE (obj));
+
+	if (!map_p)
+	  break;
+
+	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_SIZE (c2) = size_zero_node;
+      }
+      break;
+
+    case ACCESS_INDEXED_REF_TO_ARRAY:
+      {
+	if (!map_p)
+	  break;
+
+	tree virtual_origin
+	  = convert_from_reference (addr_tokens[i + 1]->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);
+
+	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_SIZE (c2)
+	  = fold_build2_loc (loc, MINUS_EXPR, ptrdiff_type_node,
+			     fold_convert_loc (loc, ptrdiff_type_node,
+					       data_addr),
+			     virtual_origin);
+      }
+      break;
+
+    case ACCESS_POINTER:
+    case ACCESS_POINTER_OFFSET:
+      {
+	if (!map_p)
+	  break;
+
+	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);
+
+	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_SIZE (c2)
+	  = fold_build2_loc (loc, MINUS_EXPR, ptrdiff_type_node,
+			     fold_convert_loc (loc, ptrdiff_type_node,
+					       data_addr),
+			     virtual_origin);
+      }
+      break;
+
+    case ACCESS_REF_TO_POINTER:
+    case ACCESS_REF_TO_POINTER_OFFSET:
+      {
+	if (!map_p)
+	  break;
+
+	tree ptr = convert_from_reference (addr_tokens[i + 1]->expr);
+	tree virtual_origin = fold_convert_loc (loc, ptrdiff_type_node,
+						ptr);
+	tree data_addr = omp_accessed_addr (addr_tokens, i + 1, expr);
+
+	/* Attach the pointer...  */
+	c2 = build_omp_clause (OMP_CLAUSE_LOCATION (c), OMP_CLAUSE_MAP);
+	OMP_CLAUSE_SET_MAP_KIND (c2, GOMP_MAP_ATTACH_DETACH);
+	OMP_CLAUSE_DECL (c2) = ptr;
+	OMP_CLAUSE_SIZE (c2)
+	  = fold_build2_loc (loc, MINUS_EXPR, ptrdiff_type_node,
+			     fold_convert_loc (loc, ptrdiff_type_node,
+					       data_addr),
+			     virtual_origin);
+
+	/* ...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_SIZE (c3) = size_zero_node;
+      }
+      break;
+
+    default:
+      *idx = i + 2;
+      return error_mark_node;
+    }
+
+  if (c3)
+    {
+      OMP_CLAUSE_CHAIN (c3) = OMP_CLAUSE_CHAIN (c);
+      OMP_CLAUSE_CHAIN (c2) = c3;
+      OMP_CLAUSE_CHAIN (c) = c2;
+      c = c3;
+    }
+  else if (c2)
+    {
+      OMP_CLAUSE_CHAIN (c2) = OMP_CLAUSE_CHAIN (c);
+      OMP_CLAUSE_CHAIN (c) = c2;
+      c = c2;
+    }
+
+  i += 2;
+  *idx = i;
+
+  if (chain_p && map_p)
+    return omp_expand_access_chain (c, expr, addr_tokens, idx);
+
+  return c;
+}
+
+/* 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,
+					    vec<omp_addr_token *> &addr_tokens,
+					    c_omp_region_type ort)
+{
+  using namespace omp_addr_tokenizer;
+  unsigned i, length = addr_tokens.length ();
+
+  for (i = 0; i < length;)
+    {
+      int remaining = length - i;
+
+      if (remaining >= 2
+	  && addr_tokens[i]->type == ARRAY_BASE
+	  && 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;
+	}
+      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;
+	}
+      else if (remaining >= 2
+	       && addr_tokens[i]->type == STRUCTURE_BASE
+	       && addr_tokens[i]->u.structure_base_kind == BASE_DECL
+	       && addr_tokens[i + 1]->type == ACCESS_METHOD)
+	{
+	  if (addr_tokens[i + 1]->u.access_kind == ACCESS_DIRECT)
+	    c_common_mark_addressable_vec (addr_tokens[i + 1]->expr);
+	  i += 2;
+	  while (addr_tokens[i]->type == ACCESS_METHOD)
+	    i++;
+	}
+      else if (remaining >= 2
+	       && addr_tokens[i]->type == STRUCTURE_BASE
+	       && addr_tokens[i]->u.structure_base_kind == BASE_ARBITRARY_EXPR
+	       && addr_tokens[i + 1]->type == ACCESS_METHOD)
+	{
+	  switch (addr_tokens[i + 1]->u.access_kind)
+	    {
+	    case ACCESS_DIRECT:
+	    case ACCESS_POINTER:
+	      i += 2;
+	      while (addr_tokens[i]->type == ACCESS_METHOD)
+		i++;
+	      break;
+	    default:
+	      return error_mark_node;
+	    }
+	}
+      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);
+	  /* We used 'expr', so these must have been the last tokens.  */
+	  gcc_assert (i == length);
+	  if (c == error_mark_node)
+	    return error_mark_node;
+	}
+      else if (remaining >= 3
+	       && addr_tokens[i]->type == COMPONENT_SELECTOR
+	       && addr_tokens[i + 1]->type == STRUCTURE_BASE
+	       && (addr_tokens[i + 1]->u.structure_base_kind
+		   == BASE_COMPONENT_EXPR)
+	       && addr_tokens[i + 2]->type == ACCESS_METHOD)
+	{
+	  i += 3;
+	  while (addr_tokens[i]->type == ACCESS_METHOD)
+	    i++;
+	}
+      else
+	break;
+    }
+
+  if (i == length)
+    return c;
+
+  return error_mark_node;
 }
 
 const struct c_omp_directive c_omp_directives[] = {
diff --git a/gcc/c/c-parser.cc b/gcc/c/c-parser.cc
index cabb18d04f7f..ae3a25737151 100644
--- a/gcc/c/c-parser.cc
+++ b/gcc/c/c-parser.cc
@@ -17659,7 +17659,8 @@ c_parser_omp_clause_detach (c_parser *parser, tree list)
 
 static tree
 c_parser_oacc_all_clauses (c_parser *parser, omp_clause_mask mask,
-			   const char *where, bool finish_p = true)
+			   const char *where, bool finish_p = true,
+			   bool target = false)
 {
   tree clauses = NULL;
   bool first = true;
@@ -17860,7 +17861,8 @@ c_parser_oacc_all_clauses (c_parser *parser, omp_clause_mask mask,
   c_parser_skip_to_pragma_eol (parser);
 
   if (finish_p)
-    return c_finish_omp_clauses (clauses, C_ORT_ACC);
+    return c_finish_omp_clauses (clauses, target ? C_ORT_ACC_TARGET
+						 : C_ORT_ACC);
 
   return clauses;
 }
@@ -18592,12 +18594,13 @@ c_parser_oacc_loop (location_t loc, c_parser *parser, char *p_name,
   mask |= OACC_LOOP_CLAUSE_MASK;
 
   tree clauses = c_parser_oacc_all_clauses (parser, mask, p_name,
-					    cclauses == NULL);
+					    /*finish_p=*/cclauses == NULL,
+					    /*target=*/is_parallel);
   if (cclauses)
     {
       clauses = c_oacc_split_loop_clauses (clauses, cclauses, is_parallel);
       if (*cclauses)
-	*cclauses = c_finish_omp_clauses (*cclauses, C_ORT_ACC);
+	*cclauses = c_finish_omp_clauses (*cclauses, C_ORT_ACC_TARGET);
       if (clauses)
 	clauses = c_finish_omp_clauses (clauses, C_ORT_ACC);
     }
@@ -18722,7 +18725,9 @@ c_parser_oacc_compute (location_t loc, c_parser *parser,
 	}
     }
 
-  tree clauses = c_parser_oacc_all_clauses (parser, mask, p_name);
+  tree clauses = c_parser_oacc_all_clauses (parser, mask, p_name,
+					    /*finish_p=*/true,
+					    /*target=*/true);
 
   tree block = c_begin_omp_parallel ();
   add_stmt (c_parser_omp_structured_block (parser, if_p));
@@ -22106,6 +22111,7 @@ c_parser_omp_target_enter_data (location_t loc, c_parser *parser,
 	  case GOMP_MAP_FIRSTPRIVATE_POINTER:
 	  case GOMP_MAP_ALWAYS_POINTER:
 	  case GOMP_MAP_ATTACH_DETACH:
+	  case GOMP_MAP_ATTACH:
 	    break;
 	  default:
 	    map_seen |= 1;
@@ -22215,6 +22221,7 @@ c_parser_omp_target_exit_data (location_t loc, c_parser *parser,
 	  case GOMP_MAP_FIRSTPRIVATE_POINTER:
 	  case GOMP_MAP_ALWAYS_POINTER:
 	  case GOMP_MAP_ATTACH_DETACH:
+	  case GOMP_MAP_DETACH:
 	    break;
 	  default:
 	    map_seen |= 1;
diff --git a/gcc/c/c-typeck.cc b/gcc/c/c-typeck.cc
index f29dbfe6526e..597b77181dc2 100644
--- a/gcc/c/c-typeck.cc
+++ b/gcc/c/c-typeck.cc
@@ -13634,10 +13634,12 @@ handle_omp_array_sections_1 (tree c, tree t, vec<tree> &types,
 			     enum c_omp_region_type ort)
 {
   tree ret, low_bound, length, type;
+  bool openacc = (ort & C_ORT_ACC) != 0;
   if (TREE_CODE (t) != TREE_LIST)
     {
       if (error_operand_p (t))
 	return error_mark_node;
+      c_omp_address_inspector ai (OMP_CLAUSE_LOCATION (c), t);
       ret = t;
       if (OMP_CLAUSE_CODE (c) != OMP_CLAUSE_AFFINITY
 	  && OMP_CLAUSE_CODE (c) != OMP_CLAUSE_DEPEND
@@ -13647,59 +13649,17 @@ handle_omp_array_sections_1 (tree c, tree t, vec<tree> &types,
 		    t, omp_clause_code_name[OMP_CLAUSE_CODE (c)]);
 	  return error_mark_node;
 	}
-      while (INDIRECT_REF_P (t))
-	{
-	  t = TREE_OPERAND (t, 0);
-	  STRIP_NOPS (t);
-	  if (TREE_CODE (t) == POINTER_PLUS_EXPR)
-	    t = TREE_OPERAND (t, 0);
-	}
-      while (TREE_CODE (t) == COMPOUND_EXPR)
-	{
-	  t = TREE_OPERAND (t, 1);
-	  STRIP_NOPS (t);
-	}
-      if (TREE_CODE (t) == COMPONENT_REF
-	  && (OMP_CLAUSE_CODE (c) == OMP_CLAUSE_MAP
-	      || OMP_CLAUSE_CODE (c) == OMP_CLAUSE_TO
-	      || OMP_CLAUSE_CODE (c) == OMP_CLAUSE_FROM))
-	{
-	  if (DECL_BIT_FIELD (TREE_OPERAND (t, 1)))
-	    {
-	      error_at (OMP_CLAUSE_LOCATION (c),
-			"bit-field %qE in %qs clause",
-			t, omp_clause_code_name[OMP_CLAUSE_CODE (c)]);
-	      return error_mark_node;
-	    }
-	  while (TREE_CODE (t) == COMPONENT_REF)
-	    {
-	      if (TREE_CODE (TREE_TYPE (TREE_OPERAND (t, 0))) == UNION_TYPE)
-		{
-		  error_at (OMP_CLAUSE_LOCATION (c),
-			    "%qE is a member of a union", t);
-		  return error_mark_node;
-		}
-	      t = TREE_OPERAND (t, 0);
-	      while (TREE_CODE (t) == MEM_REF
-		     || INDIRECT_REF_P (t)
-		     || TREE_CODE (t) == ARRAY_REF)
-		{
-		  t = TREE_OPERAND (t, 0);
-		  STRIP_NOPS (t);
-		  if (TREE_CODE (t) == POINTER_PLUS_EXPR)
-		    t = TREE_OPERAND (t, 0);
-		}
-	      if (ort == C_ORT_ACC && TREE_CODE (t) == MEM_REF)
-		{
-		  if (maybe_ne (mem_ref_offset (t), 0))
-		    error_at (OMP_CLAUSE_LOCATION (c),
-			      "cannot dereference %qE in %qs clause", t,
-			      omp_clause_code_name[OMP_CLAUSE_CODE (c)]);
-		  else
-		    t = TREE_OPERAND (t, 0);
-		}
-	    }
-	}
+      if (!ai.check_clause (c))
+	return error_mark_node;
+      else if (ai.component_access_p ()
+	       && (OMP_CLAUSE_CODE (c) == OMP_CLAUSE_MAP
+		   || OMP_CLAUSE_CODE (c) == OMP_CLAUSE_TO
+		   || OMP_CLAUSE_CODE (c) == OMP_CLAUSE_FROM))
+	t = ai.get_root_term (true);
+      else
+	t = ai.unconverted_ref_origin ();
+      if (t == error_mark_node)
+	return error_mark_node;
       if (!VAR_P (t) && TREE_CODE (t) != PARM_DECL)
 	{
 	  if (DECL_P (t))
@@ -13794,7 +13754,7 @@ handle_omp_array_sections_1 (tree c, tree t, vec<tree> &types,
 	{
 	  error_at (OMP_CLAUSE_LOCATION (c),
 		    "expected single pointer in %qs clause",
-		    user_omp_clause_code_name (c, ort == C_ORT_ACC));
+		    user_omp_clause_code_name (c, openacc));
 	  return error_mark_node;
 	}
     }
@@ -14231,55 +14191,25 @@ handle_omp_array_sections (tree c, enum c_omp_region_type ort)
       if (size)
 	size = c_fully_fold (size, false, NULL);
       OMP_CLAUSE_SIZE (c) = size;
-      if (OMP_CLAUSE_CODE (c) != OMP_CLAUSE_MAP
-	  || (TREE_CODE (t) == COMPONENT_REF
-	      && TREE_CODE (TREE_TYPE (t)) == ARRAY_TYPE))
+
+      if (OMP_CLAUSE_CODE (c) != OMP_CLAUSE_MAP)
 	return false;
-      gcc_assert (OMP_CLAUSE_MAP_KIND (c) != GOMP_MAP_FORCE_DEVICEPTR);
-      switch (OMP_CLAUSE_MAP_KIND (c))
+
+      auto_vec<omp_addr_token *, 10> addr_tokens;
+
+      if (!omp_parse_expr (addr_tokens, first))
+	return true;
+
+      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)
 	{
-	case GOMP_MAP_ALLOC:
-	case GOMP_MAP_IF_PRESENT:
-	case GOMP_MAP_TO:
-	case GOMP_MAP_FROM:
-	case GOMP_MAP_TOFROM:
-	case GOMP_MAP_ALWAYS_TO:
-	case GOMP_MAP_ALWAYS_FROM:
-	case GOMP_MAP_ALWAYS_TOFROM:
-	case GOMP_MAP_RELEASE:
-	case GOMP_MAP_DELETE:
-	case GOMP_MAP_FORCE_TO:
-	case GOMP_MAP_FORCE_FROM:
-	case GOMP_MAP_FORCE_TOFROM:
-	case GOMP_MAP_FORCE_PRESENT:
-	  OMP_CLAUSE_MAP_MAYBE_ZERO_LENGTH_ARRAY_SECTION (c) = 1;
-	  break;
-	default:
-	  break;
+	  if (ai.maybe_zero_length_array_section (c))
+	    OMP_CLAUSE_MAP_MAYBE_ZERO_LENGTH_ARRAY_SECTION (c) = 1;
+
+	  return false;
 	}
-      tree c2 = build_omp_clause (OMP_CLAUSE_LOCATION (c), OMP_CLAUSE_MAP);
-      if (TREE_CODE (t) == COMPONENT_REF)
-	OMP_CLAUSE_SET_MAP_KIND (c2, GOMP_MAP_ATTACH_DETACH);
-      else
-	OMP_CLAUSE_SET_MAP_KIND (c2, GOMP_MAP_FIRSTPRIVATE_POINTER);
-      OMP_CLAUSE_MAP_IMPLICIT (c2) = OMP_CLAUSE_MAP_IMPLICIT (c);
-      if (OMP_CLAUSE_MAP_KIND (c2) != GOMP_MAP_FIRSTPRIVATE_POINTER
-	  && !c_mark_addressable (t))
-	return false;
-      OMP_CLAUSE_DECL (c2) = t;
-      t = build_fold_addr_expr (first);
-      t = fold_convert_loc (OMP_CLAUSE_LOCATION (c), ptrdiff_type_node, t);
-      tree ptr = OMP_CLAUSE_DECL (c2);
-      if (!POINTER_TYPE_P (TREE_TYPE (ptr)))
-	ptr = build_fold_addr_expr (ptr);
-      t = fold_build2_loc (OMP_CLAUSE_LOCATION (c), MINUS_EXPR,
-			   ptrdiff_type_node, t,
-			   fold_convert_loc (OMP_CLAUSE_LOCATION (c),
-					     ptrdiff_type_node, ptr));
-      t = c_fully_fold (t, false, NULL);
-      OMP_CLAUSE_SIZE (c2) = t;
-      OMP_CLAUSE_CHAIN (c2) = OMP_CLAUSE_CHAIN (c);
-      OMP_CLAUSE_CHAIN (c) = c2;
     }
   return false;
 }
@@ -14545,7 +14475,6 @@ c_finish_omp_clauses (tree clauses, enum c_omp_region_type ort)
   tree ordered_clause = NULL_TREE;
   tree schedule_clause = NULL_TREE;
   bool oacc_async = false;
-  bool indir_component_ref_p = false;
   tree last_iterators = NULL_TREE;
   bool last_iterators_remove = false;
   tree *nogroup_seen = NULL;
@@ -14556,6 +14485,7 @@ c_finish_omp_clauses (tree clauses, enum c_omp_region_type ort)
   bool allocate_seen = false;
   bool implicit_moved = false;
   bool target_in_reduction_seen = false;
+  bool openacc = (ort & C_ORT_ACC) != 0;
 
   bitmap_obstack_initialize (NULL);
   bitmap_initialize (&generic_head, &bitmap_default_obstack);
@@ -14571,7 +14501,7 @@ c_finish_omp_clauses (tree clauses, enum c_omp_region_type ort)
   bitmap_initialize (&oacc_reduction_head, &bitmap_default_obstack);
   bitmap_initialize (&is_on_device_head, &bitmap_default_obstack);
 
-  if (ort & C_ORT_ACC)
+  if (openacc)
     for (c = clauses; c; c = OMP_CLAUSE_CHAIN (c))
       if (OMP_CLAUSE_CODE (c) == OMP_CLAUSE_ASYNC)
 	{
@@ -14965,8 +14895,7 @@ c_finish_omp_clauses (tree clauses, enum c_omp_region_type ort)
 			omp_clause_code_name[OMP_CLAUSE_CODE (c)]);
 	      remove = true;
 	    }
-	  else if ((ort == C_ORT_ACC
-		    && OMP_CLAUSE_CODE (c) == OMP_CLAUSE_REDUCTION)
+	  else if ((openacc && OMP_CLAUSE_CODE (c) == OMP_CLAUSE_REDUCTION)
 		   || (ort == C_ORT_OMP
 		       && (OMP_CLAUSE_CODE (c) == OMP_CLAUSE_USE_DEVICE_PTR
 			   || (OMP_CLAUSE_CODE (c)
@@ -14989,7 +14918,7 @@ c_finish_omp_clauses (tree clauses, enum c_omp_region_type ort)
 	      if (bitmap_bit_p (&oacc_reduction_head, DECL_UID (t)))
 		{
 		  error_at (OMP_CLAUSE_LOCATION (c),
-			    ort == C_ORT_ACC
+			    openacc
 			    ? "%qD appears more than once in reduction clauses"
 			    : "%qD appears more than once in data clauses",
 			    t);
@@ -15012,7 +14941,7 @@ c_finish_omp_clauses (tree clauses, enum c_omp_region_type ort)
 		    || OMP_CLAUSE_CODE (c) == OMP_CLAUSE_IS_DEVICE_PTR)
 		   && bitmap_bit_p (&map_head, DECL_UID (t)))
 	    {
-	      if (ort == C_ORT_ACC)
+	      if (openacc)
 		error_at (OMP_CLAUSE_LOCATION (c),
 			  "%qD appears more than once in data clauses", t);
 	      else
@@ -15077,9 +15006,10 @@ c_finish_omp_clauses (tree clauses, enum c_omp_region_type ort)
 			"%qE appears more than once in data clauses", t);
 	      remove = true;
 	    }
-	  else if (bitmap_bit_p (&map_head, DECL_UID (t)))
+	  else if (bitmap_bit_p (&map_head, DECL_UID (t))
+		   || bitmap_bit_p (&map_field_head, DECL_UID (t)))
 	    {
-	      if (ort == C_ORT_ACC)
+	      if (openacc)
 		error_at (OMP_CLAUSE_LOCATION (c),
 			  "%qD appears more than once in data clauses", t);
 	      else if (OMP_CLAUSE_FIRSTPRIVATE_IMPLICIT (c)
@@ -15347,6 +15277,9 @@ c_finish_omp_clauses (tree clauses, enum c_omp_region_type ort)
 	case OMP_CLAUSE_FROM:
 	case OMP_CLAUSE__CACHE_:
 	  {
+	    using namespace omp_addr_tokenizer;
+	    auto_vec<omp_addr_token *, 10> addr_tokens;
+
 	    t = OMP_CLAUSE_DECL (c);
 	    if (TREE_CODE (t) == TREE_LIST)
 	      {
@@ -15375,56 +15308,68 @@ c_finish_omp_clauses (tree clauses, enum c_omp_region_type ort)
 		      }
 		    while (TREE_CODE (t) == ARRAY_REF)
 		      t = TREE_OPERAND (t, 0);
-		    if (TREE_CODE (t) == COMPONENT_REF
-			&& TREE_CODE (TREE_TYPE (t)) == ARRAY_TYPE)
+
+		    c_omp_address_inspector ai (OMP_CLAUSE_LOCATION (c), t);
+
+		    if (!omp_parse_expr (addr_tokens, t))
 		      {
-			do
-			  {
-			    t = TREE_OPERAND (t, 0);
-			    if (TREE_CODE (t) == MEM_REF
-			      || INDIRECT_REF_P (t))
-			      {
-				t = TREE_OPERAND (t, 0);
-				STRIP_NOPS (t);
-				if (TREE_CODE (t) == POINTER_PLUS_EXPR)
-				  t = TREE_OPERAND (t, 0);
-			      }
-			  }
-			while (TREE_CODE (t) == COMPONENT_REF
-			       || TREE_CODE (t) == ARRAY_REF);
+			sorry_at (OMP_CLAUSE_LOCATION (c),
+				  "unsupported map expression %qE",
+				  OMP_CLAUSE_DECL (c));
+			remove = true;
+			break;
+		      }
+
+		    /* This check is to determine if this will be the only map
+		       node created for this clause.  Otherwise, we'll check
+		       the following FIRSTPRIVATE_POINTER or ATTACH_DETACH
+		       node on the next iteration(s) of the loop.   */
+		    if (addr_tokens.length () >= 4
+			&& addr_tokens[0]->type == STRUCTURE_BASE
+			&& addr_tokens[0]->u.structure_base_kind == BASE_DECL
+			&& addr_tokens[1]->type == ACCESS_METHOD
+			&& addr_tokens[2]->type == COMPONENT_SELECTOR
+			&& addr_tokens[3]->type == ACCESS_METHOD
+			&& (addr_tokens[3]->u.access_kind == ACCESS_DIRECT
+			    || (addr_tokens[3]->u.access_kind
+				== ACCESS_INDEXED_ARRAY)))
+		      {
+			tree rt = addr_tokens[1]->expr;
+
+			gcc_assert (DECL_P (rt));
 
 			if (OMP_CLAUSE_CODE (c) == OMP_CLAUSE_MAP
 			    && OMP_CLAUSE_MAP_IMPLICIT (c)
-			    && (bitmap_bit_p (&map_head, DECL_UID (t))
-				|| bitmap_bit_p (&map_field_head, DECL_UID (t))
+			    && (bitmap_bit_p (&map_head, DECL_UID (rt))
+				|| bitmap_bit_p (&map_field_head, DECL_UID (rt))
 				|| bitmap_bit_p (&map_firstprivate_head,
-						 DECL_UID (t))))
+						 DECL_UID (rt))))
 			  {
 			    remove = true;
 			    break;
 			  }
-			if (bitmap_bit_p (&map_field_head, DECL_UID (t)))
+			if (bitmap_bit_p (&map_field_head, DECL_UID (rt)))
 			  break;
-			if (bitmap_bit_p (&map_head, DECL_UID (t)))
+			if (bitmap_bit_p (&map_head, DECL_UID (rt)))
 			  {
 			    if (OMP_CLAUSE_CODE (c) != OMP_CLAUSE_MAP)
 			      error_at (OMP_CLAUSE_LOCATION (c),
 					"%qD appears more than once in motion "
-					"clauses", t);
-			    else if (ort == C_ORT_ACC)
+					"clauses", rt);
+			    else if (openacc)
 			      error_at (OMP_CLAUSE_LOCATION (c),
 					"%qD appears more than once in data "
-					"clauses", t);
+					"clauses", rt);
 			    else
 			      error_at (OMP_CLAUSE_LOCATION (c),
 					"%qD appears more than once in map "
-					"clauses", t);
+					"clauses", rt);
 			    remove = true;
 			  }
 			else
 			  {
-			    bitmap_set_bit (&map_head, DECL_UID (t));
-			    bitmap_set_bit (&map_field_head, DECL_UID (t));
+			    bitmap_set_bit (&map_head, DECL_UID (rt));
+			    bitmap_set_bit (&map_field_head, DECL_UID (rt));
 			  }
 		      }
 		  }
@@ -15441,6 +15386,14 @@ c_finish_omp_clauses (tree clauses, enum c_omp_region_type ort)
 		  OMP_CLAUSE_SIZE (c) = size_zero_node;
 		break;
 	      }
+	    else if (!omp_parse_expr (addr_tokens, t))
+	      {
+		sorry_at (OMP_CLAUSE_LOCATION (c),
+			  "unsupported map expression %qE",
+			  OMP_CLAUSE_DECL (c));
+		remove = true;
+		break;
+	      }
 	    if (t == error_mark_node)
 	      {
 		remove = true;
@@ -15459,96 +15412,41 @@ c_finish_omp_clauses (tree clauses, enum c_omp_region_type ort)
 		 bias) to zero here, so it is not set erroneously to the pointer
 		 size later on in gimplify.cc.  */
 	      OMP_CLAUSE_SIZE (c) = size_zero_node;
-	  while (INDIRECT_REF_P (t)
-		   || TREE_CODE (t) == ARRAY_REF)
+
+	    c_omp_address_inspector ai (OMP_CLAUSE_LOCATION (c), t);
+
+	    if (!ai.check_clause (c))
 	      {
-		t = TREE_OPERAND (t, 0);
-		STRIP_NOPS (t);
-		if (TREE_CODE (t) == POINTER_PLUS_EXPR)
-		  t = TREE_OPERAND (t, 0);
-	      }
-	    while (TREE_CODE (t) == COMPOUND_EXPR)
-	      {
-		t = TREE_OPERAND (t, 1);
-		STRIP_NOPS (t);
-	      }
-	    indir_component_ref_p = false;
-	    if (TREE_CODE (t) == COMPONENT_REF
-		&& (TREE_CODE (TREE_OPERAND (t, 0)) == MEM_REF
-		  || INDIRECT_REF_P (TREE_OPERAND (t, 0))
-		    || TREE_CODE (TREE_OPERAND (t, 0)) == ARRAY_REF))
-	      {
-		t = TREE_OPERAND (TREE_OPERAND (t, 0), 0);
-		indir_component_ref_p = true;
-		STRIP_NOPS (t);
-		if (TREE_CODE (t) == POINTER_PLUS_EXPR)
-		  t = TREE_OPERAND (t, 0);
+		remove = true;
+		break;
 	      }
 
-	    if (TREE_CODE (t) == COMPONENT_REF
-		&& OMP_CLAUSE_CODE (c) != OMP_CLAUSE__CACHE_)
+	    if (!ai.map_supported_p ())
 	      {
-		if (DECL_BIT_FIELD (TREE_OPERAND (t, 1)))
-		  {
-		    error_at (OMP_CLAUSE_LOCATION (c),
-			      "bit-field %qE in %qs clause",
-			      t, omp_clause_code_name[OMP_CLAUSE_CODE (c)]);
-		    remove = true;
-		  }
-		else if (!omp_mappable_type (TREE_TYPE (t)))
-		  {
-		    error_at (OMP_CLAUSE_LOCATION (c),
-			      "%qE does not have a mappable type in %qs clause",
-			      t, omp_clause_code_name[OMP_CLAUSE_CODE (c)]);
-		    remove = true;
-		  }
-		else if (TYPE_ATOMIC (TREE_TYPE (t)))
-		  {
-		    error_at (OMP_CLAUSE_LOCATION (c),
-			      "%<_Atomic%> %qE in %qs clause", t,
-			      omp_clause_code_name[OMP_CLAUSE_CODE (c)]);
-		    remove = true;
-		  }
-		while (TREE_CODE (t) == COMPONENT_REF)
-		  {
-		    if (TREE_CODE (TREE_TYPE (TREE_OPERAND (t, 0)))
-			== UNION_TYPE)
-		      {
-			error_at (OMP_CLAUSE_LOCATION (c),
-				  "%qE is a member of a union", t);
-			remove = true;
-			break;
-		      }
-		    t = TREE_OPERAND (t, 0);
-		    if (TREE_CODE (t) == MEM_REF)
-		      {
-			if (maybe_ne (mem_ref_offset (t), 0))
-			  error_at (OMP_CLAUSE_LOCATION (c),
-				    "cannot dereference %qE in %qs clause", t,
-				    omp_clause_code_name[OMP_CLAUSE_CODE (c)]);
-			else
-			  t = TREE_OPERAND (t, 0);
-		      }
-		    while (TREE_CODE (t) == MEM_REF
-			 || INDIRECT_REF_P (t)
-			   || TREE_CODE (t) == ARRAY_REF)
-		      {
-			t = TREE_OPERAND (t, 0);
-			STRIP_NOPS (t);
-			if (TREE_CODE (t) == POINTER_PLUS_EXPR)
-			  t = TREE_OPERAND (t, 0);
-		      }
-		  }
-		if (remove)
-		  break;
-		if (VAR_P (t) || TREE_CODE (t) == PARM_DECL)
-		  {
-		    if (bitmap_bit_p (&map_field_head, DECL_UID (t))
-			|| (ort != C_ORT_ACC
-			    && bitmap_bit_p (&map_head, DECL_UID (t))))
-		      break;
-		  }
+		sorry_at (OMP_CLAUSE_LOCATION (c),
+			  "unsupported map expression %qE",
+			  OMP_CLAUSE_DECL (c));
+		remove = true;
+		break;
 	      }
+
+	    gcc_assert ((addr_tokens[0]->type == ARRAY_BASE
+			 || addr_tokens[0]->type == STRUCTURE_BASE)
+			&& addr_tokens[1]->type == ACCESS_METHOD);
+
+	    t = addr_tokens[1]->expr;
+
+	    if (addr_tokens[0]->u.structure_base_kind != BASE_DECL)
+	      goto skip_decl_checks;
+
+	    /* For OpenMP, we can access a struct "t" and "t.d" on the same
+	       mapping.  OpenACC allows multiple fields of the same structure
+	       to be written.  */
+	    if (addr_tokens[0]->type == STRUCTURE_BASE
+		&& (bitmap_bit_p (&map_field_head, DECL_UID (t))
+		    || (!openacc && bitmap_bit_p (&map_head, DECL_UID (t)))))
+	      goto skip_decl_checks;
+
 	    if (!VAR_P (t) && TREE_CODE (t) != PARM_DECL)
 	      {
 		error_at (OMP_CLAUSE_LOCATION (c),
@@ -15566,7 +15464,6 @@ c_finish_omp_clauses (tree clauses, enum c_omp_region_type ort)
 	    else if ((OMP_CLAUSE_CODE (c) != OMP_CLAUSE_MAP
 		      || (OMP_CLAUSE_MAP_KIND (c)
 			  != GOMP_MAP_FIRSTPRIVATE_POINTER))
-		     && !indir_component_ref_p
 		     && !c_mark_addressable (t))
 	      remove = true;
 	    else if (!(OMP_CLAUSE_CODE (c) == OMP_CLAUSE_MAP
@@ -15612,15 +15509,11 @@ c_finish_omp_clauses (tree clauses, enum c_omp_region_type ort)
 		    remove = true;
 		  }
 		else if (bitmap_bit_p (&map_head, DECL_UID (t))
-			 && !bitmap_bit_p (&map_field_head, DECL_UID (t)))
+			 && !bitmap_bit_p (&map_field_head, DECL_UID (t))
+			 && openacc)
 		  {
-		    if (ort == C_ORT_ACC)
-		      error_at (OMP_CLAUSE_LOCATION (c),
-				"%qD appears more than once in data clauses",
-				t);
-		    else
-		      error_at (OMP_CLAUSE_LOCATION (c),
-				"%qD appears both in data and map clauses", t);
+		    error_at (OMP_CLAUSE_LOCATION (c),
+			      "%qD appears more than once in data clauses", t);
 		    remove = true;
 		  }
 		else
@@ -15632,7 +15525,7 @@ c_finish_omp_clauses (tree clauses, enum c_omp_region_type ort)
 		if (OMP_CLAUSE_CODE (c) != OMP_CLAUSE_MAP)
 		  error_at (OMP_CLAUSE_LOCATION (c),
 			    "%qD appears more than once in motion clauses", t);
-		else if (ort == C_ORT_ACC)
+		else if (openacc)
 		  error_at (OMP_CLAUSE_LOCATION (c),
 			    "%qD appears more than once in data clauses", t);
 		else
@@ -15640,8 +15533,7 @@ c_finish_omp_clauses (tree clauses, enum c_omp_region_type ort)
 			    "%qD appears more than once in map clauses", t);
 		remove = true;
 	      }
-	    else if (ort == C_ORT_ACC
-		     && bitmap_bit_p (&generic_head, DECL_UID (t)))
+	    else if (openacc && bitmap_bit_p (&generic_head, DECL_UID (t)))
 	      {
 		error_at (OMP_CLAUSE_LOCATION (c),
 			  "%qD appears more than once in data clauses", t);
@@ -15650,7 +15542,7 @@ c_finish_omp_clauses (tree clauses, enum c_omp_region_type ort)
 	    else if (bitmap_bit_p (&firstprivate_head, DECL_UID (t))
 		     || bitmap_bit_p (&is_on_device_head, DECL_UID (t)))
 	      {
-		if (ort == C_ORT_ACC)
+		if (openacc)
 		  error_at (OMP_CLAUSE_LOCATION (c),
 			    "%qD appears more than once in data clauses", t);
 		else
@@ -15658,13 +15550,37 @@ c_finish_omp_clauses (tree clauses, enum c_omp_region_type ort)
 			    "%qD appears both in data and map clauses", t);
 		remove = true;
 	      }
-	    else
+	    else if (!omp_access_chain_p (addr_tokens, 1))
 	      {
 		bitmap_set_bit (&map_head, DECL_UID (t));
 		if (t != OMP_CLAUSE_DECL (c)
 		    && TREE_CODE (OMP_CLAUSE_DECL (c)) == COMPONENT_REF)
 		  bitmap_set_bit (&map_field_head, DECL_UID (t));
 	      }
+
+	  skip_decl_checks:
+	    /* If we call omp_expand_map_clause in handle_omp_array_sections,
+	       the containing loop (here) iterates through the new nodes
+	       created by that expansion.  Avoid expanding those again (just
+	       by checking the node type).  */
+	    if (!remove
+		&& ort != C_ORT_DECLARE_SIMD
+		&& (OMP_CLAUSE_CODE (c) != OMP_CLAUSE_MAP
+		    || (OMP_CLAUSE_MAP_KIND (c) != GOMP_MAP_FIRSTPRIVATE_POINTER
+			&& (OMP_CLAUSE_MAP_KIND (c)
+			    != GOMP_MAP_FIRSTPRIVATE_REFERENCE)
+			&& OMP_CLAUSE_MAP_KIND (c) != GOMP_MAP_ALWAYS_POINTER
+			&& OMP_CLAUSE_MAP_KIND (c) != GOMP_MAP_ATTACH_DETACH
+			&& OMP_CLAUSE_MAP_KIND (c) != GOMP_MAP_ATTACH
+			&& OMP_CLAUSE_MAP_KIND (c) != GOMP_MAP_DETACH)))
+	      {
+		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;
+	      }
 	  }
 	  break;
 
@@ -15742,7 +15658,7 @@ c_finish_omp_clauses (tree clauses, enum c_omp_region_type ort)
 	  if (TREE_CODE (TREE_TYPE (t)) != POINTER_TYPE)
 	    {
 	      if (OMP_CLAUSE_CODE (c) == OMP_CLAUSE_USE_DEVICE_PTR
-		  && ort != C_ORT_ACC)
+		  && !openacc)
 		{
 		  error_at (OMP_CLAUSE_LOCATION (c),
 			    "%qs variable is not a pointer",
diff --git a/gcc/cp/parser.cc b/gcc/cp/parser.cc
index 589ac879c6d3..de655055be21 100644
--- a/gcc/cp/parser.cc
+++ b/gcc/cp/parser.cc
@@ -40962,7 +40962,7 @@ cp_parser_oacc_clause_async (cp_parser *parser, tree list)
 static tree
 cp_parser_oacc_all_clauses (cp_parser *parser, omp_clause_mask mask,
 			    const char *where, cp_token *pragma_tok,
-			    bool finish_p = true)
+			    bool finish_p = true, bool target = false)
 {
   tree clauses = NULL;
   bool first = true;
@@ -41172,7 +41172,7 @@ cp_parser_oacc_all_clauses (cp_parser *parser, omp_clause_mask mask,
   cp_parser_skip_to_pragma_eol (parser, pragma_tok);
 
   if (finish_p)
-    return finish_omp_clauses (clauses, C_ORT_ACC);
+    return finish_omp_clauses (clauses, target ? C_ORT_ACC_TARGET : C_ORT_ACC);
 
   return clauses;
 }
@@ -45437,6 +45437,7 @@ cp_parser_omp_target_enter_data (cp_parser *parser, cp_token *pragma_tok,
 	  case GOMP_MAP_FIRSTPRIVATE_REFERENCE:
 	  case GOMP_MAP_ALWAYS_POINTER:
 	  case GOMP_MAP_ATTACH_DETACH:
+	  case GOMP_MAP_ATTACH:
 	    break;
 	  default:
 	    map_seen |= 1;
@@ -45551,6 +45552,7 @@ cp_parser_omp_target_exit_data (cp_parser *parser, cp_token *pragma_tok,
 	  case GOMP_MAP_FIRSTPRIVATE_REFERENCE:
 	  case GOMP_MAP_ALWAYS_POINTER:
 	  case GOMP_MAP_ATTACH_DETACH:
+	  case GOMP_MAP_DETACH:
 	    break;
 	  default:
 	    map_seen |= 1;
@@ -45931,7 +45933,7 @@ cp_parser_oacc_declare (cp_parser *parser, cp_token *pragma_tok)
   bool found_in_scope = global_bindings_p ();
 
   clauses = cp_parser_oacc_all_clauses (parser, OACC_DECLARE_CLAUSE_MASK,
-					"#pragma acc declare", pragma_tok, true);
+					"#pragma acc declare", pragma_tok);
 
 
   if (omp_find_clause (clauses, OMP_CLAUSE_MAP) == NULL_TREE)
@@ -46179,12 +46181,13 @@ cp_parser_oacc_loop (cp_parser *parser, cp_token *pragma_tok, char *p_name,
   mask |= OACC_LOOP_CLAUSE_MASK;
 
   tree clauses = cp_parser_oacc_all_clauses (parser, mask, p_name, pragma_tok,
-					     cclauses == NULL);
+					     /*finish_p=*/cclauses == NULL,
+					     /*target=*/is_parallel);
   if (cclauses)
     {
       clauses = c_oacc_split_loop_clauses (clauses, cclauses, is_parallel);
       if (*cclauses)
-	*cclauses = finish_omp_clauses (*cclauses, C_ORT_ACC);
+	*cclauses = finish_omp_clauses (*cclauses, C_ORT_ACC_TARGET);
       if (clauses)
 	clauses = finish_omp_clauses (clauses, C_ORT_ACC);
     }
@@ -46309,7 +46312,9 @@ cp_parser_oacc_compute (cp_parser *parser, cp_token *pragma_tok,
 	}
     }
 
-  tree clauses = cp_parser_oacc_all_clauses (parser, mask, p_name, pragma_tok);
+  tree clauses = cp_parser_oacc_all_clauses (parser, mask, p_name, pragma_tok,
+					     /*finish_p=*/true,
+					     /*target=*/true);
 
   tree block = begin_omp_parallel ();
   unsigned int save = cp_parser_begin_omp_structured_block (parser);
diff --git a/gcc/cp/pt.cc b/gcc/cp/pt.cc
index 2706fa619bf8..ec76ac35217c 100644
--- a/gcc/cp/pt.cc
+++ b/gcc/cp/pt.cc
@@ -19440,8 +19440,8 @@ tsubst_expr (tree t, tree args, tsubst_flags_t complain, tree in_decl)
     case OACC_KERNELS:
     case OACC_PARALLEL:
     case OACC_SERIAL:
-      tmp = tsubst_omp_clauses (OMP_CLAUSES (t), C_ORT_ACC, args, complain,
-				in_decl);
+      tmp = tsubst_omp_clauses (OMP_CLAUSES (t), C_ORT_ACC_TARGET, args,
+				complain, in_decl);
       stmt = begin_omp_parallel ();
       RECUR (OMP_BODY (t));
       finish_omp_construct (TREE_CODE (t), stmt, tmp);
diff --git a/gcc/cp/semantics.cc b/gcc/cp/semantics.cc
index c9b031082510..30e85ed13c08 100644
--- a/gcc/cp/semantics.cc
+++ b/gcc/cp/semantics.cc
@@ -5199,6 +5199,54 @@ omp_privatize_field (tree t, bool shared)
   return v;
 }
 
+/* C++ specialisation of the c_omp_address_inspector class.  */
+
+class cp_omp_address_inspector : public c_omp_address_inspector
+{
+public:
+  cp_omp_address_inspector (location_t loc, tree t)
+    : c_omp_address_inspector (loc, t)
+    {
+    }
+
+  ~cp_omp_address_inspector ()
+    {
+    }
+
+  bool processing_template_decl_p ()
+    {
+      return processing_template_decl;
+    }
+
+  void emit_unmappable_type_notes (tree t)
+    {
+      if (TREE_TYPE (t) != error_mark_node
+	  && !COMPLETE_TYPE_P (TREE_TYPE (t)))
+	cxx_incomplete_type_inform (TREE_TYPE (t));
+    }
+
+  tree convert_from_reference (tree x)
+    {
+      return ::convert_from_reference (x);
+    }
+
+  tree build_array_ref (location_t loc, tree arr, tree idx)
+    {
+      return ::build_array_ref (loc, arr, idx);
+    }
+
+  bool check_clause (tree clause)
+    {
+      if (TREE_CODE (orig) == COMPONENT_REF
+	  && invalid_nonstatic_memfn_p (EXPR_LOCATION (orig), orig,
+					tf_warning_or_error))
+	return false;
+      if (!c_omp_address_inspector::check_clause (clause))
+	return false;
+      return true;
+    }
+};
+
 /* Helper function for handle_omp_array_sections.  Called recursively
    to handle multiple array-section-subscripts.  C is the clause,
    T current expression (initially OMP_CLAUSE_DECL), which is either
@@ -5225,63 +5273,27 @@ handle_omp_array_sections_1 (tree c, tree t, vec<tree> &types,
 			     enum c_omp_region_type ort)
 {
   tree ret, low_bound, length, type;
+  bool openacc = (ort & C_ORT_ACC) != 0;
   if (TREE_CODE (t) != TREE_LIST)
     {
       if (error_operand_p (t))
 	return error_mark_node;
-      if (REFERENCE_REF_P (t)
-	  && TREE_CODE (TREE_OPERAND (t, 0)) == COMPONENT_REF)
-	t = TREE_OPERAND (t, 0);
-      ret = t;
-      while (INDIRECT_REF_P (t))
-	{
-	  t = TREE_OPERAND (t, 0);
-	  STRIP_NOPS (t);
-	  if (TREE_CODE (t) == POINTER_PLUS_EXPR)
-	    t = TREE_OPERAND (t, 0);
-	}
-      while (TREE_CODE (t) == COMPOUND_EXPR)
-	{
-	  t = TREE_OPERAND (t, 1);
-	  STRIP_NOPS (t);
-	}
-      if (TREE_CODE (t) == COMPONENT_REF
-	  && (OMP_CLAUSE_CODE (c) == OMP_CLAUSE_MAP
-	      || OMP_CLAUSE_CODE (c) == OMP_CLAUSE_TO
-	      || OMP_CLAUSE_CODE (c) == OMP_CLAUSE_FROM)
-	  && !type_dependent_expression_p (t))
-	{
-	  if (TREE_CODE (TREE_OPERAND (t, 1)) == FIELD_DECL
-	      && DECL_BIT_FIELD (TREE_OPERAND (t, 1)))
-	    {
-	      error_at (OMP_CLAUSE_LOCATION (c),
-			"bit-field %qE in %qs clause",
-			t, omp_clause_code_name[OMP_CLAUSE_CODE (c)]);
-	      return error_mark_node;
-	    }
-	  while (TREE_CODE (t) == COMPONENT_REF)
-	    {
-	      if (TREE_TYPE (TREE_OPERAND (t, 0))
-		  && TREE_CODE (TREE_TYPE (TREE_OPERAND (t, 0))) == UNION_TYPE)
-		{
-		  error_at (OMP_CLAUSE_LOCATION (c),
-			    "%qE is a member of a union", t);
-		  return error_mark_node;
-		}
-	      t = TREE_OPERAND (t, 0);
-	      while (TREE_CODE (t) == MEM_REF
-		     || INDIRECT_REF_P (t)
-		     || TREE_CODE (t) == ARRAY_REF)
-		{
-		  t = TREE_OPERAND (t, 0);
-		  STRIP_NOPS (t);
-		  if (TREE_CODE (t) == POINTER_PLUS_EXPR)
-		    t = TREE_OPERAND (t, 0);
-		}
-	    }
-	  if (REFERENCE_REF_P (t))
-	    t = TREE_OPERAND (t, 0);
-	}
+
+      cp_omp_address_inspector ai (OMP_CLAUSE_LOCATION (c), t);
+      tree t_refto = ai.maybe_unconvert_ref (t);
+
+      if (!ai.check_clause (c))
+	return error_mark_node;
+      else if (ai.component_access_p ()
+	       && (OMP_CLAUSE_CODE (c) == OMP_CLAUSE_MAP
+		   || OMP_CLAUSE_CODE (c) == OMP_CLAUSE_TO
+		   || OMP_CLAUSE_CODE (c) == OMP_CLAUSE_FROM))
+	t = ai.get_root_term (true);
+      else
+	t = ai.unconverted_ref_origin ();
+      if (t == error_mark_node)
+	return error_mark_node;
+      ret = t_refto;
       if (TREE_CODE (t) == FIELD_DECL)
 	ret = finish_non_static_data_member (t, NULL_TREE, NULL_TREE);
       else if (!VAR_P (t) && TREE_CODE (t) != PARM_DECL)
@@ -5378,7 +5390,7 @@ handle_omp_array_sections_1 (tree c, tree t, vec<tree> &types,
 	{
 	  error_at (OMP_CLAUSE_LOCATION (c),
 		    "expected single pointer in %qs clause",
-		    user_omp_clause_code_name (c, ort == C_ORT_ACC));
+		    user_omp_clause_code_name (c, openacc));
 	  return error_mark_node;
 	}
     }
@@ -5615,7 +5627,7 @@ 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 &c, enum c_omp_region_type ort)
 {
   bool maybe_zero_len = false;
   unsigned int first_non_one = 0;
@@ -5826,115 +5838,66 @@ handle_omp_array_sections (tree c, enum c_omp_region_type ort)
 	  OMP_CLAUSE_SIZE (c) = size;
 	  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
-	      || (TREE_CODE (t) == COMPONENT_REF
-		  && TREE_CODE (TREE_TYPE (t)) == ARRAY_TYPE))
+
+	  if (OMP_CLAUSE_CODE (c) != OMP_CLAUSE_MAP)
 	    return false;
-	  switch (OMP_CLAUSE_MAP_KIND (c))
-	    {
-	    case GOMP_MAP_ALLOC:
-	    case GOMP_MAP_IF_PRESENT:
-	    case GOMP_MAP_TO:
-	    case GOMP_MAP_FROM:
-	    case GOMP_MAP_TOFROM:
-	    case GOMP_MAP_ALWAYS_TO:
-	    case GOMP_MAP_ALWAYS_FROM:
-	    case GOMP_MAP_ALWAYS_TOFROM:
-	    case GOMP_MAP_PRESENT_ALLOC:
-	    case GOMP_MAP_PRESENT_TO:
-	    case GOMP_MAP_PRESENT_FROM:
-	    case GOMP_MAP_PRESENT_TOFROM:
-	    case GOMP_MAP_ALWAYS_PRESENT_TO:
-	    case GOMP_MAP_ALWAYS_PRESENT_FROM:
-	    case GOMP_MAP_ALWAYS_PRESENT_TOFROM:
-	    case GOMP_MAP_RELEASE:
-	    case GOMP_MAP_DELETE:
-	    case GOMP_MAP_FORCE_TO:
-	    case GOMP_MAP_FORCE_FROM:
-	    case GOMP_MAP_FORCE_TOFROM:
-	    case GOMP_MAP_FORCE_PRESENT:
-	      OMP_CLAUSE_MAP_MAYBE_ZERO_LENGTH_ARRAY_SECTION (c) = 1;
-	      break;
-	    default:
-	      break;
-	    }
-	  bool reference_always_pointer = true;
-	  tree c2 = build_omp_clause (OMP_CLAUSE_LOCATION (c),
-				      OMP_CLAUSE_MAP);
-	  if (TREE_CODE (t) == COMPONENT_REF)
-	    {
-	      OMP_CLAUSE_SET_MAP_KIND (c2, GOMP_MAP_ATTACH_DETACH);
 
-	      if ((ort & C_ORT_OMP_DECLARE_SIMD) == C_ORT_OMP
-		  && TYPE_REF_P (TREE_TYPE (t)))
+	  if (TREE_CODE (first) == INDIRECT_REF)
+	    {
+	      /* Detect and skip adding extra nodes for pointer-to-member
+		 mappings.  These are unsupported for now.  */
+	      tree tmp = TREE_OPERAND (first, 0);
+
+	      if (TREE_CODE (tmp) == NON_LVALUE_EXPR)
+		tmp = TREE_OPERAND (tmp, 0);
+
+	      if (TREE_CODE (tmp) == INDIRECT_REF)
+		tmp = TREE_OPERAND (tmp, 0);
+
+	      if (TREE_CODE (tmp) == POINTER_PLUS_EXPR)
 		{
-		  if (TREE_CODE (TREE_TYPE (TREE_TYPE (t))) == ARRAY_TYPE)
-		    OMP_CLAUSE_SET_MAP_KIND (c2, GOMP_MAP_ALWAYS_POINTER);
-		  else
-		    t = convert_from_reference (t);
-
-		  reference_always_pointer = false;
+		  tree offset = TREE_OPERAND (tmp, 1);
+		  STRIP_NOPS (offset);
+		  if (TYPE_PTRMEM_P (TREE_TYPE (offset)))
+		    {
+		      sorry_at (OMP_CLAUSE_LOCATION (c),
+				"pointer-to-member mapping %qE not supported",
+				OMP_CLAUSE_DECL (c));
+		      return true;
+		    }
 		}
 	    }
-	  else if (REFERENCE_REF_P (t)
-		   && TREE_CODE (TREE_OPERAND (t, 0)) == COMPONENT_REF)
-	    {
-	      gomp_map_kind k;
-	      if ((ort & C_ORT_OMP_DECLARE_SIMD) == C_ORT_OMP
-		  && TREE_CODE (TREE_TYPE (t)) == POINTER_TYPE)
-		k = GOMP_MAP_ATTACH_DETACH;
-	      else
-		{
-		  t = TREE_OPERAND (t, 0);
-		  k = (ort == C_ORT_ACC
-		       ? GOMP_MAP_ATTACH_DETACH : GOMP_MAP_ALWAYS_POINTER);
-		}
-	      OMP_CLAUSE_SET_MAP_KIND (c2, k);
-	    }
-	  else
-	    OMP_CLAUSE_SET_MAP_KIND (c2, GOMP_MAP_FIRSTPRIVATE_POINTER);
-	  OMP_CLAUSE_MAP_IMPLICIT (c2) = OMP_CLAUSE_MAP_IMPLICIT (c);
-	  if (OMP_CLAUSE_MAP_KIND (c2) != GOMP_MAP_FIRSTPRIVATE_POINTER
-	      && !cxx_mark_addressable (t))
-	    return false;
-	  OMP_CLAUSE_DECL (c2) = t;
-	  t = build_fold_addr_expr (first);
-	  t = fold_convert_loc (OMP_CLAUSE_LOCATION (c),
-				ptrdiff_type_node, t);
-	  tree ptr = OMP_CLAUSE_DECL (c2);
-	  ptr = convert_from_reference (ptr);
-	  if (!INDIRECT_TYPE_P (TREE_TYPE (ptr)))
-	    ptr = build_fold_addr_expr (ptr);
-	  t = fold_build2_loc (OMP_CLAUSE_LOCATION (c), MINUS_EXPR,
-			       ptrdiff_type_node, t,
-			       fold_convert_loc (OMP_CLAUSE_LOCATION (c),
-						 ptrdiff_type_node, ptr));
-	  OMP_CLAUSE_SIZE (c2) = t;
-	  OMP_CLAUSE_CHAIN (c2) = OMP_CLAUSE_CHAIN (c);
-	  OMP_CLAUSE_CHAIN (c) = c2;
 
-	  ptr = OMP_CLAUSE_DECL (c2);
-	  if (reference_always_pointer
-	      && OMP_CLAUSE_MAP_KIND (c2) != GOMP_MAP_FIRSTPRIVATE_POINTER
-	      && TYPE_REF_P (TREE_TYPE (ptr))
-	      && INDIRECT_TYPE_P (TREE_TYPE (TREE_TYPE (ptr))))
+	  /* 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]".  */
+
+	  auto_vec<omp_addr_token *, 10> addr_tokens;
+
+	  if (!omp_parse_expr (addr_tokens, first))
+	    return true;
+
+	  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 c3 = build_omp_clause (OMP_CLAUSE_LOCATION (c),
-					  OMP_CLAUSE_MAP);
-	      OMP_CLAUSE_SET_MAP_KIND (c3, OMP_CLAUSE_MAP_KIND (c2));
-	      OMP_CLAUSE_MAP_IMPLICIT (c2) = OMP_CLAUSE_MAP_IMPLICIT (c);
-	      OMP_CLAUSE_DECL (c3) = ptr;
-	      if (OMP_CLAUSE_MAP_KIND (c2) == GOMP_MAP_ALWAYS_POINTER
-		  || OMP_CLAUSE_MAP_KIND (c2) == GOMP_MAP_ATTACH_DETACH)
-		{
-		  OMP_CLAUSE_DECL (c2) = build_simple_mem_ref (ptr);
-		  OMP_CLAUSE_SET_MAP_KIND (c2, GOMP_MAP_ALWAYS_POINTER);
-		}
-	      else
-		OMP_CLAUSE_DECL (c2) = convert_from_reference (ptr);
-	      OMP_CLAUSE_SIZE (c3) = size_zero_node;
-	      OMP_CLAUSE_CHAIN (c3) = OMP_CLAUSE_CHAIN (c2);
-	      OMP_CLAUSE_CHAIN (c2) = c3;
+	      using namespace omp_addr_tokenizer;
+
+	      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
+		   || 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;
+
+	      return false;
 	    }
 	}
     }
@@ -6862,6 +6825,7 @@ finish_omp_clauses (tree clauses, enum c_omp_region_type ort)
   bitmap_head oacc_reduction_head, is_on_device_head;
   tree c, t, *pc;
   tree safelen = NULL_TREE;
+  bool openacc = (ort & C_ORT_ACC) != 0;
   bool branch_seen = false;
   bool copyprivate_seen = false;
   bool ordered_seen = false;
@@ -6894,7 +6858,7 @@ finish_omp_clauses (tree clauses, enum c_omp_region_type ort)
   bitmap_initialize (&oacc_reduction_head, &bitmap_default_obstack);
   bitmap_initialize (&is_on_device_head, &bitmap_default_obstack);
 
-  if (ort & C_ORT_ACC)
+  if (openacc)
     for (c = clauses; c; c = OMP_CLAUSE_CHAIN (c))
       if (OMP_CLAUSE_CODE (c) == OMP_CLAUSE_ASYNC)
 	{
@@ -7143,7 +7107,7 @@ finish_omp_clauses (tree clauses, enum c_omp_region_type ort)
 	    t = OMP_CLAUSE_DECL (c);
 	check_dup_generic_t:
 	  if (t == current_class_ptr
-	      && ((ort != C_ORT_OMP_DECLARE_SIMD && ort != C_ORT_ACC)
+	      && ((ort != C_ORT_OMP_DECLARE_SIMD && !openacc)
 		  || (OMP_CLAUSE_CODE (c) != OMP_CLAUSE_LINEAR
 		      && OMP_CLAUSE_CODE (c) != OMP_CLAUSE_UNIFORM)))
 	    {
@@ -7168,7 +7132,7 @@ finish_omp_clauses (tree clauses, enum c_omp_region_type ort)
 			  omp_clause_code_name[OMP_CLAUSE_CODE (c)]);
 	      remove = true;
 	    }
-	  else if ((ort == C_ORT_ACC
+	  else if ((openacc
 		    && OMP_CLAUSE_CODE (c) == OMP_CLAUSE_REDUCTION)
 		   || (ort == C_ORT_OMP
 		       && (OMP_CLAUSE_CODE (c) == OMP_CLAUSE_USE_DEVICE_PTR
@@ -7192,7 +7156,7 @@ finish_omp_clauses (tree clauses, enum c_omp_region_type ort)
 	      if (bitmap_bit_p (&oacc_reduction_head, DECL_UID (t)))
 		{
 		  error_at (OMP_CLAUSE_LOCATION (c),
-			    ort == C_ORT_ACC
+			    openacc
 			    ? "%qD appears more than once in reduction clauses"
 			    : "%qD appears more than once in data clauses",
 			    t);
@@ -7215,7 +7179,7 @@ finish_omp_clauses (tree clauses, enum c_omp_region_type ort)
 		    || OMP_CLAUSE_CODE (c) == OMP_CLAUSE_IS_DEVICE_PTR)
 		   && bitmap_bit_p (&map_head, DECL_UID (t)))
 	    {
-	      if (ort == C_ORT_ACC)
+	      if (openacc)
 		error_at (OMP_CLAUSE_LOCATION (c),
 			  "%qD appears more than once in data clauses", t);
 	      else
@@ -7277,7 +7241,7 @@ finish_omp_clauses (tree clauses, enum c_omp_region_type ort)
 	    omp_note_field_privatization (t, OMP_CLAUSE_DECL (c));
 	  else
 	    t = OMP_CLAUSE_DECL (c);
-	  if (ort != C_ORT_ACC && t == current_class_ptr)
+	  if (!openacc && t == current_class_ptr)
 	    {
 	      error_at (OMP_CLAUSE_LOCATION (c),
 			"%<this%> allowed in OpenMP only in %<declare simd%>"
@@ -7313,9 +7277,10 @@ finish_omp_clauses (tree clauses, enum c_omp_region_type ort)
 			"%qD appears more than once in data clauses", t);
 	      remove = true;
 	    }
-	  else if (bitmap_bit_p (&map_head, DECL_UID (t)))
+	  else if (bitmap_bit_p (&map_head, DECL_UID (t))
+		   || bitmap_bit_p (&map_field_head, DECL_UID (t)))
 	    {
-	      if (ort == C_ORT_ACC)
+	      if (openacc)
 		error_at (OMP_CLAUSE_LOCATION (c),
 			  "%qD appears more than once in data clauses", t);
 	      else if (OMP_CLAUSE_FIRSTPRIVATE_IMPLICIT (c)
@@ -7336,7 +7301,7 @@ finish_omp_clauses (tree clauses, enum c_omp_region_type ort)
 	    omp_note_field_privatization (t, OMP_CLAUSE_DECL (c));
 	  else
 	    t = OMP_CLAUSE_DECL (c);
-	  if (ort != C_ORT_ACC && t == current_class_ptr)
+	  if (!openacc && t == current_class_ptr)
 	    {
 	      error_at (OMP_CLAUSE_LOCATION (c),
 			"%<this%> allowed in OpenMP only in %<declare simd%>"
@@ -8134,6 +8099,9 @@ finish_omp_clauses (tree clauses, enum c_omp_region_type ort)
 	case OMP_CLAUSE_FROM:
 	case OMP_CLAUSE__CACHE_:
 	  {
+	    using namespace omp_addr_tokenizer;
+	    auto_vec<omp_addr_token *, 10> addr_tokens;
+
 	    t = OMP_CLAUSE_DECL (c);
 	    if (TREE_CODE (t) == TREE_LIST)
 	      {
@@ -8160,58 +8128,73 @@ finish_omp_clauses (tree clauses, enum c_omp_region_type ort)
 		      }
 		    while (TREE_CODE (t) == ARRAY_REF)
 		      t = TREE_OPERAND (t, 0);
-		    if (TREE_CODE (t) == COMPONENT_REF
-			&& TREE_CODE (TREE_TYPE (t)) == ARRAY_TYPE)
+
+		    if (type_dependent_expression_p (t))
+		      break;
+
+		    cp_omp_address_inspector ai (OMP_CLAUSE_LOCATION (c), t);
+
+		    if (!ai.map_supported_p ()
+			|| !omp_parse_expr (addr_tokens, t))
 		      {
-			do
-			  {
-			    t = TREE_OPERAND (t, 0);
-			    if (REFERENCE_REF_P (t))
-			      t = TREE_OPERAND (t, 0);
-			    if (TREE_CODE (t) == MEM_REF
-				|| INDIRECT_REF_P (t))
-			      {
-				t = TREE_OPERAND (t, 0);
-				STRIP_NOPS (t);
-				if (TREE_CODE (t) == POINTER_PLUS_EXPR)
-				  t = TREE_OPERAND (t, 0);
-			      }
-			  }
-			while (TREE_CODE (t) == COMPONENT_REF
-			       || TREE_CODE (t) == ARRAY_REF);
+			sorry_at (OMP_CLAUSE_LOCATION (c),
+				  "unsupported map expression %qE",
+				  OMP_CLAUSE_DECL (c));
+			remove = true;
+			break;
+		      }
+
+		    /* This check is to determine if this will be the only map
+		       node created for this clause.  Otherwise, we'll check
+		       the following FIRSTPRIVATE_POINTER,
+		       FIRSTPRIVATE_REFERENCE or ATTACH_DETACH node on the next
+		       iteration(s) of the loop.   */
+		    if (addr_tokens.length () >= 4
+			&& addr_tokens[0]->type == STRUCTURE_BASE
+			&& addr_tokens[0]->u.structure_base_kind == BASE_DECL
+			&& addr_tokens[1]->type == ACCESS_METHOD
+			&& addr_tokens[2]->type == COMPONENT_SELECTOR
+			&& addr_tokens[3]->type == ACCESS_METHOD
+			&& (addr_tokens[3]->u.access_kind == ACCESS_DIRECT
+			    || (addr_tokens[3]->u.access_kind
+				== ACCESS_INDEXED_ARRAY)))
+		      {
+			tree rt = addr_tokens[1]->expr;
+
+			gcc_assert (DECL_P (rt));
 
 			if (OMP_CLAUSE_CODE (c) == OMP_CLAUSE_MAP
 			    && OMP_CLAUSE_MAP_IMPLICIT (c)
-			    && (bitmap_bit_p (&map_head, DECL_UID (t))
-				|| bitmap_bit_p (&map_field_head, DECL_UID (t))
+			    && (bitmap_bit_p (&map_head, DECL_UID (rt))
+				|| bitmap_bit_p (&map_field_head, DECL_UID (rt))
 				|| bitmap_bit_p (&map_firstprivate_head,
-						 DECL_UID (t))))
+						 DECL_UID (rt))))
 			  {
 			    remove = true;
 			    break;
 			  }
-			if (bitmap_bit_p (&map_field_head, DECL_UID (t)))
+			if (bitmap_bit_p (&map_field_head, DECL_UID (rt)))
 			  break;
-			if (bitmap_bit_p (&map_head, DECL_UID (t)))
+			if (bitmap_bit_p (&map_head, DECL_UID (rt)))
 			  {
 			    if (OMP_CLAUSE_CODE (c) != OMP_CLAUSE_MAP)
 			      error_at (OMP_CLAUSE_LOCATION (c),
 					"%qD appears more than once in motion"
-					" clauses", t);
-			    else if (ort == C_ORT_ACC)
+					" clauses", rt);
+			    else if (openacc)
 			      error_at (OMP_CLAUSE_LOCATION (c),
 					"%qD appears more than once in data"
-					" clauses", t);
+					" clauses", rt);
 			    else
 			      error_at (OMP_CLAUSE_LOCATION (c),
 					"%qD appears more than once in map"
-					" clauses", t);
+					" clauses", rt);
 			    remove = true;
 			  }
 			else
 			  {
-			    bitmap_set_bit (&map_head, DECL_UID (t));
-			    bitmap_set_bit (&map_field_head, DECL_UID (t));
+			    bitmap_set_bit (&map_head, DECL_UID (rt));
+			    bitmap_set_bit (&map_field_head, DECL_UID (rt));
 			  }
 		      }
 		  }
@@ -8228,6 +8211,16 @@ finish_omp_clauses (tree clauses, enum c_omp_region_type ort)
 		  OMP_CLAUSE_SIZE (c) = size_zero_node;
 		break;
 	      }
+	    else if (type_dependent_expression_p (t))
+	      break;
+	    else if (!omp_parse_expr (addr_tokens, t))
+	      {
+		sorry_at (OMP_CLAUSE_LOCATION (c),
+			  "unsupported map expression %qE",
+			  OMP_CLAUSE_DECL (c));
+		remove = true;
+		break;
+	      }
 	    if (t == error_mark_node)
 	      {
 		remove = true;
@@ -8246,110 +8239,49 @@ finish_omp_clauses (tree clauses, enum c_omp_region_type ort)
 		 bias) to zero here, so it is not set erroneously to the
 		 pointer size later on in gimplify.cc.  */
 	      OMP_CLAUSE_SIZE (c) = size_zero_node;
-	    if (REFERENCE_REF_P (t)
-		&& TREE_CODE (TREE_OPERAND (t, 0)) == COMPONENT_REF)
+
+	    cp_omp_address_inspector ai (OMP_CLAUSE_LOCATION (c), t);
+
+	    if (!ai.check_clause (c))
 	      {
-		t = TREE_OPERAND (t, 0);
-		if (OMP_CLAUSE_CODE (c) == OMP_CLAUSE_MAP
-		    && OMP_CLAUSE_MAP_KIND (c) != GOMP_MAP_ATTACH_DETACH)
-		  OMP_CLAUSE_DECL (c) = t;
+		remove = true;
+		break;
 	      }
-	    while (INDIRECT_REF_P (t)
-		   || TREE_CODE (t) == ARRAY_REF)
+
+	    if (!ai.map_supported_p ())
 	      {
-		t = TREE_OPERAND (t, 0);
-		STRIP_NOPS (t);
-		if (TREE_CODE (t) == POINTER_PLUS_EXPR)
-		  t = TREE_OPERAND (t, 0);
+		sorry_at (OMP_CLAUSE_LOCATION (c),
+			  "unsupported map expression %qE",
+			  OMP_CLAUSE_DECL (c));
+		remove = true;
+		break;
 	      }
-	    while (TREE_CODE (t) == COMPOUND_EXPR)
-	      {
-		t = TREE_OPERAND (t, 1);
-		STRIP_NOPS (t);
-	      }
-	    if (TREE_CODE (t) == COMPONENT_REF
-		&& invalid_nonstatic_memfn_p (EXPR_LOCATION (t), t,
-					      tf_warning_or_error))
-	      remove = true;
-	    indir_component_ref_p = false;
-	    if (TREE_CODE (t) == COMPONENT_REF
-		&& (INDIRECT_REF_P (TREE_OPERAND (t, 0))
-		    || TREE_CODE (TREE_OPERAND (t, 0)) == ARRAY_REF))
-	      {
-		t = TREE_OPERAND (TREE_OPERAND (t, 0), 0);
-		indir_component_ref_p = true;
-		STRIP_NOPS (t);
-		if (TREE_CODE (t) == POINTER_PLUS_EXPR)
-		  t = TREE_OPERAND (t, 0);
-	      }
-	    if (TREE_CODE (t) == COMPONENT_REF
-		&& OMP_CLAUSE_CODE (c) != OMP_CLAUSE__CACHE_)
-	      {
-		if (type_dependent_expression_p (t))
-		  break;
-		if (TREE_CODE (TREE_OPERAND (t, 1)) == FIELD_DECL
-		    && DECL_BIT_FIELD (TREE_OPERAND (t, 1)))
-		  {
-		    error_at (OMP_CLAUSE_LOCATION (c),
-			      "bit-field %qE in %qs clause",
-			      t, omp_clause_code_name[OMP_CLAUSE_CODE (c)]);
-		    remove = true;
-		  }
-		else if (!omp_mappable_type (TREE_TYPE (t)))
-		  {
-		    error_at (OMP_CLAUSE_LOCATION (c),
-			      "%qE does not have a mappable type in %qs clause",
-			      t, omp_clause_code_name[OMP_CLAUSE_CODE (c)]);
-		    if (TREE_TYPE (t) != error_mark_node
-			&& !COMPLETE_TYPE_P (TREE_TYPE (t)))
-		      cxx_incomplete_type_inform (TREE_TYPE (t));
-		    remove = true;
-		  }
-		while (TREE_CODE (t) == COMPONENT_REF)
-		  {
-		    if (TREE_TYPE (TREE_OPERAND (t, 0))
-			&& (TREE_CODE (TREE_TYPE (TREE_OPERAND (t, 0)))
-			    == UNION_TYPE))
-		      {
-			error_at (OMP_CLAUSE_LOCATION (c),
-				  "%qE is a member of a union", t);
-			remove = true;
-			break;
-		      }
-		    t = TREE_OPERAND (t, 0);
-		    if (TREE_CODE (t) == MEM_REF)
-		      {
-			if (maybe_ne (mem_ref_offset (t), 0))
-			  error_at (OMP_CLAUSE_LOCATION (c),
-				    "cannot dereference %qE in %qs clause", t,
-				    omp_clause_code_name[OMP_CLAUSE_CODE (c)]);
-			else
-			  t = TREE_OPERAND (t, 0);
-		      }
-		    while (TREE_CODE (t) == MEM_REF
-			   || INDIRECT_REF_P (t)
-			   || TREE_CODE (t) == ARRAY_REF)
-		      {
-			t = TREE_OPERAND (t, 0);
-			STRIP_NOPS (t);
-			if (TREE_CODE (t) == POINTER_PLUS_EXPR)
-			  t = TREE_OPERAND (t, 0);
-		      }
-		  }
-		if (remove)
-		  break;
-		if (REFERENCE_REF_P (t))
-		  t = TREE_OPERAND (t, 0);
-		if (VAR_P (t) || TREE_CODE (t) == PARM_DECL)
-		  {
-		    if (bitmap_bit_p (&map_field_head, DECL_UID (t))
-			|| (ort != C_ORT_ACC
-			    && bitmap_bit_p (&map_head, DECL_UID (t))))
-		      goto handle_map_references;
-		  }
-	      }
-	    if (!processing_template_decl
-		&& TREE_CODE (t) == FIELD_DECL)
+
+	    gcc_assert ((addr_tokens[0]->type == ARRAY_BASE
+			 || addr_tokens[0]->type == STRUCTURE_BASE)
+			&& addr_tokens[1]->type == ACCESS_METHOD);
+
+	    t = addr_tokens[1]->expr;
+
+	    /* This is used to prevent cxx_mark_addressable from being called
+	       on 'this' for expressions like 'this->a', i.e. typical member
+	       accesses.  */
+	    indir_component_ref_p
+	      = (addr_tokens[0]->type == STRUCTURE_BASE
+		 && addr_tokens[1]->u.access_kind != ACCESS_DIRECT);
+
+	    if (addr_tokens[0]->u.structure_base_kind != BASE_DECL)
+	      goto skip_decl_checks;
+
+	    /* For OpenMP, we can access a struct "t" and "t.d" on the same
+	       mapping.  OpenACC allows multiple fields of the same structure
+	       to be written.  */
+	    if (addr_tokens[0]->type == STRUCTURE_BASE
+		&& (bitmap_bit_p (&map_field_head, DECL_UID (t))
+		    || (!openacc && bitmap_bit_p (&map_head, DECL_UID (t)))))
+	      goto skip_decl_checks;
+
+	    if (!processing_template_decl && TREE_CODE (t) == FIELD_DECL)
 	      {
 		OMP_CLAUSE_DECL (c)
 		  = finish_non_static_data_member (t, NULL_TREE, NULL_TREE);
@@ -8387,12 +8319,17 @@ finish_omp_clauses (tree clauses, enum c_omp_region_type ort)
 			 || (OMP_CLAUSE_MAP_KIND (c)
 			     != GOMP_MAP_FIRSTPRIVATE_POINTER))
 		     && !indir_component_ref_p
+		     && (t != current_class_ptr
+			 || OMP_CLAUSE_CODE (c) != OMP_CLAUSE_MAP
+			 || OMP_CLAUSE_MAP_KIND (c) != GOMP_MAP_ATTACH_DETACH)
 		     && !cxx_mark_addressable (t))
 	      remove = true;
 	    else if (!(OMP_CLAUSE_CODE (c) == OMP_CLAUSE_MAP
 		       && (OMP_CLAUSE_MAP_KIND (c) == GOMP_MAP_POINTER
 			   || (OMP_CLAUSE_MAP_KIND (c)
-			       == GOMP_MAP_FIRSTPRIVATE_POINTER)))
+			       == GOMP_MAP_FIRSTPRIVATE_POINTER)
+			   || (OMP_CLAUSE_MAP_KIND (c)
+			       == GOMP_MAP_ATTACH_DETACH)))
 		     && t == OMP_CLAUSE_DECL (c)
 		     && !type_dependent_expression_p (t)
 		     && !omp_mappable_type (TYPE_REF_P (TREE_TYPE (t))
@@ -8436,27 +8373,28 @@ finish_omp_clauses (tree clauses, enum c_omp_region_type ort)
 		    remove = true;
 		  }
 		else if (bitmap_bit_p (&map_head, DECL_UID (t))
-			 && !bitmap_bit_p (&map_field_head, DECL_UID (t)))
+			 && !bitmap_bit_p (&map_field_head, DECL_UID (t))
+			 && openacc)
 		  {
-		    if (ort == C_ORT_ACC)
-		      error_at (OMP_CLAUSE_LOCATION (c),
-				"%qD appears more than once in data clauses",
-				t);
-		    else
-		      error_at (OMP_CLAUSE_LOCATION (c),
-				"%qD appears both in data and map clauses", t);
+		    error_at (OMP_CLAUSE_LOCATION (c),
+			      "%qD appears more than once in data clauses", t);
 		    remove = true;
 		  }
 		else
 		  bitmap_set_bit (&map_firstprivate_head, DECL_UID (t));
 	      }
+	    else if (OMP_CLAUSE_CODE (c) == OMP_CLAUSE_MAP
+		     && (OMP_CLAUSE_MAP_KIND (c)
+			 == GOMP_MAP_FIRSTPRIVATE_REFERENCE))
+	      bitmap_set_bit (&map_firstprivate_head, DECL_UID (t));
 	    else if (bitmap_bit_p (&map_head, DECL_UID (t))
-		     && !bitmap_bit_p (&map_field_head, DECL_UID (t)))
+		     && !bitmap_bit_p (&map_field_head, DECL_UID (t))
+		     && ort != C_ORT_OMP)
 	      {
 		if (OMP_CLAUSE_CODE (c) != OMP_CLAUSE_MAP)
 		  error_at (OMP_CLAUSE_LOCATION (c),
 			    "%qD appears more than once in motion clauses", t);
-		else if (ort == C_ORT_ACC)
+		else if (openacc)
 		  error_at (OMP_CLAUSE_LOCATION (c),
 			    "%qD appears more than once in data clauses", t);
 		else
@@ -8464,8 +8402,7 @@ finish_omp_clauses (tree clauses, enum c_omp_region_type ort)
 			    "%qD appears more than once in map clauses", t);
 		remove = true;
 	      }
-	    else if (ort == C_ORT_ACC
-		     && bitmap_bit_p (&generic_head, DECL_UID (t)))
+	    else if (openacc && bitmap_bit_p (&generic_head, DECL_UID (t)))
 	      {
 		error_at (OMP_CLAUSE_LOCATION (c),
 			  "%qD appears more than once in data clauses", t);
@@ -8474,7 +8411,7 @@ finish_omp_clauses (tree clauses, enum c_omp_region_type ort)
 	    else if (bitmap_bit_p (&firstprivate_head, DECL_UID (t))
 		     || bitmap_bit_p (&is_on_device_head, DECL_UID (t)))
 	      {
-		if (ort == C_ORT_ACC)
+		if (openacc)
 		  error_at (OMP_CLAUSE_LOCATION (c),
 			    "%qD appears more than once in data clauses", t);
 		else
@@ -8482,7 +8419,7 @@ finish_omp_clauses (tree clauses, enum c_omp_region_type ort)
 			    "%qD appears both in data and map clauses", t);
 		remove = true;
 	      }
-	    else
+	    else if (!omp_access_chain_p (addr_tokens, 1))
 	      {
 		bitmap_set_bit (&map_head, DECL_UID (t));
 
@@ -8496,49 +8433,30 @@ finish_omp_clauses (tree clauses, enum c_omp_region_type ort)
 								    0))))))
 		  bitmap_set_bit (&map_field_head, DECL_UID (t));
 	      }
-	  handle_map_references:
+
+	  skip_decl_checks:
+	    /* If we call ai.expand_map_clause in handle_omp_array_sections,
+	       the containing loop (here) iterates through the new nodes
+	       created by that expansion.  Avoid expanding those again (just
+	       by checking the node type).  */
 	    if (!remove
 		&& !processing_template_decl
 		&& ort != C_ORT_DECLARE_SIMD
-		&& TYPE_REF_P (TREE_TYPE (OMP_CLAUSE_DECL (c))))
+		&& (OMP_CLAUSE_CODE (c) != OMP_CLAUSE_MAP
+		    || (OMP_CLAUSE_MAP_KIND (c) != GOMP_MAP_FIRSTPRIVATE_POINTER
+			&& (OMP_CLAUSE_MAP_KIND (c)
+			    != GOMP_MAP_FIRSTPRIVATE_REFERENCE)
+			&& OMP_CLAUSE_MAP_KIND (c) != GOMP_MAP_ALWAYS_POINTER
+			&& OMP_CLAUSE_MAP_KIND (c) != GOMP_MAP_ATTACH_DETACH
+			&& OMP_CLAUSE_MAP_KIND (c) != GOMP_MAP_ATTACH
+			&& OMP_CLAUSE_MAP_KIND (c) != GOMP_MAP_DETACH)))
 	      {
-		t = OMP_CLAUSE_DECL (c);
-		if (OMP_CLAUSE_CODE (c) != OMP_CLAUSE_MAP)
-		  {
-		    OMP_CLAUSE_DECL (c) = build_simple_mem_ref (t);
-		    if (OMP_CLAUSE_SIZE (c) == NULL_TREE)
-		      OMP_CLAUSE_SIZE (c)
-			= TYPE_SIZE_UNIT (TREE_TYPE (TREE_TYPE (t)));
-		  }
-		else if (OMP_CLAUSE_MAP_KIND (c)
-			 != GOMP_MAP_FIRSTPRIVATE_POINTER
-			 && (OMP_CLAUSE_MAP_KIND (c)
-			     != GOMP_MAP_FIRSTPRIVATE_REFERENCE)
-			 && (OMP_CLAUSE_MAP_KIND (c)
-			     != GOMP_MAP_ALWAYS_POINTER)
-			 && (OMP_CLAUSE_MAP_KIND (c)
-			     != GOMP_MAP_ATTACH_DETACH))
-		  {
-		    grp_start_p = pc;
-		    grp_sentinel = OMP_CLAUSE_CHAIN (c);
-
-		    tree c2 = build_omp_clause (OMP_CLAUSE_LOCATION (c),
-						OMP_CLAUSE_MAP);
-		    if (TREE_CODE (t) == COMPONENT_REF)
-		      OMP_CLAUSE_SET_MAP_KIND (c2, GOMP_MAP_ALWAYS_POINTER);
-		    else
-		      OMP_CLAUSE_SET_MAP_KIND (c2,
-					       GOMP_MAP_FIRSTPRIVATE_REFERENCE);
-		    OMP_CLAUSE_DECL (c2) = t;
-		    OMP_CLAUSE_SIZE (c2) = size_zero_node;
-		    OMP_CLAUSE_CHAIN (c2) = OMP_CLAUSE_CHAIN (c);
-		    OMP_CLAUSE_CHAIN (c) = c2;
-		    OMP_CLAUSE_DECL (c) = build_simple_mem_ref (t);
-		    if (OMP_CLAUSE_SIZE (c) == NULL_TREE)
-		      OMP_CLAUSE_SIZE (c)
-			= TYPE_SIZE_UNIT (TREE_TYPE (TREE_TYPE (t)));
-		    c = c2;
-		  }
+		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;
 	      }
 	  }
 	  break;
@@ -8937,7 +8855,8 @@ finish_omp_clauses (tree clauses, enum c_omp_region_type ort)
 	  if (grp_start_p)
 	    {
 	      /* If we found a clause to remove, we want to remove the whole
-		 expanded group, otherwise gimplify can get confused.  */
+		 expanded group, otherwise gimplify
+		 (omp_resolve_clause_dependencies) can get confused.  */
 	      *grp_start_p = grp_sentinel;
 	      pc = grp_start_p;
 	      grp_start_p = NULL;
diff --git a/gcc/fortran/trans-openmp.cc b/gcc/fortran/trans-openmp.cc
index cf741cebf91b..46f80d086fd5 100644
--- a/gcc/fortran/trans-openmp.cc
+++ b/gcc/fortran/trans-openmp.cc
@@ -2405,8 +2405,8 @@ static vec<tree, va_heap, vl_embed> *doacross_steps;
 static void
 gfc_trans_omp_array_section (stmtblock_t *block, gfc_exec_op op,
 			     gfc_omp_namelist *n, tree decl, bool element,
-			     gomp_map_kind ptr_kind, tree &node, tree &node2,
-			     tree &node3, tree &node4)
+			     bool openmp, gomp_map_kind ptr_kind, tree &node,
+			     tree &node2, tree &node3, tree &node4)
 {
   gfc_se se;
   tree ptr, ptr2;
@@ -2529,7 +2529,7 @@ gfc_trans_omp_array_section (stmtblock_t *block, gfc_exec_op op,
 	 struct - and adding an 'alloc: for the 'desc.data' pointer, which
 	 would break as the 'desc' (the descriptor) is also mapped
 	 (see node4 above).  */
-      if (ptr_kind == GOMP_MAP_ATTACH_DETACH)
+      if (ptr_kind == GOMP_MAP_ATTACH_DETACH && !openmp)
 	STRIP_NOPS (OMP_CLAUSE_DECL (node3));
     }
   else
@@ -2547,7 +2547,7 @@ gfc_trans_omp_array_section (stmtblock_t *block, gfc_exec_op op,
 			       decl, offset, NULL_TREE, NULL_TREE);
 	  OMP_CLAUSE_DECL (node) = offset;
 
-	  if (ptr_kind == GOMP_MAP_ALWAYS_POINTER)
+	  if (ptr_kind == GOMP_MAP_ATTACH_DETACH && openmp)
 	    return;
 	}
       else
@@ -3524,8 +3524,9 @@ gfc_trans_omp_clauses (stmtblock_t *block, gfc_omp_clauses *clauses,
 		      && !(POINTER_TYPE_P (type)
 			   && GFC_DESCRIPTOR_TYPE_P (TREE_TYPE (type))))
 		    k = GOMP_MAP_FIRSTPRIVATE_POINTER;
-		  gfc_trans_omp_array_section (block, op, n, decl, element, k,
-					       node, node2, node3, node4);
+		  gfc_trans_omp_array_section (block, op, n, decl, element,
+					       !openacc, k, node, node2,
+					       node3, node4);
 		}
 	      else if (n->expr
 		       && n->expr->expr_type == EXPR_VARIABLE
@@ -3564,10 +3565,7 @@ gfc_trans_omp_clauses (stmtblock_t *block, gfc_omp_clauses *clauses,
 
 		      node2 = build_omp_clause (input_location,
 						OMP_CLAUSE_MAP);
-		      gomp_map_kind kind
-			= (openacc ? GOMP_MAP_ATTACH_DETACH
-				   : GOMP_MAP_ALWAYS_POINTER);
-		      OMP_CLAUSE_SET_MAP_KIND (node2, kind);
+		      OMP_CLAUSE_SET_MAP_KIND (node2, GOMP_MAP_ATTACH_DETACH);
 		      OMP_CLAUSE_DECL (node2)
 			= POINTER_TYPE_P (TREE_TYPE (se.expr))
 			  ? se.expr
@@ -3585,6 +3583,7 @@ gfc_trans_omp_clauses (stmtblock_t *block, gfc_omp_clauses *clauses,
 					   fold_convert (size_type_node,
 					       se.string_length),
 					   TYPE_SIZE_UNIT (tmp));
+			  gomp_map_kind kind;
 			  if (n->u.map_op == OMP_MAP_DELETE)
 			    kind = GOMP_MAP_DELETE;
 			  else if (op == EXEC_OMP_TARGET_EXIT_DATA)
@@ -3671,9 +3670,7 @@ gfc_trans_omp_clauses (stmtblock_t *block, gfc_omp_clauses *clauses,
 			  node2 = build_omp_clause (input_location,
 						    OMP_CLAUSE_MAP);
 			  OMP_CLAUSE_SET_MAP_KIND (node2,
-						   openacc
-						   ? GOMP_MAP_ATTACH_DETACH
-						   : GOMP_MAP_ALWAYS_POINTER);
+						   GOMP_MAP_ATTACH_DETACH);
 			  OMP_CLAUSE_DECL (node2) = build_fold_addr_expr (data);
 			  OMP_CLAUSE_SIZE (node2) = size_int (0);
 			}
@@ -3774,9 +3771,7 @@ gfc_trans_omp_clauses (stmtblock_t *block, gfc_omp_clauses *clauses,
 			  node3 = build_omp_clause (input_location,
 						    OMP_CLAUSE_MAP);
 			  OMP_CLAUSE_SET_MAP_KIND (node3,
-						   openacc
-						   ? GOMP_MAP_ATTACH_DETACH
-						   : GOMP_MAP_ALWAYS_POINTER);
+						   GOMP_MAP_ATTACH_DETACH);
 			  OMP_CLAUSE_DECL (node3)
 			    = gfc_conv_descriptor_data_get (inner);
 			  /* Similar to gfc_trans_omp_array_section (details
@@ -3799,11 +3794,10 @@ gfc_trans_omp_clauses (stmtblock_t *block, gfc_omp_clauses *clauses,
 		    {
 		      /* An array element or section.  */
 		      bool element = lastref->u.ar.type == AR_ELEMENT;
-		      gomp_map_kind kind = (openacc ? GOMP_MAP_ATTACH_DETACH
-						    : GOMP_MAP_ALWAYS_POINTER);
+		      gomp_map_kind kind = GOMP_MAP_ATTACH_DETACH;
 		      gfc_trans_omp_array_section (block, op, n, inner, element,
-						   kind, node, node2, node3,
-						   node4);
+						   !openacc, kind, node, node2,
+						   node3, node4);
 		    }
 		  else
 		    gcc_unreachable ();
diff --git a/gcc/gimplify.cc b/gcc/gimplify.cc
index 320920ed74c0..14d88b520913 100644
--- a/gcc/gimplify.cc
+++ b/gcc/gimplify.cc
@@ -8922,8 +8922,7 @@ build_omp_struct_comp_nodes (enum tree_code code, tree grp_start, tree grp_end,
 
   if (grp_mid
       && OMP_CLAUSE_CODE (grp_mid) == OMP_CLAUSE_MAP
-      && (OMP_CLAUSE_MAP_KIND (grp_mid) == GOMP_MAP_ALWAYS_POINTER
-	  || OMP_CLAUSE_MAP_KIND (grp_mid) == GOMP_MAP_ATTACH_DETACH))
+      && OMP_CLAUSE_MAP_KIND (grp_mid) == GOMP_MAP_ALWAYS_POINTER)
     {
       tree c3
 	= build_omp_clause (OMP_CLAUSE_LOCATION (grp_end), OMP_CLAUSE_MAP);
@@ -9019,6 +9018,12 @@ struct omp_mapping_group {
   /* If we've removed the group but need to reindex, mark the group as
      deleted.  */
   bool deleted;
+  /* The group points to an already-created "GOMP_MAP_STRUCT
+     GOMP_MAP_ATTACH_DETACH" pair.  */
+  bool reprocess_struct;
+  /* The group should use "zero-length" allocations for pointers that are not
+     mapped "to" on the same directive.  */
+  bool fragile;
   struct omp_mapping_group *sibling;
   struct omp_mapping_group *next;
 };
@@ -9060,38 +9065,6 @@ omp_get_base_pointer (tree expr)
   return NULL_TREE;
 }
 
-/* Remove COMPONENT_REFS and indirections from EXPR.  */
-
-static tree
-omp_strip_components_and_deref (tree expr)
-{
-  while (TREE_CODE (expr) == COMPONENT_REF
-	 || INDIRECT_REF_P (expr)
-	 || (TREE_CODE (expr) == MEM_REF
-	     && integer_zerop (TREE_OPERAND (expr, 1)))
-	 || TREE_CODE (expr) == POINTER_PLUS_EXPR
-	 || TREE_CODE (expr) == COMPOUND_EXPR)
-      if (TREE_CODE (expr) == COMPOUND_EXPR)
-	expr = TREE_OPERAND (expr, 1);
-      else
-	expr = TREE_OPERAND (expr, 0);
-
-  STRIP_NOPS (expr);
-
-  return expr;
-}
-
-static tree
-omp_strip_indirections (tree expr)
-{
-  while (INDIRECT_REF_P (expr)
-	 || (TREE_CODE (expr) == MEM_REF
-	     && integer_zerop (TREE_OPERAND (expr, 1))))
-    expr = TREE_OPERAND (expr, 0);
-
-  return expr;
-}
-
 /* An attach or detach operation depends directly on the address being
    attached/detached.  Return that address, or none if there are no
    attachments/detachments.  */
@@ -9145,6 +9118,7 @@ omp_get_attachment (omp_mapping_group *grp)
 
 	  case GOMP_MAP_ATTACH_DETACH:
 	  case GOMP_MAP_ATTACH_ZERO_LENGTH_ARRAY_SECTION:
+	  case GOMP_MAP_DETACH:
 	    return OMP_CLAUSE_DECL (node);
 
 	  default:
@@ -9220,23 +9194,43 @@ omp_group_last (tree *start_p)
 		     == GOMP_MAP_POINTER_TO_ZERO_LENGTH_ARRAY_SECTION)
 		 || (OMP_CLAUSE_MAP_KIND (nc)
 		     == GOMP_MAP_ATTACH_ZERO_LENGTH_ARRAY_SECTION)
+		 || OMP_CLAUSE_MAP_KIND (nc) == GOMP_MAP_DETACH
 		 || OMP_CLAUSE_MAP_KIND (nc) == GOMP_MAP_ALWAYS_POINTER
 		 || OMP_CLAUSE_MAP_KIND (nc) == GOMP_MAP_TO_PSET))
 	{
-	  grp_last_p = &OMP_CLAUSE_CHAIN (c);
-	  c = nc;
 	  tree nc2 = OMP_CLAUSE_CHAIN (nc);
+	  if (OMP_CLAUSE_MAP_KIND (nc) == GOMP_MAP_DETACH)
+	    {
+	      /* In the specific case we're doing "exit data" on an array
+		 slice of a reference-to-pointer struct component, we will see
+		 DETACH followed by ATTACH_DETACH here.  We want to treat that
+		 as a single group. In other cases DETACH might represent a
+		 stand-alone "detach" clause, so we don't want to consider
+		 that part of the group.  */
+	      if (nc2
+		  && OMP_CLAUSE_CODE (nc2) == OMP_CLAUSE_MAP
+		  && OMP_CLAUSE_MAP_KIND (nc2) == GOMP_MAP_ATTACH_DETACH)
+		goto consume_two_nodes;
+	      else
+		break;
+	    }
 	  if (nc2
 	      && OMP_CLAUSE_CODE (nc2) == OMP_CLAUSE_MAP
 	      && (OMP_CLAUSE_MAP_KIND (nc)
 		  == GOMP_MAP_POINTER_TO_ZERO_LENGTH_ARRAY_SECTION)
 	      && OMP_CLAUSE_MAP_KIND (nc2) == GOMP_MAP_ATTACH)
 	    {
+	    consume_two_nodes:
 	      grp_last_p = &OMP_CLAUSE_CHAIN (nc);
 	      c = nc2;
-	      nc2 = OMP_CLAUSE_CHAIN (nc2);
+	      nc = OMP_CLAUSE_CHAIN (nc2);
+	    }
+	  else
+	    {
+	      grp_last_p = &OMP_CLAUSE_CHAIN (c);
+	      c = nc;
+	      nc = nc2;
 	    }
-	   nc = nc2;
 	}
       break;
 
@@ -9300,6 +9294,8 @@ omp_gather_mapping_groups_1 (tree *list_p, vec<omp_mapping_group> *groups,
       grp.mark = UNVISITED;
       grp.sibling = NULL;
       grp.deleted = false;
+      grp.reprocess_struct = false;
+      grp.fragile = false;
       grp.next = NULL;
       groups->safe_push (grp);
 
@@ -9386,6 +9382,7 @@ omp_group_base (omp_mapping_group *grp, unsigned int *chained,
 	  case GOMP_MAP_ALWAYS_POINTER:
 	  case GOMP_MAP_ATTACH_DETACH:
 	  case GOMP_MAP_ATTACH_ZERO_LENGTH_ARRAY_SECTION:
+	  case GOMP_MAP_DETACH:
 	    return *grp->grp_start;
 
 	  default:
@@ -9434,6 +9431,8 @@ omp_group_base (omp_mapping_group *grp, unsigned int *chained,
 	    *firstprivate = OMP_CLAUSE_DECL (node);
 	    node = OMP_CLAUSE_CHAIN (node);
 	  }
+	else if (OMP_CLAUSE_MAP_KIND (node) == GOMP_MAP_ATTACH_DETACH)
+	  node = OMP_CLAUSE_CHAIN (node);
 	*chained = num_mappings;
 	return node;
       }
@@ -9485,6 +9484,9 @@ omp_index_mapping_groups_1 (hash_map<tree_operand_hash_no_se,
       if (reindexing && !above_hwm)
 	continue;
 
+      if (grp->reprocess_struct)
+	continue;
+
       tree fpp;
       unsigned int chained;
       tree node = omp_group_base (grp, &chained, &fpp);
@@ -9977,6 +9979,89 @@ omp_lastprivate_for_combined_outer_constructs (struct gimplify_omp_ctx *octx,
     omp_notice_variable (octx, decl, true);
 }
 
+/* We might have indexed several groups for DECL, e.g. a "TO" mapping and also
+   a "FIRSTPRIVATE" mapping.  Return the one that isn't firstprivate, etc.  */
+
+static omp_mapping_group *
+omp_get_nonfirstprivate_group (hash_map<tree_operand_hash_no_se,
+					omp_mapping_group *> *grpmap,
+			       tree decl, bool allow_deleted = false)
+{
+  omp_mapping_group **to_group_p = grpmap->get (decl);
+
+  if (!to_group_p)
+    return NULL;
+
+  omp_mapping_group *to_group = *to_group_p;
+
+  for (; to_group; to_group = to_group->sibling)
+    {
+      tree grp_end = to_group->grp_end;
+      switch (OMP_CLAUSE_MAP_KIND (grp_end))
+	{
+	case GOMP_MAP_FIRSTPRIVATE_POINTER:
+	case GOMP_MAP_FIRSTPRIVATE_REFERENCE:
+	  break;
+
+	default:
+	  if (allow_deleted || !to_group->deleted)
+	    return to_group;
+	}
+    }
+
+  return NULL;
+}
+
+/* Return TRUE if the directive (whose clauses are described by the hash table
+   of mapping groups, GRPMAP) maps DECL explicitly.  If TO_SPECIFICALLY is
+   true, only count TO mappings.  If ALLOW_DELETED is true, ignore the
+   "deleted" flag for groups.  If CONTAINED_IN_STRUCT is true, also return
+   TRUE if DECL is mapped as a member of a whole-struct mapping.  */
+
+static bool
+omp_directive_maps_explicitly (hash_map<tree_operand_hash_no_se,
+					omp_mapping_group *> *grpmap,
+			       tree decl, omp_mapping_group **base_group,
+			       bool to_specifically, bool allow_deleted,
+			       bool contained_in_struct)
+{
+  omp_mapping_group *decl_group
+    = omp_get_nonfirstprivate_group (grpmap, decl, allow_deleted);
+
+  *base_group = NULL;
+
+  if (decl_group)
+    {
+      tree grp_first = *decl_group->grp_start;
+      /* We might be called during omp_build_struct_sibling_lists, when
+	 GOMP_MAP_STRUCT might have been inserted at the start of the group.
+	 Skip over that, and also possibly the node after it.  */
+      if (OMP_CLAUSE_MAP_KIND (grp_first) == GOMP_MAP_STRUCT)
+	{
+	  grp_first = OMP_CLAUSE_CHAIN (grp_first);
+	  if (OMP_CLAUSE_MAP_KIND (grp_first) == GOMP_MAP_FIRSTPRIVATE_POINTER
+	      || (OMP_CLAUSE_MAP_KIND (grp_first)
+		  == GOMP_MAP_FIRSTPRIVATE_REFERENCE)
+	      || OMP_CLAUSE_MAP_KIND (grp_first) == GOMP_MAP_ATTACH_DETACH)
+	    grp_first = OMP_CLAUSE_CHAIN (grp_first);
+	}
+      enum gomp_map_kind first_kind = OMP_CLAUSE_MAP_KIND (grp_first);
+      if (!to_specifically
+	  || GOMP_MAP_COPY_TO_P (first_kind)
+	  || first_kind == GOMP_MAP_ALLOC)
+	{
+	  *base_group = decl_group;
+	  return true;
+	}
+    }
+
+  if (contained_in_struct
+      && omp_mapped_by_containing_struct (grpmap, decl, base_group))
+    return true;
+
+  return false;
+}
+
 /* If we have mappings INNER and OUTER, where INNER is a component access and
    OUTER is a mapping of the whole containing struct, check that the mappings
    are compatible.  We'll be deleting the inner mapping, so we need to make
@@ -10010,18 +10095,23 @@ omp_check_mapping_compatibility (location_t loc,
 
     case GOMP_MAP_ALWAYS_FROM:
       if (inner_kind == GOMP_MAP_FORCE_PRESENT
-	  || inner_kind == GOMP_MAP_ALLOC
+	  || inner_kind == GOMP_MAP_RELEASE
 	  || inner_kind == GOMP_MAP_FROM)
 	return true;
       break;
 
     case GOMP_MAP_TO:
-    case GOMP_MAP_FROM:
       if (inner_kind == GOMP_MAP_FORCE_PRESENT
 	  || inner_kind == GOMP_MAP_ALLOC)
 	return true;
       break;
 
+    case GOMP_MAP_FROM:
+      if (inner_kind == GOMP_MAP_RELEASE
+	  || inner_kind == GOMP_MAP_FORCE_PRESENT)
+	return true;
+      break;
+
     case GOMP_MAP_ALWAYS_TOFROM:
     case GOMP_MAP_TOFROM:
       if (inner_kind == GOMP_MAP_FORCE_PRESENT
@@ -10043,6 +10133,257 @@ omp_check_mapping_compatibility (location_t loc,
   return false;
 }
 
+/* This function handles several cases where clauses on a mapping directive
+   can interact with each other.
+
+   If we have a FIRSTPRIVATE_POINTER node and we're also mapping the pointer
+   on the same directive, change the mapping of the first node to
+   ATTACH_DETACH.  We should have detected that this will happen already in
+   c-omp.cc:c_omp_adjust_map_clauses and marked the appropriate decl
+   as addressable.  (If we didn't, bail out.)
+
+   If we have a FIRSTPRIVATE_REFERENCE (for a reference to pointer) and we're
+   mapping the base pointer also, we may need to change the mapping type to
+   ATTACH_DETACH and synthesize an alloc node for the reference itself.
+
+   If we have an ATTACH_DETACH node, this is an array section with a pointer
+   base.  If we're mapping the base on the same directive too, we can drop its
+   mapping.  However, if we have a reference to pointer, make other appropriate
+   adjustments to the mapping nodes instead.
+
+   If we have a component access but we're also mapping the whole of the
+   containing struct, drop the former access.
+
+   If the expression is a component access, and we're also mapping a base
+   pointer used in that component access in the same expression, change the
+   mapping type of the latter to ALLOC (ready for processing by
+   omp_build_struct_sibling_lists).  */
+
+void
+omp_resolve_clause_dependencies (enum tree_code code,
+				 vec<omp_mapping_group> *groups,
+				 hash_map<tree_operand_hash_no_se,
+					  omp_mapping_group *> *grpmap)
+{
+  int i;
+  omp_mapping_group *grp;
+  bool repair_chain = false;
+
+  FOR_EACH_VEC_ELT (*groups, i, grp)
+    {
+      tree grp_end = grp->grp_end;
+      tree decl = OMP_CLAUSE_DECL (grp_end);
+
+      gcc_assert (OMP_CLAUSE_CODE (grp_end) == OMP_CLAUSE_MAP);
+
+      switch (OMP_CLAUSE_MAP_KIND (grp_end))
+	{
+	case GOMP_MAP_FIRSTPRIVATE_POINTER:
+	  {
+	    omp_mapping_group *to_group
+	      = omp_get_nonfirstprivate_group (grpmap, decl);
+
+	    if (!to_group || to_group == grp)
+	      continue;
+
+	    tree grp_first = *to_group->grp_start;
+	    enum gomp_map_kind first_kind = OMP_CLAUSE_MAP_KIND (grp_first);
+
+	    if ((GOMP_MAP_COPY_TO_P (first_kind)
+		 || first_kind == GOMP_MAP_ALLOC)
+		&& (OMP_CLAUSE_MAP_KIND (to_group->grp_end)
+		    != GOMP_MAP_FIRSTPRIVATE_POINTER))
+	      {
+		gcc_assert (TREE_ADDRESSABLE (OMP_CLAUSE_DECL (grp_end)));
+		OMP_CLAUSE_SET_MAP_KIND (grp_end, GOMP_MAP_ATTACH_DETACH);
+	      }
+	  }
+	  break;
+
+	case GOMP_MAP_FIRSTPRIVATE_REFERENCE:
+	  {
+	    tree ptr = build_fold_indirect_ref (decl);
+
+	    omp_mapping_group *to_group
+	      = omp_get_nonfirstprivate_group (grpmap, ptr);
+
+	    if (!to_group || to_group == grp)
+	      continue;
+
+	    tree grp_first = *to_group->grp_start;
+	    enum gomp_map_kind first_kind = OMP_CLAUSE_MAP_KIND (grp_first);
+
+	    if (GOMP_MAP_COPY_TO_P (first_kind)
+		|| first_kind == GOMP_MAP_ALLOC)
+	      {
+		OMP_CLAUSE_SET_MAP_KIND (grp_end, GOMP_MAP_ATTACH_DETACH);
+		OMP_CLAUSE_DECL (grp_end) = ptr;
+		if ((OMP_CLAUSE_CHAIN (*to_group->grp_start)
+		     == to_group->grp_end)
+		    && (OMP_CLAUSE_MAP_KIND (to_group->grp_end)
+			== GOMP_MAP_FIRSTPRIVATE_REFERENCE))
+		  {
+		    gcc_assert (TREE_ADDRESSABLE
+				  (OMP_CLAUSE_DECL (to_group->grp_end)));
+		    OMP_CLAUSE_SET_MAP_KIND (to_group->grp_end,
+					     GOMP_MAP_ATTACH_DETACH);
+
+		    location_t loc = OMP_CLAUSE_LOCATION (to_group->grp_end);
+		    tree alloc
+		      = build_omp_clause (loc, OMP_CLAUSE_MAP);
+		    OMP_CLAUSE_SET_MAP_KIND (alloc, GOMP_MAP_ALLOC);
+		    tree tmp = build_fold_addr_expr (OMP_CLAUSE_DECL
+						      (to_group->grp_end));
+		    tree char_ptr_type = build_pointer_type (char_type_node);
+		    OMP_CLAUSE_DECL (alloc)
+		      = build2 (MEM_REF, char_type_node,
+				tmp,
+				build_int_cst (char_ptr_type, 0));
+		    OMP_CLAUSE_SIZE (alloc) = TYPE_SIZE_UNIT (TREE_TYPE (tmp));
+
+		    OMP_CLAUSE_CHAIN (alloc)
+		      = OMP_CLAUSE_CHAIN (*to_group->grp_start);
+		    OMP_CLAUSE_CHAIN (*to_group->grp_start) = alloc;
+		  }
+	      }
+	  }
+	  break;
+
+	case GOMP_MAP_ATTACH_DETACH:
+	case GOMP_MAP_ATTACH_ZERO_LENGTH_ARRAY_SECTION:
+	  {
+	    tree base_ptr, referenced_ptr_node = NULL_TREE;
+
+	    while (TREE_CODE (decl) == ARRAY_REF)
+	      decl = TREE_OPERAND (decl, 0);
+
+	    if (TREE_CODE (decl) == INDIRECT_REF)
+	      decl = TREE_OPERAND (decl, 0);
+
+	    /* Only component accesses.  */
+	    if (DECL_P (decl))
+	      continue;
+
+	    /* We want the pointer itself when checking if the base pointer is
+	       mapped elsewhere in the same directive -- if we have a
+	       reference to the pointer, don't use that.  */
+
+	    if (TREE_CODE (TREE_TYPE (decl)) == REFERENCE_TYPE
+		&& TREE_CODE (TREE_TYPE (TREE_TYPE (decl))) == POINTER_TYPE)
+	      {
+		referenced_ptr_node = OMP_CLAUSE_CHAIN (*grp->grp_start);
+		base_ptr = OMP_CLAUSE_DECL (referenced_ptr_node);
+	      }
+	    else
+	      base_ptr = decl;
+
+	    gomp_map_kind zlas_kind
+	      = (code == OACC_EXIT_DATA || code == OMP_TARGET_EXIT_DATA)
+		? GOMP_MAP_DETACH : GOMP_MAP_ATTACH_ZERO_LENGTH_ARRAY_SECTION;
+
+	    if (TREE_CODE (TREE_TYPE (base_ptr)) == POINTER_TYPE)
+	      {
+		/* If we map the base TO, and we're doing an attachment, we can
+		   skip the TO mapping altogether and create an ALLOC mapping
+		   instead, since the attachment will overwrite the device
+		   pointer in that location immediately anyway.  Otherwise,
+		   change our mapping to
+		   GOMP_MAP_ATTACH_ZERO_LENGTH_ARRAY_SECTION in case the
+		   attachment target has not been copied to the device already
+		   by some earlier directive.  */
+
+		bool base_mapped_to = false;
+
+		omp_mapping_group *base_group;
+
+		if (omp_directive_maps_explicitly (grpmap, base_ptr,
+						   &base_group, false, true,
+						   false))
+		  {
+		    if (referenced_ptr_node)
+		      {
+			base_mapped_to = true;
+			if ((OMP_CLAUSE_MAP_KIND (base_group->grp_end)
+			     == GOMP_MAP_ATTACH_DETACH)
+			    && (OMP_CLAUSE_CHAIN (*base_group->grp_start)
+				== base_group->grp_end))
+			  {
+			    OMP_CLAUSE_CHAIN (*base_group->grp_start)
+			      = OMP_CLAUSE_CHAIN (base_group->grp_end);
+			    base_group->grp_end = *base_group->grp_start;
+			    repair_chain = true;
+			  }
+		      }
+		    else
+		      {
+			base_group->deleted = true;
+			OMP_CLAUSE_ATTACHMENT_MAPPING_ERASED (grp_end) = 1;
+		      }
+		  }
+
+		/* We're dealing with a reference to a pointer, and we are
+		   attaching both the reference and the pointer.  We know the
+		   reference itself is on the target, because we are going to
+		   create an ALLOC node for it in accumulate_sibling_list.  The
+		   pointer might be on the target already or it might not, but
+		   if it isn't then it's not an error, so use
+		   GOMP_MAP_ATTACH_ZLAS for it.  */
+		if (!base_mapped_to && referenced_ptr_node)
+		  OMP_CLAUSE_SET_MAP_KIND (referenced_ptr_node, zlas_kind);
+	      }
+	    else if (TREE_CODE (TREE_TYPE (base_ptr)) == REFERENCE_TYPE
+		     && (TREE_CODE (TREE_TYPE (TREE_TYPE (base_ptr)))
+			 == ARRAY_TYPE)
+		     && OMP_CLAUSE_MAP_MAYBE_ZERO_LENGTH_ARRAY_SECTION
+			  (*grp->grp_start))
+	      OMP_CLAUSE_SET_MAP_KIND (grp->grp_end, zlas_kind);
+	  }
+	  break;
+
+	default:
+	  {
+	    omp_mapping_group *struct_group;
+	    if (omp_mapped_by_containing_struct (grpmap, decl, &struct_group)
+		&& *grp->grp_start == grp_end)
+	      {
+		omp_check_mapping_compatibility (OMP_CLAUSE_LOCATION (grp_end),
+						 struct_group, grp);
+		/* Remove the whole of this mapping -- redundant.  */
+		grp->deleted = true;
+	      }
+
+	    tree base = decl;
+	    while ((base = omp_get_base_pointer (base)))
+	      {
+		omp_mapping_group *base_group;
+
+		if (omp_directive_maps_explicitly (grpmap, base, &base_group,
+						   true, true, false))
+		  {
+		    tree grp_first = *base_group->grp_start;
+		    OMP_CLAUSE_SET_MAP_KIND (grp_first, GOMP_MAP_ALLOC);
+		  }
+	      }
+	  }
+	}
+    }
+
+  if (repair_chain)
+    {
+      /* Group start pointers may have become detached from the
+	 OMP_CLAUSE_CHAIN of previous groups if elements were removed from the
+	 end of those groups.  Fix that now.  */
+      tree *new_next = NULL;
+      FOR_EACH_VEC_ELT (*groups, i, grp)
+	{
+	  if (new_next)
+	    grp->grp_start = new_next;
+
+	  new_next = &OMP_CLAUSE_CHAIN (grp->grp_end);
+	}
+    }
+}
+
 /* Similar to omp_resolve_clause_dependencies, but for OpenACC.  The only
    clause dependencies we handle for now are struct element mappings and
    whole-struct mappings on the same directive, and duplicate clause
@@ -10260,6 +10601,72 @@ omp_siblist_move_concat_nodes_after (tree first_new, tree *last_new_tail,
   return continue_at;
 }
 
+/* Expand a chained access.  We only expect to see a quite limited range of
+   expression types here, because e.g. you can't have an array of
+   references.  See also c-omp.cc:omp_expand_access_chain.  */
+
+static void
+omp_expand_access_chain (location_t loc, tree **list_pp, tree expr,
+			 vec<omp_addr_token *> &addr_tokens,
+			 unsigned *idx, gomp_map_kind kind)
+{
+  using namespace omp_addr_tokenizer;
+  unsigned i = *idx;
+  tree c = NULL_TREE;
+
+  switch (addr_tokens[i]->u.access_kind)
+    {
+    case ACCESS_POINTER:
+    case ACCESS_POINTER_OFFSET:
+      {
+	tree virtual_origin
+	  = fold_convert_loc (loc, ptrdiff_type_node, addr_tokens[i]->expr);
+	tree data_addr = omp_accessed_addr (addr_tokens, i, expr);
+	c = build_omp_clause (loc, OMP_CLAUSE_MAP);
+	OMP_CLAUSE_SET_MAP_KIND (c, kind);
+	OMP_CLAUSE_DECL (c) = addr_tokens[i]->expr;
+	OMP_CLAUSE_SIZE (c)
+	  = fold_build2_loc (loc, MINUS_EXPR, ptrdiff_type_node,
+			     fold_convert_loc (loc, ptrdiff_type_node,
+					       data_addr),
+			     virtual_origin);
+      }
+      break;
+
+    case ACCESS_INDEXED_ARRAY:
+      break;
+
+    default:
+      return;
+    }
+
+  if (c)
+    {
+      OMP_CLAUSE_CHAIN (c) = **list_pp;
+      **list_pp = c;
+      *list_pp = &OMP_CLAUSE_CHAIN (c);
+    }
+
+  *idx = ++i;
+
+  if (addr_tokens[i]->type == ACCESS_METHOD
+      && omp_access_chain_p (addr_tokens, i))
+    omp_expand_access_chain (loc, list_pp, expr, addr_tokens, idx, kind);
+}
+
+static omp_addr_token *
+omp_first_chained_access_token (vec<omp_addr_token *> &addr_tokens)
+{
+  using namespace omp_addr_tokenizer;
+  int idx = addr_tokens.length () - 1;
+  gcc_assert (idx >= 0);
+  if (addr_tokens[idx]->type != ACCESS_METHOD)
+    return addr_tokens[idx];
+  while (idx > 0 && addr_tokens[idx - 1]->type == ACCESS_METHOD)
+    idx--;
+  return addr_tokens[idx];
+}
+
 /* Mapping struct members causes an additional set of nodes to be created,
    starting with GOMP_MAP_STRUCT followed by a number of mappings equal to the
    number of members being mapped, in order of ascending position (address or
@@ -10301,129 +10708,285 @@ static tree *
 omp_accumulate_sibling_list (enum omp_region_type region_type,
 			     enum tree_code code,
 			     hash_map<tree_operand_hash, tree>
-			       *&struct_map_to_clause, tree *grp_start_p,
-			     tree grp_end, tree *inner)
+			       *&struct_map_to_clause,
+			     hash_map<tree_operand_hash_no_se,
+				      omp_mapping_group *> *group_map,
+			     tree *grp_start_p, tree grp_end,
+			     vec<omp_addr_token *> &addr_tokens, tree **inner,
+			     bool *fragile_p, bool reprocessing_struct,
+			     tree **added_tail)
 {
+  using namespace omp_addr_tokenizer;
   poly_offset_int coffset;
   poly_int64 cbitpos;
   tree ocd = OMP_CLAUSE_DECL (grp_end);
   bool openmp = !(region_type & ORT_ACC);
+  bool target = (region_type & ORT_TARGET) != 0;
   tree *continue_at = NULL;
 
   while (TREE_CODE (ocd) == ARRAY_REF)
     ocd = TREE_OPERAND (ocd, 0);
 
-  if (INDIRECT_REF_P (ocd))
-    ocd = TREE_OPERAND (ocd, 0);
+  if (*fragile_p)
+    {
+      omp_mapping_group *to_group
+	= omp_get_nonfirstprivate_group (group_map, ocd, true);
+
+      if (to_group)
+	return NULL;
+    }
+
+  omp_addr_token *last_token = omp_first_chained_access_token (addr_tokens);
+  if (last_token->type == ACCESS_METHOD)
+    {
+      switch (last_token->u.access_kind)
+	{
+	case ACCESS_REF:
+	case ACCESS_REF_TO_POINTER:
+	case ACCESS_REF_TO_POINTER_OFFSET:
+	case ACCESS_INDEXED_REF_TO_ARRAY:
+	  /* We may see either a bare reference or a dereferenced
+	     "convert_from_reference"-like one here.  Handle either way.  */
+	  if (TREE_CODE (ocd) == INDIRECT_REF)
+	    ocd = TREE_OPERAND (ocd, 0);
+	  gcc_assert (TREE_CODE (TREE_TYPE (ocd)) == REFERENCE_TYPE);
+	  break;
+
+	default:
+	  ;
+	}
+    }
 
   tree base = extract_base_bit_offset (ocd, &cbitpos, &coffset);
 
+  int base_token;
+  for (base_token = addr_tokens.length () - 1; base_token >= 0; base_token--)
+    {
+      if (addr_tokens[base_token]->type == ARRAY_BASE
+	  || addr_tokens[base_token]->type == STRUCTURE_BASE)
+	break;
+    }
+
+  /* The two expressions in the assertion below aren't quite the same: if we
+     have 'struct_base_decl access_indexed_array' for something like
+     "myvar[2].x" then base will be "myvar" and addr_tokens[base_token]->expr
+     will be "myvar[2]" -- the actual base of the structure.
+     The former interpretation leads to a strange situation where we get
+       struct(myvar) alloc(myvar[2].ptr1)
+     That is, the array of structures is kind of treated as one big structure
+     for the purposes of gathering sibling lists, etc.  */
+  /* gcc_assert (base == addr_tokens[base_token]->expr);  */
+
   bool ptr = (OMP_CLAUSE_MAP_KIND (grp_end) == GOMP_MAP_ALWAYS_POINTER);
   bool attach_detach = ((OMP_CLAUSE_MAP_KIND (grp_end)
 			 == GOMP_MAP_ATTACH_DETACH)
 			|| (OMP_CLAUSE_MAP_KIND (grp_end)
 			    == GOMP_MAP_ATTACH_ZERO_LENGTH_ARRAY_SECTION));
-  bool attach = (OMP_CLAUSE_MAP_KIND (grp_end) == GOMP_MAP_ATTACH
-		 || OMP_CLAUSE_MAP_KIND (grp_end) == GOMP_MAP_DETACH);
-
-  /* FIXME: If we're not mapping the base pointer in some other clause on this
-     directive, I think we want to create ALLOC/RELEASE here -- i.e. not
-     early-exit.  */
-  if (openmp && attach_detach)
-    return NULL;
 
   if (!struct_map_to_clause || struct_map_to_clause->get (base) == NULL)
     {
       tree l = build_omp_clause (OMP_CLAUSE_LOCATION (grp_end), OMP_CLAUSE_MAP);
-      gomp_map_kind k = attach ? GOMP_MAP_FORCE_PRESENT : GOMP_MAP_STRUCT;
-
-      OMP_CLAUSE_SET_MAP_KIND (l, k);
 
+      OMP_CLAUSE_SET_MAP_KIND (l, GOMP_MAP_STRUCT);
       OMP_CLAUSE_DECL (l) = unshare_expr (base);
+      OMP_CLAUSE_SIZE (l) = size_int (1);
 
-      OMP_CLAUSE_SIZE (l)
-	= (!attach ? size_int (1)
-	   : (DECL_P (OMP_CLAUSE_DECL (l))
-	      ? DECL_SIZE_UNIT (OMP_CLAUSE_DECL (l))
-	      : TYPE_SIZE_UNIT (TREE_TYPE (OMP_CLAUSE_DECL (l)))));
       if (struct_map_to_clause == NULL)
 	struct_map_to_clause = new hash_map<tree_operand_hash, tree>;
       struct_map_to_clause->put (base, l);
 
+      /* On first iterating through the clause list, we insert the struct node
+	 just before the component access node that triggers the initial
+	 omp_accumulate_sibling_list call for a particular sibling list (and
+	 it then forms the first entry in that list).  When reprocessing
+	 struct bases that are themselves component accesses, we insert the
+	 struct node on an off-side list to avoid inserting the new
+	 GOMP_MAP_STRUCT into the middle of the old one.  */
+      tree *insert_node_pos = reprocessing_struct ? *added_tail : grp_start_p;
+
       if (ptr || attach_detach)
 	{
 	  tree extra_node;
 	  tree alloc_node
 	    = build_omp_struct_comp_nodes (code, *grp_start_p, grp_end,
 					   &extra_node);
+	  tree *tail;
 	  OMP_CLAUSE_CHAIN (l) = alloc_node;
 
-	  tree *insert_node_pos = grp_start_p;
-
 	  if (extra_node)
 	    {
 	      OMP_CLAUSE_CHAIN (extra_node) = *insert_node_pos;
 	      OMP_CLAUSE_CHAIN (alloc_node) = extra_node;
+	      tail = &OMP_CLAUSE_CHAIN (extra_node);
 	    }
 	  else
-	    OMP_CLAUSE_CHAIN (alloc_node) = *insert_node_pos;
+	    {
+	      OMP_CLAUSE_CHAIN (alloc_node) = *insert_node_pos;
+	      tail = &OMP_CLAUSE_CHAIN (alloc_node);
+	    }
+
+	  /* For OpenMP semantics, we don't want to implicitly allocate
+	     space for the pointer here for non-compute regions (e.g. "enter
+	     data").  A FRAGILE_P node is only being created so that
+	     omp-low.cc is able to rewrite the struct properly.
+	     For references (to pointers), we want to actually allocate the
+	     space for the reference itself in the sorted list following the
+	     struct node.
+	     For pointers, we want to allocate space if we had an explicit
+	     mapping of the attachment point, but not otherwise.  */
+	  if (*fragile_p
+	      || (openmp
+		  && !target
+		  && attach_detach
+		  && TREE_CODE (TREE_TYPE (ocd)) == POINTER_TYPE
+		  && !OMP_CLAUSE_ATTACHMENT_MAPPING_ERASED (grp_end)))
+	    {
+	      if (!lang_GNU_Fortran ())
+	       /* In Fortran, pointers are dereferenced automatically, but may
+		  be unassociated.  So we still want to allocate space for the
+		  pointer (as the base for an attach operation that should be
+		  present in the same directive's clause list also).  */
+		OMP_CLAUSE_SIZE (alloc_node) = size_zero_node;
+	      OMP_CLAUSE_MAP_MAYBE_ZERO_LENGTH_ARRAY_SECTION (alloc_node) = 1;
+	    }
 
 	  *insert_node_pos = l;
+
+	  if (reprocessing_struct)
+	    {
+	      /* When reprocessing a struct node group used as the base of a
+		 subcomponent access, if we have a reference-to-pointer base,
+		 we will see:
+		   struct(**ptr) attach(*ptr)
+		 whereas for a non-reprocess-struct group, we see, e.g.:
+		   tofrom(**ptr) attach(*ptr) attach(ptr)
+		 and we create the "alloc" for the second "attach", i.e.
+		 for the reference itself.  When reprocessing a struct group we
+		 thus change the pointer attachment into a reference attachment
+		 by stripping the indirection.  (The attachment of the
+		 referenced pointer must happen elsewhere, either on the same
+		 directive, or otherwise.)  */
+	      tree adecl = OMP_CLAUSE_DECL (alloc_node);
+
+	      if ((TREE_CODE (adecl) == INDIRECT_REF
+		   || (TREE_CODE (adecl) == MEM_REF
+		       && integer_zerop (TREE_OPERAND (adecl, 1))))
+		  && (TREE_CODE (TREE_TYPE (TREE_OPERAND (adecl, 0)))
+		      == REFERENCE_TYPE)
+		  && (TREE_CODE (TREE_TYPE (TREE_TYPE
+			(TREE_OPERAND (adecl, 0)))) == POINTER_TYPE))
+		OMP_CLAUSE_DECL (alloc_node) = TREE_OPERAND (adecl, 0);
+
+	      *added_tail = tail;
+	    }
 	}
       else
 	{
 	  gcc_assert (*grp_start_p == grp_end);
-	  grp_start_p = omp_siblist_insert_node_after (l, grp_start_p);
+	  if (reprocessing_struct)
+	    {
+	      /* If we don't have an attach/detach node, this is a
+		 "target data" directive or similar, not an offload region.
+		 Synthesize an "alloc" node using just the initiating
+		 GOMP_MAP_STRUCT decl.  */
+	      gomp_map_kind k = (code == OMP_TARGET_EXIT_DATA
+				 || code == OACC_EXIT_DATA)
+				? GOMP_MAP_RELEASE : GOMP_MAP_ALLOC;
+	      tree alloc_node
+		= build_omp_clause (OMP_CLAUSE_LOCATION (grp_end),
+				    OMP_CLAUSE_MAP);
+	      OMP_CLAUSE_SET_MAP_KIND (alloc_node, k);
+	      OMP_CLAUSE_DECL (alloc_node) = unshare_expr (last_token->expr);
+	      OMP_CLAUSE_SIZE (alloc_node)
+		= TYPE_SIZE_UNIT (TREE_TYPE (OMP_CLAUSE_DECL (alloc_node)));
+
+	      OMP_CLAUSE_CHAIN (alloc_node) = OMP_CLAUSE_CHAIN (l);
+	      OMP_CLAUSE_CHAIN (l) = alloc_node;
+	      *insert_node_pos = l;
+	      *added_tail = &OMP_CLAUSE_CHAIN (alloc_node);
+	    }
+	  else
+	    grp_start_p = omp_siblist_insert_node_after (l, insert_node_pos);
 	}
 
-      tree noind = omp_strip_indirections (base);
+      unsigned last_access = base_token + 1;
 
-      if (!openmp
-	  && (region_type & ORT_TARGET)
-	  && TREE_CODE (noind) == COMPONENT_REF)
+      while (last_access + 1 < addr_tokens.length ()
+	     && addr_tokens[last_access + 1]->type == ACCESS_METHOD)
+	last_access++;
+
+      if ((region_type & ORT_TARGET)
+	  && addr_tokens[base_token + 1]->type == ACCESS_METHOD)
 	{
-	  /* The base for this component access is a struct component access
-	     itself.  Insert a node to be processed on the next iteration of
-	     our caller's loop, which will subsequently be turned into a new,
-	     inner GOMP_MAP_STRUCT mapping.
+	  bool base_ref = false;
+	  access_method_kinds access_kind
+	    = addr_tokens[last_access]->u.access_kind;
 
-	     We need to do this else the non-DECL_P base won't be
-	     rewritten correctly in the offloaded region.  */
+	  switch (access_kind)
+	    {
+	    case ACCESS_DIRECT:
+	    case ACCESS_INDEXED_ARRAY:
+	      return NULL;
+
+	    case ACCESS_REF:
+	    case ACCESS_REF_TO_POINTER:
+	    case ACCESS_REF_TO_POINTER_OFFSET:
+	    case ACCESS_INDEXED_REF_TO_ARRAY:
+	      base_ref = true;
+	      break;
+
+	    default:
+	      ;
+	    }
 	  tree c2 = build_omp_clause (OMP_CLAUSE_LOCATION (grp_end),
 				      OMP_CLAUSE_MAP);
-	  OMP_CLAUSE_SET_MAP_KIND (c2, GOMP_MAP_FORCE_PRESENT);
-	  OMP_CLAUSE_DECL (c2) = unshare_expr (noind);
-	  OMP_CLAUSE_SIZE (c2) = TYPE_SIZE_UNIT (TREE_TYPE (noind));
-	  *inner = c2;
-	  return NULL;
-	}
+	  enum gomp_map_kind mkind;
+	  omp_mapping_group *decl_group;
+	  tree use_base;
+	  switch (access_kind)
+	    {
+	    case ACCESS_POINTER:
+	    case ACCESS_POINTER_OFFSET:
+	      use_base = addr_tokens[last_access]->expr;
+	      break;
+	    case ACCESS_REF_TO_POINTER:
+	    case ACCESS_REF_TO_POINTER_OFFSET:
+	      use_base
+		= build_fold_indirect_ref (addr_tokens[last_access]->expr);
+	      break;
+	    default:
+	      use_base = addr_tokens[base_token]->expr;
+	    }
+	  bool mapped_to_p
+	    = omp_directive_maps_explicitly (group_map, use_base, &decl_group,
+					     true, false, true);
+	  if (addr_tokens[base_token]->type == STRUCTURE_BASE
+	      && DECL_P (addr_tokens[last_access]->expr)
+	      && !mapped_to_p)
+	    mkind = base_ref ? GOMP_MAP_FIRSTPRIVATE_REFERENCE
+			     : GOMP_MAP_FIRSTPRIVATE_POINTER;
+	  else
+	    mkind = GOMP_MAP_ATTACH_DETACH;
 
-      tree sdecl = omp_strip_components_and_deref (base);
-
-      if (POINTER_TYPE_P (TREE_TYPE (sdecl)) && (region_type & ORT_TARGET))
-	{
-	  tree c2 = build_omp_clause (OMP_CLAUSE_LOCATION (grp_end),
-				      OMP_CLAUSE_MAP);
-	  bool base_ref
-	    = (INDIRECT_REF_P (base)
-	       && ((TREE_CODE (TREE_TYPE (TREE_OPERAND (base, 0)))
-		    == REFERENCE_TYPE)
-		   || (INDIRECT_REF_P (TREE_OPERAND (base, 0))
-		       && (TREE_CODE (TREE_TYPE (TREE_OPERAND
-						  (TREE_OPERAND (base, 0), 0)))
-			   == REFERENCE_TYPE))));
-	  enum gomp_map_kind mkind = base_ref ? GOMP_MAP_FIRSTPRIVATE_REFERENCE
-					      : GOMP_MAP_FIRSTPRIVATE_POINTER;
 	  OMP_CLAUSE_SET_MAP_KIND (c2, mkind);
-	  OMP_CLAUSE_DECL (c2) = sdecl;
+	  /* If we have a reference to pointer base, we want to attach the
+	     pointer here, not the reference.  The reference attachment happens
+	     elsewhere.  */
+	  bool ref_to_ptr
+	    = (access_kind == ACCESS_REF_TO_POINTER
+	       || access_kind == ACCESS_REF_TO_POINTER_OFFSET);
+	  tree sdecl = addr_tokens[last_access]->expr;
+	  tree sdecl_ptr = ref_to_ptr ? build_fold_indirect_ref (sdecl)
+				      : sdecl;
+	  /* For the FIRSTPRIVATE_REFERENCE after the struct node, we
+	     want to use the reference itself for the decl, but we
+	     still want to use the pointer to calculate the bias.  */
+	  OMP_CLAUSE_DECL (c2) = (mkind == GOMP_MAP_ATTACH_DETACH)
+				 ? sdecl_ptr : sdecl;
+	  sdecl = sdecl_ptr;
 	  tree baddr = build_fold_addr_expr (base);
 	  baddr = fold_convert_loc (OMP_CLAUSE_LOCATION (grp_end),
 				    ptrdiff_type_node, baddr);
-	  /* This isn't going to be good enough when we add support for more
-	     complicated lvalue expressions.  FIXME.  */
-	  if (TREE_CODE (TREE_TYPE (sdecl)) == REFERENCE_TYPE
-	      && TREE_CODE (TREE_TYPE (TREE_TYPE (sdecl))) == POINTER_TYPE)
-	    sdecl = build_simple_mem_ref (sdecl);
 	  tree decladdr = fold_convert_loc (OMP_CLAUSE_LOCATION (grp_end),
 					    ptrdiff_type_node, sdecl);
 	  OMP_CLAUSE_SIZE (c2)
@@ -10432,24 +10995,46 @@ omp_accumulate_sibling_list (enum omp_region_type region_type,
 	  /* Insert after struct node.  */
 	  OMP_CLAUSE_CHAIN (c2) = OMP_CLAUSE_CHAIN (l);
 	  OMP_CLAUSE_CHAIN (l) = c2;
+
+	  if (addr_tokens[base_token]->type == STRUCTURE_BASE
+	      && (addr_tokens[base_token]->u.structure_base_kind
+		  == BASE_COMPONENT_EXPR)
+	      && mkind == GOMP_MAP_ATTACH_DETACH
+	      && addr_tokens[last_access]->u.access_kind != ACCESS_REF)
+	    {
+	      *inner = insert_node_pos;
+	      if (openmp)
+		*fragile_p = true;
+	      return NULL;
+	    }
 	}
 
+      if (addr_tokens[base_token]->type == STRUCTURE_BASE
+	  && (addr_tokens[base_token]->u.structure_base_kind
+	      == BASE_COMPONENT_EXPR)
+	  && addr_tokens[last_access]->u.access_kind == ACCESS_REF)
+	*inner = insert_node_pos;
+
       return NULL;
     }
   else if (struct_map_to_clause)
     {
       tree *osc = struct_map_to_clause->get (base);
       tree *sc = NULL, *scp = NULL;
+      unsigned HOST_WIDE_INT i, elems = tree_to_uhwi (OMP_CLAUSE_SIZE (*osc));
       sc = &OMP_CLAUSE_CHAIN (*osc);
       /* The struct mapping might be immediately followed by a
-	 FIRSTPRIVATE_POINTER and/or FIRSTPRIVATE_REFERENCE -- if it's an
-	 indirect access or a reference, or both.  (This added node is removed
-	 in omp-low.c after it has been processed there.)  */
-      if (*sc != grp_end
-	  && (OMP_CLAUSE_MAP_KIND (*sc) == GOMP_MAP_FIRSTPRIVATE_POINTER
-	      || OMP_CLAUSE_MAP_KIND (*sc) == GOMP_MAP_FIRSTPRIVATE_REFERENCE))
+	 FIRSTPRIVATE_POINTER,  FIRSTPRIVATE_REFERENCE or an ATTACH_DETACH --
+	 if it's an indirect access or a reference, or if the structure base
+	 is not a decl.  The FIRSTPRIVATE_* nodes are removed in omp-low.cc
+	 after they have been processed there, and ATTACH_DETACH nodes are
+	 recomputed and moved out of the GOMP_MAP_STRUCT construct once
+	 sibling list building is complete.  */
+      if (OMP_CLAUSE_MAP_KIND (*sc) == GOMP_MAP_FIRSTPRIVATE_POINTER
+	  || OMP_CLAUSE_MAP_KIND (*sc) == GOMP_MAP_FIRSTPRIVATE_REFERENCE
+	  || OMP_CLAUSE_MAP_KIND (*sc) == GOMP_MAP_ATTACH_DETACH)
 	sc = &OMP_CLAUSE_CHAIN (*sc);
-      for (; *sc != grp_end; sc = &OMP_CLAUSE_CHAIN (*sc))
+      for (i = 0; i < elems; i++, sc = &OMP_CLAUSE_CHAIN (*sc))
 	if ((ptr || attach_detach) && sc == grp_start_p)
 	  break;
 	else if (TREE_CODE (OMP_CLAUSE_DECL (*sc)) != COMPONENT_REF
@@ -10481,6 +11066,27 @@ omp_accumulate_sibling_list (enum omp_region_type region_type,
 	      break;
 	    if (scp)
 	      continue;
+	    if ((region_type & ORT_ACC) != 0)
+	      {
+		/* For OpenACC, allow (ignore) duplicate struct accesses in
+		   the middle of a mapping clause, e.g. "mystruct->foo" in:
+		   copy(mystruct->foo->bar) copy(mystruct->foo->qux).  */
+		if (reprocessing_struct
+		    && known_eq (coffset, offset)
+		    && known_eq (cbitpos, bitpos))
+		  return NULL;
+	      }
+	    else if (known_eq (coffset, offset)
+		     && known_eq (cbitpos, bitpos))
+	      {
+		/* Having two struct members at the same offset doesn't work,
+		   so make sure we don't.  (We're allowed to ignore this.
+		   Should we report the error?)  */
+		/*error_at (OMP_CLAUSE_LOCATION (grp_end),
+			  "duplicate struct member %qE in map clauses",
+			  OMP_CLAUSE_DECL (grp_end));*/
+		return NULL;
+	      }
 	    if (maybe_lt (coffset, offset)
 		|| (known_eq (coffset, offset)
 		    && maybe_lt (cbitpos, bitpos)))
@@ -10492,9 +11098,48 @@ omp_accumulate_sibling_list (enum omp_region_type region_type,
 	      }
 	  }
 
-      if (!attach)
-	OMP_CLAUSE_SIZE (*osc)
-	  = size_binop (PLUS_EXPR, OMP_CLAUSE_SIZE (*osc), size_one_node);
+      OMP_CLAUSE_SIZE (*osc)
+	= size_binop (PLUS_EXPR, OMP_CLAUSE_SIZE (*osc), size_one_node);
+
+      if (reprocessing_struct)
+	{
+	  /* If we're reprocessing a struct node, we don't want to do most of
+	     the list manipulation below.  We only need to handle the (pointer
+	     or reference) attach/detach case.  */
+	  tree extra_node, alloc_node;
+	  if (attach_detach)
+	    alloc_node = build_omp_struct_comp_nodes (code, *grp_start_p,
+						      grp_end, &extra_node);
+	  else
+	    {
+	      /* If we don't have an attach/detach node, this is a
+		 "target data" directive or similar, not an offload region.
+		 Synthesize an "alloc" node using just the initiating
+		 GOMP_MAP_STRUCT decl.  */
+	      gomp_map_kind k = (code == OMP_TARGET_EXIT_DATA
+				 || code == OACC_EXIT_DATA)
+				? GOMP_MAP_RELEASE : GOMP_MAP_ALLOC;
+	      alloc_node
+		= build_omp_clause (OMP_CLAUSE_LOCATION (grp_end),
+				    OMP_CLAUSE_MAP);
+	      OMP_CLAUSE_SET_MAP_KIND (alloc_node, k);
+	      OMP_CLAUSE_DECL (alloc_node) = unshare_expr (last_token->expr);
+	      OMP_CLAUSE_SIZE (alloc_node)
+		= TYPE_SIZE_UNIT (TREE_TYPE (OMP_CLAUSE_DECL (alloc_node)));
+	    }
+
+	  if (scp)
+	    omp_siblist_insert_node_after (alloc_node, scp);
+	  else
+	    {
+	      tree *new_end = omp_siblist_insert_node_after (alloc_node, sc);
+	      if (sc == *added_tail)
+		*added_tail = new_end;
+	    }
+
+	  return NULL;
+	}
+
       if (ptr || attach_detach)
 	{
 	  tree cl = NULL_TREE, extra_node;
@@ -10502,6 +11147,18 @@ omp_accumulate_sibling_list (enum omp_region_type region_type,
 							 grp_end, &extra_node);
 	  tree *tail_chain = NULL;
 
+	  if (*fragile_p
+	      || (openmp
+		  && !target
+		  && attach_detach
+		  && TREE_CODE (TREE_TYPE (ocd)) == POINTER_TYPE
+		  && !OMP_CLAUSE_ATTACHMENT_MAPPING_ERASED (grp_end)))
+	    {
+	      if (!lang_GNU_Fortran ())
+		OMP_CLAUSE_SIZE (alloc_node) = size_zero_node;
+	      OMP_CLAUSE_MAP_MAYBE_ZERO_LENGTH_ARRAY_SECTION (alloc_node) = 1;
+	    }
+
 	  /* Here, we have:
 
 	     grp_end : the last (or only) node in this group.
@@ -10587,12 +11244,15 @@ omp_build_struct_sibling_lists (enum tree_code code,
 					 omp_mapping_group *> **grpmap,
 				tree *list_p)
 {
+  using namespace omp_addr_tokenizer;
   unsigned i;
   omp_mapping_group *grp;
   hash_map<tree_operand_hash, tree> *struct_map_to_clause = NULL;
   bool success = true;
   tree *new_next = NULL;
   tree *tail = &OMP_CLAUSE_CHAIN ((*groups)[groups->length () - 1].grp_end);
+  tree added_nodes = NULL_TREE;
+  tree *added_tail = &added_nodes;
   auto_vec<omp_mapping_group> pre_hwm_groups;
 
   FOR_EACH_VEC_ELT (*groups, i, grp)
@@ -10600,9 +11260,10 @@ omp_build_struct_sibling_lists (enum tree_code code,
       tree c = grp->grp_end;
       tree decl = OMP_CLAUSE_DECL (c);
       tree grp_end = grp->grp_end;
+      auto_vec<omp_addr_token *> addr_tokens;
       tree sentinel = OMP_CLAUSE_CHAIN (grp_end);
 
-      if (new_next)
+      if (new_next && !grp->reprocess_struct)
 	grp->grp_start = new_next;
 
       new_next = NULL;
@@ -10613,7 +11274,7 @@ omp_build_struct_sibling_lists (enum tree_code code,
 	continue;
 
       /* Skip groups we marked for deletion in
-	 oacc_resolve_clause_dependencies.  */
+	 {omp,oacc}_resolve_clause_dependencies.  */
       if (grp->deleted)
 	continue;
 
@@ -10630,6 +11291,39 @@ omp_build_struct_sibling_lists (enum tree_code code,
 	    continue;
 	}
 
+      tree expr = decl;
+
+      while (TREE_CODE (expr) == ARRAY_REF)
+	expr = TREE_OPERAND (expr, 0);
+
+      if (!omp_parse_expr (addr_tokens, expr))
+	continue;
+
+      omp_addr_token *last_token
+	= omp_first_chained_access_token (addr_tokens);
+
+      /* A mapping of a reference to a pointer member that doesn't specify an
+	 array section, etc., like this:
+	   *mystruct.ref_to_ptr
+	 should not be processed by the struct sibling-list handling code --
+	 it just transfers the referenced pointer.
+
+	 In contrast, the quite similar-looking construct:
+	   *mystruct.ptr
+	 which is equivalent to e.g.
+	   mystruct.ptr[0]
+	 *does* trigger sibling-list processing.
+
+	 An exception for the former case is for "fragile" groups where the
+	 reference itself is not handled otherwise; this is subject to special
+	 handling in omp_accumulate_sibling_list also.  */
+
+      if (TREE_CODE (TREE_TYPE (decl)) == POINTER_TYPE
+	  && last_token->type == ACCESS_METHOD
+	  && last_token->u.access_kind == ACCESS_REF
+	  && !grp->fragile)
+	continue;
+
       tree d = decl;
       if (TREE_CODE (d) == ARRAY_REF)
 	{
@@ -10658,14 +11352,7 @@ omp_build_struct_sibling_lists (enum tree_code code,
       omp_mapping_group *wholestruct;
       if (omp_mapped_by_containing_struct (*grpmap, OMP_CLAUSE_DECL (c),
 					   &wholestruct))
-	{
-	  if (!(region_type & ORT_ACC)
-	      && *grp_start_p == grp_end)
-	    /* Remove the whole of this mapping -- redundant.  */
-	    grp->deleted = true;
-
-	  continue;
-	}
+	continue;
 
       if (OMP_CLAUSE_MAP_KIND (c) != GOMP_MAP_TO_PSET
 	  && OMP_CLAUSE_MAP_KIND (c) != GOMP_MAP_ATTACH
@@ -10692,27 +11379,30 @@ omp_build_struct_sibling_lists (enum tree_code code,
 	      goto error_out;
 	    }
 
-	  tree inner = NULL_TREE;
+	  tree *inner = NULL;
+	  bool fragile_p = grp->fragile;
 
 	  new_next
 	    = omp_accumulate_sibling_list (region_type, code,
-					   struct_map_to_clause, grp_start_p,
-					   grp_end, &inner);
+					   struct_map_to_clause, *grpmap,
+					   grp_start_p, grp_end, addr_tokens,
+					   &inner, &fragile_p,
+					   grp->reprocess_struct, &added_tail);
 
 	  if (inner)
 	    {
-	      if (new_next && *new_next == NULL_TREE)
-		*new_next = inner;
-	      else
-		*tail = inner;
-
-	      OMP_CLAUSE_CHAIN (inner) = NULL_TREE;
 	      omp_mapping_group newgrp;
-	      newgrp.grp_start = new_next ? new_next : tail;
-	      newgrp.grp_end = inner;
+	      newgrp.grp_start = inner;
+	      if (OMP_CLAUSE_MAP_KIND (OMP_CLAUSE_CHAIN (*inner))
+		  == GOMP_MAP_ATTACH_DETACH)
+		newgrp.grp_end = OMP_CLAUSE_CHAIN (*inner);
+	      else
+		newgrp.grp_end = *inner;
 	      newgrp.mark = UNVISITED;
 	      newgrp.sibling = NULL;
 	      newgrp.deleted = false;
+	      newgrp.reprocess_struct = true;
+	      newgrp.fragile = fragile_p;
 	      newgrp.next = NULL;
 	      groups->safe_push (newgrp);
 
@@ -10723,8 +11413,6 @@ omp_build_struct_sibling_lists (enum tree_code code,
 	      *grpmap
 		= omp_reindex_mapping_groups (list_p, groups, &pre_hwm_groups,
 					      sentinel);
-
-	      tail = &OMP_CLAUSE_CHAIN (inner);
 	    }
 	}
     }
@@ -10753,6 +11441,61 @@ omp_build_struct_sibling_lists (enum tree_code code,
 	tail = &OMP_CLAUSE_CHAIN (*tail);
     }
 
+  /* Tack on the struct nodes added during nested struct reprocessing.  */
+  if (added_nodes)
+    {
+      *tail = added_nodes;
+      tail = added_tail;
+    }
+
+  /* Now we have finished building the struct sibling lists, reprocess
+     newly-added "attach" nodes: we need the address of the first
+     mapped element of each struct sibling list for the bias of the attach
+     operation -- not necessarily the base address of the whole struct.  */
+  if (struct_map_to_clause)
+    for (hash_map<tree_operand_hash, tree>::iterator iter
+	   = struct_map_to_clause->begin ();
+	 iter != struct_map_to_clause->end ();
+	 ++iter)
+      {
+	tree struct_node = (*iter).second;
+	gcc_assert (OMP_CLAUSE_CODE (struct_node) == OMP_CLAUSE_MAP);
+	tree attach = OMP_CLAUSE_CHAIN (struct_node);
+
+	if (OMP_CLAUSE_CODE (attach) != OMP_CLAUSE_MAP
+	    || OMP_CLAUSE_MAP_KIND (attach) != GOMP_MAP_ATTACH_DETACH)
+	  continue;
+
+	OMP_CLAUSE_SET_MAP_KIND (attach, GOMP_MAP_ATTACH);
+
+	/* Sanity check: the standalone attach node will not work if we have
+	   an "enter data" operation (because for those, variables need to be
+	   mapped separately and attach nodes must be grouped together with the
+	   base they attach to).  We should only have created the
+	   ATTACH_DETACH node after GOMP_MAP_STRUCT for a target region, so
+	   this should never be true.  */
+	gcc_assert ((region_type & ORT_TARGET) != 0);
+
+	/* This is the first sorted node in the struct sibling list.  Use it
+	   to recalculate the correct bias to use.
+	   (&first_node - attach_decl).  */
+	tree first_node = OMP_CLAUSE_DECL (OMP_CLAUSE_CHAIN (attach));
+	first_node = build_fold_addr_expr (first_node);
+	first_node = fold_convert (ptrdiff_type_node, first_node);
+	tree attach_decl = OMP_CLAUSE_DECL (attach);
+	attach_decl = fold_convert (ptrdiff_type_node, attach_decl);
+	OMP_CLAUSE_SIZE (attach)
+	  = fold_build2 (MINUS_EXPR, ptrdiff_type_node, first_node,
+			 attach_decl);
+
+	/* Remove GOMP_MAP_ATTACH node from after struct node.  */
+	OMP_CLAUSE_CHAIN (struct_node) = OMP_CLAUSE_CHAIN (attach);
+	/* ...and re-insert it at the end of our clause list.  */
+	*tail = attach;
+	OMP_CLAUSE_CHAIN (attach) = NULL_TREE;
+	tail = &OMP_CLAUSE_CHAIN (attach);
+      }
+
 error_out:
   if (struct_map_to_clause)
     delete struct_map_to_clause;
@@ -10768,6 +11511,7 @@ gimplify_scan_omp_clauses (tree *list_p, gimple_seq *pre_p,
 			   enum omp_region_type region_type,
 			   enum tree_code code)
 {
+  using namespace omp_addr_tokenizer;
   struct gimplify_omp_ctx *ctx, *outer_ctx;
   tree c;
   tree *orig_list_p = list_p;
@@ -10813,6 +11557,7 @@ gimplify_scan_omp_clauses (tree *list_p, gimple_seq *pre_p,
 	  hash_map<tree_operand_hash_no_se, omp_mapping_group *> *grpmap;
 	  grpmap = omp_index_mapping_groups (groups);
 
+	  omp_resolve_clause_dependencies (code, groups, grpmap);
 	  omp_build_struct_sibling_lists (code, region_type, groups, &grpmap,
 					  list_p);
 
@@ -10909,6 +11654,7 @@ gimplify_scan_omp_clauses (tree *list_p, gimple_seq *pre_p,
       const char *check_non_private = NULL;
       unsigned int flags;
       tree decl;
+      auto_vec<omp_addr_token *, 10> addr_tokens;
 
       switch (OMP_CLAUSE_CODE (c))
 	{
@@ -11215,6 +11961,13 @@ gimplify_scan_omp_clauses (tree *list_p, gimple_seq *pre_p,
 
 	case OMP_CLAUSE_MAP:
 	  decl = OMP_CLAUSE_DECL (c);
+
+	  if (!omp_parse_expr (addr_tokens, decl))
+	    {
+	      remove = true;
+	      break;
+	    }
+
 	  if (error_operand_p (decl))
 	    remove = true;
 	  switch (code)
@@ -11224,13 +11977,18 @@ gimplify_scan_omp_clauses (tree *list_p, gimple_seq *pre_p,
 	    case OACC_DATA:
 	      if (TREE_CODE (TREE_TYPE (decl)) != ARRAY_TYPE)
 		break;
+	      goto check_firstprivate;
+	    case OACC_ENTER_DATA:
+	    case OACC_EXIT_DATA:
+	      if (OMP_CLAUSE_MAP_KIND (c) == GOMP_MAP_ATTACH_DETACH
+		  && addr_tokens[0]->type == ARRAY_BASE)
+		remove = true;
 	      /* FALLTHRU */
 	    case OMP_TARGET_DATA:
 	    case OMP_TARGET_ENTER_DATA:
 	    case OMP_TARGET_EXIT_DATA:
-	    case OACC_ENTER_DATA:
-	    case OACC_EXIT_DATA:
 	    case OACC_HOST_DATA:
+	    check_firstprivate:
 	      if (OMP_CLAUSE_MAP_KIND (c) == GOMP_MAP_FIRSTPRIVATE_POINTER
 		  || (OMP_CLAUSE_MAP_KIND (c)
 		      == GOMP_MAP_FIRSTPRIVATE_REFERENCE))
@@ -11282,26 +12040,22 @@ gimplify_scan_omp_clauses (tree *list_p, gimple_seq *pre_p,
 				  GOVD_FIRSTPRIVATE | GOVD_SEEN);
 	    }
 
-	  if (OMP_CLAUSE_MAP_KIND (c) == GOMP_MAP_STRUCT)
+	  if (OMP_CLAUSE_MAP_KIND (c) == GOMP_MAP_STRUCT
+	      && (addr_tokens[0]->type == STRUCTURE_BASE
+		  || addr_tokens[0]->type == ARRAY_BASE)
+	      && addr_tokens[0]->u.structure_base_kind == BASE_DECL)
 	    {
-	      tree base = omp_strip_components_and_deref (decl);
-	      if (DECL_P (base))
-		{
-		  decl = base;
-		  splay_tree_node n
-		    = splay_tree_lookup (ctx->variables,
-					 (splay_tree_key) decl);
-		  if (seen_error ()
-		      && n
-		      && (n->value & (GOVD_MAP | GOVD_FIRSTPRIVATE)) != 0)
-		    {
-		      remove = true;
-		      break;
-		    }
-		  flags = GOVD_MAP | GOVD_EXPLICIT;
+	      gcc_assert (addr_tokens[1]->type == ACCESS_METHOD);
+	      /* If we got to this struct via a chain of pointers, maybe we
+		 want to map it implicitly instead.  */
+	      if (omp_access_chain_p (addr_tokens, 1))
+		break;
+	      decl = addr_tokens[1]->expr;
+	      flags = GOVD_MAP | GOVD_EXPLICIT;
 
-		  goto do_add_decl;
-		}
+	      gcc_assert (addr_tokens[1]->u.access_kind != ACCESS_DIRECT
+			  || TREE_ADDRESSABLE (decl));
+	      goto do_add_decl;
 	    }
 
 	  if (TREE_CODE (decl) == TARGET_EXPR)
@@ -11533,6 +12287,16 @@ gimplify_scan_omp_clauses (tree *list_p, gimple_seq *pre_p,
 					     : GOMP_MAP_ATTACH);
 	      OMP_CLAUSE_SET_MAP_KIND (c, map_kind);
 	    }
+	  else if ((code == OACC_ENTER_DATA
+		    || code == OACC_EXIT_DATA
+		    || code == OACC_PARALLEL)
+		   && OMP_CLAUSE_MAP_KIND (c) == GOMP_MAP_ATTACH_DETACH)
+	    {
+	      enum gomp_map_kind map_kind = (code == OACC_EXIT_DATA
+					     ? GOMP_MAP_DETACH
+					     : GOMP_MAP_ATTACH);
+	      OMP_CLAUSE_SET_MAP_KIND (c, map_kind);
+	    }
 
 	  goto do_add;
 
@@ -12438,7 +13202,7 @@ gimplify_adjust_omp_clauses_1 (splay_tree_node n, void *data)
       if (TREE_CODE (TREE_TYPE (decl)) == REFERENCE_TYPE
 	  && TREE_CODE (TREE_TYPE (TREE_TYPE (decl))) == POINTER_TYPE)
 	OMP_CLAUSE_DECL (clause)
-	  = build_simple_mem_ref_loc (input_location, decl);
+	  = build_fold_indirect_ref_loc (input_location, decl);
       OMP_CLAUSE_DECL (clause)
 	= build2 (MEM_REF, char_type_node, OMP_CLAUSE_DECL (clause),
 		  build_int_cst (build_pointer_type (char_type_node), 0));
diff --git a/gcc/omp-general.cc b/gcc/omp-general.cc
index eefdcb54590f..caddac939cd9 100644
--- a/gcc/omp-general.cc
+++ b/gcc/omp-general.cc
@@ -45,6 +45,8 @@ along with GCC; see the file COPYING3.  If not see
 #include "data-streamer.h"
 #include "streamer-hooks.h"
 #include "opts.h"
+#include "omp-general.h"
+#include "tree-pretty-print.h"
 
 enum omp_requires omp_requires_mask;
 
@@ -3013,4 +3015,427 @@ omp_build_component_ref (tree obj, tree field)
   return ret;
 }
 
+namespace omp_addr_tokenizer {
+
+/* We scan an expression by recursive descent, and build a vector of
+   "omp_addr_token *" pointers representing a "parsed" version of the
+   expression.  The grammar we use is something like this:
+
+     expr0::
+       expr [section-access]
+
+     expr::
+	 structured-expr access-method
+       | array-base access-method
+
+     structured-expr::
+       structure-base component-selector
+
+     arbitrary-expr::
+       (anything else)
+
+     structure-base::
+	 DECL access-method
+       | structured-expr access-method
+       | arbitrary-expr access-method
+
+     array-base::
+	 DECL
+       | arbitrary-expr
+
+     access-method::
+	 DIRECT
+       | REF
+       | POINTER
+       | REF_TO_POINTER
+       | POINTER_OFFSET
+       | REF_TO_POINTER_OFFSET
+       | INDEXED_ARRAY
+       | INDEXED_REF_TO_ARRAY
+       | index-expr
+
+     index-expr::
+	 INDEX_EXPR access-method
+
+     component-selector::
+	 component-selector COMPONENT_REF
+       | component-selector ARRAY_REF
+       | COMPONENT_REF
+
+   This tokenized form is then used both in parsing, for OpenMP clause
+   expansion (for C and C++) and in gimplify.cc for sibling-list handling
+   (for C, C++ and Fortran).  */
+
+omp_addr_token::omp_addr_token (token_type t, tree e)
+  : type(t), expr(e)
+{
+}
+
+omp_addr_token::omp_addr_token (access_method_kinds k, tree e)
+  : type(ACCESS_METHOD), expr(e)
+{
+  u.access_kind = k;
+}
+
+omp_addr_token::omp_addr_token (token_type t, structure_base_kinds k, tree e)
+  : type(t), expr(e)
+{
+  u.structure_base_kind = k;
+}
+
+static bool
+omp_parse_component_selector (tree *expr0)
+{
+  tree expr = *expr0;
+  tree last_component = NULL_TREE;
+
+  while (TREE_CODE (expr) == COMPONENT_REF
+	 || TREE_CODE (expr) == ARRAY_REF)
+    {
+      if (TREE_CODE (expr) == COMPONENT_REF)
+	last_component = expr;
+
+      expr = TREE_OPERAND (expr, 0);
+
+      if (TREE_CODE (TREE_TYPE (expr)) == REFERENCE_TYPE)
+	break;
+    }
+
+  if (!last_component)
+    return false;
+
+  *expr0 = last_component;
+  return true;
+}
+
+/* This handles references that have had convert_from_reference called on
+   them, and also those that haven't.  */
+
+static bool
+omp_parse_ref (tree *expr0)
+{
+  tree expr = *expr0;
+
+  if (TREE_CODE (TREE_TYPE (expr)) == REFERENCE_TYPE)
+    return true;
+  else if ((TREE_CODE (expr) == INDIRECT_REF
+	    || (TREE_CODE (expr) == MEM_REF
+		&& integer_zerop (TREE_OPERAND (expr, 1))))
+	   && TREE_CODE (TREE_TYPE (TREE_OPERAND (expr, 0))) == REFERENCE_TYPE)
+    {
+      *expr0 = TREE_OPERAND (expr, 0);
+      return true;
+    }
+
+  return false;
+}
+
+static bool
+omp_parse_pointer (tree *expr0, bool *has_offset)
+{
+  tree expr = *expr0;
+
+  *has_offset = false;
+
+  if ((TREE_CODE (expr) == INDIRECT_REF
+       || (TREE_CODE (expr) == MEM_REF
+	   && integer_zerop (TREE_OPERAND (expr, 1))))
+      && TREE_CODE (TREE_TYPE (TREE_OPERAND (expr, 0))) == POINTER_TYPE)
+    {
+      expr = TREE_OPERAND (expr, 0);
+
+      /* The Fortran FE sometimes emits a no-op cast here.  */
+      STRIP_NOPS (expr);
+
+      while (1)
+	{
+	  if (TREE_CODE (expr) == COMPOUND_EXPR)
+	    {
+	      expr = TREE_OPERAND (expr, 1);
+	      STRIP_NOPS (expr);
+	    }
+	  else if (TREE_CODE (expr) == SAVE_EXPR)
+	    expr = TREE_OPERAND (expr, 0);
+	  else if (TREE_CODE (expr) == POINTER_PLUS_EXPR)
+	    {
+	      *has_offset = true;
+	      expr = TREE_OPERAND (expr, 0);
+	    }
+	  else
+	    break;
+	}
+
+      STRIP_NOPS (expr);
+
+      *expr0 = expr;
+      return true;
+    }
+
+  return false;
+}
+
+static bool
+omp_parse_access_method (tree *expr0, enum access_method_kinds *kind)
+{
+  tree expr = *expr0;
+  bool has_offset;
+
+  if (omp_parse_ref (&expr))
+    *kind = ACCESS_REF;
+  else if (omp_parse_pointer (&expr, &has_offset))
+    {
+      if (omp_parse_ref (&expr))
+	*kind = has_offset ? ACCESS_REF_TO_POINTER_OFFSET
+			   : ACCESS_REF_TO_POINTER;
+      else
+	*kind = has_offset ? ACCESS_POINTER_OFFSET : ACCESS_POINTER;
+    }
+  else if (TREE_CODE (expr) == ARRAY_REF)
+    {
+      while (TREE_CODE (expr) == ARRAY_REF)
+	expr = TREE_OPERAND (expr, 0);
+      if (omp_parse_ref (&expr))
+	*kind = ACCESS_INDEXED_REF_TO_ARRAY;
+      else
+	*kind = ACCESS_INDEXED_ARRAY;
+    }
+  else
+    *kind = ACCESS_DIRECT;
+
+  STRIP_NOPS (expr);
+
+  *expr0 = expr;
+  return true;
+}
+
+static bool
+omp_parse_access_methods (vec<omp_addr_token *> &addr_tokens, tree *expr0)
+{
+  tree expr = *expr0;
+  enum access_method_kinds kind;
+  tree am_expr;
+
+  if (omp_parse_access_method (&expr, &kind))
+    am_expr = expr;
+
+  if (TREE_CODE (expr) == INDIRECT_REF
+      || TREE_CODE (expr) == MEM_REF
+      || TREE_CODE (expr) == ARRAY_REF)
+    omp_parse_access_methods (addr_tokens, &expr);
+
+  addr_tokens.safe_push (new omp_addr_token (kind, am_expr));
+
+  *expr0 = expr;
+  return true;
+}
+
+static bool omp_parse_structured_expr (vec<omp_addr_token *> &, tree *);
+
+static bool
+omp_parse_structure_base (vec<omp_addr_token *> &addr_tokens,
+			  tree *expr0, structure_base_kinds *kind,
+			  vec<omp_addr_token *> &base_access_tokens,
+			  bool allow_structured = true)
+{
+  tree expr = *expr0;
+
+  if (allow_structured)
+    omp_parse_access_methods (base_access_tokens, &expr);
+
+  if (DECL_P (expr))
+    {
+      *kind = BASE_DECL;
+      return true;
+    }
+
+  if (allow_structured && omp_parse_structured_expr (addr_tokens, &expr))
+    {
+      *kind = BASE_COMPONENT_EXPR;
+      *expr0 = expr;
+      return true;
+    }
+
+  *kind = BASE_ARBITRARY_EXPR;
+  *expr0 = expr;
+  return true;
+}
+
+static bool
+omp_parse_structured_expr (vec<omp_addr_token *> &addr_tokens, tree *expr0)
+{
+  tree expr = *expr0;
+  tree base_component = NULL_TREE;
+  structure_base_kinds struct_base_kind;
+  auto_vec<omp_addr_token *> base_access_tokens;
+
+  if (omp_parse_component_selector (&expr))
+    base_component = expr;
+  else
+    return false;
+
+  gcc_assert (TREE_CODE (expr) == COMPONENT_REF);
+  expr = TREE_OPERAND (expr, 0);
+
+  tree structure_base = expr;
+
+  if (!omp_parse_structure_base (addr_tokens, &expr, &struct_base_kind,
+				 base_access_tokens))
+    return false;
+
+  addr_tokens.safe_push (new omp_addr_token (STRUCTURE_BASE, struct_base_kind,
+					     structure_base));
+  addr_tokens.safe_splice (base_access_tokens);
+  addr_tokens.safe_push (new omp_addr_token (COMPONENT_SELECTOR,
+					     base_component));
+
+  *expr0 = expr;
+
+  return true;
+}
+
+static bool
+omp_parse_array_expr (vec<omp_addr_token *> &addr_tokens, tree *expr0)
+{
+  tree expr = *expr0;
+  structure_base_kinds s_kind;
+  auto_vec<omp_addr_token *> base_access_tokens;
+
+  if (!omp_parse_structure_base (addr_tokens, &expr, &s_kind,
+				 base_access_tokens, false))
+    return false;
+
+  addr_tokens.safe_push (new omp_addr_token (ARRAY_BASE, s_kind, expr));
+  addr_tokens.safe_splice (base_access_tokens);
+
+  *expr0 = expr;
+  return true;
+}
+
+/* Return TRUE if the ACCESS_METHOD token at index 'i' has a further
+   ACCESS_METHOD chained after it (e.g., if we're processing an expression
+   containing multiple pointer indirections).  */
+
+bool
+omp_access_chain_p (vec<omp_addr_token *> &addr_tokens, unsigned i)
+{
+  gcc_assert (addr_tokens[i]->type == ACCESS_METHOD);
+  return (i + 1 < addr_tokens.length ()
+	  && addr_tokens[i + 1]->type == ACCESS_METHOD);
+}
+
+/* Return the address of the object accessed by the ACCESS_METHOD token
+   at 'i': either of the next access method's expr, or of EXPR if we're at
+   the end of the list of tokens.  */
+
+tree
+omp_accessed_addr (vec<omp_addr_token *> &addr_tokens, unsigned i, tree expr)
+{
+  if (i + 1 < addr_tokens.length ())
+    return build_fold_addr_expr (addr_tokens[i + 1]->expr);
+  else
+    return build_fold_addr_expr (expr);
+}
+
+} /* namespace omp_addr_tokenizer.  */
+
+bool
+omp_parse_expr (vec<omp_addr_token *> &addr_tokens, tree expr)
+{
+  using namespace omp_addr_tokenizer;
+  auto_vec<omp_addr_token *> expr_access_tokens;
+
+  if (!omp_parse_access_methods (expr_access_tokens, &expr))
+    return false;
+
+  if (omp_parse_structured_expr (addr_tokens, &expr))
+    ;
+  else if (omp_parse_array_expr (addr_tokens, &expr))
+    ;
+  else
+    return false;
+
+  addr_tokens.safe_splice (expr_access_tokens);
+
+  return true;
+}
+
+DEBUG_FUNCTION void
+debug_omp_tokenized_addr (vec<omp_addr_token *> &addr_tokens,
+			  bool with_exprs)
+{
+  using namespace omp_addr_tokenizer;
+  const char *sep = with_exprs ? "  " : "";
+
+  for (auto e : addr_tokens)
+    {
+      const char *pfx = "";
+
+      fputs (sep, stderr);
+
+      switch (e->type)
+	{
+	case COMPONENT_SELECTOR:
+	  fputs ("component_selector", stderr);
+	  break;
+	case ACCESS_METHOD:
+	  switch (e->u.access_kind)
+	    {
+	    case ACCESS_DIRECT:
+	      fputs ("access_direct", stderr);
+	      break;
+	    case ACCESS_REF:
+	      fputs ("access_ref", stderr);
+	      break;
+	    case ACCESS_POINTER:
+	      fputs ("access_pointer", stderr);
+	      break;
+	    case ACCESS_POINTER_OFFSET:
+	      fputs ("access_pointer_offset", stderr);
+	      break;
+	    case ACCESS_REF_TO_POINTER:
+	      fputs ("access_ref_to_pointer", stderr);
+	      break;
+	    case ACCESS_REF_TO_POINTER_OFFSET:
+	      fputs ("access_ref_to_pointer_offset", stderr);
+	      break;
+	    case ACCESS_INDEXED_ARRAY:
+	      fputs ("access_indexed_array", stderr);
+	      break;
+	    case ACCESS_INDEXED_REF_TO_ARRAY:
+	      fputs ("access_indexed_ref_to_array", stderr);
+	      break;
+	    }
+	  break;
+	case ARRAY_BASE:
+	case STRUCTURE_BASE:
+	  pfx = e->type == ARRAY_BASE ? "array_" : "struct_";
+	  switch (e->u.structure_base_kind)
+	    {
+	    case BASE_DECL:
+	      fprintf (stderr, "%sbase_decl", pfx);
+	      break;
+	    case BASE_COMPONENT_EXPR:
+	      fputs ("base_component_expr", stderr);
+	      break;
+	    case BASE_ARBITRARY_EXPR:
+	      fprintf (stderr, "%sbase_arbitrary_expr", pfx);
+	      break;
+	    }
+	  break;
+	}
+      if (with_exprs)
+	{
+	  fputs (" [", stderr);
+	  print_generic_expr (stderr, e->expr);
+	  fputc (']', stderr);
+	  sep = ",\n  ";
+	}
+      else
+	sep = " ";
+    }
+
+  fputs ("\n", stderr);
+}
+
+
 #include "gt-omp-general.h"
diff --git a/gcc/omp-general.h b/gcc/omp-general.h
index 92717db16280..5c7fa3a4aafb 100644
--- a/gcc/omp-general.h
+++ b/gcc/omp-general.h
@@ -152,4 +152,73 @@ get_openacc_privatization_dump_flags ()
 
 extern tree omp_build_component_ref (tree obj, tree field);
 
+namespace omp_addr_tokenizer {
+
+/* These are the ways of accessing a variable that have special-case handling
+   in the middle end (gimplify, omp-lower, etc.).  */
+
+/* These are the kinds of access that an ACCESS_METHOD token can represent.  */
+
+enum access_method_kinds
+{
+  ACCESS_DIRECT,
+  ACCESS_REF,
+  ACCESS_POINTER,
+  ACCESS_REF_TO_POINTER,
+  ACCESS_POINTER_OFFSET,
+  ACCESS_REF_TO_POINTER_OFFSET,
+  ACCESS_INDEXED_ARRAY,
+  ACCESS_INDEXED_REF_TO_ARRAY
+};
+
+/* These are the kinds that a STRUCTURE_BASE or ARRAY_BASE (except
+   BASE_COMPONENT_EXPR) can represent.  */
+
+enum structure_base_kinds
+{
+  BASE_DECL,
+  BASE_COMPONENT_EXPR,
+  BASE_ARBITRARY_EXPR
+};
+
+/* The coarse type for an address token.  These can have subtypes for
+   ARRAY_BASE or STRUCTURE_BASE (structure_base_kinds) or ACCESS_METHOD
+   (access_method_kinds).  */
+
+enum token_type
+{
+  ARRAY_BASE,
+  STRUCTURE_BASE,
+  COMPONENT_SELECTOR,
+  ACCESS_METHOD
+};
+
+/* The struct that forms a single token of an address expression as parsed by
+   omp_parse_expr.  These are typically held in a vec after parsing.  */
+
+struct omp_addr_token
+{
+  enum token_type type;
+  tree expr;
+
+  union
+  {
+    access_method_kinds access_kind;
+    structure_base_kinds structure_base_kind;
+  } u;
+
+  omp_addr_token (token_type, tree);
+  omp_addr_token (access_method_kinds, tree);
+  omp_addr_token (token_type, structure_base_kinds, tree);
+};
+
+extern bool omp_access_chain_p (vec<omp_addr_token *> &, unsigned);
+extern tree omp_accessed_addr (vec<omp_addr_token *> &, unsigned, tree);
+
+}
+
+typedef omp_addr_tokenizer::omp_addr_token omp_addr_token;
+
+extern bool omp_parse_expr (vec<omp_addr_token *> &, tree);
+
 #endif /* GCC_OMP_GENERAL_H */
diff --git a/gcc/omp-low.cc b/gcc/omp-low.cc
index b882df048ef2..2b2aa7f13146 100644
--- a/gcc/omp-low.cc
+++ b/gcc/omp-low.cc
@@ -1603,10 +1603,13 @@ scan_sharing_clauses (tree clauses, omp_context *ctx)
 	    {
 	      /* If this is an offloaded region, an attach operation should
 		 only exist when the pointer variable is mapped in a prior
-		 clause.
+		 clause.  An exception is if we have a reference (to pointer):
+		 in that case we should have mapped "*decl" in a previous
+		 mapping instead of "decl".  Skip the assertion in that case.
 		 If we had an error, we may not have attempted to sort clauses
 		 properly, so avoid the test.  */
-	      if (is_gimple_omp_offloaded (ctx->stmt)
+	      if (TREE_CODE (TREE_TYPE (decl)) != REFERENCE_TYPE
+		  && is_gimple_omp_offloaded (ctx->stmt)
 		  && !seen_error ())
 		gcc_assert
 		  (maybe_lookup_decl (decl, ctx)
diff --git a/gcc/testsuite/c-c++-common/gomp/clauses-2.c b/gcc/testsuite/c-c++-common/gomp/clauses-2.c
index bbc8fb4e32bf..8f98d57a312f 100644
--- a/gcc/testsuite/c-c++-common/gomp/clauses-2.c
+++ b/gcc/testsuite/c-c++-common/gomp/clauses-2.c
@@ -11,7 +11,7 @@ foo (int *p, int q, struct S t, int i, int j, int k, int l)
     bar (p);
   #pragma omp target firstprivate (p), map (p[0]) /* { dg-error "appears more than once in data clauses" } */
     bar (p);
-  #pragma omp target map (p[0]) map (p) /* { dg-error "appears both in data and map clauses" } */
+  #pragma omp target map (p[0]) map (p)
     bar (p);
   #pragma omp target map (p) , map (p[0])
     bar (p);
diff --git a/gcc/testsuite/c-c++-common/gomp/target-50.c b/gcc/testsuite/c-c++-common/gomp/target-50.c
index 41f1d37845cf..a30a25e08936 100644
--- a/gcc/testsuite/c-c++-common/gomp/target-50.c
+++ b/gcc/testsuite/c-c++-common/gomp/target-50.c
@@ -17,7 +17,7 @@ int main()
 
   #pragma omp target map(tofrom: tmp->arr[0:10]) map(to: tmp->arr)
   { }
-/* { dg-final { scan-tree-dump-times {map\(struct:\*tmp \[len: 1\]\) map\(to:tmp[._0-9]*->arr \[len: [0-9]+\]\) map\(tofrom:\*_[0-9]+ \[len: [0-9]+\]\) map\(attach:tmp[._0-9]*->arr \[bias: 0\]\)} 2 "gimple" { target { ! { nvptx*-*-* amdgcn*-*-* } } } } } */
+/* { dg-final { scan-tree-dump-times {map\(struct:\*tmp \[len: 1\]\) map\(alloc:tmp[._0-9]*->arr \[len: [0-9]+\]\) map\(tofrom:\*_[0-9]+ \[len: [0-9]+\]\) map\(attach:tmp[._0-9]*->arr \[bias: 0\]\)} 2 "gimple" { target { ! { nvptx*-*-* amdgcn*-*-* } } } } } */
 
   return 0;
 }
diff --git a/gcc/testsuite/c-c++-common/gomp/target-enter-data-1.c b/gcc/testsuite/c-c++-common/gomp/target-enter-data-1.c
index ce766d29e2dc..37c8dd4e3285 100644
--- a/gcc/testsuite/c-c++-common/gomp/target-enter-data-1.c
+++ b/gcc/testsuite/c-c++-common/gomp/target-enter-data-1.c
@@ -21,4 +21,5 @@ void func (struct foo *f, int n, int m)
   #pragma omp target enter data map (to: f->bars[n].vectors[:f->bars[n].num_vectors])
 }
 
-/* { dg-final { scan-tree-dump-times "map\\(to:\\*_\[0-9\]+ \\\[len: _\[0-9\]+\\\]\\) map\\(attach:\[^-\]+->vectors \\\[bias: \[^\]\]+\\\]\\)" 3 "gimple" } } */
+/* { dg-final { scan-tree-dump-times {map\(struct:\*f \[len: 1\]\) map\(alloc:[a-z0-9\._]+->vectors \[len: 0\]\) map\(to:\*_[0-9]+ \[len: _[0-9]+\]\) map\(attach:[a-z0-9\._]+->vectors \[bias: [^\]]+\]\) map\(attach:\*_[0-9]+ \[bias: 0\]\)} 1 "gimple" } } */
+/* { dg-final { scan-tree-dump-times {map\(struct:\*\(f->bars \+ \(sizetype\) \(\([^\)]+\) n \* 16\)\) \[len: 1\]\) map\(alloc:[a-z0-9\._]+->vectors \[len: 0\]\) map\(to:\*_[0-9]+ \[len: _[0-9]+\]\) map\(attach:[a-z0-9\._]+->vectors \[bias: [^\]]+\]\)} 2 "gimple" } } */
diff --git a/gcc/testsuite/c-c++-common/gomp/target-implicit-map-2.c b/gcc/testsuite/c-c++-common/gomp/target-implicit-map-2.c
index 3aa1a8fc55ec..222272df5b1e 100644
--- a/gcc/testsuite/c-c++-common/gomp/target-implicit-map-2.c
+++ b/gcc/testsuite/c-c++-common/gomp/target-implicit-map-2.c
@@ -49,4 +49,5 @@ main (void)
 
 /* { dg-final { scan-tree-dump {#pragma omp target num_teams.* map\(tofrom:a \[len: [0-9]+\]\[implicit\]\)} "gimple" } } */
 
-/* { dg-final { scan-tree-dump {#pragma omp target num_teams.* map\(tofrom:a \[len: [0-9]+\]\[implicit\]\) map\(tofrom:\*_[0-9]+ \[len: [0-9]+\]\) map\(attach:a\.ptr \[bias: 0\]\)} "gimple" } } */
+/* { dg-final { scan-tree-dump {#pragma omp target num_teams.* map\(struct:a \[len: 1\]\) map\(alloc:a\.ptr \[len: [0-9]+\]\) map\(tofrom:\*_[0-9]+ \[len: [0-9]+\]\) map\(attach:a\.ptr \[bias: 0\]\)} "gimple" } } */
+/* { dg-final { scan-tree-dump-not {map\(struct:a \[len: 1\]\) map\(alloc:a\.ptr \[len: 0\]\)} "gimple" } } */
diff --git a/gcc/testsuite/g++.dg/gomp/static-component-1.C b/gcc/testsuite/g++.dg/gomp/static-component-1.C
new file mode 100644
index 000000000000..c2f959335674
--- /dev/null
+++ b/gcc/testsuite/g++.dg/gomp/static-component-1.C
@@ -0,0 +1,23 @@
+/* { dg-do compile } */
+
+/* Types with static members should be mappable.  */
+
+struct A {
+  static int x[10];
+};
+
+struct B {
+  A a;
+};
+
+int
+main (int argc, char *argv[])
+{
+  B *b = new B;
+#pragma omp target map(b->a)
+  ;
+  B bb;
+#pragma omp target map(bb.a)
+  ;
+  delete b;
+}
diff --git a/gcc/testsuite/gcc.dg/gomp/target-3.c b/gcc/testsuite/gcc.dg/gomp/target-3.c
index 3e7921270c92..3d5e05f85710 100644
--- a/gcc/testsuite/gcc.dg/gomp/target-3.c
+++ b/gcc/testsuite/gcc.dg/gomp/target-3.c
@@ -13,4 +13,4 @@ void foo (struct S *s)
   #pragma omp target enter data map (alloc: s->a, s->b)
 }
 
-/* { dg-final { scan-tree-dump-times "map\\(struct:\\*s \\\[len: 2\\\]\\) map\\(alloc:s->a \\\[len: \[0-9\]+\\\]\\) map\\(alloc:s->b \\\[len: \[0-9\]+\\\]\\)" 2 "gimple" } } */
+/* { dg-final { scan-tree-dump-times "map\\(struct:\\*s \\\[len: 2\\\]\\) map\\(alloc:s\[\\._0-9\]+->a \\\[len: \[0-9\]+\\\]\\) map\\(alloc:s\[\\._0-9\]+->b \\\[len: \[0-9\]+\\\]\\)" 2 "gimple" } } */
diff --git a/gcc/testsuite/gfortran.dg/gomp/map-9.f90 b/gcc/testsuite/gfortran.dg/gomp/map-9.f90
index b770b931beef..f930a49d9fff 100644
--- a/gcc/testsuite/gfortran.dg/gomp/map-9.f90
+++ b/gcc/testsuite/gfortran.dg/gomp/map-9.f90
@@ -2,7 +2,7 @@
 
 ! PR fortran/108545
 
-! { dg-final { scan-tree-dump "#pragma omp target enter data map\\(struct:x \\\[len: 1\\\]\\) map\\(always,to:x.a \\\[len: \[0-9\]+\\\]\\) map\\(to:MEM <integer\\(kind=4\\)\\\[0:\\\]> \\\[\\(integer\\(kind=4\\)\\\[0:\\\] \\*\\)_\[0-9\]+] \\\[len: _\[0-9\]+\\\]\\) map\\(always_pointer:x.a.data \\\[pointer assign, bias: 0\\\]\\)" "omplower" } }
+! { dg-final { scan-tree-dump "#pragma omp target enter data map\\(struct:x \\\[len: 1\\\]\\) map\\(always,to:x\.a \\\[len: \[0-9\]+\\\]\\) map\\(to:MEM <integer\\(kind=4\\)\\\[0:\\\]> \\\[\\(integer\\(kind=4\\)\\\[0:\\\] \\*\\)_\[0-9\]+] \\\[len: _\[0-9\]+\\\]\\) map\\(attach:x\.a\.data \\\[bias: 0\\\]\\)" "omplower" } }
 
 program p
    type t
diff --git a/gcc/tree.h b/gcc/tree.h
index 4c04245e2b1b..c4f7d56bf32e 100644
--- a/gcc/tree.h
+++ b/gcc/tree.h
@@ -1805,6 +1805,10 @@ class auto_suppress_location_wrappers
    NOTE: this is different than OMP_CLAUSE_MAP_IMPLICIT.  */
 #define OMP_CLAUSE_MAP_RUNTIME_IMPLICIT_P(NODE) \
   (OMP_CLAUSE_SUBCODE_CHECK (NODE, OMP_CLAUSE_MAP)->base.deprecated_flag)
+/* Nonzero for an attach/detach node whose decl was explicitly mapped on the
+   same directive.  */
+#define OMP_CLAUSE_ATTACHMENT_MAPPING_ERASED(NODE) \
+  TREE_STATIC (OMP_CLAUSE_SUBCODE_CHECK (NODE, OMP_CLAUSE_MAP))
 
 /* Flag that 'OMP_CLAUSE_DECL (NODE)' is to be made addressable during OMP
    lowering.  */
diff --git a/libgomp/target.c b/libgomp/target.c
index cd4cc1b01ca7..b9137e703045 100644
--- a/libgomp/target.c
+++ b/libgomp/target.c
@@ -702,7 +702,7 @@ gomp_map_pointer (struct target_mem_desc *tgt, struct goacc_asyncqueue *aq,
   if (n == NULL)
     {
       if (allow_zero_length_array_sections)
-	cur_node.tgt_offset = 0;
+	cur_node.tgt_offset = cur_node.host_start;
       else
 	{
 	  gomp_mutex_unlock (&devicep->lock);
@@ -741,7 +741,7 @@ gomp_map_fields_existing (struct target_mem_desc *tgt,
 
   cur_node.host_start = (uintptr_t) hostaddrs[i];
   cur_node.host_end = cur_node.host_start + sizes[i];
-  splay_tree_key n2 = splay_tree_lookup (mem_map, &cur_node);
+  splay_tree_key n2 = gomp_map_0len_lookup (mem_map, &cur_node);
   kind = get_kind (short_mapkind, kinds, i);
   implicit = get_implicit (short_mapkind, kinds, i);
   if (n2
@@ -838,8 +838,20 @@ gomp_attach_pointer (struct gomp_device_descr *devicep,
 
       if ((void *) target == NULL)
 	{
-	  gomp_mutex_unlock (&devicep->lock);
-	  gomp_fatal ("attempt to attach null pointer");
+	  /* As a special case, allow attaching NULL host pointers.  This
+	     allows e.g. unassociated Fortran pointers to be mapped
+	     properly.  */
+	  data = 0;
+
+	  gomp_debug (1,
+		      "%s: attaching NULL host pointer, target %p "
+		      "(struct base %p)\n", __FUNCTION__, (void *) devptr,
+		      (void *) (n->tgt->tgt_start + n->tgt_offset));
+
+	  gomp_copy_host2dev (devicep, aq, (void *) devptr, (void *) &data,
+			      sizeof (void *), true, cbufp);
+
+	  return;
 	}
 
       s.host_start = target + bias;
@@ -850,9 +862,8 @@ gomp_attach_pointer (struct gomp_device_descr *devicep,
 	{
 	  if (allow_zero_length_array_sections)
 	    /* When allowing attachment to zero-length array sections, we
-	       allow attaching to NULL pointers when the target region is not
-	       mapped.  */
-	    data = 0;
+	       copy the host pointer when the target region is not mapped.  */
+	    data = target;
 	  else
 	    {
 	      gomp_mutex_unlock (&devicep->lock);
@@ -1096,7 +1107,8 @@ gomp_map_vars_internal (struct gomp_device_descr *devicep,
 		  tgt->list[i].key = NULL;
 		  if (!aq
 		      && gomp_to_device_kind_p (get_kind (short_mapkind, kinds, i)
-						& typemask))
+						& typemask)
+		      && sizes[i] != 0)
 		    gomp_coalesce_buf_add (&cbuf,
 					   tgt_size - cur_node.host_end
 					   + (uintptr_t) hostaddrs[i],
@@ -1463,7 +1475,17 @@ gomp_map_vars_internal (struct gomp_device_descr *devicep,
 				    + sizes[last];
 		if (tgt->list[first].key != NULL)
 		  continue;
+		if (sizes[last] == 0)
+		  cur_node.host_end++;
 		n = splay_tree_lookup (mem_map, &cur_node);
+		if (sizes[last] == 0)
+		  cur_node.host_end--;
+		if (n == NULL && cur_node.host_start == cur_node.host_end)
+		  {
+		    gomp_mutex_unlock (&devicep->lock);
+		    gomp_fatal ("Struct pointer member not mapped (%p)",
+				(void*) hostaddrs[first]);
+		  }
 		if (n == NULL)
 		  {
 		    size_t align = (size_t) 1 << (kind >> rshift);
diff --git a/libgomp/testsuite/libgomp.c++/baseptrs-3.C b/libgomp/testsuite/libgomp.c++/baseptrs-3.C
new file mode 100644
index 000000000000..39a48a40920a
--- /dev/null
+++ b/libgomp/testsuite/libgomp.c++/baseptrs-3.C
@@ -0,0 +1,275 @@
+#include <cstdlib>
+#include <cstring>
+#include <cassert>
+
+struct sa0
+{
+  int *ptr;
+};
+
+struct sb0
+{
+  int arr[10];
+};
+
+struct sc0
+{
+  sa0 a;
+  sb0 b;
+  sc0 (sa0 &my_a, sb0 &my_b) : a(my_a), b(my_b) {}
+};
+
+void
+foo0 ()
+{
+  sa0 my_a;
+  sb0 my_b;
+
+  my_a.ptr = (int *) malloc (sizeof (int) * 10);
+  sc0 my_c(my_a, my_b);
+
+  memset (my_c.a.ptr, 0, sizeof (int) * 10);
+
+  #pragma omp target map (my_c.a.ptr, my_c.a.ptr[:10])
+  {
+    for (int i = 0; i < 10; i++)
+      my_c.a.ptr[i] = i;
+  }
+
+  for (int i = 0; i < 10; i++)
+    assert (my_c.a.ptr[i] == i);
+
+  memset (my_c.b.arr, 0, sizeof (int) * 10);
+
+  #pragma omp target map (my_c.b.arr[:10])
+  {
+    for (int i = 0; i < 10; i++)
+      my_c.b.arr[i] = i;
+  }
+
+  for (int i = 0; i < 10; i++)
+    assert (my_c.b.arr[i] == i);
+
+  free (my_a.ptr);
+}
+
+struct sa
+{
+  int *ptr;
+};
+
+struct sb
+{
+  int arr[10];
+};
+
+struct sc
+{
+  sa &a;
+  sb &b;
+  sc (sa &my_a, sb &my_b) : a(my_a), b(my_b) {}
+};
+
+void
+foo ()
+{
+  sa my_a;
+  sb my_b;
+
+  my_a.ptr = (int *) malloc (sizeof (int) * 10);
+  sc my_c(my_a, my_b);
+
+  memset (my_c.a.ptr, 0, sizeof (int) * 10);
+
+  #pragma omp target map (my_c.a.ptr, my_c.a.ptr[:10])
+  {
+    for (int i = 0; i < 10; i++)
+      my_c.a.ptr[i] = i;
+  }
+
+  for (int i = 0; i < 10; i++)
+    assert (my_c.a.ptr[i] == i);
+
+  memset (my_c.b.arr, 0, sizeof (int) * 10);
+
+  #pragma omp target map (my_c.b.arr[:10])
+  {
+    for (int i = 0; i < 10; i++)
+      my_c.b.arr[i] = i;
+  }
+
+  for (int i = 0; i < 10; i++)
+    assert (my_c.b.arr[i] == i);
+
+  free (my_a.ptr);
+}
+
+void
+bar ()
+{
+  sa my_a;
+  sb my_b;
+
+  my_a.ptr = (int *) malloc (sizeof (int) * 10);
+  sc my_c(my_a, my_b);
+  sc &my_cref = my_c;
+
+  memset (my_cref.a.ptr, 0, sizeof (int) * 10);
+
+  #pragma omp target map (my_cref.a.ptr, my_cref.a.ptr[:10])
+  {
+    for (int i = 0; i < 10; i++)
+      my_cref.a.ptr[i] = i;
+  }
+
+  for (int i = 0; i < 10; i++)
+    assert (my_cref.a.ptr[i] == i);
+
+  memset (my_cref.b.arr, 0, sizeof (int) * 10);
+
+  #pragma omp target map (my_cref.b.arr[:10])
+  {
+    for (int i = 0; i < 10; i++)
+      my_cref.b.arr[i] = i;
+  }
+
+  for (int i = 0; i < 10; i++)
+    assert (my_cref.b.arr[i] == i);
+
+  free (my_a.ptr);
+}
+
+struct scp0
+{
+  sa *a;
+  sb *b;
+  scp0 (sa *my_a, sb *my_b) : a(my_a), b(my_b) {}
+};
+
+void
+foop0 ()
+{
+  sa *my_a = new sa;
+  sb *my_b = new sb;
+
+  my_a->ptr = new int[10];
+  scp0 *my_c = new scp0(my_a, my_b);
+
+  memset (my_c->a->ptr, 0, sizeof (int) * 10);
+
+  #pragma omp target map (my_c->a, my_c->a[:1], my_c->a->ptr, my_c->a->ptr[:10])
+  {
+    for (int i = 0; i < 10; i++)
+      my_c->a->ptr[i] = i;
+  }
+
+  for (int i = 0; i < 10; i++)
+    assert (my_c->a->ptr[i] == i);
+
+  memset (my_c->b->arr, 0, sizeof (int) * 10);
+
+  #pragma omp target map (my_c->b, my_c->b[:1], my_c->b->arr[:10])
+  {
+    for (int i = 0; i < 10; i++)
+      my_c->b->arr[i] = i;
+  }
+
+  for (int i = 0; i < 10; i++)
+    assert (my_c->b->arr[i] == i);
+
+  delete[] my_a->ptr;
+  delete my_a;
+  delete my_b;
+}
+
+struct scp
+{
+  sa *&a;
+  sb *&b;
+  scp (sa *&my_a, sb *&my_b) : a(my_a), b(my_b) {}
+};
+
+void
+foop ()
+{
+  sa *my_a = new sa;
+  sb *my_b = new sb;
+
+  my_a->ptr = new int[10];
+  scp *my_c = new scp(my_a, my_b);
+
+  memset (my_c->a->ptr, 0, sizeof (int) * 10);
+
+  #pragma omp target map (my_c->a, my_c->a[:1], my_c->a->ptr, my_c->a->ptr[:10])
+  {
+    for (int i = 0; i < 10; i++)
+      my_c->a->ptr[i] = i;
+  }
+
+  for (int i = 0; i < 10; i++)
+    assert (my_c->a->ptr[i] == i);
+
+  memset (my_c->b->arr, 0, sizeof (int) * 10);
+
+  #pragma omp target map (my_c->b, my_c->b[:1], my_c->b->arr[:10])
+  {
+    for (int i = 0; i < 10; i++)
+      my_c->b->arr[i] = i;
+  }
+
+  for (int i = 0; i < 10; i++)
+    assert (my_c->b->arr[i] == i);
+
+  delete[] my_a->ptr;
+  delete my_a;
+  delete my_b;
+}
+
+void
+barp ()
+{
+  sa *my_a = new sa;
+  sb *my_b = new sb;
+
+  my_a->ptr = new int[10];
+  scp *my_c = new scp(my_a, my_b);
+  scp *&my_cref = my_c;
+
+  memset (my_cref->a->ptr, 0, sizeof (int) * 10);
+
+  #pragma omp target map (my_cref->a, my_cref->a[:1], my_cref->a->ptr, \
+			  my_cref->a->ptr[:10])
+  {
+    for (int i = 0; i < 10; i++)
+      my_cref->a->ptr[i] = i;
+  }
+
+  for (int i = 0; i < 10; i++)
+    assert (my_cref->a->ptr[i] == i);
+
+  memset (my_cref->b->arr, 0, sizeof (int) * 10);
+
+  #pragma omp target map (my_cref->b, my_cref->b[:1], my_cref->b->arr[:10])
+  {
+    for (int i = 0; i < 10; i++)
+      my_cref->b->arr[i] = i;
+  }
+
+  for (int i = 0; i < 10; i++)
+    assert (my_cref->b->arr[i] == i);
+
+  delete my_a->ptr;
+  delete my_a;
+  delete my_b;
+}
+
+int main (int argc, char *argv[])
+{
+  foo0 ();
+  foo ();
+  bar ();
+  foop0 ();
+  foop ();
+  barp ();
+  return 0;
+}
diff --git a/libgomp/testsuite/libgomp.c++/baseptrs-4.C b/libgomp/testsuite/libgomp.c++/baseptrs-4.C
new file mode 100644
index 000000000000..196029ac1868
--- /dev/null
+++ b/libgomp/testsuite/libgomp.c++/baseptrs-4.C
@@ -0,0 +1,3154 @@
+// { dg-do run }
+
+#include <cstring>
+#include <cassert>
+
+#define MAP_DECLS
+
+#define NONREF_DECL_BASE
+#define REF_DECL_BASE
+#define PTR_DECL_BASE
+#define REF2PTR_DECL_BASE
+
+#define ARRAY_DECL_BASE
+// Needs map clause "lvalue"-parsing support.
+//#define REF2ARRAY_DECL_BASE
+#define PTR_OFFSET_DECL_BASE
+// Needs map clause "lvalue"-parsing support.
+//#define REF2PTR_OFFSET_DECL_BASE
+
+#define MAP_SECTIONS
+
+#define NONREF_DECL_MEMBER_SLICE
+#define NONREF_DECL_MEMBER_SLICE_BASEPTR
+#define REF_DECL_MEMBER_SLICE
+#define REF_DECL_MEMBER_SLICE_BASEPTR
+#define PTR_DECL_MEMBER_SLICE
+#define PTR_DECL_MEMBER_SLICE_BASEPTR
+#define REF2PTR_DECL_MEMBER_SLICE
+#define REF2PTR_DECL_MEMBER_SLICE_BASEPTR
+
+#define ARRAY_DECL_MEMBER_SLICE
+#define ARRAY_DECL_MEMBER_SLICE_BASEPTR
+// Needs map clause "lvalue"-parsing support.
+//#define REF2ARRAY_DECL_MEMBER_SLICE
+//#define REF2ARRAY_DECL_MEMBER_SLICE_BASEPTR
+#define PTR_OFFSET_DECL_MEMBER_SLICE
+#define PTR_OFFSET_DECL_MEMBER_SLICE_BASEPTR
+// Needs map clause "lvalue"-parsing support.
+//#define REF2PTR_OFFSET_DECL_MEMBER_SLICE
+//#define REF2PTR_OFFSET_DECL_MEMBER_SLICE_BASEPTR
+
+#define PTRARRAY_DECL_MEMBER_SLICE
+#define PTRARRAY_DECL_MEMBER_SLICE_BASEPTR
+// Needs map clause "lvalue"-parsing support.
+//#define REF2PTRARRAY_DECL_MEMBER_SLICE
+//#define REF2PTRARRAY_DECL_MEMBER_SLICE_BASEPTR
+#define PTRPTR_OFFSET_DECL_MEMBER_SLICE
+#define PTRPTR_OFFSET_DECL_MEMBER_SLICE_BASEPTR
+// Needs map clause "lvalue"-parsing support.
+//#define REF2PTRPTR_OFFSET_DECL_MEMBER_SLICE
+//#define REF2PTRPTR_OFFSET_DECL_MEMBER_SLICE_BASEPTR
+
+#define NONREF_COMPONENT_BASE
+#define NONREF_COMPONENT_MEMBER_SLICE
+#define NONREF_COMPONENT_MEMBER_SLICE_BASEPTR
+
+#define REF_COMPONENT_BASE
+#define REF_COMPONENT_MEMBER_SLICE
+#define REF_COMPONENT_MEMBER_SLICE_BASEPTR
+
+#define PTR_COMPONENT_BASE
+#define PTR_COMPONENT_MEMBER_SLICE
+#define PTR_COMPONENT_MEMBER_SLICE_BASEPTR
+
+#define REF2PTR_COMPONENT_BASE
+#define REF2PTR_COMPONENT_MEMBER_SLICE
+#define REF2PTR_COMPONENT_MEMBER_SLICE_BASEPTR
+
+#ifdef MAP_DECLS
+void
+map_decls (void)
+{
+  int x = 0;
+  int &y = x;
+  int arr[4];
+  int (&arrref)[4] = arr;
+  int *z = &arr[0];
+  int *&t = z;
+
+  memset (arr, 0, sizeof arr);
+
+  #pragma omp target map(x)
+  {
+    x++;
+  }
+
+  #pragma omp target map(y)
+  {
+    y++;
+  }
+
+  assert (x == 2);
+  assert (y == 2);
+
+  /* "A variable that is of type pointer is treated as if it is the base
+      pointer of a zero-length array section that appeared as a list item in a
+      map clause."  */
+  #pragma omp target map(z)
+  {
+    z++;
+  }
+
+  /* "A variable that is of type reference to pointer is treated as if it had
+      appeared in a map clause as a zero-length array section."
+
+     The pointer here is *not* associated with a target address, so we're not
+     disallowed from modifying it.  */
+  #pragma omp target map(t)
+  {
+    t++;
+  }
+
+  assert (z == &arr[2]);
+  assert (t == &arr[2]);
+
+  #pragma omp target map(arr)
+  {
+    arr[2]++;
+  }
+
+  #pragma omp target map(arrref)
+  {
+    arrref[2]++;
+  }
+
+  assert (arr[2] == 2);
+  assert (arrref[2] == 2);
+}
+#endif
+
+struct S {
+  int a;
+  int &b;
+  int *c;
+  int *&d;
+  int e[4];
+  int (&f)[4];
+
+  S(int a1, int &b1, int *c1, int *&d1) :
+    a(a1), b(b1), c(c1), d(d1), f(e)
+  {
+    memset (e, 0, sizeof e);
+  }
+};
+
+#ifdef NONREF_DECL_BASE
+void
+nonref_decl_base (void)
+{
+  int a = 0, b = 0, c, *d = &c;
+  S mys(a, b, &c, d);
+
+  #pragma omp target map(mys.a)
+  {
+    mys.a++;
+  }
+
+  #pragma omp target map(mys.b)
+  {
+    mys.b++;
+  }
+
+  assert (mys.a == 1);
+  assert (mys.b == 1);
+
+  #pragma omp target map(mys.c)
+  {
+    mys.c++;
+  }
+
+  #pragma omp target map(mys.d)
+  {
+    mys.d++;
+  }
+
+  assert (mys.c == &c + 1);
+  assert (mys.d == &c + 1);
+
+  #pragma omp target map(mys.e)
+  {
+    mys.e[0]++;
+  }
+
+  #pragma omp target map(mys.f)
+  {
+    mys.f[0]++;
+  }
+
+  assert (mys.e[0] == 2);
+  assert (mys.f[0] == 2);
+}
+#endif
+
+#ifdef REF_DECL_BASE
+void
+ref_decl_base (void)
+{
+  int a = 0, b = 0, c, *d = &c;
+  S mys_orig(a, b, &c, d);
+  S &mys = mys_orig;
+
+  #pragma omp target map(mys.a)
+  {
+    mys.a++;
+  }
+
+  #pragma omp target map(mys.b)
+  {
+    mys.b++;
+  }
+
+  assert (mys.a == 1);
+  assert (mys.b == 1);
+
+  #pragma omp target map(mys.c)
+  {
+    mys.c++;
+  }
+
+  #pragma omp target map(mys.d)
+  {
+    mys.d++;
+  }
+
+  assert (mys.c == &c + 1);
+  assert (mys.d == &c + 1);
+
+  #pragma omp target map(mys.e)
+  {
+    mys.e[0]++;
+  }
+
+  #pragma omp target map(mys.f)
+  {
+    mys.f[0]++;
+  }
+
+  assert (mys.e[0] == 2);
+  assert (mys.f[0] == 2);
+}
+#endif
+
+#ifdef PTR_DECL_BASE
+void
+ptr_decl_base (void)
+{
+  int a = 0, b = 0, c, *d = &c;
+  S mys_orig(a, b, &c, d);
+  S *mys = &mys_orig;
+
+  #pragma omp target map(mys->a)
+  {
+    mys->a++;
+  }
+
+  #pragma omp target map(mys->b)
+  {
+    mys->b++;
+  }
+
+  assert (mys->a == 1);
+  assert (mys->b == 1);
+
+  #pragma omp target map(mys->c)
+  {
+    mys->c++;
+  }
+
+  #pragma omp target map(mys->d)
+  {
+    mys->d++;
+  }
+
+  assert (mys->c == &c + 1);
+  assert (mys->d == &c + 1);
+
+  #pragma omp target map(mys->e)
+  {
+    mys->e[0]++;
+  }
+
+  #pragma omp target map(mys->f)
+  {
+    mys->f[0]++;
+  }
+
+  assert (mys->e[0] == 2);
+  assert (mys->f[0] == 2);
+}
+#endif
+
+#ifdef REF2PTR_DECL_BASE
+void
+ref2ptr_decl_base (void)
+{
+  int a = 0, b = 0, c, *d = &c;
+  S mys_orig(a, b, &c, d);
+  S *mysp = &mys_orig;
+  S *&mys = mysp;
+
+  #pragma omp target map(mys->a)
+  {
+    mys->a++;
+  }
+
+  #pragma omp target map(mys->b)
+  {
+    mys->b++;
+  }
+
+  assert (mys->a == 1);
+  assert (mys->b == 1);
+
+  #pragma omp target map(mys->c)
+  {
+    mys->c++;
+  }
+
+  #pragma omp target map(mys->d)
+  {
+    mys->d++;
+  }
+
+  assert (mys->c == &c + 1);
+  assert (mys->d == &c + 1);
+
+  #pragma omp target map(mys->e)
+  {
+    mys->e[0]++;
+  }
+
+  #pragma omp target map(mys->f)
+  {
+    mys->f[0]++;
+  }
+
+  assert (mys->e[0] == 2);
+  assert (mys->f[0] == 2);
+}
+#endif
+
+#ifdef ARRAY_DECL_BASE
+void
+array_decl_base (void)
+{
+  int a = 0, b = 0, c, *d = &c;
+  S mys[4] =
+    {
+      S(a, b, &c, d),
+      S(a, b, &c, d),
+      S(a, b, &c, d),
+      S(a, b, &c, d)
+    };
+
+  #pragma omp target map(mys[2].a)
+  {
+    mys[2].a++;
+  }
+
+  #pragma omp target map(mys[2].b)
+  {
+    mys[2].b++;
+  }
+
+  assert (mys[2].a == 1);
+  assert (mys[2].b == 1);
+
+  #pragma omp target map(mys[2].c)
+  {
+    mys[2].c++;
+  }
+
+  #pragma omp target map(mys[2].d)
+  {
+    mys[2].d++;
+  }
+
+  assert (mys[2].c == &c + 1);
+  assert (mys[2].d == &c + 1);
+
+  #pragma omp target map(mys[2].e)
+  {
+    mys[2].e[0]++;
+  }
+
+  #pragma omp target map(mys[2].f)
+  {
+    mys[2].f[0]++;
+  }
+
+  assert (mys[2].e[0] == 2);
+  assert (mys[2].f[0] == 2);
+}
+#endif
+
+#ifdef REF2ARRAY_DECL_BASE
+void
+ref2array_decl_base (void)
+{
+  int a = 0, b = 0, c, *d = &c;
+  S mys_orig[4] =
+    {
+      S(a, b, &c, d),
+      S(a, b, &c, d),
+      S(a, b, &c, d),
+      S(a, b, &c, d)
+    };
+  S (&mys)[4] = mys_orig;
+
+  #pragma omp target map(mys[2].a)
+  {
+    mys[2].a++;
+  }
+
+  #pragma omp target map(mys[2].b)
+  {
+    mys[2].b++;
+  }
+
+  assert (mys[2].a == 1);
+  assert (mys[2].b == 1);
+
+  #pragma omp target map(mys[2].c)
+  {
+    mys[2].c++;
+  }
+
+  #pragma omp target map(mys[2].d)
+  {
+    mys[2].d++;
+  }
+
+  assert (mys[2].c == &c + 1);
+  assert (mys[2].d == &c + 1);
+
+  #pragma omp target map(mys[2].e)
+  {
+    mys[2].e[0]++;
+  }
+
+  #pragma omp target map(mys[2].f)
+  {
+    mys[2].f[0]++;
+  }
+
+  assert (mys[2].e[0] == 2);
+  assert (mys[2].f[0] == 2);
+}
+#endif
+
+#ifdef PTR_OFFSET_DECL_BASE
+void
+ptr_offset_decl_base (void)
+{
+  int a = 0, b = 0, c, *d = &c;
+  S mys_orig[4] =
+    {
+      S(a, b, &c, d),
+      S(a, b, &c, d),
+      S(a, b, &c, d),
+      S(a, b, &c, d)
+    };
+  S *mys = &mys_orig[0];
+
+  #pragma omp target map(mys[2].a)
+  {
+    mys[2].a++;
+  }
+
+  #pragma omp target map(mys[2].b)
+  {
+    mys[2].b++;
+  }
+
+  assert (mys[2].a == 1);
+  assert (mys[2].b == 1);
+
+  #pragma omp target map(mys[2].c)
+  {
+    mys[2].c++;
+  }
+
+  #pragma omp target map(mys[2].d)
+  {
+    mys[2].d++;
+  }
+
+  assert (mys[2].c == &c + 1);
+  assert (mys[2].d == &c + 1);
+
+  #pragma omp target map(mys[2].e)
+  {
+    mys[2].e[0]++;
+  }
+
+  #pragma omp target map(mys[2].f)
+  {
+    mys[2].f[0]++;
+  }
+
+  assert (mys[2].e[0] == 2);
+  assert (mys[2].f[0] == 2);
+}
+#endif
+
+#ifdef REF2PTR_OFFSET_DECL_BASE
+void
+ref2ptr_offset_decl_base (void)
+{
+  int a = 0, b = 0, c, *d = &c;
+  S mys_orig[4] =
+    {
+      S(a, b, &c, d),
+      S(a, b, &c, d),
+      S(a, b, &c, d),
+      S(a, b, &c, d)
+    };
+  S *mys_ptr = &mys_orig[0];
+  S *&mys = mys_ptr;
+
+  #pragma omp target map(mys[2].a)
+  {
+    mys[2].a++;
+  }
+
+  #pragma omp target map(mys[2].b)
+  {
+    mys[2].b++;
+  }
+
+  assert (mys[2].a == 1);
+  assert (mys[2].b == 1);
+
+  #pragma omp target map(mys[2].c)
+  {
+    mys[2].c++;
+  }
+
+  #pragma omp target map(mys[2].d)
+  {
+    mys[2].d++;
+  }
+
+  assert (mys[2].c == &c + 1);
+  assert (mys[2].d == &c + 1);
+
+  #pragma omp target map(mys[2].e)
+  {
+    mys[2].e[0]++;
+  }
+
+  #pragma omp target map(mys[2].f)
+  {
+    mys[2].f[0]++;
+  }
+
+  assert (mys[2].e[0] == 2);
+  assert (mys[2].f[0] == 2);
+}
+#endif
+
+#ifdef MAP_SECTIONS
+void
+map_sections (void)
+{
+  int arr[10];
+  int *ptr;
+  int (&arrref)[10] = arr;
+  int *&ptrref = ptr;
+
+  ptr = new int[10];
+  memset (ptr, 0, sizeof (int) * 10);
+  memset (arr, 0, sizeof (int) * 10);
+
+  #pragma omp target map(arr[0:10])
+  {
+    arr[2]++;
+  }
+
+  #pragma omp target map(ptr[0:10])
+  {
+    ptr[2]++;
+  }
+
+  #pragma omp target map(arrref[0:10])
+  {
+    arrref[2]++;
+  }
+
+  #pragma omp target map(ptrref[0:10])
+  {
+    ptrref[2]++;
+  }
+
+  assert (arr[2] == 2);
+  assert (ptr[2] == 2);
+
+  delete ptr;
+}
+#endif
+
+struct T {
+  int a[10];
+  int (&b)[10];
+  int *c;
+  int *&d;
+
+  T(int (&b1)[10], int *c1, int *&d1) : b(b1), c(c1), d(d1)
+  {
+    memset (a, 0, sizeof a);
+  }
+};
+
+#ifdef NONREF_DECL_MEMBER_SLICE
+void
+nonref_decl_member_slice (void)
+{
+  int c[10];
+  int *d = &c[0];
+  T myt(c, &c[0], d);
+
+  memset (c, 0, sizeof c);
+
+  #pragma omp target map(myt.a[0:10])
+  {
+    myt.a[2]++;
+  }
+
+  #pragma omp target map(myt.b[0:10])
+  {
+    myt.b[2]++;
+  }
+
+  #pragma omp target enter data map(to: myt.c)
+
+  #pragma omp target map(myt.c[0:10])
+  {
+    myt.c[2]++;
+  }
+
+  #pragma omp target exit data map(release: myt.c)
+
+  #pragma omp target enter data map(to: myt.d)
+
+  #pragma omp target map(myt.d[0:10])
+  {
+    myt.d[2]++;
+  }
+
+  #pragma omp target exit data map(from: myt.d)
+
+  assert (myt.a[2] == 1);
+  assert (myt.b[2] == 3);
+  assert (myt.c[2] == 3);
+  assert (myt.d[2] == 3);
+}
+#endif
+
+#ifdef NONREF_DECL_MEMBER_SLICE_BASEPTR
+void
+nonref_decl_member_slice_baseptr (void)
+{
+  int c[10];
+  int *d = &c[0];
+  T myt(c, &c[0], d);
+
+  memset (c, 0, sizeof c);
+
+  #pragma omp target map(to:myt.c) map(myt.c[0:10])
+  {
+    myt.c[2]++;
+  }
+
+  #pragma omp target map(to:myt.d) map(myt.d[0:10])
+  {
+    myt.d[2]++;
+  }
+
+  assert (myt.c[2] == 2);
+  assert (myt.d[2] == 2);
+}
+#endif
+
+#ifdef REF_DECL_MEMBER_SLICE
+void
+ref_decl_member_slice (void)
+{
+  int c[10];
+  int *d = &c[0];
+  T myt_real(c, &c[0], d);
+  T &myt = myt_real;
+
+  memset (c, 0, sizeof c);
+
+  #pragma omp target map(myt.a[0:10])
+  {
+    myt.a[2]++;
+  }
+
+  #pragma omp target map(myt.b[0:10])
+  {
+    myt.b[2]++;
+  }
+
+  #pragma omp target enter data map(to: myt.c)
+
+  #pragma omp target map(myt.c[0:10])
+  {
+    myt.c[2]++;
+  }
+
+  #pragma omp target exit data map(release: myt.c)
+
+  #pragma omp target enter data map(to: myt.d)
+
+  #pragma omp target map(myt.d[0:10])
+  {
+    myt.d[2]++;
+  }
+
+  #pragma omp target exit data map(release: myt.d)
+
+  assert (myt.a[2] == 1);
+  assert (myt.b[2] == 3);
+  assert (myt.c[2] == 3);
+  assert (myt.d[2] == 3);
+}
+#endif
+
+#ifdef REF_DECL_MEMBER_SLICE_BASEPTR
+void
+ref_decl_member_slice_baseptr (void)
+{
+  int c[10];
+  int *d = &c[0];
+  T myt_real(c, &c[0], d);
+  T &myt = myt_real;
+
+  memset (c, 0, sizeof c);
+
+  #pragma omp target map(to:myt.c) map(myt.c[0:10])
+  {
+    myt.c[2]++;
+  }
+
+  #pragma omp target map(to:myt.d) map(myt.d[0:10])
+  {
+    myt.d[2]++;
+  }
+
+  assert (myt.c[2] == 2);
+  assert (myt.d[2] == 2);
+}
+#endif
+
+#ifdef PTR_DECL_MEMBER_SLICE
+void
+ptr_decl_member_slice (void)
+{
+  int c[10];
+  int *d = &c[0];
+  T myt_real(c, &c[0], d);
+  T *myt = &myt_real;
+
+  memset (c, 0, sizeof c);
+
+  #pragma omp target enter data map(to: myt)
+
+  #pragma omp target map(myt->a[0:10])
+  {
+    myt->a[2]++;
+  }
+
+  #pragma omp target map(myt->b[0:10])
+  {
+    myt->b[2]++;
+  }
+
+  #pragma omp target enter data map(to: myt->c)
+
+  #pragma omp target map(myt->c[0:10])
+  {
+    myt->c[2]++;
+  }
+
+  #pragma omp target exit data map(release: myt->c)
+
+  #pragma omp target enter data map(to: myt->d)
+
+  #pragma omp target map(myt->d[0:10])
+  {
+    myt->d[2]++;
+  }
+
+  #pragma omp target exit data map(release: myt, myt->d)
+
+  assert (myt->a[2] == 1);
+  assert (myt->b[2] == 3);
+  assert (myt->c[2] == 3);
+  assert (myt->d[2] == 3);
+}
+#endif
+
+#ifdef PTR_DECL_MEMBER_SLICE_BASEPTR
+void
+ptr_decl_member_slice_baseptr (void)
+{
+  int c[10];
+  int *d = &c[0];
+  T myt_real(c, &c[0], d);
+  T *myt = &myt_real;
+
+  memset (c, 0, sizeof c);
+
+  // These ones have an implicit firstprivate for 'myt'.
+  #pragma omp target map(to:myt->c) map(myt->c[0:10])
+  {
+    myt->c[2]++;
+  }
+
+  #pragma omp target map(to:myt->d) map(myt->d[0:10])
+  {
+    myt->d[2]++;
+  }
+
+  // These ones have an explicit "TO" mapping for 'myt'.
+  #pragma omp target map(to:myt) map(to:myt->c) map(myt->c[0:10])
+  {
+    myt->c[2]++;
+  }
+
+  #pragma omp target map(to:myt) map(to:myt->d) map(myt->d[0:10])
+  {
+    myt->d[2]++;
+  }
+
+  assert (myt->c[2] == 4);
+  assert (myt->d[2] == 4);
+}
+#endif
+
+#ifdef REF2PTR_DECL_MEMBER_SLICE
+void
+ref2ptr_decl_member_slice (void)
+{
+  int c[10];
+  int *d = &c[0];
+  T myt_real(c, &c[0], d);
+  T *myt_ptr = &myt_real;
+  T *&myt = myt_ptr;
+
+  memset (c, 0, sizeof c);
+
+  #pragma omp target enter data map(to: myt)
+
+  #pragma omp target map(myt->a[0:10])
+  {
+    myt->a[2]++;
+  }
+
+  #pragma omp target map(myt->b[0:10])
+  {
+    myt->b[2]++;
+  }
+
+  #pragma omp target enter data map(to: myt->c)
+
+  #pragma omp target map(myt->c[0:10])
+  {
+    myt->c[2]++;
+  }
+
+  #pragma omp target exit data map(release: myt->c)
+
+  #pragma omp target enter data map(to: myt->d)
+
+  #pragma omp target map(myt->d[0:10])
+  {
+    myt->d[2]++;
+  }
+
+  #pragma omp target exit data map(from: myt, myt->d)
+
+  assert (myt->a[2] == 1);
+  assert (myt->b[2] == 3);
+  assert (myt->c[2] == 3);
+  assert (myt->d[2] == 3);
+}
+#endif
+
+#ifdef REF2PTR_DECL_MEMBER_SLICE_BASEPTR
+void
+ref2ptr_decl_member_slice_baseptr (void)
+{
+  int c[10];
+  int *d = &c[0];
+  T myt_real(c, &c[0], d);
+  T *myt_ptr = &myt_real;
+  T *&myt = myt_ptr;
+
+  memset (c, 0, sizeof c);
+
+  // These ones have an implicit firstprivate for 'myt'.
+  #pragma omp target map(to:myt->c) map(myt->c[0:10])
+  {
+    myt->c[2]++;
+  }
+
+  #pragma omp target map(to:myt->d) map(myt->d[0:10])
+  {
+    myt->d[2]++;
+  }
+
+  // These ones have an explicit "TO" mapping for 'myt'.
+  #pragma omp target map(to:myt) map(to:myt->c) map(myt->c[0:10])
+  {
+    myt->c[2]++;
+  }
+
+  #pragma omp target map(to:myt) map(to:myt->d) map(myt->d[0:10])
+  {
+    myt->d[2]++;
+  }
+
+  assert (myt->c[2] == 4);
+  assert (myt->d[2] == 4);
+}
+#endif
+
+#ifdef ARRAY_DECL_MEMBER_SLICE
+void
+array_decl_member_slice (void)
+{
+  int c[10];
+  int *d = &c[0];
+  T myt[4] =
+    {
+      T (c, &c[0], d),
+      T (c, &c[0], d),
+      T (c, &c[0], d),
+      T (c, &c[0], d)
+    };
+
+  memset (c, 0, sizeof c);
+
+  #pragma omp target map(myt[2].a[0:10])
+  {
+    myt[2].a[2]++;
+  }
+
+  #pragma omp target map(myt[2].b[0:10])
+  {
+    myt[2].b[2]++;
+  }
+
+  #pragma omp target enter data map(to: myt[2].c)
+
+  #pragma omp target map(myt[2].c[0:10])
+  {
+    myt[2].c[2]++;
+  }
+
+  #pragma omp target exit data map(release: myt[2].c)
+
+  #pragma omp target enter data map(to: myt[2].d)
+
+  #pragma omp target map(myt[2].d[0:10])
+  {
+    myt[2].d[2]++;
+  }
+
+  #pragma omp target exit data map(release: myt[2].d)
+
+  assert (myt[2].a[2] == 1);
+  assert (myt[2].b[2] == 3);
+  assert (myt[2].c[2] == 3);
+  assert (myt[2].d[2] == 3);
+}
+#endif
+
+#ifdef ARRAY_DECL_MEMBER_SLICE_BASEPTR
+void
+array_decl_member_slice_baseptr (void)
+{
+  int c[10];
+  int *d = &c[0];
+  T myt[4] =
+    {
+      T (c, &c[0], d),
+      T (c, &c[0], d),
+      T (c, &c[0], d),
+      T (c, &c[0], d)
+    };
+
+  memset (c, 0, sizeof c);
+
+  #pragma omp target map(to:myt[2].c) map(myt[2].c[0:10])
+  {
+    myt[2].c[2]++;
+  }
+
+  #pragma omp target map(to:myt[2].d) map(myt[2].d[0:10])
+  {
+    myt[2].d[2]++;
+  }
+
+  assert (myt[2].c[2] == 2);
+  assert (myt[2].d[2] == 2);
+}
+#endif
+
+#ifdef REF2ARRAY_DECL_MEMBER_SLICE
+void
+ref2array_decl_member_slice (void)
+{
+  int c[10];
+  int *d = &c[0];
+  T myt_real[4] =
+    {
+      T (c, &c[0], d),
+      T (c, &c[0], d),
+      T (c, &c[0], d),
+      T (c, &c[0], d)
+    };
+  T (&myt)[4] = myt_real;
+
+  memset (c, 0, sizeof c);
+
+  #pragma omp target map(myt[2].a[0:10])
+  {
+    myt[2].a[2]++;
+  }
+
+  #pragma omp target map(myt[2].b[0:10])
+  {
+    myt[2].b[2]++;
+  }
+
+  #pragma omp target enter data map(to: myt[2].c)
+
+  #pragma omp target map(myt[2].c[0:10])
+  {
+    myt[2].c[2]++;
+  }
+
+  #pragma omp target exit data map(release: myt[2].c)
+
+  #pragma omp target enter data map(to: myt[2].d)
+
+  #pragma omp target map(myt[2].d[0:10])
+  {
+    myt[2].d[2]++;
+  }
+
+  #pragma omp target exit data map(release: myt[2].d)
+
+  assert (myt[2].a[2] == 1);
+  assert (myt[2].b[2] == 3);
+  assert (myt[2].c[2] == 3);
+  assert (myt[2].d[2] == 3);
+}
+#endif
+
+#ifdef REF2ARRAY_DECL_MEMBER_SLICE_BASEPTR
+void
+ref2array_decl_member_slice_baseptr (void)
+{
+  int c[10];
+  int *d = &c[0];
+  T myt_real[4] =
+    {
+      T (c, &c[0], d),
+      T (c, &c[0], d),
+      T (c, &c[0], d),
+      T (c, &c[0], d)
+    };
+  T (&myt)[4] = myt_real;
+
+  memset (c, 0, sizeof c);
+
+  #pragma omp target map(to:myt[2].c) map(myt[2].c[0:10])
+  {
+    myt[2].c[2]++;
+  }
+
+  #pragma omp target map(to:myt[2].d) map(myt[2].d[0:10])
+  {
+    myt[2].d[2]++;
+  }
+
+  assert (myt[2].c[2] == 2);
+  assert (myt[2].d[2] == 2);
+}
+#endif
+
+#ifdef PTR_OFFSET_DECL_MEMBER_SLICE
+void
+ptr_offset_decl_member_slice (void)
+{
+  int c[10];
+  int *d = &c[0];
+  T myt_real[4] =
+    {
+      T (c, &c[0], d),
+      T (c, &c[0], d),
+      T (c, &c[0], d),
+      T (c, &c[0], d)
+    };
+  T *myt = &myt_real[0];
+
+  memset (c, 0, sizeof c);
+
+  #pragma omp target map(myt[2].a[0:10])
+  {
+    myt[2].a[2]++;
+  }
+
+  #pragma omp target map(myt[2].b[0:10])
+  {
+    myt[2].b[2]++;
+  }
+
+  #pragma omp target enter data map(to: myt[2].c)
+
+  #pragma omp target map(myt[2].c[0:10])
+  {
+    myt[2].c[2]++;
+  }
+
+  #pragma omp target exit data map(release: myt[2].c)
+
+  #pragma omp target enter data map(to: myt[2].d)
+
+  #pragma omp target map(myt[2].d[0:10])
+  {
+    myt[2].d[2]++;
+  }
+
+  #pragma omp target exit data map(release: myt[2].d)
+
+  assert (myt[2].a[2] == 1);
+  assert (myt[2].b[2] == 3);
+  assert (myt[2].c[2] == 3);
+  assert (myt[2].d[2] == 3);
+}
+#endif
+
+#ifdef PTR_OFFSET_DECL_MEMBER_SLICE_BASEPTR
+void
+ptr_offset_decl_member_slice_baseptr (void)
+{
+  int c[10];
+  int *d = &c[0];
+  T myt_real[4] =
+    {
+      T (c, &c[0], d),
+      T (c, &c[0], d),
+      T (c, &c[0], d),
+      T (c, &c[0], d)
+    };
+  T *myt = &myt_real[0];
+
+  memset (c, 0, sizeof c);
+
+  /* Implicit 'myt'.  */
+  #pragma omp target map(to:myt[2].c) map(myt[2].c[0:10])
+  {
+    myt[2].c[2]++;
+  }
+
+  #pragma omp target map(to:myt[2].d) map(myt[2].d[0:10])
+  {
+    myt[2].d[2]++;
+  }
+
+  /* Explicit 'to'-mapped 'myt'.  */
+  #pragma omp target map(to:myt) map(to:myt[2].c) map(myt[2].c[0:10])
+  {
+    myt[2].c[2]++;
+  }
+
+  #pragma omp target map(to:myt) map(to:myt[2].d) map(myt[2].d[0:10])
+  {
+    myt[2].d[2]++;
+  }
+
+  assert (myt[2].c[2] == 4);
+  assert (myt[2].d[2] == 4);
+}
+#endif
+
+#ifdef REF2PTR_OFFSET_DECL_MEMBER_SLICE
+void
+ref2ptr_offset_decl_member_slice (void)
+{
+  int c[10];
+  int *d = &c[0];
+  T myt_real[4] =
+    {
+      T (c, &c[0], d),
+      T (c, &c[0], d),
+      T (c, &c[0], d),
+      T (c, &c[0], d)
+    };
+  T *myt_ptr = &myt_real[0];
+  T *&myt = myt_ptr;
+
+  memset (c, 0, sizeof c);
+
+  #pragma omp target map(myt[2].a[0:10])
+  {
+    myt[2].a[2]++;
+  }
+
+  #pragma omp target map(myt[2].b[0:10])
+  {
+    myt[2].b[2]++;
+  }
+
+  #pragma omp target enter data map(to: myt[2].c)
+
+  #pragma omp target map(myt[2].c[0:10])
+  {
+    myt[2].c[2]++;
+  }
+
+  #pragma omp target exit data map(release: myt[2].c)
+
+  #pragma omp target enter data map(to: myt[2].d)
+
+  #pragma omp target map(myt[2].d[0:10])
+  {
+    myt[2].d[2]++;
+  }
+
+  #pragma omp target exit data map(release: myt[2].d)
+
+  assert (myt[2].a[2] == 1);
+  assert (myt[2].b[2] == 3);
+  assert (myt[2].c[2] == 3);
+  assert (myt[2].d[2] == 3);
+}
+#endif
+
+#ifdef REF2PTR_OFFSET_DECL_MEMBER_SLICE_BASEPTR
+void
+ref2ptr_offset_decl_member_slice_baseptr (void)
+{
+  int c[10];
+  int *d = &c[0];
+  T myt_real[4] =
+    {
+      T (c, &c[0], d),
+      T (c, &c[0], d),
+      T (c, &c[0], d),
+      T (c, &c[0], d)
+    };
+  T *myt_ptr = &myt_real[0];
+  T *&myt = myt_ptr;
+
+  memset (c, 0, sizeof c);
+
+  /* Implicit 'myt'.  */
+  #pragma omp target map(to:myt[2].c) map(myt[2].c[0:10])
+  {
+    myt[2].c[2]++;
+  }
+
+  #pragma omp target map(to:myt[2].d) map(myt[2].d[0:10])
+  {
+    myt[2].d[2]++;
+  }
+
+  /* Explicit 'to'-mapped 'myt'.  */
+  #pragma omp target map(to:myt) map(to:myt[2].c) map(myt[2].c[0:10])
+  {
+    myt[2].c[2]++;
+  }
+
+  #pragma omp target map(to:myt) map(to:myt[2].d) map(myt[2].d[0:10])
+  {
+    myt[2].d[2]++;
+  }
+
+  assert (myt[2].c[2] == 4);
+  assert (myt[2].d[2] == 4);
+}
+#endif
+
+#ifdef PTRARRAY_DECL_MEMBER_SLICE
+void
+ptrarray_decl_member_slice (void)
+{
+  int c[10];
+  int *d = &c[0];
+  T myt_real(c, &c[0], d);
+  T *myt[4] =
+    {
+      &myt_real,
+      &myt_real,
+      &myt_real,
+      &myt_real
+    };
+
+  memset (c, 0, sizeof c);
+
+  #pragma omp target enter data map(to: myt[2])
+
+  #pragma omp target map(myt[2]->a[0:10])
+  {
+    myt[2]->a[2]++;
+  }
+
+  #pragma omp target map(myt[2]->b[0:10])
+  {
+    myt[2]->b[2]++;
+  }
+
+  #pragma omp target enter data map(to: myt[2]->c)
+
+  #pragma omp target map(myt[2]->c[0:10])
+  {
+    myt[2]->c[2]++;
+  }
+
+  #pragma omp target exit data map(from: myt[2]->c)
+
+  #pragma omp target enter data map(to: myt[2]->d)
+
+  #pragma omp target map(myt[2]->d[0:10])
+  {
+    myt[2]->d[2]++;
+  }
+
+  #pragma omp target exit data map(from: myt[2]->d)
+
+  #pragma omp target exit data map(release: myt[2])
+
+  assert (myt[2]->a[2] == 1);
+  assert (myt[2]->b[2] == 3);
+  assert (myt[2]->c[2] == 3);
+  assert (myt[2]->d[2] == 3);
+}
+#endif
+
+#ifdef PTRARRAY_DECL_MEMBER_SLICE_BASEPTR
+void
+ptrarray_decl_member_slice_baseptr (void)
+{
+  int c[10];
+  int *d = &c[0];
+  T myt_real(c, &c[0], d);
+  T *myt[4] =
+    {
+      &myt_real,
+      &myt_real,
+      &myt_real,
+      &myt_real
+    };
+
+  memset (c, 0, sizeof c);
+
+  // Implicit 'myt'
+  #pragma omp target map(to: myt[2]->c) map(myt[2]->c[0:10])
+  {
+    myt[2]->c[2]++;
+  }
+
+  #pragma omp target map(to: myt[2]->d) map(myt[2]->d[0:10])
+  {
+    myt[2]->d[2]++;
+  }
+
+  // One element of 'myt'
+  #pragma omp target map(to:myt[2], myt[2]->c) map(myt[2]->c[0:10])
+  {
+    myt[2]->c[2]++;
+  }
+
+  #pragma omp target map(to:myt[2], myt[2]->d) map(myt[2]->d[0:10])
+  {
+    myt[2]->d[2]++;
+  }
+
+  // Explicit map of all of 'myt'
+  #pragma omp target map(to:myt, myt[2]->c) map(myt[2]->c[0:10])
+  {
+    myt[2]->c[2]++;
+  }
+
+  #pragma omp target map(to:myt, myt[2]->d) map(myt[2]->d[0:10])
+  {
+    myt[2]->d[2]++;
+  }
+
+  // Explicit map slice of 'myt'
+  #pragma omp target map(to:myt[1:3], myt[2]->c) map(myt[2]->c[0:10])
+  {
+    myt[2]->c[2]++;
+  }
+
+  #pragma omp target map(to:myt[1:3], myt[2]->d) map(myt[2]->d[0:10])
+  {
+    myt[2]->d[2]++;
+  }
+
+  assert (myt[2]->c[2] == 8);
+  assert (myt[2]->d[2] == 8);
+}
+#endif
+
+#ifdef REF2PTRARRAY_DECL_MEMBER_SLICE
+void
+ref2ptrarray_decl_member_slice (void)
+{
+  int c[10];
+  int *d = &c[0];
+  T myt_real(c, &c[0], d);
+  T *myt_ptrarr[4] =
+    {
+      &myt_real,
+      &myt_real,
+      &myt_real,
+      &myt_real
+    };
+  T *(&myt)[4] = myt_ptrarr;
+
+  memset (c, 0, sizeof c);
+
+  #pragma omp target enter data map(to: myt[2])
+
+  #pragma omp target map(myt[2]->a[0:10])
+  {
+    myt[2]->a[2]++;
+  }
+
+  #pragma omp target map(myt[2]->b[0:10])
+  {
+    myt[2]->b[2]++;
+  }
+
+  #pragma omp target enter data map(to: myt[2]->c)
+
+  #pragma omp target map(myt[2]->c[0:10])
+  {
+    myt[2]->c[2]++;
+  }
+
+  #pragma omp target exit data map(release: myt[2]->c)
+
+  #pragma omp target enter data map(to: myt[2]->d)
+
+  #pragma omp target map(myt[2]->d[0:10])
+  {
+    myt[2]->d[2]++;
+  }
+
+  #pragma omp target exit data map(release: myt[2]->d)
+
+  #pragma omp target exit data map(release: myt[2])
+
+  assert (myt[2]->a[2] == 1);
+  assert (myt[2]->b[2] == 3);
+  assert (myt[2]->c[2] == 3);
+  assert (myt[2]->d[2] == 3);
+}
+#endif
+
+#ifdef REF2PTRARRAY_DECL_MEMBER_SLICE_BASEPTR
+void
+ref2ptrarray_decl_member_slice_baseptr (void)
+{
+  int c[10];
+  int *d = &c[0];
+  T myt_real(c, &c[0], d);
+  T *myt_ptrarr[4] =
+    {
+      &myt_real,
+      &myt_real,
+      &myt_real,
+      &myt_real
+    };
+  T *(&myt)[4] = myt_ptrarr;
+
+  memset (c, 0, sizeof c);
+
+  #pragma omp target map(to:myt[2], myt[2]->c) map(myt[2]->c[0:10])
+  {
+    myt[2]->c[2]++;
+  }
+
+  #pragma omp target map(to:myt[2], myt[2]->d) map(myt[2]->d[0:10])
+  {
+    myt[2]->d[2]++;
+  }
+
+  #pragma omp target map(to:myt, myt[2]->c) map(myt[2]->c[0:10])
+  {
+    myt[2]->c[2]++;
+  }
+
+  #pragma omp target map(to:myt, myt[2]->d) map(myt[2]->d[0:10])
+  {
+    myt[2]->d[2]++;
+  }
+
+  assert (myt[2]->c[2] == 4);
+  assert (myt[2]->d[2] == 4);
+}
+#endif
+
+#ifdef PTRPTR_OFFSET_DECL_MEMBER_SLICE
+void
+ptrptr_offset_decl_member_slice (void)
+{
+  int c[10];
+  int *d = &c[0];
+  T myt_real(c, &c[0], d);
+  T *myt_ptrarr[4] =
+    {
+      &myt_real,
+      &myt_real,
+      &myt_real,
+      &myt_real
+    };
+  T **myt = &myt_ptrarr[0];
+
+  memset (c, 0, sizeof c);
+
+  #pragma omp target enter data map(to: myt[0:3])
+
+  /* NOTE: For the implicit firstprivate 'myt' to work, the zeroth element of
+     myt[] must be mapped above -- otherwise the zero-length array section
+     lookup fails.  */
+  #pragma omp target map(myt[2]->a[0:10])
+  {
+    myt[2]->a[2]++;
+  }
+
+  #pragma omp target map(myt[2]->b[0:10])
+  {
+    myt[2]->b[2]++;
+  }
+
+  #pragma omp target enter data map(to: myt[2]->c)
+
+  #pragma omp target map(myt[2]->c[0:10])
+  {
+    myt[2]->c[2]++;
+  }
+
+  #pragma omp target exit data map(from: myt[2]->c)
+
+  #pragma omp target enter data map(to: myt[2]->d)
+
+  #pragma omp target map(myt[2]->d[0:10])
+  {
+    myt[2]->d[2]++;
+  }
+
+  #pragma omp target exit data map(from: myt[0:3], myt[2]->d)
+
+  assert (myt[2]->a[2] == 1);
+  assert (myt[2]->b[2] == 3);
+  assert (myt[2]->c[2] == 3);
+  assert (myt[2]->d[2] == 3);
+}
+#endif
+
+#ifdef PTRPTR_OFFSET_DECL_MEMBER_SLICE_BASEPTR
+void
+ptrptr_offset_decl_member_slice_baseptr (void)
+{
+  int c[10];
+  int *d = &c[0];
+  T myt_real(c, &c[0], d);
+  T *myt_ptrarr[4] =
+    {
+      0,
+      0,
+      0,
+      &myt_real
+    };
+  T **myt = &myt_ptrarr[0];
+
+  memset (c, 0, sizeof c);
+
+  #pragma omp target map(to:myt[3], myt[3]->c) map(myt[3]->c[0:10])
+  {
+    myt[3]->c[2]++;
+  }
+
+  #pragma omp target map(to:myt[3], myt[3]->d) map(myt[3]->d[0:10])
+  {
+    myt[3]->d[2]++;
+  }
+
+  #pragma omp target map(to:myt, myt[3], myt[3]->c) map(myt[3]->c[0:10])
+  {
+    myt[3]->c[2]++;
+  }
+
+  #pragma omp target map(to:myt, myt[3], myt[3]->d) map(myt[3]->d[0:10])
+  {
+    myt[3]->d[2]++;
+  }
+
+  assert (myt[3]->c[2] == 4);
+  assert (myt[3]->d[2] == 4);
+}
+#endif
+
+#ifdef REF2PTRPTR_OFFSET_DECL_MEMBER_SLICE
+void
+ref2ptrptr_offset_decl_member_slice (void)
+{
+  int c[10];
+  int *d = &c[0];
+  T myt_real(c, &c[0], d);
+  T *myt_ptrarr[4] =
+    {
+      0,
+      0,
+      &myt_real,
+      0
+    };
+  T **myt_ptrptr = &myt_ptrarr[0];
+  T **&myt = myt_ptrptr;
+
+  memset (c, 0, sizeof c);
+
+  #pragma omp target enter data map(to: myt[0:3])
+
+  #pragma omp target map(myt[2]->a[0:10])
+  {
+    myt[2]->a[2]++;
+  }
+
+  #pragma omp target map(myt[2]->b[0:10])
+  {
+    myt[2]->b[2]++;
+  }
+
+  #pragma omp target enter data map(to:myt[2]->c)
+
+  #pragma omp target map(myt[2]->c[0:10])
+  {
+    myt[2]->c[2]++;
+  }
+
+  #pragma omp target exit data map(release:myt[2]->c)
+
+  #pragma omp target enter data map(to:myt[2]->d)
+
+  #pragma omp target map(myt[2]->d[0:10])
+  {
+    myt[2]->d[2]++;
+  }
+
+  #pragma omp target exit data map(release: myt[0:3], myt[2]->d)
+
+  assert (myt[2]->a[2] == 1);
+  assert (myt[2]->b[2] == 3);
+  assert (myt[2]->c[2] == 3);
+  assert (myt[2]->d[2] == 3);
+}
+#endif
+
+#ifdef REF2PTRPTR_OFFSET_DECL_MEMBER_SLICE_BASEPTR
+void
+ref2ptrptr_offset_decl_member_slice_baseptr (void)
+{
+  int c[10];
+  int *d = &c[0];
+  T myt_real(c, &c[0], d);
+  T *myt_ptrarr[4] =
+    {
+      0,
+      0,
+      &myt_real,
+      0
+    };
+  T **myt_ptrptr = &myt_ptrarr[0];
+  T **&myt = myt_ptrptr;
+
+  memset (c, 0, sizeof c);
+
+  #pragma omp target map(to:myt[2], myt[2]->c) map(myt[2]->c[0:10])
+  {
+    myt[2]->c[2]++;
+  }
+
+  #pragma omp target map(to:myt[2], myt[2]->d) map(myt[2]->d[0:10])
+  {
+    myt[2]->d[2]++;
+  }
+
+  #pragma omp target map(to:myt, myt[2], myt[2]->c) map(myt[2]->c[0:10])
+  {
+    myt[2]->c[2]++;
+  }
+
+  #pragma omp target map(to:myt, myt[2], myt[2]->d) map(myt[2]->d[0:10])
+  {
+    myt[2]->d[2]++;
+  }
+
+  assert (myt[2]->c[2] == 4);
+  assert (myt[2]->d[2] == 4);
+}
+#endif
+
+struct U
+{
+  S s1;
+  T t1;
+  S &s2;
+  T &t2;
+  S *s3;
+  T *t3;
+  S *&s4;
+  T *&t4;
+
+  U(S &sptr1, T &tptr1, S &sptr2, T &tptr2, S *sptr3, T *tptr3,
+    S *&sptr4, T *&tptr4)
+    : s1(sptr1), t1(tptr1), s2(sptr2), t2(tptr2), s3(sptr3), t3(tptr3),
+      s4(sptr4), t4(tptr4)
+  {
+  }
+};
+
+#define INIT_S(N)				\
+  int a##N = 0, b##N = 0, c##N = 0, d##N = 0;	\
+  int *d##N##ptr = &d##N;			\
+  S s##N(a##N, b##N, &c##N, d##N##ptr)
+
+#define INIT_T(N)				\
+  int arr##N[10];				\
+  int *ptr##N = &arr##N[0];			\
+  T t##N(arr##N, &arr##N[0], ptr##N);		\
+  memset (arr##N, 0, sizeof arr##N)
+
+#define INIT_ST				\
+  INIT_S(1);				\
+  INIT_T(1);				\
+  INIT_S(2);				\
+  INIT_T(2);				\
+  INIT_S(3);				\
+  INIT_T(3);				\
+  int a4 = 0, b4 = 0, c4 = 0, d4 = 0;	\
+  int *d4ptr = &d4;			\
+  S *s4 = new S(a4, b4, &c4, d4ptr);	\
+  int arr4[10];				\
+  int *ptr4 = &arr4[0];			\
+  T *t4 = new T(arr4, &arr4[0], ptr4);	\
+  memset (arr4, 0, sizeof arr4)
+
+#ifdef NONREF_COMPONENT_BASE
+void
+nonref_component_base (void)
+{
+  INIT_ST;
+  U myu(s1, t1, s2, t2, &s3, &t3, s4, t4);
+
+  #pragma omp target map(myu.s1.a, myu.s1.b, myu.s1.c, myu.s1.d)
+  {
+    myu.s1.a++;
+    myu.s1.b++;
+    myu.s1.c++;
+    myu.s1.d++;
+  }
+
+  assert (myu.s1.a == 1);
+  assert (myu.s1.b == 1);
+  assert (myu.s1.c == &c1 + 1);
+  assert (myu.s1.d == &d1 + 1);
+
+  #pragma omp target map(myu.s2.a, myu.s2.b, myu.s2.c, myu.s2.d)
+  {
+    myu.s2.a++;
+    myu.s2.b++;
+    myu.s2.c++;
+    myu.s2.d++;
+  }
+
+  assert (myu.s2.a == 1);
+  assert (myu.s2.b == 1);
+  assert (myu.s2.c == &c2 + 1);
+  assert (myu.s2.d == &d2 + 1);
+
+  #pragma omp target map(to:myu.s3) \
+		     map(myu.s3->a, myu.s3->b, myu.s3->c, myu.s3->d)
+  {
+    myu.s3->a++;
+    myu.s3->b++;
+    myu.s3->c++;
+    myu.s3->d++;
+  }
+
+  assert (myu.s3->a == 1);
+  assert (myu.s3->b == 1);
+  assert (myu.s3->c == &c3 + 1);
+  assert (myu.s3->d == &d3 + 1);
+
+  #pragma omp target map(to:myu.s4) \
+		     map(myu.s4->a, myu.s4->b, myu.s4->c, myu.s4->d)
+  {
+    myu.s4->a++;
+    myu.s4->b++;
+    myu.s4->c++;
+    myu.s4->d++;
+  }
+
+  assert (myu.s4->a == 1);
+  assert (myu.s4->b == 1);
+  assert (myu.s4->c == &c4 + 1);
+  assert (myu.s4->d == &d4 + 1);
+
+  delete s4;
+  delete t4;
+}
+#endif
+
+#ifdef NONREF_COMPONENT_MEMBER_SLICE
+void
+nonref_component_member_slice (void)
+{
+  INIT_ST;
+  U myu(s1, t1, s2, t2, &s3, &t3, s4, t4);
+
+  #pragma omp target map(myu.t1.a[2:5])
+  {
+    myu.t1.a[2]++;
+  }
+
+  #pragma omp target map(myu.t1.b[2:5])
+  {
+    myu.t1.b[2]++;
+  }
+
+  #pragma omp target enter data map(to: myu.t1.c)
+
+  #pragma omp target map(myu.t1.c[2:5])
+  {
+    myu.t1.c[2]++;
+  }
+
+  #pragma omp target exit data map(release: myu.t1.c)
+
+  #pragma omp target enter data map(to: myu.t1.d)
+
+  #pragma omp target map(myu.t1.d[2:5])
+  {
+    myu.t1.d[2]++;
+  }
+
+  #pragma omp target exit data map(from: myu.t1.d)
+
+  assert (myu.t1.a[2] == 1);
+  assert (myu.t1.b[2] == 3);
+  assert (myu.t1.c[2] == 3);
+  assert (myu.t1.d[2] == 3);
+
+  #pragma omp target map(myu.t2.a[2:5])
+  {
+    myu.t2.a[2]++;
+  }
+
+  #pragma omp target map(myu.t2.b[2:5])
+  {
+    myu.t2.b[2]++;
+  }
+
+  #pragma omp target enter data map(to: myu.t2.c)
+
+  #pragma omp target map(myu.t2.c[2:5])
+  {
+    myu.t2.c[2]++;
+  }
+
+  #pragma omp target exit data map(release: myu.t2.c)
+
+  #pragma omp target enter data map(to: myu.t2.d)
+
+  #pragma omp target map(myu.t2.d[2:5])
+  {
+    myu.t2.d[2]++;
+  }
+
+  #pragma omp target exit data map(release: myu.t2.d)
+
+  assert (myu.t2.a[2] == 1);
+  assert (myu.t2.b[2] == 3);
+  assert (myu.t2.c[2] == 3);
+  assert (myu.t2.d[2] == 3);
+
+  #pragma omp target enter data map(to: myu.t3)
+
+  #pragma omp target map(myu.t3->a[2:5])
+  {
+    myu.t3->a[2]++;
+  }
+
+  #pragma omp target map(myu.t3->b[2:5])
+  {
+    myu.t3->b[2]++;
+  }
+
+  #pragma omp target enter data map(to: myu.t3->c)
+
+  #pragma omp target map(myu.t3->c[2:5])
+  {
+    myu.t3->c[2]++;
+  }
+
+  #pragma omp target exit data map(release: myu.t3->c)
+
+  #pragma omp target enter data map(to: myu.t3->d)
+
+  #pragma omp target map(myu.t3->d[2:5])
+  {
+    myu.t3->d[2]++;
+  }
+
+  #pragma omp target exit data map(release: myu.t3, myu.t3->d)
+
+  assert (myu.t3->a[2] == 1);
+  assert (myu.t3->b[2] == 3);
+  assert (myu.t3->c[2] == 3);
+  assert (myu.t3->d[2] == 3);
+
+  #pragma omp target enter data map(to: myu.t4)
+
+  #pragma omp target map(myu.t4->a[2:5])
+  {
+    myu.t4->a[2]++;
+  }
+
+  #pragma omp target map(myu.t4->b[2:5])
+  {
+    myu.t4->b[2]++;
+  }
+
+  #pragma omp target enter data map(to: myu.t4->c)
+
+  #pragma omp target map(myu.t4->c[2:5])
+  {
+    myu.t4->c[2]++;
+  }
+
+  #pragma omp target exit data map(release: myu.t4->c)
+
+  #pragma omp target enter data map(to: myu.t4->d)
+
+  #pragma omp target map(myu.t4->d[2:5])
+  {
+    myu.t4->d[2]++;
+  }
+
+  #pragma omp target exit data map(release: myu.t4, myu.t4->d)
+
+  assert (myu.t4->a[2] == 1);
+  assert (myu.t4->b[2] == 3);
+  assert (myu.t4->c[2] == 3);
+  assert (myu.t4->d[2] == 3);
+
+  delete s4;
+  delete t4;
+}
+#endif
+
+#ifdef NONREF_COMPONENT_MEMBER_SLICE_BASEPTR
+void
+nonref_component_member_slice_baseptr (void)
+{
+  INIT_ST;
+  U myu(s1, t1, s2, t2, &s3, &t3, s4, t4);
+
+  #pragma omp target map(to: myu.t1.c) map(myu.t1.c[2:5])
+  {
+    myu.t1.c[2]++;
+  }
+
+  #pragma omp target map(to: myu.t1.d) map(myu.t1.d[2:5])
+  {
+    myu.t1.d[2]++;
+  }
+
+  assert (myu.t1.c[2] == 2);
+  assert (myu.t1.d[2] == 2);
+
+  #pragma omp target map(to: myu.t2.c) map(myu.t2.c[2:5])
+  {
+    myu.t2.c[2]++;
+  }
+
+  #pragma omp target map(to: myu.t2.d) map(myu.t2.d[2:5])
+  {
+    myu.t2.d[2]++;
+  }
+
+  assert (myu.t2.c[2] == 2);
+  assert (myu.t2.d[2] == 2);
+
+  #pragma omp target map(to: myu.t3, myu.t3->c) map(myu.t3->c[2:5])
+  {
+    myu.t3->c[2]++;
+  }
+
+  #pragma omp target map(to: myu.t3, myu.t3->d) map(myu.t3->d[2:5])
+  {
+    myu.t3->d[2]++;
+  }
+
+  assert (myu.t3->c[2] == 2);
+  assert (myu.t3->d[2] == 2);
+
+  #pragma omp target map(to: myu.t4, myu.t4->c) map(myu.t4->c[2:5])
+  {
+    myu.t4->c[2]++;
+  }
+
+  #pragma omp target map(to: myu.t4, myu.t4->d) map(myu.t4->d[2:5])
+  {
+    myu.t4->d[2]++;
+  }
+
+  assert (myu.t4->c[2] == 2);
+  assert (myu.t4->d[2] == 2);
+
+  delete s4;
+  delete t4;
+}
+#endif
+
+#ifdef REF_COMPONENT_BASE
+void
+ref_component_base (void)
+{
+  INIT_ST;
+  U myu_real(s1, t1, s2, t2, &s3, &t3, s4, t4);
+  U &myu = myu_real;
+
+  #pragma omp target map(myu.s1.a, myu.s1.b, myu.s1.c, myu.s1.d)
+  {
+    myu.s1.a++;
+    myu.s1.b++;
+    myu.s1.c++;
+    myu.s1.d++;
+  }
+
+  assert (myu.s1.a == 1);
+  assert (myu.s1.b == 1);
+  assert (myu.s1.c == &c1 + 1);
+  assert (myu.s1.d == &d1 + 1);
+
+  #pragma omp target map(myu.s2.a, myu.s2.b, myu.s2.c, myu.s2.d)
+  {
+    myu.s2.a++;
+    myu.s2.b++;
+    myu.s2.c++;
+    myu.s2.d++;
+  }
+
+  assert (myu.s2.a == 1);
+  assert (myu.s2.b == 1);
+  assert (myu.s2.c == &c2 + 1);
+  assert (myu.s2.d == &d2 + 1);
+
+  #pragma omp target map(to:myu.s3) \
+		     map(myu.s3->a, myu.s3->b, myu.s3->c, myu.s3->d)
+  {
+    myu.s3->a++;
+    myu.s3->b++;
+    myu.s3->c++;
+    myu.s3->d++;
+  }
+
+  assert (myu.s3->a == 1);
+  assert (myu.s3->b == 1);
+  assert (myu.s3->c == &c3 + 1);
+  assert (myu.s3->d == &d3 + 1);
+
+  #pragma omp target map(to:myu.s4) \
+		     map(myu.s4->a, myu.s4->b, myu.s4->c, myu.s4->d)
+  {
+    myu.s4->a++;
+    myu.s4->b++;
+    myu.s4->c++;
+    myu.s4->d++;
+  }
+
+  assert (myu.s4->a == 1);
+  assert (myu.s4->b == 1);
+  assert (myu.s4->c == &c4 + 1);
+  assert (myu.s4->d == &d4 + 1);
+
+  delete s4;
+  delete t4;
+}
+#endif
+
+#ifdef REF_COMPONENT_MEMBER_SLICE
+void
+ref_component_member_slice (void)
+{
+  INIT_ST;
+  U myu_real(s1, t1, s2, t2, &s3, &t3, s4, t4);
+  U &myu = myu_real;
+
+  #pragma omp target map(myu.t1.a[2:5])
+  {
+    myu.t1.a[2]++;
+  }
+
+  #pragma omp target map(myu.t1.b[2:5])
+  {
+    myu.t1.b[2]++;
+  }
+
+  #pragma omp target enter data map(to: myu.t1.c)
+
+  #pragma omp target map(myu.t1.c[2:5])
+  {
+    myu.t1.c[2]++;
+  }
+
+  #pragma omp target exit data map(release: myu.t1.c)
+
+  #pragma omp target enter data map(to: myu.t1.d)
+
+  #pragma omp target map(myu.t1.d[2:5])
+  {
+    myu.t1.d[2]++;
+  }
+
+  #pragma omp target exit data map(release: myu.t1.d)
+
+  assert (myu.t1.a[2] == 1);
+  assert (myu.t1.b[2] == 3);
+  assert (myu.t1.c[2] == 3);
+  assert (myu.t1.d[2] == 3);
+
+  #pragma omp target map(myu.t2.a[2:5])
+  {
+    myu.t2.a[2]++;
+  }
+
+  #pragma omp target map(myu.t2.b[2:5])
+  {
+    myu.t2.b[2]++;
+  }
+
+  #pragma omp target enter data map(to: myu.t2.c)
+
+  #pragma omp target map(myu.t2.c[2:5])
+  {
+    myu.t2.c[2]++;
+  }
+
+  #pragma omp target exit data map(release: myu.t2.c)
+
+  #pragma omp target enter data map(to: myu.t2.d)
+
+  #pragma omp target map(myu.t2.d[2:5])
+  {
+    myu.t2.d[2]++;
+  }
+
+  #pragma omp target exit data map(release: myu.t2.d)
+
+  assert (myu.t2.a[2] == 1);
+  assert (myu.t2.b[2] == 3);
+  assert (myu.t2.c[2] == 3);
+  assert (myu.t2.d[2] == 3);
+
+  #pragma omp target enter data map(to: myu.t3)
+
+  #pragma omp target map(myu.t3->a[2:5])
+  {
+    myu.t3->a[2]++;
+  }
+
+  #pragma omp target map(myu.t3->b[2:5])
+  {
+    myu.t3->b[2]++;
+  }
+
+  #pragma omp target enter data map(to: myu.t3->c)
+
+  #pragma omp target map(myu.t3->c[2:5])
+  {
+    myu.t3->c[2]++;
+  }
+
+  #pragma omp target exit data map(release: myu.t3->c)
+
+  #pragma omp target enter data map(to: myu.t3->d)
+
+  #pragma omp target map(myu.t3->d[2:5])
+  {
+    myu.t3->d[2]++;
+  }
+
+  #pragma omp target exit data map(release: myu.t3, myu.t3->d)
+
+  assert (myu.t3->a[2] == 1);
+  assert (myu.t3->b[2] == 3);
+  assert (myu.t3->c[2] == 3);
+  assert (myu.t3->d[2] == 3);
+
+  #pragma omp target enter data map(to: myu.t4)
+
+  #pragma omp target map(myu.t4->a[2:5])
+  {
+    myu.t4->a[2]++;
+  }
+
+  #pragma omp target map(myu.t4->b[2:5])
+  {
+    myu.t4->b[2]++;
+  }
+
+  #pragma omp target enter data map(to: myu.t4->c)
+
+  #pragma omp target map(myu.t4->c[2:5])
+  {
+    myu.t4->c[2]++;
+  }
+
+  #pragma omp target exit data map(release: myu.t4->c)
+
+  #pragma omp target enter data map(to: myu.t4->d)
+
+  #pragma omp target map(myu.t4->d[2:5])
+  {
+    myu.t4->d[2]++;
+  }
+
+  #pragma omp target exit data map(release: myu.t4, myu.t4->d)
+
+  assert (myu.t4->a[2] == 1);
+  assert (myu.t4->b[2] == 3);
+  assert (myu.t4->c[2] == 3);
+  assert (myu.t4->d[2] == 3);
+
+  delete s4;
+  delete t4;
+}
+#endif
+
+#ifdef REF_COMPONENT_MEMBER_SLICE_BASEPTR
+void
+ref_component_member_slice_baseptr (void)
+{
+  INIT_ST;
+  U myu_real(s1, t1, s2, t2, &s3, &t3, s4, t4);
+  U &myu = myu_real;
+
+  #pragma omp target map(to: myu.t1.c) map(myu.t1.c[2:5])
+  {
+    myu.t1.c[2]++;
+  }
+
+  #pragma omp target map(to: myu.t1.d) map(myu.t1.d[2:5])
+  {
+    myu.t1.d[2]++;
+  }
+
+  assert (myu.t1.c[2] == 2);
+  assert (myu.t1.d[2] == 2);
+
+  #pragma omp target map(to: myu.t2.c) map(myu.t2.c[2:5])
+  {
+    myu.t2.c[2]++;
+  }
+
+  #pragma omp target map(to: myu.t2.d) map(myu.t2.d[2:5])
+  {
+    myu.t2.d[2]++;
+  }
+
+  assert (myu.t2.c[2] == 2);
+  assert (myu.t2.d[2] == 2);
+
+  #pragma omp target map(to: myu.t3, myu.t3->c) map(myu.t3->c[2:5])
+  {
+    myu.t3->c[2]++;
+  }
+
+  #pragma omp target map(to: myu.t3, myu.t3->d) map(myu.t3->d[2:5])
+  {
+    myu.t3->d[2]++;
+  }
+
+  assert (myu.t3->c[2] == 2);
+  assert (myu.t3->d[2] == 2);
+
+  #pragma omp target map(to: myu.t4, myu.t4->c) map(myu.t4->c[2:5])
+  {
+    myu.t4->c[2]++;
+  }
+
+  #pragma omp target map(to: myu.t4, myu.t4->d) map(myu.t4->d[2:5])
+  {
+    myu.t4->d[2]++;
+  }
+
+  assert (myu.t4->c[2] == 2);
+  assert (myu.t4->d[2] == 2);
+
+  delete s4;
+  delete t4;
+}
+#endif
+
+#ifdef PTR_COMPONENT_BASE
+void
+ptr_component_base (void)
+{
+  INIT_ST;
+  U *myu = new U(s1, t1, s2, t2, &s3, &t3, s4, t4);
+
+  #pragma omp target map(myu->s1.a, myu->s1.b, myu->s1.c, myu->s1.d)
+  {
+    myu->s1.a++;
+    myu->s1.b++;
+    myu->s1.c++;
+    myu->s1.d++;
+  }
+
+  assert (myu->s1.a == 1);
+  assert (myu->s1.b == 1);
+  assert (myu->s1.c == &c1 + 1);
+  assert (myu->s1.d == &d1 + 1);
+
+  #pragma omp target map(myu->s2.a, myu->s2.b, myu->s2.c, myu->s2.d)
+  {
+    myu->s2.a++;
+    myu->s2.b++;
+    myu->s2.c++;
+    myu->s2.d++;
+  }
+
+  assert (myu->s2.a == 1);
+  assert (myu->s2.b == 1);
+  assert (myu->s2.c == &c2 + 1);
+  assert (myu->s2.d == &d2 + 1);
+
+  #pragma omp target map(to:myu->s3) \
+		     map(myu->s3->a, myu->s3->b, myu->s3->c, myu->s3->d)
+  {
+    myu->s3->a++;
+    myu->s3->b++;
+    myu->s3->c++;
+    myu->s3->d++;
+  }
+
+  assert (myu->s3->a == 1);
+  assert (myu->s3->b == 1);
+  assert (myu->s3->c == &c3 + 1);
+  assert (myu->s3->d == &d3 + 1);
+
+  #pragma omp target map(to:myu->s4) \
+		     map(myu->s4->a, myu->s4->b, myu->s4->c, myu->s4->d)
+  {
+    myu->s4->a++;
+    myu->s4->b++;
+    myu->s4->c++;
+    myu->s4->d++;
+  }
+
+  assert (myu->s4->a == 1);
+  assert (myu->s4->b == 1);
+  assert (myu->s4->c == &c4 + 1);
+  assert (myu->s4->d == &d4 + 1);
+
+  delete s4;
+  delete t4;
+  delete myu;
+}
+#endif
+
+#ifdef PTR_COMPONENT_MEMBER_SLICE
+void
+ptr_component_member_slice (void)
+{
+  INIT_ST;
+  U *myu = new U(s1, t1, s2, t2, &s3, &t3, s4, t4);
+
+  #pragma omp target map(myu->t1.a[2:5])
+  {
+    myu->t1.a[2]++;
+  }
+
+  #pragma omp target map(myu->t1.b[2:5])
+  {
+    myu->t1.b[2]++;
+  }
+
+  #pragma omp target enter data map(to: myu->t1.c)
+
+  #pragma omp target map(myu->t1.c[2:5])
+  {
+    myu->t1.c[2]++;
+  }
+
+  #pragma omp target exit data map(release: myu->t1.c)
+
+  #pragma omp target enter data map(to: myu->t1.d)
+
+  #pragma omp target map(myu->t1.d[2:5])
+  {
+    myu->t1.d[2]++;
+  }
+
+  #pragma omp target exit data map(release: myu->t1.d)
+
+  assert (myu->t1.a[2] == 1);
+  assert (myu->t1.b[2] == 3);
+  assert (myu->t1.c[2] == 3);
+  assert (myu->t1.d[2] == 3);
+
+  #pragma omp target map(myu->t2.a[2:5])
+  {
+    myu->t2.a[2]++;
+  }
+
+  #pragma omp target map(myu->t2.b[2:5])
+  {
+    myu->t2.b[2]++;
+  }
+
+  #pragma omp target enter data map(to: myu->t2.c)
+
+  #pragma omp target map(myu->t2.c[2:5])
+  {
+    myu->t2.c[2]++;
+  }
+
+  #pragma omp target exit data map(release: myu->t2.c)
+
+  #pragma omp target enter data map(to: myu->t2.d)
+
+  #pragma omp target map(myu->t2.d[2:5])
+  {
+    myu->t2.d[2]++;
+  }
+
+  #pragma omp target exit data map(release: myu->t2.d)
+
+  assert (myu->t2.a[2] == 1);
+  assert (myu->t2.b[2] == 3);
+  assert (myu->t2.c[2] == 3);
+  assert (myu->t2.d[2] == 3);
+
+  #pragma omp target enter data map(to: myu->t3)
+
+  #pragma omp target map(myu->t3->a[2:5])
+  {
+    myu->t3->a[2]++;
+  }
+
+  #pragma omp target map(myu->t3->b[2:5])
+  {
+    myu->t3->b[2]++;
+  }
+
+  #pragma omp target enter data map(to: myu->t3->c)
+
+  #pragma omp target map(myu->t3->c[2:5])
+  {
+    myu->t3->c[2]++;
+  }
+
+  #pragma omp target exit data map(release: myu->t3->c)
+
+  #pragma omp target enter data map(to: myu->t3->d)
+
+  #pragma omp target map(myu->t3->d[2:5])
+  {
+    myu->t3->d[2]++;
+  }
+
+  #pragma omp target exit data map(release: myu->t3, myu->t3->d)
+
+  assert (myu->t3->a[2] == 1);
+  assert (myu->t3->b[2] == 3);
+  assert (myu->t3->c[2] == 3);
+  assert (myu->t3->d[2] == 3);
+
+  #pragma omp target enter data map(to: myu->t4)
+
+  #pragma omp target map(myu->t4->a[2:5])
+  {
+    myu->t4->a[2]++;
+  }
+
+  #pragma omp target map(myu->t4->b[2:5])
+  {
+    myu->t4->b[2]++;
+  }
+
+  #pragma omp target enter data map(to: myu->t4->c)
+
+  #pragma omp target map(myu->t4->c[2:5])
+  {
+    myu->t4->c[2]++;
+  }
+
+  #pragma omp target exit data map(release: myu->t4->c)
+
+  #pragma omp target enter data map(to: myu->t4->d)
+
+  #pragma omp target map(myu->t4->d[2:5])
+  {
+    myu->t4->d[2]++;
+  }
+
+  #pragma omp target exit data map(release: myu->t4, myu->t4->d)
+
+  assert (myu->t4->a[2] == 1);
+  assert (myu->t4->b[2] == 3);
+  assert (myu->t4->c[2] == 3);
+  assert (myu->t4->d[2] == 3);
+
+  delete s4;
+  delete t4;
+  delete myu;
+}
+#endif
+
+#ifdef PTR_COMPONENT_MEMBER_SLICE_BASEPTR
+void
+ptr_component_member_slice_baseptr (void)
+{
+  INIT_ST;
+  U *myu = new U(s1, t1, s2, t2, &s3, &t3, s4, t4);
+
+  /* Implicit firstprivate 'myu'.  */
+  #pragma omp target map(to: myu->t1.c) map(myu->t1.c[2:5])
+  {
+    myu->t1.c[2]++;
+  }
+
+  #pragma omp target map(to: myu->t1.d) map(myu->t1.d[2:5])
+  {
+    myu->t1.d[2]++;
+  }
+
+  assert (myu->t1.c[2] == 2);
+  assert (myu->t1.d[2] == 2);
+
+  /* Explicitly-mapped 'myu'.  */
+  #pragma omp target map(to: myu, myu->t1.c) map(myu->t1.c[2:5])
+  {
+    myu->t1.c[2]++;
+  }
+
+  #pragma omp target map(to: myu, myu->t1.d) map(myu->t1.d[2:5])
+  {
+    myu->t1.d[2]++;
+  }
+
+  assert (myu->t1.c[2] == 4);
+  assert (myu->t1.d[2] == 4);
+
+  /* Implicit firstprivate 'myu'.  */
+  #pragma omp target map(to: myu->t2.c) map(myu->t2.c[2:5])
+  {
+    myu->t2.c[2]++;
+  }
+
+  #pragma omp target map(to: myu->t2.d) map(myu->t2.d[2:5])
+  {
+    myu->t2.d[2]++;
+  }
+
+  assert (myu->t2.c[2] == 2);
+  assert (myu->t2.d[2] == 2);
+
+  /* Explicitly-mapped 'myu'.  */
+  #pragma omp target map(to: myu, myu->t2.c) map(myu->t2.c[2:5])
+  {
+    myu->t2.c[2]++;
+  }
+
+  #pragma omp target map(to: myu, myu->t2.d) map(myu->t2.d[2:5])
+  {
+    myu->t2.d[2]++;
+  }
+
+  assert (myu->t2.c[2] == 4);
+  assert (myu->t2.d[2] == 4);
+
+  /* Implicit firstprivate 'myu'.  */
+  #pragma omp target map(to: myu->t3, myu->t3->c) map(myu->t3->c[2:5])
+  {
+    myu->t3->c[2]++;
+  }
+
+  #pragma omp target map(to: myu->t3, myu->t3->d) map(myu->t3->d[2:5])
+  {
+    myu->t3->d[2]++;
+  }
+
+  assert (myu->t3->c[2] == 2);
+  assert (myu->t3->d[2] == 2);
+
+  /* Explicitly-mapped 'myu'.  */
+  #pragma omp target map(to: myu, myu->t3, myu->t3->c) map(myu->t3->c[2:5])
+  {
+    myu->t3->c[2]++;
+  }
+
+  #pragma omp target map(to: myu, myu->t3, myu->t3->d) map(myu->t3->d[2:5])
+  {
+    myu->t3->d[2]++;
+  }
+
+  assert (myu->t3->c[2] == 4);
+  assert (myu->t3->d[2] == 4);
+
+  /* Implicit firstprivate 'myu'.  */
+  #pragma omp target map(to: myu->t4, myu->t4->c) map(myu->t4->c[2:5])
+  {
+    myu->t4->c[2]++;
+  }
+
+  #pragma omp target map(to: myu->t4, myu->t4->d) map(myu->t4->d[2:5])
+  {
+    myu->t4->d[2]++;
+  }
+
+  assert (myu->t4->c[2] == 2);
+  assert (myu->t4->d[2] == 2);
+
+  /* Explicitly-mapped 'myu'.  */
+  #pragma omp target map(to: myu, myu->t4, myu->t4->c) map(myu->t4->c[2:5])
+  {
+    myu->t4->c[2]++;
+  }
+
+  #pragma omp target map(to: myu, myu->t4, myu->t4->d) map(myu->t4->d[2:5])
+  {
+    myu->t4->d[2]++;
+  }
+
+  assert (myu->t4->c[2] == 4);
+  assert (myu->t4->d[2] == 4);
+
+  delete s4;
+  delete t4;
+  delete myu;
+}
+#endif
+
+#ifdef REF2PTR_COMPONENT_BASE
+void
+ref2ptr_component_base (void)
+{
+  INIT_ST;
+  U *myu_ptr = new U(s1, t1, s2, t2, &s3, &t3, s4, t4);
+  U *&myu = myu_ptr;
+
+  #pragma omp target map(myu->s1.a, myu->s1.b, myu->s1.c, myu->s1.d)
+  {
+    myu->s1.a++;
+    myu->s1.b++;
+    myu->s1.c++;
+    myu->s1.d++;
+  }
+
+  assert (myu->s1.a == 1);
+  assert (myu->s1.b == 1);
+  assert (myu->s1.c == &c1 + 1);
+  assert (myu->s1.d == &d1 + 1);
+
+  #pragma omp target map(myu->s2.a, myu->s2.b, myu->s2.c, myu->s2.d)
+  {
+    myu->s2.a++;
+    myu->s2.b++;
+    myu->s2.c++;
+    myu->s2.d++;
+  }
+
+  assert (myu->s2.a == 1);
+  assert (myu->s2.b == 1);
+  assert (myu->s2.c == &c2 + 1);
+  assert (myu->s2.d == &d2 + 1);
+
+  #pragma omp target map(to:myu->s3) \
+		     map(myu->s3->a, myu->s3->b, myu->s3->c, myu->s3->d)
+  {
+    myu->s3->a++;
+    myu->s3->b++;
+    myu->s3->c++;
+    myu->s3->d++;
+  }
+
+  assert (myu->s3->a == 1);
+  assert (myu->s3->b == 1);
+  assert (myu->s3->c == &c3 + 1);
+  assert (myu->s3->d == &d3 + 1);
+
+  #pragma omp target map(to:myu->s4) \
+		     map(myu->s4->a, myu->s4->b, myu->s4->c, myu->s4->d)
+  {
+    myu->s4->a++;
+    myu->s4->b++;
+    myu->s4->c++;
+    myu->s4->d++;
+  }
+
+  assert (myu->s4->a == 1);
+  assert (myu->s4->b == 1);
+  assert (myu->s4->c == &c4 + 1);
+  assert (myu->s4->d == &d4 + 1);
+
+  delete s4;
+  delete t4;
+  delete myu_ptr;
+}
+#endif
+
+#ifdef REF2PTR_COMPONENT_MEMBER_SLICE
+void
+ref2ptr_component_member_slice (void)
+{
+  INIT_ST;
+  U *myu_ptr = new U(s1, t1, s2, t2, &s3, &t3, s4, t4);
+  U *&myu = myu_ptr;
+
+  #pragma omp target map(myu->t1.a[2:5])
+  {
+    myu->t1.a[2]++;
+  }
+
+  #pragma omp target map(myu->t1.b[2:5])
+  {
+    myu->t1.b[2]++;
+  }
+
+  #pragma omp target enter data map(to: myu->t1.c)
+
+  #pragma omp target map(myu->t1.c[2:5])
+  {
+    myu->t1.c[2]++;
+  }
+
+  #pragma omp target exit data map(release: myu->t1.c)
+
+  #pragma omp target enter data map(to: myu->t1.d)
+
+  #pragma omp target map(myu->t1.d[2:5])
+  {
+    myu->t1.d[2]++;
+  }
+
+  #pragma omp target exit data map(release: myu->t1.d)
+
+  assert (myu->t1.a[2] == 1);
+  assert (myu->t1.b[2] == 3);
+  assert (myu->t1.c[2] == 3);
+  assert (myu->t1.d[2] == 3);
+
+  #pragma omp target map(myu->t2.a[2:5])
+  {
+    myu->t2.a[2]++;
+  }
+
+  #pragma omp target map(myu->t2.b[2:5])
+  {
+    myu->t2.b[2]++;
+  }
+
+  #pragma omp target enter data map(to: myu->t2.c)
+
+  #pragma omp target map(myu->t2.c[2:5])
+  {
+    myu->t2.c[2]++;
+  }
+
+  #pragma omp target exit data map(release: myu->t2.c)
+
+  #pragma omp target enter data map(to: myu->t2.d)
+
+  #pragma omp target map(myu->t2.d[2:5])
+  {
+    myu->t2.d[2]++;
+  }
+
+  #pragma omp target exit data map(release: myu->t2.d)
+
+  assert (myu->t2.a[2] == 1);
+  assert (myu->t2.b[2] == 3);
+  assert (myu->t2.c[2] == 3);
+  assert (myu->t2.d[2] == 3);
+
+  #pragma omp target enter data map(to: myu->t3)
+
+  #pragma omp target map(myu->t3->a[2:5])
+  {
+    myu->t3->a[2]++;
+  }
+
+  #pragma omp target map(myu->t3->b[2:5])
+  {
+    myu->t3->b[2]++;
+  }
+
+  #pragma omp target enter data map(to: myu->t3->c)
+
+  #pragma omp target map(myu->t3->c[2:5])
+  {
+    myu->t3->c[2]++;
+  }
+
+  #pragma omp target exit data map(release: myu->t3->c)
+
+  #pragma omp target enter data map(to: myu->t3->d)
+
+  #pragma omp target map(myu->t3->d[2:5])
+  {
+    myu->t3->d[2]++;
+  }
+
+  #pragma omp target exit data map(release: myu->t3, myu->t3->d)
+
+  assert (myu->t3->a[2] == 1);
+  assert (myu->t3->b[2] == 3);
+  assert (myu->t3->c[2] == 3);
+  assert (myu->t3->d[2] == 3);
+
+  #pragma omp target enter data map(to: myu->t4)
+
+  #pragma omp target map(myu->t4->a[2:5])
+  {
+    myu->t4->a[2]++;
+  }
+
+  #pragma omp target map(myu->t4->b[2:5])
+  {
+    myu->t4->b[2]++;
+  }
+
+  #pragma omp target enter data map(to: myu->t4->c)
+
+  #pragma omp target map(myu->t4->c[2:5])
+  {
+    myu->t4->c[2]++;
+  }
+
+  #pragma omp target exit data map(release: myu->t4->c)
+
+  #pragma omp target enter data map(to: myu->t4->d)
+
+  #pragma omp target map(myu->t4->d[2:5])
+  {
+    myu->t4->d[2]++;
+  }
+
+  #pragma omp target exit data map(release: myu->t4, myu->t4->d)
+
+  assert (myu->t4->a[2] == 1);
+  assert (myu->t4->b[2] == 3);
+  assert (myu->t4->c[2] == 3);
+  assert (myu->t4->d[2] == 3);
+
+  delete s4;
+  delete t4;
+  delete myu_ptr;
+}
+#endif
+
+#ifdef REF2PTR_COMPONENT_MEMBER_SLICE_BASEPTR
+void
+ref2ptr_component_member_slice_baseptr (void)
+{
+  INIT_ST;
+  U *myu_ptr = new U(s1, t1, s2, t2, &s3, &t3, s4, t4);
+  U *&myu = myu_ptr;
+
+  /* Implicit firstprivate 'myu'.  */
+  #pragma omp target map(to: myu->t1.c) map(myu->t1.c[2:5])
+  {
+    myu->t1.c[2]++;
+  }
+
+  #pragma omp target map(to: myu->t1.d) map(myu->t1.d[2:5])
+  {
+    myu->t1.d[2]++;
+  }
+
+  assert (myu->t1.c[2] == 2);
+  assert (myu->t1.d[2] == 2);
+
+  /* Explicitly-mapped 'myu'.  */
+  #pragma omp target map(to: myu, myu->t1.c) map(myu->t1.c[2:5])
+  {
+    myu->t1.c[2]++;
+  }
+
+  #pragma omp target map(to: myu, myu->t1.d) map(myu->t1.d[2:5])
+  {
+    myu->t1.d[2]++;
+  }
+
+  assert (myu->t1.c[2] == 4);
+  assert (myu->t1.d[2] == 4);
+
+  /* Implicit firstprivate 'myu'.  */
+  #pragma omp target map(to: myu->t2.c) map(myu->t2.c[2:5])
+  {
+    myu->t2.c[2]++;
+  }
+
+  #pragma omp target map(to: myu->t2.d) map(myu->t2.d[2:5])
+  {
+    myu->t2.d[2]++;
+  }
+
+  assert (myu->t2.c[2] == 2);
+  assert (myu->t2.d[2] == 2);
+
+  /* Explicitly-mapped 'myu'.  */
+  #pragma omp target map(to: myu, myu->t2.c) map(myu->t2.c[2:5])
+  {
+    myu->t2.c[2]++;
+  }
+
+  #pragma omp target map(to: myu, myu->t2.d) map(myu->t2.d[2:5])
+  {
+    myu->t2.d[2]++;
+  }
+
+  assert (myu->t2.c[2] == 4);
+  assert (myu->t2.d[2] == 4);
+
+  /* Implicit firstprivate 'myu'.  */
+  #pragma omp target map(to: myu->t3, myu->t3->c) map(myu->t3->c[2:5])
+  {
+    myu->t3->c[2]++;
+  }
+
+  #pragma omp target map(to: myu->t3, myu->t3->d) map(myu->t3->d[2:5])
+  {
+    myu->t3->d[2]++;
+  }
+
+  assert (myu->t3->c[2] == 2);
+  assert (myu->t3->d[2] == 2);
+
+  /* Explicitly-mapped 'myu'.  */
+  #pragma omp target map(to: myu, myu->t3, myu->t3->c) map(myu->t3->c[2:5])
+  {
+    myu->t3->c[2]++;
+  }
+
+  #pragma omp target map(to: myu, myu->t3, myu->t3->d) map(myu->t3->d[2:5])
+  {
+    myu->t3->d[2]++;
+  }
+
+  assert (myu->t3->c[2] == 4);
+  assert (myu->t3->d[2] == 4);
+
+  /* Implicit firstprivate 'myu'.  */
+  #pragma omp target map(to: myu->t4, myu->t4->c) map(myu->t4->c[2:5])
+  {
+    myu->t4->c[2]++;
+  }
+
+  #pragma omp target map(to: myu->t4, myu->t4->d) map(myu->t4->d[2:5])
+  {
+    myu->t4->d[2]++;
+  }
+
+  assert (myu->t4->c[2] == 2);
+  assert (myu->t4->d[2] == 2);
+
+  /* Explicitly-mapped 'myu'.  */
+  #pragma omp target map(to: myu, myu->t4, myu->t4->c) map(myu->t4->c[2:5])
+  {
+    myu->t4->c[2]++;
+  }
+
+  #pragma omp target map(to: myu, myu->t4, myu->t4->d) map(myu->t4->d[2:5])
+  {
+    myu->t4->d[2]++;
+  }
+
+  assert (myu->t4->c[2] == 4);
+  assert (myu->t4->d[2] == 4);
+
+  delete s4;
+  delete t4;
+  delete myu_ptr;
+}
+#endif
+
+int main (int argc, char *argv[])
+{
+#ifdef MAP_DECLS
+  map_decls ();
+#endif
+
+#ifdef NONREF_DECL_BASE
+  nonref_decl_base ();
+#endif
+#ifdef REF_DECL_BASE
+  ref_decl_base ();
+#endif
+#ifdef PTR_DECL_BASE
+  ptr_decl_base ();
+#endif
+#ifdef REF2PTR_DECL_BASE
+  ref2ptr_decl_base ();
+#endif
+
+#ifdef ARRAY_DECL_BASE
+  array_decl_base ();
+#endif
+#ifdef REF2ARRAY_DECL_BASE
+  ref2array_decl_base ();
+#endif
+#ifdef PTR_OFFSET_DECL_BASE
+  ptr_offset_decl_base ();
+#endif
+#ifdef REF2PTR_OFFSET_DECL_BASE
+  ref2ptr_offset_decl_base ();
+#endif
+
+#ifdef MAP_SECTIONS
+  map_sections ();
+#endif
+
+#ifdef NONREF_DECL_MEMBER_SLICE
+  nonref_decl_member_slice ();
+#endif
+#ifdef NONREF_DECL_MEMBER_SLICE_BASEPTR
+  nonref_decl_member_slice_baseptr ();
+#endif
+#ifdef REF_DECL_MEMBER_SLICE
+  ref_decl_member_slice ();
+#endif
+#ifdef REF_DECL_MEMBER_SLICE_BASEPTR
+  ref_decl_member_slice_baseptr ();
+#endif
+#ifdef PTR_DECL_MEMBER_SLICE
+  ptr_decl_member_slice ();
+#endif
+#ifdef PTR_DECL_MEMBER_SLICE_BASEPTR
+  ptr_decl_member_slice_baseptr ();
+#endif
+#ifdef REF2PTR_DECL_MEMBER_SLICE
+  ref2ptr_decl_member_slice ();
+#endif
+#ifdef REF2PTR_DECL_MEMBER_SLICE_BASEPTR
+  ref2ptr_decl_member_slice_baseptr ();
+#endif
+
+#ifdef ARRAY_DECL_MEMBER_SLICE
+  array_decl_member_slice ();
+#endif
+#ifdef ARRAY_DECL_MEMBER_SLICE_BASEPTR
+  array_decl_member_slice_baseptr ();
+#endif
+#ifdef REF2ARRAY_DECL_MEMBER_SLICE
+  ref2array_decl_member_slice ();
+#endif
+#ifdef REF2ARRAY_DECL_MEMBER_SLICE_BASEPTR
+  ref2array_decl_member_slice_baseptr ();
+#endif
+#ifdef PTR_OFFSET_DECL_MEMBER_SLICE
+  ptr_offset_decl_member_slice ();
+#endif
+#ifdef PTR_OFFSET_DECL_MEMBER_SLICE_BASEPTR
+  ptr_offset_decl_member_slice_baseptr ();
+#endif
+#ifdef REF2PTR_OFFSET_DECL_MEMBER_SLICE
+  ref2ptr_offset_decl_member_slice ();
+#endif
+#ifdef REF2PTR_OFFSET_DECL_MEMBER_SLICE_BASEPTR
+  ref2ptr_offset_decl_member_slice_baseptr ();
+#endif
+
+#ifdef PTRARRAY_DECL_MEMBER_SLICE
+  ptrarray_decl_member_slice ();
+#endif
+#ifdef PTRARRAY_DECL_MEMBER_SLICE_BASEPTR
+  ptrarray_decl_member_slice_baseptr ();
+#endif
+#ifdef REF2PTRARRAY_DECL_MEMBER_SLICE
+  ref2ptrarray_decl_member_slice ();
+#endif
+#ifdef REF2PTRARRAY_DECL_MEMBER_SLICE_BASEPTR
+  ref2ptrarray_decl_member_slice_baseptr ();
+#endif
+#ifdef PTRPTR_OFFSET_DECL_MEMBER_SLICE
+  ptrptr_offset_decl_member_slice ();
+#endif
+#ifdef PTRPTR_OFFSET_DECL_MEMBER_SLICE_BASEPTR
+  ptrptr_offset_decl_member_slice_baseptr ();
+#endif
+#ifdef REF2PTRPTR_OFFSET_DECL_MEMBER_SLICE
+  ref2ptrptr_offset_decl_member_slice ();
+#endif
+#ifdef REF2PTRPTR_OFFSET_DECL_MEMBER_SLICE_BASEPTR
+  ref2ptrptr_offset_decl_member_slice_baseptr ();
+#endif
+
+#ifdef NONREF_COMPONENT_BASE
+  nonref_component_base ();
+#endif
+#ifdef NONREF_COMPONENT_MEMBER_SLICE
+  nonref_component_member_slice ();
+#endif
+#ifdef NONREF_COMPONENT_MEMBER_SLICE_BASEPTR
+  nonref_component_member_slice_baseptr ();
+#endif
+
+#ifdef REF_COMPONENT_BASE
+  ref_component_base ();
+#endif
+#ifdef REF_COMPONENT_MEMBER_SLICE
+  ref_component_member_slice ();
+#endif
+#ifdef REF_COMPONENT_MEMBER_SLICE_BASEPTR
+  ref_component_member_slice_baseptr ();
+#endif
+
+#ifdef PTR_COMPONENT_BASE
+  ptr_component_base ();
+#endif
+#ifdef PTR_COMPONENT_MEMBER_SLICE
+  ptr_component_member_slice ();
+#endif
+#ifdef PTR_COMPONENT_MEMBER_SLICE_BASEPTR
+  ptr_component_member_slice_baseptr ();
+#endif
+
+#ifdef REF2PTR_COMPONENT_BASE
+  ref2ptr_component_base ();
+#endif
+#ifdef REF2PTR_COMPONENT_MEMBER_SLICE
+  ref2ptr_component_member_slice ();
+#endif
+#ifdef REF2PTR_COMPONENT_MEMBER_SLICE_BASEPTR
+  ref2ptr_component_member_slice_baseptr ();
+#endif
+
+  return 0;
+}
diff --git a/libgomp/testsuite/libgomp.c++/baseptrs-5.C b/libgomp/testsuite/libgomp.c++/baseptrs-5.C
new file mode 100644
index 000000000000..16bdfff3ae08
--- /dev/null
+++ b/libgomp/testsuite/libgomp.c++/baseptrs-5.C
@@ -0,0 +1,62 @@
+// { dg-do run }
+
+#include <cstring>
+#include <cassert>
+
+struct sa
+{
+  int *ptr;
+  int *ptr2;
+};
+
+struct sb
+{
+  int arr[10];
+};
+
+struct scp
+{
+  sa *&a;
+  sb *&b;
+  scp (sa *&my_a, sb *&my_b) : a(my_a), b(my_b) {}
+};
+
+int
+main ()
+{
+  sa *my_a = new sa;
+  sb *my_b = new sb;
+
+  my_a->ptr = new int[10];
+  my_a->ptr2 = new int[10];
+  scp *my_c = new scp(my_a, my_b);
+
+  memset (my_c->a->ptr, 0, sizeof (int) * 10);
+  memset (my_c->a->ptr2, 0, sizeof (int) * 10);
+
+  #pragma omp target map (my_c->a, \
+			  my_c->a->ptr, my_c->a->ptr[:10], \
+			  my_c->a->ptr2, my_c->a->ptr2[:10])
+  {
+    for (int i = 0; i < 10; i++)
+      {
+	my_c->a->ptr[i] = i;
+	my_c->a->ptr2[i] = i * 2;
+      }
+  }
+
+  for (int i = 0; i < 10; i++)
+    {
+      assert (my_c->a->ptr[i] == i);
+      assert (my_c->a->ptr2[i] == i * 2);
+    }
+
+  delete[] my_a->ptr;
+  delete[] my_a->ptr2;
+  delete my_a;
+  delete my_b;
+  delete my_c;
+
+  return 0;
+}
+
diff --git a/libgomp/testsuite/libgomp.c++/class-array-1.C b/libgomp/testsuite/libgomp.c++/class-array-1.C
new file mode 100644
index 000000000000..d8d3f7f1f99a
--- /dev/null
+++ b/libgomp/testsuite/libgomp.c++/class-array-1.C
@@ -0,0 +1,59 @@
+/* { dg-do run } */
+
+#include <cassert>
+
+#define N 1024
+
+class M {
+  int array[N];
+
+public:
+  M ()
+  {
+    for (int i = 0; i < N; i++)
+      array[i] = 0;
+  }
+
+  void incr_with_this (int c)
+  {
+#pragma omp target map(this->array[:N])
+    for (int i = 0; i < N; i++)
+      array[i] += c;
+  }
+
+  void incr_without_this (int c)
+  {
+#pragma omp target map(array[:N])
+    for (int i = 0; i < N; i++)
+      array[i] += c;
+  }
+
+  void incr_implicit (int c)
+  {
+#pragma omp target
+    for (int i = 0; i < N; i++)
+      array[i] += c;
+  }
+
+  void check (int c)
+  {
+    for (int i = 0; i < N; i++)
+      assert (array[i] == c);
+  }
+};
+
+int
+main (int argc, char *argv[])
+{
+  M m;
+
+  m.check (0);
+  m.incr_with_this (3);
+  m.check (3);
+  m.incr_without_this (5);
+  m.check (8);
+  m.incr_implicit (2);
+  m.check (10);
+
+  return 0;
+}
diff --git a/libgomp/testsuite/libgomp.c++/target-48.C b/libgomp/testsuite/libgomp.c++/target-48.C
new file mode 100644
index 000000000000..db171d2f5a3d
--- /dev/null
+++ b/libgomp/testsuite/libgomp.c++/target-48.C
@@ -0,0 +1,32 @@
+#include <cstring>
+#include <cassert>
+
+struct s {
+  int (&a)[10];
+  s(int (&a0)[10]) : a(a0) {}
+};
+
+int
+main (int argc, char *argv[])
+{
+  int la[10];
+  s v(la);
+
+  memset (la, 0, sizeof la);
+
+  #pragma omp target enter data map(to: v)
+
+  /* This mapping must use GOMP_MAP_ATTACH_DETACH not GOMP_MAP_ALWAYS_POINTER,
+     else the host reference v.a will be corrupted on copy-out.  */
+
+  #pragma omp target map(v.a[0:10])
+  {
+    v.a[5]++;
+  }
+
+  #pragma omp target exit data map(from: v)
+
+  assert (v.a[5] == 1);
+
+  return 0;
+}
diff --git a/libgomp/testsuite/libgomp.c++/target-49.C b/libgomp/testsuite/libgomp.c++/target-49.C
new file mode 100644
index 000000000000..efae9c9580ef
--- /dev/null
+++ b/libgomp/testsuite/libgomp.c++/target-49.C
@@ -0,0 +1,37 @@
+#include <cstring>
+#include <cassert>
+
+struct s {
+  int (&a)[10];
+  s(int (&a0)[10]) : a(a0) {}
+};
+
+int
+main (int argc, char *argv[])
+{
+  int la[10];
+  s v_real(la);
+  s *v = &v_real;
+
+  memset (la, 0, sizeof la);
+
+  #pragma omp target enter data map(to: v)
+
+  /* Copying the whole v[0] here DOES NOT WORK yet because the reference 'a' is
+     not copied "as if" it was mapped explicitly as a member.  FIXME.  */
+  #pragma omp target enter data map(to: v[0])
+
+  //#pragma omp target
+  {
+    v->a[5]++;
+  }
+
+  #pragma omp target exit data map(release: v[0])
+  #pragma omp target exit data map(from: v)
+
+  assert (v->a[5] == 1);
+
+  return 0;
+}
+
+// { dg-xfail-run-if "TODO" { *-*-* } { "-DACC_MEM_SHARED=0" } }
diff --git a/libgomp/testsuite/libgomp.c++/target-exit-data-reftoptr-1.C b/libgomp/testsuite/libgomp.c++/target-exit-data-reftoptr-1.C
new file mode 100644
index 000000000000..1a66fcb0f7fb
--- /dev/null
+++ b/libgomp/testsuite/libgomp.c++/target-exit-data-reftoptr-1.C
@@ -0,0 +1,34 @@
+#include <cstring>
+#include <cassert>
+
+struct S {
+  int *&ptr;
+  S(int *&ptr_) : ptr(ptr_) { }
+};
+
+int main()
+{
+  int *orig = new int[100];
+
+  memset (orig, 0, sizeof (int) * 100);
+
+  S svar(orig);
+
+#pragma omp target enter data map(to: svar.ptr, svar.ptr[10:80])
+
+#pragma omp target
+  {
+    for (int i = 10; i < 90; i++)
+      svar.ptr[i]++;
+  }
+
+#pragma omp target exit data map(release: svar.ptr) map(from: svar.ptr[10:80])
+
+  for (int i = 0; i < 100; i++)
+    assert (i >= 10 && i < 90 && svar.ptr[i] == 1
+	    || svar.ptr[i] == 0);
+
+  delete orig;
+
+  return 0;
+}
diff --git a/libgomp/testsuite/libgomp.c++/target-lambda-1.C b/libgomp/testsuite/libgomp.c++/target-lambda-1.C
index c5acbb8bf30c..fa882d09800f 100644
--- a/libgomp/testsuite/libgomp.c++/target-lambda-1.C
+++ b/libgomp/testsuite/libgomp.c++/target-lambda-1.C
@@ -2,6 +2,7 @@
 
 #include <cstdlib>
 #include <cstring>
+#include <cstdint>
 
 template <typename L>
 void
@@ -22,9 +23,11 @@ struct S
     auto fn = [=](void) -> bool
       {
 	bool mapped;
+	uintptr_t hostptr = (uintptr_t) ptr;
+	uintptr_t hostiptr = (uintptr_t) iptr;
 	#pragma omp target map(from:mapped)
 	{
-	  mapped = (ptr != NULL && iptr != NULL);
+	  mapped = (ptr != (int*) hostptr && iptr != (int*) hostiptr);
 	  if (mapped)
 	    {
 	      for (int i = 0; i < len; i++)
diff --git a/libgomp/testsuite/libgomp.c++/target-this-3.C b/libgomp/testsuite/libgomp.c++/target-this-3.C
index 6049ba8e2013..986582430e24 100644
--- a/libgomp/testsuite/libgomp.c++/target-this-3.C
+++ b/libgomp/testsuite/libgomp.c++/target-this-3.C
@@ -2,6 +2,7 @@
 
 #include <stdio.h>
 #include <string.h>
+#include <stdint.h>
 extern "C" void abort ();
 
 struct S
@@ -15,12 +16,13 @@ struct S
   bool set_ptr (int n)
   {
     bool mapped;
+    uintptr_t hostptr = (uintptr_t) ptr;
     #pragma omp target map(from:mapped)
     {
-      if (ptr != NULL)
+      if (ptr != (int *) hostptr)
 	for (int i = 0; i < ptr_len; i++)
 	  ptr[i] = n;
-      mapped = (ptr != NULL);
+      mapped = (ptr != (int *) hostptr);
     }
     return mapped;
   }
@@ -28,12 +30,13 @@ struct S
   bool set_refptr (int n)
   {
     bool mapped;
+    uintptr_t hostrefptr = (uintptr_t) refptr;
     #pragma omp target map(from:mapped)
     {
-      if (refptr != NULL)
+      if (refptr != (int *) hostrefptr)
 	for (int i = 0; i < refptr_len; i++)
 	  refptr[i] = n;
-      mapped = (refptr != NULL);
+      mapped = (refptr != (int *) hostrefptr);
     }
     return mapped;
   }
diff --git a/libgomp/testsuite/libgomp.c++/target-this-4.C b/libgomp/testsuite/libgomp.c++/target-this-4.C
index f0237c9b6b83..b2a593d03afa 100644
--- a/libgomp/testsuite/libgomp.c++/target-this-4.C
+++ b/libgomp/testsuite/libgomp.c++/target-this-4.C
@@ -4,6 +4,7 @@
 
 #include <cstdlib>
 #include <cstring>
+#include <cstdint>
 
 struct T
 {
@@ -18,12 +19,13 @@ struct T
     auto fn = [=](void) -> bool
       {
 	bool mapped;
+	uintptr_t hostptr = (uintptr_t) ptr;
 	#pragma omp target map(from:mapped)
 	{
-	  if (ptr)
+	  if (ptr != (int *) hostptr)
 	    for (int i = 0; i < ptr_len; i++)
 	      ptr[i] = n;
-	  mapped = (ptr != NULL);
+	  mapped = (ptr != (int *) hostptr);
 	}
 	return mapped;
       };
@@ -35,12 +37,13 @@ struct T
     auto fn = [=](void) -> bool
       {
 	bool mapped;
+	uintptr_t hostrefptr = (uintptr_t) refptr;
 	#pragma omp target map(from:mapped)
 	{
-	  if (refptr)
+	  if (refptr != (int *) hostrefptr)
 	    for (int i = 0; i < refptr_len; i++)
 	      refptr[i] = n;
-	  mapped = (refptr != NULL);
+	  mapped = (refptr != (int *) hostrefptr);
 	}
 	return mapped;
       };
diff --git a/libgomp/testsuite/libgomp.c-c++-common/baseptrs-1.c b/libgomp/testsuite/libgomp.c-c++-common/baseptrs-1.c
new file mode 100644
index 000000000000..073615625b7f
--- /dev/null
+++ b/libgomp/testsuite/libgomp.c-c++-common/baseptrs-1.c
@@ -0,0 +1,50 @@
+#include <stdlib.h>
+#include <string.h>
+#include <assert.h>
+#include <stdio.h>
+
+#define N 32
+
+typedef struct {
+  int x2[10][N];
+} x1type;
+
+typedef struct {
+  x1type x1[10];
+} p2type;
+
+typedef struct {
+  p2type *p2;
+} p1type;
+
+typedef struct {
+  p1type *p1;
+} x0type;
+
+typedef struct {
+  x0type x0[10];
+} p0type;
+
+int main(int argc, char *argv[])
+{
+  p0type *p0;
+  int k1 = 0, k2 = 0, k3 = 0, n = N;
+
+  p0 = (p0type *) malloc (sizeof *p0);
+  p0->x0[0].p1 = (p1type *) malloc (sizeof *p0->x0[0].p1);
+  p0->x0[0].p1->p2 = (p2type *) malloc (sizeof *p0->x0[0].p1->p2);
+  memset (p0->x0[0].p1->p2, 0, sizeof *p0->x0[0].p1->p2);
+
+#pragma omp target map(tofrom: p0->x0[k1].p1->p2[k2].x1[k3].x2[4][0:n]) \
+		   map(to: p0->x0[k1].p1, p0->x0[k1].p1->p2) \
+		   map(to: p0->x0[k1].p1[0])
+  {
+    for (int i = 0; i < n; i++)
+      p0->x0[k1].p1->p2[k2].x1[k3].x2[4][i] = i;
+  }
+
+  for (int i = 0; i < n; i++)
+    assert (i == p0->x0[k1].p1->p2[k2].x1[k3].x2[4][i]);
+
+  return 0;
+}
diff --git a/libgomp/testsuite/libgomp.c-c++-common/baseptrs-2.c b/libgomp/testsuite/libgomp.c-c++-common/baseptrs-2.c
new file mode 100644
index 000000000000..e335d7da9669
--- /dev/null
+++ b/libgomp/testsuite/libgomp.c-c++-common/baseptrs-2.c
@@ -0,0 +1,70 @@
+#include <stdlib.h>
+#include <string.h>
+#include <assert.h>
+
+#define N 32
+
+typedef struct {
+  int arr[N];
+  int *ptr;
+} sc;
+
+typedef struct {
+  sc *c;
+} sb;
+
+typedef struct {
+  sb *b;
+  sc *c;
+} sa;
+
+int main (int argc, char *argv[])
+{
+  sa *p;
+
+  p = (sa *) malloc (sizeof *p);
+  p->b = (sb *) malloc (sizeof *p->b);
+  p->b->c = (sc *) malloc (sizeof *p->b->c);
+  p->c = (sc *) malloc (sizeof *p->c);
+  p->b->c->ptr = (int *) malloc (N * sizeof (int));
+  p->c->ptr = (int *) malloc (N * sizeof (int));
+
+  for (int i = 0; i < N; i++)
+    {
+      p->b->c->ptr[i] = 0;
+      p->c->ptr[i] = 0;
+      p->b->c->arr[i] = 0;
+      p->c->arr[i] = 0;
+    }
+
+#pragma omp target map(to: p->b, p->b[0], p->c, p->c[0], p->b->c, p->b->c[0]) \
+		   map(to: p->b->c->ptr, p->c->ptr) \
+		   map(tofrom: p->b->c->ptr[:N], p->c->ptr[:N])
+  {
+    for (int i = 0; i < N; i++)
+      {
+	p->b->c->ptr[i] = i;
+	p->c->ptr[i] = i * 2;
+      }
+  }
+
+#pragma omp target map(to: p->b, p->b[0], p->b->c, p->c) \
+		   map(tofrom: p->c[0], p->b->c[0])
+  {
+    for (int i = 0; i < N; i++)
+      {
+	p->b->c->arr[i] = i * 3;
+	p->c->arr[i] = i * 4;
+      }
+  }
+
+  for (int i = 0; i < N; i++)
+    {
+      assert (p->b->c->ptr[i] == i);
+      assert (p->c->ptr[i] == i * 2);
+      assert (p->b->c->arr[i] == i * 3);
+      assert (p->c->arr[i] == i * 4);
+    }
+
+  return 0;
+}
diff --git a/libgomp/testsuite/libgomp.c-c++-common/ptr-attach-2.c b/libgomp/testsuite/libgomp.c-c++-common/ptr-attach-2.c
new file mode 100644
index 000000000000..889a4a253ae8
--- /dev/null
+++ b/libgomp/testsuite/libgomp.c-c++-common/ptr-attach-2.c
@@ -0,0 +1,60 @@
+#include <stdlib.h>
+
+struct blk { int x, y; };
+struct L
+{
+  #define N 10
+  struct {
+    int num_blocks[N];
+    struct blk * blocks[N];
+  } m;
+};
+
+void foo (struct L *l)
+{
+  for (int i = 0; i < N; i++)
+    {
+      l->m.blocks[i] = (struct blk *) malloc (sizeof (struct blk) * N);
+      l->m.num_blocks[i] = N;
+    }
+
+  #pragma omp target enter data map(to:l[:1])
+  for (int i = 0; i < N; i++)
+    {
+      #pragma omp target enter data map(to:l->m.blocks[i][:l->m.num_blocks[i]])
+    }
+
+  #pragma omp target
+  {
+    for (int i = 0; i < N; i++)
+      for (int j = 0; j < N; j++)
+	{
+	  l->m.blocks[i][j].x = i + j;
+	  l->m.blocks[i][j].y = i * j;
+	}
+  }
+
+  for (int i = 0; i < N; i++)
+    {
+      #pragma omp target exit data map(from:l->m.blocks[i][:l->m.num_blocks[i]])
+    }
+  #pragma omp target exit data map(from:l[:1])
+
+
+  for (int i = 0; i < N; i++)
+    for (int j = 0; j < N; j++)
+      {
+	if (l->m.blocks[i][j].x != i + j)
+	  abort ();
+	if (l->m.blocks[i][j].y != i * j)
+	  abort ();
+      }
+
+}
+
+int main (void)
+{
+  struct L l;
+  foo (&l);
+  return 0;
+}
diff --git a/libgomp/testsuite/libgomp.c-c++-common/target-implicit-map-2.c b/libgomp/testsuite/libgomp.c-c++-common/target-implicit-map-2.c
index 974a9786c3f6..4c49cd091c38 100644
--- a/libgomp/testsuite/libgomp.c-c++-common/target-implicit-map-2.c
+++ b/libgomp/testsuite/libgomp.c-c++-common/target-implicit-map-2.c
@@ -42,5 +42,7 @@ main (void)
 
   #pragma omp target exit data map(from:a.ptr, a.ptr[:N])
 
+  free (a.ptr);
+
   return 0;
 }
diff --git a/libgomp/testsuite/libgomp.c-c++-common/target-implicit-map-5.c b/libgomp/testsuite/libgomp.c-c++-common/target-implicit-map-5.c
new file mode 100644
index 000000000000..81a7752685c5
--- /dev/null
+++ b/libgomp/testsuite/libgomp.c-c++-common/target-implicit-map-5.c
@@ -0,0 +1,50 @@
+#include <stdlib.h>
+
+#define N 10
+
+struct S
+{
+  int a, b;
+  int *ptr;
+  int c, d;
+};
+
+int
+main (void)
+{
+  struct S a;
+  a.ptr = (int *) malloc (sizeof (int) * N);
+
+  for (int i = 0; i < N; i++)
+    a.ptr[i] = 0;
+
+  #pragma omp target enter data map(to: a.ptr)
+  #pragma omp target enter data map(to: a.ptr[:N])
+
+  #pragma omp target
+  for (int i = 0; i < N; i++)
+    a.ptr[i] += 1;
+
+  #pragma omp target update from(a.ptr[:N])
+
+  for (int i = 0; i < N; i++)
+    if (a.ptr[i] != 1)
+      abort ();
+
+  #pragma omp target map(a.ptr[:N])
+  for (int i = 0; i < N; i++)
+    a.ptr[i] += 1;
+
+  #pragma omp target update from(a.ptr[:N])
+
+  for (int i = 0; i < N; i++)
+    if (a.ptr[i] != 2)
+      abort ();
+
+  #pragma omp target exit data map(release: a.ptr[:N])
+  #pragma omp target exit data map(release: a.ptr)
+
+  free (a.ptr);
+
+  return 0;
+}
diff --git a/libgomp/testsuite/libgomp.c-c++-common/target-map-zlas-1.c b/libgomp/testsuite/libgomp.c-c++-common/target-map-zlas-1.c
new file mode 100644
index 000000000000..1ec0c9a0d5f9
--- /dev/null
+++ b/libgomp/testsuite/libgomp.c-c++-common/target-map-zlas-1.c
@@ -0,0 +1,36 @@
+#include <stdlib.h>
+
+#define N 10
+
+struct S
+{
+  int a, b;
+  int *ptr;
+  int c, d;
+};
+
+int
+main (void)
+{
+  struct S a;
+  a.ptr = (int *) malloc (sizeof (int) * N);
+
+  for (int i = 0; i < N; i++)
+    a.ptr[i] = 0;
+
+  #pragma omp target enter data map(to: a.ptr[:N])
+
+  #pragma omp target map(a, a.ptr[:0])
+  for (int i = 0; i < N; i++)
+    a.ptr[i] += 1;
+
+  #pragma omp target exit data map(from: a.ptr[:N])
+
+  for (int i = 0; i < N; i++)
+    if (a.ptr[i] != 1)
+      abort ();
+
+  free (a.ptr);
+
+  return 0;
+}
diff --git a/libgomp/testsuite/libgomp.fortran/struct-elem-map-1.f90 b/libgomp/testsuite/libgomp.fortran/struct-elem-map-1.f90
index 58550c79d69c..7f3d8174f97b 100644
--- a/libgomp/testsuite/libgomp.fortran/struct-elem-map-1.f90
+++ b/libgomp/testsuite/libgomp.fortran/struct-elem-map-1.f90
@@ -409,3 +409,6 @@ contains
   end subroutine eight
 
 end program main
+
+! Fixed by the "Fortran pointers and member mappings" patch
+! { dg-xfail-run-if TODO { offload_device_nonshared_as } }
-- 
2.41.0


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

* [PATCH v7 3/5] OpenMP: Pointers and member mappings
  2023-08-18 22:47 [PATCH v7 0/5] OpenMP/OpenACC: map clause and OMP gimplify rework Julian Brown
  2023-08-18 22:47 ` [PATCH v7 1/5] OpenMP/OpenACC: Reindent TO/FROM/_CACHE_ stanza in {c_}finish_omp_clause Julian Brown
  2023-08-18 22:47 ` [PATCH v7 2/5] OpenMP/OpenACC: Rework clause expansion and nested struct handling Julian Brown
@ 2023-08-18 22:47 ` Julian Brown
  2023-12-06 11:36   ` Tobias Burnus
  2023-08-18 22:47 ` [PATCH v7 4/5] OpenMP/OpenACC: Unordered/non-constant component offset runtime diagnostic Julian Brown
  2023-08-18 22:47 ` [PATCH v7 5/5] OpenMP/OpenACC: Reorganise OMP map clause handling in gimplify.cc Julian Brown
  4 siblings, 1 reply; 19+ messages in thread
From: Julian Brown @ 2023-08-18 22:47 UTC (permalink / raw)
  To: gcc-patches; +Cc: fortran, tobias, jakub

This patch changes the mapping node arrangement used for array components
of derived types in order to accommodate for changes made in the previous
patch, particularly the use of "GOMP_MAP_ATTACH_DETACH" for pointer-typed
derived-type members instead of "GOMP_MAP_ALWAYS_POINTER".

We change the mapping nodes used for a derived-type mapping like this:

  type T
  integer, pointer, dimension(:) :: arrptr
  end type T

  type(T) :: tvar
  [...]
  !$omp target map(tofrom: tvar%arrptr)

So that the nodes used look like this:

  1) map(to: tvar%arrptr)   -->
  GOMP_MAP_TO [implicit]  *tvar%arrptr%data  (the array data)
  GOMP_MAP_TO_PSET        tvar%arrptr        (the descriptor)
  GOMP_MAP_ATTACH_DETACH  tvar%arrptr%data

  2) map(tofrom: tvar%arrptr(3:8)   -->
  GOMP_MAP_TOFROM         *tvar%arrptr%data(3)  (size 8-3+1, etc.)
  GOMP_MAP_TO_PSET        tvar%arrptr
  GOMP_MAP_ATTACH_DETACH  tvar%arrptr%data      (bias 3, etc.)

In this case, we can determine in the front-end that the
whole-array/pointer mapping (1) is only needed to map the pointer
-- so we drop it entirely.  (Note also that we set -- early -- the
OMP_CLAUSE_MAP_RUNTIME_IMPLICIT_P flag for whole-array-via-pointer
mappings. See below.)

In the middle end, we process mappings using the struct sibling-list
handling machinery by moving the "GOMP_MAP_TO_PSET" node from the middle
of the group of three mapping nodes to the proper sorted position after
the GOMP_MAP_STRUCT mapping:

  GOMP_MAP_STRUCT   tvar     (len: 1)
  GOMP_MAP_TO_PSET  tvar%arr (size: 64, etc.)  <--. moved here
  [...]                                           |
  GOMP_MAP_TOFROM         *tvar%arrptr%data(3) ___|
  GOMP_MAP_ATTACH_DETACH  tvar%arrptr%data

In another case, if we have an array of derived-type values "dtarr",
and mappings like:

  i = 1
  j = 1
  map(to: dtarr(i)%arrptr) map(tofrom: dtarr(j)%arrptr(3:8))

We still map the same way, but this time we cannot prove that the base
expressions "dtarr(i) and "dtarr(j)" are the same in the front-end.
So we keep both mappings, but we move the "[implicit]" mapping of the
full-array reference to the end of the clause list in gimplify.cc (by
adjusting the topological sorting algorithm):

  GOMP_MAP_STRUCT         dtvar  (len: 2)
  GOMP_MAP_TO_PSET        dtvar(i)%arrptr
  GOMP_MAP_TO_PSET        dtvar(j)%arrptr
  [...]
  GOMP_MAP_TOFROM         *dtvar(j)%arrptr%data(3)  (size: 8-3+1)
  GOMP_MAP_ATTACH_DETACH  dtvar(j)%arrptr%data
  GOMP_MAP_TO [implicit]  *dtvar(i)%arrptr%data(1)  (size: whole array)
  GOMP_MAP_ATTACH_DETACH  dtvar(i)%arrptr%data

Always moving "[implicit]" full-array mappings after array-section
mappings (without that bit set) means that we'll avoid copying the whole
array unnecessarily -- even in cases where we can't prove that the arrays
are the same.

The patch also fixes some bugs with "enter data" and "exit data"
directives with this new mapping arrangement.  Also now if you have
mappings like this:

  #pragma omp target enter data map(to: dv, dv%arr(1:20))

The whole of the derived-type variable "dv" is mapped, so the
GOMP_MAP_TO_PSET for the array-section mapping can be dropped:

  GOMP_MAP_TO            dv

  GOMP_MAP_TO            *dv%arr%data
  GOMP_MAP_TO_PSET       dv%arr <-- deleted (array section mapping)
  GOMP_MAP_ATTACH_DETACH dv%arr%data

To accommodate for recent changes to mapping nodes made by
Tobias, this version of the patch avoids using GOMP_MAP_TO_PSET
for "exit data" directives, in favour of using the "correct"
GOMP_MAP_RELEASE/GOMP_MAP_DELETE kinds during early expansion.  A new
flag is introduced so the middle-end knows when the latter two kinds
are being used specifically for an array descriptor.

This version of the patch is based on the version posted for the og13
branch:

https://gcc.gnu.org/pipermail/gcc-patches/2023-June/622222.html

2023-08-18  Julian Brown  <julian@codesourcery.com>

gcc/fortran/
        * dependency.cc (gfc_omp_expr_prefix_same): New function.
        * dependency.h (gfc_omp_expr_prefix_same): Add prototype.
        * gfortran.h (gfc_omp_namelist): Add "duplicate_of" field to "u2"
        union.
        * trans-openmp.cc (dependency.h): Include.
        (gfc_trans_omp_array_section): Adjust mapping node arrangement for
        array descriptors.  Use GOMP_MAP_TO_PSET or
        GOMP_MAP_RELEASE/GOMP_MAP_DELETE with the OMP_CLAUSE_RELEASE_DESCRIPTOR
        flag set.
        (gfc_symbol_rooted_namelist): New function.
        (gfc_trans_omp_clauses): Check subcomponent and subarray/element
        accesses elsewhere in the clause list for pointers to derived types or
        array descriptors, and adjust or drop mapping nodes appropriately.
        Adjust for changes to mapping node arrangement.
        (gfc_trans_oacc_executable_directive): Pass code op through.

gcc/
        * gimplify.cc (omp_map_clause_descriptor_p): New function.
        (build_omp_struct_comp_nodes, omp_get_attachment, omp_group_base): Use
        above function.
        (omp_tsort_mapping_groups): Process nodes that have
        OMP_CLAUSE_MAP_RUNTIME_IMPLICIT_P set after those that don't.  Add
        enter_exit_data parameter.
        (omp_resolve_clause_dependencies): Remove GOMP_MAP_TO_PSET mappings if
        we're mapping the whole containing derived-type variable.
        (omp_accumulate_sibling_list): Adjust GOMP_MAP_TO_PSET handling.
        Remove GOMP_MAP_ALWAYS_POINTER handling.
        (gimplify_scan_omp_clauses): Pass enter_exit argument to
        omp_tsort_mapping_groups.  Don't adjust/remove GOMP_MAP_TO_PSET
        mappings for derived-type components here.
        * tree.h (OMP_CLAUSE_RELEASE_DESCRIPTOR): New macro.

gcc/testsuite/
        * gfortran.dg/gomp/map-9.f90: Adjust scan output.
        * gfortran.dg/gomp/map-subarray-2.f90: New test.
        * gfortran.dg/gomp/map-subarray.f90: New test.

libgomp/
        * testsuite/libgomp.fortran/map-subarray.f90: New test.
        * testsuite/libgomp.fortran/map-subarray-2.f90: New test.
        * testsuite/libgomp.fortran/map-subarray-3.f90: New test.
        * testsuite/libgomp.fortran/map-subarray-4.f90: New test.
        * testsuite/libgomp.fortran/map-subarray-6.f90: New test.
        * testsuite/libgomp.fortran/map-subarray-7.f90: New test.
        * testsuite/libgomp.fortran/map-subarray-8.f90: New test.
        * testsuite/libgomp.fortran/map-subcomponents.f90: New test.
        * testsuite/libgomp.fortran/struct-elem-map-1.f90: Adjust for
        descriptor-mapping changes.  Remove XFAIL.
---
 gcc/fortran/dependency.cc                     | 128 +++++++++
 gcc/fortran/dependency.h                      |   1 +
 gcc/fortran/gfortran.h                        |   1 +
 gcc/fortran/trans-openmp.cc                   | 272 +++++++++++++++---
 gcc/gimplify.cc                               | 163 ++++++++---
 gcc/testsuite/gfortran.dg/gomp/map-9.f90      |   2 +-
 .../gfortran.dg/gomp/map-subarray-2.f90       |  57 ++++
 .../gfortran.dg/gomp/map-subarray.f90         |  40 +++
 gcc/tree.h                                    |   4 +
 .../libgomp.fortran/map-subarray-2.f90        | 108 +++++++
 .../libgomp.fortran/map-subarray-3.f90        |  62 ++++
 .../libgomp.fortran/map-subarray-4.f90        |  35 +++
 .../libgomp.fortran/map-subarray-6.f90        |  26 ++
 .../libgomp.fortran/map-subarray-7.f90        |  29 ++
 .../libgomp.fortran/map-subarray-8.f90        |  47 +++
 .../libgomp.fortran/map-subarray.f90          |  33 +++
 .../libgomp.fortran/map-subcomponents.f90     |  32 +++
 .../libgomp.fortran/struct-elem-map-1.f90     | 183 +++++++++++-
 18 files changed, 1141 insertions(+), 82 deletions(-)
 create mode 100644 gcc/testsuite/gfortran.dg/gomp/map-subarray-2.f90
 create mode 100644 gcc/testsuite/gfortran.dg/gomp/map-subarray.f90
 create mode 100644 libgomp/testsuite/libgomp.fortran/map-subarray-2.f90
 create mode 100644 libgomp/testsuite/libgomp.fortran/map-subarray-3.f90
 create mode 100644 libgomp/testsuite/libgomp.fortran/map-subarray-4.f90
 create mode 100644 libgomp/testsuite/libgomp.fortran/map-subarray-6.f90
 create mode 100644 libgomp/testsuite/libgomp.fortran/map-subarray-7.f90
 create mode 100644 libgomp/testsuite/libgomp.fortran/map-subarray-8.f90
 create mode 100644 libgomp/testsuite/libgomp.fortran/map-subarray.f90
 create mode 100644 libgomp/testsuite/libgomp.fortran/map-subcomponents.f90

diff --git a/gcc/fortran/dependency.cc b/gcc/fortran/dependency.cc
index 632b3985723e..94647032ab53 100644
--- a/gcc/fortran/dependency.cc
+++ b/gcc/fortran/dependency.cc
@@ -2337,3 +2337,131 @@ gfc_dep_resolver (gfc_ref *lref, gfc_ref *rref, gfc_reverse *reverse,
 
   return fin_dep == GFC_DEP_OVERLAP;
 }
+
+/* Check if two refs are equal, for the purposes of checking if one might be
+   the base of the other for OpenMP (target directives).  Derived from
+   gfc_dep_resolver.  This function is stricter, e.g. indices arr(i) and
+   arr(j) compare as non-equal.  */
+
+bool
+gfc_omp_expr_prefix_same (gfc_expr *lexpr, gfc_expr *rexpr)
+{
+  gfc_ref *lref, *rref;
+
+  if (lexpr->symtree && rexpr->symtree)
+    {
+      /* See are_identical_variables above.  */
+      if (lexpr->symtree->n.sym->attr.dummy
+	  && rexpr->symtree->n.sym->attr.dummy)
+	{
+	  /* Dummy arguments: Only check for equal names.  */
+	  if (lexpr->symtree->n.sym->name != rexpr->symtree->n.sym->name)
+	    return false;
+	}
+      else
+	{
+	  if (lexpr->symtree->n.sym != rexpr->symtree->n.sym)
+	    return false;
+	}
+    }
+  else if (lexpr->base_expr && rexpr->base_expr)
+    {
+      if (gfc_dep_compare_expr (lexpr->base_expr, rexpr->base_expr) != 0)
+	return false;
+    }
+  else
+    return false;
+
+  lref = lexpr->ref;
+  rref = rexpr->ref;
+
+  while (lref && rref)
+    {
+      gfc_dependency fin_dep = GFC_DEP_EQUAL;
+
+      if (lref && lref->type == REF_COMPONENT && lref->u.c.component
+	  && strcmp (lref->u.c.component->name, "_data") == 0)
+	lref = lref->next;
+
+      if (rref && rref->type == REF_COMPONENT && rref->u.c.component
+	  && strcmp (rref->u.c.component->name, "_data") == 0)
+	rref = rref->next;
+
+      gcc_assert (lref->type == rref->type);
+
+      switch (lref->type)
+	{
+	case REF_COMPONENT:
+	  if (lref->u.c.component != rref->u.c.component)
+	    return false;
+	  break;
+
+	case REF_ARRAY:
+	  if (ref_same_as_full_array (lref, rref))
+	    break;
+	  if (ref_same_as_full_array (rref, lref))
+	    break;
+
+	  if (lref->u.ar.dimen != rref->u.ar.dimen)
+	    {
+	      if (lref->u.ar.type == AR_FULL
+		  && gfc_full_array_ref_p (rref, NULL))
+		break;
+	      if (rref->u.ar.type == AR_FULL
+		  && gfc_full_array_ref_p (lref, NULL))
+		break;
+	      return false;
+	    }
+
+	  for (int n = 0; n < lref->u.ar.dimen; n++)
+	    {
+	      if (lref->u.ar.dimen_type[n] == DIMEN_VECTOR
+		  && rref->u.ar.dimen_type[n] == DIMEN_VECTOR
+		  && gfc_dep_compare_expr (lref->u.ar.start[n],
+					   rref->u.ar.start[n]) == 0)
+		continue;
+	      if (lref->u.ar.dimen_type[n] == DIMEN_RANGE
+		  && rref->u.ar.dimen_type[n] == DIMEN_RANGE)
+		fin_dep = check_section_vs_section (&lref->u.ar, &rref->u.ar,
+						    n);
+	      else if (lref->u.ar.dimen_type[n] == DIMEN_ELEMENT
+		       && rref->u.ar.dimen_type[n] == DIMEN_RANGE)
+		fin_dep = gfc_check_element_vs_section (lref, rref, n);
+	      else if (rref->u.ar.dimen_type[n] == DIMEN_ELEMENT
+		       && lref->u.ar.dimen_type[n] == DIMEN_RANGE)
+		fin_dep = gfc_check_element_vs_section (rref, lref, n);
+	      else if (lref->u.ar.dimen_type[n] == DIMEN_ELEMENT
+		       && rref->u.ar.dimen_type[n] == DIMEN_ELEMENT)
+		{
+		  gfc_array_ref l_ar = lref->u.ar;
+		  gfc_array_ref r_ar = rref->u.ar;
+		  gfc_expr *l_start = l_ar.start[n];
+		  gfc_expr *r_start = r_ar.start[n];
+		  int i = gfc_dep_compare_expr (r_start, l_start);
+		  if (i == 0)
+		    fin_dep = GFC_DEP_EQUAL;
+		  else
+		    return false;
+		}
+	      else
+		return false;
+	      if (n + 1 < lref->u.ar.dimen
+		  && fin_dep != GFC_DEP_EQUAL)
+		return false;
+	    }
+
+	  if (fin_dep != GFC_DEP_EQUAL
+	      && fin_dep != GFC_DEP_OVERLAP)
+	    return false;
+
+	  break;
+
+	default:
+	  gcc_unreachable ();
+	}
+      lref = lref->next;
+      rref = rref->next;
+    }
+
+  return true;
+}
diff --git a/gcc/fortran/dependency.h b/gcc/fortran/dependency.h
index fbbede8b22ca..a2458f79034b 100644
--- a/gcc/fortran/dependency.h
+++ b/gcc/fortran/dependency.h
@@ -40,5 +40,6 @@ int gfc_expr_is_one (gfc_expr *, int);
 bool gfc_dep_resolver (gfc_ref *, gfc_ref *, gfc_reverse *,
 		      bool identical = false);
 bool gfc_are_equivalenced_arrays (gfc_expr *, gfc_expr *);
+bool gfc_omp_expr_prefix_same (gfc_expr *, gfc_expr *);
 
 gfc_expr * gfc_discard_nops (gfc_expr *);
diff --git a/gcc/fortran/gfortran.h b/gcc/fortran/gfortran.h
index 9a00e6dea6f1..3c8270a0f83a 100644
--- a/gcc/fortran/gfortran.h
+++ b/gcc/fortran/gfortran.h
@@ -1378,6 +1378,7 @@ typedef struct gfc_omp_namelist
       gfc_namespace *ns;
       gfc_expr *allocator;
       struct gfc_symbol *traits_sym;
+      struct gfc_omp_namelist *duplicate_of;
     } u2;
   struct gfc_omp_namelist *next;
   locus where;
diff --git a/gcc/fortran/trans-openmp.cc b/gcc/fortran/trans-openmp.cc
index 46f80d086fd5..a9dc1a617be5 100644
--- a/gcc/fortran/trans-openmp.cc
+++ b/gcc/fortran/trans-openmp.cc
@@ -40,6 +40,7 @@ along with GCC; see the file COPYING3.  If not see
 #include "omp-general.h"
 #include "omp-low.h"
 #include "memmodel.h"  /* For MEMMODEL_ enums.  */
+#include "dependency.h"
 
 #undef GCC_DIAG_STYLE
 #define GCC_DIAG_STYLE __gcc_tdiag__
@@ -2491,36 +2492,23 @@ gfc_trans_omp_array_section (stmtblock_t *block, gfc_exec_op op,
     }
   if (GFC_DESCRIPTOR_TYPE_P (TREE_TYPE (decl)))
     {
-      tree desc_node;
       tree type = TREE_TYPE (decl);
       ptr2 = gfc_conv_descriptor_data_get (decl);
-      desc_node = build_omp_clause (input_location, OMP_CLAUSE_MAP);
-      OMP_CLAUSE_DECL (desc_node) = decl;
-      OMP_CLAUSE_SIZE (desc_node) = TYPE_SIZE_UNIT (type);
-      if (OMP_CLAUSE_MAP_KIND (node) == GOMP_MAP_DELETE)
+      node2 = build_omp_clause (input_location, OMP_CLAUSE_MAP);
+      OMP_CLAUSE_DECL (node2) = decl;
+      OMP_CLAUSE_SIZE (node2) = TYPE_SIZE_UNIT (type);
+      if (OMP_CLAUSE_MAP_KIND (node) == GOMP_MAP_DELETE
+	  || OMP_CLAUSE_MAP_KIND (node) == GOMP_MAP_RELEASE
+	  || op == EXEC_OMP_TARGET_EXIT_DATA)
 	{
-	  OMP_CLAUSE_SET_MAP_KIND (desc_node, GOMP_MAP_DELETE);
-	  node2 = desc_node;
-	}
-      else if (OMP_CLAUSE_MAP_KIND (node) == GOMP_MAP_RELEASE
-	       || op == EXEC_OMP_TARGET_EXIT_DATA)
-	{
-	  OMP_CLAUSE_SET_MAP_KIND (desc_node, GOMP_MAP_RELEASE);
-	  node2 = desc_node;
-	}
-      else if (ptr_kind == GOMP_MAP_ALWAYS_POINTER)
-	{
-	  OMP_CLAUSE_SET_MAP_KIND (desc_node, GOMP_MAP_TO);
-	  node2 = node;
-	  node = desc_node;  /* Needs to come first.  */
+	  gomp_map_kind map_kind
+	    = (op == EXEC_OMP_TARGET_EXIT_DATA) ? GOMP_MAP_RELEASE
+						: OMP_CLAUSE_MAP_KIND (node);
+	  OMP_CLAUSE_SET_MAP_KIND (node2, map_kind);
+	  OMP_CLAUSE_RELEASE_DESCRIPTOR (node2) = 1;
 	}
       else
-	{
-	  OMP_CLAUSE_SET_MAP_KIND (desc_node, GOMP_MAP_TO_PSET);
-	  node2 = desc_node;
-	}
-      if (op == EXEC_OMP_TARGET_EXIT_DATA)
-	return;
+	OMP_CLAUSE_SET_MAP_KIND (node2, GOMP_MAP_TO_PSET);
       node3 = build_omp_clause (input_location, OMP_CLAUSE_MAP);
       OMP_CLAUSE_SET_MAP_KIND (node3, ptr_kind);
       OMP_CLAUSE_DECL (node3) = gfc_conv_descriptor_data_get (decl);
@@ -2624,6 +2612,73 @@ handle_iterator (gfc_namespace *ns, stmtblock_t *iter_block, tree block)
   return list;
 }
 
+/* To alleviate quadratic behaviour in checking each entry of a
+   gfc_omp_namelist against every other entry, we build a hashtable indexed by
+   gfc_symbol pointer, which we can use in the usual case that a map
+   expression has a symbol as its root term.  Return a namelist based on the
+   root symbol used by N, building a new table in SYM_ROOTED_NL using the
+   gfc_omp_namelist N2 (all clauses) if we haven't done so already.  */
+
+static gfc_omp_namelist *
+get_symbol_rooted_namelist (hash_map<gfc_symbol *,
+				     gfc_omp_namelist *> *&sym_rooted_nl,
+			    gfc_omp_namelist *n,
+			    gfc_omp_namelist *n2, bool *sym_based)
+{
+  /* Early-out if we have a NULL clause list (e.g. for OpenACC).  */
+  if (!n2)
+    return NULL;
+
+  gfc_symbol *use_sym = NULL;
+
+  /* We're only interested in cases where we have an expression, e.g. a
+     component access.  */
+  if (n->expr && n->expr->expr_type == EXPR_VARIABLE && n->expr->symtree)
+    use_sym = n->expr->symtree->n.sym;
+
+  *sym_based = false;
+
+  if (!use_sym)
+    return n2;
+
+  if (!sym_rooted_nl)
+    {
+      sym_rooted_nl = new hash_map<gfc_symbol *, gfc_omp_namelist *> ();
+
+      for (; n2 != NULL; n2 = n2->next)
+	{
+	  if (!n2->expr
+	      || n2->expr->expr_type != EXPR_VARIABLE
+	      || !n2->expr->symtree)
+	    continue;
+
+	  gfc_omp_namelist *nl_copy = gfc_get_omp_namelist ();
+	  memcpy (nl_copy, n2, sizeof *nl_copy);
+	  nl_copy->u2.duplicate_of = n2;
+	  nl_copy->next = NULL;
+
+	  gfc_symbol *idx_sym = n2->expr->symtree->n.sym;
+
+	  bool existed;
+	  gfc_omp_namelist *&entry
+	    = sym_rooted_nl->get_or_insert (idx_sym, &existed);
+	  if (existed)
+	    nl_copy->next = entry;
+	  entry = nl_copy;
+	}
+    }
+
+  gfc_omp_namelist **n2_sym = sym_rooted_nl->get (use_sym);
+
+  if (n2_sym)
+    {
+      *sym_based = true;
+      return *n2_sym;
+    }
+
+  return NULL;
+}
+
 static tree
 gfc_trans_omp_clauses (stmtblock_t *block, gfc_omp_clauses *clauses,
 		       locus where, bool declare_simd = false,
@@ -2641,6 +2696,8 @@ gfc_trans_omp_clauses (stmtblock_t *block, gfc_omp_clauses *clauses,
   if (clauses == NULL)
     return NULL_TREE;
 
+  hash_map<gfc_symbol *, gfc_omp_namelist *> *sym_rooted_nl = NULL;
+
   for (list = 0; list < OMP_LIST_NUM; list++)
     {
       gfc_omp_namelist *n = clauses->lists[list];
@@ -3650,6 +3707,54 @@ gfc_trans_omp_clauses (stmtblock_t *block, gfc_omp_clauses *clauses,
 			      goto finalize_map_clause;
 			    }
 
+			  gfc_omp_namelist *n2
+			    = openacc ? NULL : clauses->lists[OMP_LIST_MAP];
+
+			  bool sym_based;
+			  n2 = get_symbol_rooted_namelist (sym_rooted_nl, n,
+							   n2, &sym_based);
+
+			  /* If the last reference is a pointer to a derived
+			     type ("foo%dt_ptr"), check if any subcomponents
+			     of the same derived type member are being mapped
+			     elsewhere in the clause list ("foo%dt_ptr%x",
+			     etc.).  If we have such subcomponent mappings,
+			     we only create an ALLOC node for the pointer
+			     itself, and inhibit mapping the whole derived
+			     type.  */
+
+			  for (; n2 != NULL; n2 = n2->next)
+			    {
+			      if ((!sym_based && n == n2)
+				  || (sym_based && n == n2->u2.duplicate_of)
+				  || !n2->expr)
+				continue;
+
+			      if (!gfc_omp_expr_prefix_same (n->expr,
+							     n2->expr))
+				continue;
+
+			      gfc_ref *ref1 = n->expr->ref;
+			      gfc_ref *ref2 = n2->expr->ref;
+
+			      while (ref1->next && ref2->next)
+				{
+				  ref1 = ref1->next;
+				  ref2 = ref2->next;
+				}
+
+			      if (ref2->next)
+				{
+				  inner = build_fold_addr_expr (inner);
+				  OMP_CLAUSE_SET_MAP_KIND (node,
+							   GOMP_MAP_ALLOC);
+				  OMP_CLAUSE_DECL (node) = inner;
+				  OMP_CLAUSE_SIZE (node)
+				    = TYPE_SIZE_UNIT (TREE_TYPE (inner));
+				  goto finalize_map_clause;
+				}
+			    }
+
 			  tree data, size;
 
 			  if (lastref->u.c.component->ts.type == BT_CLASS)
@@ -3705,7 +3810,6 @@ gfc_trans_omp_clauses (stmtblock_t *block, gfc_omp_clauses *clauses,
 		      if (GFC_DESCRIPTOR_TYPE_P (TREE_TYPE (inner)))
 			{
 			  gomp_map_kind map_kind;
-			  tree desc_node;
 			  tree type = TREE_TYPE (inner);
 			  tree ptr = gfc_conv_descriptor_data_get (inner);
 			  ptr = build_fold_indirect_ref (ptr);
@@ -3750,24 +3854,93 @@ gfc_trans_omp_clauses (stmtblock_t *block, gfc_omp_clauses *clauses,
 			  OMP_CLAUSE_SIZE (node)
 			    = fold_build2 (MULT_EXPR, gfc_array_index_type,
 					   OMP_CLAUSE_SIZE (node), elemsz);
-			  desc_node = build_omp_clause (input_location,
-							OMP_CLAUSE_MAP);
-			  if (openacc)
-			    OMP_CLAUSE_SET_MAP_KIND (desc_node,
+			  node2 = build_omp_clause (input_location,
+						    OMP_CLAUSE_MAP);
+			  if (openacc
+			      || (map_kind != GOMP_MAP_RELEASE
+				  && map_kind != GOMP_MAP_DELETE))
+			    OMP_CLAUSE_SET_MAP_KIND (node2,
 						     GOMP_MAP_TO_PSET);
 			  else
-			    OMP_CLAUSE_SET_MAP_KIND (desc_node, map_kind);
-			  OMP_CLAUSE_DECL (desc_node) = inner;
-			  OMP_CLAUSE_SIZE (desc_node) = TYPE_SIZE_UNIT (type);
-			  if (openacc)
-			    node2 = desc_node;
-			  else
+			    OMP_CLAUSE_SET_MAP_KIND (node2, map_kind);
+			  OMP_CLAUSE_DECL (node2) = inner;
+			  OMP_CLAUSE_SIZE (node2) = TYPE_SIZE_UNIT (type);
+			  if (!openacc)
 			    {
-			      node2 = node;
-			      node = desc_node;  /* Put first.  */
+			      if (n->expr->ts.type == BT_DERIVED
+				  && n->expr->ts.u.derived->attr.alloc_comp)
+				{
+				  /* Save array descriptor for use
+				     in gfc_omp_deep_mapping{,_p,_cnt}; force
+				     evaluate to ensure that it is
+				     not gimplified + is a decl.  */
+				  tree tmp = OMP_CLAUSE_SIZE (node);
+				  tree var = gfc_create_var (TREE_TYPE (tmp),
+							     NULL);
+				  gfc_add_modify_loc (input_location, block,
+						      var, tmp);
+				  OMP_CLAUSE_SIZE (node) = var;
+				  gfc_allocate_lang_decl (var);
+				  GFC_DECL_SAVED_DESCRIPTOR (var) = inner;
+				}
+
+			      gfc_omp_namelist *n2
+				= clauses->lists[OMP_LIST_MAP];
+
+			      /* If we don't have a mapping of a smaller part
+				 of the array -- or we can't prove that we do
+				 statically -- set this flag.  If there is a
+				 mapping of a smaller part of the array after
+				 all, this will turn into a no-op at
+				 runtime.  */
+			      OMP_CLAUSE_MAP_RUNTIME_IMPLICIT_P (node) = 1;
+
+			      bool sym_based;
+			      n2 = get_symbol_rooted_namelist (sym_rooted_nl,
+							       n, n2,
+							       &sym_based);
+
+			      bool drop_mapping = false;
+
+			      for (; n2 != NULL; n2 = n2->next)
+				{
+				  if ((!sym_based && n == n2)
+				      || (sym_based && n == n2->u2.duplicate_of)
+				      || !n2->expr)
+				    continue;
+
+				  if (!gfc_omp_expr_prefix_same (n->expr,
+								 n2->expr))
+				    continue;
+
+				  gfc_ref *ref1 = n->expr->ref;
+				  gfc_ref *ref2 = n2->expr->ref;
+
+				  /* We know ref1 and ref2 overlap.  We're
+				     interested in whether ref2 describes a
+				     smaller part of the array than ref1, which
+				     we already know refers to the full
+				     array.  */
+
+				  while (ref1->next && ref2->next)
+				    {
+				      ref1 = ref1->next;
+				      ref2 = ref2->next;
+				    }
+
+				  if (ref2->next
+				      || (ref2->type == REF_ARRAY
+					  && (ref2->u.ar.type == AR_ELEMENT
+					      || (ref2->u.ar.type
+						  == AR_SECTION))))
+				    {
+				      drop_mapping = true;
+				      break;
+				    }
+				}
+			      if (drop_mapping)
+				continue;
 			    }
-			  if (op == EXEC_OMP_TARGET_EXIT_DATA)
-			    goto finalize_map_clause;
 			  node3 = build_omp_clause (input_location,
 						    OMP_CLAUSE_MAP);
 			  OMP_CLAUSE_SET_MAP_KIND (node3,
@@ -3931,6 +4104,23 @@ gfc_trans_omp_clauses (stmtblock_t *block, gfc_omp_clauses *clauses,
 	}
     }
 
+  /* Free hashmap if we built it.  */
+  if (sym_rooted_nl)
+    {
+      typedef hash_map<gfc_symbol *, gfc_omp_namelist *>::iterator hti;
+      for (hti it = sym_rooted_nl->begin (); it != sym_rooted_nl->end (); ++it)
+	{
+	  gfc_omp_namelist *&nl = (*it).second;
+	  while (nl)
+	    {
+	      gfc_omp_namelist *next = nl->next;
+	      free (nl);
+	      nl = next;
+	    }
+	}
+      delete sym_rooted_nl;
+    }
+
   if (clauses->if_expr)
     {
       tree if_var;
@@ -4754,7 +4944,7 @@ gfc_trans_oacc_executable_directive (gfc_code *code)
 
   gfc_start_block (&block);
   oacc_clauses = gfc_trans_omp_clauses (&block, code->ext.omp_clauses,
-					code->loc, false, true);
+					code->loc, false, true, code->op);
   stmt = build1_loc (input_location, construct_code, void_type_node, 
 		     oacc_clauses);
   gfc_add_expr_to_block (&block, stmt);
diff --git a/gcc/gimplify.cc b/gcc/gimplify.cc
index 14d88b520913..fad4308a0eb4 100644
--- a/gcc/gimplify.cc
+++ b/gcc/gimplify.cc
@@ -8878,6 +8878,25 @@ gimplify_omp_depend (tree *list_p, gimple_seq *pre_p)
   return 1;
 }
 
+/* True if mapping node C maps, or unmaps, a (Fortran) array descriptor.  */
+
+static bool
+omp_map_clause_descriptor_p (tree c)
+{
+  if (OMP_CLAUSE_CODE (c) != OMP_CLAUSE_MAP)
+    return false;
+
+  if (OMP_CLAUSE_MAP_KIND (c) == GOMP_MAP_TO_PSET)
+    return true;
+
+  if ((OMP_CLAUSE_MAP_KIND (c) == GOMP_MAP_RELEASE
+       || OMP_CLAUSE_MAP_KIND (c) == GOMP_MAP_DELETE)
+      && OMP_CLAUSE_RELEASE_DESCRIPTOR (c))
+    return true;
+
+  return false;
+}
+
 /* For a set of mappings describing an array section pointed to by a struct
    (or derived type, etc.) component, create an "alloc" or "release" node to
    insert into a list following a GOMP_MAP_STRUCT node.  For some types of
@@ -8913,9 +8932,7 @@ build_omp_struct_comp_nodes (enum tree_code code, tree grp_start, tree grp_end,
   if (OMP_CLAUSE_CHAIN (grp_start) != grp_end)
     grp_mid = OMP_CLAUSE_CHAIN (grp_start);
 
-  if (grp_mid
-      && OMP_CLAUSE_CODE (grp_mid) == OMP_CLAUSE_MAP
-      && OMP_CLAUSE_MAP_KIND (grp_mid) == GOMP_MAP_TO_PSET)
+  if (grp_mid && omp_map_clause_descriptor_p (grp_mid))
     OMP_CLAUSE_SIZE (c2) = OMP_CLAUSE_SIZE (grp_mid);
   else
     OMP_CLAUSE_SIZE (c2) = TYPE_SIZE_UNIT (ptr_type_node);
@@ -9101,7 +9118,7 @@ omp_get_attachment (omp_mapping_group *grp)
 	return NULL_TREE;
 
       node = OMP_CLAUSE_CHAIN (node);
-      if (node && OMP_CLAUSE_MAP_KIND (node) == GOMP_MAP_TO_PSET)
+      if (node && omp_map_clause_descriptor_p (node))
 	{
 	  gcc_assert (node != grp->grp_end);
 	  node = OMP_CLAUSE_CHAIN (node);
@@ -9196,7 +9213,7 @@ omp_group_last (tree *start_p)
 		     == GOMP_MAP_ATTACH_ZERO_LENGTH_ARRAY_SECTION)
 		 || OMP_CLAUSE_MAP_KIND (nc) == GOMP_MAP_DETACH
 		 || OMP_CLAUSE_MAP_KIND (nc) == GOMP_MAP_ALWAYS_POINTER
-		 || OMP_CLAUSE_MAP_KIND (nc) == GOMP_MAP_TO_PSET))
+		 || omp_map_clause_descriptor_p (nc)))
 	{
 	  tree nc2 = OMP_CLAUSE_CHAIN (nc);
 	  if (OMP_CLAUSE_MAP_KIND (nc) == GOMP_MAP_DETACH)
@@ -9363,33 +9380,32 @@ omp_group_base (omp_mapping_group *grp, unsigned int *chained,
 	return node;
 
       node = OMP_CLAUSE_CHAIN (node);
-      if (node && OMP_CLAUSE_MAP_KIND (node) == GOMP_MAP_TO_PSET)
+      if (!node)
+	internal_error ("unexpected mapping node");
+      if (omp_map_clause_descriptor_p (node))
 	{
 	  if (node == grp->grp_end)
 	    return *grp->grp_start;
 	  node = OMP_CLAUSE_CHAIN (node);
 	}
-      if (node)
-	switch (OMP_CLAUSE_MAP_KIND (node))
-	  {
-	  case GOMP_MAP_POINTER:
-	  case GOMP_MAP_FIRSTPRIVATE_POINTER:
-	  case GOMP_MAP_FIRSTPRIVATE_REFERENCE:
-	  case GOMP_MAP_POINTER_TO_ZERO_LENGTH_ARRAY_SECTION:
-	    *firstprivate = OMP_CLAUSE_DECL (node);
-	    return *grp->grp_start;
+      switch (OMP_CLAUSE_MAP_KIND (node))
+	{
+	case GOMP_MAP_POINTER:
+	case GOMP_MAP_FIRSTPRIVATE_POINTER:
+	case GOMP_MAP_FIRSTPRIVATE_REFERENCE:
+	case GOMP_MAP_POINTER_TO_ZERO_LENGTH_ARRAY_SECTION:
+	  *firstprivate = OMP_CLAUSE_DECL (node);
+	  return *grp->grp_start;
 
-	  case GOMP_MAP_ALWAYS_POINTER:
-	  case GOMP_MAP_ATTACH_DETACH:
-	  case GOMP_MAP_ATTACH_ZERO_LENGTH_ARRAY_SECTION:
-	  case GOMP_MAP_DETACH:
-	    return *grp->grp_start;
+	case GOMP_MAP_ALWAYS_POINTER:
+	case GOMP_MAP_ATTACH_DETACH:
+	case GOMP_MAP_ATTACH_ZERO_LENGTH_ARRAY_SECTION:
+	case GOMP_MAP_DETACH:
+	  return *grp->grp_start;
 
-	  default:
-	    internal_error ("unexpected mapping node");
-	  }
-      else
-	internal_error ("unexpected mapping node");
+	default:
+	  internal_error ("unexpected mapping node");
+	}
       return error_mark_node;
 
     case GOMP_MAP_TO_PSET:
@@ -9737,18 +9753,45 @@ omp_tsort_mapping_groups_1 (omp_mapping_group ***outlist,
 static omp_mapping_group *
 omp_tsort_mapping_groups (vec<omp_mapping_group> *groups,
 			  hash_map<tree_operand_hash_no_se, omp_mapping_group *>
-			    *grpmap)
+			    *grpmap,
+			  bool enter_exit_data)
 {
   omp_mapping_group *grp, *outlist = NULL, **cursor;
   unsigned int i;
+  bool saw_runtime_implicit = false;
 
   cursor = &outlist;
 
   FOR_EACH_VEC_ELT (*groups, i, grp)
     {
       if (grp->mark != PERMANENT)
-	if (!omp_tsort_mapping_groups_1 (&cursor, groups, grpmap, grp))
-	  return NULL;
+	{
+	  if (OMP_CLAUSE_MAP_RUNTIME_IMPLICIT_P (*grp->grp_start))
+	    {
+	      saw_runtime_implicit = true;
+	      continue;
+	    }
+	  if (!omp_tsort_mapping_groups_1 (&cursor, groups, grpmap, grp))
+	    return NULL;
+	}
+    }
+
+  if (!saw_runtime_implicit)
+    return outlist;
+
+  FOR_EACH_VEC_ELT (*groups, i, grp)
+    {
+      if (grp->mark != PERMANENT
+	  && OMP_CLAUSE_MAP_RUNTIME_IMPLICIT_P (*grp->grp_start))
+	{
+	  /* Clear the flag for enter/exit data because it is currently
+	     meaningless for those operations in libgomp.  */
+	  if (enter_exit_data)
+	    OMP_CLAUSE_MAP_RUNTIME_IMPLICIT_P (*grp->grp_start) = 0;
+
+	  if (!omp_tsort_mapping_groups_1 (&cursor, groups, grpmap, grp))
+	    return NULL;
+	}
     }
 
   return outlist;
@@ -10151,6 +10194,11 @@ omp_check_mapping_compatibility (location_t loc,
    mapping.  However, if we have a reference to pointer, make other appropriate
    adjustments to the mapping nodes instead.
 
+   If we have an ATTACH_DETACH node with a Fortran pointer-set (array
+   descriptor) mapping for a derived-type component, and we're also mapping the
+   whole of the derived-type variable on another clause, the pointer-set
+   mapping is removed.
+
    If we have a component access but we're also mapping the whole of the
    containing struct, drop the former access.
 
@@ -10330,6 +10378,17 @@ omp_resolve_clause_dependencies (enum tree_code code,
 		   GOMP_MAP_ATTACH_ZLAS for it.  */
 		if (!base_mapped_to && referenced_ptr_node)
 		  OMP_CLAUSE_SET_MAP_KIND (referenced_ptr_node, zlas_kind);
+
+		omp_mapping_group *struct_group;
+		tree desc;
+		if ((desc = OMP_CLAUSE_CHAIN (*grp->grp_start))
+		    && omp_map_clause_descriptor_p (desc)
+		    && omp_mapped_by_containing_struct (grpmap, decl,
+							&struct_group))
+		  /* If we have a pointer set but we're mapping (or unmapping)
+		     the whole of the containing struct, we can remove the
+		     pointer set mapping.  */
+		  OMP_CLAUSE_CHAIN (*grp->grp_start) = OMP_CLAUSE_CHAIN (desc);
 	      }
 	    else if (TREE_CODE (TREE_TYPE (base_ptr)) == REFERENCE_TYPE
 		     && (TREE_CODE (TREE_TYPE (TREE_TYPE (base_ptr)))
@@ -10777,11 +10836,17 @@ omp_accumulate_sibling_list (enum omp_region_type region_type,
      for the purposes of gathering sibling lists, etc.  */
   /* gcc_assert (base == addr_tokens[base_token]->expr);  */
 
-  bool ptr = (OMP_CLAUSE_MAP_KIND (grp_end) == GOMP_MAP_ALWAYS_POINTER);
   bool attach_detach = ((OMP_CLAUSE_MAP_KIND (grp_end)
 			 == GOMP_MAP_ATTACH_DETACH)
 			|| (OMP_CLAUSE_MAP_KIND (grp_end)
 			    == GOMP_MAP_ATTACH_ZERO_LENGTH_ARRAY_SECTION));
+  bool has_descriptor = false;
+  if (OMP_CLAUSE_CHAIN (*grp_start_p) != grp_end)
+    {
+      tree grp_mid = OMP_CLAUSE_CHAIN (*grp_start_p);
+      if (grp_mid && omp_map_clause_descriptor_p (grp_mid))
+	has_descriptor = true;
+    }
 
   if (!struct_map_to_clause || struct_map_to_clause->get (base) == NULL)
     {
@@ -10804,7 +10869,18 @@ omp_accumulate_sibling_list (enum omp_region_type region_type,
 	 GOMP_MAP_STRUCT into the middle of the old one.  */
       tree *insert_node_pos = reprocessing_struct ? *added_tail : grp_start_p;
 
-      if (ptr || attach_detach)
+      if (has_descriptor)
+	{
+	  tree desc = OMP_CLAUSE_CHAIN (*grp_start_p);
+	  if (code == OMP_TARGET_EXIT_DATA || code == OACC_EXIT_DATA)
+	    OMP_CLAUSE_SET_MAP_KIND (desc, GOMP_MAP_RELEASE);
+	  tree sc = *insert_node_pos;
+	  OMP_CLAUSE_CHAIN (l) = desc;
+	  OMP_CLAUSE_CHAIN (*grp_start_p) = OMP_CLAUSE_CHAIN (desc);
+	  OMP_CLAUSE_CHAIN (desc) = sc;
+	  *insert_node_pos = l;
+	}
+      else if (attach_detach)
 	{
 	  tree extra_node;
 	  tree alloc_node
@@ -11035,7 +11111,7 @@ omp_accumulate_sibling_list (enum omp_region_type region_type,
 	  || OMP_CLAUSE_MAP_KIND (*sc) == GOMP_MAP_ATTACH_DETACH)
 	sc = &OMP_CLAUSE_CHAIN (*sc);
       for (i = 0; i < elems; i++, sc = &OMP_CLAUSE_CHAIN (*sc))
-	if ((ptr || attach_detach) && sc == grp_start_p)
+	if (attach_detach && sc == grp_start_p)
 	  break;
 	else if (TREE_CODE (OMP_CLAUSE_DECL (*sc)) != COMPONENT_REF
 		 && TREE_CODE (OMP_CLAUSE_DECL (*sc)) != INDIRECT_REF
@@ -11091,7 +11167,7 @@ omp_accumulate_sibling_list (enum omp_region_type region_type,
 		|| (known_eq (coffset, offset)
 		    && maybe_lt (cbitpos, bitpos)))
 	      {
-		if (ptr || attach_detach)
+		if (attach_detach)
 		  scp = sc;
 		else
 		  break;
@@ -11107,7 +11183,9 @@ omp_accumulate_sibling_list (enum omp_region_type region_type,
 	     the list manipulation below.  We only need to handle the (pointer
 	     or reference) attach/detach case.  */
 	  tree extra_node, alloc_node;
-	  if (attach_detach)
+	  if (has_descriptor)
+	    gcc_unreachable ();
+	  else if (attach_detach)
 	    alloc_node = build_omp_struct_comp_nodes (code, *grp_start_p,
 						      grp_end, &extra_node);
 	  else
@@ -11140,7 +11218,17 @@ omp_accumulate_sibling_list (enum omp_region_type region_type,
 	  return NULL;
 	}
 
-      if (ptr || attach_detach)
+      if (has_descriptor)
+	{
+	  tree desc = OMP_CLAUSE_CHAIN (*grp_start_p);
+	  if (code == OMP_TARGET_EXIT_DATA
+	      || code == OACC_EXIT_DATA)
+	    OMP_CLAUSE_SET_MAP_KIND (desc, GOMP_MAP_RELEASE);
+	  omp_siblist_move_node_after (desc,
+				       &OMP_CLAUSE_CHAIN (*grp_start_p),
+				       scp ? scp : sc);
+	}
+      else if (attach_detach)
 	{
 	  tree cl = NULL_TREE, extra_node;
 	  tree alloc_node = build_omp_struct_comp_nodes (code, *grp_start_p,
@@ -11285,8 +11373,7 @@ omp_build_struct_sibling_lists (enum tree_code code,
 	     as a struct (the GOMP_MAP_POINTER following will have the form
 	     "var.data", but such mappings are handled specially).  */
 	  tree grpmid = OMP_CLAUSE_CHAIN (*grp_start_p);
-	  if (OMP_CLAUSE_CODE (grpmid) == OMP_CLAUSE_MAP
-	      && OMP_CLAUSE_MAP_KIND (grpmid) == GOMP_MAP_TO_PSET
+	  if (omp_map_clause_descriptor_p (grpmid)
 	      && DECL_P (OMP_CLAUSE_DECL (grpmid)))
 	    continue;
 	}
@@ -11562,6 +11649,8 @@ gimplify_scan_omp_clauses (tree *list_p, gimple_seq *pre_p,
 					  list_p);
 
 	  omp_mapping_group *outlist = NULL;
+	  bool enter_exit = (code == OMP_TARGET_ENTER_DATA
+			     || code == OMP_TARGET_EXIT_DATA);
 
 	  /* Topological sorting may fail if we have duplicate nodes, which
 	     we should have detected and shown an error for already.  Skip
@@ -11576,7 +11665,7 @@ gimplify_scan_omp_clauses (tree *list_p, gimple_seq *pre_p,
 	  groups = omp_gather_mapping_groups (list_p);
 	  grpmap = omp_index_mapping_groups (groups);
 
-	  outlist = omp_tsort_mapping_groups (groups, grpmap);
+	  outlist = omp_tsort_mapping_groups (groups, grpmap, enter_exit);
 	  outlist = omp_segregate_mapping_groups (outlist);
 	  list_p = omp_reorder_mapping_groups (groups, outlist, list_p);
 
diff --git a/gcc/testsuite/gfortran.dg/gomp/map-9.f90 b/gcc/testsuite/gfortran.dg/gomp/map-9.f90
index f930a49d9fff..8c8d4f7c57c4 100644
--- a/gcc/testsuite/gfortran.dg/gomp/map-9.f90
+++ b/gcc/testsuite/gfortran.dg/gomp/map-9.f90
@@ -2,7 +2,7 @@
 
 ! PR fortran/108545
 
-! { dg-final { scan-tree-dump "#pragma omp target enter data map\\(struct:x \\\[len: 1\\\]\\) map\\(always,to:x\.a \\\[len: \[0-9\]+\\\]\\) map\\(to:MEM <integer\\(kind=4\\)\\\[0:\\\]> \\\[\\(integer\\(kind=4\\)\\\[0:\\\] \\*\\)_\[0-9\]+] \\\[len: _\[0-9\]+\\\]\\) map\\(attach:x\.a\.data \\\[bias: 0\\\]\\)" "omplower" } }
+! { dg-final { scan-tree-dump "#pragma omp target enter data map\\(struct:x \\\[len: 1\\\]\\) map\\(to:x\.a \\\[pointer set, len: \[0-9\]+\\\]\\) map\\(to:MEM <integer\\(kind=4\\)\\\[0:\\\]> \\\[\\(integer\\(kind=4\\)\\\[0:\\\] \\*\\)_\[0-9\]+] \\\[len: _\[0-9\]+\\\]\\) map\\(attach:x\.a\.data \\\[bias: 0\\\]\\)" "omplower" } }
 
 program p
    type t
diff --git a/gcc/testsuite/gfortran.dg/gomp/map-subarray-2.f90 b/gcc/testsuite/gfortran.dg/gomp/map-subarray-2.f90
new file mode 100644
index 000000000000..5aaaf53e6971
--- /dev/null
+++ b/gcc/testsuite/gfortran.dg/gomp/map-subarray-2.f90
@@ -0,0 +1,57 @@
+! { dg-do compile }
+! { dg-additional-options "-fdump-tree-gimple" }
+
+type T
+integer, pointer :: arr1(:)
+integer, pointer :: arr2(:)
+integer, pointer :: arr3(:)
+integer, pointer :: arr4(:)
+end type T
+
+type(T) :: tv
+integer, allocatable, target, dimension(:) :: arr
+
+allocate(arr(1:20))
+
+tv%arr1 => arr
+tv%arr2 => arr
+tv%arr3 => arr
+tv%arr4 => arr
+
+!$omp target enter data map(to: tv%arr1)
+
+! { dg-final { scan-tree-dump {(?n)#pragma omp target enter data map\(struct:tv \[len: 1\]\) map\(to:tv\.arr1 \[pointer set, len: [0-9]+\]\) map\(to:MEM <integer\(kind=4\)\[0:\]> \[\(integer\(kind=4\)\[0:\] \*\)_[0-9]+\] \[len: _[0-9]+\]\) map\(attach:tv\.arr1\.data \[bias: 0\]\)} "gimple" } }
+
+!$omp target exit data map(from: tv%arr1)
+
+! { dg-final { scan-tree-dump {(?n)#pragma omp target exit data map\(from:MEM <integer\(kind=4\)\[0:\]> \[\(integer\(kind=4\)\[0:\] \*\)_[0-9]+\] \[len: _[0-9]+\]\) map\(release:tv\.arr1 \[len: [0-9]+\]\) map\(detach:tv\.arr1\.data \[bias: 0\]\)} "gimple" } }
+
+
+!$omp target enter data map(to: tv%arr2) map(to: tv%arr2(1:10))
+
+! { dg-final { scan-tree-dump {(?n)#pragma omp target enter data map\(struct:tv \[len: 1\]\) map\(to:tv\.arr2 \[pointer set, len: [0-9]+\]\) map\(to:MEM <integer\(kind=4\)\[0:\]> \[\(integer\(kind=4\)\[0:\] \*\)_[0-9]+\] \[len: _[0-9]+\]\) map\(attach:tv\.arr2\.data \[bias: [^\]]+\]\)} "gimple" } }
+
+!$omp target exit data map(from: tv%arr2) map(from: tv%arr2(1:10))
+
+! { dg-final { scan-tree-dump {(?n)#pragma omp target exit data map\(release:tv\.arr2 \[len: [0-9]+\]\) map\(from:MEM <integer\(kind=4\)\[0:\]> \[\(integer\(kind=4\)\[0:\] \*\)_[0-9]+\] \[len: _[0-9]+\]\) map\(detach:tv\.arr2\.data \[bias: [^\]]+\]\)} "gimple" } }
+
+
+!$omp target enter data map(to: tv, tv%arr3(1:10))
+
+! { dg-final { scan-tree-dump {(?n)#pragma omp target enter data map\(to:tv \[len: [0-9]+\]\) map\(to:MEM <integer\(kind=4\)\[0:\]> \[\(integer\(kind=4\)\[0:\] \*\)_[0-9]+\] \[len: _[0-9]+\]\) map\(attach:tv\.arr3\.data \[bias: [^\]]+\]\)} "gimple" } }
+
+!$omp target exit data map(from: tv, tv%arr3(1:10))
+
+! { dg-final { scan-tree-dump {(?n)#pragma omp target exit data map\(from:tv \[len: [0-9]+\]\) map\(from:MEM <integer\(kind=4\)\[0:\]> \[\(integer\(kind=4\)\[0:\] \*\)[_[0-9]+\] \[len: _[0-9]+\]\) map\(detach:tv\.arr3\.data \[bias: [^\]]+\]\)} "gimple" } }
+
+
+!$omp target enter data map(to: tv%arr4(1:10))
+
+! { dg-final { scan-tree-dump {(?n)#pragma omp target enter data map\(struct:tv \[len: 1\]\) map\(to:tv\.arr4 \[pointer set, len: [0-9]+\]\) map\(to:MEM <integer\(kind=4\)\[0:\]> \[\(integer\(kind=4\)\[0:\] \*\)_[0-9]+\] \[len: _[0-9]+\]\) map\(attach:tv\.arr4\.data \[bias: [^\]]+\]\)} "gimple" } }
+
+!$omp target exit data map(from: tv%arr4(1:10))
+
+! { dg-final { scan-tree-dump {(?n)#pragma omp target exit data map\(release:tv\.arr4 \[len: [0-9]+\]\) map\(from:MEM <integer\(kind=4\)\[0:\]> \[\(integer\(kind=4\)\[0:\] \*\)_[0-9]+\] \[len: _[0-9]+\]\) map\(detach:tv\.arr4\.data \[bias: [^\]]+\]\)} "gimple" } }
+
+end
+
diff --git a/gcc/testsuite/gfortran.dg/gomp/map-subarray.f90 b/gcc/testsuite/gfortran.dg/gomp/map-subarray.f90
new file mode 100644
index 000000000000..197888a43365
--- /dev/null
+++ b/gcc/testsuite/gfortran.dg/gomp/map-subarray.f90
@@ -0,0 +1,40 @@
+! { dg-do compile }
+! { dg-additional-options "-fdump-tree-gimple" }
+
+type T
+integer, pointer :: arr1(:)
+integer, pointer :: arr2(:)
+end type T
+
+type(T) :: tv
+integer, allocatable, target, dimension(:) :: arr
+
+allocate(arr(1:20))
+
+tv%arr1 => arr
+tv%arr2 => arr
+
+!$omp target map(tv%arr1)
+tv%arr1(1) = tv%arr1(1) + 1
+!$omp end target
+
+! { dg-final { scan-tree-dump {(?n)#pragma omp target.* map\(struct:tv \[len: 1\]\) map\(to:tv\.arr1 \[pointer set, len: [0-9]+\]\) map\(tofrom:MEM <integer\(kind=4\)\[0:\]> \[\(integer\(kind=4\)\[0:\] \*\)_[0-9]+\] \[len: _[0-9]+\]\[implicit\]\) map\(attach:tv\.arr1\.data \[bias: 0\]\)} "gimple" } }
+
+!$omp target map(tv%arr2) map(tv%arr2(1:10))
+tv%arr2(1) = tv%arr2(1) + 1
+!$omp end target
+
+!$omp target map(tv%arr2(1:10))
+tv%arr2(1) = tv%arr2(1) + 1
+!$omp end target
+
+! { dg-final { scan-tree-dump-times {(?n)#pragma omp target.* map\(struct:tv \[len: 1\]\) map\(to:tv\.arr2 \[pointer set, len: [0-9]+\]\) map\(tofrom:MEM <integer\(kind=4\)\[0:\]> \[\(integer\(kind=4\)\[0:\] \*\)_[0-9]+\] \[len: _[0-9]+\]\) map\(attach:tv\.arr2\.data \[bias: [^\]]+\]\)} 2 "gimple" } }
+
+!$omp target map(tv, tv%arr2(1:10))
+tv%arr2(1) = tv%arr2(1) + 1
+!$omp end target
+
+! { dg-final { scan-tree-dump {(?n)#pragma omp target.* map\(tofrom:tv \[len: [0-9]+\]\) map\(tofrom:MEM <integer\(kind=4\)\[0:\]> \[\(integer\(kind=4\)\[0:\] \*\)_[0-9]+\] \[len: _[0-9]+\]\) map\(attach:tv\.arr2\.data \[bias: [^\]]+\]\)} "gimple" } }
+
+end
+
diff --git a/gcc/tree.h b/gcc/tree.h
index c4f7d56bf32e..088e39da532a 100644
--- a/gcc/tree.h
+++ b/gcc/tree.h
@@ -1809,6 +1809,10 @@ class auto_suppress_location_wrappers
    same directive.  */
 #define OMP_CLAUSE_ATTACHMENT_MAPPING_ERASED(NODE) \
   TREE_STATIC (OMP_CLAUSE_SUBCODE_CHECK (NODE, OMP_CLAUSE_MAP))
+/* Nonzero if this is a release/delete node which refers to a (Fortran) array
+   descriptor.  */
+#define OMP_CLAUSE_RELEASE_DESCRIPTOR(NODE) \
+  TREE_NOTHROW (OMP_CLAUSE_SUBCODE_CHECK (NODE, OMP_CLAUSE_MAP))
 
 /* Flag that 'OMP_CLAUSE_DECL (NODE)' is to be made addressable during OMP
    lowering.  */
diff --git a/libgomp/testsuite/libgomp.fortran/map-subarray-2.f90 b/libgomp/testsuite/libgomp.fortran/map-subarray-2.f90
new file mode 100644
index 000000000000..02f08c52a8c3
--- /dev/null
+++ b/libgomp/testsuite/libgomp.fortran/map-subarray-2.f90
@@ -0,0 +1,108 @@
+! { dg-do run }
+
+program myprog
+type u
+  integer, dimension (:), pointer :: tarr1
+  integer, dimension (:), pointer :: tarr2
+  integer, dimension (:), pointer :: tarr3
+end type u
+
+type(u) :: myu1, myu2, myu3
+
+integer, dimension (12), target :: myarray1
+integer, dimension (12), target :: myarray2
+integer, dimension (12), target :: myarray3
+integer, dimension (12), target :: myarray4
+integer, dimension (12), target :: myarray5
+integer, dimension (12), target :: myarray6
+integer, dimension (12), target :: myarray7
+integer, dimension (12), target :: myarray8
+integer, dimension (12), target :: myarray9
+
+myu1%tarr1 => myarray1
+myu1%tarr2 => myarray2
+myu1%tarr3 => myarray3
+myu2%tarr1 => myarray4
+myu2%tarr2 => myarray5
+myu2%tarr3 => myarray6
+myu3%tarr1 => myarray7
+myu3%tarr2 => myarray8
+myu3%tarr3 => myarray9
+
+myu1%tarr1 = 0
+myu1%tarr2 = 0
+myu1%tarr3 = 0
+myu2%tarr1 = 0
+myu2%tarr2 = 0
+myu2%tarr3 = 0
+myu3%tarr1 = 0
+myu3%tarr2 = 0
+myu3%tarr3 = 0
+
+!$omp target map(to:myu1%tarr1) map(tofrom:myu1%tarr1(:)) &
+!$omp&       map(to:myu1%tarr2) map(tofrom:myu1%tarr2(:)) &
+!$omp&       map(to:myu1%tarr3) map(tofrom:myu1%tarr3(:)) &
+!$omp&       map(to:myu2%tarr1) map(tofrom:myu2%tarr1(:)) &
+!$omp&       map(to:myu2%tarr2) map(tofrom:myu2%tarr2(:)) &
+!$omp&       map(to:myu2%tarr3) map(tofrom:myu2%tarr3(:)) &
+!$omp&       map(to:myu3%tarr1) map(tofrom:myu3%tarr1(:)) &
+!$omp&       map(to:myu3%tarr2) map(tofrom:myu3%tarr2(:)) &
+!$omp&       map(to:myu3%tarr3) map(tofrom:myu3%tarr3(:))
+myu1%tarr1(1) = myu1%tarr1(1) + 1
+myu2%tarr1(1) = myu2%tarr1(1) + 1
+myu3%tarr1(1) = myu3%tarr1(1) + 1
+!$omp end target
+
+!$omp target map(to:myu1%tarr1) map(tofrom:myu1%tarr1(1:2)) &
+!$omp&       map(to:myu1%tarr2) map(tofrom:myu1%tarr2(1:2)) &
+!$omp&       map(to:myu1%tarr3) map(tofrom:myu1%tarr3(1:2)) &
+!$omp&       map(to:myu2%tarr1) map(tofrom:myu2%tarr1(1:2)) &
+!$omp&       map(to:myu2%tarr2) map(tofrom:myu2%tarr2(1:2)) &
+!$omp&       map(to:myu2%tarr3) map(tofrom:myu2%tarr3(1:2)) &
+!$omp&       map(to:myu3%tarr1) map(tofrom:myu3%tarr1(1:2)) &
+!$omp&       map(to:myu3%tarr2) map(tofrom:myu3%tarr2(1:2)) &
+!$omp&       map(to:myu3%tarr3) map(tofrom:myu3%tarr3(1:2))
+myu1%tarr2(1) = myu1%tarr2(1) + 1
+myu2%tarr2(1) = myu2%tarr2(1) + 1
+myu3%tarr2(1) = myu3%tarr2(1) + 1
+!$omp end target
+
+!$omp target map(to:myu1%tarr1) map(tofrom:myu1%tarr1(1)) &
+!$omp&       map(to:myu1%tarr2) map(tofrom:myu1%tarr2(1)) &
+!$omp&       map(to:myu1%tarr3) map(tofrom:myu1%tarr3(1)) &
+!$omp&       map(to:myu2%tarr1) map(tofrom:myu2%tarr1(1)) &
+!$omp&       map(to:myu2%tarr2) map(tofrom:myu2%tarr2(1)) &
+!$omp&       map(to:myu2%tarr3) map(tofrom:myu2%tarr3(1)) &
+!$omp&       map(to:myu3%tarr1) map(tofrom:myu3%tarr1(1)) &
+!$omp&       map(to:myu3%tarr2) map(tofrom:myu3%tarr2(1)) &
+!$omp&       map(to:myu3%tarr3) map(tofrom:myu3%tarr3(1))
+myu1%tarr3(1) = myu1%tarr3(1) + 1
+myu2%tarr3(1) = myu2%tarr3(1) + 1
+myu3%tarr3(1) = myu3%tarr3(1) + 1
+!$omp end target
+
+!$omp target map(tofrom:myu1%tarr1) &
+!$omp&       map(tofrom:myu1%tarr2) &
+!$omp&       map(tofrom:myu1%tarr3) &
+!$omp&       map(tofrom:myu2%tarr1) &
+!$omp&       map(tofrom:myu2%tarr2) &
+!$omp&       map(tofrom:myu2%tarr3) &
+!$omp&       map(tofrom:myu3%tarr1) &
+!$omp&       map(tofrom:myu3%tarr2) &
+!$omp&       map(tofrom:myu3%tarr3)
+myu1%tarr2(1) = myu1%tarr2(1) + 1
+myu2%tarr2(1) = myu2%tarr2(1) + 1
+myu3%tarr2(1) = myu3%tarr2(1) + 1
+!$omp end target
+
+if (myu1%tarr1(1).ne.1) stop 1
+if (myu2%tarr1(1).ne.1) stop 2
+if (myu3%tarr1(1).ne.1) stop 3
+if (myu1%tarr2(1).ne.2) stop 4
+if (myu2%tarr2(1).ne.2) stop 5
+if (myu3%tarr2(1).ne.2) stop 6
+if (myu1%tarr3(1).ne.1) stop 7
+if (myu2%tarr3(1).ne.1) stop 8
+if (myu3%tarr3(1).ne.1) stop 9
+
+end program myprog
diff --git a/libgomp/testsuite/libgomp.fortran/map-subarray-3.f90 b/libgomp/testsuite/libgomp.fortran/map-subarray-3.f90
new file mode 100644
index 000000000000..318e77ea44ff
--- /dev/null
+++ b/libgomp/testsuite/libgomp.fortran/map-subarray-3.f90
@@ -0,0 +1,62 @@
+! { dg-do run }
+
+module mymod
+type G
+integer :: x, y
+integer, pointer :: arr(:)
+integer :: z
+end type G
+end module mymod
+
+program myprog
+use mymod
+
+integer, target :: arr1(10)
+integer, target :: arr2(10)
+integer, target :: arr3(10)
+type(G), dimension(3) :: gvar
+
+integer :: i, j
+
+gvar(1)%arr => arr1
+gvar(2)%arr => arr2
+gvar(3)%arr => arr3
+
+gvar(1)%arr = 0
+gvar(2)%arr = 0
+gvar(3)%arr = 0
+
+i = 1
+j = 1
+
+! Here 'gvar(i)' and 'gvar(j)' are the same element, so this should work.
+! This generates a whole-array mapping for gvar(i)%arr, but with the
+! "runtime implicit" bit set so the smaller subarray gvar(j)%arr(1:5) takes
+! precedence.
+
+!$omp target map(gvar(i)%arr, gvar(j)%arr(1:5))
+gvar(i)%arr(1) = gvar(i)%arr(1) + 1
+gvar(j)%arr(1) = gvar(j)%arr(1) + 2
+!$omp end target
+
+!$omp target map(gvar(i)%arr(1:5), gvar(j)%arr)
+gvar(i)%arr(1) = gvar(i)%arr(1) + 3
+gvar(j)%arr(1) = gvar(j)%arr(1) + 4
+!$omp end target
+
+! For these ones, we know the array index is the same, so we can just
+! drop the whole-array mapping.
+
+!$omp target map(gvar(i)%arr, gvar(i)%arr(1:5))
+gvar(i)%arr(1) = gvar(i)%arr(1) + 1
+gvar(i)%arr(1) = gvar(j)%arr(1) + 2
+!$omp end target
+
+!$omp target map(gvar(i)%arr(1:5), gvar(i)%arr)
+gvar(i)%arr(1) = gvar(i)%arr(1) + 3
+gvar(i)%arr(1) = gvar(j)%arr(1) + 4
+!$omp end target
+
+if (gvar(1)%arr(1).ne.20) stop 1
+
+end program myprog
diff --git a/libgomp/testsuite/libgomp.fortran/map-subarray-4.f90 b/libgomp/testsuite/libgomp.fortran/map-subarray-4.f90
new file mode 100644
index 000000000000..5d15808f0da7
--- /dev/null
+++ b/libgomp/testsuite/libgomp.fortran/map-subarray-4.f90
@@ -0,0 +1,35 @@
+! { dg-do run }
+
+type t
+  integer, pointer :: p(:)
+end type t
+
+type(t) :: var(2)
+
+allocate (var(1)%p, source=[1,2,3,5])
+allocate (var(2)%p, source=[2,3,5])
+
+!$omp target map(var(1)%p, var(2)%p)
+var(1)%p(1) = 5
+var(2)%p(2) = 7
+!$omp end target
+
+!$omp target map(var(1)%p(1:3), var(1)%p, var(2)%p)
+var(1)%p(1) = var(1)%p(1) + 1
+var(2)%p(2) = var(2)%p(2) + 1
+!$omp end target
+
+!$omp target map(var(1)%p, var(2)%p, var(2)%p(1:3))
+var(1)%p(1) = var(1)%p(1) + 1
+var(2)%p(2) = var(2)%p(2) + 1
+!$omp end target
+
+!$omp target map(var(1)%p, var(1)%p(1:3), var(2)%p, var(2)%p(2))
+var(1)%p(1) = var(1)%p(1) + 1
+var(2)%p(2) = var(2)%p(2) + 1
+!$omp end target
+
+if (var(1)%p(1).ne.8) stop 1
+if (var(2)%p(2).ne.10) stop 2
+
+end
diff --git a/libgomp/testsuite/libgomp.fortran/map-subarray-6.f90 b/libgomp/testsuite/libgomp.fortran/map-subarray-6.f90
new file mode 100644
index 000000000000..9f0edf70890e
--- /dev/null
+++ b/libgomp/testsuite/libgomp.fortran/map-subarray-6.f90
@@ -0,0 +1,26 @@
+! { dg-do run }
+
+type t
+  integer, pointer :: p(:)
+  integer, pointer :: p2(:)
+end type t
+
+type(t) :: var
+integer, target :: tgt(5), tgt2(1000)
+var%p => tgt
+var%p2 => tgt2
+
+p = 0
+p2 = 0
+
+!$omp target map(tgt, tgt2(4:6), var)
+  var%p(1) = 5
+  var%p2(5) = 7
+!$omp end target
+
+if (var%p(1).ne.5) stop 1
+if (var%p2(5).ne.7) stop 2
+
+end
+
+! { dg-shouldfail "" { offload_device_nonshared_as } }
diff --git a/libgomp/testsuite/libgomp.fortran/map-subarray-7.f90 b/libgomp/testsuite/libgomp.fortran/map-subarray-7.f90
new file mode 100644
index 000000000000..42da72961069
--- /dev/null
+++ b/libgomp/testsuite/libgomp.fortran/map-subarray-7.f90
@@ -0,0 +1,29 @@
+type t
+integer, pointer :: p2(:)
+end type t
+
+integer, target :: A(5)
+integer, pointer :: p(:), p2(:)
+type(t) :: var
+
+allocate(p2(1:20))
+p => A
+var%p2 => p2
+
+A = 0
+p2 = 0
+
+! These arrays "share original storage", so are unsupported.  This will
+! (correctly) fail with a non-shared address space.
+
+!$omp target map(A(3:4), p2(4:8), p, var%p2)
+A(3) = A(3) + 1
+p2(4) = p2(4) + 2
+!$omp end target
+
+if (A(3).ne.1) stop 1
+if (p2(4).ne.2) stop 2
+
+end program
+
+! { dg-shouldfail "" { offload_device_nonshared_as } }
diff --git a/libgomp/testsuite/libgomp.fortran/map-subarray-8.f90 b/libgomp/testsuite/libgomp.fortran/map-subarray-8.f90
new file mode 100644
index 000000000000..a47360e10ec3
--- /dev/null
+++ b/libgomp/testsuite/libgomp.fortran/map-subarray-8.f90
@@ -0,0 +1,47 @@
+! { dg-do run }
+
+type F
+integer, pointer :: mem(:)
+end type F
+
+type(F) :: fv
+integer, allocatable, target :: arr(:)
+
+allocate(arr(1:20))
+
+fv%mem => arr
+fv%mem = 0
+
+!$omp target enter data map(to: fv%mem(1:10))
+!$omp target map(alloc: fv%mem)
+fv%mem(1) = fv%mem(1) + 1
+!$omp end target
+!$omp target exit data map(from: fv%mem(1:10))
+
+if (fv%mem(1).ne.1) stop 1
+
+!$omp target enter data map(to: fv, fv%mem(1:10))
+!$omp target
+fv%mem(1) = fv%mem(1) + 1
+!$omp end target
+!$omp target exit data map(from: fv, fv%mem(1:10))
+
+if (fv%mem(1).ne.2) stop 2
+
+!$omp target enter data map(to: fv%mem, fv%mem(1:10))
+!$omp target
+fv%mem(1) = fv%mem(1) + 1
+!$omp end target
+!$omp target exit data map(from: fv%mem, fv%mem(1:10))
+
+if (fv%mem(1).ne.3) stop 3
+
+!$omp target enter data map(to: fv%mem)
+!$omp target
+fv%mem(1) = fv%mem(1) + 1
+!$omp end target
+!$omp target exit data map(from: fv%mem)
+
+if (fv%mem(1).ne.4) stop 4
+
+end
diff --git a/libgomp/testsuite/libgomp.fortran/map-subarray.f90 b/libgomp/testsuite/libgomp.fortran/map-subarray.f90
new file mode 100644
index 000000000000..85f5af3a2a6c
--- /dev/null
+++ b/libgomp/testsuite/libgomp.fortran/map-subarray.f90
@@ -0,0 +1,33 @@
+! { dg-do run }
+
+program myprog
+type u
+  integer, dimension (:), pointer :: tarr
+end type u
+
+type(u) :: myu
+integer, dimension (12), target :: myarray
+
+myu%tarr => myarray
+
+myu%tarr = 0
+
+!$omp target map(to:myu%tarr) map(tofrom:myu%tarr(:))
+myu%tarr(1) = myu%tarr(1) + 1
+!$omp end target
+
+!$omp target map(to:myu%tarr) map(tofrom:myu%tarr(1:2))
+myu%tarr(1) = myu%tarr(1) + 1
+!$omp end target
+
+!$omp target map(to:myu%tarr) map(tofrom:myu%tarr(1))
+myu%tarr(1) = myu%tarr(1) + 1
+!$omp end target
+
+!$omp target map(tofrom:myu%tarr)
+myu%tarr(1) = myu%tarr(1) + 1
+!$omp end target
+
+if (myu%tarr(1).ne.4) stop 1
+
+end program myprog
diff --git a/libgomp/testsuite/libgomp.fortran/map-subcomponents.f90 b/libgomp/testsuite/libgomp.fortran/map-subcomponents.f90
new file mode 100644
index 000000000000..c7f90131cbae
--- /dev/null
+++ b/libgomp/testsuite/libgomp.fortran/map-subcomponents.f90
@@ -0,0 +1,32 @@
+! { dg-do run }
+
+module mymod
+type F
+integer :: a, b, c
+integer, dimension(10) :: d
+end type F
+
+type G
+integer :: x, y
+type(F), pointer :: myf
+integer :: z
+end type G
+end module mymod
+
+program myprog
+use mymod
+
+type(F), target :: ftmp
+type(G) :: gvar
+
+gvar%myf => ftmp
+
+gvar%myf%d = 0
+
+!$omp target map(to:gvar%myf) map(tofrom: gvar%myf%b, gvar%myf%d)
+gvar%myf%d(1) = gvar%myf%d(1) + 1
+!$omp end target
+
+if (gvar%myf%d(1).ne.1) stop 1
+
+end program myprog
diff --git a/libgomp/testsuite/libgomp.fortran/struct-elem-map-1.f90 b/libgomp/testsuite/libgomp.fortran/struct-elem-map-1.f90
index 7f3d8174f97b..b1d696656c0f 100644
--- a/libgomp/testsuite/libgomp.fortran/struct-elem-map-1.f90
+++ b/libgomp/testsuite/libgomp.fortran/struct-elem-map-1.f90
@@ -36,6 +36,10 @@ program main
   call six ()
   call seven ()
   call eight ()
+  call nine ()
+  call ten ()
+  call eleven ()
+  call twelve ()
 
 contains
   ! Implicitly mapped – but no pointers are mapped
@@ -408,7 +412,180 @@ contains
     !$omp end target
   end subroutine eight
 
-end program main
+  ! This is "subroutine four" but with explicit base-pointer mappings
+  ! (var%f, etc.).
+  subroutine nine()
+    type(t2) :: var
 
-! Fixed by the "Fortran pointers and member mappings" patch
-! { dg-xfail-run-if TODO { offload_device_nonshared_as } }
+    print '(g0)', '==== TESTCASE "nine" ===='
+
+    var = t2(a = 1, &
+             b = 2, c = cmplx(-1.0_8, 2.0_8,kind=8), &
+             d = [(-3*i, i = 1, 10)], &
+             str1 = "abcde", &
+             str2 = ["12345", "67890", "ABCDE", "FGHIJ"], &
+             uni1 = 4_"abcde", &
+             uni2 = [4_"12345", 4_"67890", 4_"ABCDE", 4_"FGHIJ"])
+    allocate (var%f, source=[22, 33, 44, 55])
+    allocate (var%str4, source=["Let's", "Go!!!"])
+    allocate (var%uni4, source=[4_"Let's", 4_"Go!!!"])
+
+!   !$omp target map(tofrom: var%d(4:7), var%f(2:3), var%str2(2:3)) &
+!   !$omp&       map(tofrom: var%str4(2:2), var%uni2(2:3), var%uni4(2:2))
+    !$omp target map(to: var%f) map(tofrom: var%d(4:7), var%f(2:3), &
+    !$omp&       var%str2(2:3), var%uni2(2:3))
+      if (any (var%d(4:7) /= [(-3*i, i = 4, 7)])) stop 4
+      if (any (var%str2(2:3) /= ["67890", "ABCDE"])) stop 6
+
+      if (.not. associated (var%f)) stop 9
+      if (size (var%f) /= 4) stop 10
+      if (any (var%f(2:3) /= [33, 44])) stop 11
+!     if (.not. associated (var%str4)) stop 15
+!     if (len (var%str4) /= 5) stop 16
+!     if (size (var%str4) /= 2) stop 17
+!     if (var%str4(2) /= "Go!!!") stop 18
+
+      if (any (var%uni2(2:3) /= [4_"67890", 4_"ABCDE"])) stop 19
+!     if (.not. associated (var%uni4)) stop 20
+!     if (len (var%uni4) /= 5) stop 21
+!     if (size (var%uni4) /= 2) stop 22
+!     if (var%uni4(2) /= "Go!!!") stop 23
+    !$omp end target
+
+    deallocate(var%f, var%str4)
+  end subroutine nine
+
+  ! This is "subroutine five" but with explicit base-pointer mappings.
+  subroutine ten()
+    type(t2) :: var
+
+    print '(g0)', '==== TESTCASE "ten" ===='
+
+    var = t2(a = 1, &
+             b = 2, c = cmplx(-1.0_8, 2.0_8,kind=8), &
+             d = [(-3*i, i = 1, 10)], &
+             str1 = "abcde", &
+             str2 = ["12345", "67890", "ABCDE", "FGHIJ"], &
+             uni1 = 4_"abcde", &
+             uni2 = [4_"12345", 4_"67890", 4_"ABCDE", 4_"FGHIJ"])
+    allocate (var%f, source=[22, 33, 44, 55])
+    allocate (var%str4, source=["Let's", "Go!!!"])
+
+    !$omp target map(tofrom: var%d(4:7))
+      if (any (var%d(4:7) /= [(-3*i, i = 4, 7)])) stop 4
+    !$omp end target
+    !$omp target map(tofrom: var%str2(2:3))
+      if (any (var%str2(2:3) /= ["67890", "ABCDE"])) stop 6
+    !$omp end target
+
+    !$omp target map(to: var%f) map(tofrom: var%f(2:3))
+     if (.not. associated (var%f)) stop 9
+     if (size (var%f) /= 4) stop 10
+     if (any (var%f(2:3) /= [33, 44])) stop 11
+    !$omp end target
+!  !$omp target map(tofrom: var%str4(2:2))
+!     if (.not. associated (var%str4)) stop 15
+!     if (len (var%str4) /= 5) stop 16
+!     if (size (var%str4) /= 2) stop 17
+!     if (var%str4(2) /= "Go!!!") stop 18
+!   !$omp end target
+!  !$omp target map(tofrom: var%uni4(2:2))
+!     if (.not. associated (var%uni4)) stop 15
+!     if (len (var%uni4) /= 5) stop 16
+!     if (size (var%uni4) /= 2) stop 17
+!     if (var%uni4(2) /= 4_"Go!!!") stop 18
+!  !$omp end target
+
+    deallocate(var%f, var%str4)
+  end subroutine ten
+
+  ! This is "subroutine six" but with explicit base pointer mappings.
+  subroutine eleven()
+    type(t2) :: var
+
+    print '(g0)', '==== TESTCASE "eleven" ===='
+
+    var = t2(a = 1, &
+             b = 2, c = cmplx(-1.0_8, 2.0_8,kind=8), &
+             d = [(-3*i, i = 1, 10)], &
+             str1 = "abcde", &
+             str2 = ["12345", "67890", "ABCDE", "FGHIJ"], &
+             uni1 = 4_"abcde", &
+             uni2 = [4_"12345", 4_"67890", 4_"ABCDE", 4_"FGHIJ"])
+    allocate (var%f, source=[22, 33, 44, 55])
+    allocate (var%str4, source=["Let's", "Go!!!"])
+    allocate (var%uni4, source=[4_"Let's", 4_"Go!!!"])
+
+!   !$omp target map(tofrom: var%d(5), var%f(3), var%str2(3), &
+!   !$omp                    var%str4(2), var%uni2(3), var%uni4(2))
+    !$omp target map(to: var%f) map(tofrom: var%d(5), var%f(3), &
+    !$omp&                                  var%str2(3), var%uni2(3))
+      if (var%d(5) /= -3*5) stop 4
+      if (var%str2(3) /= "ABCDE") stop 6
+      if (var%uni2(3) /= 4_"ABCDE") stop 7
+
+     if (.not. associated (var%f)) stop 9
+     if (size (var%f) /= 4) stop 10
+     if (var%f(3) /= 44) stop 11
+!     if (.not. associated (var%str4)) stop 15
+!     if (len (var%str4) /= 5) stop 16
+!     if (size (var%str4) /= 2) stop 17
+!     if (var%str4(2) /= "Go!!!") stop 18
+!     if (.not. associated (var%uni4)) stop 19
+!     if (len (var%uni4) /= 5) stop 20
+!     if (size (var%uni4) /= 2) stop 21
+!     if (var%uni4(2) /= 4_"Go!!!") stop 22
+    !$omp end target
+
+    deallocate(var%f, var%str4, var%uni4)
+  end subroutine eleven
+
+  ! This is "subroutine seven" but with explicit base-pointer mappings.
+  subroutine twelve()
+    type(t2) :: var
+
+    print '(g0)', '==== TESTCASE "twelve" ===='
+
+    var = t2(a = 1, &
+             b = 2, c = cmplx(-1.0_8, 2.0_8,kind=8), &
+             d = [(-3*i, i = 1, 10)], &
+             str1 = "abcde", &
+             str2 = ["12345", "67890", "ABCDE", "FGHIJ"], &
+             uni1 = 4_"abcde", &
+             uni2 = [4_"12345", 4_"67890", 4_"ABCDE", 4_"FGHIJ"])
+    allocate (var%f, source=[22, 33, 44, 55])
+    allocate (var%str4, source=["Let's", "Go!!!"])
+    allocate (var%uni4, source=[4_"Let's", 4_"Go!!!"])
+
+    !$omp target map(tofrom: var%d(5))
+      if (var%d(5) /= (-3*5)) stop 4
+    !$omp end target
+    !$omp target map(tofrom: var%str2(2:3))
+      if (any (var%str2(2:3) /= ["67890", "ABCDE"])) stop 6
+    !$omp end target
+    !$omp target map(tofrom: var%uni2(2:3))
+      if (any (var%uni2(2:3) /= [4_"67890", 4_"ABCDE"])) stop 7
+    !$omp end target
+
+    !$omp target map(to: var%f) map(tofrom: var%f(2:3))
+     if (.not. associated (var%f)) stop 9
+     if (size (var%f) /= 4) stop 10
+     if (any (var%f(2:3) /= [33, 44])) stop 11
+    !$omp end target
+!   !$omp target map(tofrom: var%str4(2:2))
+!     if (.not. associated (var%str4)) stop 15
+!     if (len (var%str4) /= 5) stop 16
+!     if (size (var%str4) /= 2) stop 17
+!     if (var%str4(2) /= "Go!!!") stop 18
+!   !$omp end target
+!   !$omp target map(tofrom: var%uni4(2:2))
+!     if (.not. associated (var%uni4)) stop 15
+!     if (len (var%uni4) /= 5) stop 16
+!     if (size (var%uni4) /= 2) stop 17
+!     if (var%uni4(2) /= 4_"Go!!!") stop 18
+!   !$omp end target
+
+    deallocate(var%f, var%str4, var%uni4)
+  end subroutine twelve
+
+end program main
-- 
2.41.0


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

* [PATCH v7 4/5] OpenMP/OpenACC: Unordered/non-constant component offset runtime diagnostic
  2023-08-18 22:47 [PATCH v7 0/5] OpenMP/OpenACC: map clause and OMP gimplify rework Julian Brown
                   ` (2 preceding siblings ...)
  2023-08-18 22:47 ` [PATCH v7 3/5] OpenMP: Pointers and member mappings Julian Brown
@ 2023-08-18 22:47 ` Julian Brown
  2023-12-14 14:26   ` Tobias Burnus
  2023-08-18 22:47 ` [PATCH v7 5/5] OpenMP/OpenACC: Reorganise OMP map clause handling in gimplify.cc Julian Brown
  4 siblings, 1 reply; 19+ messages in thread
From: Julian Brown @ 2023-08-18 22:47 UTC (permalink / raw)
  To: gcc-patches; +Cc: fortran, tobias, jakub

This patch adds support for non-constant component offsets in "map"
clauses for OpenMP (and the equivalants for OpenACC), which are not able
to be sorted into order at compile time.  Normally struct accesses in
such clauses are gathered together and sorted into increasing address
order after a "GOMP_MAP_STRUCT" node: if we have variable indices,
that is no longer possible.

This version of the patch scales back the previously-posted version to
merely add a diagnostic for incorrect usage of component accesses with
variably-indexed arrays of structs: the only permitted variant is where
we have multiple indices that are the same, but we could not prove so
at compile time.  Rather than silently producing the wrong result for
cases where the indices are in fact different, we error out (e.g.,
"map(dtarr(i)%arrptr, dtarr(j)%arrptr(4:8))", for different i/j).

For now, multiple *constant* array indices are still supported (see
map-arrayofstruct-1.c).  That could perhaps be addressed with a follow-up
patch, if necessary.

This version of the patch renumbers the GOMP_MAP_STRUCT_UNORD kind to
avoid clashing with the OpenACC "non-contiguous" dynamic array support
(though that is not yet applied to mainline).

2023-08-18  Julian Brown  <julian@codesourcery.com>

gcc/
	* gimplify.cc (extract_base_bit_offset): Add VARIABLE_OFFSET parameter.
	(omp_get_attachment, omp_group_last, omp_group_base,
	omp_directive_maps_explicitly): Add GOMP_MAP_STRUCT_UNORD support.
	(omp_accumulate_sibling_list): Update calls to extract_base_bit_offset.
	Support GOMP_MAP_STRUCT_UNORD.
	(omp_build_struct_sibling_lists, gimplify_scan_omp_clauses,
	gimplify_adjust_omp_clauses, gimplify_omp_target_update): Add
	GOMP_MAP_STRUCT_UNORD support.
	* omp-low.cc (lower_omp_target): Add GOMP_MAP_STRUCT_UNORD support.
	* tree-pretty-print.cc (dump_omp_clause): Likewise.

include/
	* gomp-constants.h (gomp_map_kind): Add GOMP_MAP_STRUCT_UNORD.

libgomp/
	* oacc-mem.c (find_group_last, goacc_enter_data_internal,
	goacc_exit_data_internal, GOACC_enter_exit_data): Add
	GOMP_MAP_STRUCT_UNORD support.
	* target.c (gomp_map_vars_internal): Add GOMP_MAP_STRUCT_UNORD support.
	Detect incorrect use of variable indexing of arrays of structs.
	(GOMP_target_enter_exit_data, gomp_target_task_fn): Add
	GOMP_MAP_STRUCT_UNORD support.
	* testsuite/libgomp.c-c++-common/map-arrayofstruct-1.c: New test.
	* testsuite/libgomp.c-c++-common/map-arrayofstruct-2.c: New test.
	* testsuite/libgomp.c-c++-common/map-arrayofstruct-3.c: New test.
	* testsuite/libgomp.fortran/map-subarray-5.f90: New test.
---
 gcc/gimplify.cc                               | 110 ++++++++++++++----
 gcc/omp-low.cc                                |   1 +
 gcc/tree-pretty-print.cc                      |   3 +
 include/gomp-constants.h                      |   6 +
 libgomp/oacc-mem.c                            |   6 +-
 libgomp/target.c                              |  60 +++++++++-
 .../map-arrayofstruct-1.c                     |  38 ++++++
 .../map-arrayofstruct-2.c                     |  58 +++++++++
 .../map-arrayofstruct-3.c                     |  68 +++++++++++
 .../libgomp.fortran/map-subarray-5.f90        |  54 +++++++++
 10 files changed, 377 insertions(+), 27 deletions(-)
 create mode 100644 libgomp/testsuite/libgomp.c-c++-common/map-arrayofstruct-1.c
 create mode 100644 libgomp/testsuite/libgomp.c-c++-common/map-arrayofstruct-2.c
 create mode 100644 libgomp/testsuite/libgomp.c-c++-common/map-arrayofstruct-3.c
 create mode 100644 libgomp/testsuite/libgomp.fortran/map-subarray-5.f90

diff --git a/gcc/gimplify.cc b/gcc/gimplify.cc
index fad4308a0eb4..e682583054b0 100644
--- a/gcc/gimplify.cc
+++ b/gcc/gimplify.cc
@@ -8965,7 +8965,8 @@ build_omp_struct_comp_nodes (enum tree_code code, tree grp_start, tree grp_end,
 
 static tree
 extract_base_bit_offset (tree base, poly_int64 *bitposp,
-			 poly_offset_int *poffsetp)
+			 poly_offset_int *poffsetp,
+			 bool *variable_offset)
 {
   tree offset;
   poly_int64 bitsize, bitpos;
@@ -8983,10 +8984,13 @@ extract_base_bit_offset (tree base, poly_int64 *bitposp,
   if (offset && poly_int_tree_p (offset))
     {
       poffset = wi::to_poly_offset (offset);
-      offset = NULL_TREE;
+      *variable_offset = false;
     }
   else
-    poffset = 0;
+    {
+      poffset = 0;
+      *variable_offset = (offset != NULL_TREE);
+    }
 
   if (maybe_ne (bitpos, 0))
     poffset += bits_to_bytes_round_down (bitpos);
@@ -9166,6 +9170,7 @@ omp_get_attachment (omp_mapping_group *grp)
       return error_mark_node;
 
     case GOMP_MAP_STRUCT:
+    case GOMP_MAP_STRUCT_UNORD:
     case GOMP_MAP_FORCE_DEVICEPTR:
     case GOMP_MAP_DEVICE_RESIDENT:
     case GOMP_MAP_LINK:
@@ -9271,6 +9276,7 @@ omp_group_last (tree *start_p)
       break;
 
     case GOMP_MAP_STRUCT:
+    case GOMP_MAP_STRUCT_UNORD:
       {
 	unsigned HOST_WIDE_INT num_mappings
 	  = tree_to_uhwi (OMP_CLAUSE_SIZE (c));
@@ -9437,6 +9443,7 @@ omp_group_base (omp_mapping_group *grp, unsigned int *chained,
       return error_mark_node;
 
     case GOMP_MAP_STRUCT:
+    case GOMP_MAP_STRUCT_UNORD:
       {
 	unsigned HOST_WIDE_INT num_mappings
 	  = tree_to_uhwi (OMP_CLAUSE_SIZE (node));
@@ -10079,7 +10086,8 @@ omp_directive_maps_explicitly (hash_map<tree_operand_hash_no_se,
       /* We might be called during omp_build_struct_sibling_lists, when
 	 GOMP_MAP_STRUCT might have been inserted at the start of the group.
 	 Skip over that, and also possibly the node after it.  */
-      if (OMP_CLAUSE_MAP_KIND (grp_first) == GOMP_MAP_STRUCT)
+      if (OMP_CLAUSE_MAP_KIND (grp_first) == GOMP_MAP_STRUCT
+	  || OMP_CLAUSE_MAP_KIND (grp_first) == GOMP_MAP_STRUCT_UNORD)
 	{
 	  grp_first = OMP_CLAUSE_CHAIN (grp_first);
 	  if (OMP_CLAUSE_MAP_KIND (grp_first) == GOMP_MAP_FIRSTPRIVATE_POINTER
@@ -10816,7 +10824,9 @@ omp_accumulate_sibling_list (enum omp_region_type region_type,
 	}
     }
 
-  tree base = extract_base_bit_offset (ocd, &cbitpos, &coffset);
+  bool variable_offset;
+  tree base
+    = extract_base_bit_offset (ocd, &cbitpos, &coffset, &variable_offset);
 
   int base_token;
   for (base_token = addr_tokens.length () - 1; base_token >= 0; base_token--)
@@ -10850,14 +10860,20 @@ omp_accumulate_sibling_list (enum omp_region_type region_type,
 
   if (!struct_map_to_clause || struct_map_to_clause->get (base) == NULL)
     {
-      tree l = build_omp_clause (OMP_CLAUSE_LOCATION (grp_end), OMP_CLAUSE_MAP);
-
-      OMP_CLAUSE_SET_MAP_KIND (l, GOMP_MAP_STRUCT);
-      OMP_CLAUSE_DECL (l) = unshare_expr (base);
-      OMP_CLAUSE_SIZE (l) = size_int (1);
+      enum gomp_map_kind str_kind = GOMP_MAP_STRUCT;
 
       if (struct_map_to_clause == NULL)
 	struct_map_to_clause = new hash_map<tree_operand_hash, tree>;
+
+      if (variable_offset)
+	str_kind = GOMP_MAP_STRUCT_UNORD;
+
+      tree l = build_omp_clause (OMP_CLAUSE_LOCATION (grp_end), OMP_CLAUSE_MAP);
+
+      OMP_CLAUSE_SET_MAP_KIND (l, str_kind);
+      OMP_CLAUSE_DECL (l) = unshare_expr (base);
+      OMP_CLAUSE_SIZE (l) = size_int (1);
+
       struct_map_to_clause->put (base, l);
 
       /* On first iterating through the clause list, we insert the struct node
@@ -11097,6 +11113,11 @@ omp_accumulate_sibling_list (enum omp_region_type region_type,
     {
       tree *osc = struct_map_to_clause->get (base);
       tree *sc = NULL, *scp = NULL;
+      bool unordered = false;
+
+      if (osc && OMP_CLAUSE_MAP_KIND (*osc) == GOMP_MAP_STRUCT_UNORD)
+	unordered = true;
+
       unsigned HOST_WIDE_INT i, elems = tree_to_uhwi (OMP_CLAUSE_SIZE (*osc));
       sc = &OMP_CLAUSE_CHAIN (*osc);
       /* The struct mapping might be immediately followed by a
@@ -11137,12 +11158,20 @@ omp_accumulate_sibling_list (enum omp_region_type region_type,
 			 == REFERENCE_TYPE))
 	      sc_decl = TREE_OPERAND (sc_decl, 0);
 
-	    tree base2 = extract_base_bit_offset (sc_decl, &bitpos, &offset);
+	    bool variable_offset2;
+	    tree base2 = extract_base_bit_offset (sc_decl, &bitpos, &offset,
+						  &variable_offset2);
 	    if (!base2 || !operand_equal_p (base2, base, 0))
 	      break;
 	    if (scp)
 	      continue;
-	    if ((region_type & ORT_ACC) != 0)
+	    if (variable_offset2)
+	      {
+		OMP_CLAUSE_SET_MAP_KIND (*osc, GOMP_MAP_STRUCT_UNORD);
+		unordered = true;
+		break;
+	      }
+	    else if ((region_type & ORT_ACC) != 0)
 	      {
 		/* For OpenACC, allow (ignore) duplicate struct accesses in
 		   the middle of a mapping clause, e.g. "mystruct->foo" in:
@@ -11174,6 +11203,15 @@ omp_accumulate_sibling_list (enum omp_region_type region_type,
 	      }
 	  }
 
+      /* If this is an unordered struct, just insert the new element at the
+	 end of the list.  */
+      if (unordered)
+	{
+	  for (; i < elems; i++)
+	    sc = &OMP_CLAUSE_CHAIN (*sc);
+	  scp = NULL;
+	}
+
       OMP_CLAUSE_SIZE (*osc)
 	= size_binop (PLUS_EXPR, OMP_CLAUSE_SIZE (*osc), size_one_node);
 
@@ -11565,14 +11603,42 @@ omp_build_struct_sibling_lists (enum tree_code code,
 
 	/* This is the first sorted node in the struct sibling list.  Use it
 	   to recalculate the correct bias to use.
-	   (&first_node - attach_decl).  */
-	tree first_node = OMP_CLAUSE_DECL (OMP_CLAUSE_CHAIN (attach));
-	first_node = build_fold_addr_expr (first_node);
-	first_node = fold_convert (ptrdiff_type_node, first_node);
+	   (&first_node - attach_decl).
+	   For GOMP_MAP_STRUCT_UNORD, we need e.g. the
+	   min(min(min(first,second),third),fourth) element, because the
+	   elements aren't in any particular order.  */
+	tree lowest_addr;
+	if (OMP_CLAUSE_MAP_KIND (struct_node) == GOMP_MAP_STRUCT_UNORD)
+	  {
+	    tree first_node = OMP_CLAUSE_CHAIN (attach);
+	    unsigned HOST_WIDE_INT num_mappings
+	      = tree_to_uhwi (OMP_CLAUSE_SIZE (struct_node));
+	    lowest_addr = OMP_CLAUSE_DECL (first_node);
+	    lowest_addr = build_fold_addr_expr (lowest_addr);
+	    lowest_addr = fold_convert (pointer_sized_int_node, lowest_addr);
+	    tree next_node = OMP_CLAUSE_CHAIN (first_node);
+	    while (num_mappings > 1)
+	      {
+		tree tmp = OMP_CLAUSE_DECL (next_node);
+		tmp = build_fold_addr_expr (tmp);
+		tmp = fold_convert (pointer_sized_int_node, tmp);
+		lowest_addr = fold_build2 (MIN_EXPR, pointer_sized_int_node,
+					   lowest_addr, tmp);
+		next_node = OMP_CLAUSE_CHAIN (next_node);
+		num_mappings--;
+	      }
+	    lowest_addr = fold_convert (ptrdiff_type_node, lowest_addr);
+	  }
+	else
+	  {
+	    tree first_node = OMP_CLAUSE_DECL (OMP_CLAUSE_CHAIN (attach));
+	    first_node = build_fold_addr_expr (first_node);
+	    lowest_addr = fold_convert (ptrdiff_type_node, first_node);
+	  }
 	tree attach_decl = OMP_CLAUSE_DECL (attach);
 	attach_decl = fold_convert (ptrdiff_type_node, attach_decl);
 	OMP_CLAUSE_SIZE (attach)
-	  = fold_build2 (MINUS_EXPR, ptrdiff_type_node, first_node,
+	  = fold_build2 (MINUS_EXPR, ptrdiff_type_node, lowest_addr,
 			 attach_decl);
 
 	/* Remove GOMP_MAP_ATTACH node from after struct node.  */
@@ -12129,7 +12195,8 @@ gimplify_scan_omp_clauses (tree *list_p, gimple_seq *pre_p,
 				  GOVD_FIRSTPRIVATE | GOVD_SEEN);
 	    }
 
-	  if (OMP_CLAUSE_MAP_KIND (c) == GOMP_MAP_STRUCT
+	  if ((OMP_CLAUSE_MAP_KIND (c) == GOMP_MAP_STRUCT
+	       || OMP_CLAUSE_MAP_KIND (c) == GOMP_MAP_STRUCT_UNORD)
 	      && (addr_tokens[0]->type == STRUCTURE_BASE
 		  || addr_tokens[0]->type == ARRAY_BASE)
 	      && addr_tokens[0]->u.structure_base_kind == BASE_DECL)
@@ -13718,7 +13785,8 @@ gimplify_adjust_omp_clauses (gimple_seq *pre_p, gimple_seq body, tree *list_p,
 		    }
 		}
 	    }
-	  if (OMP_CLAUSE_MAP_KIND (c) == GOMP_MAP_STRUCT
+	  if ((OMP_CLAUSE_MAP_KIND (c) == GOMP_MAP_STRUCT
+	       || OMP_CLAUSE_MAP_KIND (c) == GOMP_MAP_STRUCT_UNORD)
 	      && (code == OMP_TARGET_EXIT_DATA || code == OACC_EXIT_DATA))
 	    {
 	      remove = true;
@@ -13762,7 +13830,8 @@ gimplify_adjust_omp_clauses (gimple_seq *pre_p, gimple_seq body, tree *list_p,
 		 in target block and none of the mapping has always modifier,
 		 remove all the struct element mappings, which immediately
 		 follow the GOMP_MAP_STRUCT map clause.  */
-	      if (OMP_CLAUSE_MAP_KIND (c) == GOMP_MAP_STRUCT)
+	      if (OMP_CLAUSE_MAP_KIND (c) == GOMP_MAP_STRUCT
+		  || OMP_CLAUSE_MAP_KIND (c) == GOMP_MAP_STRUCT_UNORD)
 		{
 		  HOST_WIDE_INT cnt = tree_to_shwi (OMP_CLAUSE_SIZE (c));
 		  while (cnt--)
@@ -16541,6 +16610,7 @@ gimplify_omp_target_update (tree *expr_p, gimple_seq *pre_p)
 	      have_clause = false;
 	      break;
 	    case GOMP_MAP_STRUCT:
+	    case GOMP_MAP_STRUCT_UNORD:
 	      have_clause = false;
 	      break;
 	    default:
diff --git a/gcc/omp-low.cc b/gcc/omp-low.cc
index 2b2aa7f13146..3e2c984f8815 100644
--- a/gcc/omp-low.cc
+++ b/gcc/omp-low.cc
@@ -12811,6 +12811,7 @@ lower_omp_target (gimple_stmt_iterator *gsi_p, omp_context *ctx)
 	  case GOMP_MAP_FIRSTPRIVATE_POINTER:
 	  case GOMP_MAP_FIRSTPRIVATE_REFERENCE:
 	  case GOMP_MAP_STRUCT:
+	  case GOMP_MAP_STRUCT_UNORD:
 	  case GOMP_MAP_ALWAYS_POINTER:
 	  case GOMP_MAP_ATTACH:
 	  case GOMP_MAP_DETACH:
diff --git a/gcc/tree-pretty-print.cc b/gcc/tree-pretty-print.cc
index 25d191b10fd7..0c1d6722c5ca 100644
--- a/gcc/tree-pretty-print.cc
+++ b/gcc/tree-pretty-print.cc
@@ -967,6 +967,9 @@ dump_omp_clause (pretty_printer *pp, tree clause, int spc, dump_flags_t flags)
 	case GOMP_MAP_STRUCT:
 	  pp_string (pp, "struct");
 	  break;
+	case GOMP_MAP_STRUCT_UNORD:
+	  pp_string (pp, "struct_unord");
+	  break;
 	case GOMP_MAP_ALWAYS_POINTER:
 	  pp_string (pp, "always_pointer");
 	  break;
diff --git a/include/gomp-constants.h b/include/gomp-constants.h
index 8d4e8e813031..20c722665680 100644
--- a/include/gomp-constants.h
+++ b/include/gomp-constants.h
@@ -153,6 +153,12 @@ enum gomp_map_kind
        (address of the last adjacent entry plus its size).  */
     GOMP_MAP_STRUCT =			(GOMP_MAP_FLAG_SPECIAL_2
 					 | GOMP_MAP_FLAG_SPECIAL | 0),
+    /* As above, but followed by an unordered list of adjacent entries.
+       At present, this is used only to diagnose incorrect usage of variable
+       indices into arrays of structs.  */
+    GOMP_MAP_STRUCT_UNORD =		(GOMP_MAP_FLAG_SPECIAL_4
+					 | GOMP_MAP_FLAG_SPECIAL_2
+					 | GOMP_MAP_FLAG_SPECIAL | 0),
     /* On a location of a pointer/reference that is assumed to be already mapped
        earlier, store the translated address of the preceeding mapping.
        No refcount is bumped by this, and the store is done unconditionally.  */
diff --git a/libgomp/oacc-mem.c b/libgomp/oacc-mem.c
index fe6327407693..79d6e32c0b4e 100644
--- a/libgomp/oacc-mem.c
+++ b/libgomp/oacc-mem.c
@@ -1028,6 +1028,7 @@ find_group_last (int pos, size_t mapnum, size_t *sizes, unsigned short *kinds)
       break;
 
     case GOMP_MAP_STRUCT:
+    case GOMP_MAP_STRUCT_UNORD:
       pos += sizes[pos];
       break;
 
@@ -1088,6 +1089,7 @@ goacc_enter_data_internal (struct gomp_device_descr *acc_dev, size_t mapnum,
       switch (kinds[i] & 0xff)
 	{
 	case GOMP_MAP_STRUCT:
+	case GOMP_MAP_STRUCT_UNORD:
 	  {
 	    size = (uintptr_t) hostaddrs[group_last] + sizes[group_last]
 		   - (uintptr_t) hostaddrs[i];
@@ -1334,6 +1336,7 @@ goacc_exit_data_internal (struct gomp_device_descr *acc_dev, size_t mapnum,
 	  break;
 
 	case GOMP_MAP_STRUCT:
+	case GOMP_MAP_STRUCT_UNORD:
 	  /* Skip the 'GOMP_MAP_STRUCT' itself, and use the regular processing
 	     for all its entries.  This special handling exists for GCC 10.1
 	     compatibility; afterwards, we're not generating these no-op
@@ -1472,7 +1475,8 @@ GOACC_enter_exit_data (int flags_m, size_t mapnum, void **hostaddrs,
 
       if (kind == GOMP_MAP_POINTER
 	  || kind == GOMP_MAP_TO_PSET
-	  || kind == GOMP_MAP_STRUCT)
+	  || kind == GOMP_MAP_STRUCT
+	  || kind == GOMP_MAP_STRUCT_UNORD)
 	continue;
 
       if (kind == GOMP_MAP_FORCE_ALLOC
diff --git a/libgomp/target.c b/libgomp/target.c
index b9137e703045..a94fcea154f9 100644
--- a/libgomp/target.c
+++ b/libgomp/target.c
@@ -1083,7 +1083,8 @@ gomp_map_vars_internal (struct gomp_device_descr *devicep,
 	    tgt->list[i].offset = 0;
 	  continue;
 	}
-      else if ((kind & typemask) == GOMP_MAP_STRUCT)
+      else if ((kind & typemask) == GOMP_MAP_STRUCT
+	       || (kind & typemask) == GOMP_MAP_STRUCT_UNORD)
 	{
 	  size_t first = i + 1;
 	  size_t last = i + sizes[i];
@@ -1467,6 +1468,20 @@ gomp_map_vars_internal (struct gomp_device_descr *devicep,
 		    tgt->list[i].offset = OFFSET_INLINED;
 		  }
 		continue;
+	      case GOMP_MAP_STRUCT_UNORD:
+		if (sizes[i] > 1)
+		  {
+		    void *first = hostaddrs[i + 1];
+		    for (size_t j = i + 1; j < i + sizes[i]; j++)
+		      if (hostaddrs[j + 1] != first)
+			{
+			  gomp_mutex_unlock (&devicep->lock);
+			  gomp_fatal ("Mapped array elements must be the "
+				      "same (%p vs %p)", first,
+				      hostaddrs[j + 1]);
+			}
+		  }
+		/* Fallthrough.  */
 	      case GOMP_MAP_STRUCT:
 		first = i + 1;
 		last = i + sizes[i];
@@ -1585,9 +1600,40 @@ gomp_map_vars_internal (struct gomp_device_descr *devicep,
 	      k->host_end = k->host_start + sizeof (void *);
 	    splay_tree_key n = splay_tree_lookup (mem_map, k);
 	    if (n && n->refcount != REFCOUNT_LINK)
-	      gomp_map_vars_existing (devicep, aq, n, k, &tgt->list[i],
-				      kind & typemask, false, implicit, cbufp,
-				      refcount_set);
+	      {
+		if (field_tgt_clear != FIELD_TGT_EMPTY)
+		  {
+		    /* For this condition to be true, there must be a
+		       duplicate struct element mapping.  This can happen with
+		       GOMP_MAP_STRUCT_UNORD mappings, for example.  */
+		    tgt->list[i].key = n;
+		    if (openmp_p)
+		      {
+			assert ((n->refcount & REFCOUNT_STRUCTELEM) != 0);
+			assert (field_tgt_structelem_first != NULL);
+
+			if (i == field_tgt_clear)
+			  {
+			    n->refcount |= REFCOUNT_STRUCTELEM_FLAG_LAST;
+			    field_tgt_structelem_first = NULL;
+			  }
+		      }
+		    if (i == field_tgt_clear)
+		      field_tgt_clear = FIELD_TGT_EMPTY;
+		    gomp_increment_refcount (n, refcount_set);
+		    tgt->list[i].copy_from
+		      = GOMP_MAP_COPY_FROM_P (kind & typemask);
+		    tgt->list[i].always_copy_from
+		      = GOMP_MAP_ALWAYS_FROM_P (kind & typemask);
+		    tgt->list[i].is_attach = false;
+		    tgt->list[i].offset = 0;
+		    tgt->list[i].length = k->host_end - k->host_start;
+		  }
+		else
+		  gomp_map_vars_existing (devicep, aq, n, k, &tgt->list[i],
+					  kind & typemask, false, implicit,
+					  cbufp, refcount_set);
+	      }
 	    else
 	      {
 		k->aux = NULL;
@@ -4154,7 +4200,8 @@ GOMP_target_enter_exit_data (int device, size_t mapnum, void **hostaddrs,
   size_t i, j;
   if ((flags & GOMP_TARGET_FLAG_EXIT_DATA) == 0)
     for (i = 0; i < mapnum; i++)
-      if ((kinds[i] & 0xff) == GOMP_MAP_STRUCT)
+      if ((kinds[i] & 0xff) == GOMP_MAP_STRUCT
+	  || (kinds[i] & 0xff) == GOMP_MAP_STRUCT_UNORD)
 	{
 	  gomp_map_vars (devicep, sizes[i] + 1, &hostaddrs[i], NULL, &sizes[i],
 			 &kinds[i], true, &refcount_set,
@@ -4252,7 +4299,8 @@ gomp_target_task_fn (void *data)
       htab_t refcount_set = htab_create (ttask->mapnum);
       if ((ttask->flags & GOMP_TARGET_FLAG_EXIT_DATA) == 0)
 	for (i = 0; i < ttask->mapnum; i++)
-	  if ((ttask->kinds[i] & 0xff) == GOMP_MAP_STRUCT)
+	  if ((ttask->kinds[i] & 0xff) == GOMP_MAP_STRUCT
+	      || (ttask->kinds[i] & 0xff) == GOMP_MAP_STRUCT_UNORD)
 	    {
 	      gomp_map_vars (devicep, ttask->sizes[i] + 1, &ttask->hostaddrs[i],
 			     NULL, &ttask->sizes[i], &ttask->kinds[i], true,
diff --git a/libgomp/testsuite/libgomp.c-c++-common/map-arrayofstruct-1.c b/libgomp/testsuite/libgomp.c-c++-common/map-arrayofstruct-1.c
new file mode 100644
index 000000000000..b0994c0a7bb4
--- /dev/null
+++ b/libgomp/testsuite/libgomp.c-c++-common/map-arrayofstruct-1.c
@@ -0,0 +1,38 @@
+#include <stdlib.h>
+#include <assert.h>
+
+struct st {
+  int *p;
+};
+
+int main (void)
+{
+  struct st s[2];
+  s[0].p = (int *) calloc (5, sizeof (int));
+  s[1].p = (int *) calloc (5, sizeof (int));
+
+#pragma omp target map(s[0].p, s[1].p, s[0].p[0:2], s[1].p[1:3])
+  {
+    s[0].p[0] = 5;
+    s[1].p[1] = 7;
+  }
+
+#pragma omp target map(s, s[0].p[0:2], s[1].p[1:3])
+  {
+    s[0].p[0]++;
+    s[1].p[1]++;
+  }
+
+#pragma omp target map(s[0:2], s[0].p[0:2], s[1].p[1:3])
+  {
+    s[0].p[0]++;
+    s[1].p[1]++;
+  }
+
+  assert (s[0].p[0] == 7);
+  assert (s[1].p[1] == 9);
+
+  free (s[0].p);
+  free (s[1].p);
+  return 0;
+}
diff --git a/libgomp/testsuite/libgomp.c-c++-common/map-arrayofstruct-2.c b/libgomp/testsuite/libgomp.c-c++-common/map-arrayofstruct-2.c
new file mode 100644
index 000000000000..81f7efc27c98
--- /dev/null
+++ b/libgomp/testsuite/libgomp.c-c++-common/map-arrayofstruct-2.c
@@ -0,0 +1,58 @@
+#include <stdlib.h>
+#include <assert.h>
+
+struct st {
+  int *p;
+};
+
+int main (void)
+{
+  struct st s[10];
+
+  for (int i = 0; i < 10; i++)
+    s[i].p = (int *) calloc (5, sizeof (int));
+
+  for (int i = 0; i < 10; i++)
+    for (int j = 0; j < 10; j++)
+      for (int k = 0; k < 10; k++)
+	{
+	  if (i == j || j == k || i == k)
+	    continue;
+
+#pragma omp target map(s[i].p, s[j].p, s[k].p, s[i].p[0:2], s[j].p[1:3], \
+		       s[k].p[2])
+	  {
+	    s[i].p[0]++;
+	    s[j].p[1]++;
+	    s[k].p[2]++;
+	  }
+
+#pragma omp target map(s, s[i].p[0:2], s[j].p[1:3], s[k].p[2])
+	  {
+	    s[i].p[0]++;
+	    s[j].p[1]++;
+	    s[k].p[2]++;
+	  }
+
+#pragma omp target map(s[0:10], s[i].p[0:2], s[j].p[1:3], s[k].p[2])
+	  {
+	    s[i].p[0]++;
+	    s[j].p[1]++;
+	    s[k].p[2]++;
+	  }
+	}
+
+  for (int i = 0; i < 10; i++)
+    {
+      assert (s[i].p[0] == 216);
+      assert (s[i].p[1] == 216);
+      assert (s[i].p[2] == 216);
+      free (s[i].p);
+    }
+
+  return 0;
+}
+
+/* { dg-output "(\n|\r|\r\n)" } */
+/* { dg-output "libgomp: Mapped array elements must be the same .*(\n|\r|\r\n)+" } */
+/* { dg-shouldfail "" { offload_device_nonshared_as } } */
diff --git a/libgomp/testsuite/libgomp.c-c++-common/map-arrayofstruct-3.c b/libgomp/testsuite/libgomp.c-c++-common/map-arrayofstruct-3.c
new file mode 100644
index 000000000000..639a0d2bc1e3
--- /dev/null
+++ b/libgomp/testsuite/libgomp.c-c++-common/map-arrayofstruct-3.c
@@ -0,0 +1,68 @@
+#include <stdlib.h>
+#include <assert.h>
+
+struct st {
+  int *p;
+};
+
+struct tt {
+  struct st a[10];
+};
+
+struct ut {
+  struct tt *t;
+};
+
+int main (void)
+{
+  struct tt *t = (struct tt *) malloc (sizeof *t);
+  struct ut *u = (struct ut *) malloc (sizeof *u);
+
+  for (int i = 0; i < 10; i++)
+    t->a[i].p = (int *) calloc (5, sizeof (int));
+
+  u->t = t;
+
+  for (int i = 0; i < 10; i++)
+    for (int j = 0; j < 10; j++)
+      for (int k = 0; k < 10; k++)
+	{
+	  if (i == j || j == k || i == k)
+	    continue;
+
+	  /* This one can use "firstprivate" for T...  */
+#pragma omp target map(t->a[i].p, t->a[j].p, t->a[k].p, \
+		       t->a[i].p[0:2], t->a[j].p[1:3], t->a[k].p[2])
+	  {
+	    t->a[i].p[0]++;
+	    t->a[j].p[1]++;
+	    t->a[k].p[2]++;
+	  }
+
+	  /* ...but this one must use attach/detach for T.  */
+#pragma omp target map(u->t, u->t->a[i].p, u->t->a[j].p, u->t->a[k].p, \
+		       u->t->a[i].p[0:2], u->t->a[j].p[1:3], u->t->a[k].p[2])
+	  {
+	    u->t->a[i].p[0]++;
+	    u->t->a[j].p[1]++;
+	    u->t->a[k].p[2]++;
+	  }
+	}
+
+  for (int i = 0; i < 10; i++)
+    {
+      assert (t->a[i].p[0] == 144);
+      assert (t->a[i].p[1] == 144);
+      assert (t->a[i].p[2] == 144);
+      free (t->a[i].p);
+    }
+
+  free (u);
+  free (t);
+
+  return 0;
+}
+
+/* { dg-output "(\n|\r|\r\n)" } */
+/* { dg-output "libgomp: Mapped array elements must be the same .*(\n|\r|\r\n)+" } */
+/* { dg-shouldfail "" { offload_device_nonshared_as } } */
diff --git a/libgomp/testsuite/libgomp.fortran/map-subarray-5.f90 b/libgomp/testsuite/libgomp.fortran/map-subarray-5.f90
new file mode 100644
index 000000000000..e7cdf11e6108
--- /dev/null
+++ b/libgomp/testsuite/libgomp.fortran/map-subarray-5.f90
@@ -0,0 +1,54 @@
+! { dg-do run }
+
+type t
+  integer, pointer :: p(:)
+end type t
+
+type(t) :: var(3)
+integer :: i, j
+
+allocate (var(1)%p, source=[1,2,3,5])
+allocate (var(2)%p, source=[2,3,5])
+allocate (var(3)%p(1:3))
+
+var(3)%p = 0
+
+do i = 1, 3
+  do j = 1, 3
+!$omp target map(var(i)%p, var(j)%p)
+    var(i)%p(1) = 5
+    var(j)%p(2) = 7
+!$omp end target
+
+    if (i.ne.j) then
+!$omp target map(var(i)%p(1:3), var(i)%p, var(j)%p)
+      var(i)%p(1) = var(i)%p(1) + 1
+      var(j)%p(2) = var(j)%p(2) + 1
+!$omp end target
+
+!$omp target map(var(i)%p, var(j)%p, var(j)%p(1:3))
+      var(i)%p(1) = var(i)%p(1) + 1
+      var(j)%p(2) = var(j)%p(2) + 1
+!$omp end target
+
+!$omp target map(var(i)%p, var(i)%p(1:3), var(j)%p, var(j)%p(2))
+      var(i)%p(1) = var(i)%p(1) + 1
+      var(j)%p(2) = var(j)%p(2) + 1
+!$omp end target
+    end if
+
+    if (i.eq.j) then
+      if (var(i)%p(1).ne.5) stop 1
+      if (var(j)%p(2).ne.7) stop 2
+    else
+      if (var(i)%p(1).ne.8) stop 3
+      if (var(j)%p(2).ne.10) stop 4
+    end if
+  end do
+end do
+
+end
+
+! { dg-output "(\n|\r|\r\n)" }
+! { dg-output "libgomp: Mapped array elements must be the same .*(\n|\r|\r\n)+" }
+! { dg-shouldfail "" { offload_device_nonshared_as } }
-- 
2.41.0


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

* [PATCH v7 5/5] OpenMP/OpenACC: Reorganise OMP map clause handling in gimplify.cc
  2023-08-18 22:47 [PATCH v7 0/5] OpenMP/OpenACC: map clause and OMP gimplify rework Julian Brown
                   ` (3 preceding siblings ...)
  2023-08-18 22:47 ` [PATCH v7 4/5] OpenMP/OpenACC: Unordered/non-constant component offset runtime diagnostic Julian Brown
@ 2023-08-18 22:47 ` Julian Brown
       [not found]   ` <20231216132507.5991c79e@squid.athome>
  4 siblings, 1 reply; 19+ messages in thread
From: Julian Brown @ 2023-08-18 22:47 UTC (permalink / raw)
  To: gcc-patches; +Cc: fortran, tobias, jakub

This patch has been separated out from the C++ "declare mapper"
support patch.  It contains just the gimplify.cc rearrangement
work, mostly moving gimplification from gimplify_scan_omp_clauses
to gimplify_adjust_omp_clauses for map clauses.

The motivation for doing this was that we don't know if we need to
instantiate mappers implicitly until the body of an offload region has
been scanned, i.e. in gimplify_adjust_omp_clauses, but we also need the
un-gimplified form of clauses to sort by base-pointer dependencies after
mapper instantiation has taken place.

The patch also reimplements the "present" clause sorting code to avoid
another sorting pass on mapping nodes.

This version of the patch is based on the version posted for og13, and
additionally incorporates a follow-on fix for DECL_VALUE_EXPR handling
in gimplify_adjust_omp_clauses:

"OpenMP/OpenACC: Reorganise OMP map clause handling in gimplify.cc"
https://gcc.gnu.org/pipermail/gcc-patches/2023-June/622223.html

Parts of:
"OpenMP: OpenMP 5.2 semantics for pointers with unmapped target"
https://gcc.gnu.org/pipermail/gcc-patches/2023-June/623351.html

2023-08-18  Julian Brown  <julian@codesourcery.com>

gcc/
	* gimplify.cc (omp_segregate_mapping_groups): Handle "present" groups.
	(gimplify_scan_omp_clauses): Use mapping group functionality to
	iterate through mapping nodes.  Remove most gimplification of
	OMP_CLAUSE_MAP nodes from here, but still populate ctx->variables
	splay tree.
	(gimplify_adjust_omp_clauses): Move most gimplification of
	OMP_CLAUSE_MAP nodes here.

gcc/testsuite/
	* gfortran.dg/gomp/map-12.f90: Adjust scan output.
---
 gcc/gimplify.cc                           | 667 +++++++++++++---------
 gcc/testsuite/gfortran.dg/gomp/map-12.f90 |   2 +-
 2 files changed, 386 insertions(+), 283 deletions(-)

diff --git a/gcc/gimplify.cc b/gcc/gimplify.cc
index e682583054b0..1e32ad48b844 100644
--- a/gcc/gimplify.cc
+++ b/gcc/gimplify.cc
@@ -9804,10 +9804,15 @@ omp_tsort_mapping_groups (vec<omp_mapping_group> *groups,
   return outlist;
 }
 
-/* Split INLIST into two parts, moving groups corresponding to
-   ALLOC/RELEASE/DELETE mappings to one list, and other mappings to another.
-   The former list is then appended to the latter.  Each sub-list retains the
-   order of the original list.
+/* Split INLIST into four parts:
+
+     - "present" to/from groups
+     - "present" alloc groups
+     - other to/from groups
+     - other alloc/release/delete groups
+
+   These sub-lists are then concatenated together to form the final list.
+   Each sub-list retains the order of the original list.
    Note that ATTACH nodes are later moved to the end of the list in
    gimplify_adjust_omp_clauses, for target regions.  */
 
@@ -9815,7 +9820,9 @@ static omp_mapping_group *
 omp_segregate_mapping_groups (omp_mapping_group *inlist)
 {
   omp_mapping_group *ard_groups = NULL, *tf_groups = NULL;
+  omp_mapping_group *pa_groups = NULL, *ptf_groups = NULL;
   omp_mapping_group **ard_tail = &ard_groups, **tf_tail = &tf_groups;
+  omp_mapping_group **pa_tail = &pa_groups, **ptf_tail = &ptf_groups;
 
   for (omp_mapping_group *w = inlist; w;)
     {
@@ -9834,6 +9841,20 @@ omp_segregate_mapping_groups (omp_mapping_group *inlist)
 	  ard_tail = &w->next;
 	  break;
 
+	case GOMP_MAP_PRESENT_ALLOC:
+	  *pa_tail = w;
+	  w->next = NULL;
+	  pa_tail = &w->next;
+	  break;
+
+	case GOMP_MAP_PRESENT_FROM:
+	case GOMP_MAP_PRESENT_TO:
+	case GOMP_MAP_PRESENT_TOFROM:
+	  *ptf_tail = w;
+	  w->next = NULL;
+	  ptf_tail = &w->next;
+	  break;
+
 	default:
 	  *tf_tail = w;
 	  w->next = NULL;
@@ -9845,8 +9866,10 @@ omp_segregate_mapping_groups (omp_mapping_group *inlist)
 
   /* Now splice the lists together...  */
   *tf_tail = ard_groups;
+  *pa_tail = tf_groups;
+  *ptf_tail = pa_groups;
 
-  return tf_groups;
+  return ptf_groups;
 }
 
 /* Given a list LIST_P containing groups of mappings given by GROUPS, reorder
@@ -11698,119 +11721,30 @@ gimplify_scan_omp_clauses (tree *list_p, gimple_seq *pre_p,
 	break;
       }
 
-  if (code == OMP_TARGET
-      || code == OMP_TARGET_DATA
-      || code == OMP_TARGET_ENTER_DATA
-      || code == OMP_TARGET_EXIT_DATA)
-    {
-      vec<omp_mapping_group> *groups;
-      groups = omp_gather_mapping_groups (list_p);
-      if (groups)
-	{
-	  hash_map<tree_operand_hash_no_se, omp_mapping_group *> *grpmap;
-	  grpmap = omp_index_mapping_groups (groups);
+  vec<omp_mapping_group> *groups = omp_gather_mapping_groups (list_p);
+  hash_map<tree_operand_hash_no_se, omp_mapping_group *> *grpmap = NULL;
+  unsigned grpnum = 0;
+  tree *grp_start_p = NULL, grp_end = NULL_TREE;
 
-	  omp_resolve_clause_dependencies (code, groups, grpmap);
-	  omp_build_struct_sibling_lists (code, region_type, groups, &grpmap,
-					  list_p);
-
-	  omp_mapping_group *outlist = NULL;
-	  bool enter_exit = (code == OMP_TARGET_ENTER_DATA
-			     || code == OMP_TARGET_EXIT_DATA);
-
-	  /* Topological sorting may fail if we have duplicate nodes, which
-	     we should have detected and shown an error for already.  Skip
-	     sorting in that case.  */
-	  if (seen_error ())
-	    goto failure;
-
-	  delete grpmap;
-	  delete groups;
-
-	  /* Rebuild now we have struct sibling lists.  */
-	  groups = omp_gather_mapping_groups (list_p);
-	  grpmap = omp_index_mapping_groups (groups);
-
-	  outlist = omp_tsort_mapping_groups (groups, grpmap, enter_exit);
-	  outlist = omp_segregate_mapping_groups (outlist);
-	  list_p = omp_reorder_mapping_groups (groups, outlist, list_p);
-
-	failure:
-	  delete grpmap;
-	  delete groups;
-	}
-
-      /* OpenMP map clauses with 'present' need to go in front of those
-	 without.  */
-      tree present_map_head = NULL;
-      tree *present_map_tail_p = &present_map_head;
-      tree *first_map_clause_p = NULL;
-
-      for (tree *c_p = list_p; *c_p; )
-	{
-	  tree c = *c_p;
-	  tree *next_c_p = &OMP_CLAUSE_CHAIN (c);
-
-	  if (OMP_CLAUSE_CODE (c) == OMP_CLAUSE_MAP)
-	    {
-	      if (!first_map_clause_p)
-		first_map_clause_p = c_p;
-	      switch (OMP_CLAUSE_MAP_KIND (c))
-		{
-		case GOMP_MAP_PRESENT_ALLOC:
-		case GOMP_MAP_PRESENT_FROM:
-		case GOMP_MAP_PRESENT_TO:
-		case GOMP_MAP_PRESENT_TOFROM:
-		  next_c_p = c_p;
-		  *c_p = OMP_CLAUSE_CHAIN (c);
-
-		  OMP_CLAUSE_CHAIN (c) = NULL;
-		  *present_map_tail_p = c;
-		  present_map_tail_p = &OMP_CLAUSE_CHAIN (c);
-
-		  break;
-
-		default:
-		  break;
-		}
-	    }
-
-	  c_p = next_c_p;
-	}
-      if (first_map_clause_p && present_map_head)
-	{
-	  tree next = *first_map_clause_p;
-	  *first_map_clause_p = present_map_head;
-	  *present_map_tail_p = next;
-	}
-    }
-  else if (region_type & ORT_ACC)
-    {
-      vec<omp_mapping_group> *groups;
-      groups = omp_gather_mapping_groups (list_p);
-      if (groups)
-	{
-	  hash_map<tree_operand_hash_no_se, omp_mapping_group *> *grpmap;
-	  grpmap = omp_index_mapping_groups (groups);
-
-	  oacc_resolve_clause_dependencies (groups, grpmap);
-	  omp_build_struct_sibling_lists (code, region_type, groups, &grpmap,
-					  list_p);
-
-	  delete groups;
-	  delete grpmap;
-	}
-    }
+  if (groups)
+    grpmap = omp_index_mapping_groups (groups);
 
   while ((c = *list_p) != NULL)
     {
       bool remove = false;
       bool notice_outer = true;
+      bool map_descriptor;
       const char *check_non_private = NULL;
       unsigned int flags;
       tree decl;
       auto_vec<omp_addr_token *, 10> addr_tokens;
 
+      if (grp_end && c == OMP_CLAUSE_CHAIN (grp_end))
+	{
+	  grp_start_p = NULL;
+	  grp_end = NULL_TREE;
+	}
+
       switch (OMP_CLAUSE_CODE (c))
 	{
 	case OMP_CLAUSE_PRIVATE:
@@ -12115,45 +12049,26 @@ gimplify_scan_omp_clauses (tree *list_p, gimple_seq *pre_p,
 	  goto do_add;
 
 	case OMP_CLAUSE_MAP:
+	  if (!grp_start_p)
+	    {
+	      grp_start_p = list_p;
+	      grp_end = (*groups)[grpnum].grp_end;
+	      grpnum++;
+	    }
 	  decl = OMP_CLAUSE_DECL (c);
 
+	  if (error_operand_p (decl))
+	    {
+	      remove = true;
+	      break;
+	    }
+
 	  if (!omp_parse_expr (addr_tokens, decl))
 	    {
 	      remove = true;
 	      break;
 	    }
 
-	  if (error_operand_p (decl))
-	    remove = true;
-	  switch (code)
-	    {
-	    case OMP_TARGET:
-	      break;
-	    case OACC_DATA:
-	      if (TREE_CODE (TREE_TYPE (decl)) != ARRAY_TYPE)
-		break;
-	      goto check_firstprivate;
-	    case OACC_ENTER_DATA:
-	    case OACC_EXIT_DATA:
-	      if (OMP_CLAUSE_MAP_KIND (c) == GOMP_MAP_ATTACH_DETACH
-		  && addr_tokens[0]->type == ARRAY_BASE)
-		remove = true;
-	      /* FALLTHRU */
-	    case OMP_TARGET_DATA:
-	    case OMP_TARGET_ENTER_DATA:
-	    case OMP_TARGET_EXIT_DATA:
-	    case OACC_HOST_DATA:
-	    check_firstprivate:
-	      if (OMP_CLAUSE_MAP_KIND (c) == GOMP_MAP_FIRSTPRIVATE_POINTER
-		  || (OMP_CLAUSE_MAP_KIND (c)
-		      == GOMP_MAP_FIRSTPRIVATE_REFERENCE))
-		/* For target {,enter ,exit }data only the array slice is
-		   mapped, but not the pointer to it.  */
-		remove = true;
-	      break;
-	    default:
-	      break;
-	    }
 	  if (remove)
 	    break;
 	  if (DECL_P (decl) && outer_ctx && (region_type & ORT_ACC))
@@ -12172,41 +12087,61 @@ gimplify_scan_omp_clauses (tree *list_p, gimple_seq *pre_p,
 			      DECL_NAME (decl));
 		}
 	    }
-	  if (OMP_CLAUSE_SIZE (c) == NULL_TREE)
-	    OMP_CLAUSE_SIZE (c) = DECL_P (decl) ? DECL_SIZE_UNIT (decl)
-				  : TYPE_SIZE_UNIT (TREE_TYPE (decl));
-	  if (gimplify_expr (&OMP_CLAUSE_SIZE (c), pre_p,
-			     NULL, is_gimple_val, fb_rvalue) == GS_ERROR)
-	    {
-	      remove = true;
-	      break;
-	    }
-	  else if ((OMP_CLAUSE_MAP_KIND (c) == GOMP_MAP_FIRSTPRIVATE_POINTER
-		    || (OMP_CLAUSE_MAP_KIND (c)
-			== GOMP_MAP_FIRSTPRIVATE_REFERENCE)
-		    || OMP_CLAUSE_MAP_KIND (c) == GOMP_MAP_ATTACH_DETACH)
-		   && TREE_CODE (OMP_CLAUSE_SIZE (c)) != INTEGER_CST)
-	    {
-	      OMP_CLAUSE_SIZE (c)
-		= get_initialized_tmp_var (OMP_CLAUSE_SIZE (c), pre_p, NULL,
-					   false);
-	      if ((region_type & ORT_TARGET) != 0)
-		omp_add_variable (ctx, OMP_CLAUSE_SIZE (c),
-				  GOVD_FIRSTPRIVATE | GOVD_SEEN);
-	    }
 
-	  if ((OMP_CLAUSE_MAP_KIND (c) == GOMP_MAP_STRUCT
-	       || OMP_CLAUSE_MAP_KIND (c) == GOMP_MAP_STRUCT_UNORD)
-	      && (addr_tokens[0]->type == STRUCTURE_BASE
-		  || addr_tokens[0]->type == ARRAY_BASE)
-	      && addr_tokens[0]->u.structure_base_kind == BASE_DECL)
+	  map_descriptor = false;
+
+	  /* This condition checks if we're mapping an array descriptor that
+	     isn't inside a derived type -- these have special handling, and
+	     are not handled as structs in omp_build_struct_sibling_lists.
+	     See that function for further details.  */
+	  if (*grp_start_p != grp_end
+	      && OMP_CLAUSE_CHAIN (*grp_start_p)
+	      && OMP_CLAUSE_CHAIN (*grp_start_p) != grp_end)
+	    {
+	      tree grp_mid = OMP_CLAUSE_CHAIN (*grp_start_p);
+	      if (omp_map_clause_descriptor_p (grp_mid)
+		  && DECL_P (OMP_CLAUSE_DECL (grp_mid)))
+		map_descriptor = true;
+	    }
+	  else if (OMP_CLAUSE_CODE (grp_end) == OMP_CLAUSE_MAP
+		   && (OMP_CLAUSE_MAP_KIND (grp_end) == GOMP_MAP_RELEASE
+		       || OMP_CLAUSE_MAP_KIND (grp_end) == GOMP_MAP_DELETE)
+		   && OMP_CLAUSE_RELEASE_DESCRIPTOR (grp_end))
+	    map_descriptor = true;
+
+	  /* Adding the decl for a struct access: we haven't created
+	     GOMP_MAP_STRUCT nodes yet, so this statement needs to predict
+	     whether they will be created in gimplify_adjust_omp_clauses.
+	     NOTE: Technically we should probably look through DECL_VALUE_EXPR
+	     here because something that looks like a DECL_P may actually be a
+	     struct access, e.g. variables in a lambda closure
+	     (__closure->__foo) or class members (this->foo). Currently in both
+	     those cases we map the whole of the containing object (directly in
+	     the C++ FE) though, so struct nodes are not created.  */
+	  if (c == grp_end
+	      && addr_tokens[0]->type == STRUCTURE_BASE
+	      && addr_tokens[0]->u.structure_base_kind == BASE_DECL
+	      && !map_descriptor)
 	    {
 	      gcc_assert (addr_tokens[1]->type == ACCESS_METHOD);
 	      /* If we got to this struct via a chain of pointers, maybe we
 		 want to map it implicitly instead.  */
 	      if (omp_access_chain_p (addr_tokens, 1))
 		break;
+	      omp_mapping_group *wholestruct;
+	      if (!(region_type & ORT_ACC)
+		  && omp_mapped_by_containing_struct (grpmap,
+						      OMP_CLAUSE_DECL (c),
+						      &wholestruct))
+		break;
 	      decl = addr_tokens[1]->expr;
+	      if (splay_tree_lookup (ctx->variables, (splay_tree_key) decl))
+		break;
+	      /* Standalone attach or detach clauses for a struct element
+		 should not inhibit implicit mapping of the whole struct.  */
+	      if (OMP_CLAUSE_MAP_KIND (c) == GOMP_MAP_ATTACH
+		  || OMP_CLAUSE_MAP_KIND (c) == GOMP_MAP_DETACH)
+		break;
 	      flags = GOVD_MAP | GOVD_EXPLICIT;
 
 	      gcc_assert (addr_tokens[1]->u.access_kind != ACCESS_DIRECT
@@ -12214,14 +12149,7 @@ gimplify_scan_omp_clauses (tree *list_p, gimple_seq *pre_p,
 	      goto do_add_decl;
 	    }
 
-	  if (TREE_CODE (decl) == TARGET_EXPR)
-	    {
-	      if (gimplify_expr (&OMP_CLAUSE_DECL (c), pre_p, NULL,
-				 is_gimple_lvalue, fb_lvalue)
-		  == GS_ERROR)
-		remove = true;
-	    }
-	  else if (!DECL_P (decl))
+	  if (!DECL_P (decl))
 	    {
 	      tree d = decl, *pd;
 	      if (TREE_CODE (d) == ARRAY_REF)
@@ -12244,56 +12172,20 @@ gimplify_scan_omp_clauses (tree *list_p, gimple_seq *pre_p,
 		  pd = &TREE_OPERAND (decl, 0);
 		  decl = TREE_OPERAND (decl, 0);
 		}
-	      /* An "attach/detach" operation on an update directive should
-		 behave as a GOMP_MAP_ALWAYS_POINTER.  Beware that
-		 unlike attach or detach map kinds, GOMP_MAP_ALWAYS_POINTER
-		 depends on the previous mapping.  */
-	      if (code == OACC_UPDATE
-		  && OMP_CLAUSE_MAP_KIND (c) == GOMP_MAP_ATTACH_DETACH)
-		OMP_CLAUSE_SET_MAP_KIND (c, GOMP_MAP_ALWAYS_POINTER);
 
-	      if (OMP_CLAUSE_MAP_KIND (c) == GOMP_MAP_ATTACH_DETACH)
+	      if (addr_tokens[0]->type == STRUCTURE_BASE
+		  && addr_tokens[0]->u.structure_base_kind == BASE_DECL
+		  && addr_tokens[1]->type == ACCESS_METHOD
+		  && (addr_tokens[1]->u.access_kind == ACCESS_POINTER
+		      || (addr_tokens[1]->u.access_kind
+			  == ACCESS_POINTER_OFFSET))
+		  && GOMP_MAP_ALWAYS_P (OMP_CLAUSE_MAP_KIND (c)))
 		{
-		  if (TREE_CODE (TREE_TYPE (OMP_CLAUSE_DECL (c)))
-		      == ARRAY_TYPE)
-		    remove = true;
-		  else
-		    {
-		      gomp_map_kind k = ((code == OACC_EXIT_DATA
-					  || code == OMP_TARGET_EXIT_DATA)
-					 ? GOMP_MAP_DETACH : GOMP_MAP_ATTACH);
-		      OMP_CLAUSE_SET_MAP_KIND (c, k);
-		    }
-		}
-
-	      tree cref = decl;
-
-	      while (TREE_CODE (cref) == ARRAY_REF)
-		cref = TREE_OPERAND (cref, 0);
-
-	      if (TREE_CODE (cref) == INDIRECT_REF)
-		cref = TREE_OPERAND (cref, 0);
-
-	      if (TREE_CODE (cref) == COMPONENT_REF)
-		{
-		  tree base = cref;
-		  while (base && !DECL_P (base))
-		    {
-		      tree innerbase = omp_get_base_pointer (base);
-		      if (!innerbase)
-			break;
-		      base = innerbase;
-		    }
-		  if (base
-		      && DECL_P (base)
-		      && GOMP_MAP_ALWAYS_P (OMP_CLAUSE_MAP_KIND (c))
-		      && POINTER_TYPE_P (TREE_TYPE (base)))
-		    {
-		      splay_tree_node n
-			= splay_tree_lookup (ctx->variables,
-					     (splay_tree_key) base);
-		      n->value |= GOVD_SEEN;
-		    }
+		  tree base = addr_tokens[1]->expr;
+		  splay_tree_node n
+		    = splay_tree_lookup (ctx->variables,
+					 (splay_tree_key) base);
+		  n->value |= GOVD_SEEN;
 		}
 
 	      if (code == OMP_TARGET && OMP_CLAUSE_MAP_IN_REDUCTION (c))
@@ -12404,56 +12296,12 @@ gimplify_scan_omp_clauses (tree *list_p, gimple_seq *pre_p,
 			}
 		    }
 		}
-	      else if (gimplify_expr (pd, pre_p, NULL, is_gimple_lvalue,
-				      fb_lvalue) == GS_ERROR)
-		{
-		  remove = true;
-		  break;
-		}
 	      break;
 	    }
 	  flags = GOVD_MAP | GOVD_EXPLICIT;
 	  if (OMP_CLAUSE_MAP_KIND (c) == GOMP_MAP_ALWAYS_TO
 	      || OMP_CLAUSE_MAP_KIND (c) == GOMP_MAP_ALWAYS_TOFROM)
 	    flags |= GOVD_MAP_ALWAYS_TO;
-
-	  if ((code == OMP_TARGET
-	       || code == OMP_TARGET_DATA
-	       || code == OMP_TARGET_ENTER_DATA
-	       || code == OMP_TARGET_EXIT_DATA)
-	      && OMP_CLAUSE_MAP_KIND (c) == GOMP_MAP_ATTACH_DETACH)
-	    {
-	      for (struct gimplify_omp_ctx *octx = outer_ctx; octx;
-		   octx = octx->outer_context)
-		{
-		  splay_tree_node n
-		    = splay_tree_lookup (octx->variables,
-					 (splay_tree_key) OMP_CLAUSE_DECL (c));
-		  /* If this is contained in an outer OpenMP region as a
-		     firstprivate value, remove the attach/detach.  */
-		  if (n && (n->value & GOVD_FIRSTPRIVATE))
-		    {
-		      OMP_CLAUSE_SET_MAP_KIND (c, GOMP_MAP_FIRSTPRIVATE_POINTER);
-		      goto do_add;
-		    }
-		}
-
-	      enum gomp_map_kind map_kind = (code == OMP_TARGET_EXIT_DATA
-					     ? GOMP_MAP_DETACH
-					     : GOMP_MAP_ATTACH);
-	      OMP_CLAUSE_SET_MAP_KIND (c, map_kind);
-	    }
-	  else if ((code == OACC_ENTER_DATA
-		    || code == OACC_EXIT_DATA
-		    || code == OACC_PARALLEL)
-		   && OMP_CLAUSE_MAP_KIND (c) == GOMP_MAP_ATTACH_DETACH)
-	    {
-	      enum gomp_map_kind map_kind = (code == OACC_EXIT_DATA
-					     ? GOMP_MAP_DETACH
-					     : GOMP_MAP_ATTACH);
-	      OMP_CLAUSE_SET_MAP_KIND (c, map_kind);
-	    }
-
 	  goto do_add;
 
 	case OMP_CLAUSE_AFFINITY:
@@ -13092,6 +12940,12 @@ gimplify_scan_omp_clauses (tree *list_p, gimple_seq *pre_p,
 	list_p = &OMP_CLAUSE_CHAIN (c);
     }
 
+  if (groups)
+    {
+      delete grpmap;
+      delete groups;
+    }
+
   ctx->clauses = *orig_list_p;
   gimplify_omp_ctxp = ctx;
 }
@@ -13562,15 +13416,75 @@ gimplify_adjust_omp_clauses (gimple_seq *pre_p, gimple_seq body, tree *list_p,
 	  }
     }
 
+  if (code == OMP_TARGET
+      || code == OMP_TARGET_DATA
+      || code == OMP_TARGET_ENTER_DATA
+      || code == OMP_TARGET_EXIT_DATA)
+    {
+      vec<omp_mapping_group> *groups;
+      groups = omp_gather_mapping_groups (list_p);
+      hash_map<tree_operand_hash_no_se, omp_mapping_group *> *grpmap = NULL;
+
+      if (groups)
+	{
+	  grpmap = omp_index_mapping_groups (groups);
+
+	  omp_resolve_clause_dependencies (code, groups, grpmap);
+	  omp_build_struct_sibling_lists (code, ctx->region_type, groups,
+					  &grpmap, list_p);
+
+	  omp_mapping_group *outlist = NULL;
+
+	  delete grpmap;
+	  delete groups;
+
+	  /* Rebuild now we have struct sibling lists.  */
+	  groups = omp_gather_mapping_groups (list_p);
+	  grpmap = omp_index_mapping_groups (groups);
+
+	  bool enter_exit = (code == OMP_TARGET_ENTER_DATA
+			     || code == OMP_TARGET_EXIT_DATA);
+
+	  outlist = omp_tsort_mapping_groups (groups, grpmap, enter_exit);
+	  outlist = omp_segregate_mapping_groups (outlist);
+	  list_p = omp_reorder_mapping_groups (groups, outlist, list_p);
+
+	  delete grpmap;
+	  delete groups;
+	}
+    }
+  else if (ctx->region_type & ORT_ACC)
+    {
+      vec<omp_mapping_group> *groups;
+      groups = omp_gather_mapping_groups (list_p);
+      if (groups)
+	{
+	  hash_map<tree_operand_hash_no_se, omp_mapping_group *> *grpmap;
+	  grpmap = omp_index_mapping_groups (groups);
+
+	  oacc_resolve_clause_dependencies (groups, grpmap);
+	  omp_build_struct_sibling_lists (code, ctx->region_type, groups,
+					  &grpmap, list_p);
+
+	  delete groups;
+	  delete grpmap;
+	}
+    }
+
   tree attach_list = NULL_TREE;
   tree *attach_tail = &attach_list;
 
+  tree *grp_start_p = NULL, grp_end = NULL_TREE;
+
   while ((c = *list_p) != NULL)
     {
       splay_tree_node n;
       bool remove = false;
       bool move_attach = false;
 
+      if (grp_end && c == OMP_CLAUSE_CHAIN (grp_end))
+	grp_end = NULL_TREE;
+
       switch (OMP_CLAUSE_CODE (c))
 	{
 	case OMP_CLAUSE_FIRSTPRIVATE:
@@ -13725,6 +13639,12 @@ gimplify_adjust_omp_clauses (gimple_seq *pre_p, gimple_seq body, tree *list_p,
 	  break;
 
 	case OMP_CLAUSE_MAP:
+	  decl = OMP_CLAUSE_DECL (c);
+	  if (!grp_end)
+	    {
+	      grp_start_p = list_p;
+	      grp_end = *omp_group_last (grp_start_p);
+	    }
 	  switch (OMP_CLAUSE_MAP_KIND (c))
 	    {
 	    case GOMP_MAP_PRESENT_ALLOC:
@@ -13736,26 +13656,62 @@ gimplify_adjust_omp_clauses (gimple_seq *pre_p, gimple_seq body, tree *list_p,
 	    default:
 	      break;
 	    }
-	  if (code == OMP_TARGET_EXIT_DATA
-	      && OMP_CLAUSE_MAP_KIND (c) == GOMP_MAP_ALWAYS_POINTER)
+	  switch (code)
 	    {
+	    case OMP_TARGET:
+	      break;
+	    case OACC_DATA:
+	      if (TREE_CODE (TREE_TYPE (decl)) != ARRAY_TYPE)
+		break;
+	      goto check_firstprivate;
+	    case OACC_ENTER_DATA:
+	    case OACC_EXIT_DATA:
+	    case OMP_TARGET_DATA:
+	    case OMP_TARGET_ENTER_DATA:
+	    case OMP_TARGET_EXIT_DATA:
+	    case OACC_HOST_DATA:
+	    check_firstprivate:
+	      if (OMP_CLAUSE_MAP_KIND (c) == GOMP_MAP_FIRSTPRIVATE_POINTER
+		  || (OMP_CLAUSE_MAP_KIND (c)
+		      == GOMP_MAP_FIRSTPRIVATE_REFERENCE))
+		/* For target {,enter ,exit }data only the array slice is
+		   mapped, but not the pointer to it.  */
+		remove = true;
+	      if (code == OMP_TARGET_EXIT_DATA
+		  && (OMP_CLAUSE_MAP_KIND (c) == GOMP_MAP_ALWAYS_POINTER
+		      || OMP_CLAUSE_MAP_KIND (c) == GOMP_MAP_POINTER))
+		remove = true;
+	      break;
+	    default:
+	      break;
+	    }
+	  if (remove)
+	    break;
+	  if (OMP_CLAUSE_SIZE (c) == NULL_TREE)
+	    OMP_CLAUSE_SIZE (c) = DECL_P (decl) ? DECL_SIZE_UNIT (decl)
+				  : TYPE_SIZE_UNIT (TREE_TYPE (decl));
+	  gimplify_omp_ctxp = ctx->outer_context;
+	  if (gimplify_expr (&OMP_CLAUSE_SIZE (c), pre_p, NULL,
+				  is_gimple_val, fb_rvalue) == GS_ERROR)
+	    {
+	      gimplify_omp_ctxp = ctx;
 	      remove = true;
 	      break;
 	    }
-	  /* If we have a target region, we can push all the attaches to the
-	     end of the list (we may have standalone "attach" operations
-	     synthesized for GOMP_MAP_STRUCT nodes that must be processed after
-	     the attachment point AND the pointed-to block have been mapped).
-	     If we have something else, e.g. "enter data", we need to keep
-	     "attach" nodes together with the previous node they attach to so
-	     that separate "exit data" operations work properly (see
-	     libgomp/target.c).  */
-	  if ((ctx->region_type & ORT_TARGET) != 0
-	      && (OMP_CLAUSE_MAP_KIND (c) == GOMP_MAP_ATTACH
-		  || (OMP_CLAUSE_MAP_KIND (c)
-		      == GOMP_MAP_ATTACH_ZERO_LENGTH_ARRAY_SECTION)))
-	    move_attach = true;
-	  decl = OMP_CLAUSE_DECL (c);
+	  else if ((OMP_CLAUSE_MAP_KIND (c) == GOMP_MAP_FIRSTPRIVATE_POINTER
+		    || (OMP_CLAUSE_MAP_KIND (c)
+			== GOMP_MAP_FIRSTPRIVATE_REFERENCE)
+		    || OMP_CLAUSE_MAP_KIND (c) == GOMP_MAP_ATTACH_DETACH)
+		   && TREE_CODE (OMP_CLAUSE_SIZE (c)) != INTEGER_CST)
+	    {
+	      OMP_CLAUSE_SIZE (c)
+		= get_initialized_tmp_var (OMP_CLAUSE_SIZE (c), pre_p, NULL,
+					   false);
+	      if ((ctx->region_type & ORT_TARGET) != 0)
+		omp_add_variable (ctx, OMP_CLAUSE_SIZE (c),
+				  GOVD_FIRSTPRIVATE | GOVD_SEEN);
+	    }
+	  gimplify_omp_ctxp = ctx;
 	  /* Data clauses associated with reductions must be
 	     compatible with present_or_copy.  Warn and adjust the clause
 	     if that is not the case.  */
@@ -13792,7 +13748,25 @@ gimplify_adjust_omp_clauses (gimple_seq *pre_p, gimple_seq body, tree *list_p,
 	      remove = true;
 	      break;
 	    }
-	  if (!DECL_P (decl))
+	  /* If we have a DECL_VALUE_EXPR (e.g. this is a class member and/or
+	     a variable captured in a lambda closure), look through that now
+	     before the DECL_P check below.  (A code other than COMPONENT_REF,
+	     i.e. INDIRECT_REF, will be a VLA/variable-length array
+	     section.  A global var may be a variable in a common block.  We
+	     don't want to do this here for either of those.)  */
+	  if ((ctx->region_type & ORT_ACC) == 0
+	      && DECL_P (decl)
+	      && !is_global_var (decl)
+	      && DECL_HAS_VALUE_EXPR_P (decl)
+	      && TREE_CODE (DECL_VALUE_EXPR (decl)) == COMPONENT_REF)
+	    decl = OMP_CLAUSE_DECL (c) = DECL_VALUE_EXPR (decl);
+	  if (TREE_CODE (decl) == TARGET_EXPR)
+	    {
+	      if (gimplify_expr (&OMP_CLAUSE_DECL (c), pre_p, NULL,
+				 is_gimple_lvalue, fb_lvalue) == GS_ERROR)
+		remove = true;
+	    }
+	  else if (!DECL_P (decl))
 	    {
 	      if ((ctx->region_type & ORT_TARGET) != 0
 		  && OMP_CLAUSE_MAP_KIND (c) == GOMP_MAP_FIRSTPRIVATE_POINTER)
@@ -13815,8 +13789,122 @@ gimplify_adjust_omp_clauses (gimple_seq *pre_p, gimple_seq body, tree *list_p,
 			}
 		    }
 		}
+
+	      tree d = decl, *pd;
+	      if (TREE_CODE (d) == ARRAY_REF)
+		{
+		  while (TREE_CODE (d) == ARRAY_REF)
+		    d = TREE_OPERAND (d, 0);
+		  if (TREE_CODE (d) == COMPONENT_REF
+		      && TREE_CODE (TREE_TYPE (d)) == ARRAY_TYPE)
+		    decl = d;
+		}
+	      pd = &OMP_CLAUSE_DECL (c);
+	      if (d == decl
+		  && TREE_CODE (decl) == INDIRECT_REF
+		  && TREE_CODE (TREE_OPERAND (decl, 0)) == COMPONENT_REF
+		  && (TREE_CODE (TREE_TYPE (TREE_OPERAND (decl, 0)))
+		      == REFERENCE_TYPE)
+		  && (OMP_CLAUSE_MAP_KIND (c)
+		      != GOMP_MAP_POINTER_TO_ZERO_LENGTH_ARRAY_SECTION))
+		{
+		  pd = &TREE_OPERAND (decl, 0);
+		  decl = TREE_OPERAND (decl, 0);
+		}
+
+	      if (OMP_CLAUSE_MAP_KIND (c) == GOMP_MAP_ATTACH_DETACH)
+		switch (code)
+		  {
+		  case OACC_ENTER_DATA:
+		  case OACC_EXIT_DATA:
+		    if (TREE_CODE (TREE_TYPE (OMP_CLAUSE_DECL (c)))
+			== ARRAY_TYPE)
+		      remove = true;
+		    else if (code == OACC_ENTER_DATA)
+		      goto change_to_attach;
+		    /* Fallthrough.  */
+		  case OMP_TARGET_EXIT_DATA:
+		    OMP_CLAUSE_SET_MAP_KIND (c, GOMP_MAP_DETACH);
+		    break;
+		  case OACC_UPDATE:
+		    /* An "attach/detach" operation on an update directive
+		       should behave as a GOMP_MAP_ALWAYS_POINTER.  Note that
+		       both GOMP_MAP_ATTACH_DETACH and GOMP_MAP_ALWAYS_POINTER
+		       kinds depend on the previous mapping (for non-TARGET
+		       regions).  */
+		    OMP_CLAUSE_SET_MAP_KIND (c, GOMP_MAP_ALWAYS_POINTER);
+		    break;
+		  default:
+		  change_to_attach:
+		    OMP_CLAUSE_SET_MAP_KIND (c, GOMP_MAP_ATTACH);
+		    if ((ctx->region_type & ORT_TARGET) != 0)
+		      move_attach = true;
+		  }
+	      else if ((ctx->region_type & ORT_TARGET) != 0
+		       && (OMP_CLAUSE_MAP_KIND (c) == GOMP_MAP_ATTACH
+			   || (OMP_CLAUSE_MAP_KIND (c)
+			       == GOMP_MAP_ATTACH_ZERO_LENGTH_ARRAY_SECTION)))
+		move_attach = true;
+
+	      /* If we have e.g. map(struct: *var), don't gimplify the
+		 argument since omp-low.cc wants to see the decl itself.  */
+	      if (OMP_CLAUSE_MAP_KIND (c) == GOMP_MAP_STRUCT)
+		break;
+
+	      /* 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))
+		break;
+
+	      gimplify_omp_ctxp = ctx->outer_context;
+	      if (gimplify_expr (pd, pre_p, NULL, is_gimple_lvalue,
+				 fb_lvalue) == GS_ERROR)
+		remove = true;
+	      gimplify_omp_ctxp = ctx;
 	      break;
 	    }
+
+	 if ((code == OMP_TARGET
+	      || code == OMP_TARGET_DATA
+	      || code == OMP_TARGET_ENTER_DATA
+	      || code == OMP_TARGET_EXIT_DATA)
+	     && OMP_CLAUSE_MAP_KIND (c) == GOMP_MAP_ATTACH_DETACH)
+	   {
+	     bool firstprivatize = false;
+
+	     for (struct gimplify_omp_ctx *octx = ctx->outer_context; octx;
+		  octx = octx->outer_context)
+	       {
+		 splay_tree_node n
+		   = splay_tree_lookup (octx->variables,
+					(splay_tree_key) OMP_CLAUSE_DECL (c));
+		 /* If this is contained in an outer OpenMP region as a
+		    firstprivate value, remove the attach/detach.  */
+		 if (n && (n->value & GOVD_FIRSTPRIVATE))
+		   {
+		     firstprivatize = true;
+		     break;
+		   }
+	       }
+
+	     enum gomp_map_kind map_kind;
+	     if (firstprivatize)
+	       map_kind = GOMP_MAP_FIRSTPRIVATE_POINTER;
+	     else if (code == OMP_TARGET_EXIT_DATA)
+	       map_kind = GOMP_MAP_DETACH;
+	     else
+	       map_kind = GOMP_MAP_ATTACH;
+	     OMP_CLAUSE_SET_MAP_KIND (c, map_kind);
+	   }
+	 else if ((ctx->region_type & ORT_ACC) != 0
+		  && OMP_CLAUSE_MAP_KIND (c) == GOMP_MAP_ATTACH_DETACH)
+	   {
+	     enum gomp_map_kind map_kind = (code == OACC_EXIT_DATA
+					    ? GOMP_MAP_DETACH
+					    : GOMP_MAP_ATTACH);
+	     OMP_CLAUSE_SET_MAP_KIND (c, map_kind);
+	   }
+
 	  n = splay_tree_lookup (ctx->variables, (splay_tree_key) decl);
 	  if ((ctx->region_type & ORT_TARGET) != 0
 	      && !(n->value & GOVD_SEEN)
@@ -13891,6 +13979,21 @@ gimplify_adjust_omp_clauses (gimple_seq *pre_p, gimple_seq body, tree *list_p,
 			  || ((n->value & (GOVD_PRIVATE | GOVD_FIRSTPRIVATE))
 			      == 0));
 	    }
+
+	  /* If we have a target region, we can push all the attaches to the
+	     end of the list (we may have standalone "attach" operations
+	     synthesized for GOMP_MAP_STRUCT nodes that must be processed after
+	     the attachment point AND the pointed-to block have been mapped).
+	     If we have something else, e.g. "enter data", we need to keep
+	     "attach" nodes together with the previous node they attach to so
+	     that separate "exit data" operations work properly (see
+	     libgomp/target.c).  */
+	  if ((ctx->region_type & ORT_TARGET) != 0
+	      && (OMP_CLAUSE_MAP_KIND (c) == GOMP_MAP_ATTACH
+		  || (OMP_CLAUSE_MAP_KIND (c)
+		      == GOMP_MAP_ATTACH_ZERO_LENGTH_ARRAY_SECTION)))
+	    move_attach = true;
+
 	  break;
 
 	case OMP_CLAUSE_TO:
diff --git a/gcc/testsuite/gfortran.dg/gomp/map-12.f90 b/gcc/testsuite/gfortran.dg/gomp/map-12.f90
index ac9a0f8aae04..433bf98911c8 100644
--- a/gcc/testsuite/gfortran.dg/gomp/map-12.f90
+++ b/gcc/testsuite/gfortran.dg/gomp/map-12.f90
@@ -60,7 +60,7 @@ end subroutine
 ! { dg-final { scan-tree-dump-times "#pragma omp target data map\\(always,present,tofrom:b1 \\\[len: 4\\\]\\)\[\r\n\]" 2 "omplower" } }
 ! { dg-final { scan-tree-dump-times "#pragma omp target enter data map\\(to:b \\\[len: 4\\\]\\) map\\(to:b1 \\\[len: 4\\\]\\) map\\(alloc:a \\\[len: 4\\\]\\)\[\r\n\]" 2 "omplower" } }
 ! { dg-final { scan-tree-dump-times "#pragma omp target enter data map\\(always,to:b \\\[len: 4\\\]\\) map\\(always,to:b1 \\\[len: 4\\\]\\) map\\(alloc:a \\\[len: 4\\\]\\)\[\r\n\]" 2 "omplower" } }
-! { dg-final { scan-tree-dump-times "#pragma omp target enter data map\\(force_present:a \\\[len: 4\\\]\\) map\\(force_present:b \\\[len: 4\\\]\\) map\\(force_present:b1 \\\[len: 4\\\]\\)\[\r\n\]" 2 "omplower" } }
+! { dg-final { scan-tree-dump-times "#pragma omp target enter data map\\(force_present:b \\\[len: 4\\\]\\) map\\(force_present:b1 \\\[len: 4\\\]\\) map\\(force_present:a \\\[len: 4\\\]\\)\[\r\n\]" 2 "omplower" } }
 ! { dg-final { scan-tree-dump-times "#pragma omp target enter data map\\(force_present:a \\\[len: 4\\\]\\) map\\(always,present,to:b \\\[len: 4\\\]\\) map\\(always,present,to:b1 \\\[len: 4\\\]\\)\[\r\n\]" 2 "omplower" } }
 ! { dg-final { scan-tree-dump-times "#pragma omp target exit data map\\(from:b1 \\\[len: 4\\\]\\) map\\(delete:a \\\[len: 4\\\]\\) map\\(release:b \\\[len: 4\\\]\\)\[\r\n\]" 2 "omplower" } }
 ! { dg-final { scan-tree-dump-times "#pragma omp target exit data map\\(always,from:b1 \\\[len: 4\\\]\\) map\\(delete:a \\\[len: 4\\\]\\) map\\(release:b \\\[len: 4\\\]\\)\[\r\n\]" 2 "omplower" } }
-- 
2.41.0


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

* Re: [PATCH v7 2/5] OpenMP/OpenACC: Rework clause expansion and nested struct handling
  2023-08-18 22:47 ` [PATCH v7 2/5] OpenMP/OpenACC: Rework clause expansion and nested struct handling Julian Brown
@ 2023-11-14 10:21   ` Tobias Burnus
  2023-11-29 11:43     ` Julian Brown
  0 siblings, 1 reply; 19+ messages in thread
From: Tobias Burnus @ 2023-11-14 10:21 UTC (permalink / raw)
  To: gcc-patches, Julian Brown; +Cc: fortran, jakub

Hi Julian,

first round of comments - I think I need a second pass as the patch is
long and complex. The depth of review also was decreasing, hence, I
assume I will spot things in later parts of the compiler.

In any case, I think the patch is a huge leap forward and very useful!

Contrary to previous iterations of the patch series, it looks as if
{1,2}/5 could be sensibly applied before {3,4,5}/5. However, those are
smaller or tiny, it should be way quicker to review them!

* * *

CROSS REFS

This patch is part of the patch set
"[PATCH v7 0/5] OpenMP/OpenACC: map clause and OMP gimplify rework"
https://gcc.gnu.org/pipermail/gcc-patches/2023-August/627895.html

This patch set in turn is required for the follow-up patch set
https://gcc.gnu.org/pipermail/gcc-patches/2023-September/629363.html
'[PATCH 0/8] OpenMP: lvalue parsing and "declare mapper" support'

and it looks as if it would be very useful for some other WIP patches
like map+iterator.

* * *

The patch 1/5 is just an indentation patch to make the following patch
smaller - and it has been approved multiple times.

This patch can be found at
   https://gcc.gnu.org/pipermail/gcc-patches/2023-August/627897.html
in case someone wants to read the full patch or Julian's patch-intro
comments.

* * *

I do note that all 5 patches of this patch set apply cleanly with some
fuzzy line matching, but:

WARNING: at least for this patch (2/5), code gets wrongly applied
in (c-)parser.cc - see below.

On 19.08.23 00:47, Julian Brown wrote:
> This patch reworks clause expansion in the C, C++ and (to a lesser
> extent) Fortran front ends for OpenMP and OpenACC mapping nodes used in
> GPU offloading support.
>
> At present a single clause may be turned into several mapping nodes,
> or have its mapping type changed, in several places scattered through
> the front- and middle-end.  The analysis relating to which particular
> transformations are needed for some given expression has become quite hard
> to follow.  Briefly, we manipulate clause types in the following places:

[...]

[As the patch is very large, I copied the interesting bits out of the patch
and comment on them - I hope that preserves enough context to be useful.]


   tree c_omp_address_inspector::get_address ()


This class member function is unused. It returns 'orig' and that member variable
is used a couple of times in the member functions - but not this function.

I think it can be removed. (I also find the name slightly misleading.)

* * *


+c_omp_address_inspector::maybe_zero_length_array_section (tree clause)
+{
+  switch (OMP_CLAUSE_MAP_KIND (clause))
+    {
+    case GOMP_MAP_ALLOC:
+    case GOMP_MAP_IF_PRESENT:
+    case GOMP_MAP_TO:
+    case GOMP_MAP_FROM:
+    case GOMP_MAP_TOFROM:
+    case GOMP_MAP_ALWAYS_TO:
+    case GOMP_MAP_ALWAYS_FROM:
+    case GOMP_MAP_ALWAYS_TOFROM:
+    case GOMP_MAP_RELEASE:
+    case GOMP_MAP_DELETE:
+    case GOMP_MAP_FORCE_TO:
+    case GOMP_MAP_FORCE_FROM:
+    case GOMP_MAP_FORCE_TOFROM:
+    case GOMP_MAP_FORCE_PRESENT:

Shouldn't this also include:

GOMP_MAP_ALWAYS_PRESENT_TO:
GOMP_MAP_ALWAYS_PRESENT_FROM:
GOMP_MAP_ALWAYS_PRESENT_TOFROM:
GOMP_MAP_PRESENT_ALLOC:
GOMP_MAP_PRESENT_TO:

?

* * *

+omp_expand_access_chain (tree c, tree expr, vec<omp_addr_token *> &addr_tokens,
+                        unsigned *idx)
+{
+  using namespace omp_addr_tokenizer;
+  location_t loc = OMP_CLAUSE_LOCATION (c);
+  unsigned i = *idx;
+  tree c2 = NULL_TREE;
+  gomp_map_kind kind
+    = (OMP_CLAUSE_CODE (c) == OMP_CLAUSE_FROM
+       || (OMP_CLAUSE_CODE (c) == OMP_CLAUSE_MAP
+          && (OMP_CLAUSE_MAP_KIND (c) == GOMP_MAP_FROM
+              || OMP_CLAUSE_MAP_KIND (c) == GOMP_MAP_DELETE
+              || OMP_CLAUSE_MAP_KIND (c) == GOMP_MAP_RELEASE
+              || OMP_CLAUSE_MAP_KIND (c) == GOMP_MAP_ALWAYS_FROM
+              || OMP_CLAUSE_MAP_KIND (c) == GOMP_MAP_FORCE_FROM
+              || OMP_CLAUSE_MAP_KIND (c) == GOMP_MAP_PRESENT_FROM)))
+      ? GOMP_MAP_DETACH : GOMP_MAP_ATTACH;

GOMP_MAP_ALWAYS_PRESENT_FROM  missing?

And I personally find it more readable to have
   if (...
       ...
       ...)
     kind = GOMP_MAP_DETACH;
   else
     kind = GOMP_MAP_ATTACH;

as in the assignment above, the lhs is in this case really far from the rhs.


* * *

I do see in omp_expand_access_chain, which is shared by C and C++, that the following
accesses are handled:

   ACCESS_POINTER,
   ACCESS_POINTER_OFFSET,
   ACCESS_INDEXED_ARRAY,

But not:
   ACCESS_REF_TO_POINTER,
   ACCESS_REF_TO_POINTER_OFFSET,
   ACCESS_INDEXED_REF_TO_ARRAY

Do those need to be handled as well?

SIDENOTE: As Fortran allocatable components, reference-type variables in a C++ class ('class', 'struct')
are automatically be copied when doing 'map(var)' - contrary to pointer members that need to be explicitly
be mapped (either at mapping side or via a mapper). (And contrary to Fortran allocatables, reference-type members
are always "there" - not like with allocatables can change from unallocated to allocated - with all compilations
of polymorphism, array sizes, string lengths, ...).

* * *

+tree
+c_omp_address_inspector::expand_array_base (tree c,
...
+  bool map_p = OMP_CLAUSE_CODE (c) == OMP_CLAUSE_MAP;
+  bool implicit_p = (OMP_CLAUSE_CODE (c) == OMP_CLAUSE_MAP
+                    && OMP_CLAUSE_MAP_IMPLICIT (c));
...
+  if (!openmp
+      && OMP_CLAUSE_CODE (c) == OMP_CLAUSE_MAP

How about using 'map_p' (twice), given that it is already
defined?

...

+       if (target)
+         {
+           c2 = build_omp_clause (loc, OMP_CLAUSE_MAP);
+           if (target
+               && !ref_to_ptr

Remove 'target &&' from the second condition.


* * *

+++ b/gcc/c/c-parser.cc
@@ -17659,7 +17659,8 @@ c_parser_omp_clause_detach (c_parser *parser, tree list)

  static tree
  c_parser_oacc_all_clauses (c_parser *parser, omp_clause_mask mask,
-                          const char *where, bool finish_p = true)
+                          const char *where, bool finish_p = true,
+                          bool target = false)

Here but also in gcc/c-family/c-omp.cc - I wonder whether this should be target_p for
consistency with most other boolean variables. The code is not really consistent,
but given that there is 'finish_p' here and map_p etc. in the other file, it seems
to make sense to use 'target_p.

* * *

If I try the example at
   https://discourse.llvm.org/t/how-do-i-map-an-array-of-pointers-using-array-notation/53640/3
it fails to compile with:

foo.c:11:34: error: ‘#pragma omp target enter data’ with map-type other than ‘to’, ‘tofrom’ or ‘alloc’ on ‘map’ clause
    11 | #pragma omp target enter data map(to: a[i][0:1])


The problem is that fuzzy 'patch' mis-apply the patch for c-parser.cc's

@@ -22106,6 +22111,7 @@ c_parser_omp_target_enter_data (location_t loc, c_parser *parser,
           case GOMP_MAP_FIRSTPRIVATE_POINTER:
           case GOMP_MAP_ALWAYS_POINTER:
           case GOMP_MAP_ATTACH_DETACH:
+         case GOMP_MAP_ATTACH:
             break;
           default:
             map_seen |= 1;
@@ -22215,6 +22221,7 @@ c_parser_omp_target_exit_data (location_t loc, c_parser *parser,
           case GOMP_MAP_FIRSTPRIVATE_POINTER:
           case GOMP_MAP_ALWAYS_POINTER:
           case GOMP_MAP_ATTACH_DETACH:
+         case GOMP_MAP_DETACH:
             break;
           default:
             map_seen |= 1;

Namely after applying it, the result is:


@@ -23329,0 +23335 @@ c_parser_omp_target_data (location_t loc, c_parser *parser, bool *if_p)
+         case GOMP_MAP_ATTACH:
@@ -23494,0 +23501 @@ c_parser_omp_target_enter_data (location_t loc, c_parser *parser,
+         case GOMP_MAP_DETACH:

That is, the new code is added
   to c_parser_omp_target_data       and c_parser_omp_target_enter_data
instead of
   to c_parser_omp_target_enter_data and c_parser_omp_target_exit_data.

* * *

Likewise issue for C++'s cp/parser.cc.

* * *

The first question is whether such code is also needed for 'target' and 'target data'
besides 'target enter/exit data'. It looks as if omp_expand_access_chain should also
apply here.

* * *

And trying the following snippet:

void f(){
   int **a,i,j,n;

   #pragma omp target enter data map(a[i][j:n])
   #pragma omp target exit data map(a[i][j:n])

   #pragma omp target data map(a[i][j:n])
   {
   #pragma omp target map(a[i][j:n])
      ;
   }
}

Results - when putting the enums to enter/exit data
instead of to target data / target enter data:


g++ fails, unless I messed up, with:

fiof.c:4:36: error: ‘#pragma omp target exit data’ with map-type other than ‘from’, ‘tofrom’, ‘release’ or ‘delete’ on ‘map’ clause
     4 |   #pragma omp target exit data map(a[i][j:n])
       |                                    ^
fiof.c:5:31: error: ‘#pragma omp target data’ with map-type other than ‘to’, ‘from’, ‘tofrom’ or ‘alloc’ on ‘map’ clause
     5 |   #pragma omp target data map(a[i][j:n])


And gcc fails with:

fiof.c:3:36: error: ‘a’ appears more than once in map clauses
     3 |   #pragma omp target enter data map(a[i][j:n])
       |                                    ^
fiof.c:4:35: error: ‘a’ appears more than once in map clauses
     4 |   #pragma omp target exit data map(a[i][j:n])
       |                                   ^
fiof.c:5:30: error: ‘a’ appears more than once in map clauses
     5 |   #pragma omp target data map(a[i][j:n])
       |                              ^
fiof.c:7:25: error: ‘a’ appears more than once in map clauses
     7 |   #pragma omp target map(a[i][j:n])

(Plus all '... data' ones: '... must contain at least one ...')


Can you fix this - and add an example in the spirit of the LLVM example above,
except that it should test all four 'target' variants.

Thanks,

Tobias

-----------------
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] 19+ messages in thread

* Re: [PATCH v7 2/5] OpenMP/OpenACC: Rework clause expansion and nested struct handling
  2023-11-14 10:21   ` Tobias Burnus
@ 2023-11-29 11:43     ` Julian Brown
  2023-11-29 16:03       ` Tobias Burnus
  2023-12-14  7:14       ` [committed] testsuite: Fix up target-enter-data-1.c on 32-bit targets Jakub Jelinek
  0 siblings, 2 replies; 19+ messages in thread
From: Julian Brown @ 2023-11-29 11:43 UTC (permalink / raw)
  To: Tobias Burnus; +Cc: gcc-patches, fortran, jakub

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

Hi Tobias!

Here is a patch incorporating your initial review comments
(hopefully!). I've attached both an incremental patch (against a
rebased version of the previous 2/5 patch -- in an attempt to ease
review of those changes) and a combined patch.

This version fixes a few previously-missed cases for manually-managed
ragged array mappings (including cases using references).  Some
arbitrary differences between handling of clause expansion for C vs.
C++ have also been fixed, and some fragments from later in the patch
series have been moved forward (where they were useful for fixing
bugs).  Several new test cases have been added.  Further comments below.

On Tue, 14 Nov 2023 11:21:30 +0100
Tobias Burnus <tobias@codesourcery.com> wrote:

> first round of comments - I think I need a second pass as the patch is
> long and complex. The depth of review also was decreasing, hence, I
> assume I will spot things in later parts of the compiler.
> 
> In any case, I think the patch is a huge leap forward and very useful!

Thank you, I hope so!

> The patch 1/5 is just an indentation patch to make the following patch
> smaller - and it has been approved multiple times.
> 
> This patch can be found at
>    https://gcc.gnu.org/pipermail/gcc-patches/2023-August/627897.html
> in case someone wants to read the full patch or Julian's patch-intro
> comments.
> 
> * * *
> 
> I do note that all 5 patches of this patch set apply cleanly with some
> fuzzy line matching, but:
> 
> WARNING: at least for this patch (2/5), code gets wrongly applied
> in (c-)parser.cc - see below.

(I didn't hit this updating "the other way", i.e. by rebasing my patch
series onto a more-recent trunk, FWIW.)

> On 19.08.23 00:47, Julian Brown wrote:
> > This patch reworks clause expansion in the C, C++ and (to a lesser
> > extent) Fortran front ends for OpenMP and OpenACC mapping nodes
> > used in GPU offloading support.
> >
> > At present a single clause may be turned into several mapping nodes,
> > or have its mapping type changed, in several places scattered
> > through the front- and middle-end.  The analysis relating to which
> > particular transformations are needed for some given expression has
> > become quite hard to follow.  Briefly, we manipulate clause types
> > in the following places:  
> 
> [...]
> 
> [As the patch is very large, I copied the interesting bits out of the
> patch and comment on them - I hope that preserves enough context to
> be useful.]
> 
> 
>    tree c_omp_address_inspector::get_address ()
> 
> 
> This class member function is unused. It returns 'orig' and that
> member variable is used a couple of times in the member functions -
> but not this function.
> 
> I think it can be removed. (I also find the name slightly misleading.)

I've removed it, thanks.

> * * *
> 
> 
> +c_omp_address_inspector::maybe_zero_length_array_section (tree
> clause) +{
> +  switch (OMP_CLAUSE_MAP_KIND (clause))
> +    {
> +    case GOMP_MAP_ALLOC:
> +    case GOMP_MAP_IF_PRESENT:
> +    case GOMP_MAP_TO:
> +    case GOMP_MAP_FROM:
> +    case GOMP_MAP_TOFROM:
> +    case GOMP_MAP_ALWAYS_TO:
> +    case GOMP_MAP_ALWAYS_FROM:
> +    case GOMP_MAP_ALWAYS_TOFROM:
> +    case GOMP_MAP_RELEASE:
> +    case GOMP_MAP_DELETE:
> +    case GOMP_MAP_FORCE_TO:
> +    case GOMP_MAP_FORCE_FROM:
> +    case GOMP_MAP_FORCE_TOFROM:
> +    case GOMP_MAP_FORCE_PRESENT:
> 
> Shouldn't this also include:
> 
> GOMP_MAP_ALWAYS_PRESENT_TO:
> GOMP_MAP_ALWAYS_PRESENT_FROM:
> GOMP_MAP_ALWAYS_PRESENT_TOFROM:
> GOMP_MAP_PRESENT_ALLOC:
> GOMP_MAP_PRESENT_TO:

Strangely those map kinds are listed for C++ but not C on current
mainline (and I guess I missed that during a rebase at some point).
I've added them in the new unified place.

> * * *
> 
> +omp_expand_access_chain (tree c, tree expr, vec<omp_addr_token *>
> &addr_tokens,
> +                        unsigned *idx)
> +{
> +  using namespace omp_addr_tokenizer;
> +  location_t loc = OMP_CLAUSE_LOCATION (c);
> +  unsigned i = *idx;
> +  tree c2 = NULL_TREE;
> +  gomp_map_kind kind
> +    = (OMP_CLAUSE_CODE (c) == OMP_CLAUSE_FROM
> +       || (OMP_CLAUSE_CODE (c) == OMP_CLAUSE_MAP
> +          && (OMP_CLAUSE_MAP_KIND (c) == GOMP_MAP_FROM
> +              || OMP_CLAUSE_MAP_KIND (c) == GOMP_MAP_DELETE
> +              || OMP_CLAUSE_MAP_KIND (c) == GOMP_MAP_RELEASE
> +              || OMP_CLAUSE_MAP_KIND (c) == GOMP_MAP_ALWAYS_FROM
> +              || OMP_CLAUSE_MAP_KIND (c) == GOMP_MAP_FORCE_FROM
> +              || OMP_CLAUSE_MAP_KIND (c) == GOMP_MAP_PRESENT_FROM)))
> +      ? GOMP_MAP_DETACH : GOMP_MAP_ATTACH;
> 
> GOMP_MAP_ALWAYS_PRESENT_FROM  missing?
> 
> And I personally find it more readable to have
>    if (...
>        ...
>        ...)
>      kind = GOMP_MAP_DETACH;
>    else
>      kind = GOMP_MAP_ATTACH;
> 
> as in the assignment above, the lhs is in this case really far from
> the rhs.

I've made that change and added the missing kind.

> * * *
> 
> I do see in omp_expand_access_chain, which is shared by C and C++,
> that the following accesses are handled:
> 
>    ACCESS_POINTER,
>    ACCESS_POINTER_OFFSET,
>    ACCESS_INDEXED_ARRAY,
> 
> But not:
>    ACCESS_REF_TO_POINTER,
>    ACCESS_REF_TO_POINTER_OFFSET,
>    ACCESS_INDEXED_REF_TO_ARRAY
> 
> Do those need to be handled as well?

I don't think so, because you can't stack up references to references
like you can pointers to pointers, and also things such as arrays of
references aren't allowed in C++.  (Counterexamples welcome though!)

> +tree
> +c_omp_address_inspector::expand_array_base (tree c,
> ...
> +  bool map_p = OMP_CLAUSE_CODE (c) == OMP_CLAUSE_MAP;
> +  bool implicit_p = (OMP_CLAUSE_CODE (c) == OMP_CLAUSE_MAP
> +                    && OMP_CLAUSE_MAP_IMPLICIT (c));
> ...
> +  if (!openmp
> +      && OMP_CLAUSE_CODE (c) == OMP_CLAUSE_MAP
> 
> How about using 'map_p' (twice), given that it is already
> defined?

Done.

> ...
> 
> +       if (target)
> +         {
> +           c2 = build_omp_clause (loc, OMP_CLAUSE_MAP);
> +           if (target
> +               && !ref_to_ptr
> 
> Remove 'target &&' from the second condition.

Done -- actually, this bit has been reformatted to break early for
!target and unindent the braced block.

> * * *
> 
> +++ b/gcc/c/c-parser.cc
> @@ -17659,7 +17659,8 @@ c_parser_omp_clause_detach (c_parser *parser,
> tree list)
> 
>   static tree
>   c_parser_oacc_all_clauses (c_parser *parser, omp_clause_mask mask,
> -                          const char *where, bool finish_p = true)
> +                          const char *where, bool finish_p = true,
> +                          bool target = false)
> 
> Here but also in gcc/c-family/c-omp.cc - I wonder whether this should
> be target_p for consistency with most other boolean variables. The
> code is not really consistent, but given that there is 'finish_p'
> here and map_p etc. in the other file, it seems to make sense to use
> 'target_p.

I changed that, though on preparing these patches I noticed that "bool
openmp" and "bool openacc" are still being introduced in a couple of
places, and those could be changed too.

> * * *
> 
> The first question is whether such code is also needed for 'target'
> and 'target data' besides 'target enter/exit data'. It looks as if
> omp_expand_access_chain should also apply here.
> 
> * * *
> 
> And trying the following snippet:
> 
> void f(){
>    int **a,i,j,n;
> 
>    #pragma omp target enter data map(a[i][j:n])
>    #pragma omp target exit data map(a[i][j:n])
> 
>    #pragma omp target data map(a[i][j:n])
>    {
>    #pragma omp target map(a[i][j:n])
>       ;
>    }
> }
> 
> Results - when putting the enums to enter/exit data
> instead of to target data / target enter data:

Yes, thanks (and there were a couple of other problems too, now
hopefully fixed).

> g++ fails, unless I messed up, with:
> 
> fiof.c:4:36: error: ‘#pragma omp target exit data’ with map-type
> other than ‘from’, ‘tofrom’, ‘release’ or ‘delete’ on ‘map’ clause 4
> |   #pragma omp target exit data map(a[i][j:n]) |
>                ^ fiof.c:5:31: error: ‘#pragma omp target data’ with
> map-type other than ‘to’, ‘from’, ‘tofrom’ or ‘alloc’ on ‘map’ clause
> 5 |   #pragma omp target data map(a[i][j:n])
> 
> 
> And gcc fails with:
> 
> fiof.c:3:36: error: ‘a’ appears more than once in map clauses
>      3 |   #pragma omp target enter data map(a[i][j:n])
>        |                                    ^
> fiof.c:4:35: error: ‘a’ appears more than once in map clauses
>      4 |   #pragma omp target exit data map(a[i][j:n])
>        |                                   ^
> fiof.c:5:30: error: ‘a’ appears more than once in map clauses
>      5 |   #pragma omp target data map(a[i][j:n])
>        |                              ^
> fiof.c:7:25: error: ‘a’ appears more than once in map clauses
>      7 |   #pragma omp target map(a[i][j:n])
> 
> (Plus all '... data' ones: '... must contain at least one ...')
> 
> 
> Can you fix this - and add an example in the spirit of the LLVM
> example above, except that it should test all four 'target' variants.

I've added new tests (baseptrs-6.c, baseptrs-7.c, baseptrs-8.C,
baseptrs-9.C, ref-mapping-1.C) which hopefully cover those cases.

I also noticed gimplify.cc:omp_expand_access_chain is unused, so I've
removed it (it calls itself recursively which makes it "used" enough
for GCC, so I didn't notice previously).  We can resurrect it if it's
needed by later patches in the series (I've forgotten when/where it was
originally called, now).

Re-tested with offloading to NVPTX and bootstrapped.  Test results are
fine apart from (now) two XFAILed Fortran tests which need changes from
later in the parent patch series to work again, as noted. (I also
noticed the gcc.dg/analyzer/data-model-20.c test twinkling, but I think
that must be unrelated.)

Julian

ChangeLog

2023-11-29  Julian Brown  <julian@codesourcery.com>

gcc/c-family/
	* c-common.h (c_omp_region_type): Add C_ORT_EXIT_DATA,
	C_ORT_OMP_EXIT_DATA and C_ORT_ACC_TARGET.
	(omp_addr_token): Add forward declaration.
	(c_omp_address_inspector): New class.
	* c-omp.cc (c_omp_adjust_map_clauses): Mark decls addressable here, but
	do not change any mapping node types.
	(c_omp_address_inspector::unconverted_ref_origin,
	c_omp_address_inspector::component_access_p,
	c_omp_address_inspector::check_clause,
	c_omp_address_inspector::get_root_term,
	c_omp_address_inspector::map_supported_p,
	c_omp_address_inspector::get_origin,
	c_omp_address_inspector::maybe_unconvert_ref,
	c_omp_address_inspector::maybe_zero_length_array_section,
	c_omp_address_inspector::expand_array_base,
	c_omp_address_inspector::expand_component_selector,
	c_omp_address_inspector::expand_map_clause): New methods.
	(omp_expand_access_chain): New function.

gcc/c/
	* c-parser.cc (c_parser_oacc_all_clauses): Add TARGET_P parameter. Use
	to select region type for c_finish_omp_clauses call.
	(c_parser_oacc_loop): Update calls to c_parser_oacc_all_clauses.
	(c_parser_oacc_compute): Likewise.
	(c_parser_omp_target_data, c_parser_omp_target_enter_data): Support
	ATTACH kind.
	(c_parser_omp_target_exit_data): Support DETACH kind.
	(check_clauses): Handle GOMP_MAP_POINTER and GOMP_MAP_ATTACH here.
	* c-typeck.cc (handle_omp_array_sections_1,
	handle_omp_array_sections, c_finish_omp_clauses): Use
	c_omp_address_inspector class and OMP address tokenizer to analyze and
	expand map clause expressions.  Fix some diagnostics.  Fix "is OpenACC"
	condition for C_ORT_ACC_TARGET addition.

gcc/cp/
	* parser.cc (cp_parser_oacc_all_clauses): Add TARGET_P parameter. Use
	to select region type for finish_omp_clauses call.
	(cp_parser_omp_target_data, cp_parser_omp_target_enter_data): Support
	GOMP_MAP_ATTACH kind.
	(cp_parser_omp_target_exit_data): Support GOMP_MAP_DETACH kind.
	(cp_parser_oacc_declare): Update call to cp_parser_oacc_all_clauses.
	(cp_parser_oacc_loop): Update calls to cp_parser_oacc_all_clauses.
	(cp_parser_oacc_compute): Likewise.
	* pt.cc (tsubst_expr): Use C_ORT_ACC_TARGET for call to
	tsubst_omp_clauses for OpenACC compute regions.
	* semantics.cc (cp_omp_address_inspector): New class, derived from
	c_omp_address_inspector.
	(handle_omp_array_sections_1, handle_omp_array_sections,
	finish_omp_clauses): Use cp_omp_address_inspector class and OMP address
	tokenizer to analyze and expand OpenMP map clause expressions.  Fix
	some diagnostics.  Support C_ORT_ACC_TARGET.
	(finish_omp_target): Handle GOMP_MAP_POINTER.

gcc/fortran/
	* trans-openmp.cc (gfc_trans_omp_array_section): Add OPENMP parameter.
	Use GOMP_MAP_ATTACH_DETACH instead of GOMP_MAP_ALWAYS_POINTER for
	derived type components.
	(gfc_trans_omp_clauses): Update calls to gfc_trans_omp_array_section.

gcc/
	* gimplify.cc (build_struct_comp_nodes): Don't process
	GOMP_MAP_ATTACH_DETACH "middle" nodes here.
	(omp_mapping_group): Add REPROCESS_STRUCT and FRAGILE booleans for
	nested struct handling.
	(omp_strip_components_and_deref, omp_strip_indirections): Remove
	functions.
	(omp_get_attachment): Handle GOMP_MAP_DETACH here.
	(omp_group_last): Handle GOMP_MAP_*, GOMP_MAP_DETACH,
	GOMP_MAP_ATTACH_DETACH groups for "exit data" of reference-to-pointer
	component array sections.
	(omp_gather_mapping_groups_1): Initialise reprocess_struct and fragile
	fields.
	(omp_group_base): Handle GOMP_MAP_ATTACH_DETACH after GOMP_MAP_STRUCT.
	(omp_index_mapping_groups_1): Skip reprocess_struct groups.
	(omp_get_nonfirstprivate_group, omp_directive_maps_explicitly,
	omp_resolve_clause_dependencies, omp_first_chained_access_token): New
	functions.
	(omp_check_mapping_compatibility): Adjust accepted node combinations
	for "from" clauses using release instead of alloc.
	(omp_accumulate_sibling_list): Add GROUP_MAP, ADDR_TOKENS, FRAGILE_P,
	REPROCESSING_STRUCT, ADDED_TAIL parameters.  Use OMP address tokenizer
	to analyze addresses.  Reimplement nested struct handling, and
	implement "fragile groups".
	(omp_build_struct_sibling_lists): Adjust for changes to
	omp_accumulate_sibling_list.  Recalculate bias for ATTACH_DETACH nodes
	after GOMP_MAP_STRUCT nodes.
	(gimplify_scan_omp_clauses): Call omp_resolve_clause_dependencies.  Use
	OMP address tokenizer.
	(gimplify_adjust_omp_clauses_1): Use build_fold_indirect_ref_loc
	instead of build_simple_mem_ref_loc.
	* omp-general.cc (omp-general.h, tree-pretty-print.h): Include.
	(omp_addr_tokenizer): New namespace.
	(omp_addr_tokenizer::omp_addr_token): New.
	(omp_addr_tokenizer::omp_parse_component_selector,
	omp_addr_tokenizer::omp_parse_ref,
	omp_addr_tokenizer::omp_parse_pointer,
	omp_addr_tokenizer::omp_parse_access_method,
	omp_addr_tokenizer::omp_parse_access_methods,
	omp_addr_tokenizer::omp_parse_structure_base,
	omp_addr_tokenizer::omp_parse_structured_expr,
	omp_addr_tokenizer::omp_parse_array_expr,
	omp_addr_tokenizer::omp_access_chain_p,
	omp_addr_tokenizer::omp_accessed_addr): New functions.
	(omp_parse_expr, debug_omp_tokenized_addr): New functions.
	* omp-general.h (omp_addr_tokenizer::access_method_kinds,
	omp_addr_tokenizer::structure_base_kinds,
	omp_addr_tokenizer::token_type,
	omp_addr_tokenizer::omp_addr_token,
	omp_addr_tokenizer::omp_access_chain_p,
	omp_addr_tokenizer::omp_accessed_addr): New.
	(omp_addr_token, omp_parse_expr): New.
	* omp-low.cc (scan_sharing_clauses): Skip error check for references
	to pointers.
	* tree.h (OMP_CLAUSE_ATTACHMENT_MAPPING_ERASED): New macro.

gcc/testsuite/
	* c-c++-common/gomp/clauses-2.c: Fix error output.
	* c-c++-common/gomp/target-implicit-map-2.c: Adjust scan output.
	* c-c++-common/gomp/target-50.c: Adjust scan output.
	* c-c++-common/gomp/target-enter-data-1.c: Adjust scan output.
	* g++.dg/gomp/static-component-1.C: New test.
	* gcc.dg/gomp/target-3.c: Adjust scan output.
	* gfortran.dg/gomp/map-9.c.f90: Adjust scan output.
	* c-c++-comon/gomp/target-implicit-map-2.c: Update expected scan output.

libgomp/
	* target.c (gomp_map_pointer): Modify zero-length array section
	pointer handling.
	(gomp_attach_pointer): Likewise.
	(gomp_map_fields_existing): Use gomp_map_0len_lookup.
	(gomp_attach_pointer): Allow attaching null pointers (or Fortran
	"unassociated" pointers).
	(gomp_map_vars_internal): Handle zero-sized struct members.  Add
	diagnostic for unmapped struct pointer members.
	* testsuite/libgomp.c-c++-common/baseptrs-1.c: New test.
	* testsuite/libgomp.c-c++-common/baseptrs-2.c: New test.
	* testsuite/libgomp.c-c++-common/baseptrs-6.c: New test.
	* testsuite/libgomp.c-c++-common/baseptrs-7.c: New test.
	* testsuite/libgomp.c-c++-common/ptr-attach-2.c: New test.
	* testsuite/libgomp.c-c++-common/target-implicit-map-2.c: Fix missing
	"free".
	* testsuite/libgomp.c-c++-common/target-implicit-map-5.c: New test.
	* testsuite/libgomp.c-c++-common/target-map-zlas-1.c: New test.
	* testsuite/libgomp.c++/class-array-1.C: New test.
	* testsuite/libgomp.c++/baseptrs-3.C: New test.
	* testsuite/libgomp.c++/baseptrs-4.C: New test.
	* testsuite/libgomp.c++/baseptrs-5.C: New test.
	* testsuite/libgomp.c++/baseptrs-8.C: New test.
	* testsuite/libgomp.c++/baseptrs-9.C: New test.
	* testsuite/libgomp.c++/ref-mapping-1.C: New test.
	* testsuite/libgomp.c++/target-48.C: New test.
	* testsuite/libgomp.c++/target-49.C: New test.
	* testsuite/libgomp.c++/target-exit-data-reftoptr-1.C: New test.
	* testsuite/libgomp.c++/target-lambda-1.C: Update for OpenMP 5.2
	semantics.
	* testsuite/libgomp.c++/target-this-3.C: Likewise.
	* testsuite/libgomp.c++/target-this-4.C: Likewise.
	* testsuite/libgomp.fortran/struct-elem-map-1.f90: Add temporary XFAIL.
	* testsuite/libgomp.fortran/target-enter-exit-data-6.f90: Likewise.

[-- Attachment #2: clause-expansion-8.diff --]
[-- Type: text/x-patch, Size: 258656 bytes --]

diff --git a/gcc/c-family/c-common.h b/gcc/c-family/c-common.h
index 53ca67f1a2f..9177a9342db 100644
--- a/gcc/c-family/c-common.h
+++ b/gcc/c-family/c-common.h
@@ -1275,8 +1275,11 @@ enum c_omp_region_type
   C_ORT_ACC			= 1 << 1,
   C_ORT_DECLARE_SIMD		= 1 << 2,
   C_ORT_TARGET			= 1 << 3,
+  C_ORT_EXIT_DATA		= 1 << 4,
   C_ORT_OMP_DECLARE_SIMD	= C_ORT_OMP | C_ORT_DECLARE_SIMD,
-  C_ORT_OMP_TARGET		= C_ORT_OMP | C_ORT_TARGET
+  C_ORT_OMP_TARGET		= C_ORT_OMP | C_ORT_TARGET,
+  C_ORT_OMP_EXIT_DATA		= C_ORT_OMP | C_ORT_EXIT_DATA,
+  C_ORT_ACC_TARGET		= C_ORT_ACC | C_ORT_TARGET
 };
 
 extern tree c_finish_omp_master (location_t, tree);
@@ -1313,6 +1316,72 @@ extern tree c_omp_check_context_selector (location_t, tree);
 extern void c_omp_mark_declare_variant (location_t, tree, tree);
 extern void c_omp_adjust_map_clauses (tree, bool);
 
+namespace omp_addr_tokenizer { struct omp_addr_token; }
+typedef omp_addr_tokenizer::omp_addr_token omp_addr_token;
+
+class c_omp_address_inspector
+{
+  location_t loc;
+  tree root_term;
+  bool indirections;
+  int map_supported;
+
+protected:
+  tree orig;
+
+public:
+  c_omp_address_inspector (location_t loc, tree t)
+    : loc (loc), root_term (NULL_TREE), indirections (false),
+      map_supported (-1), orig (t)
+    {
+    }
+
+  ~c_omp_address_inspector ()
+    {
+    }
+
+  virtual bool processing_template_decl_p ()
+    {
+      return false;
+    }
+
+  virtual void emit_unmappable_type_notes (tree)
+    {
+    }
+
+  virtual tree convert_from_reference (tree)
+    {
+      gcc_unreachable ();
+    }
+
+  virtual tree build_array_ref (location_t loc, tree arr, tree idx)
+    {
+      tree eltype = TREE_TYPE (TREE_TYPE (arr));
+      return build4_loc (loc, ARRAY_REF, eltype, arr, idx, NULL_TREE,
+			 NULL_TREE);
+    }
+
+  virtual bool check_clause (tree);
+  tree get_root_term (bool);
+
+  tree unconverted_ref_origin ();
+  bool component_access_p ();
+
+  bool map_supported_p ();
+
+  static tree get_origin (tree);
+  static tree maybe_unconvert_ref (tree);
+
+  bool maybe_zero_length_array_section (tree);
+
+  tree expand_array_base (tree, vec<omp_addr_token *> &, tree, unsigned *,
+			  c_omp_region_type);
+  tree expand_component_selector (tree, vec<omp_addr_token *> &, tree,
+				  unsigned *, c_omp_region_type);
+  tree expand_map_clause (tree, tree, vec<omp_addr_token *> &,
+			  c_omp_region_type);
+};
+
 enum c_omp_directive_kind {
   C_OMP_DIR_STANDALONE,
   C_OMP_DIR_CONSTRUCT,
diff --git a/gcc/c-family/c-omp.cc b/gcc/c-family/c-omp.cc
index 95b6c1e623f..5e534aa21cd 100644
--- a/gcc/c-family/c-omp.cc
+++ b/gcc/c-family/c-omp.cc
@@ -3169,8 +3169,9 @@ struct map_clause
     decl_mapped (false), omp_declare_target (false) { }
 };
 
-/* Adjust map clauses after normal clause parsing, mainly to turn specific
-   base-pointer map cases into attach/detach and mark them addressable.  */
+/* Adjust map clauses after normal clause parsing, mainly to mark specific
+   base-pointer map cases addressable that may be turned into attach/detach
+   operations during gimplification.  */
 void
 c_omp_adjust_map_clauses (tree clauses, bool is_target)
 {
@@ -3186,7 +3187,6 @@ c_omp_adjust_map_clauses (tree clauses, bool is_target)
 	    && POINTER_TYPE_P (TREE_TYPE (OMP_CLAUSE_DECL (c))))
 	  {
 	    tree ptr = OMP_CLAUSE_DECL (c);
-	    OMP_CLAUSE_SET_MAP_KIND (c, GOMP_MAP_ATTACH_DETACH);
 	    c_common_mark_addressable_vec (ptr);
 	  }
       return;
@@ -3199,7 +3199,7 @@ c_omp_adjust_map_clauses (tree clauses, bool is_target)
 	&& DECL_P (OMP_CLAUSE_DECL (c)))
       {
 	/* If this is for a target construct, the firstprivate pointer
-	   is changed to attach/detach if either is true:
+	   is marked addressable if either is true:
 	   (1) the base-pointer is mapped in this same construct, or
 	   (2) the base-pointer is a variable place on the device by
 	       "declare target" directives.
@@ -3241,11 +3241,874 @@ c_omp_adjust_map_clauses (tree clauses, bool is_target)
 
       if (mc.firstprivate_ptr_p
 	  && (mc.decl_mapped || mc.omp_declare_target))
+	c_common_mark_addressable_vec (OMP_CLAUSE_DECL (mc.clause));
+    }
+}
+
+/* Maybe strip off an indirection from a "converted" reference, then find the
+   origin of a pointer (i.e. without any offset).  */
+
+tree
+c_omp_address_inspector::unconverted_ref_origin ()
+{
+  tree t = orig;
+
+  /* We may have a reference-typed component access at the outermost level
+     that has had convert_from_reference called on it.  Get the un-dereferenced
+     reference itself.  */
+  t = maybe_unconvert_ref (t);
+
+  /* Find base pointer for POINTER_PLUS_EXPR, etc.  */
+  t = get_origin (t);
+
+  return t;
+}
+
+/* Return TRUE if the address is a component access.  */
+
+bool
+c_omp_address_inspector::component_access_p ()
+{
+  tree t = maybe_unconvert_ref (orig);
+
+  t = get_origin (t);
+
+  return TREE_CODE (t) == COMPONENT_REF;
+}
+
+/* Perform various checks on the address, as described by clause CLAUSE (we
+   only use its code and location here).  */
+
+bool
+c_omp_address_inspector::check_clause (tree clause)
+{
+  tree t = unconverted_ref_origin ();
+
+  if (TREE_CODE (t) != COMPONENT_REF)
+    return true;
+
+  if (TREE_CODE (TREE_OPERAND (t, 1)) == FIELD_DECL
+      && DECL_BIT_FIELD (TREE_OPERAND (t, 1)))
+    {
+      error_at (OMP_CLAUSE_LOCATION (clause),
+		"bit-field %qE in %qs clause",
+		t, omp_clause_code_name[OMP_CLAUSE_CODE (clause)]);
+      return false;
+    }
+  else if (!processing_template_decl_p ()
+	   && !omp_mappable_type (TREE_TYPE (t)))
+    {
+      error_at (OMP_CLAUSE_LOCATION (clause),
+		"%qE does not have a mappable type in %qs clause",
+		t, omp_clause_code_name[OMP_CLAUSE_CODE (clause)]);
+      emit_unmappable_type_notes (TREE_TYPE (t));
+      return false;
+    }
+  else if (TREE_TYPE (t) && TYPE_ATOMIC (TREE_TYPE (t)))
+    {
+      error_at (OMP_CLAUSE_LOCATION (clause),
+		"%<_Atomic%> %qE in %qs clause", t,
+		omp_clause_code_name[OMP_CLAUSE_CODE (clause)]);
+      return false;
+    }
+
+  return true;
+}
+
+/* Find the "root term" for the address.  This is the innermost decl, etc.
+   of the access.  */
+
+tree
+c_omp_address_inspector::get_root_term (bool checking)
+{
+  if (root_term && !checking)
+    return root_term;
+
+  tree t = unconverted_ref_origin ();
+
+  while (TREE_CODE (t) == COMPONENT_REF)
+    {
+      if (checking
+	  && TREE_TYPE (TREE_OPERAND (t, 0))
+	  && TREE_CODE (TREE_TYPE (TREE_OPERAND (t, 0))) == UNION_TYPE)
 	{
-	  OMP_CLAUSE_SET_MAP_KIND (mc.clause, GOMP_MAP_ATTACH_DETACH);
-	  c_common_mark_addressable_vec (OMP_CLAUSE_DECL (mc.clause));
+	  error_at (loc, "%qE is a member of a union", t);
+	  return error_mark_node;
+	}
+      t = TREE_OPERAND (t, 0);
+      while (TREE_CODE (t) == MEM_REF
+	     || TREE_CODE (t) == INDIRECT_REF
+	     || TREE_CODE (t) == ARRAY_REF)
+	{
+	  if (TREE_CODE (t) == MEM_REF
+	      || TREE_CODE (t) == INDIRECT_REF)
+	    indirections = true;
+	  t = TREE_OPERAND (t, 0);
+	  STRIP_NOPS (t);
+	  if (TREE_CODE (t) == POINTER_PLUS_EXPR)
+	    t = TREE_OPERAND (t, 0);
 	}
     }
+
+  root_term = t;
+
+  return t;
+}
+
+/* Return TRUE if the address is supported in mapping clauses.  At present,
+   this means that the innermost expression is a DECL_P, but could be extended
+   to other types of expression in the future.  */
+
+bool
+c_omp_address_inspector::map_supported_p ()
+{
+  /* If we've already decided if the mapped address is supported, return
+     that.  */
+  if (map_supported != -1)
+    return map_supported;
+
+  tree t = unconverted_ref_origin ();
+
+  STRIP_NOPS (t);
+
+  while (TREE_CODE (t) == INDIRECT_REF
+	 || TREE_CODE (t) == MEM_REF
+	 || TREE_CODE (t) == ARRAY_REF
+	 || TREE_CODE (t) == COMPONENT_REF
+	 || TREE_CODE (t) == COMPOUND_EXPR
+	 || TREE_CODE (t) == SAVE_EXPR
+	 || TREE_CODE (t) == POINTER_PLUS_EXPR
+	 || TREE_CODE (t) == NON_LVALUE_EXPR
+	 || TREE_CODE (t) == NOP_EXPR)
+    if (TREE_CODE (t) == COMPOUND_EXPR)
+      t = TREE_OPERAND (t, 1);
+    else
+      t = TREE_OPERAND (t, 0);
+
+  STRIP_NOPS (t);
+
+  map_supported = DECL_P (t);
+
+  return map_supported;
+}
+
+/* Get the origin of an address T, stripping off offsets and some other
+   bits.  */
+
+tree
+c_omp_address_inspector::get_origin (tree t)
+{
+  while (1)
+    {
+      if (TREE_CODE (t) == COMPOUND_EXPR)
+	{
+	  t = TREE_OPERAND (t, 1);
+	  STRIP_NOPS (t);
+	}
+      else if (TREE_CODE (t) == POINTER_PLUS_EXPR
+	       || TREE_CODE (t) == SAVE_EXPR)
+	t = TREE_OPERAND (t, 0);
+      else if (TREE_CODE (t) == INDIRECT_REF
+	       && TREE_CODE (TREE_TYPE (TREE_OPERAND (t, 0))) == REFERENCE_TYPE)
+	t = TREE_OPERAND (t, 0);
+      else
+	break;
+    }
+  STRIP_NOPS (t);
+  return t;
+}
+
+/* For an address T that might be a reference that has had
+   "convert_from_reference" called on it, return the actual reference without
+   any indirection.  */
+
+tree
+c_omp_address_inspector::maybe_unconvert_ref (tree t)
+{
+  if (TREE_CODE (t) == INDIRECT_REF
+      && TREE_CODE (TREE_TYPE (TREE_OPERAND (t, 0))) == REFERENCE_TYPE)
+    return TREE_OPERAND (t, 0);
+
+  return t;
+}
+
+/* Return TRUE if CLAUSE might describe a zero-length array section.  */
+
+bool
+c_omp_address_inspector::maybe_zero_length_array_section (tree clause)
+{
+  switch (OMP_CLAUSE_MAP_KIND (clause))
+    {
+    case GOMP_MAP_ALLOC:
+    case GOMP_MAP_IF_PRESENT:
+    case GOMP_MAP_TO:
+    case GOMP_MAP_FROM:
+    case GOMP_MAP_TOFROM:
+    case GOMP_MAP_ALWAYS_TO:
+    case GOMP_MAP_ALWAYS_FROM:
+    case GOMP_MAP_ALWAYS_TOFROM:
+    case GOMP_MAP_PRESENT_ALLOC:
+    case GOMP_MAP_PRESENT_TO:
+    case GOMP_MAP_PRESENT_FROM:
+    case GOMP_MAP_PRESENT_TOFROM:
+    case GOMP_MAP_ALWAYS_PRESENT_TO:
+    case GOMP_MAP_ALWAYS_PRESENT_FROM:
+    case GOMP_MAP_ALWAYS_PRESENT_TOFROM:
+    case GOMP_MAP_RELEASE:
+    case GOMP_MAP_DELETE:
+    case GOMP_MAP_FORCE_TO:
+    case GOMP_MAP_FORCE_FROM:
+    case GOMP_MAP_FORCE_TOFROM:
+    case GOMP_MAP_FORCE_PRESENT:
+      return true;
+    default:
+      return false;
+    }
+}
+
+/* Expand a chained access.  We only expect to see a quite limited range of
+   expression types here, because e.g. you can't have an array of
+   references.  */
+
+static tree
+omp_expand_access_chain (tree c, tree expr, vec<omp_addr_token *> &addr_tokens,
+			 unsigned *idx, c_omp_region_type ort)
+{
+  using namespace omp_addr_tokenizer;
+  location_t loc = OMP_CLAUSE_LOCATION (c);
+  unsigned i = *idx;
+  tree c2 = NULL_TREE;
+  gomp_map_kind kind;
+
+  if ((ort & C_ORT_EXIT_DATA) != 0
+      || OMP_CLAUSE_CODE (c) == OMP_CLAUSE_FROM
+      || (OMP_CLAUSE_CODE (c) == OMP_CLAUSE_MAP
+	  && (OMP_CLAUSE_MAP_KIND (c) == GOMP_MAP_FROM
+	      || OMP_CLAUSE_MAP_KIND (c) == GOMP_MAP_DELETE
+	      || OMP_CLAUSE_MAP_KIND (c) == GOMP_MAP_RELEASE
+	      || OMP_CLAUSE_MAP_KIND (c) == GOMP_MAP_ALWAYS_FROM
+	      || OMP_CLAUSE_MAP_KIND (c) == GOMP_MAP_FORCE_FROM
+	      || OMP_CLAUSE_MAP_KIND (c) == GOMP_MAP_PRESENT_FROM
+	      || OMP_CLAUSE_MAP_KIND (c) == GOMP_MAP_ALWAYS_PRESENT_FROM)))
+    kind = GOMP_MAP_DETACH;
+  else
+    kind = GOMP_MAP_ATTACH;
+
+  switch (addr_tokens[i]->u.access_kind)
+    {
+    case ACCESS_POINTER:
+    case ACCESS_POINTER_OFFSET:
+      {
+	tree virtual_origin
+	  = fold_convert_loc (loc, ptrdiff_type_node, addr_tokens[i]->expr);
+	tree data_addr = omp_accessed_addr (addr_tokens, i, expr);
+	c2 = build_omp_clause (loc, OMP_CLAUSE_MAP);
+	OMP_CLAUSE_SET_MAP_KIND (c2, kind);
+	OMP_CLAUSE_DECL (c2) = addr_tokens[i]->expr;
+	OMP_CLAUSE_SIZE (c2)
+	  = fold_build2_loc (loc, MINUS_EXPR, ptrdiff_type_node,
+			     fold_convert_loc (loc, ptrdiff_type_node,
+					       data_addr),
+			     virtual_origin);
+      }
+      break;
+
+    case ACCESS_INDEXED_ARRAY:
+      break;
+
+    default:
+      return error_mark_node;
+    }
+
+  if (c2)
+    {
+      OMP_CLAUSE_CHAIN (c2) = OMP_CLAUSE_CHAIN (c);
+      OMP_CLAUSE_CHAIN (c) = c2;
+      c = c2;
+    }
+
+  *idx = ++i;
+
+  if (i < addr_tokens.length ()
+      && addr_tokens[i]->type == ACCESS_METHOD)
+    return omp_expand_access_chain (c, expr, addr_tokens, idx, ort);
+
+  return c;
+}
+
+/* Translate "array_base_decl access_method" to OMP mapping clauses.  */
+
+tree
+c_omp_address_inspector::expand_array_base (tree c,
+					    vec<omp_addr_token *> &addr_tokens,
+					    tree expr, unsigned *idx,
+					    c_omp_region_type ort)
+{
+  using namespace omp_addr_tokenizer;
+  location_t loc = OMP_CLAUSE_LOCATION (c);
+  int i = *idx;
+  tree decl = addr_tokens[i + 1]->expr;
+  bool decl_p = DECL_P (decl);
+  bool declare_target_p = (decl_p
+			   && is_global_var (decl)
+			   && lookup_attribute ("omp declare target",
+						DECL_ATTRIBUTES (decl)));
+  bool map_p = OMP_CLAUSE_CODE (c) == OMP_CLAUSE_MAP;
+  bool implicit_p = map_p && OMP_CLAUSE_MAP_IMPLICIT (c);
+  bool chain_p = omp_access_chain_p (addr_tokens, i + 1);
+  tree c2 = NULL_TREE, c3 = NULL_TREE;
+  unsigned consume_tokens = 2;
+  bool target_p = (ort & C_ORT_TARGET) != 0;
+  bool openmp_p = (ort & C_ORT_OMP) != 0;
+
+  gcc_assert (i == 0);
+
+  if (!openmp_p
+      && map_p
+      && (OMP_CLAUSE_MAP_KIND (c) == GOMP_MAP_ATTACH
+	  || OMP_CLAUSE_MAP_KIND (c) == GOMP_MAP_DETACH))
+    {
+      i += 2;
+      *idx = i;
+      return c;
+    }
+
+  switch (addr_tokens[i + 1]->u.access_kind)
+    {
+    case ACCESS_DIRECT:
+      if (decl_p && !target_p)
+	c_common_mark_addressable_vec (addr_tokens[i + 1]->expr);
+      break;
+
+    case ACCESS_REF:
+      {
+	/* Copy the referenced object.  Note that we do this even for !MAP_P
+	   clauses.  */
+	tree obj = convert_from_reference (addr_tokens[i + 1]->expr);
+	if (TREE_CODE (TREE_TYPE (obj)) == ARRAY_TYPE)
+	  /* We have a ref to array: add a [0] element as the ME expects.  */
+	  OMP_CLAUSE_DECL (c) = build_array_ref (loc, obj, integer_zero_node);
+	else
+	  OMP_CLAUSE_DECL (c) = obj;
+	OMP_CLAUSE_SIZE (c) = TYPE_SIZE_UNIT (TREE_TYPE (obj));
+
+	if (!map_p)
+	  {
+	    if (decl_p)
+	      c_common_mark_addressable_vec (addr_tokens[i + 1]->expr);
+	    break;
+	  }
+
+	if (!target_p)
+	  break;
+
+	/* If we have a reference to a pointer, avoid using
+	   FIRSTPRIVATE_REFERENCE here in case the pointer is modified in the
+	   offload region (we can only do that if the pointer does not point
+	   to a mapped block).  We could avoid doing this if we don't have a
+	   FROM mapping...  */
+	bool ref_to_ptr = TREE_CODE (TREE_TYPE (obj)) == POINTER_TYPE;
+
+	c2 = build_omp_clause (loc, OMP_CLAUSE_MAP);
+	if (!ref_to_ptr
+	    && !declare_target_p
+	    && decl_p)
+	  OMP_CLAUSE_SET_MAP_KIND (c2, GOMP_MAP_FIRSTPRIVATE_REFERENCE);
+	else
+	  {
+	    OMP_CLAUSE_SET_MAP_KIND (c2, GOMP_MAP_ATTACH_DETACH);
+	    if (decl_p)
+	      c_common_mark_addressable_vec (addr_tokens[i + 1]->expr);
+	  }
+	OMP_CLAUSE_DECL (c2) = addr_tokens[i + 1]->expr;
+	OMP_CLAUSE_SIZE (c2) = size_zero_node;
+
+	if (ref_to_ptr)
+	  {
+	    c3 = c2;
+	    c2 = build_omp_clause (loc, OMP_CLAUSE_MAP);
+	    OMP_CLAUSE_SET_MAP_KIND (c2, GOMP_MAP_ALLOC);
+	    OMP_CLAUSE_DECL (c2) = addr_tokens[i + 1]->expr;
+	    OMP_CLAUSE_SIZE (c2)
+	      = TYPE_SIZE_UNIT (TREE_TYPE (OMP_CLAUSE_DECL (c2)));
+	  }
+      }
+      break;
+
+    case ACCESS_INDEXED_REF_TO_ARRAY:
+      {
+	if (!map_p)
+	  {
+	    if (decl_p)
+	      c_common_mark_addressable_vec (addr_tokens[i + 1]->expr);
+	    break;
+	  }
+
+	if (!target_p)
+	  break;
+
+	tree virtual_origin
+	  = convert_from_reference (addr_tokens[i + 1]->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);
+	c2 = build_omp_clause (loc, OMP_CLAUSE_MAP);
+	if (decl_p && target_p && !declare_target_p)
+	  {
+	    /* It appears that omp-low.cc mishandles cases where we have a
+	       [reference to an] array of pointers such as:
+
+		 int *arr[N];   (or "int *(&arr)[N] = ...")
+		 #pragma omp target map(arr[a][b:c])
+		 { ... }
+
+	       in such cases chain_p will be true.  For now, fall back to
+	       GOMP_MAP_POINTER.  */
+	    enum gomp_map_kind k = chain_p ? GOMP_MAP_POINTER
+					   : GOMP_MAP_FIRSTPRIVATE_REFERENCE;
+	    OMP_CLAUSE_SET_MAP_KIND (c2, k);
+	  }
+	else
+	  {
+	    if (decl_p)
+	      c_common_mark_addressable_vec (addr_tokens[i + 1]->expr);
+	    OMP_CLAUSE_SET_MAP_KIND (c2, GOMP_MAP_ATTACH_DETACH);
+	  }
+	OMP_CLAUSE_DECL (c2) = addr_tokens[i + 1]->expr;
+	OMP_CLAUSE_SIZE (c2)
+	  = fold_build2_loc (loc, MINUS_EXPR, ptrdiff_type_node,
+			     fold_convert_loc (loc, ptrdiff_type_node,
+					       data_addr),
+			     virtual_origin);
+      }
+      break;
+
+    case ACCESS_INDEXED_ARRAY:
+      {
+	if (!map_p)
+	  {
+	    if (decl_p)
+	      c_common_mark_addressable_vec (addr_tokens[i + 1]->expr);
+	    break;
+	  }
+
+	/* The code handling "firstprivatize_array_bases" in gimplify.cc is
+	   relevant here.  What do we need to create for arrays at this
+	   stage?  (This condition doesn't feel quite right.  FIXME?)  */
+	if (!target_p
+	    && (TREE_CODE (TREE_TYPE (addr_tokens[i + 1]->expr))
+		== ARRAY_TYPE))
+	  break;
+
+	tree virtual_origin
+	  = build_fold_addr_expr (addr_tokens[i + 1]->expr);
+	virtual_origin = fold_convert_loc (loc, ptrdiff_type_node,
+					   virtual_origin);
+	tree data_addr = omp_accessed_addr (addr_tokens, i + 1, expr);
+	c2 = build_omp_clause (loc, OMP_CLAUSE_MAP);
+	if (decl_p && target_p)
+	  {
+	    /* See comment for ACCESS_INDEXED_REF_TO_ARRAY above.  */
+	    enum gomp_map_kind k = chain_p ? GOMP_MAP_POINTER
+					   : GOMP_MAP_FIRSTPRIVATE_POINTER;
+	    OMP_CLAUSE_SET_MAP_KIND (c2, k);
+	  }
+	else
+	  {
+	    if (decl_p)
+	      c_common_mark_addressable_vec (addr_tokens[i + 1]->expr);
+	    OMP_CLAUSE_SET_MAP_KIND (c2, GOMP_MAP_ATTACH_DETACH);
+	  }
+	OMP_CLAUSE_DECL (c2) = addr_tokens[i + 1]->expr;
+	OMP_CLAUSE_SIZE (c2)
+	  = fold_build2_loc (loc, MINUS_EXPR, ptrdiff_type_node,
+			     fold_convert_loc (loc, ptrdiff_type_node,
+					       data_addr),
+			     virtual_origin);
+      }
+      break;
+
+    case ACCESS_POINTER:
+    case ACCESS_POINTER_OFFSET:
+      {
+	if (!map_p)
+	  {
+	    if (decl_p)
+	      c_common_mark_addressable_vec (addr_tokens[i + 1]->expr);
+	    break;
+	  }
+
+	unsigned last_access = i + 1;
+	tree virtual_origin;
+
+	if (chain_p
+	    && addr_tokens[i + 2]->type == ACCESS_METHOD
+	    && addr_tokens[i + 2]->u.access_kind == ACCESS_INDEXED_ARRAY)
+	  {
+	    /* !!! This seems wrong for ACCESS_POINTER_OFFSET.  */
+	    consume_tokens = 3;
+	    chain_p = omp_access_chain_p (addr_tokens, i + 2);
+	    last_access = i + 2;
+	    virtual_origin
+	      = build_array_ref (loc, addr_tokens[last_access]->expr,
+				 integer_zero_node);
+	    virtual_origin = build_fold_addr_expr (virtual_origin);
+	    virtual_origin = fold_convert_loc (loc, ptrdiff_type_node,
+					       virtual_origin);
+	  }
+	else
+	  virtual_origin = fold_convert_loc (loc, ptrdiff_type_node,
+					     addr_tokens[last_access]->expr);
+	tree data_addr = omp_accessed_addr (addr_tokens, last_access, expr);
+	c2 = build_omp_clause (loc, OMP_CLAUSE_MAP);
+	/* For OpenACC, use FIRSTPRIVATE_POINTER for decls even on non-compute
+	   regions (e.g. "acc data" constructs).  It'll be removed anyway in
+	   gimplify.cc, but doing it this way maintains diagnostic
+	   behaviour.  */
+	if (decl_p && (target_p || !openmp_p) && !chain_p && !declare_target_p)
+	  OMP_CLAUSE_SET_MAP_KIND (c2, GOMP_MAP_FIRSTPRIVATE_POINTER);
+	else
+	  {
+	    if (decl_p)
+	      c_common_mark_addressable_vec (addr_tokens[i + 1]->expr);
+	    OMP_CLAUSE_SET_MAP_KIND (c2, GOMP_MAP_ATTACH_DETACH);
+	  }
+	OMP_CLAUSE_DECL (c2) = addr_tokens[i + 1]->expr;
+	OMP_CLAUSE_SIZE (c2)
+	  = fold_build2_loc (loc, MINUS_EXPR, ptrdiff_type_node,
+			     fold_convert_loc (loc, ptrdiff_type_node,
+					       data_addr),
+			     virtual_origin);
+      }
+      break;
+
+    case ACCESS_REF_TO_POINTER:
+    case ACCESS_REF_TO_POINTER_OFFSET:
+      {
+	if (!map_p)
+	  {
+	    if (decl_p)
+	      c_common_mark_addressable_vec (addr_tokens[i + 1]->expr);
+	    break;
+	  }
+
+	unsigned last_access = i + 1;
+	tree virtual_origin;
+
+	if (chain_p
+	    && addr_tokens[i + 2]->type == ACCESS_METHOD
+	    && addr_tokens[i + 2]->u.access_kind == ACCESS_INDEXED_ARRAY)
+	  {
+	    /* !!! This seems wrong for ACCESS_POINTER_OFFSET.  */
+	    consume_tokens = 3;
+	    chain_p = omp_access_chain_p (addr_tokens, i + 2);
+	    last_access = i + 2;
+	    virtual_origin
+	      = build_array_ref (loc, addr_tokens[last_access]->expr,
+				 integer_zero_node);
+	    virtual_origin = build_fold_addr_expr (virtual_origin);
+	    virtual_origin = fold_convert_loc (loc, ptrdiff_type_node,
+					       virtual_origin);
+	  }
+	else
+	  {
+	    virtual_origin
+	      = convert_from_reference (addr_tokens[last_access]->expr);
+	    virtual_origin = fold_convert_loc (loc, ptrdiff_type_node,
+					       virtual_origin);
+	  }
+
+	tree data_addr = omp_accessed_addr (addr_tokens, last_access, expr);
+	c2 = build_omp_clause (loc, OMP_CLAUSE_MAP);
+	if (decl_p && target_p && !chain_p && !declare_target_p)
+	  {
+	    OMP_CLAUSE_SET_MAP_KIND (c2, GOMP_MAP_FIRSTPRIVATE_REFERENCE);
+	    OMP_CLAUSE_DECL (c2) = addr_tokens[i + 1]->expr;
+	  }
+	else
+	  {
+	    if (decl_p)
+	      c_common_mark_addressable_vec (addr_tokens[i + 1]->expr);
+	    OMP_CLAUSE_SET_MAP_KIND (c2, GOMP_MAP_ATTACH_DETACH);
+	    OMP_CLAUSE_DECL (c2)
+	      = convert_from_reference (addr_tokens[i + 1]->expr);
+	  }
+	OMP_CLAUSE_SIZE (c2)
+	  = fold_build2_loc (loc, MINUS_EXPR, ptrdiff_type_node,
+			     fold_convert_loc (loc, ptrdiff_type_node,
+					       data_addr),
+			     virtual_origin);
+      }
+      break;
+
+    default:
+      *idx = i + consume_tokens;
+      return error_mark_node;
+    }
+
+  if (c3)
+    {
+      OMP_CLAUSE_CHAIN (c3) = OMP_CLAUSE_CHAIN (c);
+      OMP_CLAUSE_CHAIN (c2) = c3;
+      OMP_CLAUSE_CHAIN (c) = c2;
+      if (implicit_p)
+	{
+	  OMP_CLAUSE_MAP_IMPLICIT (c2) = 1;
+	  OMP_CLAUSE_MAP_IMPLICIT (c3) = 1;
+	}
+      c = c3;
+    }
+  else if (c2)
+    {
+      OMP_CLAUSE_CHAIN (c2) = OMP_CLAUSE_CHAIN (c);
+      OMP_CLAUSE_CHAIN (c) = c2;
+      if (implicit_p)
+	OMP_CLAUSE_MAP_IMPLICIT (c2) = 1;
+      c = c2;
+    }
+
+  i += consume_tokens;
+  *idx = i;
+
+  if (chain_p && map_p)
+    return omp_expand_access_chain (c, expr, addr_tokens, idx, ort);
+
+  return c;
+}
+
+/* Translate "component_selector access_method" to OMP mapping clauses.  */
+
+tree
+c_omp_address_inspector::expand_component_selector (tree c,
+						    vec<omp_addr_token *>
+						      &addr_tokens,
+						    tree expr, unsigned *idx,
+						    c_omp_region_type ort)
+{
+  using namespace omp_addr_tokenizer;
+  location_t loc = OMP_CLAUSE_LOCATION (c);
+  unsigned i = *idx;
+  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;
+
+  switch (addr_tokens[i + 1]->u.access_kind)
+    {
+    case ACCESS_DIRECT:
+    case ACCESS_INDEXED_ARRAY:
+      break;
+
+    case ACCESS_REF:
+      {
+	/* Copy the referenced object.  Note that we also do this for !MAP_P
+	   clauses.  */
+	tree obj = convert_from_reference (addr_tokens[i + 1]->expr);
+	OMP_CLAUSE_DECL (c) = obj;
+	OMP_CLAUSE_SIZE (c) = TYPE_SIZE_UNIT (TREE_TYPE (obj));
+
+	if (!map_p)
+	  break;
+
+	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_SIZE (c2) = size_zero_node;
+      }
+      break;
+
+    case ACCESS_INDEXED_REF_TO_ARRAY:
+      {
+	if (!map_p)
+	  break;
+
+	tree virtual_origin
+	  = convert_from_reference (addr_tokens[i + 1]->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);
+
+	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_SIZE (c2)
+	  = fold_build2_loc (loc, MINUS_EXPR, ptrdiff_type_node,
+			     fold_convert_loc (loc, ptrdiff_type_node,
+					       data_addr),
+			     virtual_origin);
+      }
+      break;
+
+    case ACCESS_POINTER:
+    case ACCESS_POINTER_OFFSET:
+      {
+	if (!map_p)
+	  break;
+
+	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);
+
+	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_SIZE (c2)
+	  = fold_build2_loc (loc, MINUS_EXPR, ptrdiff_type_node,
+			     fold_convert_loc (loc, ptrdiff_type_node,
+					       data_addr),
+			     virtual_origin);
+      }
+      break;
+
+    case ACCESS_REF_TO_POINTER:
+    case ACCESS_REF_TO_POINTER_OFFSET:
+      {
+	if (!map_p)
+	  break;
+
+	tree ptr = convert_from_reference (addr_tokens[i + 1]->expr);
+	tree virtual_origin = fold_convert_loc (loc, ptrdiff_type_node,
+						ptr);
+	tree data_addr = omp_accessed_addr (addr_tokens, i + 1, expr);
+
+	/* Attach the pointer...  */
+	c2 = build_omp_clause (OMP_CLAUSE_LOCATION (c), OMP_CLAUSE_MAP);
+	OMP_CLAUSE_SET_MAP_KIND (c2, GOMP_MAP_ATTACH_DETACH);
+	OMP_CLAUSE_DECL (c2) = ptr;
+	OMP_CLAUSE_SIZE (c2)
+	  = fold_build2_loc (loc, MINUS_EXPR, ptrdiff_type_node,
+			     fold_convert_loc (loc, ptrdiff_type_node,
+					       data_addr),
+			     virtual_origin);
+
+	/* ...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_SIZE (c3) = size_zero_node;
+      }
+      break;
+
+    default:
+      *idx = i + 2;
+      return error_mark_node;
+    }
+
+  if (c3)
+    {
+      OMP_CLAUSE_CHAIN (c3) = OMP_CLAUSE_CHAIN (c);
+      OMP_CLAUSE_CHAIN (c2) = c3;
+      OMP_CLAUSE_CHAIN (c) = c2;
+      c = c3;
+    }
+  else if (c2)
+    {
+      OMP_CLAUSE_CHAIN (c2) = OMP_CLAUSE_CHAIN (c);
+      OMP_CLAUSE_CHAIN (c) = c2;
+      c = c2;
+    }
+
+  i += 2;
+  *idx = i;
+
+  if (chain_p && map_p)
+    return omp_expand_access_chain (c, expr, addr_tokens, idx, ort);
+
+  return c;
+}
+
+/* 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,
+					    vec<omp_addr_token *> &addr_tokens,
+					    c_omp_region_type ort)
+{
+  using namespace omp_addr_tokenizer;
+  unsigned i, length = addr_tokens.length ();
+
+  for (i = 0; i < length;)
+    {
+      int remaining = length - i;
+
+      if (remaining >= 2
+	  && addr_tokens[i]->type == ARRAY_BASE
+	  && 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);
+	  if (c == error_mark_node)
+	    return error_mark_node;
+	}
+      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);
+	  if (c == error_mark_node)
+	    return error_mark_node;
+	}
+      else if (remaining >= 2
+	       && addr_tokens[i]->type == STRUCTURE_BASE
+	       && addr_tokens[i]->u.structure_base_kind == BASE_DECL
+	       && addr_tokens[i + 1]->type == ACCESS_METHOD)
+	{
+	  if (addr_tokens[i + 1]->u.access_kind == ACCESS_DIRECT)
+	    c_common_mark_addressable_vec (addr_tokens[i + 1]->expr);
+	  i += 2;
+	  while (addr_tokens[i]->type == ACCESS_METHOD)
+	    i++;
+	}
+      else if (remaining >= 2
+	       && addr_tokens[i]->type == STRUCTURE_BASE
+	       && addr_tokens[i]->u.structure_base_kind == BASE_ARBITRARY_EXPR
+	       && addr_tokens[i + 1]->type == ACCESS_METHOD)
+	{
+	  switch (addr_tokens[i + 1]->u.access_kind)
+	    {
+	    case ACCESS_DIRECT:
+	    case ACCESS_POINTER:
+	      i += 2;
+	      while (addr_tokens[i]->type == ACCESS_METHOD)
+		i++;
+	      break;
+	    default:
+	      return error_mark_node;
+	    }
+	}
+      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, ort);
+	  /* We used 'expr', so these must have been the last tokens.  */
+	  gcc_assert (i == length);
+	  if (c == error_mark_node)
+	    return error_mark_node;
+	}
+      else if (remaining >= 3
+	       && addr_tokens[i]->type == COMPONENT_SELECTOR
+	       && addr_tokens[i + 1]->type == STRUCTURE_BASE
+	       && (addr_tokens[i + 1]->u.structure_base_kind
+		   == BASE_COMPONENT_EXPR)
+	       && addr_tokens[i + 2]->type == ACCESS_METHOD)
+	{
+	  i += 3;
+	  while (addr_tokens[i]->type == ACCESS_METHOD)
+	    i++;
+	}
+      else
+	break;
+    }
+
+  if (i == length)
+    return c;
+
+  return error_mark_node;
 }
 
 const struct c_omp_directive c_omp_directives[] = {
diff --git a/gcc/c/c-parser.cc b/gcc/c/c-parser.cc
index 371dd29557b..69d2bee9c16 100644
--- a/gcc/c/c-parser.cc
+++ b/gcc/c/c-parser.cc
@@ -19056,7 +19056,8 @@ c_parser_omp_clause_detach (c_parser *parser, tree list)
 
 static tree
 c_parser_oacc_all_clauses (c_parser *parser, omp_clause_mask mask,
-			   const char *where, bool finish_p = true)
+			   const char *where, bool finish_p = true,
+			   bool target_p = false)
 {
   tree clauses = NULL;
   bool first = true;
@@ -19266,7 +19267,8 @@ c_parser_oacc_all_clauses (c_parser *parser, omp_clause_mask mask,
   c_parser_skip_to_pragma_eol (parser);
 
   if (finish_p)
-    return c_finish_omp_clauses (clauses, C_ORT_ACC);
+    return c_finish_omp_clauses (clauses, target_p ? C_ORT_ACC_TARGET
+						   : C_ORT_ACC);
 
   return clauses;
 }
@@ -20004,12 +20006,13 @@ c_parser_oacc_loop (location_t loc, c_parser *parser, char *p_name,
   mask |= OACC_LOOP_CLAUSE_MASK;
 
   tree clauses = c_parser_oacc_all_clauses (parser, mask, p_name,
-					    cclauses == NULL);
+					    /*finish_p=*/cclauses == NULL,
+					    /*target=*/is_parallel);
   if (cclauses)
     {
       clauses = c_oacc_split_loop_clauses (clauses, cclauses, is_parallel);
       if (*cclauses)
-	*cclauses = c_finish_omp_clauses (*cclauses, C_ORT_ACC);
+	*cclauses = c_finish_omp_clauses (*cclauses, C_ORT_ACC_TARGET);
       if (clauses)
 	clauses = c_finish_omp_clauses (clauses, C_ORT_ACC);
     }
@@ -20137,7 +20140,9 @@ c_parser_oacc_compute (location_t loc, c_parser *parser,
 	}
     }
 
-  tree clauses = c_parser_oacc_all_clauses (parser, mask, p_name);
+  tree clauses = c_parser_oacc_all_clauses (parser, mask, p_name,
+					    /*finish_p=*/true,
+					    /*target=*/true);
 
   tree block = c_begin_omp_parallel ();
   add_stmt (c_parser_omp_structured_block (parser, if_p));
@@ -23618,6 +23623,7 @@ c_parser_omp_target_data (location_t loc, c_parser *parser, bool *if_p)
 	  case GOMP_MAP_FIRSTPRIVATE_POINTER:
 	  case GOMP_MAP_ALWAYS_POINTER:
 	  case GOMP_MAP_ATTACH_DETACH:
+	  case GOMP_MAP_ATTACH:
 	    break;
 	  default:
 	    map_seen |= 1;
@@ -23783,6 +23789,7 @@ c_parser_omp_target_enter_data (location_t loc, c_parser *parser,
 	  case GOMP_MAP_FIRSTPRIVATE_POINTER:
 	  case GOMP_MAP_ALWAYS_POINTER:
 	  case GOMP_MAP_ATTACH_DETACH:
+	  case GOMP_MAP_ATTACH:
 	    break;
 	  default:
 	    map_seen |= 1;
@@ -23857,7 +23864,8 @@ c_parser_omp_target_exit_data (location_t loc, c_parser *parser,
 
   tree clauses
     = c_parser_omp_all_clauses (parser, OMP_TARGET_EXIT_DATA_CLAUSE_MASK,
-				"#pragma omp target exit data");
+				"#pragma omp target exit data", false);
+  clauses = c_finish_omp_clauses (clauses, C_ORT_OMP_EXIT_DATA);
   c_omp_adjust_map_clauses (clauses, false);
   int map_seen = 0;
   for (tree *pc = &clauses; *pc;)
@@ -23892,6 +23900,7 @@ c_parser_omp_target_exit_data (location_t loc, c_parser *parser,
 	  case GOMP_MAP_FIRSTPRIVATE_POINTER:
 	  case GOMP_MAP_ALWAYS_POINTER:
 	  case GOMP_MAP_ATTACH_DETACH:
+	  case GOMP_MAP_DETACH:
 	    break;
 	  default:
 	    map_seen |= 1;
@@ -24148,7 +24157,9 @@ check_clauses:
 	  case GOMP_MAP_PRESENT_ALLOC:
 	  case GOMP_MAP_FIRSTPRIVATE_POINTER:
 	  case GOMP_MAP_ALWAYS_POINTER:
+	  case GOMP_MAP_POINTER:
 	  case GOMP_MAP_ATTACH_DETACH:
+	  case GOMP_MAP_ATTACH:
 	    break;
 	  default:
 	    error_at (OMP_CLAUSE_LOCATION (*pc),
diff --git a/gcc/c/c-typeck.cc b/gcc/c/c-typeck.cc
index 18a0891014a..011a1991d8a 100644
--- a/gcc/c/c-typeck.cc
+++ b/gcc/c/c-typeck.cc
@@ -13546,10 +13546,12 @@ handle_omp_array_sections_1 (tree c, tree t, vec<tree> &types,
 			     enum c_omp_region_type ort)
 {
   tree ret, low_bound, length, type;
+  bool openacc = (ort & C_ORT_ACC) != 0;
   if (TREE_CODE (t) != TREE_LIST)
     {
       if (error_operand_p (t))
 	return error_mark_node;
+      c_omp_address_inspector ai (OMP_CLAUSE_LOCATION (c), t);
       ret = t;
       if (OMP_CLAUSE_CODE (c) != OMP_CLAUSE_AFFINITY
 	  && OMP_CLAUSE_CODE (c) != OMP_CLAUSE_DEPEND
@@ -13559,59 +13561,17 @@ handle_omp_array_sections_1 (tree c, tree t, vec<tree> &types,
 		    t, omp_clause_code_name[OMP_CLAUSE_CODE (c)]);
 	  return error_mark_node;
 	}
-      while (INDIRECT_REF_P (t))
-	{
-	  t = TREE_OPERAND (t, 0);
-	  STRIP_NOPS (t);
-	  if (TREE_CODE (t) == POINTER_PLUS_EXPR)
-	    t = TREE_OPERAND (t, 0);
-	}
-      while (TREE_CODE (t) == COMPOUND_EXPR)
-	{
-	  t = TREE_OPERAND (t, 1);
-	  STRIP_NOPS (t);
-	}
-      if (TREE_CODE (t) == COMPONENT_REF
-	  && (OMP_CLAUSE_CODE (c) == OMP_CLAUSE_MAP
-	      || OMP_CLAUSE_CODE (c) == OMP_CLAUSE_TO
-	      || OMP_CLAUSE_CODE (c) == OMP_CLAUSE_FROM))
-	{
-	  if (DECL_BIT_FIELD (TREE_OPERAND (t, 1)))
-	    {
-	      error_at (OMP_CLAUSE_LOCATION (c),
-			"bit-field %qE in %qs clause",
-			t, omp_clause_code_name[OMP_CLAUSE_CODE (c)]);
-	      return error_mark_node;
-	    }
-	  while (TREE_CODE (t) == COMPONENT_REF)
-	    {
-	      if (TREE_CODE (TREE_TYPE (TREE_OPERAND (t, 0))) == UNION_TYPE)
-		{
-		  error_at (OMP_CLAUSE_LOCATION (c),
-			    "%qE is a member of a union", t);
-		  return error_mark_node;
-		}
-	      t = TREE_OPERAND (t, 0);
-	      while (TREE_CODE (t) == MEM_REF
-		     || INDIRECT_REF_P (t)
-		     || TREE_CODE (t) == ARRAY_REF)
-		{
-		  t = TREE_OPERAND (t, 0);
-		  STRIP_NOPS (t);
-		  if (TREE_CODE (t) == POINTER_PLUS_EXPR)
-		    t = TREE_OPERAND (t, 0);
-		}
-	      if (ort == C_ORT_ACC && TREE_CODE (t) == MEM_REF)
-		{
-		  if (maybe_ne (mem_ref_offset (t), 0))
-		    error_at (OMP_CLAUSE_LOCATION (c),
-			      "cannot dereference %qE in %qs clause", t,
-			      omp_clause_code_name[OMP_CLAUSE_CODE (c)]);
-		  else
-		    t = TREE_OPERAND (t, 0);
-		}
-	    }
-	}
+      if (!ai.check_clause (c))
+	return error_mark_node;
+      else if (ai.component_access_p ()
+	       && (OMP_CLAUSE_CODE (c) == OMP_CLAUSE_MAP
+		   || OMP_CLAUSE_CODE (c) == OMP_CLAUSE_TO
+		   || OMP_CLAUSE_CODE (c) == OMP_CLAUSE_FROM))
+	t = ai.get_root_term (true);
+      else
+	t = ai.unconverted_ref_origin ();
+      if (t == error_mark_node)
+	return error_mark_node;
       if (!VAR_P (t) && TREE_CODE (t) != PARM_DECL)
 	{
 	  if (DECL_P (t))
@@ -13706,7 +13666,7 @@ handle_omp_array_sections_1 (tree c, tree t, vec<tree> &types,
 	{
 	  error_at (OMP_CLAUSE_LOCATION (c),
 		    "expected single pointer in %qs clause",
-		    user_omp_clause_code_name (c, ort == C_ORT_ACC));
+		    user_omp_clause_code_name (c, openacc));
 	  return error_mark_node;
 	}
     }
@@ -13931,7 +13891,7 @@ 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 &c, enum c_omp_region_type ort)
 {
   bool maybe_zero_len = false;
   unsigned int first_non_one = 0;
@@ -14140,58 +14100,47 @@ handle_omp_array_sections (tree c, enum c_omp_region_type ort)
       OMP_CLAUSE_DECL (c) = first;
       if (OMP_CLAUSE_CODE (c) == OMP_CLAUSE_HAS_DEVICE_ADDR)
 	return false;
-      if (size)
-	size = c_fully_fold (size, false, NULL);
-      OMP_CLAUSE_SIZE (c) = size;
+      /* Don't set OMP_CLAUSE_SIZE for bare attach/detach clauses.  */
       if (OMP_CLAUSE_CODE (c) != OMP_CLAUSE_MAP
-	  || (TREE_CODE (t) == COMPONENT_REF
-	      && TREE_CODE (TREE_TYPE (t)) == ARRAY_TYPE))
-	return false;
-      gcc_assert (OMP_CLAUSE_MAP_KIND (c) != GOMP_MAP_FORCE_DEVICEPTR);
-      switch (OMP_CLAUSE_MAP_KIND (c))
+	  || (OMP_CLAUSE_MAP_KIND (c) != GOMP_MAP_ATTACH
+	      && OMP_CLAUSE_MAP_KIND (c) != GOMP_MAP_DETACH
+	      && OMP_CLAUSE_MAP_KIND (c) != GOMP_MAP_FORCE_DETACH))
 	{
-	case GOMP_MAP_ALLOC:
-	case GOMP_MAP_IF_PRESENT:
-	case GOMP_MAP_TO:
-	case GOMP_MAP_FROM:
-	case GOMP_MAP_TOFROM:
-	case GOMP_MAP_ALWAYS_TO:
-	case GOMP_MAP_ALWAYS_FROM:
-	case GOMP_MAP_ALWAYS_TOFROM:
-	case GOMP_MAP_RELEASE:
-	case GOMP_MAP_DELETE:
-	case GOMP_MAP_FORCE_TO:
-	case GOMP_MAP_FORCE_FROM:
-	case GOMP_MAP_FORCE_TOFROM:
-	case GOMP_MAP_FORCE_PRESENT:
-	  OMP_CLAUSE_MAP_MAYBE_ZERO_LENGTH_ARRAY_SECTION (c) = 1;
-	  break;
-	default:
-	  break;
+	  if (size)
+	    size = c_fully_fold (size, false, NULL);
+	  OMP_CLAUSE_SIZE (c) = size;
 	}
-      tree c2 = build_omp_clause (OMP_CLAUSE_LOCATION (c), OMP_CLAUSE_MAP);
-      if (TREE_CODE (t) == COMPONENT_REF)
-	OMP_CLAUSE_SET_MAP_KIND (c2, GOMP_MAP_ATTACH_DETACH);
-      else
-	OMP_CLAUSE_SET_MAP_KIND (c2, GOMP_MAP_FIRSTPRIVATE_POINTER);
-      OMP_CLAUSE_MAP_IMPLICIT (c2) = OMP_CLAUSE_MAP_IMPLICIT (c);
-      if (OMP_CLAUSE_MAP_KIND (c2) != GOMP_MAP_FIRSTPRIVATE_POINTER
-	  && !c_mark_addressable (t))
+
+      if (OMP_CLAUSE_CODE (c) != OMP_CLAUSE_MAP)
 	return false;
-      OMP_CLAUSE_DECL (c2) = t;
-      t = build_fold_addr_expr (first);
-      t = fold_convert_loc (OMP_CLAUSE_LOCATION (c), ptrdiff_type_node, t);
-      tree ptr = OMP_CLAUSE_DECL (c2);
-      if (!POINTER_TYPE_P (TREE_TYPE (ptr)))
-	ptr = build_fold_addr_expr (ptr);
-      t = fold_build2_loc (OMP_CLAUSE_LOCATION (c), MINUS_EXPR,
-			   ptrdiff_type_node, t,
-			   fold_convert_loc (OMP_CLAUSE_LOCATION (c),
-					     ptrdiff_type_node, ptr));
-      t = c_fully_fold (t, false, NULL);
-      OMP_CLAUSE_SIZE (c2) = t;
-      OMP_CLAUSE_CHAIN (c2) = OMP_CLAUSE_CHAIN (c);
-      OMP_CLAUSE_CHAIN (c) = c2;
+
+      auto_vec<omp_addr_token *, 10> addr_tokens;
+
+      if (!omp_parse_expr (addr_tokens, first))
+	return true;
+
+      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)
+	{
+	  using namespace omp_addr_tokenizer;
+
+	  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
+	       || 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;
+
+	  return false;
+	}
     }
   return false;
 }
@@ -14457,7 +14406,6 @@ c_finish_omp_clauses (tree clauses, enum c_omp_region_type ort)
   tree ordered_clause = NULL_TREE;
   tree schedule_clause = NULL_TREE;
   bool oacc_async = false;
-  bool indir_component_ref_p = false;
   tree last_iterators = NULL_TREE;
   bool last_iterators_remove = false;
   tree *nogroup_seen = NULL;
@@ -14468,6 +14416,7 @@ c_finish_omp_clauses (tree clauses, enum c_omp_region_type ort)
   bool allocate_seen = false;
   bool implicit_moved = false;
   bool target_in_reduction_seen = false;
+  bool openacc = (ort & C_ORT_ACC) != 0;
 
   bitmap_obstack_initialize (NULL);
   bitmap_initialize (&generic_head, &bitmap_default_obstack);
@@ -14483,7 +14432,7 @@ c_finish_omp_clauses (tree clauses, enum c_omp_region_type ort)
   bitmap_initialize (&oacc_reduction_head, &bitmap_default_obstack);
   bitmap_initialize (&is_on_device_head, &bitmap_default_obstack);
 
-  if (ort & C_ORT_ACC)
+  if (openacc)
     for (c = clauses; c; c = OMP_CLAUSE_CHAIN (c))
       if (OMP_CLAUSE_CODE (c) == OMP_CLAUSE_ASYNC)
 	{
@@ -14877,8 +14826,7 @@ c_finish_omp_clauses (tree clauses, enum c_omp_region_type ort)
 			omp_clause_code_name[OMP_CLAUSE_CODE (c)]);
 	      remove = true;
 	    }
-	  else if ((ort == C_ORT_ACC
-		    && OMP_CLAUSE_CODE (c) == OMP_CLAUSE_REDUCTION)
+	  else if ((openacc && OMP_CLAUSE_CODE (c) == OMP_CLAUSE_REDUCTION)
 		   || (ort == C_ORT_OMP
 		       && (OMP_CLAUSE_CODE (c) == OMP_CLAUSE_USE_DEVICE_PTR
 			   || (OMP_CLAUSE_CODE (c)
@@ -14901,7 +14849,7 @@ c_finish_omp_clauses (tree clauses, enum c_omp_region_type ort)
 	      if (bitmap_bit_p (&oacc_reduction_head, DECL_UID (t)))
 		{
 		  error_at (OMP_CLAUSE_LOCATION (c),
-			    ort == C_ORT_ACC
+			    openacc
 			    ? "%qD appears more than once in reduction clauses"
 			    : "%qD appears more than once in data clauses",
 			    t);
@@ -14924,7 +14872,7 @@ c_finish_omp_clauses (tree clauses, enum c_omp_region_type ort)
 		    || OMP_CLAUSE_CODE (c) == OMP_CLAUSE_IS_DEVICE_PTR)
 		   && bitmap_bit_p (&map_head, DECL_UID (t)))
 	    {
-	      if (ort == C_ORT_ACC)
+	      if (openacc)
 		error_at (OMP_CLAUSE_LOCATION (c),
 			  "%qD appears more than once in data clauses", t);
 	      else
@@ -14989,9 +14937,10 @@ c_finish_omp_clauses (tree clauses, enum c_omp_region_type ort)
 			"%qE appears more than once in data clauses", t);
 	      remove = true;
 	    }
-	  else if (bitmap_bit_p (&map_head, DECL_UID (t)))
+	  else if (bitmap_bit_p (&map_head, DECL_UID (t))
+		   || bitmap_bit_p (&map_field_head, DECL_UID (t)))
 	    {
-	      if (ort == C_ORT_ACC)
+	      if (openacc)
 		error_at (OMP_CLAUSE_LOCATION (c),
 			  "%qD appears more than once in data clauses", t);
 	      else if (OMP_CLAUSE_FIRSTPRIVATE_IMPLICIT (c)
@@ -15259,6 +15208,9 @@ c_finish_omp_clauses (tree clauses, enum c_omp_region_type ort)
 	case OMP_CLAUSE_FROM:
 	case OMP_CLAUSE__CACHE_:
 	  {
+	    using namespace omp_addr_tokenizer;
+	    auto_vec<omp_addr_token *, 10> addr_tokens;
+
 	    t = OMP_CLAUSE_DECL (c);
 	    if (TREE_CODE (t) == TREE_LIST)
 	      {
@@ -15287,56 +15239,68 @@ c_finish_omp_clauses (tree clauses, enum c_omp_region_type ort)
 		      }
 		    while (TREE_CODE (t) == ARRAY_REF)
 		      t = TREE_OPERAND (t, 0);
-		    if (TREE_CODE (t) == COMPONENT_REF
-			&& TREE_CODE (TREE_TYPE (t)) == ARRAY_TYPE)
+
+		    c_omp_address_inspector ai (OMP_CLAUSE_LOCATION (c), t);
+
+		    if (!omp_parse_expr (addr_tokens, t))
 		      {
-			do
-			  {
-			    t = TREE_OPERAND (t, 0);
-			    if (TREE_CODE (t) == MEM_REF
-			      || INDIRECT_REF_P (t))
-			      {
-				t = TREE_OPERAND (t, 0);
-				STRIP_NOPS (t);
-				if (TREE_CODE (t) == POINTER_PLUS_EXPR)
-				  t = TREE_OPERAND (t, 0);
-			      }
-			  }
-			while (TREE_CODE (t) == COMPONENT_REF
-			       || TREE_CODE (t) == ARRAY_REF);
+			sorry_at (OMP_CLAUSE_LOCATION (c),
+				  "unsupported map expression %qE",
+				  OMP_CLAUSE_DECL (c));
+			remove = true;
+			break;
+		      }
+
+		    /* This check is to determine if this will be the only map
+		       node created for this clause.  Otherwise, we'll check
+		       the following FIRSTPRIVATE_POINTER or ATTACH_DETACH
+		       node on the next iteration(s) of the loop.   */
+		    if (addr_tokens.length () >= 4
+			&& addr_tokens[0]->type == STRUCTURE_BASE
+			&& addr_tokens[0]->u.structure_base_kind == BASE_DECL
+			&& addr_tokens[1]->type == ACCESS_METHOD
+			&& addr_tokens[2]->type == COMPONENT_SELECTOR
+			&& addr_tokens[3]->type == ACCESS_METHOD
+			&& (addr_tokens[3]->u.access_kind == ACCESS_DIRECT
+			    || (addr_tokens[3]->u.access_kind
+				== ACCESS_INDEXED_ARRAY)))
+		      {
+			tree rt = addr_tokens[1]->expr;
+
+			gcc_assert (DECL_P (rt));
 
 			if (OMP_CLAUSE_CODE (c) == OMP_CLAUSE_MAP
 			    && OMP_CLAUSE_MAP_IMPLICIT (c)
-			    && (bitmap_bit_p (&map_head, DECL_UID (t))
-				|| bitmap_bit_p (&map_field_head, DECL_UID (t))
+			    && (bitmap_bit_p (&map_head, DECL_UID (rt))
+				|| bitmap_bit_p (&map_field_head, DECL_UID (rt))
 				|| bitmap_bit_p (&map_firstprivate_head,
-						 DECL_UID (t))))
+						 DECL_UID (rt))))
 			  {
 			    remove = true;
 			    break;
 			  }
-			if (bitmap_bit_p (&map_field_head, DECL_UID (t)))
+			if (bitmap_bit_p (&map_field_head, DECL_UID (rt)))
 			  break;
-			if (bitmap_bit_p (&map_head, DECL_UID (t)))
+			if (bitmap_bit_p (&map_head, DECL_UID (rt)))
 			  {
 			    if (OMP_CLAUSE_CODE (c) != OMP_CLAUSE_MAP)
 			      error_at (OMP_CLAUSE_LOCATION (c),
 					"%qD appears more than once in motion "
-					"clauses", t);
-			    else if (ort == C_ORT_ACC)
+					"clauses", rt);
+			    else if (openacc)
 			      error_at (OMP_CLAUSE_LOCATION (c),
 					"%qD appears more than once in data "
-					"clauses", t);
+					"clauses", rt);
 			    else
 			      error_at (OMP_CLAUSE_LOCATION (c),
 					"%qD appears more than once in map "
-					"clauses", t);
+					"clauses", rt);
 			    remove = true;
 			  }
 			else
 			  {
-			    bitmap_set_bit (&map_head, DECL_UID (t));
-			    bitmap_set_bit (&map_field_head, DECL_UID (t));
+			    bitmap_set_bit (&map_head, DECL_UID (rt));
+			    bitmap_set_bit (&map_field_head, DECL_UID (rt));
 			  }
 		      }
 		  }
@@ -15344,7 +15308,8 @@ c_finish_omp_clauses (tree clauses, enum c_omp_region_type ort)
 		  remove = true;
 		if (OMP_CLAUSE_CODE (c) == OMP_CLAUSE_MAP
 		    && (OMP_CLAUSE_MAP_KIND (c) == GOMP_MAP_ATTACH
-			|| OMP_CLAUSE_MAP_KIND (c) == GOMP_MAP_DETACH))
+			|| OMP_CLAUSE_MAP_KIND (c) == GOMP_MAP_DETACH)
+		    && !OMP_CLAUSE_SIZE (c))
 		  /* In this case, we have a single array element which is a
 		     pointer, and we already set OMP_CLAUSE_SIZE in
 		     handle_omp_array_sections above.  For attach/detach
@@ -15353,6 +15318,14 @@ c_finish_omp_clauses (tree clauses, enum c_omp_region_type ort)
 		  OMP_CLAUSE_SIZE (c) = size_zero_node;
 		break;
 	      }
+	    else if (!omp_parse_expr (addr_tokens, t))
+	      {
+		sorry_at (OMP_CLAUSE_LOCATION (c),
+			  "unsupported map expression %qE",
+			  OMP_CLAUSE_DECL (c));
+		remove = true;
+		break;
+	      }
 	    if (t == error_mark_node)
 	      {
 		remove = true;
@@ -15366,101 +15339,47 @@ c_finish_omp_clauses (tree clauses, enum c_omp_region_type ort)
 	      }
 	    if (OMP_CLAUSE_CODE (c) == OMP_CLAUSE_MAP
 		&& (OMP_CLAUSE_MAP_KIND (c) == GOMP_MAP_ATTACH
-		    || OMP_CLAUSE_MAP_KIND (c) == GOMP_MAP_DETACH))
+		    || OMP_CLAUSE_MAP_KIND (c) == GOMP_MAP_DETACH)
+		&& !OMP_CLAUSE_SIZE (c))
 	      /* For attach/detach clauses, set OMP_CLAUSE_SIZE (representing a
 		 bias) to zero here, so it is not set erroneously to the pointer
 		 size later on in gimplify.cc.  */
 	      OMP_CLAUSE_SIZE (c) = size_zero_node;
-	  while (INDIRECT_REF_P (t)
-		   || TREE_CODE (t) == ARRAY_REF)
+
+	    c_omp_address_inspector ai (OMP_CLAUSE_LOCATION (c), t);
+
+	    if (!ai.check_clause (c))
 	      {
-		t = TREE_OPERAND (t, 0);
-		STRIP_NOPS (t);
-		if (TREE_CODE (t) == POINTER_PLUS_EXPR)
-		  t = TREE_OPERAND (t, 0);
-	      }
-	    while (TREE_CODE (t) == COMPOUND_EXPR)
-	      {
-		t = TREE_OPERAND (t, 1);
-		STRIP_NOPS (t);
-	      }
-	    indir_component_ref_p = false;
-	    if (TREE_CODE (t) == COMPONENT_REF
-		&& (TREE_CODE (TREE_OPERAND (t, 0)) == MEM_REF
-		  || INDIRECT_REF_P (TREE_OPERAND (t, 0))
-		    || TREE_CODE (TREE_OPERAND (t, 0)) == ARRAY_REF))
-	      {
-		t = TREE_OPERAND (TREE_OPERAND (t, 0), 0);
-		indir_component_ref_p = true;
-		STRIP_NOPS (t);
-		if (TREE_CODE (t) == POINTER_PLUS_EXPR)
-		  t = TREE_OPERAND (t, 0);
+		remove = true;
+		break;
 	      }
 
-	    if (TREE_CODE (t) == COMPONENT_REF
-		&& OMP_CLAUSE_CODE (c) != OMP_CLAUSE__CACHE_)
+	    if (!ai.map_supported_p ())
 	      {
-		if (DECL_BIT_FIELD (TREE_OPERAND (t, 1)))
-		  {
-		    error_at (OMP_CLAUSE_LOCATION (c),
-			      "bit-field %qE in %qs clause",
-			      t, omp_clause_code_name[OMP_CLAUSE_CODE (c)]);
-		    remove = true;
-		  }
-		else if (!omp_mappable_type (TREE_TYPE (t)))
-		  {
-		    error_at (OMP_CLAUSE_LOCATION (c),
-			      "%qE does not have a mappable type in %qs clause",
-			      t, omp_clause_code_name[OMP_CLAUSE_CODE (c)]);
-		    remove = true;
-		  }
-		else if (TYPE_ATOMIC (TREE_TYPE (t)))
-		  {
-		    error_at (OMP_CLAUSE_LOCATION (c),
-			      "%<_Atomic%> %qE in %qs clause", t,
-			      omp_clause_code_name[OMP_CLAUSE_CODE (c)]);
-		    remove = true;
-		  }
-		while (TREE_CODE (t) == COMPONENT_REF)
-		  {
-		    if (TREE_CODE (TREE_TYPE (TREE_OPERAND (t, 0)))
-			== UNION_TYPE)
-		      {
-			error_at (OMP_CLAUSE_LOCATION (c),
-				  "%qE is a member of a union", t);
-			remove = true;
-			break;
-		      }
-		    t = TREE_OPERAND (t, 0);
-		    if (TREE_CODE (t) == MEM_REF)
-		      {
-			if (maybe_ne (mem_ref_offset (t), 0))
-			  error_at (OMP_CLAUSE_LOCATION (c),
-				    "cannot dereference %qE in %qs clause", t,
-				    omp_clause_code_name[OMP_CLAUSE_CODE (c)]);
-			else
-			  t = TREE_OPERAND (t, 0);
-		      }
-		    while (TREE_CODE (t) == MEM_REF
-			 || INDIRECT_REF_P (t)
-			   || TREE_CODE (t) == ARRAY_REF)
-		      {
-			t = TREE_OPERAND (t, 0);
-			STRIP_NOPS (t);
-			if (TREE_CODE (t) == POINTER_PLUS_EXPR)
-			  t = TREE_OPERAND (t, 0);
-		      }
-		  }
-		if (remove)
-		  break;
-		if (VAR_P (t) || TREE_CODE (t) == PARM_DECL)
-		  {
-		    if (bitmap_bit_p (&map_field_head, DECL_UID (t))
-			|| (ort != C_ORT_ACC
-			    && bitmap_bit_p (&map_head, DECL_UID (t))))
-		      break;
-		  }
+		sorry_at (OMP_CLAUSE_LOCATION (c),
+			  "unsupported map expression %qE",
+			  OMP_CLAUSE_DECL (c));
+		remove = true;
+		break;
 	      }
+
+	    gcc_assert ((addr_tokens[0]->type == ARRAY_BASE
+			 || addr_tokens[0]->type == STRUCTURE_BASE)
+			&& addr_tokens[1]->type == ACCESS_METHOD);
+
+	    t = addr_tokens[1]->expr;
+
+	    if (addr_tokens[0]->u.structure_base_kind != BASE_DECL)
+	      goto skip_decl_checks;
+
+	    /* For OpenMP, we can access a struct "t" and "t.d" on the same
+	       mapping.  OpenACC allows multiple fields of the same structure
+	       to be written.  */
+	    if (addr_tokens[0]->type == STRUCTURE_BASE
+		&& (bitmap_bit_p (&map_field_head, DECL_UID (t))
+		    || (!openacc && bitmap_bit_p (&map_head, DECL_UID (t)))))
+	      goto skip_decl_checks;
+
 	    if (!VAR_P (t) && TREE_CODE (t) != PARM_DECL)
 	      {
 		error_at (OMP_CLAUSE_LOCATION (c),
@@ -15478,7 +15397,6 @@ c_finish_omp_clauses (tree clauses, enum c_omp_region_type ort)
 	    else if ((OMP_CLAUSE_CODE (c) != OMP_CLAUSE_MAP
 		      || (OMP_CLAUSE_MAP_KIND (c)
 			  != GOMP_MAP_FIRSTPRIVATE_POINTER))
-		     && !indir_component_ref_p
 		     && !c_mark_addressable (t))
 	      remove = true;
 	    else if (!(OMP_CLAUSE_CODE (c) == OMP_CLAUSE_MAP
@@ -15524,27 +15442,25 @@ c_finish_omp_clauses (tree clauses, enum c_omp_region_type ort)
 		    remove = true;
 		  }
 		else if (bitmap_bit_p (&map_head, DECL_UID (t))
-			 && !bitmap_bit_p (&map_field_head, DECL_UID (t)))
+			 && !bitmap_bit_p (&map_field_head, DECL_UID (t))
+			 && openacc)
 		  {
-		    if (ort == C_ORT_ACC)
-		      error_at (OMP_CLAUSE_LOCATION (c),
-				"%qD appears more than once in data clauses",
-				t);
-		    else
-		      error_at (OMP_CLAUSE_LOCATION (c),
-				"%qD appears both in data and map clauses", t);
+		    error_at (OMP_CLAUSE_LOCATION (c),
+			      "%qD appears more than once in data clauses", t);
 		    remove = true;
 		  }
 		else
 		  bitmap_set_bit (&map_firstprivate_head, DECL_UID (t));
 	      }
 	    else if (bitmap_bit_p (&map_head, DECL_UID (t))
-		     && !bitmap_bit_p (&map_field_head, DECL_UID (t)))
+		     && !bitmap_bit_p (&map_field_head, DECL_UID (t))
+		     && ort != C_ORT_OMP
+		     && ort != C_ORT_OMP_EXIT_DATA)
 	      {
 		if (OMP_CLAUSE_CODE (c) != OMP_CLAUSE_MAP)
 		  error_at (OMP_CLAUSE_LOCATION (c),
 			    "%qD appears more than once in motion clauses", t);
-		else if (ort == C_ORT_ACC)
+		else if (openacc)
 		  error_at (OMP_CLAUSE_LOCATION (c),
 			    "%qD appears more than once in data clauses", t);
 		else
@@ -15552,8 +15468,7 @@ c_finish_omp_clauses (tree clauses, enum c_omp_region_type ort)
 			    "%qD appears more than once in map clauses", t);
 		remove = true;
 	      }
-	    else if (ort == C_ORT_ACC
-		     && bitmap_bit_p (&generic_head, DECL_UID (t)))
+	    else if (openacc && bitmap_bit_p (&generic_head, DECL_UID (t)))
 	      {
 		error_at (OMP_CLAUSE_LOCATION (c),
 			  "%qD appears more than once in data clauses", t);
@@ -15562,7 +15477,7 @@ c_finish_omp_clauses (tree clauses, enum c_omp_region_type ort)
 	    else if (bitmap_bit_p (&firstprivate_head, DECL_UID (t))
 		     || bitmap_bit_p (&is_on_device_head, DECL_UID (t)))
 	      {
-		if (ort == C_ORT_ACC)
+		if (openacc)
 		  error_at (OMP_CLAUSE_LOCATION (c),
 			    "%qD appears more than once in data clauses", t);
 		else
@@ -15570,13 +15485,37 @@ c_finish_omp_clauses (tree clauses, enum c_omp_region_type ort)
 			    "%qD appears both in data and map clauses", t);
 		remove = true;
 	      }
-	    else
+	    else if (!omp_access_chain_p (addr_tokens, 1))
 	      {
 		bitmap_set_bit (&map_head, DECL_UID (t));
 		if (t != OMP_CLAUSE_DECL (c)
 		    && TREE_CODE (OMP_CLAUSE_DECL (c)) == COMPONENT_REF)
 		  bitmap_set_bit (&map_field_head, DECL_UID (t));
 	      }
+
+	  skip_decl_checks:
+	    /* If we call omp_expand_map_clause in handle_omp_array_sections,
+	       the containing loop (here) iterates through the new nodes
+	       created by that expansion.  Avoid expanding those again (just
+	       by checking the node type).  */
+	    if (!remove
+		&& ort != C_ORT_DECLARE_SIMD
+		&& (OMP_CLAUSE_CODE (c) != OMP_CLAUSE_MAP
+		    || (OMP_CLAUSE_MAP_KIND (c) != GOMP_MAP_FIRSTPRIVATE_POINTER
+			&& (OMP_CLAUSE_MAP_KIND (c)
+			    != GOMP_MAP_FIRSTPRIVATE_REFERENCE)
+			&& OMP_CLAUSE_MAP_KIND (c) != GOMP_MAP_ALWAYS_POINTER
+			&& OMP_CLAUSE_MAP_KIND (c) != GOMP_MAP_ATTACH_DETACH
+			&& OMP_CLAUSE_MAP_KIND (c) != GOMP_MAP_ATTACH
+			&& OMP_CLAUSE_MAP_KIND (c) != GOMP_MAP_DETACH)))
+	      {
+		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;
+	      }
 	  }
 	  break;
 
@@ -15654,7 +15593,7 @@ c_finish_omp_clauses (tree clauses, enum c_omp_region_type ort)
 	  if (TREE_CODE (TREE_TYPE (t)) != POINTER_TYPE)
 	    {
 	      if (OMP_CLAUSE_CODE (c) == OMP_CLAUSE_USE_DEVICE_PTR
-		  && ort != C_ORT_ACC)
+		  && !openacc)
 		{
 		  error_at (OMP_CLAUSE_LOCATION (c),
 			    "%qs variable is not a pointer",
diff --git a/gcc/cp/parser.cc b/gcc/cp/parser.cc
index f6d088bc73f..e54acd39025 100644
--- a/gcc/cp/parser.cc
+++ b/gcc/cp/parser.cc
@@ -41358,7 +41358,7 @@ cp_parser_oacc_compute_clause_self (cp_parser *parser, tree list)
 static tree
 cp_parser_oacc_all_clauses (cp_parser *parser, omp_clause_mask mask,
 			    const char *where, cp_token *pragma_tok,
-			    bool finish_p = true)
+			    bool finish_p = true, bool target_p = false)
 {
   tree clauses = NULL;
   bool first = true;
@@ -41577,7 +41577,8 @@ cp_parser_oacc_all_clauses (cp_parser *parser, omp_clause_mask mask,
   cp_parser_skip_to_pragma_eol (parser, pragma_tok);
 
   if (finish_p)
-    return finish_omp_clauses (clauses, C_ORT_ACC);
+    return finish_omp_clauses (clauses, target_p ? C_ORT_ACC_TARGET
+						 : C_ORT_ACC);
 
   return clauses;
 }
@@ -46121,6 +46122,7 @@ cp_parser_omp_target_data (cp_parser *parser, cp_token *pragma_tok, bool *if_p)
 	  case GOMP_MAP_FIRSTPRIVATE_REFERENCE:
 	  case GOMP_MAP_ALWAYS_POINTER:
 	  case GOMP_MAP_ATTACH_DETACH:
+	  case GOMP_MAP_ATTACH:
 	    break;
 	  default:
 	    map_seen |= 1;
@@ -46243,6 +46245,7 @@ cp_parser_omp_target_enter_data (cp_parser *parser, cp_token *pragma_tok,
 	  case GOMP_MAP_FIRSTPRIVATE_REFERENCE:
 	  case GOMP_MAP_ALWAYS_POINTER:
 	  case GOMP_MAP_ATTACH_DETACH:
+	  case GOMP_MAP_ATTACH:
 	    break;
 	  default:
 	    map_seen |= 1;
@@ -46321,7 +46324,9 @@ cp_parser_omp_target_exit_data (cp_parser *parser, cp_token *pragma_tok,
 
   tree clauses
     = cp_parser_omp_all_clauses (parser, OMP_TARGET_EXIT_DATA_CLAUSE_MASK,
-				 "#pragma omp target exit data", pragma_tok);
+				 "#pragma omp target exit data", pragma_tok,
+				 false);
+  clauses = finish_omp_clauses (clauses, C_ORT_OMP_EXIT_DATA);
   c_omp_adjust_map_clauses (clauses, false);
   int map_seen = 0;
   for (tree *pc = &clauses; *pc;)
@@ -46357,6 +46362,7 @@ cp_parser_omp_target_exit_data (cp_parser *parser, cp_token *pragma_tok,
 	  case GOMP_MAP_FIRSTPRIVATE_REFERENCE:
 	  case GOMP_MAP_ALWAYS_POINTER:
 	  case GOMP_MAP_ATTACH_DETACH:
+	  case GOMP_MAP_DETACH:
 	    break;
 	  default:
 	    map_seen |= 1;
@@ -46738,7 +46744,7 @@ cp_parser_oacc_declare (cp_parser *parser, cp_token *pragma_tok)
   bool found_in_scope = global_bindings_p ();
 
   clauses = cp_parser_oacc_all_clauses (parser, OACC_DECLARE_CLAUSE_MASK,
-					"#pragma acc declare", pragma_tok, true);
+					"#pragma acc declare", pragma_tok);
 
 
   if (omp_find_clause (clauses, OMP_CLAUSE_MAP) == NULL_TREE)
@@ -46986,12 +46992,13 @@ cp_parser_oacc_loop (cp_parser *parser, cp_token *pragma_tok, char *p_name,
   mask |= OACC_LOOP_CLAUSE_MASK;
 
   tree clauses = cp_parser_oacc_all_clauses (parser, mask, p_name, pragma_tok,
-					     cclauses == NULL);
+					     /*finish_p=*/cclauses == NULL,
+					     /*target=*/is_parallel);
   if (cclauses)
     {
       clauses = c_oacc_split_loop_clauses (clauses, cclauses, is_parallel);
       if (*cclauses)
-	*cclauses = finish_omp_clauses (*cclauses, C_ORT_ACC);
+	*cclauses = finish_omp_clauses (*cclauses, C_ORT_ACC_TARGET);
       if (clauses)
 	clauses = finish_omp_clauses (clauses, C_ORT_ACC);
     }
@@ -47127,7 +47134,9 @@ cp_parser_oacc_compute (cp_parser *parser, cp_token *pragma_tok,
 	}
     }
 
-  tree clauses = cp_parser_oacc_all_clauses (parser, mask, p_name, pragma_tok);
+  tree clauses = cp_parser_oacc_all_clauses (parser, mask, p_name, pragma_tok,
+					     /*finish_p=*/true,
+					     /*target=*/true);
 
   tree block = begin_omp_parallel ();
   unsigned int save = cp_parser_begin_omp_structured_block (parser);
diff --git a/gcc/cp/pt.cc b/gcc/cp/pt.cc
index 092e6fdfd36..81c287670cf 100644
--- a/gcc/cp/pt.cc
+++ b/gcc/cp/pt.cc
@@ -18724,8 +18724,8 @@ tsubst_stmt (tree t, tree args, tsubst_flags_t complain, tree in_decl)
     case OACC_KERNELS:
     case OACC_PARALLEL:
     case OACC_SERIAL:
-      tmp = tsubst_omp_clauses (OMP_CLAUSES (t), C_ORT_ACC, args, complain,
-				in_decl);
+      tmp = tsubst_omp_clauses (OMP_CLAUSES (t), C_ORT_ACC_TARGET, args,
+				complain, in_decl);
       stmt = begin_omp_parallel ();
       RECUR (OMP_BODY (t));
       finish_omp_construct (TREE_CODE (t), stmt, tmp);
diff --git a/gcc/cp/semantics.cc b/gcc/cp/semantics.cc
index 401a40e7e9f..ae85df0590a 100644
--- a/gcc/cp/semantics.cc
+++ b/gcc/cp/semantics.cc
@@ -5267,6 +5267,54 @@ omp_privatize_field (tree t, bool shared)
   return v;
 }
 
+/* C++ specialisation of the c_omp_address_inspector class.  */
+
+class cp_omp_address_inspector : public c_omp_address_inspector
+{
+public:
+  cp_omp_address_inspector (location_t loc, tree t)
+    : c_omp_address_inspector (loc, t)
+    {
+    }
+
+  ~cp_omp_address_inspector ()
+    {
+    }
+
+  bool processing_template_decl_p ()
+    {
+      return processing_template_decl;
+    }
+
+  void emit_unmappable_type_notes (tree t)
+    {
+      if (TREE_TYPE (t) != error_mark_node
+	  && !COMPLETE_TYPE_P (TREE_TYPE (t)))
+	cxx_incomplete_type_inform (TREE_TYPE (t));
+    }
+
+  tree convert_from_reference (tree x)
+    {
+      return ::convert_from_reference (x);
+    }
+
+  tree build_array_ref (location_t loc, tree arr, tree idx)
+    {
+      return ::build_array_ref (loc, arr, idx);
+    }
+
+  bool check_clause (tree clause)
+    {
+      if (TREE_CODE (orig) == COMPONENT_REF
+	  && invalid_nonstatic_memfn_p (EXPR_LOCATION (orig), orig,
+					tf_warning_or_error))
+	return false;
+      if (!c_omp_address_inspector::check_clause (clause))
+	return false;
+      return true;
+    }
+};
+
 /* Helper function for handle_omp_array_sections.  Called recursively
    to handle multiple array-section-subscripts.  C is the clause,
    T current expression (initially OMP_CLAUSE_DECL), which is either
@@ -5293,63 +5341,27 @@ handle_omp_array_sections_1 (tree c, tree t, vec<tree> &types,
 			     enum c_omp_region_type ort)
 {
   tree ret, low_bound, length, type;
+  bool openacc = (ort & C_ORT_ACC) != 0;
   if (TREE_CODE (t) != TREE_LIST)
     {
       if (error_operand_p (t))
 	return error_mark_node;
-      if (REFERENCE_REF_P (t)
-	  && TREE_CODE (TREE_OPERAND (t, 0)) == COMPONENT_REF)
-	t = TREE_OPERAND (t, 0);
-      ret = t;
-      while (INDIRECT_REF_P (t))
-	{
-	  t = TREE_OPERAND (t, 0);
-	  STRIP_NOPS (t);
-	  if (TREE_CODE (t) == POINTER_PLUS_EXPR)
-	    t = TREE_OPERAND (t, 0);
-	}
-      while (TREE_CODE (t) == COMPOUND_EXPR)
-	{
-	  t = TREE_OPERAND (t, 1);
-	  STRIP_NOPS (t);
-	}
-      if (TREE_CODE (t) == COMPONENT_REF
-	  && (OMP_CLAUSE_CODE (c) == OMP_CLAUSE_MAP
-	      || OMP_CLAUSE_CODE (c) == OMP_CLAUSE_TO
-	      || OMP_CLAUSE_CODE (c) == OMP_CLAUSE_FROM)
-	  && !type_dependent_expression_p (t))
-	{
-	  if (TREE_CODE (TREE_OPERAND (t, 1)) == FIELD_DECL
-	      && DECL_BIT_FIELD (TREE_OPERAND (t, 1)))
-	    {
-	      error_at (OMP_CLAUSE_LOCATION (c),
-			"bit-field %qE in %qs clause",
-			t, omp_clause_code_name[OMP_CLAUSE_CODE (c)]);
-	      return error_mark_node;
-	    }
-	  while (TREE_CODE (t) == COMPONENT_REF)
-	    {
-	      if (TREE_TYPE (TREE_OPERAND (t, 0))
-		  && TREE_CODE (TREE_TYPE (TREE_OPERAND (t, 0))) == UNION_TYPE)
-		{
-		  error_at (OMP_CLAUSE_LOCATION (c),
-			    "%qE is a member of a union", t);
-		  return error_mark_node;
-		}
-	      t = TREE_OPERAND (t, 0);
-	      while (TREE_CODE (t) == MEM_REF
-		     || INDIRECT_REF_P (t)
-		     || TREE_CODE (t) == ARRAY_REF)
-		{
-		  t = TREE_OPERAND (t, 0);
-		  STRIP_NOPS (t);
-		  if (TREE_CODE (t) == POINTER_PLUS_EXPR)
-		    t = TREE_OPERAND (t, 0);
-		}
-	    }
-	  if (REFERENCE_REF_P (t))
-	    t = TREE_OPERAND (t, 0);
-	}
+
+      cp_omp_address_inspector ai (OMP_CLAUSE_LOCATION (c), t);
+      tree t_refto = ai.maybe_unconvert_ref (t);
+
+      if (!ai.check_clause (c))
+	return error_mark_node;
+      else if (ai.component_access_p ()
+	       && (OMP_CLAUSE_CODE (c) == OMP_CLAUSE_MAP
+		   || OMP_CLAUSE_CODE (c) == OMP_CLAUSE_TO
+		   || OMP_CLAUSE_CODE (c) == OMP_CLAUSE_FROM))
+	t = ai.get_root_term (true);
+      else
+	t = ai.unconverted_ref_origin ();
+      if (t == error_mark_node)
+	return error_mark_node;
+      ret = t_refto;
       if (TREE_CODE (t) == FIELD_DECL)
 	ret = finish_non_static_data_member (t, NULL_TREE, NULL_TREE);
       else if (!VAR_P (t) && TREE_CODE (t) != PARM_DECL)
@@ -5446,7 +5458,7 @@ handle_omp_array_sections_1 (tree c, tree t, vec<tree> &types,
 	{
 	  error_at (OMP_CLAUSE_LOCATION (c),
 		    "expected single pointer in %qs clause",
-		    user_omp_clause_code_name (c, ort == C_ORT_ACC));
+		    user_omp_clause_code_name (c, openacc));
 	  return error_mark_node;
 	}
     }
@@ -5683,7 +5695,7 @@ 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 &c, enum c_omp_region_type ort)
 {
   bool maybe_zero_len = false;
   unsigned int first_non_one = 0;
@@ -5891,118 +5903,73 @@ handle_omp_array_sections (tree c, enum c_omp_region_type ort)
 	  OMP_CLAUSE_DECL (c) = first;
 	  if (OMP_CLAUSE_CODE (c) == OMP_CLAUSE_HAS_DEVICE_ADDR)
 	    return false;
-	  OMP_CLAUSE_SIZE (c) = size;
+	  if (OMP_CLAUSE_CODE (c) != OMP_CLAUSE_MAP
+	      || (OMP_CLAUSE_MAP_KIND (c) != GOMP_MAP_ATTACH
+		  && OMP_CLAUSE_MAP_KIND (c) != GOMP_MAP_DETACH
+		  && OMP_CLAUSE_MAP_KIND (c) != GOMP_MAP_FORCE_DETACH))
+	    OMP_CLAUSE_SIZE (c) = size;
 	  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
-	      || (TREE_CODE (t) == COMPONENT_REF
-		  && TREE_CODE (TREE_TYPE (t)) == ARRAY_TYPE))
+
+	  if (OMP_CLAUSE_CODE (c) != OMP_CLAUSE_MAP)
 	    return false;
-	  switch (OMP_CLAUSE_MAP_KIND (c))
-	    {
-	    case GOMP_MAP_ALLOC:
-	    case GOMP_MAP_IF_PRESENT:
-	    case GOMP_MAP_TO:
-	    case GOMP_MAP_FROM:
-	    case GOMP_MAP_TOFROM:
-	    case GOMP_MAP_ALWAYS_TO:
-	    case GOMP_MAP_ALWAYS_FROM:
-	    case GOMP_MAP_ALWAYS_TOFROM:
-	    case GOMP_MAP_PRESENT_ALLOC:
-	    case GOMP_MAP_PRESENT_TO:
-	    case GOMP_MAP_PRESENT_FROM:
-	    case GOMP_MAP_PRESENT_TOFROM:
-	    case GOMP_MAP_ALWAYS_PRESENT_TO:
-	    case GOMP_MAP_ALWAYS_PRESENT_FROM:
-	    case GOMP_MAP_ALWAYS_PRESENT_TOFROM:
-	    case GOMP_MAP_RELEASE:
-	    case GOMP_MAP_DELETE:
-	    case GOMP_MAP_FORCE_TO:
-	    case GOMP_MAP_FORCE_FROM:
-	    case GOMP_MAP_FORCE_TOFROM:
-	    case GOMP_MAP_FORCE_PRESENT:
-	      OMP_CLAUSE_MAP_MAYBE_ZERO_LENGTH_ARRAY_SECTION (c) = 1;
-	      break;
-	    default:
-	      break;
-	    }
-	  bool reference_always_pointer = true;
-	  tree c2 = build_omp_clause (OMP_CLAUSE_LOCATION (c),
-				      OMP_CLAUSE_MAP);
-	  if (TREE_CODE (t) == COMPONENT_REF)
-	    {
-	      OMP_CLAUSE_SET_MAP_KIND (c2, GOMP_MAP_ATTACH_DETACH);
 
-	      if ((ort & C_ORT_OMP_DECLARE_SIMD) == C_ORT_OMP
-		  && TYPE_REF_P (TREE_TYPE (t)))
+	  if (TREE_CODE (first) == INDIRECT_REF)
+	    {
+	      /* Detect and skip adding extra nodes for pointer-to-member
+		 mappings.  These are unsupported for now.  */
+	      tree tmp = TREE_OPERAND (first, 0);
+
+	      if (TREE_CODE (tmp) == NON_LVALUE_EXPR)
+		tmp = TREE_OPERAND (tmp, 0);
+
+	      if (TREE_CODE (tmp) == INDIRECT_REF)
+		tmp = TREE_OPERAND (tmp, 0);
+
+	      if (TREE_CODE (tmp) == POINTER_PLUS_EXPR)
 		{
-		  if (TREE_CODE (TREE_TYPE (TREE_TYPE (t))) == ARRAY_TYPE)
-		    OMP_CLAUSE_SET_MAP_KIND (c2, GOMP_MAP_ALWAYS_POINTER);
-		  else
-		    t = convert_from_reference (t);
-
-		  reference_always_pointer = false;
+		  tree offset = TREE_OPERAND (tmp, 1);
+		  STRIP_NOPS (offset);
+		  if (TYPE_PTRMEM_P (TREE_TYPE (offset)))
+		    {
+		      sorry_at (OMP_CLAUSE_LOCATION (c),
+				"pointer-to-member mapping %qE not supported",
+				OMP_CLAUSE_DECL (c));
+		      return true;
+		    }
 		}
 	    }
-	  else if (REFERENCE_REF_P (t)
-		   && TREE_CODE (TREE_OPERAND (t, 0)) == COMPONENT_REF)
-	    {
-	      gomp_map_kind k;
-	      if ((ort & C_ORT_OMP_DECLARE_SIMD) == C_ORT_OMP
-		  && TREE_CODE (TREE_TYPE (t)) == POINTER_TYPE)
-		k = GOMP_MAP_ATTACH_DETACH;
-	      else
-		{
-		  t = TREE_OPERAND (t, 0);
-		  k = (ort == C_ORT_ACC
-		       ? GOMP_MAP_ATTACH_DETACH : GOMP_MAP_ALWAYS_POINTER);
-		}
-	      OMP_CLAUSE_SET_MAP_KIND (c2, k);
-	    }
-	  else
-	    OMP_CLAUSE_SET_MAP_KIND (c2, GOMP_MAP_FIRSTPRIVATE_POINTER);
-	  OMP_CLAUSE_MAP_IMPLICIT (c2) = OMP_CLAUSE_MAP_IMPLICIT (c);
-	  if (OMP_CLAUSE_MAP_KIND (c2) != GOMP_MAP_FIRSTPRIVATE_POINTER
-	      && !cxx_mark_addressable (t))
-	    return false;
-	  OMP_CLAUSE_DECL (c2) = t;
-	  t = build_fold_addr_expr (first);
-	  t = fold_convert_loc (OMP_CLAUSE_LOCATION (c),
-				ptrdiff_type_node, t);
-	  tree ptr = OMP_CLAUSE_DECL (c2);
-	  ptr = convert_from_reference (ptr);
-	  if (!INDIRECT_TYPE_P (TREE_TYPE (ptr)))
-	    ptr = build_fold_addr_expr (ptr);
-	  t = fold_build2_loc (OMP_CLAUSE_LOCATION (c), MINUS_EXPR,
-			       ptrdiff_type_node, t,
-			       fold_convert_loc (OMP_CLAUSE_LOCATION (c),
-						 ptrdiff_type_node, ptr));
-	  OMP_CLAUSE_SIZE (c2) = t;
-	  OMP_CLAUSE_CHAIN (c2) = OMP_CLAUSE_CHAIN (c);
-	  OMP_CLAUSE_CHAIN (c) = c2;
 
-	  ptr = OMP_CLAUSE_DECL (c2);
-	  if (reference_always_pointer
-	      && OMP_CLAUSE_MAP_KIND (c2) != GOMP_MAP_FIRSTPRIVATE_POINTER
-	      && TYPE_REF_P (TREE_TYPE (ptr))
-	      && INDIRECT_TYPE_P (TREE_TYPE (TREE_TYPE (ptr))))
+	  /* 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]".  */
+
+	  auto_vec<omp_addr_token *, 10> addr_tokens;
+
+	  if (!omp_parse_expr (addr_tokens, first))
+	    return true;
+
+	  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 c3 = build_omp_clause (OMP_CLAUSE_LOCATION (c),
-					  OMP_CLAUSE_MAP);
-	      OMP_CLAUSE_SET_MAP_KIND (c3, OMP_CLAUSE_MAP_KIND (c2));
-	      OMP_CLAUSE_MAP_IMPLICIT (c2) = OMP_CLAUSE_MAP_IMPLICIT (c);
-	      OMP_CLAUSE_DECL (c3) = ptr;
-	      if (OMP_CLAUSE_MAP_KIND (c2) == GOMP_MAP_ALWAYS_POINTER
-		  || OMP_CLAUSE_MAP_KIND (c2) == GOMP_MAP_ATTACH_DETACH)
-		{
-		  OMP_CLAUSE_DECL (c2) = build_simple_mem_ref (ptr);
-		  OMP_CLAUSE_SET_MAP_KIND (c2, GOMP_MAP_ALWAYS_POINTER);
-		}
-	      else
-		OMP_CLAUSE_DECL (c2) = convert_from_reference (ptr);
-	      OMP_CLAUSE_SIZE (c3) = size_zero_node;
-	      OMP_CLAUSE_CHAIN (c3) = OMP_CLAUSE_CHAIN (c2);
-	      OMP_CLAUSE_CHAIN (c2) = c3;
+	      using namespace omp_addr_tokenizer;
+
+	      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
+		   || 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;
+
+	      return false;
 	    }
 	}
     }
@@ -6930,6 +6897,7 @@ finish_omp_clauses (tree clauses, enum c_omp_region_type ort)
   bitmap_head oacc_reduction_head, is_on_device_head;
   tree c, t, *pc;
   tree safelen = NULL_TREE;
+  bool openacc = (ort & C_ORT_ACC) != 0;
   bool branch_seen = false;
   bool copyprivate_seen = false;
   bool ordered_seen = false;
@@ -6962,7 +6930,7 @@ finish_omp_clauses (tree clauses, enum c_omp_region_type ort)
   bitmap_initialize (&oacc_reduction_head, &bitmap_default_obstack);
   bitmap_initialize (&is_on_device_head, &bitmap_default_obstack);
 
-  if (ort & C_ORT_ACC)
+  if (openacc)
     for (c = clauses; c; c = OMP_CLAUSE_CHAIN (c))
       if (OMP_CLAUSE_CODE (c) == OMP_CLAUSE_ASYNC)
 	{
@@ -7211,7 +7179,7 @@ finish_omp_clauses (tree clauses, enum c_omp_region_type ort)
 	    t = OMP_CLAUSE_DECL (c);
 	check_dup_generic_t:
 	  if (t == current_class_ptr
-	      && ((ort != C_ORT_OMP_DECLARE_SIMD && ort != C_ORT_ACC)
+	      && ((ort != C_ORT_OMP_DECLARE_SIMD && !openacc)
 		  || (OMP_CLAUSE_CODE (c) != OMP_CLAUSE_LINEAR
 		      && OMP_CLAUSE_CODE (c) != OMP_CLAUSE_UNIFORM)))
 	    {
@@ -7236,7 +7204,7 @@ finish_omp_clauses (tree clauses, enum c_omp_region_type ort)
 			  omp_clause_code_name[OMP_CLAUSE_CODE (c)]);
 	      remove = true;
 	    }
-	  else if ((ort == C_ORT_ACC
+	  else if ((openacc
 		    && OMP_CLAUSE_CODE (c) == OMP_CLAUSE_REDUCTION)
 		   || (ort == C_ORT_OMP
 		       && (OMP_CLAUSE_CODE (c) == OMP_CLAUSE_USE_DEVICE_PTR
@@ -7260,7 +7228,7 @@ finish_omp_clauses (tree clauses, enum c_omp_region_type ort)
 	      if (bitmap_bit_p (&oacc_reduction_head, DECL_UID (t)))
 		{
 		  error_at (OMP_CLAUSE_LOCATION (c),
-			    ort == C_ORT_ACC
+			    openacc
 			    ? "%qD appears more than once in reduction clauses"
 			    : "%qD appears more than once in data clauses",
 			    t);
@@ -7283,7 +7251,7 @@ finish_omp_clauses (tree clauses, enum c_omp_region_type ort)
 		    || OMP_CLAUSE_CODE (c) == OMP_CLAUSE_IS_DEVICE_PTR)
 		   && bitmap_bit_p (&map_head, DECL_UID (t)))
 	    {
-	      if (ort == C_ORT_ACC)
+	      if (openacc)
 		error_at (OMP_CLAUSE_LOCATION (c),
 			  "%qD appears more than once in data clauses", t);
 	      else
@@ -7345,7 +7313,7 @@ finish_omp_clauses (tree clauses, enum c_omp_region_type ort)
 	    omp_note_field_privatization (t, OMP_CLAUSE_DECL (c));
 	  else
 	    t = OMP_CLAUSE_DECL (c);
-	  if (ort != C_ORT_ACC && t == current_class_ptr)
+	  if (!openacc && t == current_class_ptr)
 	    {
 	      error_at (OMP_CLAUSE_LOCATION (c),
 			"%<this%> allowed in OpenMP only in %<declare simd%>"
@@ -7381,9 +7349,10 @@ finish_omp_clauses (tree clauses, enum c_omp_region_type ort)
 			"%qD appears more than once in data clauses", t);
 	      remove = true;
 	    }
-	  else if (bitmap_bit_p (&map_head, DECL_UID (t)))
+	  else if (bitmap_bit_p (&map_head, DECL_UID (t))
+		   || bitmap_bit_p (&map_field_head, DECL_UID (t)))
 	    {
-	      if (ort == C_ORT_ACC)
+	      if (openacc)
 		error_at (OMP_CLAUSE_LOCATION (c),
 			  "%qD appears more than once in data clauses", t);
 	      else if (OMP_CLAUSE_FIRSTPRIVATE_IMPLICIT (c)
@@ -7404,7 +7373,7 @@ finish_omp_clauses (tree clauses, enum c_omp_region_type ort)
 	    omp_note_field_privatization (t, OMP_CLAUSE_DECL (c));
 	  else
 	    t = OMP_CLAUSE_DECL (c);
-	  if (ort != C_ORT_ACC && t == current_class_ptr)
+	  if (!openacc && t == current_class_ptr)
 	    {
 	      error_at (OMP_CLAUSE_LOCATION (c),
 			"%<this%> allowed in OpenMP only in %<declare simd%>"
@@ -8203,6 +8172,9 @@ finish_omp_clauses (tree clauses, enum c_omp_region_type ort)
 	case OMP_CLAUSE_FROM:
 	case OMP_CLAUSE__CACHE_:
 	  {
+	    using namespace omp_addr_tokenizer;
+	    auto_vec<omp_addr_token *, 10> addr_tokens;
+
 	    t = OMP_CLAUSE_DECL (c);
 	    if (TREE_CODE (t) == TREE_LIST)
 	      {
@@ -8229,58 +8201,73 @@ finish_omp_clauses (tree clauses, enum c_omp_region_type ort)
 		      }
 		    while (TREE_CODE (t) == ARRAY_REF)
 		      t = TREE_OPERAND (t, 0);
-		    if (TREE_CODE (t) == COMPONENT_REF
-			&& TREE_CODE (TREE_TYPE (t)) == ARRAY_TYPE)
+
+		    if (type_dependent_expression_p (t))
+		      break;
+
+		    cp_omp_address_inspector ai (OMP_CLAUSE_LOCATION (c), t);
+
+		    if (!ai.map_supported_p ()
+			|| !omp_parse_expr (addr_tokens, t))
 		      {
-			do
-			  {
-			    t = TREE_OPERAND (t, 0);
-			    if (REFERENCE_REF_P (t))
-			      t = TREE_OPERAND (t, 0);
-			    if (TREE_CODE (t) == MEM_REF
-				|| INDIRECT_REF_P (t))
-			      {
-				t = TREE_OPERAND (t, 0);
-				STRIP_NOPS (t);
-				if (TREE_CODE (t) == POINTER_PLUS_EXPR)
-				  t = TREE_OPERAND (t, 0);
-			      }
-			  }
-			while (TREE_CODE (t) == COMPONENT_REF
-			       || TREE_CODE (t) == ARRAY_REF);
+			sorry_at (OMP_CLAUSE_LOCATION (c),
+				  "unsupported map expression %qE",
+				  OMP_CLAUSE_DECL (c));
+			remove = true;
+			break;
+		      }
+
+		    /* This check is to determine if this will be the only map
+		       node created for this clause.  Otherwise, we'll check
+		       the following FIRSTPRIVATE_POINTER,
+		       FIRSTPRIVATE_REFERENCE or ATTACH_DETACH node on the next
+		       iteration(s) of the loop.   */
+		    if (addr_tokens.length () >= 4
+			&& addr_tokens[0]->type == STRUCTURE_BASE
+			&& addr_tokens[0]->u.structure_base_kind == BASE_DECL
+			&& addr_tokens[1]->type == ACCESS_METHOD
+			&& addr_tokens[2]->type == COMPONENT_SELECTOR
+			&& addr_tokens[3]->type == ACCESS_METHOD
+			&& (addr_tokens[3]->u.access_kind == ACCESS_DIRECT
+			    || (addr_tokens[3]->u.access_kind
+				== ACCESS_INDEXED_ARRAY)))
+		      {
+			tree rt = addr_tokens[1]->expr;
+
+			gcc_assert (DECL_P (rt));
 
 			if (OMP_CLAUSE_CODE (c) == OMP_CLAUSE_MAP
 			    && OMP_CLAUSE_MAP_IMPLICIT (c)
-			    && (bitmap_bit_p (&map_head, DECL_UID (t))
-				|| bitmap_bit_p (&map_field_head, DECL_UID (t))
+			    && (bitmap_bit_p (&map_head, DECL_UID (rt))
+				|| bitmap_bit_p (&map_field_head, DECL_UID (rt))
 				|| bitmap_bit_p (&map_firstprivate_head,
-						 DECL_UID (t))))
+						 DECL_UID (rt))))
 			  {
 			    remove = true;
 			    break;
 			  }
-			if (bitmap_bit_p (&map_field_head, DECL_UID (t)))
+			if (bitmap_bit_p (&map_field_head, DECL_UID (rt)))
 			  break;
-			if (bitmap_bit_p (&map_head, DECL_UID (t)))
+			if (bitmap_bit_p (&map_head, DECL_UID (rt)))
 			  {
 			    if (OMP_CLAUSE_CODE (c) != OMP_CLAUSE_MAP)
 			      error_at (OMP_CLAUSE_LOCATION (c),
 					"%qD appears more than once in motion"
-					" clauses", t);
-			    else if (ort == C_ORT_ACC)
+					" clauses", rt);
+			    else if (openacc)
 			      error_at (OMP_CLAUSE_LOCATION (c),
 					"%qD appears more than once in data"
-					" clauses", t);
+					" clauses", rt);
 			    else
 			      error_at (OMP_CLAUSE_LOCATION (c),
 					"%qD appears more than once in map"
-					" clauses", t);
+					" clauses", rt);
 			    remove = true;
 			  }
 			else
 			  {
-			    bitmap_set_bit (&map_head, DECL_UID (t));
-			    bitmap_set_bit (&map_field_head, DECL_UID (t));
+			    bitmap_set_bit (&map_head, DECL_UID (rt));
+			    bitmap_set_bit (&map_field_head, DECL_UID (rt));
 			  }
 		      }
 		  }
@@ -8288,7 +8275,8 @@ finish_omp_clauses (tree clauses, enum c_omp_region_type ort)
 		  remove = true;
 		if (OMP_CLAUSE_CODE (c) == OMP_CLAUSE_MAP
 		    && (OMP_CLAUSE_MAP_KIND (c) == GOMP_MAP_ATTACH
-			|| OMP_CLAUSE_MAP_KIND (c) == GOMP_MAP_DETACH))
+			|| OMP_CLAUSE_MAP_KIND (c) == GOMP_MAP_DETACH)
+		    && !OMP_CLAUSE_SIZE (c))
 		  /* In this case, we have a single array element which is a
 		     pointer, and we already set OMP_CLAUSE_SIZE in
 		     handle_omp_array_sections above.  For attach/detach
@@ -8297,6 +8285,16 @@ finish_omp_clauses (tree clauses, enum c_omp_region_type ort)
 		  OMP_CLAUSE_SIZE (c) = size_zero_node;
 		break;
 	      }
+	    else if (type_dependent_expression_p (t))
+	      break;
+	    else if (!omp_parse_expr (addr_tokens, t))
+	      {
+		sorry_at (OMP_CLAUSE_LOCATION (c),
+			  "unsupported map expression %qE",
+			  OMP_CLAUSE_DECL (c));
+		remove = true;
+		break;
+	      }
 	    if (t == error_mark_node)
 	      {
 		remove = true;
@@ -8310,115 +8308,55 @@ finish_omp_clauses (tree clauses, enum c_omp_region_type ort)
 	      }
 	    if (OMP_CLAUSE_CODE (c) == OMP_CLAUSE_MAP
 		&& (OMP_CLAUSE_MAP_KIND (c) == GOMP_MAP_ATTACH
-		    || OMP_CLAUSE_MAP_KIND (c) == GOMP_MAP_DETACH))
+		    || OMP_CLAUSE_MAP_KIND (c) == GOMP_MAP_DETACH)
+		&& !OMP_CLAUSE_SIZE (c))
 	      /* For attach/detach clauses, set OMP_CLAUSE_SIZE (representing a
 		 bias) to zero here, so it is not set erroneously to the
 		 pointer size later on in gimplify.cc.  */
 	      OMP_CLAUSE_SIZE (c) = size_zero_node;
-	    if (REFERENCE_REF_P (t)
-		&& TREE_CODE (TREE_OPERAND (t, 0)) == COMPONENT_REF)
+
+	    cp_omp_address_inspector ai (OMP_CLAUSE_LOCATION (c), t);
+
+	    if (!ai.check_clause (c))
 	      {
-		t = TREE_OPERAND (t, 0);
-		if (OMP_CLAUSE_CODE (c) == OMP_CLAUSE_MAP
-		    && OMP_CLAUSE_MAP_KIND (c) != GOMP_MAP_ATTACH_DETACH)
-		  OMP_CLAUSE_DECL (c) = t;
+		remove = true;
+		break;
 	      }
-	    while (INDIRECT_REF_P (t)
-		   || TREE_CODE (t) == ARRAY_REF)
+
+	    if (!ai.map_supported_p ())
 	      {
-		t = TREE_OPERAND (t, 0);
-		STRIP_NOPS (t);
-		if (TREE_CODE (t) == POINTER_PLUS_EXPR)
-		  t = TREE_OPERAND (t, 0);
+		sorry_at (OMP_CLAUSE_LOCATION (c),
+			  "unsupported map expression %qE",
+			  OMP_CLAUSE_DECL (c));
+		remove = true;
+		break;
 	      }
-	    while (TREE_CODE (t) == COMPOUND_EXPR)
-	      {
-		t = TREE_OPERAND (t, 1);
-		STRIP_NOPS (t);
-	      }
-	    if (TREE_CODE (t) == COMPONENT_REF
-		&& invalid_nonstatic_memfn_p (EXPR_LOCATION (t), t,
-					      tf_warning_or_error))
-	      remove = true;
-	    indir_component_ref_p = false;
-	    if (TREE_CODE (t) == COMPONENT_REF
-		&& (INDIRECT_REF_P (TREE_OPERAND (t, 0))
-		    || TREE_CODE (TREE_OPERAND (t, 0)) == ARRAY_REF))
-	      {
-		t = TREE_OPERAND (TREE_OPERAND (t, 0), 0);
-		indir_component_ref_p = true;
-		STRIP_NOPS (t);
-		if (TREE_CODE (t) == POINTER_PLUS_EXPR)
-		  t = TREE_OPERAND (t, 0);
-	      }
-	    if (TREE_CODE (t) == COMPONENT_REF
-		&& OMP_CLAUSE_CODE (c) != OMP_CLAUSE__CACHE_)
-	      {
-		if (type_dependent_expression_p (t))
-		  break;
-		if (TREE_CODE (TREE_OPERAND (t, 1)) == FIELD_DECL
-		    && DECL_BIT_FIELD (TREE_OPERAND (t, 1)))
-		  {
-		    error_at (OMP_CLAUSE_LOCATION (c),
-			      "bit-field %qE in %qs clause",
-			      t, omp_clause_code_name[OMP_CLAUSE_CODE (c)]);
-		    remove = true;
-		  }
-		else if (!omp_mappable_type (TREE_TYPE (t)))
-		  {
-		    error_at (OMP_CLAUSE_LOCATION (c),
-			      "%qE does not have a mappable type in %qs clause",
-			      t, omp_clause_code_name[OMP_CLAUSE_CODE (c)]);
-		    if (TREE_TYPE (t) != error_mark_node
-			&& !COMPLETE_TYPE_P (TREE_TYPE (t)))
-		      cxx_incomplete_type_inform (TREE_TYPE (t));
-		    remove = true;
-		  }
-		while (TREE_CODE (t) == COMPONENT_REF)
-		  {
-		    if (TREE_TYPE (TREE_OPERAND (t, 0))
-			&& (TREE_CODE (TREE_TYPE (TREE_OPERAND (t, 0)))
-			    == UNION_TYPE))
-		      {
-			error_at (OMP_CLAUSE_LOCATION (c),
-				  "%qE is a member of a union", t);
-			remove = true;
-			break;
-		      }
-		    t = TREE_OPERAND (t, 0);
-		    if (TREE_CODE (t) == MEM_REF)
-		      {
-			if (maybe_ne (mem_ref_offset (t), 0))
-			  error_at (OMP_CLAUSE_LOCATION (c),
-				    "cannot dereference %qE in %qs clause", t,
-				    omp_clause_code_name[OMP_CLAUSE_CODE (c)]);
-			else
-			  t = TREE_OPERAND (t, 0);
-		      }
-		    while (TREE_CODE (t) == MEM_REF
-			   || INDIRECT_REF_P (t)
-			   || TREE_CODE (t) == ARRAY_REF)
-		      {
-			t = TREE_OPERAND (t, 0);
-			STRIP_NOPS (t);
-			if (TREE_CODE (t) == POINTER_PLUS_EXPR)
-			  t = TREE_OPERAND (t, 0);
-		      }
-		  }
-		if (remove)
-		  break;
-		if (REFERENCE_REF_P (t))
-		  t = TREE_OPERAND (t, 0);
-		if (VAR_P (t) || TREE_CODE (t) == PARM_DECL)
-		  {
-		    if (bitmap_bit_p (&map_field_head, DECL_UID (t))
-			|| (ort != C_ORT_ACC
-			    && bitmap_bit_p (&map_head, DECL_UID (t))))
-		      goto handle_map_references;
-		  }
-	      }
-	    if (!processing_template_decl
-		&& TREE_CODE (t) == FIELD_DECL)
+
+	    gcc_assert ((addr_tokens[0]->type == ARRAY_BASE
+			 || addr_tokens[0]->type == STRUCTURE_BASE)
+			&& addr_tokens[1]->type == ACCESS_METHOD);
+
+	    t = addr_tokens[1]->expr;
+
+	    /* This is used to prevent cxx_mark_addressable from being called
+	       on 'this' for expressions like 'this->a', i.e. typical member
+	       accesses.  */
+	    indir_component_ref_p
+	      = (addr_tokens[0]->type == STRUCTURE_BASE
+		 && addr_tokens[1]->u.access_kind != ACCESS_DIRECT);
+
+	    if (addr_tokens[0]->u.structure_base_kind != BASE_DECL)
+	      goto skip_decl_checks;
+
+	    /* For OpenMP, we can access a struct "t" and "t.d" on the same
+	       mapping.  OpenACC allows multiple fields of the same structure
+	       to be written.  */
+	    if (addr_tokens[0]->type == STRUCTURE_BASE
+		&& (bitmap_bit_p (&map_field_head, DECL_UID (t))
+		    || (!openacc && bitmap_bit_p (&map_head, DECL_UID (t)))))
+	      goto skip_decl_checks;
+
+	    if (!processing_template_decl && TREE_CODE (t) == FIELD_DECL)
 	      {
 		OMP_CLAUSE_DECL (c)
 		  = finish_non_static_data_member (t, NULL_TREE, NULL_TREE);
@@ -8456,12 +8394,17 @@ finish_omp_clauses (tree clauses, enum c_omp_region_type ort)
 			 || (OMP_CLAUSE_MAP_KIND (c)
 			     != GOMP_MAP_FIRSTPRIVATE_POINTER))
 		     && !indir_component_ref_p
+		     && (t != current_class_ptr
+			 || OMP_CLAUSE_CODE (c) != OMP_CLAUSE_MAP
+			 || OMP_CLAUSE_MAP_KIND (c) != GOMP_MAP_ATTACH_DETACH)
 		     && !cxx_mark_addressable (t))
 	      remove = true;
 	    else if (!(OMP_CLAUSE_CODE (c) == OMP_CLAUSE_MAP
 		       && (OMP_CLAUSE_MAP_KIND (c) == GOMP_MAP_POINTER
 			   || (OMP_CLAUSE_MAP_KIND (c)
-			       == GOMP_MAP_FIRSTPRIVATE_POINTER)))
+			       == GOMP_MAP_FIRSTPRIVATE_POINTER)
+			   || (OMP_CLAUSE_MAP_KIND (c)
+			       == GOMP_MAP_ATTACH_DETACH)))
 		     && t == OMP_CLAUSE_DECL (c)
 		     && !type_dependent_expression_p (t)
 		     && !omp_mappable_type (TYPE_REF_P (TREE_TYPE (t))
@@ -8505,27 +8448,29 @@ finish_omp_clauses (tree clauses, enum c_omp_region_type ort)
 		    remove = true;
 		  }
 		else if (bitmap_bit_p (&map_head, DECL_UID (t))
-			 && !bitmap_bit_p (&map_field_head, DECL_UID (t)))
+			 && !bitmap_bit_p (&map_field_head, DECL_UID (t))
+			 && openacc)
 		  {
-		    if (ort == C_ORT_ACC)
-		      error_at (OMP_CLAUSE_LOCATION (c),
-				"%qD appears more than once in data clauses",
-				t);
-		    else
-		      error_at (OMP_CLAUSE_LOCATION (c),
-				"%qD appears both in data and map clauses", t);
+		    error_at (OMP_CLAUSE_LOCATION (c),
+			      "%qD appears more than once in data clauses", t);
 		    remove = true;
 		  }
 		else
 		  bitmap_set_bit (&map_firstprivate_head, DECL_UID (t));
 	      }
+	    else if (OMP_CLAUSE_CODE (c) == OMP_CLAUSE_MAP
+		     && (OMP_CLAUSE_MAP_KIND (c)
+			 == GOMP_MAP_FIRSTPRIVATE_REFERENCE))
+	      bitmap_set_bit (&map_firstprivate_head, DECL_UID (t));
 	    else if (bitmap_bit_p (&map_head, DECL_UID (t))
-		     && !bitmap_bit_p (&map_field_head, DECL_UID (t)))
+		     && !bitmap_bit_p (&map_field_head, DECL_UID (t))
+		     && ort != C_ORT_OMP
+		     && ort != C_ORT_OMP_EXIT_DATA)
 	      {
 		if (OMP_CLAUSE_CODE (c) != OMP_CLAUSE_MAP)
 		  error_at (OMP_CLAUSE_LOCATION (c),
 			    "%qD appears more than once in motion clauses", t);
-		else if (ort == C_ORT_ACC)
+		else if (openacc)
 		  error_at (OMP_CLAUSE_LOCATION (c),
 			    "%qD appears more than once in data clauses", t);
 		else
@@ -8533,8 +8478,7 @@ finish_omp_clauses (tree clauses, enum c_omp_region_type ort)
 			    "%qD appears more than once in map clauses", t);
 		remove = true;
 	      }
-	    else if (ort == C_ORT_ACC
-		     && bitmap_bit_p (&generic_head, DECL_UID (t)))
+	    else if (openacc && bitmap_bit_p (&generic_head, DECL_UID (t)))
 	      {
 		error_at (OMP_CLAUSE_LOCATION (c),
 			  "%qD appears more than once in data clauses", t);
@@ -8543,7 +8487,7 @@ finish_omp_clauses (tree clauses, enum c_omp_region_type ort)
 	    else if (bitmap_bit_p (&firstprivate_head, DECL_UID (t))
 		     || bitmap_bit_p (&is_on_device_head, DECL_UID (t)))
 	      {
-		if (ort == C_ORT_ACC)
+		if (openacc)
 		  error_at (OMP_CLAUSE_LOCATION (c),
 			    "%qD appears more than once in data clauses", t);
 		else
@@ -8551,7 +8495,7 @@ finish_omp_clauses (tree clauses, enum c_omp_region_type ort)
 			    "%qD appears both in data and map clauses", t);
 		remove = true;
 	      }
-	    else
+	    else if (!omp_access_chain_p (addr_tokens, 1))
 	      {
 		bitmap_set_bit (&map_head, DECL_UID (t));
 
@@ -8565,49 +8509,30 @@ finish_omp_clauses (tree clauses, enum c_omp_region_type ort)
 								    0))))))
 		  bitmap_set_bit (&map_field_head, DECL_UID (t));
 	      }
-	  handle_map_references:
+
+	  skip_decl_checks:
+	    /* If we call ai.expand_map_clause in handle_omp_array_sections,
+	       the containing loop (here) iterates through the new nodes
+	       created by that expansion.  Avoid expanding those again (just
+	       by checking the node type).  */
 	    if (!remove
 		&& !processing_template_decl
 		&& ort != C_ORT_DECLARE_SIMD
-		&& TYPE_REF_P (TREE_TYPE (OMP_CLAUSE_DECL (c))))
+		&& (OMP_CLAUSE_CODE (c) != OMP_CLAUSE_MAP
+		    || (OMP_CLAUSE_MAP_KIND (c) != GOMP_MAP_FIRSTPRIVATE_POINTER
+			&& (OMP_CLAUSE_MAP_KIND (c)
+			    != GOMP_MAP_FIRSTPRIVATE_REFERENCE)
+			&& OMP_CLAUSE_MAP_KIND (c) != GOMP_MAP_ALWAYS_POINTER
+			&& OMP_CLAUSE_MAP_KIND (c) != GOMP_MAP_ATTACH_DETACH
+			&& OMP_CLAUSE_MAP_KIND (c) != GOMP_MAP_ATTACH
+			&& OMP_CLAUSE_MAP_KIND (c) != GOMP_MAP_DETACH)))
 	      {
-		t = OMP_CLAUSE_DECL (c);
-		if (OMP_CLAUSE_CODE (c) != OMP_CLAUSE_MAP)
-		  {
-		    OMP_CLAUSE_DECL (c) = build_simple_mem_ref (t);
-		    if (OMP_CLAUSE_SIZE (c) == NULL_TREE)
-		      OMP_CLAUSE_SIZE (c)
-			= TYPE_SIZE_UNIT (TREE_TYPE (TREE_TYPE (t)));
-		  }
-		else if (OMP_CLAUSE_MAP_KIND (c)
-			 != GOMP_MAP_FIRSTPRIVATE_POINTER
-			 && (OMP_CLAUSE_MAP_KIND (c)
-			     != GOMP_MAP_FIRSTPRIVATE_REFERENCE)
-			 && (OMP_CLAUSE_MAP_KIND (c)
-			     != GOMP_MAP_ALWAYS_POINTER)
-			 && (OMP_CLAUSE_MAP_KIND (c)
-			     != GOMP_MAP_ATTACH_DETACH))
-		  {
-		    grp_start_p = pc;
-		    grp_sentinel = OMP_CLAUSE_CHAIN (c);
-
-		    tree c2 = build_omp_clause (OMP_CLAUSE_LOCATION (c),
-						OMP_CLAUSE_MAP);
-		    if (TREE_CODE (t) == COMPONENT_REF)
-		      OMP_CLAUSE_SET_MAP_KIND (c2, GOMP_MAP_ALWAYS_POINTER);
-		    else
-		      OMP_CLAUSE_SET_MAP_KIND (c2,
-					       GOMP_MAP_FIRSTPRIVATE_REFERENCE);
-		    OMP_CLAUSE_DECL (c2) = t;
-		    OMP_CLAUSE_SIZE (c2) = size_zero_node;
-		    OMP_CLAUSE_CHAIN (c2) = OMP_CLAUSE_CHAIN (c);
-		    OMP_CLAUSE_CHAIN (c) = c2;
-		    OMP_CLAUSE_DECL (c) = build_simple_mem_ref (t);
-		    if (OMP_CLAUSE_SIZE (c) == NULL_TREE)
-		      OMP_CLAUSE_SIZE (c)
-			= TYPE_SIZE_UNIT (TREE_TYPE (TREE_TYPE (t)));
-		    c = c2;
-		  }
+		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;
 	      }
 	  }
 	  break;
@@ -9007,7 +8932,8 @@ finish_omp_clauses (tree clauses, enum c_omp_region_type ort)
 	  if (grp_start_p)
 	    {
 	      /* If we found a clause to remove, we want to remove the whole
-		 expanded group, otherwise gimplify can get confused.  */
+		 expanded group, otherwise gimplify
+		 (omp_resolve_clause_dependencies) can get confused.  */
 	      *grp_start_p = grp_sentinel;
 	      pc = grp_start_p;
 	      grp_start_p = NULL;
@@ -10195,6 +10121,7 @@ finish_omp_target (location_t loc, tree clauses, tree body, bool combined_p)
 	  case GOMP_MAP_ATTACH_DETACH:
 	  case GOMP_MAP_ATTACH:
 	  case GOMP_MAP_ATTACH_ZERO_LENGTH_ARRAY_SECTION:
+	  case GOMP_MAP_POINTER:
 	  case GOMP_MAP_POINTER_TO_ZERO_LENGTH_ARRAY_SECTION:
 	    break;
 	  default:
diff --git a/gcc/fortran/trans-openmp.cc b/gcc/fortran/trans-openmp.cc
index 82bbc41b388..33216f5c7f2 100644
--- a/gcc/fortran/trans-openmp.cc
+++ b/gcc/fortran/trans-openmp.cc
@@ -2405,8 +2405,8 @@ static vec<tree, va_heap, vl_embed> *doacross_steps;
 static void
 gfc_trans_omp_array_section (stmtblock_t *block, gfc_exec_op op,
 			     gfc_omp_namelist *n, tree decl, bool element,
-			     gomp_map_kind ptr_kind, tree &node, tree &node2,
-			     tree &node3, tree &node4)
+			     bool openmp, gomp_map_kind ptr_kind, tree &node,
+			     tree &node2, tree &node3, tree &node4)
 {
   gfc_se se;
   tree ptr, ptr2;
@@ -2529,7 +2529,7 @@ gfc_trans_omp_array_section (stmtblock_t *block, gfc_exec_op op,
 	 struct - and adding an 'alloc: for the 'desc.data' pointer, which
 	 would break as the 'desc' (the descriptor) is also mapped
 	 (see node4 above).  */
-      if (ptr_kind == GOMP_MAP_ATTACH_DETACH)
+      if (ptr_kind == GOMP_MAP_ATTACH_DETACH && !openmp)
 	STRIP_NOPS (OMP_CLAUSE_DECL (node3));
     }
   else
@@ -2547,7 +2547,7 @@ gfc_trans_omp_array_section (stmtblock_t *block, gfc_exec_op op,
 			       decl, offset, NULL_TREE, NULL_TREE);
 	  OMP_CLAUSE_DECL (node) = offset;
 
-	  if (ptr_kind == GOMP_MAP_ALWAYS_POINTER)
+	  if (ptr_kind == GOMP_MAP_ATTACH_DETACH && openmp)
 	    return;
 	}
       else
@@ -3538,8 +3538,9 @@ gfc_trans_omp_clauses (stmtblock_t *block, gfc_omp_clauses *clauses,
 		      && !(POINTER_TYPE_P (type)
 			   && GFC_DESCRIPTOR_TYPE_P (TREE_TYPE (type))))
 		    k = GOMP_MAP_FIRSTPRIVATE_POINTER;
-		  gfc_trans_omp_array_section (block, op, n, decl, element, k,
-					       node, node2, node3, node4);
+		  gfc_trans_omp_array_section (block, op, n, decl, element,
+					       !openacc, k, node, node2,
+					       node3, node4);
 		}
 	      else if (n->expr
 		       && n->expr->expr_type == EXPR_VARIABLE
@@ -3578,10 +3579,7 @@ gfc_trans_omp_clauses (stmtblock_t *block, gfc_omp_clauses *clauses,
 
 		      node2 = build_omp_clause (input_location,
 						OMP_CLAUSE_MAP);
-		      gomp_map_kind kind
-			= (openacc ? GOMP_MAP_ATTACH_DETACH
-				   : GOMP_MAP_ALWAYS_POINTER);
-		      OMP_CLAUSE_SET_MAP_KIND (node2, kind);
+		      OMP_CLAUSE_SET_MAP_KIND (node2, GOMP_MAP_ATTACH_DETACH);
 		      OMP_CLAUSE_DECL (node2)
 			= POINTER_TYPE_P (TREE_TYPE (se.expr))
 			  ? se.expr
@@ -3599,6 +3597,7 @@ gfc_trans_omp_clauses (stmtblock_t *block, gfc_omp_clauses *clauses,
 					   fold_convert (size_type_node,
 					       se.string_length),
 					   TYPE_SIZE_UNIT (tmp));
+			  gomp_map_kind kind;
 			  if (n->u.map_op == OMP_MAP_DELETE)
 			    kind = GOMP_MAP_DELETE;
 			  else if (op == EXEC_OMP_TARGET_EXIT_DATA)
@@ -3685,9 +3684,7 @@ gfc_trans_omp_clauses (stmtblock_t *block, gfc_omp_clauses *clauses,
 			  node2 = build_omp_clause (input_location,
 						    OMP_CLAUSE_MAP);
 			  OMP_CLAUSE_SET_MAP_KIND (node2,
-						   openacc
-						   ? GOMP_MAP_ATTACH_DETACH
-						   : GOMP_MAP_ALWAYS_POINTER);
+						   GOMP_MAP_ATTACH_DETACH);
 			  OMP_CLAUSE_DECL (node2) = build_fold_addr_expr (data);
 			  OMP_CLAUSE_SIZE (node2) = size_int (0);
 			}
@@ -3788,9 +3785,7 @@ gfc_trans_omp_clauses (stmtblock_t *block, gfc_omp_clauses *clauses,
 			  node3 = build_omp_clause (input_location,
 						    OMP_CLAUSE_MAP);
 			  OMP_CLAUSE_SET_MAP_KIND (node3,
-						   openacc
-						   ? GOMP_MAP_ATTACH_DETACH
-						   : GOMP_MAP_ALWAYS_POINTER);
+						   GOMP_MAP_ATTACH_DETACH);
 			  OMP_CLAUSE_DECL (node3)
 			    = gfc_conv_descriptor_data_get (inner);
 			  /* Similar to gfc_trans_omp_array_section (details
@@ -3813,11 +3808,10 @@ gfc_trans_omp_clauses (stmtblock_t *block, gfc_omp_clauses *clauses,
 		    {
 		      /* An array element or section.  */
 		      bool element = lastref->u.ar.type == AR_ELEMENT;
-		      gomp_map_kind kind = (openacc ? GOMP_MAP_ATTACH_DETACH
-						    : GOMP_MAP_ALWAYS_POINTER);
+		      gomp_map_kind kind = GOMP_MAP_ATTACH_DETACH;
 		      gfc_trans_omp_array_section (block, op, n, inner, element,
-						   kind, node, node2, node3,
-						   node4);
+						   !openacc, kind, node, node2,
+						   node3, node4);
 		    }
 		  else
 		    gcc_unreachable ();
diff --git a/gcc/gimplify.cc b/gcc/gimplify.cc
index d52d71b9b6b..1e34bde19be 100644
--- a/gcc/gimplify.cc
+++ b/gcc/gimplify.cc
@@ -9190,8 +9190,7 @@ build_omp_struct_comp_nodes (enum tree_code code, tree grp_start, tree grp_end,
 
   if (grp_mid
       && OMP_CLAUSE_CODE (grp_mid) == OMP_CLAUSE_MAP
-      && (OMP_CLAUSE_MAP_KIND (grp_mid) == GOMP_MAP_ALWAYS_POINTER
-	  || OMP_CLAUSE_MAP_KIND (grp_mid) == GOMP_MAP_ATTACH_DETACH))
+      && OMP_CLAUSE_MAP_KIND (grp_mid) == GOMP_MAP_ALWAYS_POINTER)
     {
       tree c3
 	= build_omp_clause (OMP_CLAUSE_LOCATION (grp_end), OMP_CLAUSE_MAP);
@@ -9287,6 +9286,12 @@ struct omp_mapping_group {
   /* If we've removed the group but need to reindex, mark the group as
      deleted.  */
   bool deleted;
+  /* The group points to an already-created "GOMP_MAP_STRUCT
+     GOMP_MAP_ATTACH_DETACH" pair.  */
+  bool reprocess_struct;
+  /* The group should use "zero-length" allocations for pointers that are not
+     mapped "to" on the same directive.  */
+  bool fragile;
   struct omp_mapping_group *sibling;
   struct omp_mapping_group *next;
 };
@@ -9328,38 +9333,6 @@ omp_get_base_pointer (tree expr)
   return NULL_TREE;
 }
 
-/* Remove COMPONENT_REFS and indirections from EXPR.  */
-
-static tree
-omp_strip_components_and_deref (tree expr)
-{
-  while (TREE_CODE (expr) == COMPONENT_REF
-	 || INDIRECT_REF_P (expr)
-	 || (TREE_CODE (expr) == MEM_REF
-	     && integer_zerop (TREE_OPERAND (expr, 1)))
-	 || TREE_CODE (expr) == POINTER_PLUS_EXPR
-	 || TREE_CODE (expr) == COMPOUND_EXPR)
-      if (TREE_CODE (expr) == COMPOUND_EXPR)
-	expr = TREE_OPERAND (expr, 1);
-      else
-	expr = TREE_OPERAND (expr, 0);
-
-  STRIP_NOPS (expr);
-
-  return expr;
-}
-
-static tree
-omp_strip_indirections (tree expr)
-{
-  while (INDIRECT_REF_P (expr)
-	 || (TREE_CODE (expr) == MEM_REF
-	     && integer_zerop (TREE_OPERAND (expr, 1))))
-    expr = TREE_OPERAND (expr, 0);
-
-  return expr;
-}
-
 /* An attach or detach operation depends directly on the address being
    attached/detached.  Return that address, or none if there are no
    attachments/detachments.  */
@@ -9413,6 +9386,7 @@ omp_get_attachment (omp_mapping_group *grp)
 
 	  case GOMP_MAP_ATTACH_DETACH:
 	  case GOMP_MAP_ATTACH_ZERO_LENGTH_ARRAY_SECTION:
+	  case GOMP_MAP_DETACH:
 	    return OMP_CLAUSE_DECL (node);
 
 	  default:
@@ -9488,23 +9462,43 @@ omp_group_last (tree *start_p)
 		     == GOMP_MAP_POINTER_TO_ZERO_LENGTH_ARRAY_SECTION)
 		 || (OMP_CLAUSE_MAP_KIND (nc)
 		     == GOMP_MAP_ATTACH_ZERO_LENGTH_ARRAY_SECTION)
+		 || OMP_CLAUSE_MAP_KIND (nc) == GOMP_MAP_DETACH
 		 || OMP_CLAUSE_MAP_KIND (nc) == GOMP_MAP_ALWAYS_POINTER
 		 || OMP_CLAUSE_MAP_KIND (nc) == GOMP_MAP_TO_PSET))
 	{
-	  grp_last_p = &OMP_CLAUSE_CHAIN (c);
-	  c = nc;
 	  tree nc2 = OMP_CLAUSE_CHAIN (nc);
+	  if (OMP_CLAUSE_MAP_KIND (nc) == GOMP_MAP_DETACH)
+	    {
+	      /* In the specific case we're doing "exit data" on an array
+		 slice of a reference-to-pointer struct component, we will see
+		 DETACH followed by ATTACH_DETACH here.  We want to treat that
+		 as a single group. In other cases DETACH might represent a
+		 stand-alone "detach" clause, so we don't want to consider
+		 that part of the group.  */
+	      if (nc2
+		  && OMP_CLAUSE_CODE (nc2) == OMP_CLAUSE_MAP
+		  && OMP_CLAUSE_MAP_KIND (nc2) == GOMP_MAP_ATTACH_DETACH)
+		goto consume_two_nodes;
+	      else
+		break;
+	    }
 	  if (nc2
 	      && OMP_CLAUSE_CODE (nc2) == OMP_CLAUSE_MAP
 	      && (OMP_CLAUSE_MAP_KIND (nc)
 		  == GOMP_MAP_POINTER_TO_ZERO_LENGTH_ARRAY_SECTION)
 	      && OMP_CLAUSE_MAP_KIND (nc2) == GOMP_MAP_ATTACH)
 	    {
+	    consume_two_nodes:
 	      grp_last_p = &OMP_CLAUSE_CHAIN (nc);
 	      c = nc2;
-	      nc2 = OMP_CLAUSE_CHAIN (nc2);
+	      nc = OMP_CLAUSE_CHAIN (nc2);
+	    }
+	  else
+	    {
+	      grp_last_p = &OMP_CLAUSE_CHAIN (c);
+	      c = nc;
+	      nc = nc2;
 	    }
-	   nc = nc2;
 	}
       break;
 
@@ -9568,6 +9562,8 @@ omp_gather_mapping_groups_1 (tree *list_p, vec<omp_mapping_group> *groups,
       grp.mark = UNVISITED;
       grp.sibling = NULL;
       grp.deleted = false;
+      grp.reprocess_struct = false;
+      grp.fragile = false;
       grp.next = NULL;
       groups->safe_push (grp);
 
@@ -9654,6 +9650,7 @@ omp_group_base (omp_mapping_group *grp, unsigned int *chained,
 	  case GOMP_MAP_ALWAYS_POINTER:
 	  case GOMP_MAP_ATTACH_DETACH:
 	  case GOMP_MAP_ATTACH_ZERO_LENGTH_ARRAY_SECTION:
+	  case GOMP_MAP_DETACH:
 	    return *grp->grp_start;
 
 	  default:
@@ -9702,6 +9699,8 @@ omp_group_base (omp_mapping_group *grp, unsigned int *chained,
 	    *firstprivate = OMP_CLAUSE_DECL (node);
 	    node = OMP_CLAUSE_CHAIN (node);
 	  }
+	else if (OMP_CLAUSE_MAP_KIND (node) == GOMP_MAP_ATTACH_DETACH)
+	  node = OMP_CLAUSE_CHAIN (node);
 	*chained = num_mappings;
 	return node;
       }
@@ -9753,6 +9752,9 @@ omp_index_mapping_groups_1 (hash_map<tree_operand_hash_no_se,
       if (reindexing && !above_hwm)
 	continue;
 
+      if (grp->reprocess_struct)
+	continue;
+
       tree fpp;
       unsigned int chained;
       tree node = omp_group_base (grp, &chained, &fpp);
@@ -10245,6 +10247,89 @@ omp_lastprivate_for_combined_outer_constructs (struct gimplify_omp_ctx *octx,
     omp_notice_variable (octx, decl, true);
 }
 
+/* We might have indexed several groups for DECL, e.g. a "TO" mapping and also
+   a "FIRSTPRIVATE" mapping.  Return the one that isn't firstprivate, etc.  */
+
+static omp_mapping_group *
+omp_get_nonfirstprivate_group (hash_map<tree_operand_hash_no_se,
+					omp_mapping_group *> *grpmap,
+			       tree decl, bool allow_deleted = false)
+{
+  omp_mapping_group **to_group_p = grpmap->get (decl);
+
+  if (!to_group_p)
+    return NULL;
+
+  omp_mapping_group *to_group = *to_group_p;
+
+  for (; to_group; to_group = to_group->sibling)
+    {
+      tree grp_end = to_group->grp_end;
+      switch (OMP_CLAUSE_MAP_KIND (grp_end))
+	{
+	case GOMP_MAP_FIRSTPRIVATE_POINTER:
+	case GOMP_MAP_FIRSTPRIVATE_REFERENCE:
+	  break;
+
+	default:
+	  if (allow_deleted || !to_group->deleted)
+	    return to_group;
+	}
+    }
+
+  return NULL;
+}
+
+/* Return TRUE if the directive (whose clauses are described by the hash table
+   of mapping groups, GRPMAP) maps DECL explicitly.  If TO_SPECIFICALLY is
+   true, only count TO mappings.  If ALLOW_DELETED is true, ignore the
+   "deleted" flag for groups.  If CONTAINED_IN_STRUCT is true, also return
+   TRUE if DECL is mapped as a member of a whole-struct mapping.  */
+
+static bool
+omp_directive_maps_explicitly (hash_map<tree_operand_hash_no_se,
+					omp_mapping_group *> *grpmap,
+			       tree decl, omp_mapping_group **base_group,
+			       bool to_specifically, bool allow_deleted,
+			       bool contained_in_struct)
+{
+  omp_mapping_group *decl_group
+    = omp_get_nonfirstprivate_group (grpmap, decl, allow_deleted);
+
+  *base_group = NULL;
+
+  if (decl_group)
+    {
+      tree grp_first = *decl_group->grp_start;
+      /* We might be called during omp_build_struct_sibling_lists, when
+	 GOMP_MAP_STRUCT might have been inserted at the start of the group.
+	 Skip over that, and also possibly the node after it.  */
+      if (OMP_CLAUSE_MAP_KIND (grp_first) == GOMP_MAP_STRUCT)
+	{
+	  grp_first = OMP_CLAUSE_CHAIN (grp_first);
+	  if (OMP_CLAUSE_MAP_KIND (grp_first) == GOMP_MAP_FIRSTPRIVATE_POINTER
+	      || (OMP_CLAUSE_MAP_KIND (grp_first)
+		  == GOMP_MAP_FIRSTPRIVATE_REFERENCE)
+	      || OMP_CLAUSE_MAP_KIND (grp_first) == GOMP_MAP_ATTACH_DETACH)
+	    grp_first = OMP_CLAUSE_CHAIN (grp_first);
+	}
+      enum gomp_map_kind first_kind = OMP_CLAUSE_MAP_KIND (grp_first);
+      if (!to_specifically
+	  || GOMP_MAP_COPY_TO_P (first_kind)
+	  || first_kind == GOMP_MAP_ALLOC)
+	{
+	  *base_group = decl_group;
+	  return true;
+	}
+    }
+
+  if (contained_in_struct
+      && omp_mapped_by_containing_struct (grpmap, decl, base_group))
+    return true;
+
+  return false;
+}
+
 /* If we have mappings INNER and OUTER, where INNER is a component access and
    OUTER is a mapping of the whole containing struct, check that the mappings
    are compatible.  We'll be deleting the inner mapping, so we need to make
@@ -10278,18 +10363,23 @@ omp_check_mapping_compatibility (location_t loc,
 
     case GOMP_MAP_ALWAYS_FROM:
       if (inner_kind == GOMP_MAP_FORCE_PRESENT
-	  || inner_kind == GOMP_MAP_ALLOC
+	  || inner_kind == GOMP_MAP_RELEASE
 	  || inner_kind == GOMP_MAP_FROM)
 	return true;
       break;
 
     case GOMP_MAP_TO:
-    case GOMP_MAP_FROM:
       if (inner_kind == GOMP_MAP_FORCE_PRESENT
 	  || inner_kind == GOMP_MAP_ALLOC)
 	return true;
       break;
 
+    case GOMP_MAP_FROM:
+      if (inner_kind == GOMP_MAP_RELEASE
+	  || inner_kind == GOMP_MAP_FORCE_PRESENT)
+	return true;
+      break;
+
     case GOMP_MAP_ALWAYS_TOFROM:
     case GOMP_MAP_TOFROM:
       if (inner_kind == GOMP_MAP_FORCE_PRESENT
@@ -10311,6 +10401,261 @@ omp_check_mapping_compatibility (location_t loc,
   return false;
 }
 
+/* This function handles several cases where clauses on a mapping directive
+   can interact with each other.
+
+   If we have a FIRSTPRIVATE_POINTER node and we're also mapping the pointer
+   on the same directive, change the mapping of the first node to
+   ATTACH_DETACH.  We should have detected that this will happen already in
+   c-omp.cc:c_omp_adjust_map_clauses and marked the appropriate decl
+   as addressable.  (If we didn't, bail out.)
+
+   If we have a FIRSTPRIVATE_REFERENCE (for a reference to pointer) and we're
+   mapping the base pointer also, we may need to change the mapping type to
+   ATTACH_DETACH and synthesize an alloc node for the reference itself.
+
+   If we have an ATTACH_DETACH node, this is an array section with a pointer
+   base.  If we're mapping the base on the same directive too, we can drop its
+   mapping.  However, if we have a reference to pointer, make other appropriate
+   adjustments to the mapping nodes instead.
+
+   If we have a component access but we're also mapping the whole of the
+   containing struct, drop the former access.
+
+   If the expression is a component access, and we're also mapping a base
+   pointer used in that component access in the same expression, change the
+   mapping type of the latter to ALLOC (ready for processing by
+   omp_build_struct_sibling_lists).  */
+
+void
+omp_resolve_clause_dependencies (enum tree_code code,
+				 vec<omp_mapping_group> *groups,
+				 hash_map<tree_operand_hash_no_se,
+					  omp_mapping_group *> *grpmap)
+{
+  int i;
+  omp_mapping_group *grp;
+  bool repair_chain = false;
+
+  FOR_EACH_VEC_ELT (*groups, i, grp)
+    {
+      tree grp_end = grp->grp_end;
+      tree decl = OMP_CLAUSE_DECL (grp_end);
+
+      gcc_assert (OMP_CLAUSE_CODE (grp_end) == OMP_CLAUSE_MAP);
+
+      switch (OMP_CLAUSE_MAP_KIND (grp_end))
+	{
+	case GOMP_MAP_FIRSTPRIVATE_POINTER:
+	  {
+	    omp_mapping_group *to_group
+	      = omp_get_nonfirstprivate_group (grpmap, decl);
+
+	    if (!to_group || to_group == grp)
+	      continue;
+
+	    tree grp_first = *to_group->grp_start;
+	    enum gomp_map_kind first_kind = OMP_CLAUSE_MAP_KIND (grp_first);
+
+	    if ((GOMP_MAP_COPY_TO_P (first_kind)
+		 || first_kind == GOMP_MAP_ALLOC)
+		&& (OMP_CLAUSE_MAP_KIND (to_group->grp_end)
+		    != GOMP_MAP_FIRSTPRIVATE_POINTER))
+	      {
+		gcc_assert (TREE_ADDRESSABLE (OMP_CLAUSE_DECL (grp_end)));
+		OMP_CLAUSE_SET_MAP_KIND (grp_end, GOMP_MAP_ATTACH_DETACH);
+	      }
+	  }
+	  break;
+
+	case GOMP_MAP_FIRSTPRIVATE_REFERENCE:
+	  {
+	    tree ptr = build_fold_indirect_ref (decl);
+
+	    omp_mapping_group *to_group
+	      = omp_get_nonfirstprivate_group (grpmap, ptr);
+
+	    if (!to_group || to_group == grp)
+	      continue;
+
+	    tree grp_first = *to_group->grp_start;
+	    enum gomp_map_kind first_kind = OMP_CLAUSE_MAP_KIND (grp_first);
+
+	    if (GOMP_MAP_COPY_TO_P (first_kind)
+		|| first_kind == GOMP_MAP_ALLOC)
+	      {
+		OMP_CLAUSE_SET_MAP_KIND (grp_end, GOMP_MAP_ATTACH_DETACH);
+		OMP_CLAUSE_DECL (grp_end) = ptr;
+		if ((OMP_CLAUSE_CHAIN (*to_group->grp_start)
+		     == to_group->grp_end)
+		    && (OMP_CLAUSE_MAP_KIND (to_group->grp_end)
+			== GOMP_MAP_FIRSTPRIVATE_REFERENCE))
+		  {
+		    gcc_assert (TREE_ADDRESSABLE
+				  (OMP_CLAUSE_DECL (to_group->grp_end)));
+		    OMP_CLAUSE_SET_MAP_KIND (to_group->grp_end,
+					     GOMP_MAP_ATTACH_DETACH);
+
+		    location_t loc = OMP_CLAUSE_LOCATION (to_group->grp_end);
+		    tree alloc
+		      = build_omp_clause (loc, OMP_CLAUSE_MAP);
+		    OMP_CLAUSE_SET_MAP_KIND (alloc, GOMP_MAP_ALLOC);
+		    tree tmp = build_fold_addr_expr (OMP_CLAUSE_DECL
+						      (to_group->grp_end));
+		    tree char_ptr_type = build_pointer_type (char_type_node);
+		    OMP_CLAUSE_DECL (alloc)
+		      = build2 (MEM_REF, char_type_node,
+				tmp,
+				build_int_cst (char_ptr_type, 0));
+		    OMP_CLAUSE_SIZE (alloc) = TYPE_SIZE_UNIT (TREE_TYPE (tmp));
+
+		    OMP_CLAUSE_CHAIN (alloc)
+		      = OMP_CLAUSE_CHAIN (*to_group->grp_start);
+		    OMP_CLAUSE_CHAIN (*to_group->grp_start) = alloc;
+		  }
+	      }
+	  }
+	  break;
+
+	case GOMP_MAP_ATTACH_DETACH:
+	case GOMP_MAP_ATTACH_ZERO_LENGTH_ARRAY_SECTION:
+	  {
+	    tree base_ptr, referenced_ptr_node = NULL_TREE;
+
+	    while (TREE_CODE (decl) == ARRAY_REF)
+	      decl = TREE_OPERAND (decl, 0);
+
+	    if (TREE_CODE (decl) == INDIRECT_REF)
+	      decl = TREE_OPERAND (decl, 0);
+
+	    /* Only component accesses.  */
+	    if (DECL_P (decl))
+	      continue;
+
+	    /* We want the pointer itself when checking if the base pointer is
+	       mapped elsewhere in the same directive -- if we have a
+	       reference to the pointer, don't use that.  */
+
+	    if (TREE_CODE (TREE_TYPE (decl)) == REFERENCE_TYPE
+		&& TREE_CODE (TREE_TYPE (TREE_TYPE (decl))) == POINTER_TYPE)
+	      {
+		referenced_ptr_node = OMP_CLAUSE_CHAIN (*grp->grp_start);
+		base_ptr = OMP_CLAUSE_DECL (referenced_ptr_node);
+	      }
+	    else
+	      base_ptr = decl;
+
+	    gomp_map_kind zlas_kind
+	      = (code == OACC_EXIT_DATA || code == OMP_TARGET_EXIT_DATA)
+		? GOMP_MAP_DETACH : GOMP_MAP_ATTACH_ZERO_LENGTH_ARRAY_SECTION;
+
+	    if (TREE_CODE (TREE_TYPE (base_ptr)) == POINTER_TYPE)
+	      {
+		/* If we map the base TO, and we're doing an attachment, we can
+		   skip the TO mapping altogether and create an ALLOC mapping
+		   instead, since the attachment will overwrite the device
+		   pointer in that location immediately anyway.  Otherwise,
+		   change our mapping to
+		   GOMP_MAP_ATTACH_ZERO_LENGTH_ARRAY_SECTION in case the
+		   attachment target has not been copied to the device already
+		   by some earlier directive.  */
+
+		bool base_mapped_to = false;
+
+		omp_mapping_group *base_group;
+
+		if (omp_directive_maps_explicitly (grpmap, base_ptr,
+						   &base_group, false, true,
+						   false))
+		  {
+		    if (referenced_ptr_node)
+		      {
+			base_mapped_to = true;
+			if ((OMP_CLAUSE_MAP_KIND (base_group->grp_end)
+			     == GOMP_MAP_ATTACH_DETACH)
+			    && (OMP_CLAUSE_CHAIN (*base_group->grp_start)
+				== base_group->grp_end))
+			  {
+			    OMP_CLAUSE_CHAIN (*base_group->grp_start)
+			      = OMP_CLAUSE_CHAIN (base_group->grp_end);
+			    base_group->grp_end = *base_group->grp_start;
+			    repair_chain = true;
+			  }
+		      }
+		    else
+		      {
+			base_group->deleted = true;
+			OMP_CLAUSE_ATTACHMENT_MAPPING_ERASED (grp_end) = 1;
+		      }
+		  }
+
+		/* We're dealing with a reference to a pointer, and we are
+		   attaching both the reference and the pointer.  We know the
+		   reference itself is on the target, because we are going to
+		   create an ALLOC node for it in accumulate_sibling_list.  The
+		   pointer might be on the target already or it might not, but
+		   if it isn't then it's not an error, so use
+		   GOMP_MAP_ATTACH_ZLAS for it.  */
+		if (!base_mapped_to && referenced_ptr_node)
+		  OMP_CLAUSE_SET_MAP_KIND (referenced_ptr_node, zlas_kind);
+	      }
+	    else if (TREE_CODE (TREE_TYPE (base_ptr)) == REFERENCE_TYPE
+		     && (TREE_CODE (TREE_TYPE (TREE_TYPE (base_ptr)))
+			 == ARRAY_TYPE)
+		     && OMP_CLAUSE_MAP_MAYBE_ZERO_LENGTH_ARRAY_SECTION
+			  (*grp->grp_start))
+	      OMP_CLAUSE_SET_MAP_KIND (grp->grp_end, zlas_kind);
+	  }
+	  break;
+
+	case GOMP_MAP_ATTACH:
+	  /* Ignore standalone attach here.  */
+	  break;
+
+	default:
+	  {
+	    omp_mapping_group *struct_group;
+	    if (omp_mapped_by_containing_struct (grpmap, decl, &struct_group)
+		&& *grp->grp_start == grp_end)
+	      {
+		omp_check_mapping_compatibility (OMP_CLAUSE_LOCATION (grp_end),
+						 struct_group, grp);
+		/* Remove the whole of this mapping -- redundant.  */
+		grp->deleted = true;
+	      }
+
+	    tree base = decl;
+	    while ((base = omp_get_base_pointer (base)))
+	      {
+		omp_mapping_group *base_group;
+
+		if (omp_directive_maps_explicitly (grpmap, base, &base_group,
+						   true, true, false))
+		  {
+		    tree grp_first = *base_group->grp_start;
+		    OMP_CLAUSE_SET_MAP_KIND (grp_first, GOMP_MAP_ALLOC);
+		  }
+	      }
+	  }
+	}
+    }
+
+  if (repair_chain)
+    {
+      /* Group start pointers may have become detached from the
+	 OMP_CLAUSE_CHAIN of previous groups if elements were removed from the
+	 end of those groups.  Fix that now.  */
+      tree *new_next = NULL;
+      FOR_EACH_VEC_ELT (*groups, i, grp)
+	{
+	  if (new_next)
+	    grp->grp_start = new_next;
+
+	  new_next = &OMP_CLAUSE_CHAIN (grp->grp_end);
+	}
+    }
+}
+
 /* Similar to omp_resolve_clause_dependencies, but for OpenACC.  The only
    clause dependencies we handle for now are struct element mappings and
    whole-struct mappings on the same directive, and duplicate clause
@@ -10528,6 +10873,19 @@ omp_siblist_move_concat_nodes_after (tree first_new, tree *last_new_tail,
   return continue_at;
 }
 
+static omp_addr_token *
+omp_first_chained_access_token (vec<omp_addr_token *> &addr_tokens)
+{
+  using namespace omp_addr_tokenizer;
+  int idx = addr_tokens.length () - 1;
+  gcc_assert (idx >= 0);
+  if (addr_tokens[idx]->type != ACCESS_METHOD)
+    return addr_tokens[idx];
+  while (idx > 0 && addr_tokens[idx - 1]->type == ACCESS_METHOD)
+    idx--;
+  return addr_tokens[idx];
+}
+
 /* Mapping struct members causes an additional set of nodes to be created,
    starting with GOMP_MAP_STRUCT followed by a number of mappings equal to the
    number of members being mapped, in order of ascending position (address or
@@ -10569,129 +10927,285 @@ static tree *
 omp_accumulate_sibling_list (enum omp_region_type region_type,
 			     enum tree_code code,
 			     hash_map<tree_operand_hash, tree>
-			       *&struct_map_to_clause, tree *grp_start_p,
-			     tree grp_end, tree *inner)
+			       *&struct_map_to_clause,
+			     hash_map<tree_operand_hash_no_se,
+				      omp_mapping_group *> *group_map,
+			     tree *grp_start_p, tree grp_end,
+			     vec<omp_addr_token *> &addr_tokens, tree **inner,
+			     bool *fragile_p, bool reprocessing_struct,
+			     tree **added_tail)
 {
+  using namespace omp_addr_tokenizer;
   poly_offset_int coffset;
   poly_int64 cbitpos;
   tree ocd = OMP_CLAUSE_DECL (grp_end);
   bool openmp = !(region_type & ORT_ACC);
+  bool target = (region_type & ORT_TARGET) != 0;
   tree *continue_at = NULL;
 
   while (TREE_CODE (ocd) == ARRAY_REF)
     ocd = TREE_OPERAND (ocd, 0);
 
-  if (INDIRECT_REF_P (ocd))
-    ocd = TREE_OPERAND (ocd, 0);
+  if (*fragile_p)
+    {
+      omp_mapping_group *to_group
+	= omp_get_nonfirstprivate_group (group_map, ocd, true);
+
+      if (to_group)
+	return NULL;
+    }
+
+  omp_addr_token *last_token = omp_first_chained_access_token (addr_tokens);
+  if (last_token->type == ACCESS_METHOD)
+    {
+      switch (last_token->u.access_kind)
+	{
+	case ACCESS_REF:
+	case ACCESS_REF_TO_POINTER:
+	case ACCESS_REF_TO_POINTER_OFFSET:
+	case ACCESS_INDEXED_REF_TO_ARRAY:
+	  /* We may see either a bare reference or a dereferenced
+	     "convert_from_reference"-like one here.  Handle either way.  */
+	  if (TREE_CODE (ocd) == INDIRECT_REF)
+	    ocd = TREE_OPERAND (ocd, 0);
+	  gcc_assert (TREE_CODE (TREE_TYPE (ocd)) == REFERENCE_TYPE);
+	  break;
+
+	default:
+	  ;
+	}
+    }
 
   tree base = extract_base_bit_offset (ocd, &cbitpos, &coffset);
 
+  int base_token;
+  for (base_token = addr_tokens.length () - 1; base_token >= 0; base_token--)
+    {
+      if (addr_tokens[base_token]->type == ARRAY_BASE
+	  || addr_tokens[base_token]->type == STRUCTURE_BASE)
+	break;
+    }
+
+  /* The two expressions in the assertion below aren't quite the same: if we
+     have 'struct_base_decl access_indexed_array' for something like
+     "myvar[2].x" then base will be "myvar" and addr_tokens[base_token]->expr
+     will be "myvar[2]" -- the actual base of the structure.
+     The former interpretation leads to a strange situation where we get
+       struct(myvar) alloc(myvar[2].ptr1)
+     That is, the array of structures is kind of treated as one big structure
+     for the purposes of gathering sibling lists, etc.  */
+  /* gcc_assert (base == addr_tokens[base_token]->expr);  */
+
   bool ptr = (OMP_CLAUSE_MAP_KIND (grp_end) == GOMP_MAP_ALWAYS_POINTER);
   bool attach_detach = ((OMP_CLAUSE_MAP_KIND (grp_end)
 			 == GOMP_MAP_ATTACH_DETACH)
 			|| (OMP_CLAUSE_MAP_KIND (grp_end)
 			    == GOMP_MAP_ATTACH_ZERO_LENGTH_ARRAY_SECTION));
-  bool attach = (OMP_CLAUSE_MAP_KIND (grp_end) == GOMP_MAP_ATTACH
-		 || OMP_CLAUSE_MAP_KIND (grp_end) == GOMP_MAP_DETACH);
-
-  /* FIXME: If we're not mapping the base pointer in some other clause on this
-     directive, I think we want to create ALLOC/RELEASE here -- i.e. not
-     early-exit.  */
-  if (openmp && attach_detach)
-    return NULL;
 
   if (!struct_map_to_clause || struct_map_to_clause->get (base) == NULL)
     {
       tree l = build_omp_clause (OMP_CLAUSE_LOCATION (grp_end), OMP_CLAUSE_MAP);
-      gomp_map_kind k = attach ? GOMP_MAP_FORCE_PRESENT : GOMP_MAP_STRUCT;
-
-      OMP_CLAUSE_SET_MAP_KIND (l, k);
 
+      OMP_CLAUSE_SET_MAP_KIND (l, GOMP_MAP_STRUCT);
       OMP_CLAUSE_DECL (l) = unshare_expr (base);
+      OMP_CLAUSE_SIZE (l) = size_int (1);
 
-      OMP_CLAUSE_SIZE (l)
-	= (!attach ? size_int (1)
-	   : (DECL_P (OMP_CLAUSE_DECL (l))
-	      ? DECL_SIZE_UNIT (OMP_CLAUSE_DECL (l))
-	      : TYPE_SIZE_UNIT (TREE_TYPE (OMP_CLAUSE_DECL (l)))));
       if (struct_map_to_clause == NULL)
 	struct_map_to_clause = new hash_map<tree_operand_hash, tree>;
       struct_map_to_clause->put (base, l);
 
+      /* On first iterating through the clause list, we insert the struct node
+	 just before the component access node that triggers the initial
+	 omp_accumulate_sibling_list call for a particular sibling list (and
+	 it then forms the first entry in that list).  When reprocessing
+	 struct bases that are themselves component accesses, we insert the
+	 struct node on an off-side list to avoid inserting the new
+	 GOMP_MAP_STRUCT into the middle of the old one.  */
+      tree *insert_node_pos = reprocessing_struct ? *added_tail : grp_start_p;
+
       if (ptr || attach_detach)
 	{
 	  tree extra_node;
 	  tree alloc_node
 	    = build_omp_struct_comp_nodes (code, *grp_start_p, grp_end,
 					   &extra_node);
+	  tree *tail;
 	  OMP_CLAUSE_CHAIN (l) = alloc_node;
 
-	  tree *insert_node_pos = grp_start_p;
-
 	  if (extra_node)
 	    {
 	      OMP_CLAUSE_CHAIN (extra_node) = *insert_node_pos;
 	      OMP_CLAUSE_CHAIN (alloc_node) = extra_node;
+	      tail = &OMP_CLAUSE_CHAIN (extra_node);
 	    }
 	  else
-	    OMP_CLAUSE_CHAIN (alloc_node) = *insert_node_pos;
+	    {
+	      OMP_CLAUSE_CHAIN (alloc_node) = *insert_node_pos;
+	      tail = &OMP_CLAUSE_CHAIN (alloc_node);
+	    }
+
+	  /* For OpenMP semantics, we don't want to implicitly allocate
+	     space for the pointer here for non-compute regions (e.g. "enter
+	     data").  A FRAGILE_P node is only being created so that
+	     omp-low.cc is able to rewrite the struct properly.
+	     For references (to pointers), we want to actually allocate the
+	     space for the reference itself in the sorted list following the
+	     struct node.
+	     For pointers, we want to allocate space if we had an explicit
+	     mapping of the attachment point, but not otherwise.  */
+	  if (*fragile_p
+	      || (openmp
+		  && !target
+		  && attach_detach
+		  && TREE_CODE (TREE_TYPE (ocd)) == POINTER_TYPE
+		  && !OMP_CLAUSE_ATTACHMENT_MAPPING_ERASED (grp_end)))
+	    {
+	      if (!lang_GNU_Fortran ())
+	       /* In Fortran, pointers are dereferenced automatically, but may
+		  be unassociated.  So we still want to allocate space for the
+		  pointer (as the base for an attach operation that should be
+		  present in the same directive's clause list also).  */
+		OMP_CLAUSE_SIZE (alloc_node) = size_zero_node;
+	      OMP_CLAUSE_MAP_MAYBE_ZERO_LENGTH_ARRAY_SECTION (alloc_node) = 1;
+	    }
 
 	  *insert_node_pos = l;
+
+	  if (reprocessing_struct)
+	    {
+	      /* When reprocessing a struct node group used as the base of a
+		 subcomponent access, if we have a reference-to-pointer base,
+		 we will see:
+		   struct(**ptr) attach(*ptr)
+		 whereas for a non-reprocess-struct group, we see, e.g.:
+		   tofrom(**ptr) attach(*ptr) attach(ptr)
+		 and we create the "alloc" for the second "attach", i.e.
+		 for the reference itself.  When reprocessing a struct group we
+		 thus change the pointer attachment into a reference attachment
+		 by stripping the indirection.  (The attachment of the
+		 referenced pointer must happen elsewhere, either on the same
+		 directive, or otherwise.)  */
+	      tree adecl = OMP_CLAUSE_DECL (alloc_node);
+
+	      if ((TREE_CODE (adecl) == INDIRECT_REF
+		   || (TREE_CODE (adecl) == MEM_REF
+		       && integer_zerop (TREE_OPERAND (adecl, 1))))
+		  && (TREE_CODE (TREE_TYPE (TREE_OPERAND (adecl, 0)))
+		      == REFERENCE_TYPE)
+		  && (TREE_CODE (TREE_TYPE (TREE_TYPE
+			(TREE_OPERAND (adecl, 0)))) == POINTER_TYPE))
+		OMP_CLAUSE_DECL (alloc_node) = TREE_OPERAND (adecl, 0);
+
+	      *added_tail = tail;
+	    }
 	}
       else
 	{
 	  gcc_assert (*grp_start_p == grp_end);
-	  grp_start_p = omp_siblist_insert_node_after (l, grp_start_p);
+	  if (reprocessing_struct)
+	    {
+	      /* If we don't have an attach/detach node, this is a
+		 "target data" directive or similar, not an offload region.
+		 Synthesize an "alloc" node using just the initiating
+		 GOMP_MAP_STRUCT decl.  */
+	      gomp_map_kind k = (code == OMP_TARGET_EXIT_DATA
+				 || code == OACC_EXIT_DATA)
+				? GOMP_MAP_RELEASE : GOMP_MAP_ALLOC;
+	      tree alloc_node
+		= build_omp_clause (OMP_CLAUSE_LOCATION (grp_end),
+				    OMP_CLAUSE_MAP);
+	      OMP_CLAUSE_SET_MAP_KIND (alloc_node, k);
+	      OMP_CLAUSE_DECL (alloc_node) = unshare_expr (last_token->expr);
+	      OMP_CLAUSE_SIZE (alloc_node)
+		= TYPE_SIZE_UNIT (TREE_TYPE (OMP_CLAUSE_DECL (alloc_node)));
+
+	      OMP_CLAUSE_CHAIN (alloc_node) = OMP_CLAUSE_CHAIN (l);
+	      OMP_CLAUSE_CHAIN (l) = alloc_node;
+	      *insert_node_pos = l;
+	      *added_tail = &OMP_CLAUSE_CHAIN (alloc_node);
+	    }
+	  else
+	    grp_start_p = omp_siblist_insert_node_after (l, insert_node_pos);
 	}
 
-      tree noind = omp_strip_indirections (base);
+      unsigned last_access = base_token + 1;
 
-      if (!openmp
-	  && (region_type & ORT_TARGET)
-	  && TREE_CODE (noind) == COMPONENT_REF)
+      while (last_access + 1 < addr_tokens.length ()
+	     && addr_tokens[last_access + 1]->type == ACCESS_METHOD)
+	last_access++;
+
+      if ((region_type & ORT_TARGET)
+	  && addr_tokens[base_token + 1]->type == ACCESS_METHOD)
 	{
-	  /* The base for this component access is a struct component access
-	     itself.  Insert a node to be processed on the next iteration of
-	     our caller's loop, which will subsequently be turned into a new,
-	     inner GOMP_MAP_STRUCT mapping.
+	  bool base_ref = false;
+	  access_method_kinds access_kind
+	    = addr_tokens[last_access]->u.access_kind;
 
-	     We need to do this else the non-DECL_P base won't be
-	     rewritten correctly in the offloaded region.  */
+	  switch (access_kind)
+	    {
+	    case ACCESS_DIRECT:
+	    case ACCESS_INDEXED_ARRAY:
+	      return NULL;
+
+	    case ACCESS_REF:
+	    case ACCESS_REF_TO_POINTER:
+	    case ACCESS_REF_TO_POINTER_OFFSET:
+	    case ACCESS_INDEXED_REF_TO_ARRAY:
+	      base_ref = true;
+	      break;
+
+	    default:
+	      ;
+	    }
 	  tree c2 = build_omp_clause (OMP_CLAUSE_LOCATION (grp_end),
 				      OMP_CLAUSE_MAP);
-	  OMP_CLAUSE_SET_MAP_KIND (c2, GOMP_MAP_FORCE_PRESENT);
-	  OMP_CLAUSE_DECL (c2) = unshare_expr (noind);
-	  OMP_CLAUSE_SIZE (c2) = TYPE_SIZE_UNIT (TREE_TYPE (noind));
-	  *inner = c2;
-	  return NULL;
-	}
+	  enum gomp_map_kind mkind;
+	  omp_mapping_group *decl_group;
+	  tree use_base;
+	  switch (access_kind)
+	    {
+	    case ACCESS_POINTER:
+	    case ACCESS_POINTER_OFFSET:
+	      use_base = addr_tokens[last_access]->expr;
+	      break;
+	    case ACCESS_REF_TO_POINTER:
+	    case ACCESS_REF_TO_POINTER_OFFSET:
+	      use_base
+		= build_fold_indirect_ref (addr_tokens[last_access]->expr);
+	      break;
+	    default:
+	      use_base = addr_tokens[base_token]->expr;
+	    }
+	  bool mapped_to_p
+	    = omp_directive_maps_explicitly (group_map, use_base, &decl_group,
+					     true, false, true);
+	  if (addr_tokens[base_token]->type == STRUCTURE_BASE
+	      && DECL_P (addr_tokens[last_access]->expr)
+	      && !mapped_to_p)
+	    mkind = base_ref ? GOMP_MAP_FIRSTPRIVATE_REFERENCE
+			     : GOMP_MAP_FIRSTPRIVATE_POINTER;
+	  else
+	    mkind = GOMP_MAP_ATTACH_DETACH;
 
-      tree sdecl = omp_strip_components_and_deref (base);
-
-      if (POINTER_TYPE_P (TREE_TYPE (sdecl)) && (region_type & ORT_TARGET))
-	{
-	  tree c2 = build_omp_clause (OMP_CLAUSE_LOCATION (grp_end),
-				      OMP_CLAUSE_MAP);
-	  bool base_ref
-	    = (INDIRECT_REF_P (base)
-	       && ((TREE_CODE (TREE_TYPE (TREE_OPERAND (base, 0)))
-		    == REFERENCE_TYPE)
-		   || (INDIRECT_REF_P (TREE_OPERAND (base, 0))
-		       && (TREE_CODE (TREE_TYPE (TREE_OPERAND
-						  (TREE_OPERAND (base, 0), 0)))
-			   == REFERENCE_TYPE))));
-	  enum gomp_map_kind mkind = base_ref ? GOMP_MAP_FIRSTPRIVATE_REFERENCE
-					      : GOMP_MAP_FIRSTPRIVATE_POINTER;
 	  OMP_CLAUSE_SET_MAP_KIND (c2, mkind);
-	  OMP_CLAUSE_DECL (c2) = sdecl;
+	  /* If we have a reference to pointer base, we want to attach the
+	     pointer here, not the reference.  The reference attachment happens
+	     elsewhere.  */
+	  bool ref_to_ptr
+	    = (access_kind == ACCESS_REF_TO_POINTER
+	       || access_kind == ACCESS_REF_TO_POINTER_OFFSET);
+	  tree sdecl = addr_tokens[last_access]->expr;
+	  tree sdecl_ptr = ref_to_ptr ? build_fold_indirect_ref (sdecl)
+				      : sdecl;
+	  /* For the FIRSTPRIVATE_REFERENCE after the struct node, we
+	     want to use the reference itself for the decl, but we
+	     still want to use the pointer to calculate the bias.  */
+	  OMP_CLAUSE_DECL (c2) = (mkind == GOMP_MAP_ATTACH_DETACH)
+				 ? sdecl_ptr : sdecl;
+	  sdecl = sdecl_ptr;
 	  tree baddr = build_fold_addr_expr (base);
 	  baddr = fold_convert_loc (OMP_CLAUSE_LOCATION (grp_end),
 				    ptrdiff_type_node, baddr);
-	  /* This isn't going to be good enough when we add support for more
-	     complicated lvalue expressions.  FIXME.  */
-	  if (TREE_CODE (TREE_TYPE (sdecl)) == REFERENCE_TYPE
-	      && TREE_CODE (TREE_TYPE (TREE_TYPE (sdecl))) == POINTER_TYPE)
-	    sdecl = build_simple_mem_ref (sdecl);
 	  tree decladdr = fold_convert_loc (OMP_CLAUSE_LOCATION (grp_end),
 					    ptrdiff_type_node, sdecl);
 	  OMP_CLAUSE_SIZE (c2)
@@ -10700,24 +11214,46 @@ omp_accumulate_sibling_list (enum omp_region_type region_type,
 	  /* Insert after struct node.  */
 	  OMP_CLAUSE_CHAIN (c2) = OMP_CLAUSE_CHAIN (l);
 	  OMP_CLAUSE_CHAIN (l) = c2;
+
+	  if (addr_tokens[base_token]->type == STRUCTURE_BASE
+	      && (addr_tokens[base_token]->u.structure_base_kind
+		  == BASE_COMPONENT_EXPR)
+	      && mkind == GOMP_MAP_ATTACH_DETACH
+	      && addr_tokens[last_access]->u.access_kind != ACCESS_REF)
+	    {
+	      *inner = insert_node_pos;
+	      if (openmp)
+		*fragile_p = true;
+	      return NULL;
+	    }
 	}
 
+      if (addr_tokens[base_token]->type == STRUCTURE_BASE
+	  && (addr_tokens[base_token]->u.structure_base_kind
+	      == BASE_COMPONENT_EXPR)
+	  && addr_tokens[last_access]->u.access_kind == ACCESS_REF)
+	*inner = insert_node_pos;
+
       return NULL;
     }
   else if (struct_map_to_clause)
     {
       tree *osc = struct_map_to_clause->get (base);
       tree *sc = NULL, *scp = NULL;
+      unsigned HOST_WIDE_INT i, elems = tree_to_uhwi (OMP_CLAUSE_SIZE (*osc));
       sc = &OMP_CLAUSE_CHAIN (*osc);
       /* The struct mapping might be immediately followed by a
-	 FIRSTPRIVATE_POINTER and/or FIRSTPRIVATE_REFERENCE -- if it's an
-	 indirect access or a reference, or both.  (This added node is removed
-	 in omp-low.c after it has been processed there.)  */
-      if (*sc != grp_end
-	  && (OMP_CLAUSE_MAP_KIND (*sc) == GOMP_MAP_FIRSTPRIVATE_POINTER
-	      || OMP_CLAUSE_MAP_KIND (*sc) == GOMP_MAP_FIRSTPRIVATE_REFERENCE))
+	 FIRSTPRIVATE_POINTER,  FIRSTPRIVATE_REFERENCE or an ATTACH_DETACH --
+	 if it's an indirect access or a reference, or if the structure base
+	 is not a decl.  The FIRSTPRIVATE_* nodes are removed in omp-low.cc
+	 after they have been processed there, and ATTACH_DETACH nodes are
+	 recomputed and moved out of the GOMP_MAP_STRUCT construct once
+	 sibling list building is complete.  */
+      if (OMP_CLAUSE_MAP_KIND (*sc) == GOMP_MAP_FIRSTPRIVATE_POINTER
+	  || OMP_CLAUSE_MAP_KIND (*sc) == GOMP_MAP_FIRSTPRIVATE_REFERENCE
+	  || OMP_CLAUSE_MAP_KIND (*sc) == GOMP_MAP_ATTACH_DETACH)
 	sc = &OMP_CLAUSE_CHAIN (*sc);
-      for (; *sc != grp_end; sc = &OMP_CLAUSE_CHAIN (*sc))
+      for (i = 0; i < elems; i++, sc = &OMP_CLAUSE_CHAIN (*sc))
 	if ((ptr || attach_detach) && sc == grp_start_p)
 	  break;
 	else if (TREE_CODE (OMP_CLAUSE_DECL (*sc)) != COMPONENT_REF
@@ -10749,6 +11285,27 @@ omp_accumulate_sibling_list (enum omp_region_type region_type,
 	      break;
 	    if (scp)
 	      continue;
+	    if ((region_type & ORT_ACC) != 0)
+	      {
+		/* For OpenACC, allow (ignore) duplicate struct accesses in
+		   the middle of a mapping clause, e.g. "mystruct->foo" in:
+		   copy(mystruct->foo->bar) copy(mystruct->foo->qux).  */
+		if (reprocessing_struct
+		    && known_eq (coffset, offset)
+		    && known_eq (cbitpos, bitpos))
+		  return NULL;
+	      }
+	    else if (known_eq (coffset, offset)
+		     && known_eq (cbitpos, bitpos))
+	      {
+		/* Having two struct members at the same offset doesn't work,
+		   so make sure we don't.  (We're allowed to ignore this.
+		   Should we report the error?)  */
+		/*error_at (OMP_CLAUSE_LOCATION (grp_end),
+			  "duplicate struct member %qE in map clauses",
+			  OMP_CLAUSE_DECL (grp_end));*/
+		return NULL;
+	      }
 	    if (maybe_lt (coffset, offset)
 		|| (known_eq (coffset, offset)
 		    && maybe_lt (cbitpos, bitpos)))
@@ -10760,9 +11317,48 @@ omp_accumulate_sibling_list (enum omp_region_type region_type,
 	      }
 	  }
 
-      if (!attach)
-	OMP_CLAUSE_SIZE (*osc)
-	  = size_binop (PLUS_EXPR, OMP_CLAUSE_SIZE (*osc), size_one_node);
+      OMP_CLAUSE_SIZE (*osc)
+	= size_binop (PLUS_EXPR, OMP_CLAUSE_SIZE (*osc), size_one_node);
+
+      if (reprocessing_struct)
+	{
+	  /* If we're reprocessing a struct node, we don't want to do most of
+	     the list manipulation below.  We only need to handle the (pointer
+	     or reference) attach/detach case.  */
+	  tree extra_node, alloc_node;
+	  if (attach_detach)
+	    alloc_node = build_omp_struct_comp_nodes (code, *grp_start_p,
+						      grp_end, &extra_node);
+	  else
+	    {
+	      /* If we don't have an attach/detach node, this is a
+		 "target data" directive or similar, not an offload region.
+		 Synthesize an "alloc" node using just the initiating
+		 GOMP_MAP_STRUCT decl.  */
+	      gomp_map_kind k = (code == OMP_TARGET_EXIT_DATA
+				 || code == OACC_EXIT_DATA)
+				? GOMP_MAP_RELEASE : GOMP_MAP_ALLOC;
+	      alloc_node
+		= build_omp_clause (OMP_CLAUSE_LOCATION (grp_end),
+				    OMP_CLAUSE_MAP);
+	      OMP_CLAUSE_SET_MAP_KIND (alloc_node, k);
+	      OMP_CLAUSE_DECL (alloc_node) = unshare_expr (last_token->expr);
+	      OMP_CLAUSE_SIZE (alloc_node)
+		= TYPE_SIZE_UNIT (TREE_TYPE (OMP_CLAUSE_DECL (alloc_node)));
+	    }
+
+	  if (scp)
+	    omp_siblist_insert_node_after (alloc_node, scp);
+	  else
+	    {
+	      tree *new_end = omp_siblist_insert_node_after (alloc_node, sc);
+	      if (sc == *added_tail)
+		*added_tail = new_end;
+	    }
+
+	  return NULL;
+	}
+
       if (ptr || attach_detach)
 	{
 	  tree cl = NULL_TREE, extra_node;
@@ -10770,6 +11366,18 @@ omp_accumulate_sibling_list (enum omp_region_type region_type,
 							 grp_end, &extra_node);
 	  tree *tail_chain = NULL;
 
+	  if (*fragile_p
+	      || (openmp
+		  && !target
+		  && attach_detach
+		  && TREE_CODE (TREE_TYPE (ocd)) == POINTER_TYPE
+		  && !OMP_CLAUSE_ATTACHMENT_MAPPING_ERASED (grp_end)))
+	    {
+	      if (!lang_GNU_Fortran ())
+		OMP_CLAUSE_SIZE (alloc_node) = size_zero_node;
+	      OMP_CLAUSE_MAP_MAYBE_ZERO_LENGTH_ARRAY_SECTION (alloc_node) = 1;
+	    }
+
 	  /* Here, we have:
 
 	     grp_end : the last (or only) node in this group.
@@ -10855,12 +11463,15 @@ omp_build_struct_sibling_lists (enum tree_code code,
 					 omp_mapping_group *> **grpmap,
 				tree *list_p)
 {
+  using namespace omp_addr_tokenizer;
   unsigned i;
   omp_mapping_group *grp;
   hash_map<tree_operand_hash, tree> *struct_map_to_clause = NULL;
   bool success = true;
   tree *new_next = NULL;
   tree *tail = &OMP_CLAUSE_CHAIN ((*groups)[groups->length () - 1].grp_end);
+  tree added_nodes = NULL_TREE;
+  tree *added_tail = &added_nodes;
   auto_vec<omp_mapping_group> pre_hwm_groups;
 
   FOR_EACH_VEC_ELT (*groups, i, grp)
@@ -10868,9 +11479,10 @@ omp_build_struct_sibling_lists (enum tree_code code,
       tree c = grp->grp_end;
       tree decl = OMP_CLAUSE_DECL (c);
       tree grp_end = grp->grp_end;
+      auto_vec<omp_addr_token *> addr_tokens;
       tree sentinel = OMP_CLAUSE_CHAIN (grp_end);
 
-      if (new_next)
+      if (new_next && !grp->reprocess_struct)
 	grp->grp_start = new_next;
 
       new_next = NULL;
@@ -10881,7 +11493,7 @@ omp_build_struct_sibling_lists (enum tree_code code,
 	continue;
 
       /* Skip groups we marked for deletion in
-	 oacc_resolve_clause_dependencies.  */
+	 {omp,oacc}_resolve_clause_dependencies.  */
       if (grp->deleted)
 	continue;
 
@@ -10898,6 +11510,39 @@ omp_build_struct_sibling_lists (enum tree_code code,
 	    continue;
 	}
 
+      tree expr = decl;
+
+      while (TREE_CODE (expr) == ARRAY_REF)
+	expr = TREE_OPERAND (expr, 0);
+
+      if (!omp_parse_expr (addr_tokens, expr))
+	continue;
+
+      omp_addr_token *last_token
+	= omp_first_chained_access_token (addr_tokens);
+
+      /* A mapping of a reference to a pointer member that doesn't specify an
+	 array section, etc., like this:
+	   *mystruct.ref_to_ptr
+	 should not be processed by the struct sibling-list handling code --
+	 it just transfers the referenced pointer.
+
+	 In contrast, the quite similar-looking construct:
+	   *mystruct.ptr
+	 which is equivalent to e.g.
+	   mystruct.ptr[0]
+	 *does* trigger sibling-list processing.
+
+	 An exception for the former case is for "fragile" groups where the
+	 reference itself is not handled otherwise; this is subject to special
+	 handling in omp_accumulate_sibling_list also.  */
+
+      if (TREE_CODE (TREE_TYPE (decl)) == POINTER_TYPE
+	  && last_token->type == ACCESS_METHOD
+	  && last_token->u.access_kind == ACCESS_REF
+	  && !grp->fragile)
+	continue;
+
       tree d = decl;
       if (TREE_CODE (d) == ARRAY_REF)
 	{
@@ -10926,14 +11571,7 @@ omp_build_struct_sibling_lists (enum tree_code code,
       omp_mapping_group *wholestruct;
       if (omp_mapped_by_containing_struct (*grpmap, OMP_CLAUSE_DECL (c),
 					   &wholestruct))
-	{
-	  if (!(region_type & ORT_ACC)
-	      && *grp_start_p == grp_end)
-	    /* Remove the whole of this mapping -- redundant.  */
-	    grp->deleted = true;
-
-	  continue;
-	}
+	continue;
 
       if (OMP_CLAUSE_MAP_KIND (c) != GOMP_MAP_TO_PSET
 	  && OMP_CLAUSE_MAP_KIND (c) != GOMP_MAP_ATTACH
@@ -10960,27 +11598,30 @@ omp_build_struct_sibling_lists (enum tree_code code,
 	      goto error_out;
 	    }
 
-	  tree inner = NULL_TREE;
+	  tree *inner = NULL;
+	  bool fragile_p = grp->fragile;
 
 	  new_next
 	    = omp_accumulate_sibling_list (region_type, code,
-					   struct_map_to_clause, grp_start_p,
-					   grp_end, &inner);
+					   struct_map_to_clause, *grpmap,
+					   grp_start_p, grp_end, addr_tokens,
+					   &inner, &fragile_p,
+					   grp->reprocess_struct, &added_tail);
 
 	  if (inner)
 	    {
-	      if (new_next && *new_next == NULL_TREE)
-		*new_next = inner;
-	      else
-		*tail = inner;
-
-	      OMP_CLAUSE_CHAIN (inner) = NULL_TREE;
 	      omp_mapping_group newgrp;
-	      newgrp.grp_start = new_next ? new_next : tail;
-	      newgrp.grp_end = inner;
+	      newgrp.grp_start = inner;
+	      if (OMP_CLAUSE_MAP_KIND (OMP_CLAUSE_CHAIN (*inner))
+		  == GOMP_MAP_ATTACH_DETACH)
+		newgrp.grp_end = OMP_CLAUSE_CHAIN (*inner);
+	      else
+		newgrp.grp_end = *inner;
 	      newgrp.mark = UNVISITED;
 	      newgrp.sibling = NULL;
 	      newgrp.deleted = false;
+	      newgrp.reprocess_struct = true;
+	      newgrp.fragile = fragile_p;
 	      newgrp.next = NULL;
 	      groups->safe_push (newgrp);
 
@@ -10991,8 +11632,6 @@ omp_build_struct_sibling_lists (enum tree_code code,
 	      *grpmap
 		= omp_reindex_mapping_groups (list_p, groups, &pre_hwm_groups,
 					      sentinel);
-
-	      tail = &OMP_CLAUSE_CHAIN (inner);
 	    }
 	}
     }
@@ -11021,6 +11660,61 @@ omp_build_struct_sibling_lists (enum tree_code code,
 	tail = &OMP_CLAUSE_CHAIN (*tail);
     }
 
+  /* Tack on the struct nodes added during nested struct reprocessing.  */
+  if (added_nodes)
+    {
+      *tail = added_nodes;
+      tail = added_tail;
+    }
+
+  /* Now we have finished building the struct sibling lists, reprocess
+     newly-added "attach" nodes: we need the address of the first
+     mapped element of each struct sibling list for the bias of the attach
+     operation -- not necessarily the base address of the whole struct.  */
+  if (struct_map_to_clause)
+    for (hash_map<tree_operand_hash, tree>::iterator iter
+	   = struct_map_to_clause->begin ();
+	 iter != struct_map_to_clause->end ();
+	 ++iter)
+      {
+	tree struct_node = (*iter).second;
+	gcc_assert (OMP_CLAUSE_CODE (struct_node) == OMP_CLAUSE_MAP);
+	tree attach = OMP_CLAUSE_CHAIN (struct_node);
+
+	if (OMP_CLAUSE_CODE (attach) != OMP_CLAUSE_MAP
+	    || OMP_CLAUSE_MAP_KIND (attach) != GOMP_MAP_ATTACH_DETACH)
+	  continue;
+
+	OMP_CLAUSE_SET_MAP_KIND (attach, GOMP_MAP_ATTACH);
+
+	/* Sanity check: the standalone attach node will not work if we have
+	   an "enter data" operation (because for those, variables need to be
+	   mapped separately and attach nodes must be grouped together with the
+	   base they attach to).  We should only have created the
+	   ATTACH_DETACH node after GOMP_MAP_STRUCT for a target region, so
+	   this should never be true.  */
+	gcc_assert ((region_type & ORT_TARGET) != 0);
+
+	/* This is the first sorted node in the struct sibling list.  Use it
+	   to recalculate the correct bias to use.
+	   (&first_node - attach_decl).  */
+	tree first_node = OMP_CLAUSE_DECL (OMP_CLAUSE_CHAIN (attach));
+	first_node = build_fold_addr_expr (first_node);
+	first_node = fold_convert (ptrdiff_type_node, first_node);
+	tree attach_decl = OMP_CLAUSE_DECL (attach);
+	attach_decl = fold_convert (ptrdiff_type_node, attach_decl);
+	OMP_CLAUSE_SIZE (attach)
+	  = fold_build2 (MINUS_EXPR, ptrdiff_type_node, first_node,
+			 attach_decl);
+
+	/* Remove GOMP_MAP_ATTACH node from after struct node.  */
+	OMP_CLAUSE_CHAIN (struct_node) = OMP_CLAUSE_CHAIN (attach);
+	/* ...and re-insert it at the end of our clause list.  */
+	*tail = attach;
+	OMP_CLAUSE_CHAIN (attach) = NULL_TREE;
+	tail = &OMP_CLAUSE_CHAIN (attach);
+      }
+
 error_out:
   if (struct_map_to_clause)
     delete struct_map_to_clause;
@@ -11036,6 +11730,7 @@ gimplify_scan_omp_clauses (tree *list_p, gimple_seq *pre_p,
 			   enum omp_region_type region_type,
 			   enum tree_code code)
 {
+  using namespace omp_addr_tokenizer;
   struct gimplify_omp_ctx *ctx, *outer_ctx;
   tree c;
   tree *orig_list_p = list_p;
@@ -11081,6 +11776,7 @@ gimplify_scan_omp_clauses (tree *list_p, gimple_seq *pre_p,
 	  hash_map<tree_operand_hash_no_se, omp_mapping_group *> *grpmap;
 	  grpmap = omp_index_mapping_groups (groups);
 
+	  omp_resolve_clause_dependencies (code, groups, grpmap);
 	  omp_build_struct_sibling_lists (code, region_type, groups, &grpmap,
 					  list_p);
 
@@ -11177,6 +11873,7 @@ gimplify_scan_omp_clauses (tree *list_p, gimple_seq *pre_p,
       const char *check_non_private = NULL;
       unsigned int flags;
       tree decl;
+      auto_vec<omp_addr_token *, 10> addr_tokens;
 
       switch (OMP_CLAUSE_CODE (c))
 	{
@@ -11483,6 +12180,13 @@ gimplify_scan_omp_clauses (tree *list_p, gimple_seq *pre_p,
 
 	case OMP_CLAUSE_MAP:
 	  decl = OMP_CLAUSE_DECL (c);
+
+	  if (!omp_parse_expr (addr_tokens, decl))
+	    {
+	      remove = true;
+	      break;
+	    }
+
 	  if (error_operand_p (decl))
 	    remove = true;
 	  switch (code)
@@ -11492,13 +12196,18 @@ gimplify_scan_omp_clauses (tree *list_p, gimple_seq *pre_p,
 	    case OACC_DATA:
 	      if (TREE_CODE (TREE_TYPE (decl)) != ARRAY_TYPE)
 		break;
+	      goto check_firstprivate;
+	    case OACC_ENTER_DATA:
+	    case OACC_EXIT_DATA:
+	      if (OMP_CLAUSE_MAP_KIND (c) == GOMP_MAP_ATTACH_DETACH
+		  && addr_tokens[0]->type == ARRAY_BASE)
+		remove = true;
 	      /* FALLTHRU */
 	    case OMP_TARGET_DATA:
 	    case OMP_TARGET_ENTER_DATA:
 	    case OMP_TARGET_EXIT_DATA:
-	    case OACC_ENTER_DATA:
-	    case OACC_EXIT_DATA:
 	    case OACC_HOST_DATA:
+	    check_firstprivate:
 	      if (OMP_CLAUSE_MAP_KIND (c) == GOMP_MAP_FIRSTPRIVATE_POINTER
 		  || (OMP_CLAUSE_MAP_KIND (c)
 		      == GOMP_MAP_FIRSTPRIVATE_REFERENCE))
@@ -11528,8 +12237,19 @@ gimplify_scan_omp_clauses (tree *list_p, gimple_seq *pre_p,
 		}
 	    }
 	  if (OMP_CLAUSE_SIZE (c) == NULL_TREE)
-	    OMP_CLAUSE_SIZE (c) = DECL_P (decl) ? DECL_SIZE_UNIT (decl)
-				  : TYPE_SIZE_UNIT (TREE_TYPE (decl));
+	    {
+	      /* Sanity check: attach/detach map kinds use the size as a bias,
+		 and it's never right to use the decl size for such
+		 mappings.  */
+	      gcc_assert (OMP_CLAUSE_MAP_KIND (c) != GOMP_MAP_ATTACH
+			  && OMP_CLAUSE_MAP_KIND (c) != GOMP_MAP_DETACH
+			  && OMP_CLAUSE_MAP_KIND (c) != GOMP_MAP_FORCE_DETACH
+			  && OMP_CLAUSE_MAP_KIND (c) != GOMP_MAP_ATTACH_DETACH
+			  && (OMP_CLAUSE_MAP_KIND (c)
+			      != GOMP_MAP_ATTACH_ZERO_LENGTH_ARRAY_SECTION));
+	      OMP_CLAUSE_SIZE (c) = DECL_P (decl) ? DECL_SIZE_UNIT (decl)
+				    : TYPE_SIZE_UNIT (TREE_TYPE (decl));
+	    }
 	  if (gimplify_expr (&OMP_CLAUSE_SIZE (c), pre_p,
 			     NULL, is_gimple_val, fb_rvalue) == GS_ERROR)
 	    {
@@ -11550,26 +12270,22 @@ gimplify_scan_omp_clauses (tree *list_p, gimple_seq *pre_p,
 				  GOVD_FIRSTPRIVATE | GOVD_SEEN);
 	    }
 
-	  if (OMP_CLAUSE_MAP_KIND (c) == GOMP_MAP_STRUCT)
+	  if (OMP_CLAUSE_MAP_KIND (c) == GOMP_MAP_STRUCT
+	      && (addr_tokens[0]->type == STRUCTURE_BASE
+		  || addr_tokens[0]->type == ARRAY_BASE)
+	      && addr_tokens[0]->u.structure_base_kind == BASE_DECL)
 	    {
-	      tree base = omp_strip_components_and_deref (decl);
-	      if (DECL_P (base))
-		{
-		  decl = base;
-		  splay_tree_node n
-		    = splay_tree_lookup (ctx->variables,
-					 (splay_tree_key) decl);
-		  if (seen_error ()
-		      && n
-		      && (n->value & (GOVD_MAP | GOVD_FIRSTPRIVATE)) != 0)
-		    {
-		      remove = true;
-		      break;
-		    }
-		  flags = GOVD_MAP | GOVD_EXPLICIT;
+	      gcc_assert (addr_tokens[1]->type == ACCESS_METHOD);
+	      /* If we got to this struct via a chain of pointers, maybe we
+		 want to map it implicitly instead.  */
+	      if (omp_access_chain_p (addr_tokens, 1))
+		break;
+	      decl = addr_tokens[1]->expr;
+	      flags = GOVD_MAP | GOVD_EXPLICIT;
 
-		  goto do_add_decl;
-		}
+	      gcc_assert (addr_tokens[1]->u.access_kind != ACCESS_DIRECT
+			  || TREE_ADDRESSABLE (decl));
+	      goto do_add_decl;
 	    }
 
 	  if (TREE_CODE (decl) == TARGET_EXPR)
@@ -11800,6 +12516,42 @@ gimplify_scan_omp_clauses (tree *list_p, gimple_seq *pre_p,
 					     ? GOMP_MAP_DETACH
 					     : GOMP_MAP_ATTACH);
 	      OMP_CLAUSE_SET_MAP_KIND (c, map_kind);
+
+	      /* If we have attach/detach but the decl we have is a pointer to
+		 pointer, we're probably mapping the "base level" array
+		 implicitly.  Make sure we don't add the decl as if we mapped
+		 it explicitly.  That is,
+
+		   int **arr;
+		   [...]
+		   #pragma omp target map(arr[a][b:c])
+
+		 should *not* map "arr" explicitly.  That way we get a
+		 zero-length "alloc" mapping for it, and assuming it's been
+		 mapped by some previous directive, etc., things work as they
+		 should.  */
+
+	      tree basetype = TREE_TYPE (addr_tokens[0]->expr);
+
+	      if (TREE_CODE (basetype) == REFERENCE_TYPE)
+		basetype = TREE_TYPE (basetype);
+
+	      if (code == OMP_TARGET
+		  && addr_tokens[0]->type == ARRAY_BASE
+		  && addr_tokens[0]->u.structure_base_kind == BASE_DECL
+		  && TREE_CODE (basetype) == POINTER_TYPE
+		  && TREE_CODE (TREE_TYPE (basetype)) == POINTER_TYPE)
+		break;
+	    }
+	  else if ((code == OACC_ENTER_DATA
+		    || code == OACC_EXIT_DATA
+		    || code == OACC_PARALLEL)
+		   && OMP_CLAUSE_MAP_KIND (c) == GOMP_MAP_ATTACH_DETACH)
+	    {
+	      enum gomp_map_kind map_kind = (code == OACC_EXIT_DATA
+					     ? GOMP_MAP_DETACH
+					     : GOMP_MAP_ATTACH);
+	      OMP_CLAUSE_SET_MAP_KIND (c, map_kind);
 	    }
 
 	  goto do_add;
@@ -12708,7 +13460,7 @@ gimplify_adjust_omp_clauses_1 (splay_tree_node n, void *data)
       if (TREE_CODE (TREE_TYPE (decl)) == REFERENCE_TYPE
 	  && TREE_CODE (TREE_TYPE (TREE_TYPE (decl))) == POINTER_TYPE)
 	OMP_CLAUSE_DECL (clause)
-	  = build_simple_mem_ref_loc (input_location, decl);
+	  = build_fold_indirect_ref_loc (input_location, decl);
       OMP_CLAUSE_DECL (clause)
 	= build2 (MEM_REF, char_type_node, OMP_CLAUSE_DECL (clause),
 		  build_int_cst (build_pointer_type (char_type_node), 0));
@@ -12716,7 +13468,16 @@ gimplify_adjust_omp_clauses_1 (splay_tree_node n, void *data)
       OMP_CLAUSE_SIZE (nc) = size_zero_node;
       OMP_CLAUSE_SET_MAP_KIND (clause, GOMP_MAP_ALLOC);
       OMP_CLAUSE_MAP_MAYBE_ZERO_LENGTH_ARRAY_SECTION (clause) = 1;
-      OMP_CLAUSE_SET_MAP_KIND (nc, GOMP_MAP_FIRSTPRIVATE_POINTER);
+      tree dtype = TREE_TYPE (decl);
+      if (TREE_CODE (dtype) == REFERENCE_TYPE)
+	dtype = TREE_TYPE (dtype);
+      /* FIRSTPRIVATE_POINTER doesn't work well if we have a
+	 multiply-indirected pointer.  */
+      if (TREE_CODE (dtype) == POINTER_TYPE
+	  && TREE_CODE (TREE_TYPE (dtype)) == POINTER_TYPE)
+	OMP_CLAUSE_SET_MAP_KIND (nc, GOMP_MAP_POINTER);
+      else
+	OMP_CLAUSE_SET_MAP_KIND (nc, GOMP_MAP_FIRSTPRIVATE_POINTER);
       OMP_CLAUSE_CHAIN (nc) = chain;
       OMP_CLAUSE_CHAIN (clause) = nc;
       struct gimplify_omp_ctx *ctx = gimplify_omp_ctxp;
diff --git a/gcc/omp-general.cc b/gcc/omp-general.cc
index b88d5930aab..bd0392cc6f3 100644
--- a/gcc/omp-general.cc
+++ b/gcc/omp-general.cc
@@ -45,6 +45,8 @@ along with GCC; see the file COPYING3.  If not see
 #include "data-streamer.h"
 #include "streamer-hooks.h"
 #include "opts.h"
+#include "omp-general.h"
+#include "tree-pretty-print.h"
 
 enum omp_requires omp_requires_mask;
 
@@ -3155,4 +3157,427 @@ omp_runtime_api_call (const_tree fndecl)
   return omp_runtime_api_procname (IDENTIFIER_POINTER (declname));
 }
 
+namespace omp_addr_tokenizer {
+
+/* We scan an expression by recursive descent, and build a vector of
+   "omp_addr_token *" pointers representing a "parsed" version of the
+   expression.  The grammar we use is something like this:
+
+     expr0::
+       expr [section-access]
+
+     expr::
+	 structured-expr access-method
+       | array-base access-method
+
+     structured-expr::
+       structure-base component-selector
+
+     arbitrary-expr::
+       (anything else)
+
+     structure-base::
+	 DECL access-method
+       | structured-expr access-method
+       | arbitrary-expr access-method
+
+     array-base::
+	 DECL
+       | arbitrary-expr
+
+     access-method::
+	 DIRECT
+       | REF
+       | POINTER
+       | REF_TO_POINTER
+       | POINTER_OFFSET
+       | REF_TO_POINTER_OFFSET
+       | INDEXED_ARRAY
+       | INDEXED_REF_TO_ARRAY
+       | index-expr
+
+     index-expr::
+	 INDEX_EXPR access-method
+
+     component-selector::
+	 component-selector COMPONENT_REF
+       | component-selector ARRAY_REF
+       | COMPONENT_REF
+
+   This tokenized form is then used both in parsing, for OpenMP clause
+   expansion (for C and C++) and in gimplify.cc for sibling-list handling
+   (for C, C++ and Fortran).  */
+
+omp_addr_token::omp_addr_token (token_type t, tree e)
+  : type(t), expr(e)
+{
+}
+
+omp_addr_token::omp_addr_token (access_method_kinds k, tree e)
+  : type(ACCESS_METHOD), expr(e)
+{
+  u.access_kind = k;
+}
+
+omp_addr_token::omp_addr_token (token_type t, structure_base_kinds k, tree e)
+  : type(t), expr(e)
+{
+  u.structure_base_kind = k;
+}
+
+static bool
+omp_parse_component_selector (tree *expr0)
+{
+  tree expr = *expr0;
+  tree last_component = NULL_TREE;
+
+  while (TREE_CODE (expr) == COMPONENT_REF
+	 || TREE_CODE (expr) == ARRAY_REF)
+    {
+      if (TREE_CODE (expr) == COMPONENT_REF)
+	last_component = expr;
+
+      expr = TREE_OPERAND (expr, 0);
+
+      if (TREE_CODE (TREE_TYPE (expr)) == REFERENCE_TYPE)
+	break;
+    }
+
+  if (!last_component)
+    return false;
+
+  *expr0 = last_component;
+  return true;
+}
+
+/* This handles references that have had convert_from_reference called on
+   them, and also those that haven't.  */
+
+static bool
+omp_parse_ref (tree *expr0)
+{
+  tree expr = *expr0;
+
+  if (TREE_CODE (TREE_TYPE (expr)) == REFERENCE_TYPE)
+    return true;
+  else if ((TREE_CODE (expr) == INDIRECT_REF
+	    || (TREE_CODE (expr) == MEM_REF
+		&& integer_zerop (TREE_OPERAND (expr, 1))))
+	   && TREE_CODE (TREE_TYPE (TREE_OPERAND (expr, 0))) == REFERENCE_TYPE)
+    {
+      *expr0 = TREE_OPERAND (expr, 0);
+      return true;
+    }
+
+  return false;
+}
+
+static bool
+omp_parse_pointer (tree *expr0, bool *has_offset)
+{
+  tree expr = *expr0;
+
+  *has_offset = false;
+
+  if ((TREE_CODE (expr) == INDIRECT_REF
+       || (TREE_CODE (expr) == MEM_REF
+	   && integer_zerop (TREE_OPERAND (expr, 1))))
+      && TREE_CODE (TREE_TYPE (TREE_OPERAND (expr, 0))) == POINTER_TYPE)
+    {
+      expr = TREE_OPERAND (expr, 0);
+
+      /* The Fortran FE sometimes emits a no-op cast here.  */
+      STRIP_NOPS (expr);
+
+      while (1)
+	{
+	  if (TREE_CODE (expr) == COMPOUND_EXPR)
+	    {
+	      expr = TREE_OPERAND (expr, 1);
+	      STRIP_NOPS (expr);
+	    }
+	  else if (TREE_CODE (expr) == SAVE_EXPR)
+	    expr = TREE_OPERAND (expr, 0);
+	  else if (TREE_CODE (expr) == POINTER_PLUS_EXPR)
+	    {
+	      *has_offset = true;
+	      expr = TREE_OPERAND (expr, 0);
+	    }
+	  else
+	    break;
+	}
+
+      STRIP_NOPS (expr);
+
+      *expr0 = expr;
+      return true;
+    }
+
+  return false;
+}
+
+static bool
+omp_parse_access_method (tree *expr0, enum access_method_kinds *kind)
+{
+  tree expr = *expr0;
+  bool has_offset;
+
+  if (omp_parse_ref (&expr))
+    *kind = ACCESS_REF;
+  else if (omp_parse_pointer (&expr, &has_offset))
+    {
+      if (omp_parse_ref (&expr))
+	*kind = has_offset ? ACCESS_REF_TO_POINTER_OFFSET
+			   : ACCESS_REF_TO_POINTER;
+      else
+	*kind = has_offset ? ACCESS_POINTER_OFFSET : ACCESS_POINTER;
+    }
+  else if (TREE_CODE (expr) == ARRAY_REF)
+    {
+      while (TREE_CODE (expr) == ARRAY_REF)
+	expr = TREE_OPERAND (expr, 0);
+      if (omp_parse_ref (&expr))
+	*kind = ACCESS_INDEXED_REF_TO_ARRAY;
+      else
+	*kind = ACCESS_INDEXED_ARRAY;
+    }
+  else
+    *kind = ACCESS_DIRECT;
+
+  STRIP_NOPS (expr);
+
+  *expr0 = expr;
+  return true;
+}
+
+static bool
+omp_parse_access_methods (vec<omp_addr_token *> &addr_tokens, tree *expr0)
+{
+  tree expr = *expr0;
+  enum access_method_kinds kind;
+  tree am_expr;
+
+  if (omp_parse_access_method (&expr, &kind))
+    am_expr = expr;
+
+  if (TREE_CODE (expr) == INDIRECT_REF
+      || TREE_CODE (expr) == MEM_REF
+      || TREE_CODE (expr) == ARRAY_REF)
+    omp_parse_access_methods (addr_tokens, &expr);
+
+  addr_tokens.safe_push (new omp_addr_token (kind, am_expr));
+
+  *expr0 = expr;
+  return true;
+}
+
+static bool omp_parse_structured_expr (vec<omp_addr_token *> &, tree *);
+
+static bool
+omp_parse_structure_base (vec<omp_addr_token *> &addr_tokens,
+			  tree *expr0, structure_base_kinds *kind,
+			  vec<omp_addr_token *> &base_access_tokens,
+			  bool allow_structured = true)
+{
+  tree expr = *expr0;
+
+  if (allow_structured)
+    omp_parse_access_methods (base_access_tokens, &expr);
+
+  if (DECL_P (expr))
+    {
+      *kind = BASE_DECL;
+      return true;
+    }
+
+  if (allow_structured && omp_parse_structured_expr (addr_tokens, &expr))
+    {
+      *kind = BASE_COMPONENT_EXPR;
+      *expr0 = expr;
+      return true;
+    }
+
+  *kind = BASE_ARBITRARY_EXPR;
+  *expr0 = expr;
+  return true;
+}
+
+static bool
+omp_parse_structured_expr (vec<omp_addr_token *> &addr_tokens, tree *expr0)
+{
+  tree expr = *expr0;
+  tree base_component = NULL_TREE;
+  structure_base_kinds struct_base_kind;
+  auto_vec<omp_addr_token *> base_access_tokens;
+
+  if (omp_parse_component_selector (&expr))
+    base_component = expr;
+  else
+    return false;
+
+  gcc_assert (TREE_CODE (expr) == COMPONENT_REF);
+  expr = TREE_OPERAND (expr, 0);
+
+  tree structure_base = expr;
+
+  if (!omp_parse_structure_base (addr_tokens, &expr, &struct_base_kind,
+				 base_access_tokens))
+    return false;
+
+  addr_tokens.safe_push (new omp_addr_token (STRUCTURE_BASE, struct_base_kind,
+					     structure_base));
+  addr_tokens.safe_splice (base_access_tokens);
+  addr_tokens.safe_push (new omp_addr_token (COMPONENT_SELECTOR,
+					     base_component));
+
+  *expr0 = expr;
+
+  return true;
+}
+
+static bool
+omp_parse_array_expr (vec<omp_addr_token *> &addr_tokens, tree *expr0)
+{
+  tree expr = *expr0;
+  structure_base_kinds s_kind;
+  auto_vec<omp_addr_token *> base_access_tokens;
+
+  if (!omp_parse_structure_base (addr_tokens, &expr, &s_kind,
+				 base_access_tokens, false))
+    return false;
+
+  addr_tokens.safe_push (new omp_addr_token (ARRAY_BASE, s_kind, expr));
+  addr_tokens.safe_splice (base_access_tokens);
+
+  *expr0 = expr;
+  return true;
+}
+
+/* Return TRUE if the ACCESS_METHOD token at index 'i' has a further
+   ACCESS_METHOD chained after it (e.g., if we're processing an expression
+   containing multiple pointer indirections).  */
+
+bool
+omp_access_chain_p (vec<omp_addr_token *> &addr_tokens, unsigned i)
+{
+  gcc_assert (addr_tokens[i]->type == ACCESS_METHOD);
+  return (i + 1 < addr_tokens.length ()
+	  && addr_tokens[i + 1]->type == ACCESS_METHOD);
+}
+
+/* Return the address of the object accessed by the ACCESS_METHOD token
+   at 'i': either of the next access method's expr, or of EXPR if we're at
+   the end of the list of tokens.  */
+
+tree
+omp_accessed_addr (vec<omp_addr_token *> &addr_tokens, unsigned i, tree expr)
+{
+  if (i + 1 < addr_tokens.length ())
+    return build_fold_addr_expr (addr_tokens[i + 1]->expr);
+  else
+    return build_fold_addr_expr (expr);
+}
+
+} /* namespace omp_addr_tokenizer.  */
+
+bool
+omp_parse_expr (vec<omp_addr_token *> &addr_tokens, tree expr)
+{
+  using namespace omp_addr_tokenizer;
+  auto_vec<omp_addr_token *> expr_access_tokens;
+
+  if (!omp_parse_access_methods (expr_access_tokens, &expr))
+    return false;
+
+  if (omp_parse_structured_expr (addr_tokens, &expr))
+    ;
+  else if (omp_parse_array_expr (addr_tokens, &expr))
+    ;
+  else
+    return false;
+
+  addr_tokens.safe_splice (expr_access_tokens);
+
+  return true;
+}
+
+DEBUG_FUNCTION void
+debug_omp_tokenized_addr (vec<omp_addr_token *> &addr_tokens,
+			  bool with_exprs)
+{
+  using namespace omp_addr_tokenizer;
+  const char *sep = with_exprs ? "  " : "";
+
+  for (auto e : addr_tokens)
+    {
+      const char *pfx = "";
+
+      fputs (sep, stderr);
+
+      switch (e->type)
+	{
+	case COMPONENT_SELECTOR:
+	  fputs ("component_selector", stderr);
+	  break;
+	case ACCESS_METHOD:
+	  switch (e->u.access_kind)
+	    {
+	    case ACCESS_DIRECT:
+	      fputs ("access_direct", stderr);
+	      break;
+	    case ACCESS_REF:
+	      fputs ("access_ref", stderr);
+	      break;
+	    case ACCESS_POINTER:
+	      fputs ("access_pointer", stderr);
+	      break;
+	    case ACCESS_POINTER_OFFSET:
+	      fputs ("access_pointer_offset", stderr);
+	      break;
+	    case ACCESS_REF_TO_POINTER:
+	      fputs ("access_ref_to_pointer", stderr);
+	      break;
+	    case ACCESS_REF_TO_POINTER_OFFSET:
+	      fputs ("access_ref_to_pointer_offset", stderr);
+	      break;
+	    case ACCESS_INDEXED_ARRAY:
+	      fputs ("access_indexed_array", stderr);
+	      break;
+	    case ACCESS_INDEXED_REF_TO_ARRAY:
+	      fputs ("access_indexed_ref_to_array", stderr);
+	      break;
+	    }
+	  break;
+	case ARRAY_BASE:
+	case STRUCTURE_BASE:
+	  pfx = e->type == ARRAY_BASE ? "array_" : "struct_";
+	  switch (e->u.structure_base_kind)
+	    {
+	    case BASE_DECL:
+	      fprintf (stderr, "%sbase_decl", pfx);
+	      break;
+	    case BASE_COMPONENT_EXPR:
+	      fputs ("base_component_expr", stderr);
+	      break;
+	    case BASE_ARBITRARY_EXPR:
+	      fprintf (stderr, "%sbase_arbitrary_expr", pfx);
+	      break;
+	    }
+	  break;
+	}
+      if (with_exprs)
+	{
+	  fputs (" [", stderr);
+	  print_generic_expr (stderr, e->expr);
+	  fputc (']', stderr);
+	  sep = ",\n  ";
+	}
+      else
+	sep = " ";
+    }
+
+  fputs ("\n", stderr);
+}
+
+
 #include "gt-omp-general.h"
diff --git a/gcc/omp-general.h b/gcc/omp-general.h
index 1a52bfdb56b..759e84738c6 100644
--- a/gcc/omp-general.h
+++ b/gcc/omp-general.h
@@ -153,4 +153,73 @@ get_openacc_privatization_dump_flags ()
 
 extern tree omp_build_component_ref (tree obj, tree field);
 
+namespace omp_addr_tokenizer {
+
+/* These are the ways of accessing a variable that have special-case handling
+   in the middle end (gimplify, omp-lower, etc.).  */
+
+/* These are the kinds of access that an ACCESS_METHOD token can represent.  */
+
+enum access_method_kinds
+{
+  ACCESS_DIRECT,
+  ACCESS_REF,
+  ACCESS_POINTER,
+  ACCESS_REF_TO_POINTER,
+  ACCESS_POINTER_OFFSET,
+  ACCESS_REF_TO_POINTER_OFFSET,
+  ACCESS_INDEXED_ARRAY,
+  ACCESS_INDEXED_REF_TO_ARRAY
+};
+
+/* These are the kinds that a STRUCTURE_BASE or ARRAY_BASE (except
+   BASE_COMPONENT_EXPR) can represent.  */
+
+enum structure_base_kinds
+{
+  BASE_DECL,
+  BASE_COMPONENT_EXPR,
+  BASE_ARBITRARY_EXPR
+};
+
+/* The coarse type for an address token.  These can have subtypes for
+   ARRAY_BASE or STRUCTURE_BASE (structure_base_kinds) or ACCESS_METHOD
+   (access_method_kinds).  */
+
+enum token_type
+{
+  ARRAY_BASE,
+  STRUCTURE_BASE,
+  COMPONENT_SELECTOR,
+  ACCESS_METHOD
+};
+
+/* The struct that forms a single token of an address expression as parsed by
+   omp_parse_expr.  These are typically held in a vec after parsing.  */
+
+struct omp_addr_token
+{
+  enum token_type type;
+  tree expr;
+
+  union
+  {
+    access_method_kinds access_kind;
+    structure_base_kinds structure_base_kind;
+  } u;
+
+  omp_addr_token (token_type, tree);
+  omp_addr_token (access_method_kinds, tree);
+  omp_addr_token (token_type, structure_base_kinds, tree);
+};
+
+extern bool omp_access_chain_p (vec<omp_addr_token *> &, unsigned);
+extern tree omp_accessed_addr (vec<omp_addr_token *> &, unsigned, tree);
+
+}
+
+typedef omp_addr_tokenizer::omp_addr_token omp_addr_token;
+
+extern bool omp_parse_expr (vec<omp_addr_token *> &, tree);
+
 #endif /* GCC_OMP_GENERAL_H */
diff --git a/gcc/omp-low.cc b/gcc/omp-low.cc
index 161bcfeec05..c5d63c96c20 100644
--- a/gcc/omp-low.cc
+++ b/gcc/omp-low.cc
@@ -1604,10 +1604,13 @@ scan_sharing_clauses (tree clauses, omp_context *ctx)
 	    {
 	      /* If this is an offloaded region, an attach operation should
 		 only exist when the pointer variable is mapped in a prior
-		 clause.
+		 clause.  An exception is if we have a reference (to pointer):
+		 in that case we should have mapped "*decl" in a previous
+		 mapping instead of "decl".  Skip the assertion in that case.
 		 If we had an error, we may not have attempted to sort clauses
 		 properly, so avoid the test.  */
-	      if (is_gimple_omp_offloaded (ctx->stmt)
+	      if (TREE_CODE (TREE_TYPE (decl)) != REFERENCE_TYPE
+		  && is_gimple_omp_offloaded (ctx->stmt)
 		  && !seen_error ())
 		gcc_assert
 		  (maybe_lookup_decl (decl, ctx)
diff --git a/gcc/testsuite/c-c++-common/gomp/clauses-2.c b/gcc/testsuite/c-c++-common/gomp/clauses-2.c
index bbc8fb4e32b..8f98d57a312 100644
--- a/gcc/testsuite/c-c++-common/gomp/clauses-2.c
+++ b/gcc/testsuite/c-c++-common/gomp/clauses-2.c
@@ -11,7 +11,7 @@ foo (int *p, int q, struct S t, int i, int j, int k, int l)
     bar (p);
   #pragma omp target firstprivate (p), map (p[0]) /* { dg-error "appears more than once in data clauses" } */
     bar (p);
-  #pragma omp target map (p[0]) map (p) /* { dg-error "appears both in data and map clauses" } */
+  #pragma omp target map (p[0]) map (p)
     bar (p);
   #pragma omp target map (p) , map (p[0])
     bar (p);
diff --git a/gcc/testsuite/c-c++-common/gomp/target-50.c b/gcc/testsuite/c-c++-common/gomp/target-50.c
index 41f1d37845c..a30a25e0893 100644
--- a/gcc/testsuite/c-c++-common/gomp/target-50.c
+++ b/gcc/testsuite/c-c++-common/gomp/target-50.c
@@ -17,7 +17,7 @@ int main()
 
   #pragma omp target map(tofrom: tmp->arr[0:10]) map(to: tmp->arr)
   { }
-/* { dg-final { scan-tree-dump-times {map\(struct:\*tmp \[len: 1\]\) map\(to:tmp[._0-9]*->arr \[len: [0-9]+\]\) map\(tofrom:\*_[0-9]+ \[len: [0-9]+\]\) map\(attach:tmp[._0-9]*->arr \[bias: 0\]\)} 2 "gimple" { target { ! { nvptx*-*-* amdgcn*-*-* } } } } } */
+/* { dg-final { scan-tree-dump-times {map\(struct:\*tmp \[len: 1\]\) map\(alloc:tmp[._0-9]*->arr \[len: [0-9]+\]\) map\(tofrom:\*_[0-9]+ \[len: [0-9]+\]\) map\(attach:tmp[._0-9]*->arr \[bias: 0\]\)} 2 "gimple" { target { ! { nvptx*-*-* amdgcn*-*-* } } } } } */
 
   return 0;
 }
diff --git a/gcc/testsuite/c-c++-common/gomp/target-enter-data-1.c b/gcc/testsuite/c-c++-common/gomp/target-enter-data-1.c
index ce766d29e2d..4913d338e5f 100644
--- a/gcc/testsuite/c-c++-common/gomp/target-enter-data-1.c
+++ b/gcc/testsuite/c-c++-common/gomp/target-enter-data-1.c
@@ -21,4 +21,5 @@ void func (struct foo *f, int n, int m)
   #pragma omp target enter data map (to: f->bars[n].vectors[:f->bars[n].num_vectors])
 }
 
-/* { dg-final { scan-tree-dump-times "map\\(to:\\*_\[0-9\]+ \\\[len: _\[0-9\]+\\\]\\) map\\(attach:\[^-\]+->vectors \\\[bias: \[^\]\]+\\\]\\)" 3 "gimple" } } */
+/* { dg-final { scan-tree-dump-times {map\(struct:\*f \[len: 1\]\) map\(alloc:[a-z0-9\._]+->vectors \[len: 0\]\) map\(to:\*_[0-9]+ \[len: _[0-9]+\]\) map\(attach:[a-z0-9\._]+->vectors \[bias: [^\]]+\]\) map\(attach:\*_[0-9]+ \[bias: _[0-9]+\]\)} 1 "gimple" } } */
+/* { dg-final { scan-tree-dump-times {map\(struct:\*\(f->bars \+ \(sizetype\) \(\([^\)]+\) n \* 16\)\) \[len: 1\]\) map\(alloc:[a-z0-9\._]+->vectors \[len: 0\]\) map\(to:\*_[0-9]+ \[len: _[0-9]+\]\) map\(attach:[a-z0-9\._]+->vectors \[bias: [^\]]+\]\)} 2 "gimple" } } */
diff --git a/gcc/testsuite/c-c++-common/gomp/target-implicit-map-2.c b/gcc/testsuite/c-c++-common/gomp/target-implicit-map-2.c
index 3aa1a8fc55e..222272df5b1 100644
--- a/gcc/testsuite/c-c++-common/gomp/target-implicit-map-2.c
+++ b/gcc/testsuite/c-c++-common/gomp/target-implicit-map-2.c
@@ -49,4 +49,5 @@ main (void)
 
 /* { dg-final { scan-tree-dump {#pragma omp target num_teams.* map\(tofrom:a \[len: [0-9]+\]\[implicit\]\)} "gimple" } } */
 
-/* { dg-final { scan-tree-dump {#pragma omp target num_teams.* map\(tofrom:a \[len: [0-9]+\]\[implicit\]\) map\(tofrom:\*_[0-9]+ \[len: [0-9]+\]\) map\(attach:a\.ptr \[bias: 0\]\)} "gimple" } } */
+/* { dg-final { scan-tree-dump {#pragma omp target num_teams.* map\(struct:a \[len: 1\]\) map\(alloc:a\.ptr \[len: [0-9]+\]\) map\(tofrom:\*_[0-9]+ \[len: [0-9]+\]\) map\(attach:a\.ptr \[bias: 0\]\)} "gimple" } } */
+/* { dg-final { scan-tree-dump-not {map\(struct:a \[len: 1\]\) map\(alloc:a\.ptr \[len: 0\]\)} "gimple" } } */
diff --git a/gcc/testsuite/g++.dg/gomp/static-component-1.C b/gcc/testsuite/g++.dg/gomp/static-component-1.C
new file mode 100644
index 00000000000..c2f95933567
--- /dev/null
+++ b/gcc/testsuite/g++.dg/gomp/static-component-1.C
@@ -0,0 +1,23 @@
+/* { dg-do compile } */
+
+/* Types with static members should be mappable.  */
+
+struct A {
+  static int x[10];
+};
+
+struct B {
+  A a;
+};
+
+int
+main (int argc, char *argv[])
+{
+  B *b = new B;
+#pragma omp target map(b->a)
+  ;
+  B bb;
+#pragma omp target map(bb.a)
+  ;
+  delete b;
+}
diff --git a/gcc/testsuite/gcc.dg/gomp/target-3.c b/gcc/testsuite/gcc.dg/gomp/target-3.c
index 3e7921270c9..3d5e05f8571 100644
--- a/gcc/testsuite/gcc.dg/gomp/target-3.c
+++ b/gcc/testsuite/gcc.dg/gomp/target-3.c
@@ -13,4 +13,4 @@ void foo (struct S *s)
   #pragma omp target enter data map (alloc: s->a, s->b)
 }
 
-/* { dg-final { scan-tree-dump-times "map\\(struct:\\*s \\\[len: 2\\\]\\) map\\(alloc:s->a \\\[len: \[0-9\]+\\\]\\) map\\(alloc:s->b \\\[len: \[0-9\]+\\\]\\)" 2 "gimple" } } */
+/* { dg-final { scan-tree-dump-times "map\\(struct:\\*s \\\[len: 2\\\]\\) map\\(alloc:s\[\\._0-9\]+->a \\\[len: \[0-9\]+\\\]\\) map\\(alloc:s\[\\._0-9\]+->b \\\[len: \[0-9\]+\\\]\\)" 2 "gimple" } } */
diff --git a/gcc/testsuite/gfortran.dg/gomp/map-9.f90 b/gcc/testsuite/gfortran.dg/gomp/map-9.f90
index b770b931bee..f930a49d9ff 100644
--- a/gcc/testsuite/gfortran.dg/gomp/map-9.f90
+++ b/gcc/testsuite/gfortran.dg/gomp/map-9.f90
@@ -2,7 +2,7 @@
 
 ! PR fortran/108545
 
-! { dg-final { scan-tree-dump "#pragma omp target enter data map\\(struct:x \\\[len: 1\\\]\\) map\\(always,to:x.a \\\[len: \[0-9\]+\\\]\\) map\\(to:MEM <integer\\(kind=4\\)\\\[0:\\\]> \\\[\\(integer\\(kind=4\\)\\\[0:\\\] \\*\\)_\[0-9\]+] \\\[len: _\[0-9\]+\\\]\\) map\\(always_pointer:x.a.data \\\[pointer assign, bias: 0\\\]\\)" "omplower" } }
+! { dg-final { scan-tree-dump "#pragma omp target enter data map\\(struct:x \\\[len: 1\\\]\\) map\\(always,to:x\.a \\\[len: \[0-9\]+\\\]\\) map\\(to:MEM <integer\\(kind=4\\)\\\[0:\\\]> \\\[\\(integer\\(kind=4\\)\\\[0:\\\] \\*\\)_\[0-9\]+] \\\[len: _\[0-9\]+\\\]\\) map\\(attach:x\.a\.data \\\[bias: 0\\\]\\)" "omplower" } }
 
 program p
    type t
diff --git a/gcc/tree.h b/gcc/tree.h
index 086b55f0375..4fd75e6131b 100644
--- a/gcc/tree.h
+++ b/gcc/tree.h
@@ -1825,6 +1825,10 @@ class auto_suppress_location_wrappers
    NOTE: this is different than OMP_CLAUSE_MAP_IMPLICIT.  */
 #define OMP_CLAUSE_MAP_RUNTIME_IMPLICIT_P(NODE) \
   (OMP_CLAUSE_SUBCODE_CHECK (NODE, OMP_CLAUSE_MAP)->base.deprecated_flag)
+/* Nonzero for an attach/detach node whose decl was explicitly mapped on the
+   same directive.  */
+#define OMP_CLAUSE_ATTACHMENT_MAPPING_ERASED(NODE) \
+  TREE_STATIC (OMP_CLAUSE_SUBCODE_CHECK (NODE, OMP_CLAUSE_MAP))
 
 /* Flag that 'OMP_CLAUSE_DECL (NODE)' is to be made addressable during OMP
    lowering.  */
diff --git a/libgomp/target.c b/libgomp/target.c
index f30c20255d3..69f82e9a428 100644
--- a/libgomp/target.c
+++ b/libgomp/target.c
@@ -702,7 +702,7 @@ gomp_map_pointer (struct target_mem_desc *tgt, struct goacc_asyncqueue *aq,
   if (n == NULL)
     {
       if (allow_zero_length_array_sections)
-	cur_node.tgt_offset = 0;
+	cur_node.tgt_offset = cur_node.host_start;
       else
 	{
 	  gomp_mutex_unlock (&devicep->lock);
@@ -741,7 +741,7 @@ gomp_map_fields_existing (struct target_mem_desc *tgt,
 
   cur_node.host_start = (uintptr_t) hostaddrs[i];
   cur_node.host_end = cur_node.host_start + sizes[i];
-  splay_tree_key n2 = splay_tree_lookup (mem_map, &cur_node);
+  splay_tree_key n2 = gomp_map_0len_lookup (mem_map, &cur_node);
   kind = get_kind (short_mapkind, kinds, i);
   implicit = get_implicit (short_mapkind, kinds, i);
   if (n2
@@ -838,8 +838,20 @@ gomp_attach_pointer (struct gomp_device_descr *devicep,
 
       if ((void *) target == NULL)
 	{
-	  gomp_mutex_unlock (&devicep->lock);
-	  gomp_fatal ("attempt to attach null pointer");
+	  /* As a special case, allow attaching NULL host pointers.  This
+	     allows e.g. unassociated Fortran pointers to be mapped
+	     properly.  */
+	  data = 0;
+
+	  gomp_debug (1,
+		      "%s: attaching NULL host pointer, target %p "
+		      "(struct base %p)\n", __FUNCTION__, (void *) devptr,
+		      (void *) (n->tgt->tgt_start + n->tgt_offset));
+
+	  gomp_copy_host2dev (devicep, aq, (void *) devptr, (void *) &data,
+			      sizeof (void *), true, cbufp);
+
+	  return;
 	}
 
       s.host_start = target + bias;
@@ -850,9 +862,8 @@ gomp_attach_pointer (struct gomp_device_descr *devicep,
 	{
 	  if (allow_zero_length_array_sections)
 	    /* When allowing attachment to zero-length array sections, we
-	       allow attaching to NULL pointers when the target region is not
-	       mapped.  */
-	    data = 0;
+	       copy the host pointer when the target region is not mapped.  */
+	    data = target;
 	  else
 	    {
 	      gomp_mutex_unlock (&devicep->lock);
@@ -1096,7 +1107,8 @@ gomp_map_vars_internal (struct gomp_device_descr *devicep,
 		  tgt->list[i].key = NULL;
 		  if (!aq
 		      && gomp_to_device_kind_p (get_kind (short_mapkind, kinds, i)
-						& typemask))
+						& typemask)
+		      && sizes[i] != 0)
 		    gomp_coalesce_buf_add (&cbuf,
 					   tgt_size - cur_node.host_end
 					   + (uintptr_t) hostaddrs[i],
@@ -1463,7 +1475,17 @@ gomp_map_vars_internal (struct gomp_device_descr *devicep,
 				    + sizes[last];
 		if (tgt->list[first].key != NULL)
 		  continue;
+		if (sizes[last] == 0)
+		  cur_node.host_end++;
 		n = splay_tree_lookup (mem_map, &cur_node);
+		if (sizes[last] == 0)
+		  cur_node.host_end--;
+		if (n == NULL && cur_node.host_start == cur_node.host_end)
+		  {
+		    gomp_mutex_unlock (&devicep->lock);
+		    gomp_fatal ("Struct pointer member not mapped (%p)",
+				(void*) hostaddrs[first]);
+		  }
 		if (n == NULL)
 		  {
 		    size_t align = (size_t) 1 << (kind >> rshift);
diff --git a/libgomp/testsuite/libgomp.c++/baseptrs-3.C b/libgomp/testsuite/libgomp.c++/baseptrs-3.C
new file mode 100644
index 00000000000..39a48a40920
--- /dev/null
+++ b/libgomp/testsuite/libgomp.c++/baseptrs-3.C
@@ -0,0 +1,275 @@
+#include <cstdlib>
+#include <cstring>
+#include <cassert>
+
+struct sa0
+{
+  int *ptr;
+};
+
+struct sb0
+{
+  int arr[10];
+};
+
+struct sc0
+{
+  sa0 a;
+  sb0 b;
+  sc0 (sa0 &my_a, sb0 &my_b) : a(my_a), b(my_b) {}
+};
+
+void
+foo0 ()
+{
+  sa0 my_a;
+  sb0 my_b;
+
+  my_a.ptr = (int *) malloc (sizeof (int) * 10);
+  sc0 my_c(my_a, my_b);
+
+  memset (my_c.a.ptr, 0, sizeof (int) * 10);
+
+  #pragma omp target map (my_c.a.ptr, my_c.a.ptr[:10])
+  {
+    for (int i = 0; i < 10; i++)
+      my_c.a.ptr[i] = i;
+  }
+
+  for (int i = 0; i < 10; i++)
+    assert (my_c.a.ptr[i] == i);
+
+  memset (my_c.b.arr, 0, sizeof (int) * 10);
+
+  #pragma omp target map (my_c.b.arr[:10])
+  {
+    for (int i = 0; i < 10; i++)
+      my_c.b.arr[i] = i;
+  }
+
+  for (int i = 0; i < 10; i++)
+    assert (my_c.b.arr[i] == i);
+
+  free (my_a.ptr);
+}
+
+struct sa
+{
+  int *ptr;
+};
+
+struct sb
+{
+  int arr[10];
+};
+
+struct sc
+{
+  sa &a;
+  sb &b;
+  sc (sa &my_a, sb &my_b) : a(my_a), b(my_b) {}
+};
+
+void
+foo ()
+{
+  sa my_a;
+  sb my_b;
+
+  my_a.ptr = (int *) malloc (sizeof (int) * 10);
+  sc my_c(my_a, my_b);
+
+  memset (my_c.a.ptr, 0, sizeof (int) * 10);
+
+  #pragma omp target map (my_c.a.ptr, my_c.a.ptr[:10])
+  {
+    for (int i = 0; i < 10; i++)
+      my_c.a.ptr[i] = i;
+  }
+
+  for (int i = 0; i < 10; i++)
+    assert (my_c.a.ptr[i] == i);
+
+  memset (my_c.b.arr, 0, sizeof (int) * 10);
+
+  #pragma omp target map (my_c.b.arr[:10])
+  {
+    for (int i = 0; i < 10; i++)
+      my_c.b.arr[i] = i;
+  }
+
+  for (int i = 0; i < 10; i++)
+    assert (my_c.b.arr[i] == i);
+
+  free (my_a.ptr);
+}
+
+void
+bar ()
+{
+  sa my_a;
+  sb my_b;
+
+  my_a.ptr = (int *) malloc (sizeof (int) * 10);
+  sc my_c(my_a, my_b);
+  sc &my_cref = my_c;
+
+  memset (my_cref.a.ptr, 0, sizeof (int) * 10);
+
+  #pragma omp target map (my_cref.a.ptr, my_cref.a.ptr[:10])
+  {
+    for (int i = 0; i < 10; i++)
+      my_cref.a.ptr[i] = i;
+  }
+
+  for (int i = 0; i < 10; i++)
+    assert (my_cref.a.ptr[i] == i);
+
+  memset (my_cref.b.arr, 0, sizeof (int) * 10);
+
+  #pragma omp target map (my_cref.b.arr[:10])
+  {
+    for (int i = 0; i < 10; i++)
+      my_cref.b.arr[i] = i;
+  }
+
+  for (int i = 0; i < 10; i++)
+    assert (my_cref.b.arr[i] == i);
+
+  free (my_a.ptr);
+}
+
+struct scp0
+{
+  sa *a;
+  sb *b;
+  scp0 (sa *my_a, sb *my_b) : a(my_a), b(my_b) {}
+};
+
+void
+foop0 ()
+{
+  sa *my_a = new sa;
+  sb *my_b = new sb;
+
+  my_a->ptr = new int[10];
+  scp0 *my_c = new scp0(my_a, my_b);
+
+  memset (my_c->a->ptr, 0, sizeof (int) * 10);
+
+  #pragma omp target map (my_c->a, my_c->a[:1], my_c->a->ptr, my_c->a->ptr[:10])
+  {
+    for (int i = 0; i < 10; i++)
+      my_c->a->ptr[i] = i;
+  }
+
+  for (int i = 0; i < 10; i++)
+    assert (my_c->a->ptr[i] == i);
+
+  memset (my_c->b->arr, 0, sizeof (int) * 10);
+
+  #pragma omp target map (my_c->b, my_c->b[:1], my_c->b->arr[:10])
+  {
+    for (int i = 0; i < 10; i++)
+      my_c->b->arr[i] = i;
+  }
+
+  for (int i = 0; i < 10; i++)
+    assert (my_c->b->arr[i] == i);
+
+  delete[] my_a->ptr;
+  delete my_a;
+  delete my_b;
+}
+
+struct scp
+{
+  sa *&a;
+  sb *&b;
+  scp (sa *&my_a, sb *&my_b) : a(my_a), b(my_b) {}
+};
+
+void
+foop ()
+{
+  sa *my_a = new sa;
+  sb *my_b = new sb;
+
+  my_a->ptr = new int[10];
+  scp *my_c = new scp(my_a, my_b);
+
+  memset (my_c->a->ptr, 0, sizeof (int) * 10);
+
+  #pragma omp target map (my_c->a, my_c->a[:1], my_c->a->ptr, my_c->a->ptr[:10])
+  {
+    for (int i = 0; i < 10; i++)
+      my_c->a->ptr[i] = i;
+  }
+
+  for (int i = 0; i < 10; i++)
+    assert (my_c->a->ptr[i] == i);
+
+  memset (my_c->b->arr, 0, sizeof (int) * 10);
+
+  #pragma omp target map (my_c->b, my_c->b[:1], my_c->b->arr[:10])
+  {
+    for (int i = 0; i < 10; i++)
+      my_c->b->arr[i] = i;
+  }
+
+  for (int i = 0; i < 10; i++)
+    assert (my_c->b->arr[i] == i);
+
+  delete[] my_a->ptr;
+  delete my_a;
+  delete my_b;
+}
+
+void
+barp ()
+{
+  sa *my_a = new sa;
+  sb *my_b = new sb;
+
+  my_a->ptr = new int[10];
+  scp *my_c = new scp(my_a, my_b);
+  scp *&my_cref = my_c;
+
+  memset (my_cref->a->ptr, 0, sizeof (int) * 10);
+
+  #pragma omp target map (my_cref->a, my_cref->a[:1], my_cref->a->ptr, \
+			  my_cref->a->ptr[:10])
+  {
+    for (int i = 0; i < 10; i++)
+      my_cref->a->ptr[i] = i;
+  }
+
+  for (int i = 0; i < 10; i++)
+    assert (my_cref->a->ptr[i] == i);
+
+  memset (my_cref->b->arr, 0, sizeof (int) * 10);
+
+  #pragma omp target map (my_cref->b, my_cref->b[:1], my_cref->b->arr[:10])
+  {
+    for (int i = 0; i < 10; i++)
+      my_cref->b->arr[i] = i;
+  }
+
+  for (int i = 0; i < 10; i++)
+    assert (my_cref->b->arr[i] == i);
+
+  delete my_a->ptr;
+  delete my_a;
+  delete my_b;
+}
+
+int main (int argc, char *argv[])
+{
+  foo0 ();
+  foo ();
+  bar ();
+  foop0 ();
+  foop ();
+  barp ();
+  return 0;
+}
diff --git a/libgomp/testsuite/libgomp.c++/baseptrs-4.C b/libgomp/testsuite/libgomp.c++/baseptrs-4.C
new file mode 100644
index 00000000000..196029ac186
--- /dev/null
+++ b/libgomp/testsuite/libgomp.c++/baseptrs-4.C
@@ -0,0 +1,3154 @@
+// { dg-do run }
+
+#include <cstring>
+#include <cassert>
+
+#define MAP_DECLS
+
+#define NONREF_DECL_BASE
+#define REF_DECL_BASE
+#define PTR_DECL_BASE
+#define REF2PTR_DECL_BASE
+
+#define ARRAY_DECL_BASE
+// Needs map clause "lvalue"-parsing support.
+//#define REF2ARRAY_DECL_BASE
+#define PTR_OFFSET_DECL_BASE
+// Needs map clause "lvalue"-parsing support.
+//#define REF2PTR_OFFSET_DECL_BASE
+
+#define MAP_SECTIONS
+
+#define NONREF_DECL_MEMBER_SLICE
+#define NONREF_DECL_MEMBER_SLICE_BASEPTR
+#define REF_DECL_MEMBER_SLICE
+#define REF_DECL_MEMBER_SLICE_BASEPTR
+#define PTR_DECL_MEMBER_SLICE
+#define PTR_DECL_MEMBER_SLICE_BASEPTR
+#define REF2PTR_DECL_MEMBER_SLICE
+#define REF2PTR_DECL_MEMBER_SLICE_BASEPTR
+
+#define ARRAY_DECL_MEMBER_SLICE
+#define ARRAY_DECL_MEMBER_SLICE_BASEPTR
+// Needs map clause "lvalue"-parsing support.
+//#define REF2ARRAY_DECL_MEMBER_SLICE
+//#define REF2ARRAY_DECL_MEMBER_SLICE_BASEPTR
+#define PTR_OFFSET_DECL_MEMBER_SLICE
+#define PTR_OFFSET_DECL_MEMBER_SLICE_BASEPTR
+// Needs map clause "lvalue"-parsing support.
+//#define REF2PTR_OFFSET_DECL_MEMBER_SLICE
+//#define REF2PTR_OFFSET_DECL_MEMBER_SLICE_BASEPTR
+
+#define PTRARRAY_DECL_MEMBER_SLICE
+#define PTRARRAY_DECL_MEMBER_SLICE_BASEPTR
+// Needs map clause "lvalue"-parsing support.
+//#define REF2PTRARRAY_DECL_MEMBER_SLICE
+//#define REF2PTRARRAY_DECL_MEMBER_SLICE_BASEPTR
+#define PTRPTR_OFFSET_DECL_MEMBER_SLICE
+#define PTRPTR_OFFSET_DECL_MEMBER_SLICE_BASEPTR
+// Needs map clause "lvalue"-parsing support.
+//#define REF2PTRPTR_OFFSET_DECL_MEMBER_SLICE
+//#define REF2PTRPTR_OFFSET_DECL_MEMBER_SLICE_BASEPTR
+
+#define NONREF_COMPONENT_BASE
+#define NONREF_COMPONENT_MEMBER_SLICE
+#define NONREF_COMPONENT_MEMBER_SLICE_BASEPTR
+
+#define REF_COMPONENT_BASE
+#define REF_COMPONENT_MEMBER_SLICE
+#define REF_COMPONENT_MEMBER_SLICE_BASEPTR
+
+#define PTR_COMPONENT_BASE
+#define PTR_COMPONENT_MEMBER_SLICE
+#define PTR_COMPONENT_MEMBER_SLICE_BASEPTR
+
+#define REF2PTR_COMPONENT_BASE
+#define REF2PTR_COMPONENT_MEMBER_SLICE
+#define REF2PTR_COMPONENT_MEMBER_SLICE_BASEPTR
+
+#ifdef MAP_DECLS
+void
+map_decls (void)
+{
+  int x = 0;
+  int &y = x;
+  int arr[4];
+  int (&arrref)[4] = arr;
+  int *z = &arr[0];
+  int *&t = z;
+
+  memset (arr, 0, sizeof arr);
+
+  #pragma omp target map(x)
+  {
+    x++;
+  }
+
+  #pragma omp target map(y)
+  {
+    y++;
+  }
+
+  assert (x == 2);
+  assert (y == 2);
+
+  /* "A variable that is of type pointer is treated as if it is the base
+      pointer of a zero-length array section that appeared as a list item in a
+      map clause."  */
+  #pragma omp target map(z)
+  {
+    z++;
+  }
+
+  /* "A variable that is of type reference to pointer is treated as if it had
+      appeared in a map clause as a zero-length array section."
+
+     The pointer here is *not* associated with a target address, so we're not
+     disallowed from modifying it.  */
+  #pragma omp target map(t)
+  {
+    t++;
+  }
+
+  assert (z == &arr[2]);
+  assert (t == &arr[2]);
+
+  #pragma omp target map(arr)
+  {
+    arr[2]++;
+  }
+
+  #pragma omp target map(arrref)
+  {
+    arrref[2]++;
+  }
+
+  assert (arr[2] == 2);
+  assert (arrref[2] == 2);
+}
+#endif
+
+struct S {
+  int a;
+  int &b;
+  int *c;
+  int *&d;
+  int e[4];
+  int (&f)[4];
+
+  S(int a1, int &b1, int *c1, int *&d1) :
+    a(a1), b(b1), c(c1), d(d1), f(e)
+  {
+    memset (e, 0, sizeof e);
+  }
+};
+
+#ifdef NONREF_DECL_BASE
+void
+nonref_decl_base (void)
+{
+  int a = 0, b = 0, c, *d = &c;
+  S mys(a, b, &c, d);
+
+  #pragma omp target map(mys.a)
+  {
+    mys.a++;
+  }
+
+  #pragma omp target map(mys.b)
+  {
+    mys.b++;
+  }
+
+  assert (mys.a == 1);
+  assert (mys.b == 1);
+
+  #pragma omp target map(mys.c)
+  {
+    mys.c++;
+  }
+
+  #pragma omp target map(mys.d)
+  {
+    mys.d++;
+  }
+
+  assert (mys.c == &c + 1);
+  assert (mys.d == &c + 1);
+
+  #pragma omp target map(mys.e)
+  {
+    mys.e[0]++;
+  }
+
+  #pragma omp target map(mys.f)
+  {
+    mys.f[0]++;
+  }
+
+  assert (mys.e[0] == 2);
+  assert (mys.f[0] == 2);
+}
+#endif
+
+#ifdef REF_DECL_BASE
+void
+ref_decl_base (void)
+{
+  int a = 0, b = 0, c, *d = &c;
+  S mys_orig(a, b, &c, d);
+  S &mys = mys_orig;
+
+  #pragma omp target map(mys.a)
+  {
+    mys.a++;
+  }
+
+  #pragma omp target map(mys.b)
+  {
+    mys.b++;
+  }
+
+  assert (mys.a == 1);
+  assert (mys.b == 1);
+
+  #pragma omp target map(mys.c)
+  {
+    mys.c++;
+  }
+
+  #pragma omp target map(mys.d)
+  {
+    mys.d++;
+  }
+
+  assert (mys.c == &c + 1);
+  assert (mys.d == &c + 1);
+
+  #pragma omp target map(mys.e)
+  {
+    mys.e[0]++;
+  }
+
+  #pragma omp target map(mys.f)
+  {
+    mys.f[0]++;
+  }
+
+  assert (mys.e[0] == 2);
+  assert (mys.f[0] == 2);
+}
+#endif
+
+#ifdef PTR_DECL_BASE
+void
+ptr_decl_base (void)
+{
+  int a = 0, b = 0, c, *d = &c;
+  S mys_orig(a, b, &c, d);
+  S *mys = &mys_orig;
+
+  #pragma omp target map(mys->a)
+  {
+    mys->a++;
+  }
+
+  #pragma omp target map(mys->b)
+  {
+    mys->b++;
+  }
+
+  assert (mys->a == 1);
+  assert (mys->b == 1);
+
+  #pragma omp target map(mys->c)
+  {
+    mys->c++;
+  }
+
+  #pragma omp target map(mys->d)
+  {
+    mys->d++;
+  }
+
+  assert (mys->c == &c + 1);
+  assert (mys->d == &c + 1);
+
+  #pragma omp target map(mys->e)
+  {
+    mys->e[0]++;
+  }
+
+  #pragma omp target map(mys->f)
+  {
+    mys->f[0]++;
+  }
+
+  assert (mys->e[0] == 2);
+  assert (mys->f[0] == 2);
+}
+#endif
+
+#ifdef REF2PTR_DECL_BASE
+void
+ref2ptr_decl_base (void)
+{
+  int a = 0, b = 0, c, *d = &c;
+  S mys_orig(a, b, &c, d);
+  S *mysp = &mys_orig;
+  S *&mys = mysp;
+
+  #pragma omp target map(mys->a)
+  {
+    mys->a++;
+  }
+
+  #pragma omp target map(mys->b)
+  {
+    mys->b++;
+  }
+
+  assert (mys->a == 1);
+  assert (mys->b == 1);
+
+  #pragma omp target map(mys->c)
+  {
+    mys->c++;
+  }
+
+  #pragma omp target map(mys->d)
+  {
+    mys->d++;
+  }
+
+  assert (mys->c == &c + 1);
+  assert (mys->d == &c + 1);
+
+  #pragma omp target map(mys->e)
+  {
+    mys->e[0]++;
+  }
+
+  #pragma omp target map(mys->f)
+  {
+    mys->f[0]++;
+  }
+
+  assert (mys->e[0] == 2);
+  assert (mys->f[0] == 2);
+}
+#endif
+
+#ifdef ARRAY_DECL_BASE
+void
+array_decl_base (void)
+{
+  int a = 0, b = 0, c, *d = &c;
+  S mys[4] =
+    {
+      S(a, b, &c, d),
+      S(a, b, &c, d),
+      S(a, b, &c, d),
+      S(a, b, &c, d)
+    };
+
+  #pragma omp target map(mys[2].a)
+  {
+    mys[2].a++;
+  }
+
+  #pragma omp target map(mys[2].b)
+  {
+    mys[2].b++;
+  }
+
+  assert (mys[2].a == 1);
+  assert (mys[2].b == 1);
+
+  #pragma omp target map(mys[2].c)
+  {
+    mys[2].c++;
+  }
+
+  #pragma omp target map(mys[2].d)
+  {
+    mys[2].d++;
+  }
+
+  assert (mys[2].c == &c + 1);
+  assert (mys[2].d == &c + 1);
+
+  #pragma omp target map(mys[2].e)
+  {
+    mys[2].e[0]++;
+  }
+
+  #pragma omp target map(mys[2].f)
+  {
+    mys[2].f[0]++;
+  }
+
+  assert (mys[2].e[0] == 2);
+  assert (mys[2].f[0] == 2);
+}
+#endif
+
+#ifdef REF2ARRAY_DECL_BASE
+void
+ref2array_decl_base (void)
+{
+  int a = 0, b = 0, c, *d = &c;
+  S mys_orig[4] =
+    {
+      S(a, b, &c, d),
+      S(a, b, &c, d),
+      S(a, b, &c, d),
+      S(a, b, &c, d)
+    };
+  S (&mys)[4] = mys_orig;
+
+  #pragma omp target map(mys[2].a)
+  {
+    mys[2].a++;
+  }
+
+  #pragma omp target map(mys[2].b)
+  {
+    mys[2].b++;
+  }
+
+  assert (mys[2].a == 1);
+  assert (mys[2].b == 1);
+
+  #pragma omp target map(mys[2].c)
+  {
+    mys[2].c++;
+  }
+
+  #pragma omp target map(mys[2].d)
+  {
+    mys[2].d++;
+  }
+
+  assert (mys[2].c == &c + 1);
+  assert (mys[2].d == &c + 1);
+
+  #pragma omp target map(mys[2].e)
+  {
+    mys[2].e[0]++;
+  }
+
+  #pragma omp target map(mys[2].f)
+  {
+    mys[2].f[0]++;
+  }
+
+  assert (mys[2].e[0] == 2);
+  assert (mys[2].f[0] == 2);
+}
+#endif
+
+#ifdef PTR_OFFSET_DECL_BASE
+void
+ptr_offset_decl_base (void)
+{
+  int a = 0, b = 0, c, *d = &c;
+  S mys_orig[4] =
+    {
+      S(a, b, &c, d),
+      S(a, b, &c, d),
+      S(a, b, &c, d),
+      S(a, b, &c, d)
+    };
+  S *mys = &mys_orig[0];
+
+  #pragma omp target map(mys[2].a)
+  {
+    mys[2].a++;
+  }
+
+  #pragma omp target map(mys[2].b)
+  {
+    mys[2].b++;
+  }
+
+  assert (mys[2].a == 1);
+  assert (mys[2].b == 1);
+
+  #pragma omp target map(mys[2].c)
+  {
+    mys[2].c++;
+  }
+
+  #pragma omp target map(mys[2].d)
+  {
+    mys[2].d++;
+  }
+
+  assert (mys[2].c == &c + 1);
+  assert (mys[2].d == &c + 1);
+
+  #pragma omp target map(mys[2].e)
+  {
+    mys[2].e[0]++;
+  }
+
+  #pragma omp target map(mys[2].f)
+  {
+    mys[2].f[0]++;
+  }
+
+  assert (mys[2].e[0] == 2);
+  assert (mys[2].f[0] == 2);
+}
+#endif
+
+#ifdef REF2PTR_OFFSET_DECL_BASE
+void
+ref2ptr_offset_decl_base (void)
+{
+  int a = 0, b = 0, c, *d = &c;
+  S mys_orig[4] =
+    {
+      S(a, b, &c, d),
+      S(a, b, &c, d),
+      S(a, b, &c, d),
+      S(a, b, &c, d)
+    };
+  S *mys_ptr = &mys_orig[0];
+  S *&mys = mys_ptr;
+
+  #pragma omp target map(mys[2].a)
+  {
+    mys[2].a++;
+  }
+
+  #pragma omp target map(mys[2].b)
+  {
+    mys[2].b++;
+  }
+
+  assert (mys[2].a == 1);
+  assert (mys[2].b == 1);
+
+  #pragma omp target map(mys[2].c)
+  {
+    mys[2].c++;
+  }
+
+  #pragma omp target map(mys[2].d)
+  {
+    mys[2].d++;
+  }
+
+  assert (mys[2].c == &c + 1);
+  assert (mys[2].d == &c + 1);
+
+  #pragma omp target map(mys[2].e)
+  {
+    mys[2].e[0]++;
+  }
+
+  #pragma omp target map(mys[2].f)
+  {
+    mys[2].f[0]++;
+  }
+
+  assert (mys[2].e[0] == 2);
+  assert (mys[2].f[0] == 2);
+}
+#endif
+
+#ifdef MAP_SECTIONS
+void
+map_sections (void)
+{
+  int arr[10];
+  int *ptr;
+  int (&arrref)[10] = arr;
+  int *&ptrref = ptr;
+
+  ptr = new int[10];
+  memset (ptr, 0, sizeof (int) * 10);
+  memset (arr, 0, sizeof (int) * 10);
+
+  #pragma omp target map(arr[0:10])
+  {
+    arr[2]++;
+  }
+
+  #pragma omp target map(ptr[0:10])
+  {
+    ptr[2]++;
+  }
+
+  #pragma omp target map(arrref[0:10])
+  {
+    arrref[2]++;
+  }
+
+  #pragma omp target map(ptrref[0:10])
+  {
+    ptrref[2]++;
+  }
+
+  assert (arr[2] == 2);
+  assert (ptr[2] == 2);
+
+  delete ptr;
+}
+#endif
+
+struct T {
+  int a[10];
+  int (&b)[10];
+  int *c;
+  int *&d;
+
+  T(int (&b1)[10], int *c1, int *&d1) : b(b1), c(c1), d(d1)
+  {
+    memset (a, 0, sizeof a);
+  }
+};
+
+#ifdef NONREF_DECL_MEMBER_SLICE
+void
+nonref_decl_member_slice (void)
+{
+  int c[10];
+  int *d = &c[0];
+  T myt(c, &c[0], d);
+
+  memset (c, 0, sizeof c);
+
+  #pragma omp target map(myt.a[0:10])
+  {
+    myt.a[2]++;
+  }
+
+  #pragma omp target map(myt.b[0:10])
+  {
+    myt.b[2]++;
+  }
+
+  #pragma omp target enter data map(to: myt.c)
+
+  #pragma omp target map(myt.c[0:10])
+  {
+    myt.c[2]++;
+  }
+
+  #pragma omp target exit data map(release: myt.c)
+
+  #pragma omp target enter data map(to: myt.d)
+
+  #pragma omp target map(myt.d[0:10])
+  {
+    myt.d[2]++;
+  }
+
+  #pragma omp target exit data map(from: myt.d)
+
+  assert (myt.a[2] == 1);
+  assert (myt.b[2] == 3);
+  assert (myt.c[2] == 3);
+  assert (myt.d[2] == 3);
+}
+#endif
+
+#ifdef NONREF_DECL_MEMBER_SLICE_BASEPTR
+void
+nonref_decl_member_slice_baseptr (void)
+{
+  int c[10];
+  int *d = &c[0];
+  T myt(c, &c[0], d);
+
+  memset (c, 0, sizeof c);
+
+  #pragma omp target map(to:myt.c) map(myt.c[0:10])
+  {
+    myt.c[2]++;
+  }
+
+  #pragma omp target map(to:myt.d) map(myt.d[0:10])
+  {
+    myt.d[2]++;
+  }
+
+  assert (myt.c[2] == 2);
+  assert (myt.d[2] == 2);
+}
+#endif
+
+#ifdef REF_DECL_MEMBER_SLICE
+void
+ref_decl_member_slice (void)
+{
+  int c[10];
+  int *d = &c[0];
+  T myt_real(c, &c[0], d);
+  T &myt = myt_real;
+
+  memset (c, 0, sizeof c);
+
+  #pragma omp target map(myt.a[0:10])
+  {
+    myt.a[2]++;
+  }
+
+  #pragma omp target map(myt.b[0:10])
+  {
+    myt.b[2]++;
+  }
+
+  #pragma omp target enter data map(to: myt.c)
+
+  #pragma omp target map(myt.c[0:10])
+  {
+    myt.c[2]++;
+  }
+
+  #pragma omp target exit data map(release: myt.c)
+
+  #pragma omp target enter data map(to: myt.d)
+
+  #pragma omp target map(myt.d[0:10])
+  {
+    myt.d[2]++;
+  }
+
+  #pragma omp target exit data map(release: myt.d)
+
+  assert (myt.a[2] == 1);
+  assert (myt.b[2] == 3);
+  assert (myt.c[2] == 3);
+  assert (myt.d[2] == 3);
+}
+#endif
+
+#ifdef REF_DECL_MEMBER_SLICE_BASEPTR
+void
+ref_decl_member_slice_baseptr (void)
+{
+  int c[10];
+  int *d = &c[0];
+  T myt_real(c, &c[0], d);
+  T &myt = myt_real;
+
+  memset (c, 0, sizeof c);
+
+  #pragma omp target map(to:myt.c) map(myt.c[0:10])
+  {
+    myt.c[2]++;
+  }
+
+  #pragma omp target map(to:myt.d) map(myt.d[0:10])
+  {
+    myt.d[2]++;
+  }
+
+  assert (myt.c[2] == 2);
+  assert (myt.d[2] == 2);
+}
+#endif
+
+#ifdef PTR_DECL_MEMBER_SLICE
+void
+ptr_decl_member_slice (void)
+{
+  int c[10];
+  int *d = &c[0];
+  T myt_real(c, &c[0], d);
+  T *myt = &myt_real;
+
+  memset (c, 0, sizeof c);
+
+  #pragma omp target enter data map(to: myt)
+
+  #pragma omp target map(myt->a[0:10])
+  {
+    myt->a[2]++;
+  }
+
+  #pragma omp target map(myt->b[0:10])
+  {
+    myt->b[2]++;
+  }
+
+  #pragma omp target enter data map(to: myt->c)
+
+  #pragma omp target map(myt->c[0:10])
+  {
+    myt->c[2]++;
+  }
+
+  #pragma omp target exit data map(release: myt->c)
+
+  #pragma omp target enter data map(to: myt->d)
+
+  #pragma omp target map(myt->d[0:10])
+  {
+    myt->d[2]++;
+  }
+
+  #pragma omp target exit data map(release: myt, myt->d)
+
+  assert (myt->a[2] == 1);
+  assert (myt->b[2] == 3);
+  assert (myt->c[2] == 3);
+  assert (myt->d[2] == 3);
+}
+#endif
+
+#ifdef PTR_DECL_MEMBER_SLICE_BASEPTR
+void
+ptr_decl_member_slice_baseptr (void)
+{
+  int c[10];
+  int *d = &c[0];
+  T myt_real(c, &c[0], d);
+  T *myt = &myt_real;
+
+  memset (c, 0, sizeof c);
+
+  // These ones have an implicit firstprivate for 'myt'.
+  #pragma omp target map(to:myt->c) map(myt->c[0:10])
+  {
+    myt->c[2]++;
+  }
+
+  #pragma omp target map(to:myt->d) map(myt->d[0:10])
+  {
+    myt->d[2]++;
+  }
+
+  // These ones have an explicit "TO" mapping for 'myt'.
+  #pragma omp target map(to:myt) map(to:myt->c) map(myt->c[0:10])
+  {
+    myt->c[2]++;
+  }
+
+  #pragma omp target map(to:myt) map(to:myt->d) map(myt->d[0:10])
+  {
+    myt->d[2]++;
+  }
+
+  assert (myt->c[2] == 4);
+  assert (myt->d[2] == 4);
+}
+#endif
+
+#ifdef REF2PTR_DECL_MEMBER_SLICE
+void
+ref2ptr_decl_member_slice (void)
+{
+  int c[10];
+  int *d = &c[0];
+  T myt_real(c, &c[0], d);
+  T *myt_ptr = &myt_real;
+  T *&myt = myt_ptr;
+
+  memset (c, 0, sizeof c);
+
+  #pragma omp target enter data map(to: myt)
+
+  #pragma omp target map(myt->a[0:10])
+  {
+    myt->a[2]++;
+  }
+
+  #pragma omp target map(myt->b[0:10])
+  {
+    myt->b[2]++;
+  }
+
+  #pragma omp target enter data map(to: myt->c)
+
+  #pragma omp target map(myt->c[0:10])
+  {
+    myt->c[2]++;
+  }
+
+  #pragma omp target exit data map(release: myt->c)
+
+  #pragma omp target enter data map(to: myt->d)
+
+  #pragma omp target map(myt->d[0:10])
+  {
+    myt->d[2]++;
+  }
+
+  #pragma omp target exit data map(from: myt, myt->d)
+
+  assert (myt->a[2] == 1);
+  assert (myt->b[2] == 3);
+  assert (myt->c[2] == 3);
+  assert (myt->d[2] == 3);
+}
+#endif
+
+#ifdef REF2PTR_DECL_MEMBER_SLICE_BASEPTR
+void
+ref2ptr_decl_member_slice_baseptr (void)
+{
+  int c[10];
+  int *d = &c[0];
+  T myt_real(c, &c[0], d);
+  T *myt_ptr = &myt_real;
+  T *&myt = myt_ptr;
+
+  memset (c, 0, sizeof c);
+
+  // These ones have an implicit firstprivate for 'myt'.
+  #pragma omp target map(to:myt->c) map(myt->c[0:10])
+  {
+    myt->c[2]++;
+  }
+
+  #pragma omp target map(to:myt->d) map(myt->d[0:10])
+  {
+    myt->d[2]++;
+  }
+
+  // These ones have an explicit "TO" mapping for 'myt'.
+  #pragma omp target map(to:myt) map(to:myt->c) map(myt->c[0:10])
+  {
+    myt->c[2]++;
+  }
+
+  #pragma omp target map(to:myt) map(to:myt->d) map(myt->d[0:10])
+  {
+    myt->d[2]++;
+  }
+
+  assert (myt->c[2] == 4);
+  assert (myt->d[2] == 4);
+}
+#endif
+
+#ifdef ARRAY_DECL_MEMBER_SLICE
+void
+array_decl_member_slice (void)
+{
+  int c[10];
+  int *d = &c[0];
+  T myt[4] =
+    {
+      T (c, &c[0], d),
+      T (c, &c[0], d),
+      T (c, &c[0], d),
+      T (c, &c[0], d)
+    };
+
+  memset (c, 0, sizeof c);
+
+  #pragma omp target map(myt[2].a[0:10])
+  {
+    myt[2].a[2]++;
+  }
+
+  #pragma omp target map(myt[2].b[0:10])
+  {
+    myt[2].b[2]++;
+  }
+
+  #pragma omp target enter data map(to: myt[2].c)
+
+  #pragma omp target map(myt[2].c[0:10])
+  {
+    myt[2].c[2]++;
+  }
+
+  #pragma omp target exit data map(release: myt[2].c)
+
+  #pragma omp target enter data map(to: myt[2].d)
+
+  #pragma omp target map(myt[2].d[0:10])
+  {
+    myt[2].d[2]++;
+  }
+
+  #pragma omp target exit data map(release: myt[2].d)
+
+  assert (myt[2].a[2] == 1);
+  assert (myt[2].b[2] == 3);
+  assert (myt[2].c[2] == 3);
+  assert (myt[2].d[2] == 3);
+}
+#endif
+
+#ifdef ARRAY_DECL_MEMBER_SLICE_BASEPTR
+void
+array_decl_member_slice_baseptr (void)
+{
+  int c[10];
+  int *d = &c[0];
+  T myt[4] =
+    {
+      T (c, &c[0], d),
+      T (c, &c[0], d),
+      T (c, &c[0], d),
+      T (c, &c[0], d)
+    };
+
+  memset (c, 0, sizeof c);
+
+  #pragma omp target map(to:myt[2].c) map(myt[2].c[0:10])
+  {
+    myt[2].c[2]++;
+  }
+
+  #pragma omp target map(to:myt[2].d) map(myt[2].d[0:10])
+  {
+    myt[2].d[2]++;
+  }
+
+  assert (myt[2].c[2] == 2);
+  assert (myt[2].d[2] == 2);
+}
+#endif
+
+#ifdef REF2ARRAY_DECL_MEMBER_SLICE
+void
+ref2array_decl_member_slice (void)
+{
+  int c[10];
+  int *d = &c[0];
+  T myt_real[4] =
+    {
+      T (c, &c[0], d),
+      T (c, &c[0], d),
+      T (c, &c[0], d),
+      T (c, &c[0], d)
+    };
+  T (&myt)[4] = myt_real;
+
+  memset (c, 0, sizeof c);
+
+  #pragma omp target map(myt[2].a[0:10])
+  {
+    myt[2].a[2]++;
+  }
+
+  #pragma omp target map(myt[2].b[0:10])
+  {
+    myt[2].b[2]++;
+  }
+
+  #pragma omp target enter data map(to: myt[2].c)
+
+  #pragma omp target map(myt[2].c[0:10])
+  {
+    myt[2].c[2]++;
+  }
+
+  #pragma omp target exit data map(release: myt[2].c)
+
+  #pragma omp target enter data map(to: myt[2].d)
+
+  #pragma omp target map(myt[2].d[0:10])
+  {
+    myt[2].d[2]++;
+  }
+
+  #pragma omp target exit data map(release: myt[2].d)
+
+  assert (myt[2].a[2] == 1);
+  assert (myt[2].b[2] == 3);
+  assert (myt[2].c[2] == 3);
+  assert (myt[2].d[2] == 3);
+}
+#endif
+
+#ifdef REF2ARRAY_DECL_MEMBER_SLICE_BASEPTR
+void
+ref2array_decl_member_slice_baseptr (void)
+{
+  int c[10];
+  int *d = &c[0];
+  T myt_real[4] =
+    {
+      T (c, &c[0], d),
+      T (c, &c[0], d),
+      T (c, &c[0], d),
+      T (c, &c[0], d)
+    };
+  T (&myt)[4] = myt_real;
+
+  memset (c, 0, sizeof c);
+
+  #pragma omp target map(to:myt[2].c) map(myt[2].c[0:10])
+  {
+    myt[2].c[2]++;
+  }
+
+  #pragma omp target map(to:myt[2].d) map(myt[2].d[0:10])
+  {
+    myt[2].d[2]++;
+  }
+
+  assert (myt[2].c[2] == 2);
+  assert (myt[2].d[2] == 2);
+}
+#endif
+
+#ifdef PTR_OFFSET_DECL_MEMBER_SLICE
+void
+ptr_offset_decl_member_slice (void)
+{
+  int c[10];
+  int *d = &c[0];
+  T myt_real[4] =
+    {
+      T (c, &c[0], d),
+      T (c, &c[0], d),
+      T (c, &c[0], d),
+      T (c, &c[0], d)
+    };
+  T *myt = &myt_real[0];
+
+  memset (c, 0, sizeof c);
+
+  #pragma omp target map(myt[2].a[0:10])
+  {
+    myt[2].a[2]++;
+  }
+
+  #pragma omp target map(myt[2].b[0:10])
+  {
+    myt[2].b[2]++;
+  }
+
+  #pragma omp target enter data map(to: myt[2].c)
+
+  #pragma omp target map(myt[2].c[0:10])
+  {
+    myt[2].c[2]++;
+  }
+
+  #pragma omp target exit data map(release: myt[2].c)
+
+  #pragma omp target enter data map(to: myt[2].d)
+
+  #pragma omp target map(myt[2].d[0:10])
+  {
+    myt[2].d[2]++;
+  }
+
+  #pragma omp target exit data map(release: myt[2].d)
+
+  assert (myt[2].a[2] == 1);
+  assert (myt[2].b[2] == 3);
+  assert (myt[2].c[2] == 3);
+  assert (myt[2].d[2] == 3);
+}
+#endif
+
+#ifdef PTR_OFFSET_DECL_MEMBER_SLICE_BASEPTR
+void
+ptr_offset_decl_member_slice_baseptr (void)
+{
+  int c[10];
+  int *d = &c[0];
+  T myt_real[4] =
+    {
+      T (c, &c[0], d),
+      T (c, &c[0], d),
+      T (c, &c[0], d),
+      T (c, &c[0], d)
+    };
+  T *myt = &myt_real[0];
+
+  memset (c, 0, sizeof c);
+
+  /* Implicit 'myt'.  */
+  #pragma omp target map(to:myt[2].c) map(myt[2].c[0:10])
+  {
+    myt[2].c[2]++;
+  }
+
+  #pragma omp target map(to:myt[2].d) map(myt[2].d[0:10])
+  {
+    myt[2].d[2]++;
+  }
+
+  /* Explicit 'to'-mapped 'myt'.  */
+  #pragma omp target map(to:myt) map(to:myt[2].c) map(myt[2].c[0:10])
+  {
+    myt[2].c[2]++;
+  }
+
+  #pragma omp target map(to:myt) map(to:myt[2].d) map(myt[2].d[0:10])
+  {
+    myt[2].d[2]++;
+  }
+
+  assert (myt[2].c[2] == 4);
+  assert (myt[2].d[2] == 4);
+}
+#endif
+
+#ifdef REF2PTR_OFFSET_DECL_MEMBER_SLICE
+void
+ref2ptr_offset_decl_member_slice (void)
+{
+  int c[10];
+  int *d = &c[0];
+  T myt_real[4] =
+    {
+      T (c, &c[0], d),
+      T (c, &c[0], d),
+      T (c, &c[0], d),
+      T (c, &c[0], d)
+    };
+  T *myt_ptr = &myt_real[0];
+  T *&myt = myt_ptr;
+
+  memset (c, 0, sizeof c);
+
+  #pragma omp target map(myt[2].a[0:10])
+  {
+    myt[2].a[2]++;
+  }
+
+  #pragma omp target map(myt[2].b[0:10])
+  {
+    myt[2].b[2]++;
+  }
+
+  #pragma omp target enter data map(to: myt[2].c)
+
+  #pragma omp target map(myt[2].c[0:10])
+  {
+    myt[2].c[2]++;
+  }
+
+  #pragma omp target exit data map(release: myt[2].c)
+
+  #pragma omp target enter data map(to: myt[2].d)
+
+  #pragma omp target map(myt[2].d[0:10])
+  {
+    myt[2].d[2]++;
+  }
+
+  #pragma omp target exit data map(release: myt[2].d)
+
+  assert (myt[2].a[2] == 1);
+  assert (myt[2].b[2] == 3);
+  assert (myt[2].c[2] == 3);
+  assert (myt[2].d[2] == 3);
+}
+#endif
+
+#ifdef REF2PTR_OFFSET_DECL_MEMBER_SLICE_BASEPTR
+void
+ref2ptr_offset_decl_member_slice_baseptr (void)
+{
+  int c[10];
+  int *d = &c[0];
+  T myt_real[4] =
+    {
+      T (c, &c[0], d),
+      T (c, &c[0], d),
+      T (c, &c[0], d),
+      T (c, &c[0], d)
+    };
+  T *myt_ptr = &myt_real[0];
+  T *&myt = myt_ptr;
+
+  memset (c, 0, sizeof c);
+
+  /* Implicit 'myt'.  */
+  #pragma omp target map(to:myt[2].c) map(myt[2].c[0:10])
+  {
+    myt[2].c[2]++;
+  }
+
+  #pragma omp target map(to:myt[2].d) map(myt[2].d[0:10])
+  {
+    myt[2].d[2]++;
+  }
+
+  /* Explicit 'to'-mapped 'myt'.  */
+  #pragma omp target map(to:myt) map(to:myt[2].c) map(myt[2].c[0:10])
+  {
+    myt[2].c[2]++;
+  }
+
+  #pragma omp target map(to:myt) map(to:myt[2].d) map(myt[2].d[0:10])
+  {
+    myt[2].d[2]++;
+  }
+
+  assert (myt[2].c[2] == 4);
+  assert (myt[2].d[2] == 4);
+}
+#endif
+
+#ifdef PTRARRAY_DECL_MEMBER_SLICE
+void
+ptrarray_decl_member_slice (void)
+{
+  int c[10];
+  int *d = &c[0];
+  T myt_real(c, &c[0], d);
+  T *myt[4] =
+    {
+      &myt_real,
+      &myt_real,
+      &myt_real,
+      &myt_real
+    };
+
+  memset (c, 0, sizeof c);
+
+  #pragma omp target enter data map(to: myt[2])
+
+  #pragma omp target map(myt[2]->a[0:10])
+  {
+    myt[2]->a[2]++;
+  }
+
+  #pragma omp target map(myt[2]->b[0:10])
+  {
+    myt[2]->b[2]++;
+  }
+
+  #pragma omp target enter data map(to: myt[2]->c)
+
+  #pragma omp target map(myt[2]->c[0:10])
+  {
+    myt[2]->c[2]++;
+  }
+
+  #pragma omp target exit data map(from: myt[2]->c)
+
+  #pragma omp target enter data map(to: myt[2]->d)
+
+  #pragma omp target map(myt[2]->d[0:10])
+  {
+    myt[2]->d[2]++;
+  }
+
+  #pragma omp target exit data map(from: myt[2]->d)
+
+  #pragma omp target exit data map(release: myt[2])
+
+  assert (myt[2]->a[2] == 1);
+  assert (myt[2]->b[2] == 3);
+  assert (myt[2]->c[2] == 3);
+  assert (myt[2]->d[2] == 3);
+}
+#endif
+
+#ifdef PTRARRAY_DECL_MEMBER_SLICE_BASEPTR
+void
+ptrarray_decl_member_slice_baseptr (void)
+{
+  int c[10];
+  int *d = &c[0];
+  T myt_real(c, &c[0], d);
+  T *myt[4] =
+    {
+      &myt_real,
+      &myt_real,
+      &myt_real,
+      &myt_real
+    };
+
+  memset (c, 0, sizeof c);
+
+  // Implicit 'myt'
+  #pragma omp target map(to: myt[2]->c) map(myt[2]->c[0:10])
+  {
+    myt[2]->c[2]++;
+  }
+
+  #pragma omp target map(to: myt[2]->d) map(myt[2]->d[0:10])
+  {
+    myt[2]->d[2]++;
+  }
+
+  // One element of 'myt'
+  #pragma omp target map(to:myt[2], myt[2]->c) map(myt[2]->c[0:10])
+  {
+    myt[2]->c[2]++;
+  }
+
+  #pragma omp target map(to:myt[2], myt[2]->d) map(myt[2]->d[0:10])
+  {
+    myt[2]->d[2]++;
+  }
+
+  // Explicit map of all of 'myt'
+  #pragma omp target map(to:myt, myt[2]->c) map(myt[2]->c[0:10])
+  {
+    myt[2]->c[2]++;
+  }
+
+  #pragma omp target map(to:myt, myt[2]->d) map(myt[2]->d[0:10])
+  {
+    myt[2]->d[2]++;
+  }
+
+  // Explicit map slice of 'myt'
+  #pragma omp target map(to:myt[1:3], myt[2]->c) map(myt[2]->c[0:10])
+  {
+    myt[2]->c[2]++;
+  }
+
+  #pragma omp target map(to:myt[1:3], myt[2]->d) map(myt[2]->d[0:10])
+  {
+    myt[2]->d[2]++;
+  }
+
+  assert (myt[2]->c[2] == 8);
+  assert (myt[2]->d[2] == 8);
+}
+#endif
+
+#ifdef REF2PTRARRAY_DECL_MEMBER_SLICE
+void
+ref2ptrarray_decl_member_slice (void)
+{
+  int c[10];
+  int *d = &c[0];
+  T myt_real(c, &c[0], d);
+  T *myt_ptrarr[4] =
+    {
+      &myt_real,
+      &myt_real,
+      &myt_real,
+      &myt_real
+    };
+  T *(&myt)[4] = myt_ptrarr;
+
+  memset (c, 0, sizeof c);
+
+  #pragma omp target enter data map(to: myt[2])
+
+  #pragma omp target map(myt[2]->a[0:10])
+  {
+    myt[2]->a[2]++;
+  }
+
+  #pragma omp target map(myt[2]->b[0:10])
+  {
+    myt[2]->b[2]++;
+  }
+
+  #pragma omp target enter data map(to: myt[2]->c)
+
+  #pragma omp target map(myt[2]->c[0:10])
+  {
+    myt[2]->c[2]++;
+  }
+
+  #pragma omp target exit data map(release: myt[2]->c)
+
+  #pragma omp target enter data map(to: myt[2]->d)
+
+  #pragma omp target map(myt[2]->d[0:10])
+  {
+    myt[2]->d[2]++;
+  }
+
+  #pragma omp target exit data map(release: myt[2]->d)
+
+  #pragma omp target exit data map(release: myt[2])
+
+  assert (myt[2]->a[2] == 1);
+  assert (myt[2]->b[2] == 3);
+  assert (myt[2]->c[2] == 3);
+  assert (myt[2]->d[2] == 3);
+}
+#endif
+
+#ifdef REF2PTRARRAY_DECL_MEMBER_SLICE_BASEPTR
+void
+ref2ptrarray_decl_member_slice_baseptr (void)
+{
+  int c[10];
+  int *d = &c[0];
+  T myt_real(c, &c[0], d);
+  T *myt_ptrarr[4] =
+    {
+      &myt_real,
+      &myt_real,
+      &myt_real,
+      &myt_real
+    };
+  T *(&myt)[4] = myt_ptrarr;
+
+  memset (c, 0, sizeof c);
+
+  #pragma omp target map(to:myt[2], myt[2]->c) map(myt[2]->c[0:10])
+  {
+    myt[2]->c[2]++;
+  }
+
+  #pragma omp target map(to:myt[2], myt[2]->d) map(myt[2]->d[0:10])
+  {
+    myt[2]->d[2]++;
+  }
+
+  #pragma omp target map(to:myt, myt[2]->c) map(myt[2]->c[0:10])
+  {
+    myt[2]->c[2]++;
+  }
+
+  #pragma omp target map(to:myt, myt[2]->d) map(myt[2]->d[0:10])
+  {
+    myt[2]->d[2]++;
+  }
+
+  assert (myt[2]->c[2] == 4);
+  assert (myt[2]->d[2] == 4);
+}
+#endif
+
+#ifdef PTRPTR_OFFSET_DECL_MEMBER_SLICE
+void
+ptrptr_offset_decl_member_slice (void)
+{
+  int c[10];
+  int *d = &c[0];
+  T myt_real(c, &c[0], d);
+  T *myt_ptrarr[4] =
+    {
+      &myt_real,
+      &myt_real,
+      &myt_real,
+      &myt_real
+    };
+  T **myt = &myt_ptrarr[0];
+
+  memset (c, 0, sizeof c);
+
+  #pragma omp target enter data map(to: myt[0:3])
+
+  /* NOTE: For the implicit firstprivate 'myt' to work, the zeroth element of
+     myt[] must be mapped above -- otherwise the zero-length array section
+     lookup fails.  */
+  #pragma omp target map(myt[2]->a[0:10])
+  {
+    myt[2]->a[2]++;
+  }
+
+  #pragma omp target map(myt[2]->b[0:10])
+  {
+    myt[2]->b[2]++;
+  }
+
+  #pragma omp target enter data map(to: myt[2]->c)
+
+  #pragma omp target map(myt[2]->c[0:10])
+  {
+    myt[2]->c[2]++;
+  }
+
+  #pragma omp target exit data map(from: myt[2]->c)
+
+  #pragma omp target enter data map(to: myt[2]->d)
+
+  #pragma omp target map(myt[2]->d[0:10])
+  {
+    myt[2]->d[2]++;
+  }
+
+  #pragma omp target exit data map(from: myt[0:3], myt[2]->d)
+
+  assert (myt[2]->a[2] == 1);
+  assert (myt[2]->b[2] == 3);
+  assert (myt[2]->c[2] == 3);
+  assert (myt[2]->d[2] == 3);
+}
+#endif
+
+#ifdef PTRPTR_OFFSET_DECL_MEMBER_SLICE_BASEPTR
+void
+ptrptr_offset_decl_member_slice_baseptr (void)
+{
+  int c[10];
+  int *d = &c[0];
+  T myt_real(c, &c[0], d);
+  T *myt_ptrarr[4] =
+    {
+      0,
+      0,
+      0,
+      &myt_real
+    };
+  T **myt = &myt_ptrarr[0];
+
+  memset (c, 0, sizeof c);
+
+  #pragma omp target map(to:myt[3], myt[3]->c) map(myt[3]->c[0:10])
+  {
+    myt[3]->c[2]++;
+  }
+
+  #pragma omp target map(to:myt[3], myt[3]->d) map(myt[3]->d[0:10])
+  {
+    myt[3]->d[2]++;
+  }
+
+  #pragma omp target map(to:myt, myt[3], myt[3]->c) map(myt[3]->c[0:10])
+  {
+    myt[3]->c[2]++;
+  }
+
+  #pragma omp target map(to:myt, myt[3], myt[3]->d) map(myt[3]->d[0:10])
+  {
+    myt[3]->d[2]++;
+  }
+
+  assert (myt[3]->c[2] == 4);
+  assert (myt[3]->d[2] == 4);
+}
+#endif
+
+#ifdef REF2PTRPTR_OFFSET_DECL_MEMBER_SLICE
+void
+ref2ptrptr_offset_decl_member_slice (void)
+{
+  int c[10];
+  int *d = &c[0];
+  T myt_real(c, &c[0], d);
+  T *myt_ptrarr[4] =
+    {
+      0,
+      0,
+      &myt_real,
+      0
+    };
+  T **myt_ptrptr = &myt_ptrarr[0];
+  T **&myt = myt_ptrptr;
+
+  memset (c, 0, sizeof c);
+
+  #pragma omp target enter data map(to: myt[0:3])
+
+  #pragma omp target map(myt[2]->a[0:10])
+  {
+    myt[2]->a[2]++;
+  }
+
+  #pragma omp target map(myt[2]->b[0:10])
+  {
+    myt[2]->b[2]++;
+  }
+
+  #pragma omp target enter data map(to:myt[2]->c)
+
+  #pragma omp target map(myt[2]->c[0:10])
+  {
+    myt[2]->c[2]++;
+  }
+
+  #pragma omp target exit data map(release:myt[2]->c)
+
+  #pragma omp target enter data map(to:myt[2]->d)
+
+  #pragma omp target map(myt[2]->d[0:10])
+  {
+    myt[2]->d[2]++;
+  }
+
+  #pragma omp target exit data map(release: myt[0:3], myt[2]->d)
+
+  assert (myt[2]->a[2] == 1);
+  assert (myt[2]->b[2] == 3);
+  assert (myt[2]->c[2] == 3);
+  assert (myt[2]->d[2] == 3);
+}
+#endif
+
+#ifdef REF2PTRPTR_OFFSET_DECL_MEMBER_SLICE_BASEPTR
+void
+ref2ptrptr_offset_decl_member_slice_baseptr (void)
+{
+  int c[10];
+  int *d = &c[0];
+  T myt_real(c, &c[0], d);
+  T *myt_ptrarr[4] =
+    {
+      0,
+      0,
+      &myt_real,
+      0
+    };
+  T **myt_ptrptr = &myt_ptrarr[0];
+  T **&myt = myt_ptrptr;
+
+  memset (c, 0, sizeof c);
+
+  #pragma omp target map(to:myt[2], myt[2]->c) map(myt[2]->c[0:10])
+  {
+    myt[2]->c[2]++;
+  }
+
+  #pragma omp target map(to:myt[2], myt[2]->d) map(myt[2]->d[0:10])
+  {
+    myt[2]->d[2]++;
+  }
+
+  #pragma omp target map(to:myt, myt[2], myt[2]->c) map(myt[2]->c[0:10])
+  {
+    myt[2]->c[2]++;
+  }
+
+  #pragma omp target map(to:myt, myt[2], myt[2]->d) map(myt[2]->d[0:10])
+  {
+    myt[2]->d[2]++;
+  }
+
+  assert (myt[2]->c[2] == 4);
+  assert (myt[2]->d[2] == 4);
+}
+#endif
+
+struct U
+{
+  S s1;
+  T t1;
+  S &s2;
+  T &t2;
+  S *s3;
+  T *t3;
+  S *&s4;
+  T *&t4;
+
+  U(S &sptr1, T &tptr1, S &sptr2, T &tptr2, S *sptr3, T *tptr3,
+    S *&sptr4, T *&tptr4)
+    : s1(sptr1), t1(tptr1), s2(sptr2), t2(tptr2), s3(sptr3), t3(tptr3),
+      s4(sptr4), t4(tptr4)
+  {
+  }
+};
+
+#define INIT_S(N)				\
+  int a##N = 0, b##N = 0, c##N = 0, d##N = 0;	\
+  int *d##N##ptr = &d##N;			\
+  S s##N(a##N, b##N, &c##N, d##N##ptr)
+
+#define INIT_T(N)				\
+  int arr##N[10];				\
+  int *ptr##N = &arr##N[0];			\
+  T t##N(arr##N, &arr##N[0], ptr##N);		\
+  memset (arr##N, 0, sizeof arr##N)
+
+#define INIT_ST				\
+  INIT_S(1);				\
+  INIT_T(1);				\
+  INIT_S(2);				\
+  INIT_T(2);				\
+  INIT_S(3);				\
+  INIT_T(3);				\
+  int a4 = 0, b4 = 0, c4 = 0, d4 = 0;	\
+  int *d4ptr = &d4;			\
+  S *s4 = new S(a4, b4, &c4, d4ptr);	\
+  int arr4[10];				\
+  int *ptr4 = &arr4[0];			\
+  T *t4 = new T(arr4, &arr4[0], ptr4);	\
+  memset (arr4, 0, sizeof arr4)
+
+#ifdef NONREF_COMPONENT_BASE
+void
+nonref_component_base (void)
+{
+  INIT_ST;
+  U myu(s1, t1, s2, t2, &s3, &t3, s4, t4);
+
+  #pragma omp target map(myu.s1.a, myu.s1.b, myu.s1.c, myu.s1.d)
+  {
+    myu.s1.a++;
+    myu.s1.b++;
+    myu.s1.c++;
+    myu.s1.d++;
+  }
+
+  assert (myu.s1.a == 1);
+  assert (myu.s1.b == 1);
+  assert (myu.s1.c == &c1 + 1);
+  assert (myu.s1.d == &d1 + 1);
+
+  #pragma omp target map(myu.s2.a, myu.s2.b, myu.s2.c, myu.s2.d)
+  {
+    myu.s2.a++;
+    myu.s2.b++;
+    myu.s2.c++;
+    myu.s2.d++;
+  }
+
+  assert (myu.s2.a == 1);
+  assert (myu.s2.b == 1);
+  assert (myu.s2.c == &c2 + 1);
+  assert (myu.s2.d == &d2 + 1);
+
+  #pragma omp target map(to:myu.s3) \
+		     map(myu.s3->a, myu.s3->b, myu.s3->c, myu.s3->d)
+  {
+    myu.s3->a++;
+    myu.s3->b++;
+    myu.s3->c++;
+    myu.s3->d++;
+  }
+
+  assert (myu.s3->a == 1);
+  assert (myu.s3->b == 1);
+  assert (myu.s3->c == &c3 + 1);
+  assert (myu.s3->d == &d3 + 1);
+
+  #pragma omp target map(to:myu.s4) \
+		     map(myu.s4->a, myu.s4->b, myu.s4->c, myu.s4->d)
+  {
+    myu.s4->a++;
+    myu.s4->b++;
+    myu.s4->c++;
+    myu.s4->d++;
+  }
+
+  assert (myu.s4->a == 1);
+  assert (myu.s4->b == 1);
+  assert (myu.s4->c == &c4 + 1);
+  assert (myu.s4->d == &d4 + 1);
+
+  delete s4;
+  delete t4;
+}
+#endif
+
+#ifdef NONREF_COMPONENT_MEMBER_SLICE
+void
+nonref_component_member_slice (void)
+{
+  INIT_ST;
+  U myu(s1, t1, s2, t2, &s3, &t3, s4, t4);
+
+  #pragma omp target map(myu.t1.a[2:5])
+  {
+    myu.t1.a[2]++;
+  }
+
+  #pragma omp target map(myu.t1.b[2:5])
+  {
+    myu.t1.b[2]++;
+  }
+
+  #pragma omp target enter data map(to: myu.t1.c)
+
+  #pragma omp target map(myu.t1.c[2:5])
+  {
+    myu.t1.c[2]++;
+  }
+
+  #pragma omp target exit data map(release: myu.t1.c)
+
+  #pragma omp target enter data map(to: myu.t1.d)
+
+  #pragma omp target map(myu.t1.d[2:5])
+  {
+    myu.t1.d[2]++;
+  }
+
+  #pragma omp target exit data map(from: myu.t1.d)
+
+  assert (myu.t1.a[2] == 1);
+  assert (myu.t1.b[2] == 3);
+  assert (myu.t1.c[2] == 3);
+  assert (myu.t1.d[2] == 3);
+
+  #pragma omp target map(myu.t2.a[2:5])
+  {
+    myu.t2.a[2]++;
+  }
+
+  #pragma omp target map(myu.t2.b[2:5])
+  {
+    myu.t2.b[2]++;
+  }
+
+  #pragma omp target enter data map(to: myu.t2.c)
+
+  #pragma omp target map(myu.t2.c[2:5])
+  {
+    myu.t2.c[2]++;
+  }
+
+  #pragma omp target exit data map(release: myu.t2.c)
+
+  #pragma omp target enter data map(to: myu.t2.d)
+
+  #pragma omp target map(myu.t2.d[2:5])
+  {
+    myu.t2.d[2]++;
+  }
+
+  #pragma omp target exit data map(release: myu.t2.d)
+
+  assert (myu.t2.a[2] == 1);
+  assert (myu.t2.b[2] == 3);
+  assert (myu.t2.c[2] == 3);
+  assert (myu.t2.d[2] == 3);
+
+  #pragma omp target enter data map(to: myu.t3)
+
+  #pragma omp target map(myu.t3->a[2:5])
+  {
+    myu.t3->a[2]++;
+  }
+
+  #pragma omp target map(myu.t3->b[2:5])
+  {
+    myu.t3->b[2]++;
+  }
+
+  #pragma omp target enter data map(to: myu.t3->c)
+
+  #pragma omp target map(myu.t3->c[2:5])
+  {
+    myu.t3->c[2]++;
+  }
+
+  #pragma omp target exit data map(release: myu.t3->c)
+
+  #pragma omp target enter data map(to: myu.t3->d)
+
+  #pragma omp target map(myu.t3->d[2:5])
+  {
+    myu.t3->d[2]++;
+  }
+
+  #pragma omp target exit data map(release: myu.t3, myu.t3->d)
+
+  assert (myu.t3->a[2] == 1);
+  assert (myu.t3->b[2] == 3);
+  assert (myu.t3->c[2] == 3);
+  assert (myu.t3->d[2] == 3);
+
+  #pragma omp target enter data map(to: myu.t4)
+
+  #pragma omp target map(myu.t4->a[2:5])
+  {
+    myu.t4->a[2]++;
+  }
+
+  #pragma omp target map(myu.t4->b[2:5])
+  {
+    myu.t4->b[2]++;
+  }
+
+  #pragma omp target enter data map(to: myu.t4->c)
+
+  #pragma omp target map(myu.t4->c[2:5])
+  {
+    myu.t4->c[2]++;
+  }
+
+  #pragma omp target exit data map(release: myu.t4->c)
+
+  #pragma omp target enter data map(to: myu.t4->d)
+
+  #pragma omp target map(myu.t4->d[2:5])
+  {
+    myu.t4->d[2]++;
+  }
+
+  #pragma omp target exit data map(release: myu.t4, myu.t4->d)
+
+  assert (myu.t4->a[2] == 1);
+  assert (myu.t4->b[2] == 3);
+  assert (myu.t4->c[2] == 3);
+  assert (myu.t4->d[2] == 3);
+
+  delete s4;
+  delete t4;
+}
+#endif
+
+#ifdef NONREF_COMPONENT_MEMBER_SLICE_BASEPTR
+void
+nonref_component_member_slice_baseptr (void)
+{
+  INIT_ST;
+  U myu(s1, t1, s2, t2, &s3, &t3, s4, t4);
+
+  #pragma omp target map(to: myu.t1.c) map(myu.t1.c[2:5])
+  {
+    myu.t1.c[2]++;
+  }
+
+  #pragma omp target map(to: myu.t1.d) map(myu.t1.d[2:5])
+  {
+    myu.t1.d[2]++;
+  }
+
+  assert (myu.t1.c[2] == 2);
+  assert (myu.t1.d[2] == 2);
+
+  #pragma omp target map(to: myu.t2.c) map(myu.t2.c[2:5])
+  {
+    myu.t2.c[2]++;
+  }
+
+  #pragma omp target map(to: myu.t2.d) map(myu.t2.d[2:5])
+  {
+    myu.t2.d[2]++;
+  }
+
+  assert (myu.t2.c[2] == 2);
+  assert (myu.t2.d[2] == 2);
+
+  #pragma omp target map(to: myu.t3, myu.t3->c) map(myu.t3->c[2:5])
+  {
+    myu.t3->c[2]++;
+  }
+
+  #pragma omp target map(to: myu.t3, myu.t3->d) map(myu.t3->d[2:5])
+  {
+    myu.t3->d[2]++;
+  }
+
+  assert (myu.t3->c[2] == 2);
+  assert (myu.t3->d[2] == 2);
+
+  #pragma omp target map(to: myu.t4, myu.t4->c) map(myu.t4->c[2:5])
+  {
+    myu.t4->c[2]++;
+  }
+
+  #pragma omp target map(to: myu.t4, myu.t4->d) map(myu.t4->d[2:5])
+  {
+    myu.t4->d[2]++;
+  }
+
+  assert (myu.t4->c[2] == 2);
+  assert (myu.t4->d[2] == 2);
+
+  delete s4;
+  delete t4;
+}
+#endif
+
+#ifdef REF_COMPONENT_BASE
+void
+ref_component_base (void)
+{
+  INIT_ST;
+  U myu_real(s1, t1, s2, t2, &s3, &t3, s4, t4);
+  U &myu = myu_real;
+
+  #pragma omp target map(myu.s1.a, myu.s1.b, myu.s1.c, myu.s1.d)
+  {
+    myu.s1.a++;
+    myu.s1.b++;
+    myu.s1.c++;
+    myu.s1.d++;
+  }
+
+  assert (myu.s1.a == 1);
+  assert (myu.s1.b == 1);
+  assert (myu.s1.c == &c1 + 1);
+  assert (myu.s1.d == &d1 + 1);
+
+  #pragma omp target map(myu.s2.a, myu.s2.b, myu.s2.c, myu.s2.d)
+  {
+    myu.s2.a++;
+    myu.s2.b++;
+    myu.s2.c++;
+    myu.s2.d++;
+  }
+
+  assert (myu.s2.a == 1);
+  assert (myu.s2.b == 1);
+  assert (myu.s2.c == &c2 + 1);
+  assert (myu.s2.d == &d2 + 1);
+
+  #pragma omp target map(to:myu.s3) \
+		     map(myu.s3->a, myu.s3->b, myu.s3->c, myu.s3->d)
+  {
+    myu.s3->a++;
+    myu.s3->b++;
+    myu.s3->c++;
+    myu.s3->d++;
+  }
+
+  assert (myu.s3->a == 1);
+  assert (myu.s3->b == 1);
+  assert (myu.s3->c == &c3 + 1);
+  assert (myu.s3->d == &d3 + 1);
+
+  #pragma omp target map(to:myu.s4) \
+		     map(myu.s4->a, myu.s4->b, myu.s4->c, myu.s4->d)
+  {
+    myu.s4->a++;
+    myu.s4->b++;
+    myu.s4->c++;
+    myu.s4->d++;
+  }
+
+  assert (myu.s4->a == 1);
+  assert (myu.s4->b == 1);
+  assert (myu.s4->c == &c4 + 1);
+  assert (myu.s4->d == &d4 + 1);
+
+  delete s4;
+  delete t4;
+}
+#endif
+
+#ifdef REF_COMPONENT_MEMBER_SLICE
+void
+ref_component_member_slice (void)
+{
+  INIT_ST;
+  U myu_real(s1, t1, s2, t2, &s3, &t3, s4, t4);
+  U &myu = myu_real;
+
+  #pragma omp target map(myu.t1.a[2:5])
+  {
+    myu.t1.a[2]++;
+  }
+
+  #pragma omp target map(myu.t1.b[2:5])
+  {
+    myu.t1.b[2]++;
+  }
+
+  #pragma omp target enter data map(to: myu.t1.c)
+
+  #pragma omp target map(myu.t1.c[2:5])
+  {
+    myu.t1.c[2]++;
+  }
+
+  #pragma omp target exit data map(release: myu.t1.c)
+
+  #pragma omp target enter data map(to: myu.t1.d)
+
+  #pragma omp target map(myu.t1.d[2:5])
+  {
+    myu.t1.d[2]++;
+  }
+
+  #pragma omp target exit data map(release: myu.t1.d)
+
+  assert (myu.t1.a[2] == 1);
+  assert (myu.t1.b[2] == 3);
+  assert (myu.t1.c[2] == 3);
+  assert (myu.t1.d[2] == 3);
+
+  #pragma omp target map(myu.t2.a[2:5])
+  {
+    myu.t2.a[2]++;
+  }
+
+  #pragma omp target map(myu.t2.b[2:5])
+  {
+    myu.t2.b[2]++;
+  }
+
+  #pragma omp target enter data map(to: myu.t2.c)
+
+  #pragma omp target map(myu.t2.c[2:5])
+  {
+    myu.t2.c[2]++;
+  }
+
+  #pragma omp target exit data map(release: myu.t2.c)
+
+  #pragma omp target enter data map(to: myu.t2.d)
+
+  #pragma omp target map(myu.t2.d[2:5])
+  {
+    myu.t2.d[2]++;
+  }
+
+  #pragma omp target exit data map(release: myu.t2.d)
+
+  assert (myu.t2.a[2] == 1);
+  assert (myu.t2.b[2] == 3);
+  assert (myu.t2.c[2] == 3);
+  assert (myu.t2.d[2] == 3);
+
+  #pragma omp target enter data map(to: myu.t3)
+
+  #pragma omp target map(myu.t3->a[2:5])
+  {
+    myu.t3->a[2]++;
+  }
+
+  #pragma omp target map(myu.t3->b[2:5])
+  {
+    myu.t3->b[2]++;
+  }
+
+  #pragma omp target enter data map(to: myu.t3->c)
+
+  #pragma omp target map(myu.t3->c[2:5])
+  {
+    myu.t3->c[2]++;
+  }
+
+  #pragma omp target exit data map(release: myu.t3->c)
+
+  #pragma omp target enter data map(to: myu.t3->d)
+
+  #pragma omp target map(myu.t3->d[2:5])
+  {
+    myu.t3->d[2]++;
+  }
+
+  #pragma omp target exit data map(release: myu.t3, myu.t3->d)
+
+  assert (myu.t3->a[2] == 1);
+  assert (myu.t3->b[2] == 3);
+  assert (myu.t3->c[2] == 3);
+  assert (myu.t3->d[2] == 3);
+
+  #pragma omp target enter data map(to: myu.t4)
+
+  #pragma omp target map(myu.t4->a[2:5])
+  {
+    myu.t4->a[2]++;
+  }
+
+  #pragma omp target map(myu.t4->b[2:5])
+  {
+    myu.t4->b[2]++;
+  }
+
+  #pragma omp target enter data map(to: myu.t4->c)
+
+  #pragma omp target map(myu.t4->c[2:5])
+  {
+    myu.t4->c[2]++;
+  }
+
+  #pragma omp target exit data map(release: myu.t4->c)
+
+  #pragma omp target enter data map(to: myu.t4->d)
+
+  #pragma omp target map(myu.t4->d[2:5])
+  {
+    myu.t4->d[2]++;
+  }
+
+  #pragma omp target exit data map(release: myu.t4, myu.t4->d)
+
+  assert (myu.t4->a[2] == 1);
+  assert (myu.t4->b[2] == 3);
+  assert (myu.t4->c[2] == 3);
+  assert (myu.t4->d[2] == 3);
+
+  delete s4;
+  delete t4;
+}
+#endif
+
+#ifdef REF_COMPONENT_MEMBER_SLICE_BASEPTR
+void
+ref_component_member_slice_baseptr (void)
+{
+  INIT_ST;
+  U myu_real(s1, t1, s2, t2, &s3, &t3, s4, t4);
+  U &myu = myu_real;
+
+  #pragma omp target map(to: myu.t1.c) map(myu.t1.c[2:5])
+  {
+    myu.t1.c[2]++;
+  }
+
+  #pragma omp target map(to: myu.t1.d) map(myu.t1.d[2:5])
+  {
+    myu.t1.d[2]++;
+  }
+
+  assert (myu.t1.c[2] == 2);
+  assert (myu.t1.d[2] == 2);
+
+  #pragma omp target map(to: myu.t2.c) map(myu.t2.c[2:5])
+  {
+    myu.t2.c[2]++;
+  }
+
+  #pragma omp target map(to: myu.t2.d) map(myu.t2.d[2:5])
+  {
+    myu.t2.d[2]++;
+  }
+
+  assert (myu.t2.c[2] == 2);
+  assert (myu.t2.d[2] == 2);
+
+  #pragma omp target map(to: myu.t3, myu.t3->c) map(myu.t3->c[2:5])
+  {
+    myu.t3->c[2]++;
+  }
+
+  #pragma omp target map(to: myu.t3, myu.t3->d) map(myu.t3->d[2:5])
+  {
+    myu.t3->d[2]++;
+  }
+
+  assert (myu.t3->c[2] == 2);
+  assert (myu.t3->d[2] == 2);
+
+  #pragma omp target map(to: myu.t4, myu.t4->c) map(myu.t4->c[2:5])
+  {
+    myu.t4->c[2]++;
+  }
+
+  #pragma omp target map(to: myu.t4, myu.t4->d) map(myu.t4->d[2:5])
+  {
+    myu.t4->d[2]++;
+  }
+
+  assert (myu.t4->c[2] == 2);
+  assert (myu.t4->d[2] == 2);
+
+  delete s4;
+  delete t4;
+}
+#endif
+
+#ifdef PTR_COMPONENT_BASE
+void
+ptr_component_base (void)
+{
+  INIT_ST;
+  U *myu = new U(s1, t1, s2, t2, &s3, &t3, s4, t4);
+
+  #pragma omp target map(myu->s1.a, myu->s1.b, myu->s1.c, myu->s1.d)
+  {
+    myu->s1.a++;
+    myu->s1.b++;
+    myu->s1.c++;
+    myu->s1.d++;
+  }
+
+  assert (myu->s1.a == 1);
+  assert (myu->s1.b == 1);
+  assert (myu->s1.c == &c1 + 1);
+  assert (myu->s1.d == &d1 + 1);
+
+  #pragma omp target map(myu->s2.a, myu->s2.b, myu->s2.c, myu->s2.d)
+  {
+    myu->s2.a++;
+    myu->s2.b++;
+    myu->s2.c++;
+    myu->s2.d++;
+  }
+
+  assert (myu->s2.a == 1);
+  assert (myu->s2.b == 1);
+  assert (myu->s2.c == &c2 + 1);
+  assert (myu->s2.d == &d2 + 1);
+
+  #pragma omp target map(to:myu->s3) \
+		     map(myu->s3->a, myu->s3->b, myu->s3->c, myu->s3->d)
+  {
+    myu->s3->a++;
+    myu->s3->b++;
+    myu->s3->c++;
+    myu->s3->d++;
+  }
+
+  assert (myu->s3->a == 1);
+  assert (myu->s3->b == 1);
+  assert (myu->s3->c == &c3 + 1);
+  assert (myu->s3->d == &d3 + 1);
+
+  #pragma omp target map(to:myu->s4) \
+		     map(myu->s4->a, myu->s4->b, myu->s4->c, myu->s4->d)
+  {
+    myu->s4->a++;
+    myu->s4->b++;
+    myu->s4->c++;
+    myu->s4->d++;
+  }
+
+  assert (myu->s4->a == 1);
+  assert (myu->s4->b == 1);
+  assert (myu->s4->c == &c4 + 1);
+  assert (myu->s4->d == &d4 + 1);
+
+  delete s4;
+  delete t4;
+  delete myu;
+}
+#endif
+
+#ifdef PTR_COMPONENT_MEMBER_SLICE
+void
+ptr_component_member_slice (void)
+{
+  INIT_ST;
+  U *myu = new U(s1, t1, s2, t2, &s3, &t3, s4, t4);
+
+  #pragma omp target map(myu->t1.a[2:5])
+  {
+    myu->t1.a[2]++;
+  }
+
+  #pragma omp target map(myu->t1.b[2:5])
+  {
+    myu->t1.b[2]++;
+  }
+
+  #pragma omp target enter data map(to: myu->t1.c)
+
+  #pragma omp target map(myu->t1.c[2:5])
+  {
+    myu->t1.c[2]++;
+  }
+
+  #pragma omp target exit data map(release: myu->t1.c)
+
+  #pragma omp target enter data map(to: myu->t1.d)
+
+  #pragma omp target map(myu->t1.d[2:5])
+  {
+    myu->t1.d[2]++;
+  }
+
+  #pragma omp target exit data map(release: myu->t1.d)
+
+  assert (myu->t1.a[2] == 1);
+  assert (myu->t1.b[2] == 3);
+  assert (myu->t1.c[2] == 3);
+  assert (myu->t1.d[2] == 3);
+
+  #pragma omp target map(myu->t2.a[2:5])
+  {
+    myu->t2.a[2]++;
+  }
+
+  #pragma omp target map(myu->t2.b[2:5])
+  {
+    myu->t2.b[2]++;
+  }
+
+  #pragma omp target enter data map(to: myu->t2.c)
+
+  #pragma omp target map(myu->t2.c[2:5])
+  {
+    myu->t2.c[2]++;
+  }
+
+  #pragma omp target exit data map(release: myu->t2.c)
+
+  #pragma omp target enter data map(to: myu->t2.d)
+
+  #pragma omp target map(myu->t2.d[2:5])
+  {
+    myu->t2.d[2]++;
+  }
+
+  #pragma omp target exit data map(release: myu->t2.d)
+
+  assert (myu->t2.a[2] == 1);
+  assert (myu->t2.b[2] == 3);
+  assert (myu->t2.c[2] == 3);
+  assert (myu->t2.d[2] == 3);
+
+  #pragma omp target enter data map(to: myu->t3)
+
+  #pragma omp target map(myu->t3->a[2:5])
+  {
+    myu->t3->a[2]++;
+  }
+
+  #pragma omp target map(myu->t3->b[2:5])
+  {
+    myu->t3->b[2]++;
+  }
+
+  #pragma omp target enter data map(to: myu->t3->c)
+
+  #pragma omp target map(myu->t3->c[2:5])
+  {
+    myu->t3->c[2]++;
+  }
+
+  #pragma omp target exit data map(release: myu->t3->c)
+
+  #pragma omp target enter data map(to: myu->t3->d)
+
+  #pragma omp target map(myu->t3->d[2:5])
+  {
+    myu->t3->d[2]++;
+  }
+
+  #pragma omp target exit data map(release: myu->t3, myu->t3->d)
+
+  assert (myu->t3->a[2] == 1);
+  assert (myu->t3->b[2] == 3);
+  assert (myu->t3->c[2] == 3);
+  assert (myu->t3->d[2] == 3);
+
+  #pragma omp target enter data map(to: myu->t4)
+
+  #pragma omp target map(myu->t4->a[2:5])
+  {
+    myu->t4->a[2]++;
+  }
+
+  #pragma omp target map(myu->t4->b[2:5])
+  {
+    myu->t4->b[2]++;
+  }
+
+  #pragma omp target enter data map(to: myu->t4->c)
+
+  #pragma omp target map(myu->t4->c[2:5])
+  {
+    myu->t4->c[2]++;
+  }
+
+  #pragma omp target exit data map(release: myu->t4->c)
+
+  #pragma omp target enter data map(to: myu->t4->d)
+
+  #pragma omp target map(myu->t4->d[2:5])
+  {
+    myu->t4->d[2]++;
+  }
+
+  #pragma omp target exit data map(release: myu->t4, myu->t4->d)
+
+  assert (myu->t4->a[2] == 1);
+  assert (myu->t4->b[2] == 3);
+  assert (myu->t4->c[2] == 3);
+  assert (myu->t4->d[2] == 3);
+
+  delete s4;
+  delete t4;
+  delete myu;
+}
+#endif
+
+#ifdef PTR_COMPONENT_MEMBER_SLICE_BASEPTR
+void
+ptr_component_member_slice_baseptr (void)
+{
+  INIT_ST;
+  U *myu = new U(s1, t1, s2, t2, &s3, &t3, s4, t4);
+
+  /* Implicit firstprivate 'myu'.  */
+  #pragma omp target map(to: myu->t1.c) map(myu->t1.c[2:5])
+  {
+    myu->t1.c[2]++;
+  }
+
+  #pragma omp target map(to: myu->t1.d) map(myu->t1.d[2:5])
+  {
+    myu->t1.d[2]++;
+  }
+
+  assert (myu->t1.c[2] == 2);
+  assert (myu->t1.d[2] == 2);
+
+  /* Explicitly-mapped 'myu'.  */
+  #pragma omp target map(to: myu, myu->t1.c) map(myu->t1.c[2:5])
+  {
+    myu->t1.c[2]++;
+  }
+
+  #pragma omp target map(to: myu, myu->t1.d) map(myu->t1.d[2:5])
+  {
+    myu->t1.d[2]++;
+  }
+
+  assert (myu->t1.c[2] == 4);
+  assert (myu->t1.d[2] == 4);
+
+  /* Implicit firstprivate 'myu'.  */
+  #pragma omp target map(to: myu->t2.c) map(myu->t2.c[2:5])
+  {
+    myu->t2.c[2]++;
+  }
+
+  #pragma omp target map(to: myu->t2.d) map(myu->t2.d[2:5])
+  {
+    myu->t2.d[2]++;
+  }
+
+  assert (myu->t2.c[2] == 2);
+  assert (myu->t2.d[2] == 2);
+
+  /* Explicitly-mapped 'myu'.  */
+  #pragma omp target map(to: myu, myu->t2.c) map(myu->t2.c[2:5])
+  {
+    myu->t2.c[2]++;
+  }
+
+  #pragma omp target map(to: myu, myu->t2.d) map(myu->t2.d[2:5])
+  {
+    myu->t2.d[2]++;
+  }
+
+  assert (myu->t2.c[2] == 4);
+  assert (myu->t2.d[2] == 4);
+
+  /* Implicit firstprivate 'myu'.  */
+  #pragma omp target map(to: myu->t3, myu->t3->c) map(myu->t3->c[2:5])
+  {
+    myu->t3->c[2]++;
+  }
+
+  #pragma omp target map(to: myu->t3, myu->t3->d) map(myu->t3->d[2:5])
+  {
+    myu->t3->d[2]++;
+  }
+
+  assert (myu->t3->c[2] == 2);
+  assert (myu->t3->d[2] == 2);
+
+  /* Explicitly-mapped 'myu'.  */
+  #pragma omp target map(to: myu, myu->t3, myu->t3->c) map(myu->t3->c[2:5])
+  {
+    myu->t3->c[2]++;
+  }
+
+  #pragma omp target map(to: myu, myu->t3, myu->t3->d) map(myu->t3->d[2:5])
+  {
+    myu->t3->d[2]++;
+  }
+
+  assert (myu->t3->c[2] == 4);
+  assert (myu->t3->d[2] == 4);
+
+  /* Implicit firstprivate 'myu'.  */
+  #pragma omp target map(to: myu->t4, myu->t4->c) map(myu->t4->c[2:5])
+  {
+    myu->t4->c[2]++;
+  }
+
+  #pragma omp target map(to: myu->t4, myu->t4->d) map(myu->t4->d[2:5])
+  {
+    myu->t4->d[2]++;
+  }
+
+  assert (myu->t4->c[2] == 2);
+  assert (myu->t4->d[2] == 2);
+
+  /* Explicitly-mapped 'myu'.  */
+  #pragma omp target map(to: myu, myu->t4, myu->t4->c) map(myu->t4->c[2:5])
+  {
+    myu->t4->c[2]++;
+  }
+
+  #pragma omp target map(to: myu, myu->t4, myu->t4->d) map(myu->t4->d[2:5])
+  {
+    myu->t4->d[2]++;
+  }
+
+  assert (myu->t4->c[2] == 4);
+  assert (myu->t4->d[2] == 4);
+
+  delete s4;
+  delete t4;
+  delete myu;
+}
+#endif
+
+#ifdef REF2PTR_COMPONENT_BASE
+void
+ref2ptr_component_base (void)
+{
+  INIT_ST;
+  U *myu_ptr = new U(s1, t1, s2, t2, &s3, &t3, s4, t4);
+  U *&myu = myu_ptr;
+
+  #pragma omp target map(myu->s1.a, myu->s1.b, myu->s1.c, myu->s1.d)
+  {
+    myu->s1.a++;
+    myu->s1.b++;
+    myu->s1.c++;
+    myu->s1.d++;
+  }
+
+  assert (myu->s1.a == 1);
+  assert (myu->s1.b == 1);
+  assert (myu->s1.c == &c1 + 1);
+  assert (myu->s1.d == &d1 + 1);
+
+  #pragma omp target map(myu->s2.a, myu->s2.b, myu->s2.c, myu->s2.d)
+  {
+    myu->s2.a++;
+    myu->s2.b++;
+    myu->s2.c++;
+    myu->s2.d++;
+  }
+
+  assert (myu->s2.a == 1);
+  assert (myu->s2.b == 1);
+  assert (myu->s2.c == &c2 + 1);
+  assert (myu->s2.d == &d2 + 1);
+
+  #pragma omp target map(to:myu->s3) \
+		     map(myu->s3->a, myu->s3->b, myu->s3->c, myu->s3->d)
+  {
+    myu->s3->a++;
+    myu->s3->b++;
+    myu->s3->c++;
+    myu->s3->d++;
+  }
+
+  assert (myu->s3->a == 1);
+  assert (myu->s3->b == 1);
+  assert (myu->s3->c == &c3 + 1);
+  assert (myu->s3->d == &d3 + 1);
+
+  #pragma omp target map(to:myu->s4) \
+		     map(myu->s4->a, myu->s4->b, myu->s4->c, myu->s4->d)
+  {
+    myu->s4->a++;
+    myu->s4->b++;
+    myu->s4->c++;
+    myu->s4->d++;
+  }
+
+  assert (myu->s4->a == 1);
+  assert (myu->s4->b == 1);
+  assert (myu->s4->c == &c4 + 1);
+  assert (myu->s4->d == &d4 + 1);
+
+  delete s4;
+  delete t4;
+  delete myu_ptr;
+}
+#endif
+
+#ifdef REF2PTR_COMPONENT_MEMBER_SLICE
+void
+ref2ptr_component_member_slice (void)
+{
+  INIT_ST;
+  U *myu_ptr = new U(s1, t1, s2, t2, &s3, &t3, s4, t4);
+  U *&myu = myu_ptr;
+
+  #pragma omp target map(myu->t1.a[2:5])
+  {
+    myu->t1.a[2]++;
+  }
+
+  #pragma omp target map(myu->t1.b[2:5])
+  {
+    myu->t1.b[2]++;
+  }
+
+  #pragma omp target enter data map(to: myu->t1.c)
+
+  #pragma omp target map(myu->t1.c[2:5])
+  {
+    myu->t1.c[2]++;
+  }
+
+  #pragma omp target exit data map(release: myu->t1.c)
+
+  #pragma omp target enter data map(to: myu->t1.d)
+
+  #pragma omp target map(myu->t1.d[2:5])
+  {
+    myu->t1.d[2]++;
+  }
+
+  #pragma omp target exit data map(release: myu->t1.d)
+
+  assert (myu->t1.a[2] == 1);
+  assert (myu->t1.b[2] == 3);
+  assert (myu->t1.c[2] == 3);
+  assert (myu->t1.d[2] == 3);
+
+  #pragma omp target map(myu->t2.a[2:5])
+  {
+    myu->t2.a[2]++;
+  }
+
+  #pragma omp target map(myu->t2.b[2:5])
+  {
+    myu->t2.b[2]++;
+  }
+
+  #pragma omp target enter data map(to: myu->t2.c)
+
+  #pragma omp target map(myu->t2.c[2:5])
+  {
+    myu->t2.c[2]++;
+  }
+
+  #pragma omp target exit data map(release: myu->t2.c)
+
+  #pragma omp target enter data map(to: myu->t2.d)
+
+  #pragma omp target map(myu->t2.d[2:5])
+  {
+    myu->t2.d[2]++;
+  }
+
+  #pragma omp target exit data map(release: myu->t2.d)
+
+  assert (myu->t2.a[2] == 1);
+  assert (myu->t2.b[2] == 3);
+  assert (myu->t2.c[2] == 3);
+  assert (myu->t2.d[2] == 3);
+
+  #pragma omp target enter data map(to: myu->t3)
+
+  #pragma omp target map(myu->t3->a[2:5])
+  {
+    myu->t3->a[2]++;
+  }
+
+  #pragma omp target map(myu->t3->b[2:5])
+  {
+    myu->t3->b[2]++;
+  }
+
+  #pragma omp target enter data map(to: myu->t3->c)
+
+  #pragma omp target map(myu->t3->c[2:5])
+  {
+    myu->t3->c[2]++;
+  }
+
+  #pragma omp target exit data map(release: myu->t3->c)
+
+  #pragma omp target enter data map(to: myu->t3->d)
+
+  #pragma omp target map(myu->t3->d[2:5])
+  {
+    myu->t3->d[2]++;
+  }
+
+  #pragma omp target exit data map(release: myu->t3, myu->t3->d)
+
+  assert (myu->t3->a[2] == 1);
+  assert (myu->t3->b[2] == 3);
+  assert (myu->t3->c[2] == 3);
+  assert (myu->t3->d[2] == 3);
+
+  #pragma omp target enter data map(to: myu->t4)
+
+  #pragma omp target map(myu->t4->a[2:5])
+  {
+    myu->t4->a[2]++;
+  }
+
+  #pragma omp target map(myu->t4->b[2:5])
+  {
+    myu->t4->b[2]++;
+  }
+
+  #pragma omp target enter data map(to: myu->t4->c)
+
+  #pragma omp target map(myu->t4->c[2:5])
+  {
+    myu->t4->c[2]++;
+  }
+
+  #pragma omp target exit data map(release: myu->t4->c)
+
+  #pragma omp target enter data map(to: myu->t4->d)
+
+  #pragma omp target map(myu->t4->d[2:5])
+  {
+    myu->t4->d[2]++;
+  }
+
+  #pragma omp target exit data map(release: myu->t4, myu->t4->d)
+
+  assert (myu->t4->a[2] == 1);
+  assert (myu->t4->b[2] == 3);
+  assert (myu->t4->c[2] == 3);
+  assert (myu->t4->d[2] == 3);
+
+  delete s4;
+  delete t4;
+  delete myu_ptr;
+}
+#endif
+
+#ifdef REF2PTR_COMPONENT_MEMBER_SLICE_BASEPTR
+void
+ref2ptr_component_member_slice_baseptr (void)
+{
+  INIT_ST;
+  U *myu_ptr = new U(s1, t1, s2, t2, &s3, &t3, s4, t4);
+  U *&myu = myu_ptr;
+
+  /* Implicit firstprivate 'myu'.  */
+  #pragma omp target map(to: myu->t1.c) map(myu->t1.c[2:5])
+  {
+    myu->t1.c[2]++;
+  }
+
+  #pragma omp target map(to: myu->t1.d) map(myu->t1.d[2:5])
+  {
+    myu->t1.d[2]++;
+  }
+
+  assert (myu->t1.c[2] == 2);
+  assert (myu->t1.d[2] == 2);
+
+  /* Explicitly-mapped 'myu'.  */
+  #pragma omp target map(to: myu, myu->t1.c) map(myu->t1.c[2:5])
+  {
+    myu->t1.c[2]++;
+  }
+
+  #pragma omp target map(to: myu, myu->t1.d) map(myu->t1.d[2:5])
+  {
+    myu->t1.d[2]++;
+  }
+
+  assert (myu->t1.c[2] == 4);
+  assert (myu->t1.d[2] == 4);
+
+  /* Implicit firstprivate 'myu'.  */
+  #pragma omp target map(to: myu->t2.c) map(myu->t2.c[2:5])
+  {
+    myu->t2.c[2]++;
+  }
+
+  #pragma omp target map(to: myu->t2.d) map(myu->t2.d[2:5])
+  {
+    myu->t2.d[2]++;
+  }
+
+  assert (myu->t2.c[2] == 2);
+  assert (myu->t2.d[2] == 2);
+
+  /* Explicitly-mapped 'myu'.  */
+  #pragma omp target map(to: myu, myu->t2.c) map(myu->t2.c[2:5])
+  {
+    myu->t2.c[2]++;
+  }
+
+  #pragma omp target map(to: myu, myu->t2.d) map(myu->t2.d[2:5])
+  {
+    myu->t2.d[2]++;
+  }
+
+  assert (myu->t2.c[2] == 4);
+  assert (myu->t2.d[2] == 4);
+
+  /* Implicit firstprivate 'myu'.  */
+  #pragma omp target map(to: myu->t3, myu->t3->c) map(myu->t3->c[2:5])
+  {
+    myu->t3->c[2]++;
+  }
+
+  #pragma omp target map(to: myu->t3, myu->t3->d) map(myu->t3->d[2:5])
+  {
+    myu->t3->d[2]++;
+  }
+
+  assert (myu->t3->c[2] == 2);
+  assert (myu->t3->d[2] == 2);
+
+  /* Explicitly-mapped 'myu'.  */
+  #pragma omp target map(to: myu, myu->t3, myu->t3->c) map(myu->t3->c[2:5])
+  {
+    myu->t3->c[2]++;
+  }
+
+  #pragma omp target map(to: myu, myu->t3, myu->t3->d) map(myu->t3->d[2:5])
+  {
+    myu->t3->d[2]++;
+  }
+
+  assert (myu->t3->c[2] == 4);
+  assert (myu->t3->d[2] == 4);
+
+  /* Implicit firstprivate 'myu'.  */
+  #pragma omp target map(to: myu->t4, myu->t4->c) map(myu->t4->c[2:5])
+  {
+    myu->t4->c[2]++;
+  }
+
+  #pragma omp target map(to: myu->t4, myu->t4->d) map(myu->t4->d[2:5])
+  {
+    myu->t4->d[2]++;
+  }
+
+  assert (myu->t4->c[2] == 2);
+  assert (myu->t4->d[2] == 2);
+
+  /* Explicitly-mapped 'myu'.  */
+  #pragma omp target map(to: myu, myu->t4, myu->t4->c) map(myu->t4->c[2:5])
+  {
+    myu->t4->c[2]++;
+  }
+
+  #pragma omp target map(to: myu, myu->t4, myu->t4->d) map(myu->t4->d[2:5])
+  {
+    myu->t4->d[2]++;
+  }
+
+  assert (myu->t4->c[2] == 4);
+  assert (myu->t4->d[2] == 4);
+
+  delete s4;
+  delete t4;
+  delete myu_ptr;
+}
+#endif
+
+int main (int argc, char *argv[])
+{
+#ifdef MAP_DECLS
+  map_decls ();
+#endif
+
+#ifdef NONREF_DECL_BASE
+  nonref_decl_base ();
+#endif
+#ifdef REF_DECL_BASE
+  ref_decl_base ();
+#endif
+#ifdef PTR_DECL_BASE
+  ptr_decl_base ();
+#endif
+#ifdef REF2PTR_DECL_BASE
+  ref2ptr_decl_base ();
+#endif
+
+#ifdef ARRAY_DECL_BASE
+  array_decl_base ();
+#endif
+#ifdef REF2ARRAY_DECL_BASE
+  ref2array_decl_base ();
+#endif
+#ifdef PTR_OFFSET_DECL_BASE
+  ptr_offset_decl_base ();
+#endif
+#ifdef REF2PTR_OFFSET_DECL_BASE
+  ref2ptr_offset_decl_base ();
+#endif
+
+#ifdef MAP_SECTIONS
+  map_sections ();
+#endif
+
+#ifdef NONREF_DECL_MEMBER_SLICE
+  nonref_decl_member_slice ();
+#endif
+#ifdef NONREF_DECL_MEMBER_SLICE_BASEPTR
+  nonref_decl_member_slice_baseptr ();
+#endif
+#ifdef REF_DECL_MEMBER_SLICE
+  ref_decl_member_slice ();
+#endif
+#ifdef REF_DECL_MEMBER_SLICE_BASEPTR
+  ref_decl_member_slice_baseptr ();
+#endif
+#ifdef PTR_DECL_MEMBER_SLICE
+  ptr_decl_member_slice ();
+#endif
+#ifdef PTR_DECL_MEMBER_SLICE_BASEPTR
+  ptr_decl_member_slice_baseptr ();
+#endif
+#ifdef REF2PTR_DECL_MEMBER_SLICE
+  ref2ptr_decl_member_slice ();
+#endif
+#ifdef REF2PTR_DECL_MEMBER_SLICE_BASEPTR
+  ref2ptr_decl_member_slice_baseptr ();
+#endif
+
+#ifdef ARRAY_DECL_MEMBER_SLICE
+  array_decl_member_slice ();
+#endif
+#ifdef ARRAY_DECL_MEMBER_SLICE_BASEPTR
+  array_decl_member_slice_baseptr ();
+#endif
+#ifdef REF2ARRAY_DECL_MEMBER_SLICE
+  ref2array_decl_member_slice ();
+#endif
+#ifdef REF2ARRAY_DECL_MEMBER_SLICE_BASEPTR
+  ref2array_decl_member_slice_baseptr ();
+#endif
+#ifdef PTR_OFFSET_DECL_MEMBER_SLICE
+  ptr_offset_decl_member_slice ();
+#endif
+#ifdef PTR_OFFSET_DECL_MEMBER_SLICE_BASEPTR
+  ptr_offset_decl_member_slice_baseptr ();
+#endif
+#ifdef REF2PTR_OFFSET_DECL_MEMBER_SLICE
+  ref2ptr_offset_decl_member_slice ();
+#endif
+#ifdef REF2PTR_OFFSET_DECL_MEMBER_SLICE_BASEPTR
+  ref2ptr_offset_decl_member_slice_baseptr ();
+#endif
+
+#ifdef PTRARRAY_DECL_MEMBER_SLICE
+  ptrarray_decl_member_slice ();
+#endif
+#ifdef PTRARRAY_DECL_MEMBER_SLICE_BASEPTR
+  ptrarray_decl_member_slice_baseptr ();
+#endif
+#ifdef REF2PTRARRAY_DECL_MEMBER_SLICE
+  ref2ptrarray_decl_member_slice ();
+#endif
+#ifdef REF2PTRARRAY_DECL_MEMBER_SLICE_BASEPTR
+  ref2ptrarray_decl_member_slice_baseptr ();
+#endif
+#ifdef PTRPTR_OFFSET_DECL_MEMBER_SLICE
+  ptrptr_offset_decl_member_slice ();
+#endif
+#ifdef PTRPTR_OFFSET_DECL_MEMBER_SLICE_BASEPTR
+  ptrptr_offset_decl_member_slice_baseptr ();
+#endif
+#ifdef REF2PTRPTR_OFFSET_DECL_MEMBER_SLICE
+  ref2ptrptr_offset_decl_member_slice ();
+#endif
+#ifdef REF2PTRPTR_OFFSET_DECL_MEMBER_SLICE_BASEPTR
+  ref2ptrptr_offset_decl_member_slice_baseptr ();
+#endif
+
+#ifdef NONREF_COMPONENT_BASE
+  nonref_component_base ();
+#endif
+#ifdef NONREF_COMPONENT_MEMBER_SLICE
+  nonref_component_member_slice ();
+#endif
+#ifdef NONREF_COMPONENT_MEMBER_SLICE_BASEPTR
+  nonref_component_member_slice_baseptr ();
+#endif
+
+#ifdef REF_COMPONENT_BASE
+  ref_component_base ();
+#endif
+#ifdef REF_COMPONENT_MEMBER_SLICE
+  ref_component_member_slice ();
+#endif
+#ifdef REF_COMPONENT_MEMBER_SLICE_BASEPTR
+  ref_component_member_slice_baseptr ();
+#endif
+
+#ifdef PTR_COMPONENT_BASE
+  ptr_component_base ();
+#endif
+#ifdef PTR_COMPONENT_MEMBER_SLICE
+  ptr_component_member_slice ();
+#endif
+#ifdef PTR_COMPONENT_MEMBER_SLICE_BASEPTR
+  ptr_component_member_slice_baseptr ();
+#endif
+
+#ifdef REF2PTR_COMPONENT_BASE
+  ref2ptr_component_base ();
+#endif
+#ifdef REF2PTR_COMPONENT_MEMBER_SLICE
+  ref2ptr_component_member_slice ();
+#endif
+#ifdef REF2PTR_COMPONENT_MEMBER_SLICE_BASEPTR
+  ref2ptr_component_member_slice_baseptr ();
+#endif
+
+  return 0;
+}
diff --git a/libgomp/testsuite/libgomp.c++/baseptrs-5.C b/libgomp/testsuite/libgomp.c++/baseptrs-5.C
new file mode 100644
index 00000000000..16bdfff3ae0
--- /dev/null
+++ b/libgomp/testsuite/libgomp.c++/baseptrs-5.C
@@ -0,0 +1,62 @@
+// { dg-do run }
+
+#include <cstring>
+#include <cassert>
+
+struct sa
+{
+  int *ptr;
+  int *ptr2;
+};
+
+struct sb
+{
+  int arr[10];
+};
+
+struct scp
+{
+  sa *&a;
+  sb *&b;
+  scp (sa *&my_a, sb *&my_b) : a(my_a), b(my_b) {}
+};
+
+int
+main ()
+{
+  sa *my_a = new sa;
+  sb *my_b = new sb;
+
+  my_a->ptr = new int[10];
+  my_a->ptr2 = new int[10];
+  scp *my_c = new scp(my_a, my_b);
+
+  memset (my_c->a->ptr, 0, sizeof (int) * 10);
+  memset (my_c->a->ptr2, 0, sizeof (int) * 10);
+
+  #pragma omp target map (my_c->a, \
+			  my_c->a->ptr, my_c->a->ptr[:10], \
+			  my_c->a->ptr2, my_c->a->ptr2[:10])
+  {
+    for (int i = 0; i < 10; i++)
+      {
+	my_c->a->ptr[i] = i;
+	my_c->a->ptr2[i] = i * 2;
+      }
+  }
+
+  for (int i = 0; i < 10; i++)
+    {
+      assert (my_c->a->ptr[i] == i);
+      assert (my_c->a->ptr2[i] == i * 2);
+    }
+
+  delete[] my_a->ptr;
+  delete[] my_a->ptr2;
+  delete my_a;
+  delete my_b;
+  delete my_c;
+
+  return 0;
+}
+
diff --git a/libgomp/testsuite/libgomp.c++/baseptrs-8.C b/libgomp/testsuite/libgomp.c++/baseptrs-8.C
new file mode 100644
index 00000000000..f9991818551
--- /dev/null
+++ b/libgomp/testsuite/libgomp.c++/baseptrs-8.C
@@ -0,0 +1,70 @@
+/* { dg-do run } */
+
+#include <stdlib.h>
+#include <assert.h>
+
+#define N 1024
+#define M 64
+
+int main (void)
+{
+  int *a_orig[N];
+  int *(&a)[N] = a_orig;
+
+  for (int i = 0; i < N; i++)
+    a[i] = (int *) calloc (M, sizeof (int));
+
+  /* 'target enter data'/'target exit data' with array of pointers.  */
+#pragma omp target enter data map(alloc: a[0:N])
+
+  for (int i = 0; i < N; i++)
+  {
+#pragma omp target enter data map(to: a[i][0:M])
+  }
+
+#pragma omp target map(alloc: a)
+  {
+    for (int i = 0; i < N; i++)
+      for (int j = 0; j < M; j++)
+	a[i][j] = i + j;
+  }
+
+for (int i = 0; i < N; i++)
+  {
+#pragma omp target exit data map(release: a[i]) map(from: a[i][0:M])
+  }
+
+#pragma omp target exit data map(release: a, a[0:N])
+
+  /* 'target data' with array of pointers.  */
+#pragma omp target data map(alloc: a[0:N])
+  {
+#pragma omp target data map(tofrom: a[5][0:M])
+    {
+#pragma omp target map(alloc: a)
+      {
+	for (int i = 0; i < M; i++)
+	  a[5][i]++;
+      }
+    }
+  }
+
+  /* 'target' with array of pointers.  */
+#pragma omp target data map(alloc: a[0:N])
+  {
+#pragma omp target map(tofrom: a[7][0:M])
+    {
+      for (int i = 0; i < M; i++)
+	a[7][i] += 2;
+    }
+  }
+
+  for (int i = 0; i < N; i++)
+    for (int j = 0; j < M; j++)
+      assert (a[i][j] == i + j + (i == 5) + 2 * (i == 7));
+
+  for (int i = 0; i < N; i++)
+    free (a[i]);
+
+  return 0;
+}
diff --git a/libgomp/testsuite/libgomp.c++/baseptrs-9.C b/libgomp/testsuite/libgomp.c++/baseptrs-9.C
new file mode 100644
index 00000000000..95e7eebb0ed
--- /dev/null
+++ b/libgomp/testsuite/libgomp.c++/baseptrs-9.C
@@ -0,0 +1,57 @@
+/* { dg-do run } */
+
+#include <stdlib.h>
+#include <assert.h>
+
+int main (void)
+{
+  int **a_orig,i,j,n;
+  int **&a = a_orig;
+
+  j = 3;
+  n = 12;
+
+  a = (int **) calloc (32, sizeof (int *));
+  for (int x = 0; x < 32; x++)
+    a[x] = (int *) calloc (32, sizeof (int));
+
+  for (int i = 2; i < 32; i++)
+    {
+      #pragma omp target enter data map(a, a[2:30])
+      #pragma omp target enter data map(a[i][j:n])
+      #pragma omp target map(alloc: a)
+      {
+	for (int x = j; x < j + n; x++)
+	  a[i][x]++;
+      }
+      #pragma omp target exit data map(a[i][j:n])
+
+      #pragma omp target data map(a, a[i][j:n])
+      {
+	#pragma omp target map(alloc: a)
+	{
+	  for (int x = j; x < j + n; x++)
+	    a[i][x]++;
+	}
+      }
+      #pragma omp target exit data map(a, a[2:30])
+
+      #pragma omp target data map(a, a[2:30])
+      {
+	#pragma omp target map(a[i][j:n])
+	{
+	  for (int x = j; x < j + n; x++)
+	    a[i][x]++;
+	}
+      }
+    }
+
+  for (int i = 0; i < 32; i++)
+    for (int j = 0; j < 32; j++)
+      if (i >= 2 && j >= 3 && j < 15)
+	assert (a[i][j] == 3);
+      else
+	assert (a[i][j] == 0);
+
+  return 0;
+}
diff --git a/libgomp/testsuite/libgomp.c++/class-array-1.C b/libgomp/testsuite/libgomp.c++/class-array-1.C
new file mode 100644
index 00000000000..d8d3f7f1f99
--- /dev/null
+++ b/libgomp/testsuite/libgomp.c++/class-array-1.C
@@ -0,0 +1,59 @@
+/* { dg-do run } */
+
+#include <cassert>
+
+#define N 1024
+
+class M {
+  int array[N];
+
+public:
+  M ()
+  {
+    for (int i = 0; i < N; i++)
+      array[i] = 0;
+  }
+
+  void incr_with_this (int c)
+  {
+#pragma omp target map(this->array[:N])
+    for (int i = 0; i < N; i++)
+      array[i] += c;
+  }
+
+  void incr_without_this (int c)
+  {
+#pragma omp target map(array[:N])
+    for (int i = 0; i < N; i++)
+      array[i] += c;
+  }
+
+  void incr_implicit (int c)
+  {
+#pragma omp target
+    for (int i = 0; i < N; i++)
+      array[i] += c;
+  }
+
+  void check (int c)
+  {
+    for (int i = 0; i < N; i++)
+      assert (array[i] == c);
+  }
+};
+
+int
+main (int argc, char *argv[])
+{
+  M m;
+
+  m.check (0);
+  m.incr_with_this (3);
+  m.check (3);
+  m.incr_without_this (5);
+  m.check (8);
+  m.incr_implicit (2);
+  m.check (10);
+
+  return 0;
+}
diff --git a/libgomp/testsuite/libgomp.c++/ref-mapping-1.C b/libgomp/testsuite/libgomp.c++/ref-mapping-1.C
new file mode 100644
index 00000000000..9aa232f3f67
--- /dev/null
+++ b/libgomp/testsuite/libgomp.c++/ref-mapping-1.C
@@ -0,0 +1,80 @@
+/* { dg-do run } */
+
+#include <cassert>
+
+void test_ref ()
+{
+  int c_orig = 5;
+  int &c = c_orig;
+
+#pragma omp target map(tofrom: c)
+  {
+    c++;
+  }
+
+  assert (c == 6);
+}
+
+void test_ref_to_ptr ()
+{
+  int val = 5;
+  int *ptr_orig = &val;
+  int *&ptr_ref = ptr_orig;
+
+#pragma omp target map(tofrom: ptr_ref[0])
+  {
+    (*ptr_ref)++;
+  }
+
+  assert (val == 6);
+}
+
+void test_ref_to_array ()
+{
+  int arr[1];
+  int (&arr_ref)[1] = arr;
+
+  arr_ref[0] = 5;
+
+#pragma omp target map(tofrom: arr_ref[0:1])
+  {
+    arr_ref[0]++;
+  }
+
+  assert (arr_ref[0] == 6);
+
+#pragma omp target map(tofrom: arr_ref[0])
+  {
+    arr_ref[0]++;
+  }
+
+  assert (arr_ref[0] == 7);
+}
+
+void test_ref_to_ptr_array ()
+{
+  int *arr[1];
+  int *(&arr_ref)[1] = arr;
+  int val = 5;
+
+  arr_ref[0] = &val;
+
+#pragma omp target data map(alloc: arr_ref, arr_ref[0])
+  {
+#pragma omp target map(tofrom: arr_ref[0][0:1])
+    {
+      arr_ref[0][0]++;
+    }
+  }
+
+  assert (arr_ref[0][0] == 6);
+}
+
+int main ()
+{
+  test_ref ();
+  test_ref_to_ptr ();
+  test_ref_to_array ();
+  test_ref_to_ptr_array ();
+  return 0;
+}
diff --git a/libgomp/testsuite/libgomp.c++/target-48.C b/libgomp/testsuite/libgomp.c++/target-48.C
new file mode 100644
index 00000000000..db171d2f5a3
--- /dev/null
+++ b/libgomp/testsuite/libgomp.c++/target-48.C
@@ -0,0 +1,32 @@
+#include <cstring>
+#include <cassert>
+
+struct s {
+  int (&a)[10];
+  s(int (&a0)[10]) : a(a0) {}
+};
+
+int
+main (int argc, char *argv[])
+{
+  int la[10];
+  s v(la);
+
+  memset (la, 0, sizeof la);
+
+  #pragma omp target enter data map(to: v)
+
+  /* This mapping must use GOMP_MAP_ATTACH_DETACH not GOMP_MAP_ALWAYS_POINTER,
+     else the host reference v.a will be corrupted on copy-out.  */
+
+  #pragma omp target map(v.a[0:10])
+  {
+    v.a[5]++;
+  }
+
+  #pragma omp target exit data map(from: v)
+
+  assert (v.a[5] == 1);
+
+  return 0;
+}
diff --git a/libgomp/testsuite/libgomp.c++/target-49.C b/libgomp/testsuite/libgomp.c++/target-49.C
new file mode 100644
index 00000000000..efae9c9580e
--- /dev/null
+++ b/libgomp/testsuite/libgomp.c++/target-49.C
@@ -0,0 +1,37 @@
+#include <cstring>
+#include <cassert>
+
+struct s {
+  int (&a)[10];
+  s(int (&a0)[10]) : a(a0) {}
+};
+
+int
+main (int argc, char *argv[])
+{
+  int la[10];
+  s v_real(la);
+  s *v = &v_real;
+
+  memset (la, 0, sizeof la);
+
+  #pragma omp target enter data map(to: v)
+
+  /* Copying the whole v[0] here DOES NOT WORK yet because the reference 'a' is
+     not copied "as if" it was mapped explicitly as a member.  FIXME.  */
+  #pragma omp target enter data map(to: v[0])
+
+  //#pragma omp target
+  {
+    v->a[5]++;
+  }
+
+  #pragma omp target exit data map(release: v[0])
+  #pragma omp target exit data map(from: v)
+
+  assert (v->a[5] == 1);
+
+  return 0;
+}
+
+// { dg-xfail-run-if "TODO" { *-*-* } { "-DACC_MEM_SHARED=0" } }
diff --git a/libgomp/testsuite/libgomp.c++/target-exit-data-reftoptr-1.C b/libgomp/testsuite/libgomp.c++/target-exit-data-reftoptr-1.C
new file mode 100644
index 00000000000..1a66fcb0f7f
--- /dev/null
+++ b/libgomp/testsuite/libgomp.c++/target-exit-data-reftoptr-1.C
@@ -0,0 +1,34 @@
+#include <cstring>
+#include <cassert>
+
+struct S {
+  int *&ptr;
+  S(int *&ptr_) : ptr(ptr_) { }
+};
+
+int main()
+{
+  int *orig = new int[100];
+
+  memset (orig, 0, sizeof (int) * 100);
+
+  S svar(orig);
+
+#pragma omp target enter data map(to: svar.ptr, svar.ptr[10:80])
+
+#pragma omp target
+  {
+    for (int i = 10; i < 90; i++)
+      svar.ptr[i]++;
+  }
+
+#pragma omp target exit data map(release: svar.ptr) map(from: svar.ptr[10:80])
+
+  for (int i = 0; i < 100; i++)
+    assert (i >= 10 && i < 90 && svar.ptr[i] == 1
+	    || svar.ptr[i] == 0);
+
+  delete orig;
+
+  return 0;
+}
diff --git a/libgomp/testsuite/libgomp.c++/target-lambda-1.C b/libgomp/testsuite/libgomp.c++/target-lambda-1.C
index c5acbb8bf30..fa882d09800 100644
--- a/libgomp/testsuite/libgomp.c++/target-lambda-1.C
+++ b/libgomp/testsuite/libgomp.c++/target-lambda-1.C
@@ -2,6 +2,7 @@
 
 #include <cstdlib>
 #include <cstring>
+#include <cstdint>
 
 template <typename L>
 void
@@ -22,9 +23,11 @@ struct S
     auto fn = [=](void) -> bool
       {
 	bool mapped;
+	uintptr_t hostptr = (uintptr_t) ptr;
+	uintptr_t hostiptr = (uintptr_t) iptr;
 	#pragma omp target map(from:mapped)
 	{
-	  mapped = (ptr != NULL && iptr != NULL);
+	  mapped = (ptr != (int*) hostptr && iptr != (int*) hostiptr);
 	  if (mapped)
 	    {
 	      for (int i = 0; i < len; i++)
diff --git a/libgomp/testsuite/libgomp.c++/target-this-3.C b/libgomp/testsuite/libgomp.c++/target-this-3.C
index 6049ba8e201..986582430e2 100644
--- a/libgomp/testsuite/libgomp.c++/target-this-3.C
+++ b/libgomp/testsuite/libgomp.c++/target-this-3.C
@@ -2,6 +2,7 @@
 
 #include <stdio.h>
 #include <string.h>
+#include <stdint.h>
 extern "C" void abort ();
 
 struct S
@@ -15,12 +16,13 @@ struct S
   bool set_ptr (int n)
   {
     bool mapped;
+    uintptr_t hostptr = (uintptr_t) ptr;
     #pragma omp target map(from:mapped)
     {
-      if (ptr != NULL)
+      if (ptr != (int *) hostptr)
 	for (int i = 0; i < ptr_len; i++)
 	  ptr[i] = n;
-      mapped = (ptr != NULL);
+      mapped = (ptr != (int *) hostptr);
     }
     return mapped;
   }
@@ -28,12 +30,13 @@ struct S
   bool set_refptr (int n)
   {
     bool mapped;
+    uintptr_t hostrefptr = (uintptr_t) refptr;
     #pragma omp target map(from:mapped)
     {
-      if (refptr != NULL)
+      if (refptr != (int *) hostrefptr)
 	for (int i = 0; i < refptr_len; i++)
 	  refptr[i] = n;
-      mapped = (refptr != NULL);
+      mapped = (refptr != (int *) hostrefptr);
     }
     return mapped;
   }
diff --git a/libgomp/testsuite/libgomp.c++/target-this-4.C b/libgomp/testsuite/libgomp.c++/target-this-4.C
index f0237c9b6b8..b2a593d03af 100644
--- a/libgomp/testsuite/libgomp.c++/target-this-4.C
+++ b/libgomp/testsuite/libgomp.c++/target-this-4.C
@@ -4,6 +4,7 @@
 
 #include <cstdlib>
 #include <cstring>
+#include <cstdint>
 
 struct T
 {
@@ -18,12 +19,13 @@ struct T
     auto fn = [=](void) -> bool
       {
 	bool mapped;
+	uintptr_t hostptr = (uintptr_t) ptr;
 	#pragma omp target map(from:mapped)
 	{
-	  if (ptr)
+	  if (ptr != (int *) hostptr)
 	    for (int i = 0; i < ptr_len; i++)
 	      ptr[i] = n;
-	  mapped = (ptr != NULL);
+	  mapped = (ptr != (int *) hostptr);
 	}
 	return mapped;
       };
@@ -35,12 +37,13 @@ struct T
     auto fn = [=](void) -> bool
       {
 	bool mapped;
+	uintptr_t hostrefptr = (uintptr_t) refptr;
 	#pragma omp target map(from:mapped)
 	{
-	  if (refptr)
+	  if (refptr != (int *) hostrefptr)
 	    for (int i = 0; i < refptr_len; i++)
 	      refptr[i] = n;
-	  mapped = (refptr != NULL);
+	  mapped = (refptr != (int *) hostrefptr);
 	}
 	return mapped;
       };
diff --git a/libgomp/testsuite/libgomp.c-c++-common/baseptrs-1.c b/libgomp/testsuite/libgomp.c-c++-common/baseptrs-1.c
new file mode 100644
index 00000000000..073615625b7
--- /dev/null
+++ b/libgomp/testsuite/libgomp.c-c++-common/baseptrs-1.c
@@ -0,0 +1,50 @@
+#include <stdlib.h>
+#include <string.h>
+#include <assert.h>
+#include <stdio.h>
+
+#define N 32
+
+typedef struct {
+  int x2[10][N];
+} x1type;
+
+typedef struct {
+  x1type x1[10];
+} p2type;
+
+typedef struct {
+  p2type *p2;
+} p1type;
+
+typedef struct {
+  p1type *p1;
+} x0type;
+
+typedef struct {
+  x0type x0[10];
+} p0type;
+
+int main(int argc, char *argv[])
+{
+  p0type *p0;
+  int k1 = 0, k2 = 0, k3 = 0, n = N;
+
+  p0 = (p0type *) malloc (sizeof *p0);
+  p0->x0[0].p1 = (p1type *) malloc (sizeof *p0->x0[0].p1);
+  p0->x0[0].p1->p2 = (p2type *) malloc (sizeof *p0->x0[0].p1->p2);
+  memset (p0->x0[0].p1->p2, 0, sizeof *p0->x0[0].p1->p2);
+
+#pragma omp target map(tofrom: p0->x0[k1].p1->p2[k2].x1[k3].x2[4][0:n]) \
+		   map(to: p0->x0[k1].p1, p0->x0[k1].p1->p2) \
+		   map(to: p0->x0[k1].p1[0])
+  {
+    for (int i = 0; i < n; i++)
+      p0->x0[k1].p1->p2[k2].x1[k3].x2[4][i] = i;
+  }
+
+  for (int i = 0; i < n; i++)
+    assert (i == p0->x0[k1].p1->p2[k2].x1[k3].x2[4][i]);
+
+  return 0;
+}
diff --git a/libgomp/testsuite/libgomp.c-c++-common/baseptrs-2.c b/libgomp/testsuite/libgomp.c-c++-common/baseptrs-2.c
new file mode 100644
index 00000000000..e335d7da966
--- /dev/null
+++ b/libgomp/testsuite/libgomp.c-c++-common/baseptrs-2.c
@@ -0,0 +1,70 @@
+#include <stdlib.h>
+#include <string.h>
+#include <assert.h>
+
+#define N 32
+
+typedef struct {
+  int arr[N];
+  int *ptr;
+} sc;
+
+typedef struct {
+  sc *c;
+} sb;
+
+typedef struct {
+  sb *b;
+  sc *c;
+} sa;
+
+int main (int argc, char *argv[])
+{
+  sa *p;
+
+  p = (sa *) malloc (sizeof *p);
+  p->b = (sb *) malloc (sizeof *p->b);
+  p->b->c = (sc *) malloc (sizeof *p->b->c);
+  p->c = (sc *) malloc (sizeof *p->c);
+  p->b->c->ptr = (int *) malloc (N * sizeof (int));
+  p->c->ptr = (int *) malloc (N * sizeof (int));
+
+  for (int i = 0; i < N; i++)
+    {
+      p->b->c->ptr[i] = 0;
+      p->c->ptr[i] = 0;
+      p->b->c->arr[i] = 0;
+      p->c->arr[i] = 0;
+    }
+
+#pragma omp target map(to: p->b, p->b[0], p->c, p->c[0], p->b->c, p->b->c[0]) \
+		   map(to: p->b->c->ptr, p->c->ptr) \
+		   map(tofrom: p->b->c->ptr[:N], p->c->ptr[:N])
+  {
+    for (int i = 0; i < N; i++)
+      {
+	p->b->c->ptr[i] = i;
+	p->c->ptr[i] = i * 2;
+      }
+  }
+
+#pragma omp target map(to: p->b, p->b[0], p->b->c, p->c) \
+		   map(tofrom: p->c[0], p->b->c[0])
+  {
+    for (int i = 0; i < N; i++)
+      {
+	p->b->c->arr[i] = i * 3;
+	p->c->arr[i] = i * 4;
+      }
+  }
+
+  for (int i = 0; i < N; i++)
+    {
+      assert (p->b->c->ptr[i] == i);
+      assert (p->c->ptr[i] == i * 2);
+      assert (p->b->c->arr[i] == i * 3);
+      assert (p->c->arr[i] == i * 4);
+    }
+
+  return 0;
+}
diff --git a/libgomp/testsuite/libgomp.c-c++-common/baseptrs-6.c b/libgomp/testsuite/libgomp.c-c++-common/baseptrs-6.c
new file mode 100644
index 00000000000..4b6e237471a
--- /dev/null
+++ b/libgomp/testsuite/libgomp.c-c++-common/baseptrs-6.c
@@ -0,0 +1,69 @@
+/* { dg-do run } */
+
+#include <stdlib.h>
+#include <assert.h>
+
+#define N 1024
+#define M 64
+
+int main (void)
+{
+  int *a[N];
+
+  for (int i = 0; i < N; i++)
+    a[i] = (int *) calloc (M, sizeof (int));
+
+  /* 'target enter data'/'target exit data' with array of pointers.  */
+#pragma omp target enter data map(alloc: a[0:N])
+
+  for (int i = 0; i < N; i++)
+  {
+#pragma omp target enter data map(to: a[i][0:M])
+  }
+
+#pragma omp target map(alloc: a)
+  {
+    for (int i = 0; i < N; i++)
+      for (int j = 0; j < M; j++)
+	a[i][j] = i + j;
+  }
+
+for (int i = 0; i < N; i++)
+  {
+#pragma omp target exit data map(release: a[i]) map(from: a[i][0:M])
+  }
+
+#pragma omp target exit data map(release: a, a[0:N])
+
+  /* 'target data' with array of pointers.  */
+#pragma omp target data map(alloc: a[0:N])
+  {
+#pragma omp target data map(tofrom: a[5][0:M])
+    {
+#pragma omp target map(alloc: a)
+      {
+	for (int i = 0; i < M; i++)
+	  a[5][i]++;
+      }
+    }
+  }
+
+  /* 'target' with array of pointers.  */
+#pragma omp target data map(alloc: a[0:N])
+  {
+#pragma omp target map(tofrom: a[7][0:M])
+    {
+      for (int i = 0; i < M; i++)
+	a[7][i] += 2;
+    }
+  }
+
+  for (int i = 0; i < N; i++)
+    for (int j = 0; j < M; j++)
+      assert (a[i][j] == i + j + (i == 5) + 2 * (i == 7));
+
+  for (int i = 0; i < N; i++)
+    free (a[i]);
+
+  return 0;
+}
diff --git a/libgomp/testsuite/libgomp.c-c++-common/baseptrs-7.c b/libgomp/testsuite/libgomp.c-c++-common/baseptrs-7.c
new file mode 100644
index 00000000000..9c6710e4e5b
--- /dev/null
+++ b/libgomp/testsuite/libgomp.c-c++-common/baseptrs-7.c
@@ -0,0 +1,56 @@
+/* { dg-do run } */
+
+#include <stdlib.h>
+#include <assert.h>
+
+int main (void)
+{
+  int **a,i,j,n;
+
+  j = 3;
+  n = 12;
+
+  a = (int **) calloc (32, sizeof (int *));
+  for (int x = 0; x < 32; x++)
+    a[x] = (int *) calloc (32, sizeof (int));
+
+  for (int i = 2; i < 32; i++)
+    {
+      #pragma omp target enter data map(a, a[2:30])
+      #pragma omp target enter data map(a[i][j:n])
+      #pragma omp target map(alloc: a)
+      {
+	for (int x = j; x < j + n; x++)
+	  a[i][x]++;
+      }
+      #pragma omp target exit data map(a[i][j:n])
+
+      #pragma omp target data map(a, a[i][j:n])
+      {
+	#pragma omp target map(alloc: a)
+	{
+	  for (int x = j; x < j + n; x++)
+	    a[i][x]++;
+	}
+      }
+      #pragma omp target exit data map(a, a[2:30])
+
+      #pragma omp target data map(a, a[0:32])
+      {
+	#pragma omp target map(a[i][j:n])
+	{
+	  for (int x = j; x < j + n; x++)
+	    a[i][x]++;
+	}
+      }
+    }
+
+  for (int i = 0; i < 32; i++)
+    for (int j = 0; j < 32; j++)
+      if (i >= 2 && j >= 3 && j < 15)
+	assert (a[i][j] == 3);
+      else
+	assert (a[i][j] == 0);
+
+  return 0;
+}
diff --git a/libgomp/testsuite/libgomp.c-c++-common/ptr-attach-2.c b/libgomp/testsuite/libgomp.c-c++-common/ptr-attach-2.c
new file mode 100644
index 00000000000..889a4a253ae
--- /dev/null
+++ b/libgomp/testsuite/libgomp.c-c++-common/ptr-attach-2.c
@@ -0,0 +1,60 @@
+#include <stdlib.h>
+
+struct blk { int x, y; };
+struct L
+{
+  #define N 10
+  struct {
+    int num_blocks[N];
+    struct blk * blocks[N];
+  } m;
+};
+
+void foo (struct L *l)
+{
+  for (int i = 0; i < N; i++)
+    {
+      l->m.blocks[i] = (struct blk *) malloc (sizeof (struct blk) * N);
+      l->m.num_blocks[i] = N;
+    }
+
+  #pragma omp target enter data map(to:l[:1])
+  for (int i = 0; i < N; i++)
+    {
+      #pragma omp target enter data map(to:l->m.blocks[i][:l->m.num_blocks[i]])
+    }
+
+  #pragma omp target
+  {
+    for (int i = 0; i < N; i++)
+      for (int j = 0; j < N; j++)
+	{
+	  l->m.blocks[i][j].x = i + j;
+	  l->m.blocks[i][j].y = i * j;
+	}
+  }
+
+  for (int i = 0; i < N; i++)
+    {
+      #pragma omp target exit data map(from:l->m.blocks[i][:l->m.num_blocks[i]])
+    }
+  #pragma omp target exit data map(from:l[:1])
+
+
+  for (int i = 0; i < N; i++)
+    for (int j = 0; j < N; j++)
+      {
+	if (l->m.blocks[i][j].x != i + j)
+	  abort ();
+	if (l->m.blocks[i][j].y != i * j)
+	  abort ();
+      }
+
+}
+
+int main (void)
+{
+  struct L l;
+  foo (&l);
+  return 0;
+}
diff --git a/libgomp/testsuite/libgomp.c-c++-common/target-implicit-map-2.c b/libgomp/testsuite/libgomp.c-c++-common/target-implicit-map-2.c
index 974a9786c3f..4c49cd091c3 100644
--- a/libgomp/testsuite/libgomp.c-c++-common/target-implicit-map-2.c
+++ b/libgomp/testsuite/libgomp.c-c++-common/target-implicit-map-2.c
@@ -42,5 +42,7 @@ main (void)
 
   #pragma omp target exit data map(from:a.ptr, a.ptr[:N])
 
+  free (a.ptr);
+
   return 0;
 }
diff --git a/libgomp/testsuite/libgomp.c-c++-common/target-implicit-map-5.c b/libgomp/testsuite/libgomp.c-c++-common/target-implicit-map-5.c
new file mode 100644
index 00000000000..81a7752685c
--- /dev/null
+++ b/libgomp/testsuite/libgomp.c-c++-common/target-implicit-map-5.c
@@ -0,0 +1,50 @@
+#include <stdlib.h>
+
+#define N 10
+
+struct S
+{
+  int a, b;
+  int *ptr;
+  int c, d;
+};
+
+int
+main (void)
+{
+  struct S a;
+  a.ptr = (int *) malloc (sizeof (int) * N);
+
+  for (int i = 0; i < N; i++)
+    a.ptr[i] = 0;
+
+  #pragma omp target enter data map(to: a.ptr)
+  #pragma omp target enter data map(to: a.ptr[:N])
+
+  #pragma omp target
+  for (int i = 0; i < N; i++)
+    a.ptr[i] += 1;
+
+  #pragma omp target update from(a.ptr[:N])
+
+  for (int i = 0; i < N; i++)
+    if (a.ptr[i] != 1)
+      abort ();
+
+  #pragma omp target map(a.ptr[:N])
+  for (int i = 0; i < N; i++)
+    a.ptr[i] += 1;
+
+  #pragma omp target update from(a.ptr[:N])
+
+  for (int i = 0; i < N; i++)
+    if (a.ptr[i] != 2)
+      abort ();
+
+  #pragma omp target exit data map(release: a.ptr[:N])
+  #pragma omp target exit data map(release: a.ptr)
+
+  free (a.ptr);
+
+  return 0;
+}
diff --git a/libgomp/testsuite/libgomp.c-c++-common/target-map-zlas-1.c b/libgomp/testsuite/libgomp.c-c++-common/target-map-zlas-1.c
new file mode 100644
index 00000000000..1ec0c9a0d5f
--- /dev/null
+++ b/libgomp/testsuite/libgomp.c-c++-common/target-map-zlas-1.c
@@ -0,0 +1,36 @@
+#include <stdlib.h>
+
+#define N 10
+
+struct S
+{
+  int a, b;
+  int *ptr;
+  int c, d;
+};
+
+int
+main (void)
+{
+  struct S a;
+  a.ptr = (int *) malloc (sizeof (int) * N);
+
+  for (int i = 0; i < N; i++)
+    a.ptr[i] = 0;
+
+  #pragma omp target enter data map(to: a.ptr[:N])
+
+  #pragma omp target map(a, a.ptr[:0])
+  for (int i = 0; i < N; i++)
+    a.ptr[i] += 1;
+
+  #pragma omp target exit data map(from: a.ptr[:N])
+
+  for (int i = 0; i < N; i++)
+    if (a.ptr[i] != 1)
+      abort ();
+
+  free (a.ptr);
+
+  return 0;
+}
diff --git a/libgomp/testsuite/libgomp.fortran/struct-elem-map-1.f90 b/libgomp/testsuite/libgomp.fortran/struct-elem-map-1.f90
index 58550c79d69..7f3d8174f97 100644
--- a/libgomp/testsuite/libgomp.fortran/struct-elem-map-1.f90
+++ b/libgomp/testsuite/libgomp.fortran/struct-elem-map-1.f90
@@ -409,3 +409,6 @@ contains
   end subroutine eight
 
 end program main
+
+! Fixed by the "Fortran pointers and member mappings" patch
+! { dg-xfail-run-if TODO { offload_device_nonshared_as } }
diff --git a/libgomp/testsuite/libgomp.fortran/target-enter-data-6.f90 b/libgomp/testsuite/libgomp.fortran/target-enter-data-6.f90
index 80d30edbfc5..b55d0b268d4 100644
--- a/libgomp/testsuite/libgomp.fortran/target-enter-data-6.f90
+++ b/libgomp/testsuite/libgomp.fortran/target-enter-data-6.f90
@@ -3,6 +3,16 @@
 ! - arrays with array descriptors
 ! For those, the array descriptor / string length must be mapped with 'to:'
 
+! This test fails without the following additional patches:
+!
+! "OpenMP: Pointers and member mappings":
+! https://gcc.gnu.org/pipermail/gcc-patches/2023-August/627898.html
+!
+! "OpenMP/OpenACC: Reorganise OMP map clause handling in gimplify.cc":
+! https://gcc.gnu.org/pipermail/gcc-patches/2023-August/627900.html
+!
+! { dg-xfail-run-if TODO { offload_device_nonshared_as } }
+
 program main
 implicit none
 

[-- Attachment #3: incremental-8.diff --]
[-- Type: text/x-patch, Size: 38076 bytes --]

diff --git a/gcc/c-family/c-common.h b/gcc/c-family/c-common.h
index ded1b5ded04..9177a9342db 100644
--- a/gcc/c-family/c-common.h
+++ b/gcc/c-family/c-common.h
@@ -1275,8 +1275,10 @@ enum c_omp_region_type
   C_ORT_ACC			= 1 << 1,
   C_ORT_DECLARE_SIMD		= 1 << 2,
   C_ORT_TARGET			= 1 << 3,
+  C_ORT_EXIT_DATA		= 1 << 4,
   C_ORT_OMP_DECLARE_SIMD	= C_ORT_OMP | C_ORT_DECLARE_SIMD,
   C_ORT_OMP_TARGET		= C_ORT_OMP | C_ORT_TARGET,
+  C_ORT_OMP_EXIT_DATA		= C_ORT_OMP | C_ORT_EXIT_DATA,
   C_ORT_ACC_TARGET		= C_ORT_ACC | C_ORT_TARGET
 };
 
@@ -1362,11 +1364,6 @@ public:
   virtual bool check_clause (tree);
   tree get_root_term (bool);
 
-  tree get_address ()
-    {
-      return orig;
-    }
-
   tree unconverted_ref_origin ();
   bool component_access_p ();
 
@@ -1378,9 +1375,9 @@ 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 *);
+				  unsigned *, c_omp_region_type);
   tree expand_map_clause (tree, tree, vec<omp_addr_token *> &,
 			  c_omp_region_type);
 };
diff --git a/gcc/c-family/c-omp.cc b/gcc/c-family/c-omp.cc
index 6700567afaf..5e534aa21cd 100644
--- a/gcc/c-family/c-omp.cc
+++ b/gcc/c-family/c-omp.cc
@@ -3447,6 +3447,13 @@ c_omp_address_inspector::maybe_zero_length_array_section (tree clause)
     case GOMP_MAP_ALWAYS_TO:
     case GOMP_MAP_ALWAYS_FROM:
     case GOMP_MAP_ALWAYS_TOFROM:
+    case GOMP_MAP_PRESENT_ALLOC:
+    case GOMP_MAP_PRESENT_TO:
+    case GOMP_MAP_PRESENT_FROM:
+    case GOMP_MAP_PRESENT_TOFROM:
+    case GOMP_MAP_ALWAYS_PRESENT_TO:
+    case GOMP_MAP_ALWAYS_PRESENT_FROM:
+    case GOMP_MAP_ALWAYS_PRESENT_TOFROM:
     case GOMP_MAP_RELEASE:
     case GOMP_MAP_DELETE:
     case GOMP_MAP_FORCE_TO:
@@ -3461,26 +3468,31 @@ c_omp_address_inspector::maybe_zero_length_array_section (tree clause)
 
 /* Expand a chained access.  We only expect to see a quite limited range of
    expression types here, because e.g. you can't have an array of
-   references.  See also gimplify.cc:omp_expand_access_chain.  */
+   references.  */
 
 static tree
 omp_expand_access_chain (tree c, tree expr, vec<omp_addr_token *> &addr_tokens,
-			 unsigned *idx)
+			 unsigned *idx, c_omp_region_type ort)
 {
   using namespace omp_addr_tokenizer;
   location_t loc = OMP_CLAUSE_LOCATION (c);
   unsigned i = *idx;
   tree c2 = NULL_TREE;
-  gomp_map_kind kind
-    = (OMP_CLAUSE_CODE (c) == OMP_CLAUSE_FROM
-       || (OMP_CLAUSE_CODE (c) == OMP_CLAUSE_MAP
-	   && (OMP_CLAUSE_MAP_KIND (c) == GOMP_MAP_FROM
-	       || OMP_CLAUSE_MAP_KIND (c) == GOMP_MAP_DELETE
-	       || OMP_CLAUSE_MAP_KIND (c) == GOMP_MAP_RELEASE
-	       || OMP_CLAUSE_MAP_KIND (c) == GOMP_MAP_ALWAYS_FROM
-	       || OMP_CLAUSE_MAP_KIND (c) == GOMP_MAP_FORCE_FROM
-	       || OMP_CLAUSE_MAP_KIND (c) == GOMP_MAP_PRESENT_FROM)))
-      ? GOMP_MAP_DETACH : GOMP_MAP_ATTACH;
+  gomp_map_kind kind;
+
+  if ((ort & C_ORT_EXIT_DATA) != 0
+      || OMP_CLAUSE_CODE (c) == OMP_CLAUSE_FROM
+      || (OMP_CLAUSE_CODE (c) == OMP_CLAUSE_MAP
+	  && (OMP_CLAUSE_MAP_KIND (c) == GOMP_MAP_FROM
+	      || OMP_CLAUSE_MAP_KIND (c) == GOMP_MAP_DELETE
+	      || OMP_CLAUSE_MAP_KIND (c) == GOMP_MAP_RELEASE
+	      || OMP_CLAUSE_MAP_KIND (c) == GOMP_MAP_ALWAYS_FROM
+	      || OMP_CLAUSE_MAP_KIND (c) == GOMP_MAP_FORCE_FROM
+	      || OMP_CLAUSE_MAP_KIND (c) == GOMP_MAP_PRESENT_FROM
+	      || OMP_CLAUSE_MAP_KIND (c) == GOMP_MAP_ALWAYS_PRESENT_FROM)))
+    kind = GOMP_MAP_DETACH;
+  else
+    kind = GOMP_MAP_ATTACH;
 
   switch (addr_tokens[i]->u.access_kind)
     {
@@ -3519,7 +3531,7 @@ omp_expand_access_chain (tree c, tree expr, vec<omp_addr_token *> &addr_tokens,
 
   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 (c, expr, addr_tokens, idx, ort);
 
   return c;
 }
@@ -3530,41 +3542,41 @@ tree
 c_omp_address_inspector::expand_array_base (tree c,
 					    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;
   location_t loc = OMP_CLAUSE_LOCATION (c);
   int i = *idx;
   tree decl = addr_tokens[i + 1]->expr;
+  bool decl_p = DECL_P (decl);
   bool declare_target_p = (decl_p
 			   && is_global_var (decl)
 			   && lookup_attribute ("omp declare target",
 						DECL_ATTRIBUTES (decl)));
   bool map_p = OMP_CLAUSE_CODE (c) == OMP_CLAUSE_MAP;
-  bool implicit_p = (OMP_CLAUSE_CODE (c) == OMP_CLAUSE_MAP
-		     && OMP_CLAUSE_MAP_IMPLICIT (c));
+  bool implicit_p = map_p && OMP_CLAUSE_MAP_IMPLICIT (c);
   bool chain_p = omp_access_chain_p (addr_tokens, i + 1);
   tree c2 = NULL_TREE, c3 = NULL_TREE;
   unsigned consume_tokens = 2;
-  bool target = (ort & C_ORT_TARGET) != 0;
-  bool openmp = (ort & C_ORT_OMP) != 0;
+  bool target_p = (ort & C_ORT_TARGET) != 0;
+  bool openmp_p = (ort & C_ORT_OMP) != 0;
 
   gcc_assert (i == 0);
 
-  if (!openmp
-      && OMP_CLAUSE_CODE (c) == OMP_CLAUSE_MAP
+  if (!openmp_p
+      && map_p
       && (OMP_CLAUSE_MAP_KIND (c) == GOMP_MAP_ATTACH
 	  || OMP_CLAUSE_MAP_KIND (c) == GOMP_MAP_DETACH))
     {
-      *idx = ++i;
+      i += 2;
+      *idx = i;
       return c;
     }
 
   switch (addr_tokens[i + 1]->u.access_kind)
     {
     case ACCESS_DIRECT:
-      if (decl_p && !target)
+      if (decl_p && !target_p)
 	c_common_mark_addressable_vec (addr_tokens[i + 1]->expr);
       break;
 
@@ -3573,7 +3585,11 @@ c_omp_address_inspector::expand_array_base (tree c,
 	/* Copy the referenced object.  Note that we do this even for !MAP_P
 	   clauses.  */
 	tree obj = convert_from_reference (addr_tokens[i + 1]->expr);
-	OMP_CLAUSE_DECL (c) = obj;
+	if (TREE_CODE (TREE_TYPE (obj)) == ARRAY_TYPE)
+	  /* We have a ref to array: add a [0] element as the ME expects.  */
+	  OMP_CLAUSE_DECL (c) = build_array_ref (loc, obj, integer_zero_node);
+	else
+	  OMP_CLAUSE_DECL (c) = obj;
 	OMP_CLAUSE_SIZE (c) = TYPE_SIZE_UNIT (TREE_TYPE (obj));
 
 	if (!map_p)
@@ -3583,6 +3599,9 @@ c_omp_address_inspector::expand_array_base (tree c,
 	    break;
 	  }
 
+	if (!target_p)
+	  break;
+
 	/* If we have a reference to a pointer, avoid using
 	   FIRSTPRIVATE_REFERENCE here in case the pointer is modified in the
 	   offload region (we can only do that if the pointer does not point
@@ -3590,32 +3609,28 @@ c_omp_address_inspector::expand_array_base (tree c,
 	   FROM mapping...  */
 	bool ref_to_ptr = TREE_CODE (TREE_TYPE (obj)) == POINTER_TYPE;
 
-	if (target)
+	c2 = build_omp_clause (loc, OMP_CLAUSE_MAP);
+	if (!ref_to_ptr
+	    && !declare_target_p
+	    && decl_p)
+	  OMP_CLAUSE_SET_MAP_KIND (c2, GOMP_MAP_FIRSTPRIVATE_REFERENCE);
+	else
 	  {
-	    c2 = build_omp_clause (loc, OMP_CLAUSE_MAP);
-	    if (target
-		&& !ref_to_ptr
-		&& !declare_target_p
-		&& decl_p)
-	      OMP_CLAUSE_SET_MAP_KIND (c2, GOMP_MAP_FIRSTPRIVATE_REFERENCE);
-	    else
-	      {
-		OMP_CLAUSE_SET_MAP_KIND (c2, GOMP_MAP_ATTACH_DETACH);
-		if (decl_p)
-		  c_common_mark_addressable_vec (addr_tokens[i + 1]->expr);
-	      }
-	    OMP_CLAUSE_DECL (c2) = addr_tokens[i + 1]->expr;
-	    OMP_CLAUSE_SIZE (c2) = size_zero_node;
+	    OMP_CLAUSE_SET_MAP_KIND (c2, GOMP_MAP_ATTACH_DETACH);
+	    if (decl_p)
+	      c_common_mark_addressable_vec (addr_tokens[i + 1]->expr);
+	  }
+	OMP_CLAUSE_DECL (c2) = addr_tokens[i + 1]->expr;
+	OMP_CLAUSE_SIZE (c2) = size_zero_node;
 
-	    if (ref_to_ptr)
-	      {
-		c3 = c2;
-		c2 = build_omp_clause (loc, OMP_CLAUSE_MAP);
-		OMP_CLAUSE_SET_MAP_KIND (c2, GOMP_MAP_ALLOC);
-		OMP_CLAUSE_DECL (c2) = addr_tokens[i + 1]->expr;
-		OMP_CLAUSE_SIZE (c2)
-		  = TYPE_SIZE_UNIT (TREE_TYPE (OMP_CLAUSE_DECL (c2)));
-	      }
+	if (ref_to_ptr)
+	  {
+	    c3 = c2;
+	    c2 = build_omp_clause (loc, OMP_CLAUSE_MAP);
+	    OMP_CLAUSE_SET_MAP_KIND (c2, GOMP_MAP_ALLOC);
+	    OMP_CLAUSE_DECL (c2) = addr_tokens[i + 1]->expr;
+	    OMP_CLAUSE_SIZE (c2)
+	      = TYPE_SIZE_UNIT (TREE_TYPE (OMP_CLAUSE_DECL (c2)));
 	  }
       }
       break;
@@ -3629,6 +3644,9 @@ c_omp_address_inspector::expand_array_base (tree c,
 	    break;
 	  }
 
+	if (!target_p)
+	  break;
+
 	tree virtual_origin
 	  = convert_from_reference (addr_tokens[i + 1]->expr);
 	virtual_origin = build_fold_addr_expr (virtual_origin);
@@ -3636,8 +3654,21 @@ c_omp_address_inspector::expand_array_base (tree c,
 					   virtual_origin);
 	tree data_addr = omp_accessed_addr (addr_tokens, i + 1, expr);
 	c2 = build_omp_clause (loc, OMP_CLAUSE_MAP);
-	if (decl_p && target && !declare_target_p)
-	  OMP_CLAUSE_SET_MAP_KIND (c2, GOMP_MAP_FIRSTPRIVATE_REFERENCE);
+	if (decl_p && target_p && !declare_target_p)
+	  {
+	    /* It appears that omp-low.cc mishandles cases where we have a
+	       [reference to an] array of pointers such as:
+
+		 int *arr[N];   (or "int *(&arr)[N] = ...")
+		 #pragma omp target map(arr[a][b:c])
+		 { ... }
+
+	       in such cases chain_p will be true.  For now, fall back to
+	       GOMP_MAP_POINTER.  */
+	    enum gomp_map_kind k = chain_p ? GOMP_MAP_POINTER
+					   : GOMP_MAP_FIRSTPRIVATE_REFERENCE;
+	    OMP_CLAUSE_SET_MAP_KIND (c2, k);
+	  }
 	else
 	  {
 	    if (decl_p)
@@ -3665,7 +3696,7 @@ c_omp_address_inspector::expand_array_base (tree c,
 	/* The code handling "firstprivatize_array_bases" in gimplify.cc is
 	   relevant here.  What do we need to create for arrays at this
 	   stage?  (This condition doesn't feel quite right.  FIXME?)  */
-	if (!target
+	if (!target_p
 	    && (TREE_CODE (TREE_TYPE (addr_tokens[i + 1]->expr))
 		== ARRAY_TYPE))
 	  break;
@@ -3676,8 +3707,13 @@ c_omp_address_inspector::expand_array_base (tree c,
 					   virtual_origin);
 	tree data_addr = omp_accessed_addr (addr_tokens, i + 1, expr);
 	c2 = build_omp_clause (loc, OMP_CLAUSE_MAP);
-	if (decl_p && target)
-	  OMP_CLAUSE_SET_MAP_KIND (c2, GOMP_MAP_FIRSTPRIVATE_POINTER);
+	if (decl_p && target_p)
+	  {
+	    /* See comment for ACCESS_INDEXED_REF_TO_ARRAY above.  */
+	    enum gomp_map_kind k = chain_p ? GOMP_MAP_POINTER
+					   : GOMP_MAP_FIRSTPRIVATE_POINTER;
+	    OMP_CLAUSE_SET_MAP_KIND (c2, k);
+	  }
 	else
 	  {
 	    if (decl_p)
@@ -3730,7 +3766,7 @@ c_omp_address_inspector::expand_array_base (tree c,
 	   regions (e.g. "acc data" constructs).  It'll be removed anyway in
 	   gimplify.cc, but doing it this way maintains diagnostic
 	   behaviour.  */
-	if (decl_p && (target || !openmp) && !chain_p && !declare_target_p)
+	if (decl_p && (target_p || !openmp_p) && !chain_p && !declare_target_p)
 	  OMP_CLAUSE_SET_MAP_KIND (c2, GOMP_MAP_FIRSTPRIVATE_POINTER);
 	else
 	  {
@@ -3785,15 +3821,19 @@ c_omp_address_inspector::expand_array_base (tree c,
 
 	tree data_addr = omp_accessed_addr (addr_tokens, last_access, expr);
 	c2 = build_omp_clause (loc, OMP_CLAUSE_MAP);
-	if (decl_p && target && !declare_target_p)
-	  OMP_CLAUSE_SET_MAP_KIND (c2, GOMP_MAP_FIRSTPRIVATE_REFERENCE);
+	if (decl_p && target_p && !chain_p && !declare_target_p)
+	  {
+	    OMP_CLAUSE_SET_MAP_KIND (c2, GOMP_MAP_FIRSTPRIVATE_REFERENCE);
+	    OMP_CLAUSE_DECL (c2) = addr_tokens[i + 1]->expr;
+	  }
 	else
 	  {
 	    if (decl_p)
 	      c_common_mark_addressable_vec (addr_tokens[i + 1]->expr);
 	    OMP_CLAUSE_SET_MAP_KIND (c2, GOMP_MAP_ATTACH_DETACH);
+	    OMP_CLAUSE_DECL (c2)
+	      = convert_from_reference (addr_tokens[i + 1]->expr);
 	  }
-	OMP_CLAUSE_DECL (c2) = addr_tokens[i + 1]->expr;
 	OMP_CLAUSE_SIZE (c2)
 	  = fold_build2_loc (loc, MINUS_EXPR, ptrdiff_type_node,
 			     fold_convert_loc (loc, ptrdiff_type_node,
@@ -3832,7 +3872,7 @@ c_omp_address_inspector::expand_array_base (tree c,
   *idx = i;
 
   if (chain_p && map_p)
-    return omp_expand_access_chain (c, expr, addr_tokens, idx);
+    return omp_expand_access_chain (c, expr, addr_tokens, idx, ort);
 
   return c;
 }
@@ -3843,7 +3883,8 @@ tree
 c_omp_address_inspector::expand_component_selector (tree c,
 						    vec<omp_addr_token *>
 						      &addr_tokens,
-						    tree expr, unsigned *idx)
+						    tree expr, unsigned *idx,
+						    c_omp_region_type ort)
 {
   using namespace omp_addr_tokenizer;
   location_t loc = OMP_CLAUSE_LOCATION (c);
@@ -3973,7 +4014,7 @@ c_omp_address_inspector::expand_component_selector (tree c,
   *idx = i;
 
   if (chain_p && map_p)
-    return omp_expand_access_chain (c, expr, addr_tokens, idx);
+    return omp_expand_access_chain (c, expr, addr_tokens, idx, ort);
 
   return c;
 }
@@ -3998,7 +4039,7 @@ 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);
+	  c = expand_array_base (c, addr_tokens, expr, &i, ort);
 	  if (c == error_mark_node)
 	    return error_mark_node;
 	}
@@ -4007,7 +4048,7 @@ c_omp_address_inspector::expand_map_clause (tree c, tree expr,
 	       && 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);
+	  c = expand_array_base (c, addr_tokens, expr, &i, ort);
 	  if (c == error_mark_node)
 	    return error_mark_node;
 	}
@@ -4043,7 +4084,7 @@ c_omp_address_inspector::expand_map_clause (tree c, tree expr,
 	       && addr_tokens[i]->type == COMPONENT_SELECTOR
 	       && addr_tokens[i + 1]->type == ACCESS_METHOD)
 	{
-	  c = expand_component_selector (c, addr_tokens, expr, &i);
+	  c = expand_component_selector (c, addr_tokens, expr, &i, ort);
 	  /* We used 'expr', so these must have been the last tokens.  */
 	  gcc_assert (i == length);
 	  if (c == error_mark_node)
diff --git a/gcc/c/c-parser.cc b/gcc/c/c-parser.cc
index 891d449b1c0..69d2bee9c16 100644
--- a/gcc/c/c-parser.cc
+++ b/gcc/c/c-parser.cc
@@ -19057,7 +19057,7 @@ c_parser_omp_clause_detach (c_parser *parser, tree list)
 static tree
 c_parser_oacc_all_clauses (c_parser *parser, omp_clause_mask mask,
 			   const char *where, bool finish_p = true,
-			   bool target = false)
+			   bool target_p = false)
 {
   tree clauses = NULL;
   bool first = true;
@@ -19267,8 +19267,8 @@ c_parser_oacc_all_clauses (c_parser *parser, omp_clause_mask mask,
   c_parser_skip_to_pragma_eol (parser);
 
   if (finish_p)
-    return c_finish_omp_clauses (clauses, target ? C_ORT_ACC_TARGET
-						 : C_ORT_ACC);
+    return c_finish_omp_clauses (clauses, target_p ? C_ORT_ACC_TARGET
+						   : C_ORT_ACC);
 
   return clauses;
 }
@@ -23623,6 +23623,7 @@ c_parser_omp_target_data (location_t loc, c_parser *parser, bool *if_p)
 	  case GOMP_MAP_FIRSTPRIVATE_POINTER:
 	  case GOMP_MAP_ALWAYS_POINTER:
 	  case GOMP_MAP_ATTACH_DETACH:
+	  case GOMP_MAP_ATTACH:
 	    break;
 	  default:
 	    map_seen |= 1;
@@ -23863,7 +23864,8 @@ c_parser_omp_target_exit_data (location_t loc, c_parser *parser,
 
   tree clauses
     = c_parser_omp_all_clauses (parser, OMP_TARGET_EXIT_DATA_CLAUSE_MASK,
-				"#pragma omp target exit data");
+				"#pragma omp target exit data", false);
+  clauses = c_finish_omp_clauses (clauses, C_ORT_OMP_EXIT_DATA);
   c_omp_adjust_map_clauses (clauses, false);
   int map_seen = 0;
   for (tree *pc = &clauses; *pc;)
@@ -24155,7 +24157,9 @@ check_clauses:
 	  case GOMP_MAP_PRESENT_ALLOC:
 	  case GOMP_MAP_FIRSTPRIVATE_POINTER:
 	  case GOMP_MAP_ALWAYS_POINTER:
+	  case GOMP_MAP_POINTER:
 	  case GOMP_MAP_ATTACH_DETACH:
+	  case GOMP_MAP_ATTACH:
 	    break;
 	  default:
 	    error_at (OMP_CLAUSE_LOCATION (*pc),
diff --git a/gcc/c/c-typeck.cc b/gcc/c/c-typeck.cc
index 10ca0a2b343..011a1991d8a 100644
--- a/gcc/c/c-typeck.cc
+++ b/gcc/c/c-typeck.cc
@@ -13891,7 +13891,7 @@ 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 &c, enum c_omp_region_type ort)
 {
   bool maybe_zero_len = false;
   unsigned int first_non_one = 0;
@@ -14100,9 +14100,16 @@ handle_omp_array_sections (tree c, enum c_omp_region_type ort)
       OMP_CLAUSE_DECL (c) = first;
       if (OMP_CLAUSE_CODE (c) == OMP_CLAUSE_HAS_DEVICE_ADDR)
 	return false;
-      if (size)
-	size = c_fully_fold (size, false, NULL);
-      OMP_CLAUSE_SIZE (c) = size;
+      /* Don't set OMP_CLAUSE_SIZE for bare attach/detach clauses.  */
+      if (OMP_CLAUSE_CODE (c) != OMP_CLAUSE_MAP
+	  || (OMP_CLAUSE_MAP_KIND (c) != GOMP_MAP_ATTACH
+	      && OMP_CLAUSE_MAP_KIND (c) != GOMP_MAP_DETACH
+	      && OMP_CLAUSE_MAP_KIND (c) != GOMP_MAP_FORCE_DETACH))
+	{
+	  if (size)
+	    size = c_fully_fold (size, false, NULL);
+	  OMP_CLAUSE_SIZE (c) = size;
+	}
 
       if (OMP_CLAUSE_CODE (c) != OMP_CLAUSE_MAP)
 	return false;
@@ -14117,9 +14124,21 @@ handle_omp_array_sections (tree c, enum c_omp_region_type ort)
       tree nc = ai.expand_map_clause (c, first, addr_tokens, ort);
       if (nc != error_mark_node)
 	{
+	  using namespace omp_addr_tokenizer;
+
 	  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
+	       || 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;
+
 	  return false;
 	}
     }
@@ -15289,7 +15308,8 @@ c_finish_omp_clauses (tree clauses, enum c_omp_region_type ort)
 		  remove = true;
 		if (OMP_CLAUSE_CODE (c) == OMP_CLAUSE_MAP
 		    && (OMP_CLAUSE_MAP_KIND (c) == GOMP_MAP_ATTACH
-			|| OMP_CLAUSE_MAP_KIND (c) == GOMP_MAP_DETACH))
+			|| OMP_CLAUSE_MAP_KIND (c) == GOMP_MAP_DETACH)
+		    && !OMP_CLAUSE_SIZE (c))
 		  /* In this case, we have a single array element which is a
 		     pointer, and we already set OMP_CLAUSE_SIZE in
 		     handle_omp_array_sections above.  For attach/detach
@@ -15319,7 +15339,8 @@ c_finish_omp_clauses (tree clauses, enum c_omp_region_type ort)
 	      }
 	    if (OMP_CLAUSE_CODE (c) == OMP_CLAUSE_MAP
 		&& (OMP_CLAUSE_MAP_KIND (c) == GOMP_MAP_ATTACH
-		    || OMP_CLAUSE_MAP_KIND (c) == GOMP_MAP_DETACH))
+		    || OMP_CLAUSE_MAP_KIND (c) == GOMP_MAP_DETACH)
+		&& !OMP_CLAUSE_SIZE (c))
 	      /* For attach/detach clauses, set OMP_CLAUSE_SIZE (representing a
 		 bias) to zero here, so it is not set erroneously to the pointer
 		 size later on in gimplify.cc.  */
@@ -15432,7 +15453,9 @@ c_finish_omp_clauses (tree clauses, enum c_omp_region_type ort)
 		  bitmap_set_bit (&map_firstprivate_head, DECL_UID (t));
 	      }
 	    else if (bitmap_bit_p (&map_head, DECL_UID (t))
-		     && !bitmap_bit_p (&map_field_head, DECL_UID (t)))
+		     && !bitmap_bit_p (&map_field_head, DECL_UID (t))
+		     && ort != C_ORT_OMP
+		     && ort != C_ORT_OMP_EXIT_DATA)
 	      {
 		if (OMP_CLAUSE_CODE (c) != OMP_CLAUSE_MAP)
 		  error_at (OMP_CLAUSE_LOCATION (c),
diff --git a/gcc/cp/parser.cc b/gcc/cp/parser.cc
index d25a6c7734a..e54acd39025 100644
--- a/gcc/cp/parser.cc
+++ b/gcc/cp/parser.cc
@@ -41358,7 +41358,7 @@ cp_parser_oacc_compute_clause_self (cp_parser *parser, tree list)
 static tree
 cp_parser_oacc_all_clauses (cp_parser *parser, omp_clause_mask mask,
 			    const char *where, cp_token *pragma_tok,
-			    bool finish_p = true, bool target = false)
+			    bool finish_p = true, bool target_p = false)
 {
   tree clauses = NULL;
   bool first = true;
@@ -41577,7 +41577,8 @@ cp_parser_oacc_all_clauses (cp_parser *parser, omp_clause_mask mask,
   cp_parser_skip_to_pragma_eol (parser, pragma_tok);
 
   if (finish_p)
-    return finish_omp_clauses (clauses, target ? C_ORT_ACC_TARGET : C_ORT_ACC);
+    return finish_omp_clauses (clauses, target_p ? C_ORT_ACC_TARGET
+						 : C_ORT_ACC);
 
   return clauses;
 }
@@ -46121,6 +46122,7 @@ cp_parser_omp_target_data (cp_parser *parser, cp_token *pragma_tok, bool *if_p)
 	  case GOMP_MAP_FIRSTPRIVATE_REFERENCE:
 	  case GOMP_MAP_ALWAYS_POINTER:
 	  case GOMP_MAP_ATTACH_DETACH:
+	  case GOMP_MAP_ATTACH:
 	    break;
 	  default:
 	    map_seen |= 1;
@@ -46322,7 +46324,9 @@ cp_parser_omp_target_exit_data (cp_parser *parser, cp_token *pragma_tok,
 
   tree clauses
     = cp_parser_omp_all_clauses (parser, OMP_TARGET_EXIT_DATA_CLAUSE_MASK,
-				 "#pragma omp target exit data", pragma_tok);
+				 "#pragma omp target exit data", pragma_tok,
+				 false);
+  clauses = finish_omp_clauses (clauses, C_ORT_OMP_EXIT_DATA);
   c_omp_adjust_map_clauses (clauses, false);
   int map_seen = 0;
   for (tree *pc = &clauses; *pc;)
diff --git a/gcc/cp/semantics.cc b/gcc/cp/semantics.cc
index 3f90fdbe51f..ae85df0590a 100644
--- a/gcc/cp/semantics.cc
+++ b/gcc/cp/semantics.cc
@@ -5903,7 +5903,11 @@ handle_omp_array_sections (tree &c, enum c_omp_region_type ort)
 	  OMP_CLAUSE_DECL (c) = first;
 	  if (OMP_CLAUSE_CODE (c) == OMP_CLAUSE_HAS_DEVICE_ADDR)
 	    return false;
-	  OMP_CLAUSE_SIZE (c) = size;
+	  if (OMP_CLAUSE_CODE (c) != OMP_CLAUSE_MAP
+	      || (OMP_CLAUSE_MAP_KIND (c) != GOMP_MAP_ATTACH
+		  && OMP_CLAUSE_MAP_KIND (c) != GOMP_MAP_DETACH
+		  && OMP_CLAUSE_MAP_KIND (c) != GOMP_MAP_FORCE_DETACH))
+	    OMP_CLAUSE_SIZE (c) = size;
 	  if (TREE_CODE (t) == FIELD_DECL)
 	    t = finish_non_static_data_member (t, NULL_TREE, NULL_TREE);
 
@@ -8271,7 +8275,8 @@ finish_omp_clauses (tree clauses, enum c_omp_region_type ort)
 		  remove = true;
 		if (OMP_CLAUSE_CODE (c) == OMP_CLAUSE_MAP
 		    && (OMP_CLAUSE_MAP_KIND (c) == GOMP_MAP_ATTACH
-			|| OMP_CLAUSE_MAP_KIND (c) == GOMP_MAP_DETACH))
+			|| OMP_CLAUSE_MAP_KIND (c) == GOMP_MAP_DETACH)
+		    && !OMP_CLAUSE_SIZE (c))
 		  /* In this case, we have a single array element which is a
 		     pointer, and we already set OMP_CLAUSE_SIZE in
 		     handle_omp_array_sections above.  For attach/detach
@@ -8303,7 +8308,8 @@ finish_omp_clauses (tree clauses, enum c_omp_region_type ort)
 	      }
 	    if (OMP_CLAUSE_CODE (c) == OMP_CLAUSE_MAP
 		&& (OMP_CLAUSE_MAP_KIND (c) == GOMP_MAP_ATTACH
-		    || OMP_CLAUSE_MAP_KIND (c) == GOMP_MAP_DETACH))
+		    || OMP_CLAUSE_MAP_KIND (c) == GOMP_MAP_DETACH)
+		&& !OMP_CLAUSE_SIZE (c))
 	      /* For attach/detach clauses, set OMP_CLAUSE_SIZE (representing a
 		 bias) to zero here, so it is not set erroneously to the
 		 pointer size later on in gimplify.cc.  */
@@ -8458,7 +8464,8 @@ finish_omp_clauses (tree clauses, enum c_omp_region_type ort)
 	      bitmap_set_bit (&map_firstprivate_head, DECL_UID (t));
 	    else if (bitmap_bit_p (&map_head, DECL_UID (t))
 		     && !bitmap_bit_p (&map_field_head, DECL_UID (t))
-		     && ort != C_ORT_OMP)
+		     && ort != C_ORT_OMP
+		     && ort != C_ORT_OMP_EXIT_DATA)
 	      {
 		if (OMP_CLAUSE_CODE (c) != OMP_CLAUSE_MAP)
 		  error_at (OMP_CLAUSE_LOCATION (c),
@@ -10114,6 +10121,7 @@ finish_omp_target (location_t loc, tree clauses, tree body, bool combined_p)
 	  case GOMP_MAP_ATTACH_DETACH:
 	  case GOMP_MAP_ATTACH:
 	  case GOMP_MAP_ATTACH_ZERO_LENGTH_ARRAY_SECTION:
+	  case GOMP_MAP_POINTER:
 	  case GOMP_MAP_POINTER_TO_ZERO_LENGTH_ARRAY_SECTION:
 	    break;
 	  default:
diff --git a/gcc/gimplify.cc b/gcc/gimplify.cc
index 15ca2832608..1e34bde19be 100644
--- a/gcc/gimplify.cc
+++ b/gcc/gimplify.cc
@@ -10608,6 +10608,10 @@ omp_resolve_clause_dependencies (enum tree_code code,
 	  }
 	  break;
 
+	case GOMP_MAP_ATTACH:
+	  /* Ignore standalone attach here.  */
+	  break;
+
 	default:
 	  {
 	    omp_mapping_group *struct_group;
@@ -10869,59 +10873,6 @@ omp_siblist_move_concat_nodes_after (tree first_new, tree *last_new_tail,
   return continue_at;
 }
 
-/* Expand a chained access.  We only expect to see a quite limited range of
-   expression types here, because e.g. you can't have an array of
-   references.  See also c-omp.cc:omp_expand_access_chain.  */
-
-static void
-omp_expand_access_chain (location_t loc, tree **list_pp, tree expr,
-			 vec<omp_addr_token *> &addr_tokens,
-			 unsigned *idx, gomp_map_kind kind)
-{
-  using namespace omp_addr_tokenizer;
-  unsigned i = *idx;
-  tree c = NULL_TREE;
-
-  switch (addr_tokens[i]->u.access_kind)
-    {
-    case ACCESS_POINTER:
-    case ACCESS_POINTER_OFFSET:
-      {
-	tree virtual_origin
-	  = fold_convert_loc (loc, ptrdiff_type_node, addr_tokens[i]->expr);
-	tree data_addr = omp_accessed_addr (addr_tokens, i, expr);
-	c = build_omp_clause (loc, OMP_CLAUSE_MAP);
-	OMP_CLAUSE_SET_MAP_KIND (c, kind);
-	OMP_CLAUSE_DECL (c) = addr_tokens[i]->expr;
-	OMP_CLAUSE_SIZE (c)
-	  = fold_build2_loc (loc, MINUS_EXPR, ptrdiff_type_node,
-			     fold_convert_loc (loc, ptrdiff_type_node,
-					       data_addr),
-			     virtual_origin);
-      }
-      break;
-
-    case ACCESS_INDEXED_ARRAY:
-      break;
-
-    default:
-      return;
-    }
-
-  if (c)
-    {
-      OMP_CLAUSE_CHAIN (c) = **list_pp;
-      **list_pp = c;
-      *list_pp = &OMP_CLAUSE_CHAIN (c);
-    }
-
-  *idx = ++i;
-
-  if (addr_tokens[i]->type == ACCESS_METHOD
-      && omp_access_chain_p (addr_tokens, i))
-    omp_expand_access_chain (loc, list_pp, expr, addr_tokens, idx, kind);
-}
-
 static omp_addr_token *
 omp_first_chained_access_token (vec<omp_addr_token *> &addr_tokens)
 {
@@ -12286,8 +12237,19 @@ gimplify_scan_omp_clauses (tree *list_p, gimple_seq *pre_p,
 		}
 	    }
 	  if (OMP_CLAUSE_SIZE (c) == NULL_TREE)
-	    OMP_CLAUSE_SIZE (c) = DECL_P (decl) ? DECL_SIZE_UNIT (decl)
-				  : TYPE_SIZE_UNIT (TREE_TYPE (decl));
+	    {
+	      /* Sanity check: attach/detach map kinds use the size as a bias,
+		 and it's never right to use the decl size for such
+		 mappings.  */
+	      gcc_assert (OMP_CLAUSE_MAP_KIND (c) != GOMP_MAP_ATTACH
+			  && OMP_CLAUSE_MAP_KIND (c) != GOMP_MAP_DETACH
+			  && OMP_CLAUSE_MAP_KIND (c) != GOMP_MAP_FORCE_DETACH
+			  && OMP_CLAUSE_MAP_KIND (c) != GOMP_MAP_ATTACH_DETACH
+			  && (OMP_CLAUSE_MAP_KIND (c)
+			      != GOMP_MAP_ATTACH_ZERO_LENGTH_ARRAY_SECTION));
+	      OMP_CLAUSE_SIZE (c) = DECL_P (decl) ? DECL_SIZE_UNIT (decl)
+				    : TYPE_SIZE_UNIT (TREE_TYPE (decl));
+	    }
 	  if (gimplify_expr (&OMP_CLAUSE_SIZE (c), pre_p,
 			     NULL, is_gimple_val, fb_rvalue) == GS_ERROR)
 	    {
@@ -12554,6 +12516,32 @@ gimplify_scan_omp_clauses (tree *list_p, gimple_seq *pre_p,
 					     ? GOMP_MAP_DETACH
 					     : GOMP_MAP_ATTACH);
 	      OMP_CLAUSE_SET_MAP_KIND (c, map_kind);
+
+	      /* If we have attach/detach but the decl we have is a pointer to
+		 pointer, we're probably mapping the "base level" array
+		 implicitly.  Make sure we don't add the decl as if we mapped
+		 it explicitly.  That is,
+
+		   int **arr;
+		   [...]
+		   #pragma omp target map(arr[a][b:c])
+
+		 should *not* map "arr" explicitly.  That way we get a
+		 zero-length "alloc" mapping for it, and assuming it's been
+		 mapped by some previous directive, etc., things work as they
+		 should.  */
+
+	      tree basetype = TREE_TYPE (addr_tokens[0]->expr);
+
+	      if (TREE_CODE (basetype) == REFERENCE_TYPE)
+		basetype = TREE_TYPE (basetype);
+
+	      if (code == OMP_TARGET
+		  && addr_tokens[0]->type == ARRAY_BASE
+		  && addr_tokens[0]->u.structure_base_kind == BASE_DECL
+		  && TREE_CODE (basetype) == POINTER_TYPE
+		  && TREE_CODE (TREE_TYPE (basetype)) == POINTER_TYPE)
+		break;
 	    }
 	  else if ((code == OACC_ENTER_DATA
 		    || code == OACC_EXIT_DATA
@@ -13480,7 +13468,16 @@ gimplify_adjust_omp_clauses_1 (splay_tree_node n, void *data)
       OMP_CLAUSE_SIZE (nc) = size_zero_node;
       OMP_CLAUSE_SET_MAP_KIND (clause, GOMP_MAP_ALLOC);
       OMP_CLAUSE_MAP_MAYBE_ZERO_LENGTH_ARRAY_SECTION (clause) = 1;
-      OMP_CLAUSE_SET_MAP_KIND (nc, GOMP_MAP_FIRSTPRIVATE_POINTER);
+      tree dtype = TREE_TYPE (decl);
+      if (TREE_CODE (dtype) == REFERENCE_TYPE)
+	dtype = TREE_TYPE (dtype);
+      /* FIRSTPRIVATE_POINTER doesn't work well if we have a
+	 multiply-indirected pointer.  */
+      if (TREE_CODE (dtype) == POINTER_TYPE
+	  && TREE_CODE (TREE_TYPE (dtype)) == POINTER_TYPE)
+	OMP_CLAUSE_SET_MAP_KIND (nc, GOMP_MAP_POINTER);
+      else
+	OMP_CLAUSE_SET_MAP_KIND (nc, GOMP_MAP_FIRSTPRIVATE_POINTER);
       OMP_CLAUSE_CHAIN (nc) = chain;
       OMP_CLAUSE_CHAIN (clause) = nc;
       struct gimplify_omp_ctx *ctx = gimplify_omp_ctxp;
diff --git a/gcc/testsuite/c-c++-common/gomp/target-enter-data-1.c b/gcc/testsuite/c-c++-common/gomp/target-enter-data-1.c
index 37c8dd4e328..4913d338e5f 100644
--- a/gcc/testsuite/c-c++-common/gomp/target-enter-data-1.c
+++ b/gcc/testsuite/c-c++-common/gomp/target-enter-data-1.c
@@ -21,5 +21,5 @@ void func (struct foo *f, int n, int m)
   #pragma omp target enter data map (to: f->bars[n].vectors[:f->bars[n].num_vectors])
 }
 
-/* { dg-final { scan-tree-dump-times {map\(struct:\*f \[len: 1\]\) map\(alloc:[a-z0-9\._]+->vectors \[len: 0\]\) map\(to:\*_[0-9]+ \[len: _[0-9]+\]\) map\(attach:[a-z0-9\._]+->vectors \[bias: [^\]]+\]\) map\(attach:\*_[0-9]+ \[bias: 0\]\)} 1 "gimple" } } */
+/* { dg-final { scan-tree-dump-times {map\(struct:\*f \[len: 1\]\) map\(alloc:[a-z0-9\._]+->vectors \[len: 0\]\) map\(to:\*_[0-9]+ \[len: _[0-9]+\]\) map\(attach:[a-z0-9\._]+->vectors \[bias: [^\]]+\]\) map\(attach:\*_[0-9]+ \[bias: _[0-9]+\]\)} 1 "gimple" } } */
 /* { dg-final { scan-tree-dump-times {map\(struct:\*\(f->bars \+ \(sizetype\) \(\([^\)]+\) n \* 16\)\) \[len: 1\]\) map\(alloc:[a-z0-9\._]+->vectors \[len: 0\]\) map\(to:\*_[0-9]+ \[len: _[0-9]+\]\) map\(attach:[a-z0-9\._]+->vectors \[bias: [^\]]+\]\)} 2 "gimple" } } */
diff --git a/libgomp/testsuite/libgomp.c++/baseptrs-8.C b/libgomp/testsuite/libgomp.c++/baseptrs-8.C
new file mode 100644
index 00000000000..f9991818551
--- /dev/null
+++ b/libgomp/testsuite/libgomp.c++/baseptrs-8.C
@@ -0,0 +1,70 @@
+/* { dg-do run } */
+
+#include <stdlib.h>
+#include <assert.h>
+
+#define N 1024
+#define M 64
+
+int main (void)
+{
+  int *a_orig[N];
+  int *(&a)[N] = a_orig;
+
+  for (int i = 0; i < N; i++)
+    a[i] = (int *) calloc (M, sizeof (int));
+
+  /* 'target enter data'/'target exit data' with array of pointers.  */
+#pragma omp target enter data map(alloc: a[0:N])
+
+  for (int i = 0; i < N; i++)
+  {
+#pragma omp target enter data map(to: a[i][0:M])
+  }
+
+#pragma omp target map(alloc: a)
+  {
+    for (int i = 0; i < N; i++)
+      for (int j = 0; j < M; j++)
+	a[i][j] = i + j;
+  }
+
+for (int i = 0; i < N; i++)
+  {
+#pragma omp target exit data map(release: a[i]) map(from: a[i][0:M])
+  }
+
+#pragma omp target exit data map(release: a, a[0:N])
+
+  /* 'target data' with array of pointers.  */
+#pragma omp target data map(alloc: a[0:N])
+  {
+#pragma omp target data map(tofrom: a[5][0:M])
+    {
+#pragma omp target map(alloc: a)
+      {
+	for (int i = 0; i < M; i++)
+	  a[5][i]++;
+      }
+    }
+  }
+
+  /* 'target' with array of pointers.  */
+#pragma omp target data map(alloc: a[0:N])
+  {
+#pragma omp target map(tofrom: a[7][0:M])
+    {
+      for (int i = 0; i < M; i++)
+	a[7][i] += 2;
+    }
+  }
+
+  for (int i = 0; i < N; i++)
+    for (int j = 0; j < M; j++)
+      assert (a[i][j] == i + j + (i == 5) + 2 * (i == 7));
+
+  for (int i = 0; i < N; i++)
+    free (a[i]);
+
+  return 0;
+}
diff --git a/libgomp/testsuite/libgomp.c++/baseptrs-9.C b/libgomp/testsuite/libgomp.c++/baseptrs-9.C
new file mode 100644
index 00000000000..95e7eebb0ed
--- /dev/null
+++ b/libgomp/testsuite/libgomp.c++/baseptrs-9.C
@@ -0,0 +1,57 @@
+/* { dg-do run } */
+
+#include <stdlib.h>
+#include <assert.h>
+
+int main (void)
+{
+  int **a_orig,i,j,n;
+  int **&a = a_orig;
+
+  j = 3;
+  n = 12;
+
+  a = (int **) calloc (32, sizeof (int *));
+  for (int x = 0; x < 32; x++)
+    a[x] = (int *) calloc (32, sizeof (int));
+
+  for (int i = 2; i < 32; i++)
+    {
+      #pragma omp target enter data map(a, a[2:30])
+      #pragma omp target enter data map(a[i][j:n])
+      #pragma omp target map(alloc: a)
+      {
+	for (int x = j; x < j + n; x++)
+	  a[i][x]++;
+      }
+      #pragma omp target exit data map(a[i][j:n])
+
+      #pragma omp target data map(a, a[i][j:n])
+      {
+	#pragma omp target map(alloc: a)
+	{
+	  for (int x = j; x < j + n; x++)
+	    a[i][x]++;
+	}
+      }
+      #pragma omp target exit data map(a, a[2:30])
+
+      #pragma omp target data map(a, a[2:30])
+      {
+	#pragma omp target map(a[i][j:n])
+	{
+	  for (int x = j; x < j + n; x++)
+	    a[i][x]++;
+	}
+      }
+    }
+
+  for (int i = 0; i < 32; i++)
+    for (int j = 0; j < 32; j++)
+      if (i >= 2 && j >= 3 && j < 15)
+	assert (a[i][j] == 3);
+      else
+	assert (a[i][j] == 0);
+
+  return 0;
+}
diff --git a/libgomp/testsuite/libgomp.c++/ref-mapping-1.C b/libgomp/testsuite/libgomp.c++/ref-mapping-1.C
new file mode 100644
index 00000000000..9aa232f3f67
--- /dev/null
+++ b/libgomp/testsuite/libgomp.c++/ref-mapping-1.C
@@ -0,0 +1,80 @@
+/* { dg-do run } */
+
+#include <cassert>
+
+void test_ref ()
+{
+  int c_orig = 5;
+  int &c = c_orig;
+
+#pragma omp target map(tofrom: c)
+  {
+    c++;
+  }
+
+  assert (c == 6);
+}
+
+void test_ref_to_ptr ()
+{
+  int val = 5;
+  int *ptr_orig = &val;
+  int *&ptr_ref = ptr_orig;
+
+#pragma omp target map(tofrom: ptr_ref[0])
+  {
+    (*ptr_ref)++;
+  }
+
+  assert (val == 6);
+}
+
+void test_ref_to_array ()
+{
+  int arr[1];
+  int (&arr_ref)[1] = arr;
+
+  arr_ref[0] = 5;
+
+#pragma omp target map(tofrom: arr_ref[0:1])
+  {
+    arr_ref[0]++;
+  }
+
+  assert (arr_ref[0] == 6);
+
+#pragma omp target map(tofrom: arr_ref[0])
+  {
+    arr_ref[0]++;
+  }
+
+  assert (arr_ref[0] == 7);
+}
+
+void test_ref_to_ptr_array ()
+{
+  int *arr[1];
+  int *(&arr_ref)[1] = arr;
+  int val = 5;
+
+  arr_ref[0] = &val;
+
+#pragma omp target data map(alloc: arr_ref, arr_ref[0])
+  {
+#pragma omp target map(tofrom: arr_ref[0][0:1])
+    {
+      arr_ref[0][0]++;
+    }
+  }
+
+  assert (arr_ref[0][0] == 6);
+}
+
+int main ()
+{
+  test_ref ();
+  test_ref_to_ptr ();
+  test_ref_to_array ();
+  test_ref_to_ptr_array ();
+  return 0;
+}
diff --git a/libgomp/testsuite/libgomp.c-c++-common/baseptrs-6.c b/libgomp/testsuite/libgomp.c-c++-common/baseptrs-6.c
new file mode 100644
index 00000000000..4b6e237471a
--- /dev/null
+++ b/libgomp/testsuite/libgomp.c-c++-common/baseptrs-6.c
@@ -0,0 +1,69 @@
+/* { dg-do run } */
+
+#include <stdlib.h>
+#include <assert.h>
+
+#define N 1024
+#define M 64
+
+int main (void)
+{
+  int *a[N];
+
+  for (int i = 0; i < N; i++)
+    a[i] = (int *) calloc (M, sizeof (int));
+
+  /* 'target enter data'/'target exit data' with array of pointers.  */
+#pragma omp target enter data map(alloc: a[0:N])
+
+  for (int i = 0; i < N; i++)
+  {
+#pragma omp target enter data map(to: a[i][0:M])
+  }
+
+#pragma omp target map(alloc: a)
+  {
+    for (int i = 0; i < N; i++)
+      for (int j = 0; j < M; j++)
+	a[i][j] = i + j;
+  }
+
+for (int i = 0; i < N; i++)
+  {
+#pragma omp target exit data map(release: a[i]) map(from: a[i][0:M])
+  }
+
+#pragma omp target exit data map(release: a, a[0:N])
+
+  /* 'target data' with array of pointers.  */
+#pragma omp target data map(alloc: a[0:N])
+  {
+#pragma omp target data map(tofrom: a[5][0:M])
+    {
+#pragma omp target map(alloc: a)
+      {
+	for (int i = 0; i < M; i++)
+	  a[5][i]++;
+      }
+    }
+  }
+
+  /* 'target' with array of pointers.  */
+#pragma omp target data map(alloc: a[0:N])
+  {
+#pragma omp target map(tofrom: a[7][0:M])
+    {
+      for (int i = 0; i < M; i++)
+	a[7][i] += 2;
+    }
+  }
+
+  for (int i = 0; i < N; i++)
+    for (int j = 0; j < M; j++)
+      assert (a[i][j] == i + j + (i == 5) + 2 * (i == 7));
+
+  for (int i = 0; i < N; i++)
+    free (a[i]);
+
+  return 0;
+}
diff --git a/libgomp/testsuite/libgomp.c-c++-common/baseptrs-7.c b/libgomp/testsuite/libgomp.c-c++-common/baseptrs-7.c
new file mode 100644
index 00000000000..9c6710e4e5b
--- /dev/null
+++ b/libgomp/testsuite/libgomp.c-c++-common/baseptrs-7.c
@@ -0,0 +1,56 @@
+/* { dg-do run } */
+
+#include <stdlib.h>
+#include <assert.h>
+
+int main (void)
+{
+  int **a,i,j,n;
+
+  j = 3;
+  n = 12;
+
+  a = (int **) calloc (32, sizeof (int *));
+  for (int x = 0; x < 32; x++)
+    a[x] = (int *) calloc (32, sizeof (int));
+
+  for (int i = 2; i < 32; i++)
+    {
+      #pragma omp target enter data map(a, a[2:30])
+      #pragma omp target enter data map(a[i][j:n])
+      #pragma omp target map(alloc: a)
+      {
+	for (int x = j; x < j + n; x++)
+	  a[i][x]++;
+      }
+      #pragma omp target exit data map(a[i][j:n])
+
+      #pragma omp target data map(a, a[i][j:n])
+      {
+	#pragma omp target map(alloc: a)
+	{
+	  for (int x = j; x < j + n; x++)
+	    a[i][x]++;
+	}
+      }
+      #pragma omp target exit data map(a, a[2:30])
+
+      #pragma omp target data map(a, a[0:32])
+      {
+	#pragma omp target map(a[i][j:n])
+	{
+	  for (int x = j; x < j + n; x++)
+	    a[i][x]++;
+	}
+      }
+    }
+
+  for (int i = 0; i < 32; i++)
+    for (int j = 0; j < 32; j++)
+      if (i >= 2 && j >= 3 && j < 15)
+	assert (a[i][j] == 3);
+      else
+	assert (a[i][j] == 0);
+
+  return 0;
+}
diff --git a/libgomp/testsuite/libgomp.fortran/target-enter-data-6.f90 b/libgomp/testsuite/libgomp.fortran/target-enter-data-6.f90
index 80d30edbfc5..b55d0b268d4 100644
--- a/libgomp/testsuite/libgomp.fortran/target-enter-data-6.f90
+++ b/libgomp/testsuite/libgomp.fortran/target-enter-data-6.f90
@@ -3,6 +3,16 @@
 ! - arrays with array descriptors
 ! For those, the array descriptor / string length must be mapped with 'to:'
 
+! This test fails without the following additional patches:
+!
+! "OpenMP: Pointers and member mappings":
+! https://gcc.gnu.org/pipermail/gcc-patches/2023-August/627898.html
+!
+! "OpenMP/OpenACC: Reorganise OMP map clause handling in gimplify.cc":
+! https://gcc.gnu.org/pipermail/gcc-patches/2023-August/627900.html
+!
+! { dg-xfail-run-if TODO { offload_device_nonshared_as } }
+
 program main
 implicit none
 

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

* Re: [PATCH v7 2/5] OpenMP/OpenACC: Rework clause expansion and nested struct handling
  2023-11-29 11:43     ` Julian Brown
@ 2023-11-29 16:03       ` Tobias Burnus
  2023-12-14  7:14       ` [committed] testsuite: Fix up target-enter-data-1.c on 32-bit targets Jakub Jelinek
  1 sibling, 0 replies; 19+ messages in thread
From: Tobias Burnus @ 2023-11-29 16:03 UTC (permalink / raw)
  To: Julian Brown; +Cc: gcc-patches, fortran, jakub

Hi Julian,

On 29.11.23 12:43, Julian Brown wrote:
> Here is a patch incorporating your initial review comments
> (hopefully!).

Thanks.

The patch LGTM - with the two remarks below addressed.

(i.e. fixing one testcase and filing two PRs (or common PR) about the features
missing and exposed by the two test cases, referencing also to those testcases
- and for the lvalues mentioning the OpenMP spec issue number.)

* * *

BTW: The 1/5 has been several times approved and is just reindenting - and
is obviously still OK.


(Review wise, 3/5, 4/5 and 5/5 still has to be done.

I think the patch can go in before - given the huge improvements, even though
it regresses for a few cases (xfail added for 2 Fortran testcases). 3/5 un-xfails
one and a half of the textcases, 5/5 un-xfails the remaining half and all of {3,4,5}/5
contain very useful improvements besides this. - But maybe waiting for at least 3/5
makes sense.

In either case, I try to review the remaining patches soon.)

* * *

Question regarding the following:
(a) The dg-xfail-run-if looks bogus as this an OpenMP test and not an OpenACC test
(b) If there is shared memory, using 'omp target' should be fine.

Namely, given that:

> --- /dev/null +++ b/libgomp/testsuite/libgomp.c++/target-49.C @@ -0,0
> +1,37 @@ +#include <cstring> +#include <cassert> + +struct s { + int
> (&a)[10]; + s(int (&a0)[10]) : a(a0) {} +}; + +int +main (int argc,
> char *argv[]) +{ + int la[10]; + s v_real(la); + s *v = &v_real; + +
> memset (la, 0, sizeof la); + + #pragma omp target enter data map(to:
> v) + + /* Copying the whole v[0] here DOES NOT WORK yet because the
> reference 'a' is + not copied "as if" it was mapped explicitly as a
> member. FIXME. */ + #pragma omp target enter data map(to: v[0]) + +
> //#pragma omp target + { + v->a[5]++; + } + + #pragma omp target exit
> data map(release: v[0]) + #pragma omp target exit data map(from: v) +
> + assert (v->a[5] == 1); + + return 0; +} + +// { dg-xfail-run-if
> "TODO" { *-*-* } { "-DACC_MEM_SHARED=0" } }
Shouldn't the XFAIL not be based on '{ target offload_device_nonshared_as }'
and the 'omp target' be uncommented?

And I wonder whether we need to file a PR about this issue - I guess it is not
addressed by any of the follow-up issues and might get forgotten unless there is PR.


* * *
> libgomp/testsuite/libgomp.c++/baseptrs-4.C ... // Needs map clause
> "lvalue"-parsing support. //#define REF2ARRAY_DECL_BASE

There is an open OpenMP issue to disallow some lvalues, namely:
OpenMP Issue 2618 ("Clarify behavior of mapping lvalues on target construct")
talks about code like the following

   map(*p = 10)
   map(x = 20)
   map(x ? y[0] : p[1])
   map(f(y))

is valid or not. The sentiment was to require that a 'map' clause list item
must have a base pointer or a base variable.


However, it looks as your examples would be valid in this regard. Can you file
a PR about this one? Referencing both to this testcase and to the OpenMP issue?

(I do note that Clang and GCC reject the lvalue examples from the OpenMP issue
but not your reference examples; those are accepted by clang++-14.)


Tobias

-----------------
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] 19+ messages in thread

* Re: [PATCH v7 3/5] OpenMP: Pointers and member mappings
  2023-08-18 22:47 ` [PATCH v7 3/5] OpenMP: Pointers and member mappings Julian Brown
@ 2023-12-06 11:36   ` Tobias Burnus
  2023-12-07 17:24     ` Julian Brown
  0 siblings, 1 reply; 19+ messages in thread
From: Tobias Burnus @ 2023-12-06 11:36 UTC (permalink / raw)
  To: Julian Brown, gcc-patches; +Cc: fortran, jakub

Hi Julian,

LGTM, except for:

* The 'target exit data' handling - comments below - looks a bit
fishy/inconsistent.

I intent to have a closer look with more code context, but maybe you
should have a look at it as well.

BTW: Fortran deep-mapping is not yet on mainline. Are you aware of
changes or in particular testcases on OG13 related to your patch series
that should be included when upstreaming that auto-mapping of
allocatable components patch?

* * *

On 19.08.23 00:47, Julian Brown wrote:
> This patch changes the mapping node arrangement used for array components
> of derived types in order to accommodate for changes made in the previous
> patch, particularly the use of "GOMP_MAP_ATTACH_DETACH" for pointer-typed
> derived-type members instead of "GOMP_MAP_ALWAYS_POINTER".
>
> We change the mapping nodes used for a derived-type mapping like this:
>
>    type T
>    integer, pointer, dimension(:) :: arrptr
>    end type T
>
>    type(T) :: tvar
>    [...]
>    !$omp target map(tofrom: tvar%arrptr)
>
> So that the nodes used look like this:
>
>    1) map(to: tvar%arrptr)   -->
>    GOMP_MAP_TO [implicit]  *tvar%arrptr%data  (the array data)
>    GOMP_MAP_TO_PSET        tvar%arrptr        (the descriptor)
>    GOMP_MAP_ATTACH_DETACH  tvar%arrptr%data
>
>    2) map(tofrom: tvar%arrptr(3:8)   -->
>    GOMP_MAP_TOFROM         *tvar%arrptr%data(3)  (size 8-3+1, etc.)

[Clarification for the patch readers - not to the patch writer]

At least to me the wording or implications of (1) were not fully clear to
me. However, it is intended that the implicit mapping ensures that the
pointer is actually mapped.

Fortunately, that's indeed the case :-)
as testing and also the new testcase libgomp/testsuite/libgomp.fortran/map-subarray-4.f90  shows.

The related part comes from OpenMP 5.1 [349:9-12] or slightly refined
later, here the TR12 wording:

If a list item in a map clause is an associated pointer that is not attach-ineligible and the pointer is
not the base pointer of another list item in a map clause on the same construct, then it is treated as if
its pointer target is implicitly mapped in the same clause. For the purposes of the map clause, the
mapped pointer target is treated as if its base pointer is the associated pointer."  [213:18-21]

* * *

> [...]
> In the middle end, we process mappings using the struct sibling-list
> handling machinery by moving the "GOMP_MAP_TO_PSET" node from the middle
> of the group of three mapping nodes to the proper sorted position after
> the GOMP_MAP_STRUCT mapping:
>
>    GOMP_MAP_STRUCT   tvar     (len: 1)
>    GOMP_MAP_TO_PSET  tvar%arr (size: 64, etc.)  <--. moved here
>    [...]                                           |
>    GOMP_MAP_TOFROM         *tvar%arrptr%data(3) ___|
>    GOMP_MAP_ATTACH_DETACH  tvar%arrptr%data
>
> In another case, if we have an array of derived-type values "dtarr",
> and mappings like:
>
>    i = 1
>    j = 1
>    map(to: dtarr(i)%arrptr) map(tofrom: dtarr(j)%arrptr(3:8))
>
> We still map the same way, but this time we cannot prove that the base
> expressions "dtarr(i) and "dtarr(j)" are the same in the front-end.
> So we keep both mappings, but we move the "[implicit]" mapping of the
> full-array reference to the end of the clause list in gimplify.cc (by
> adjusting the topological sorting algorithm):
>
>    GOMP_MAP_STRUCT         dtvar  (len: 2)
>    GOMP_MAP_TO_PSET        dtvar(i)%arrptr
>    GOMP_MAP_TO_PSET        dtvar(j)%arrptr
>    [...]
>    GOMP_MAP_TOFROM         *dtvar(j)%arrptr%data(3)  (size: 8-3+1)
>    GOMP_MAP_ATTACH_DETACH  dtvar(j)%arrptr%data
>    GOMP_MAP_TO [implicit]  *dtvar(i)%arrptr%data(1)  (size: whole array)
>    GOMP_MAP_ATTACH_DETACH  dtvar(i)%arrptr%data
>
> Always moving "[implicit]" full-array mappings after array-section
> mappings (without that bit set) means that we'll avoid copying the whole
> array unnecessarily -- even in cases where we can't prove that the arrays
> are the same.
>
> The patch also fixes some bugs with "enter data" and "exit data"
> directives with this new mapping arrangement.  Also now if you have
> mappings like this:
>
>    #pragma omp target enter data map(to: dv, dv%arr(1:20))
>
> The whole of the derived-type variable "dv" is mapped, so the
> GOMP_MAP_TO_PSET for the array-section mapping can be dropped:
>
>    GOMP_MAP_TO            dv
>
>    GOMP_MAP_TO            *dv%arr%data
>    GOMP_MAP_TO_PSET       dv%arr <-- deleted (array section mapping)
>    GOMP_MAP_ATTACH_DETACH dv%arr%data
>
> To accommodate for recent changes to mapping nodes made by
> Tobias, this version of the patch avoids using GOMP_MAP_TO_PSET
> for "exit data" directives, in favour of using the "correct"
> GOMP_MAP_RELEASE/GOMP_MAP_DELETE kinds during early expansion.  A new
> flag is introduced so the middle-end knows when the latter two kinds
> are being used specifically for an array descriptor.
>
> This version of the patch is based on the version posted for the og13
> branch:
>
> https://gcc.gnu.org/pipermail/gcc-patches/2023-June/622222.html
[...]

> +      if (OMP_CLAUSE_MAP_KIND (node) == GOMP_MAP_DELETE
> +       || OMP_CLAUSE_MAP_KIND (node) == GOMP_MAP_RELEASE
> +       || op == EXEC_OMP_TARGET_EXIT_DATA)
>       {
> [...]
> +       gomp_map_kind map_kind
> +         = (op == EXEC_OMP_TARGET_EXIT_DATA) ? GOMP_MAP_RELEASE
> +                                             : OMP_CLAUSE_MAP_KIND (node);
> +       OMP_CLAUSE_SET_MAP_KIND (node2, map_kind);
> +       OMP_CLAUSE_RELEASE_DESCRIPTOR (node2) = 1;

For '!$omp target exit data map(delete: array)' this looks wrong as it
replaces 'delete' by 'release' for the descriptor - while for
  '!$omp target (data) map(delete: array)'
it remains 'delete'.

Thus, I wonder whether that shouldn't be instead
   OMP_CLAUSE_MAP_KIND (node) == GOMP_MAP_DELETE
   ? GOMP_MAP_DELETE : GOMP_MAP_RELEASE;

* * *

We later have the following; just reading the patch, I wonder whether GOMP_TO_PSET is correct
for a generic 'target exit data' here or not.  It seems at a glance as
if an "|| op == 'target exit data'" is missing here:

> -                       if (openacc)
> -                         OMP_CLAUSE_SET_MAP_KIND (desc_node,
> +                       if (openacc
> +                           || (map_kind != GOMP_MAP_RELEASE
> +                               && map_kind != GOMP_MAP_DELETE))
> +                         OMP_CLAUSE_SET_MAP_KIND (node2,
>                                                    GOMP_MAP_TO_PSET);
>                         else
> -                         OMP_CLAUSE_SET_MAP_KIND (desc_node, map_kind);
> -                       OMP_CLAUSE_DECL (desc_node) = inner;
> -                       OMP_CLAUSE_SIZE (desc_node) = TYPE_SIZE_UNIT (type);
> -                       if (openacc)
> -                         node2 = desc_node;
> -                       else
> +                         OMP_CLAUSE_SET_MAP_KIND (node2, map_kind);

(Here, without setting OMP_CLAUSE_RELEASE_DESCRIPTOR.)

And for completeness, we later have:

> +omp_map_clause_descriptor_p (tree c)
> +{
> +  if (OMP_CLAUSE_CODE (c) != OMP_CLAUSE_MAP)
> +    return false;
> +
> +  if (OMP_CLAUSE_MAP_KIND (c) == GOMP_MAP_TO_PSET)
> +    return true;
> +
> +  if ((OMP_CLAUSE_MAP_KIND (c) == GOMP_MAP_RELEASE
> +       || OMP_CLAUSE_MAP_KIND (c) == GOMP_MAP_DELETE)
> +      && OMP_CLAUSE_RELEASE_DESCRIPTOR (c))
> +    return true;


* * *

> +                               /* Save array descriptor for use
> +                                  in gfc_omp_deep_mapping{,_p,_cnt}; force
> +                                  evaluate to ensure that it is
> +                                  not gimplified + is a decl.  */
This is part of my Fortran allocatable-components deep-mapping patch that is currently
only on OG9 (?) to OG13, badly needs to be upstreamed but required that Jakub had a
look at it - well, I still would like that he has a look at the omp-low.cc parts and
it needs to be re-diffed.

Hence, while I wouldn't mind to keep it to avoid more diffing work, I think it will
be cleaner not to keep it here.

* * *

Thanks,

Tobias

> 2023-08-18  Julian Brown  <julian@codesourcery.com>
>
> gcc/fortran/
>          * dependency.cc (gfc_omp_expr_prefix_same): New function.
>          * dependency.h (gfc_omp_expr_prefix_same): Add prototype.
>          * gfortran.h (gfc_omp_namelist): Add "duplicate_of" field to "u2"
>          union.
>          * trans-openmp.cc (dependency.h): Include.
>          (gfc_trans_omp_array_section): Adjust mapping node arrangement for
>          array descriptors.  Use GOMP_MAP_TO_PSET or
>          GOMP_MAP_RELEASE/GOMP_MAP_DELETE with the OMP_CLAUSE_RELEASE_DESCRIPTOR
>          flag set.
>          (gfc_symbol_rooted_namelist): New function.
>          (gfc_trans_omp_clauses): Check subcomponent and subarray/element
>          accesses elsewhere in the clause list for pointers to derived types or
>          array descriptors, and adjust or drop mapping nodes appropriately.
>          Adjust for changes to mapping node arrangement.
>          (gfc_trans_oacc_executable_directive): Pass code op through.
>
> gcc/
>          * gimplify.cc (omp_map_clause_descriptor_p): New function.
>          (build_omp_struct_comp_nodes, omp_get_attachment, omp_group_base): Use
>          above function.
>          (omp_tsort_mapping_groups): Process nodes that have
>          OMP_CLAUSE_MAP_RUNTIME_IMPLICIT_P set after those that don't.  Add
>          enter_exit_data parameter.
>          (omp_resolve_clause_dependencies): Remove GOMP_MAP_TO_PSET mappings if
>          we're mapping the whole containing derived-type variable.
>          (omp_accumulate_sibling_list): Adjust GOMP_MAP_TO_PSET handling.
>          Remove GOMP_MAP_ALWAYS_POINTER handling.
>          (gimplify_scan_omp_clauses): Pass enter_exit argument to
>          omp_tsort_mapping_groups.  Don't adjust/remove GOMP_MAP_TO_PSET
>          mappings for derived-type components here.
>          * tree.h (OMP_CLAUSE_RELEASE_DESCRIPTOR): New macro.
>
> gcc/testsuite/
>          * gfortran.dg/gomp/map-9.f90: Adjust scan output.
>          * gfortran.dg/gomp/map-subarray-2.f90: New test.
>          * gfortran.dg/gomp/map-subarray.f90: New test.
>
> libgomp/
>          * testsuite/libgomp.fortran/map-subarray.f90: New test.
>          * testsuite/libgomp.fortran/map-subarray-2.f90: New test.
>          * testsuite/libgomp.fortran/map-subarray-3.f90: New test.
>          * testsuite/libgomp.fortran/map-subarray-4.f90: New test.
>          * testsuite/libgomp.fortran/map-subarray-6.f90: New test.
>          * testsuite/libgomp.fortran/map-subarray-7.f90: New test.
>          * testsuite/libgomp.fortran/map-subarray-8.f90: New test.
>          * testsuite/libgomp.fortran/map-subcomponents.f90: New test.
>          * testsuite/libgomp.fortran/struct-elem-map-1.f90: Adjust for
>          descriptor-mapping changes.  Remove XFAIL.
-----------------
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] 19+ messages in thread

* Re: [PATCH v7 3/5] OpenMP: Pointers and member mappings
  2023-12-06 11:36   ` Tobias Burnus
@ 2023-12-07 17:24     ` Julian Brown
  2023-12-11 11:44       ` Tobias Burnus
  0 siblings, 1 reply; 19+ messages in thread
From: Julian Brown @ 2023-12-07 17:24 UTC (permalink / raw)
  To: Tobias Burnus; +Cc: gcc-patches, fortran, jakub

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

Hi Tobias!

On Wed, 6 Dec 2023 12:36:34 +0100
Tobias Burnus <tobias@codesourcery.com> wrote:

> Hi Julian,
> 
> LGTM, except for:
> 
> * The 'target exit data' handling - comments below - looks a bit
> fishy/inconsistent.
> 
> I intent to have a closer look with more code context, but maybe you
> should have a look at it as well.
> 
> BTW: Fortran deep-mapping is not yet on mainline. Are you aware of
> changes or in particular testcases on OG13 related to your patch
> series that should be included when upstreaming that auto-mapping of
> allocatable components patch?

I thought I'd adjusted some tests to use "pointer" instead of
"allocatable" at some point for mainline submission, but now I can't
find where I'm thinking of.  So possibly.  (I'll keep an eye out...)

> > +      if (OMP_CLAUSE_MAP_KIND (node) == GOMP_MAP_DELETE
> > +       || OMP_CLAUSE_MAP_KIND (node) == GOMP_MAP_RELEASE
> > +       || op == EXEC_OMP_TARGET_EXIT_DATA)
> >       {
> > [...]
> > +       gomp_map_kind map_kind
> > +         = (op == EXEC_OMP_TARGET_EXIT_DATA) ? GOMP_MAP_RELEASE
> > +                                             : OMP_CLAUSE_MAP_KIND
> > (node);
> > +       OMP_CLAUSE_SET_MAP_KIND (node2, map_kind);
> > +       OMP_CLAUSE_RELEASE_DESCRIPTOR (node2) = 1;  
> 
> For '!$omp target exit data map(delete: array)' this looks wrong as it
> replaces 'delete' by 'release' for the descriptor - while for
>   '!$omp target (data) map(delete: array)'
> it remains 'delete'.
> 
> Thus, I wonder whether that shouldn't be instead
>    OMP_CLAUSE_MAP_KIND (node) == GOMP_MAP_DELETE
>    ? GOMP_MAP_DELETE : GOMP_MAP_RELEASE;

I've fixed that as you suggest.  Actually I've made OpenACC use the new
node layout as well, since (a) it works and (b) it was weirdly
inconsistent before.  That is, exit data directives will no longer use
e.g.:

  GOMP_MAP_FROM
  GOMP_MAP_TO_PSET
  GOMP_MAP_ATTACH_DETACH

but instead,

  GOMP_MAP_FROM
  GOMP_MAP_RELEASE (with OMP_CLAUSE_RELEASE_DESCRIPTOR set)
  GOMP_MAP_ATTACH_DETACH

actually the current state is that GOMP_MAP_TO_PSET will be used for
the descriptor on an "exit data" directive if you refer to the whole
array, but GOMP_MAP_RELEASE (etc.) will be used if you refer to an array
section (without the flag newly added in this patch, of course). I
don't think there's any reason to maintain that inconsistency.

> We later have the following; just reading the patch, I wonder whether
> GOMP_TO_PSET is correct for a generic 'target exit data' here or not.
>  It seems at a glance as if an "|| op == 'target exit data'" is
> missing here:
> 
> > -                       if (openacc)
> > -                         OMP_CLAUSE_SET_MAP_KIND (desc_node,
> > +                       if (openacc
> > +                           || (map_kind != GOMP_MAP_RELEASE
> > +                               && map_kind != GOMP_MAP_DELETE))
> > +                         OMP_CLAUSE_SET_MAP_KIND (node2,
> >                                                    GOMP_MAP_TO_PSET);
> >                         else
> > -                         OMP_CLAUSE_SET_MAP_KIND (desc_node,
> > map_kind);
> > -                       OMP_CLAUSE_DECL (desc_node) = inner;
> > -                       OMP_CLAUSE_SIZE (desc_node) =
> > TYPE_SIZE_UNIT (type);
> > -                       if (openacc)
> > -                         node2 = desc_node;
> > -                       else
> > +                         OMP_CLAUSE_SET_MAP_KIND (node2,
> > map_kind);  
> 
> (Here, without setting OMP_CLAUSE_RELEASE_DESCRIPTOR.)
> 
> And for completeness, we later have:
> 
> > +omp_map_clause_descriptor_p (tree c)
> > +{
> > +  if (OMP_CLAUSE_CODE (c) != OMP_CLAUSE_MAP)
> > +    return false;
> > +
> > +  if (OMP_CLAUSE_MAP_KIND (c) == GOMP_MAP_TO_PSET)
> > +    return true;
> > +
> > +  if ((OMP_CLAUSE_MAP_KIND (c) == GOMP_MAP_RELEASE
> > +       || OMP_CLAUSE_MAP_KIND (c) == GOMP_MAP_DELETE)
> > +      && OMP_CLAUSE_RELEASE_DESCRIPTOR (c))
> > +    return true;  

In that section, the "exit data" directive case is handled a few lines
higher up the function, with a condition that (now) reads:

  else if (op == EXEC_OMP_TARGET_EXIT_DATA
           || op == EXEC_OACC_EXIT_DATA)
    map_kind = GOMP_MAP_RELEASE;

so I think we're OK -- unless I missed something?  (Removing the
OpenACC special case and inverting a conditional simplifies the logic
here a bit.)

> > +                               /* Save array descriptor for use
> > +                                  in
> > gfc_omp_deep_mapping{,_p,_cnt}; force
> > +                                  evaluate to ensure that it is
> > +                                  not gimplified + is a decl.  */  
> This is part of my Fortran allocatable-components deep-mapping patch
> that is currently only on OG9 (?) to OG13, badly needs to be
> upstreamed but required that Jakub had a look at it - well, I still
> would like that he has a look at the omp-low.cc parts and it needs to
> be re-diffed.
> 
> Hence, while I wouldn't mind to keep it to avoid more diffing work, I
> think it will be cleaner not to keep it here.

Agreed -- that looks like a bad bit of merge resolution on my part, in
fact.  I've removed it from this patch.

I've re-tested this version.  Does it look better now?

Thanks,

Julian


[-- Warning: decoded text below may be mangled, UTF-8 assumed --]
[-- Attachment #2: pointers-and-member-mappings-8.diff --]
[-- Type: text/x-patch, Size: 73944 bytes --]

commit 86563a13497a324d4e72790551706bb5c2c22f76
Author: Julian Brown <julian@codesourcery.com>
Date:   Mon Oct 17 16:44:31 2022 +0000

    OpenMP: Pointers and member mappings
    
    This patch changes the mapping node arrangement used for array components
    of derived types in order to accommodate for changes made in the previous
    patch, particularly the use of "GOMP_MAP_ATTACH_DETACH" for pointer-typed
    derived-type members instead of "GOMP_MAP_ALWAYS_POINTER".
    
    We change the mapping nodes used for a derived-type mapping like this:
    
      type T
      integer, pointer, dimension(:) :: arrptr
      end type T
    
      type(T) :: tvar
      [...]
      !$omp target map(tofrom: tvar%arrptr)
    
    So that the nodes used look like this:
    
      1) map(to: tvar%arrptr)   -->
      GOMP_MAP_TO [implicit]  *tvar%arrptr%data  (the array data)
      GOMP_MAP_TO_PSET        tvar%arrptr        (the descriptor)
      GOMP_MAP_ATTACH_DETACH  tvar%arrptr%data
    
      2) map(tofrom: tvar%arrptr(3:8)   -->
      GOMP_MAP_TOFROM         *tvar%arrptr%data(3)  (size 8-3+1, etc.)
      GOMP_MAP_TO_PSET        tvar%arrptr
      GOMP_MAP_ATTACH_DETACH  tvar%arrptr%data      (bias 3, etc.)
    
    In this case, we can determine in the front-end that the
    whole-array/pointer mapping (1) is only needed to map the pointer
    -- so we drop it entirely.  (Note also that we set -- early -- the
    OMP_CLAUSE_MAP_RUNTIME_IMPLICIT_P flag for whole-array-via-pointer
    mappings. See below.)
    
    In the middle end, we process mappings using the struct sibling-list
    handling machinery by moving the "GOMP_MAP_TO_PSET" node from the middle
    of the group of three mapping nodes to the proper sorted position after
    the GOMP_MAP_STRUCT mapping:
    
      GOMP_MAP_STRUCT   tvar     (len: 1)
      GOMP_MAP_TO_PSET  tvar%arr (size: 64, etc.)  <--. moved here
      [...]                                           |
      GOMP_MAP_TOFROM         *tvar%arrptr%data(3) ___|
      GOMP_MAP_ATTACH_DETACH  tvar%arrptr%data
    
    In another case, if we have an array of derived-type values "dtarr",
    and mappings like:
    
      i = 1
      j = 1
      map(to: dtarr(i)%arrptr) map(tofrom: dtarr(j)%arrptr(3:8))
    
    We still map the same way, but this time we cannot prove that the base
    expressions "dtarr(i) and "dtarr(j)" are the same in the front-end.
    So we keep both mappings, but we move the "[implicit]" mapping of the
    full-array reference to the end of the clause list in gimplify.cc (by
    adjusting the topological sorting algorithm):
    
      GOMP_MAP_STRUCT         dtvar  (len: 2)
      GOMP_MAP_TO_PSET        dtvar(i)%arrptr
      GOMP_MAP_TO_PSET        dtvar(j)%arrptr
      [...]
      GOMP_MAP_TOFROM         *dtvar(j)%arrptr%data(3)  (size: 8-3+1)
      GOMP_MAP_ATTACH_DETACH  dtvar(j)%arrptr%data
      GOMP_MAP_TO [implicit]  *dtvar(i)%arrptr%data(1)  (size: whole array)
      GOMP_MAP_ATTACH_DETACH  dtvar(i)%arrptr%data
    
    Always moving "[implicit]" full-array mappings after array-section
    mappings (without that bit set) means that we'll avoid copying the whole
    array unnecessarily -- even in cases where we can't prove that the arrays
    are the same.
    
    The patch also fixes some bugs with "enter data" and "exit data"
    directives with this new mapping arrangement.  Also now if you have
    mappings like this:
    
      #pragma omp target enter data map(to: dv, dv%arr(1:20))
    
    The whole of the derived-type variable "dv" is mapped, so the
    GOMP_MAP_TO_PSET for the array-section mapping can be dropped:
    
      GOMP_MAP_TO            dv
    
      GOMP_MAP_TO            *dv%arr%data
      GOMP_MAP_TO_PSET       dv%arr <-- deleted (array section mapping)
      GOMP_MAP_ATTACH_DETACH dv%arr%data
    
    To accommodate for recent changes to mapping nodes made by
    Tobias, this version of the patch avoids using GOMP_MAP_TO_PSET
    for "exit data" directives, in favour of using the "correct"
    GOMP_MAP_RELEASE/GOMP_MAP_DELETE kinds during early expansion.  A new
    flag is introduced so the middle-end knows when the latter two kinds
    are being used specifically for an array descriptor.
    
    This version of the patch fixes "omp target exit data" handling
    for GOMP_MAP_DELETE, and adds pretty-printing dump output
    for the OMP_CLAUSE_RELEASE_DESCRIPTOR flag (for a little extra
    clarity).
    
    Also I noticed the handling of descriptors on *OpenACC*
    exit-data directives was inconsistent, so I've made those use
    GOMP_MAP_RELEASE/GOMP_MAP_DELETE with the new flag in the same way as
    OpenMP too.  In the end it doesn't actually matter to the runtime,
    which handles GOMP_MAP_RELEASE/GOMP_MAP_DELETE/GOMP_MAP_TO_PSET for
    array descriptors on OpenACC "exit data" directives the same, anyway,
    and doing it this way in the FE avoids needless divergence.
    
    I've added a couple of new tests (gomp/target-enter-exit-data.f90 and
    goacc/enter-exit-data-2.f90).
    
    2023-12-07  Julian Brown  <julian@codesourcery.com>
    
    gcc/fortran/
            * dependency.cc (gfc_omp_expr_prefix_same): New function.
            * dependency.h (gfc_omp_expr_prefix_same): Add prototype.
            * gfortran.h (gfc_omp_namelist): Add "duplicate_of" field to "u2"
            union.
            * trans-openmp.cc (dependency.h): Include.
            (gfc_trans_omp_array_section): Adjust mapping node arrangement for
            array descriptors.  Use GOMP_MAP_TO_PSET or
            GOMP_MAP_RELEASE/GOMP_MAP_DELETE with the OMP_CLAUSE_RELEASE_DESCRIPTOR
            flag set.
            (gfc_symbol_rooted_namelist): New function.
            (gfc_trans_omp_clauses): Check subcomponent and subarray/element
            accesses elsewhere in the clause list for pointers to derived types or
            array descriptors, and adjust or drop mapping nodes appropriately.
            Adjust for changes to mapping node arrangement.
            (gfc_trans_oacc_executable_directive): Pass code op through.
    
    gcc/
            * gimplify.cc (omp_map_clause_descriptor_p): New function.
            (build_omp_struct_comp_nodes, omp_get_attachment, omp_group_base): Use
            above function.
            (omp_tsort_mapping_groups): Process nodes that have
            OMP_CLAUSE_MAP_RUNTIME_IMPLICIT_P set after those that don't.  Add
            enter_exit_data parameter.
            (omp_resolve_clause_dependencies): Remove GOMP_MAP_TO_PSET mappings if
            we're mapping the whole containing derived-type variable.
            (omp_accumulate_sibling_list): Adjust GOMP_MAP_TO_PSET handling.
            Remove GOMP_MAP_ALWAYS_POINTER handling.
            (gimplify_scan_omp_clauses): Pass enter_exit argument to
            omp_tsort_mapping_groups.  Don't adjust/remove GOMP_MAP_TO_PSET
            mappings for derived-type components here.
            * tree.h (OMP_CLAUSE_RELEASE_DESCRIPTOR): New macro.
            * tree-pretty-print.cc (dump_omp_clause): Show
            OMP_CLAUSE_RELEASE_DESCRIPTOR in dump output (with
            GOMP_MAP_TO_PSET-like syntax).
    
    gcc/testsuite/
            * gfortran.dg/goacc/enter-exit-data-2.f90: New test.
            * gfortran.dg/goacc/finalize-1.f: Adjust scan output.
            * gfortran.dg/gomp/map-9.f90: Adjust scan output.
            * gfortran.dg/gomp/map-subarray-2.f90: New test.
            * gfortran.dg/gomp/map-subarray.f90: New test.
            * gfortran.dg/gomp/target-enter-exit-data.f90: New test.
    
    libgomp/
            * testsuite/libgomp.fortran/map-subarray.f90: New test.
            * testsuite/libgomp.fortran/map-subarray-2.f90: New test.
            * testsuite/libgomp.fortran/map-subarray-3.f90: New test.
            * testsuite/libgomp.fortran/map-subarray-4.f90: New test.
            * testsuite/libgomp.fortran/map-subarray-6.f90: New test.
            * testsuite/libgomp.fortran/map-subarray-7.f90: New test.
            * testsuite/libgomp.fortran/map-subarray-8.f90: New test.
            * testsuite/libgomp.fortran/map-subcomponents.f90: New test.
            * testsuite/libgomp.fortran/struct-elem-map-1.f90: Adjust for
            descriptor-mapping changes.  Remove XFAIL.

diff --git a/gcc/fortran/dependency.cc b/gcc/fortran/dependency.cc
index 632b3985723..94647032ab5 100644
--- a/gcc/fortran/dependency.cc
+++ b/gcc/fortran/dependency.cc
@@ -2337,3 +2337,131 @@ gfc_dep_resolver (gfc_ref *lref, gfc_ref *rref, gfc_reverse *reverse,
 
   return fin_dep == GFC_DEP_OVERLAP;
 }
+
+/* Check if two refs are equal, for the purposes of checking if one might be
+   the base of the other for OpenMP (target directives).  Derived from
+   gfc_dep_resolver.  This function is stricter, e.g. indices arr(i) and
+   arr(j) compare as non-equal.  */
+
+bool
+gfc_omp_expr_prefix_same (gfc_expr *lexpr, gfc_expr *rexpr)
+{
+  gfc_ref *lref, *rref;
+
+  if (lexpr->symtree && rexpr->symtree)
+    {
+      /* See are_identical_variables above.  */
+      if (lexpr->symtree->n.sym->attr.dummy
+	  && rexpr->symtree->n.sym->attr.dummy)
+	{
+	  /* Dummy arguments: Only check for equal names.  */
+	  if (lexpr->symtree->n.sym->name != rexpr->symtree->n.sym->name)
+	    return false;
+	}
+      else
+	{
+	  if (lexpr->symtree->n.sym != rexpr->symtree->n.sym)
+	    return false;
+	}
+    }
+  else if (lexpr->base_expr && rexpr->base_expr)
+    {
+      if (gfc_dep_compare_expr (lexpr->base_expr, rexpr->base_expr) != 0)
+	return false;
+    }
+  else
+    return false;
+
+  lref = lexpr->ref;
+  rref = rexpr->ref;
+
+  while (lref && rref)
+    {
+      gfc_dependency fin_dep = GFC_DEP_EQUAL;
+
+      if (lref && lref->type == REF_COMPONENT && lref->u.c.component
+	  && strcmp (lref->u.c.component->name, "_data") == 0)
+	lref = lref->next;
+
+      if (rref && rref->type == REF_COMPONENT && rref->u.c.component
+	  && strcmp (rref->u.c.component->name, "_data") == 0)
+	rref = rref->next;
+
+      gcc_assert (lref->type == rref->type);
+
+      switch (lref->type)
+	{
+	case REF_COMPONENT:
+	  if (lref->u.c.component != rref->u.c.component)
+	    return false;
+	  break;
+
+	case REF_ARRAY:
+	  if (ref_same_as_full_array (lref, rref))
+	    break;
+	  if (ref_same_as_full_array (rref, lref))
+	    break;
+
+	  if (lref->u.ar.dimen != rref->u.ar.dimen)
+	    {
+	      if (lref->u.ar.type == AR_FULL
+		  && gfc_full_array_ref_p (rref, NULL))
+		break;
+	      if (rref->u.ar.type == AR_FULL
+		  && gfc_full_array_ref_p (lref, NULL))
+		break;
+	      return false;
+	    }
+
+	  for (int n = 0; n < lref->u.ar.dimen; n++)
+	    {
+	      if (lref->u.ar.dimen_type[n] == DIMEN_VECTOR
+		  && rref->u.ar.dimen_type[n] == DIMEN_VECTOR
+		  && gfc_dep_compare_expr (lref->u.ar.start[n],
+					   rref->u.ar.start[n]) == 0)
+		continue;
+	      if (lref->u.ar.dimen_type[n] == DIMEN_RANGE
+		  && rref->u.ar.dimen_type[n] == DIMEN_RANGE)
+		fin_dep = check_section_vs_section (&lref->u.ar, &rref->u.ar,
+						    n);
+	      else if (lref->u.ar.dimen_type[n] == DIMEN_ELEMENT
+		       && rref->u.ar.dimen_type[n] == DIMEN_RANGE)
+		fin_dep = gfc_check_element_vs_section (lref, rref, n);
+	      else if (rref->u.ar.dimen_type[n] == DIMEN_ELEMENT
+		       && lref->u.ar.dimen_type[n] == DIMEN_RANGE)
+		fin_dep = gfc_check_element_vs_section (rref, lref, n);
+	      else if (lref->u.ar.dimen_type[n] == DIMEN_ELEMENT
+		       && rref->u.ar.dimen_type[n] == DIMEN_ELEMENT)
+		{
+		  gfc_array_ref l_ar = lref->u.ar;
+		  gfc_array_ref r_ar = rref->u.ar;
+		  gfc_expr *l_start = l_ar.start[n];
+		  gfc_expr *r_start = r_ar.start[n];
+		  int i = gfc_dep_compare_expr (r_start, l_start);
+		  if (i == 0)
+		    fin_dep = GFC_DEP_EQUAL;
+		  else
+		    return false;
+		}
+	      else
+		return false;
+	      if (n + 1 < lref->u.ar.dimen
+		  && fin_dep != GFC_DEP_EQUAL)
+		return false;
+	    }
+
+	  if (fin_dep != GFC_DEP_EQUAL
+	      && fin_dep != GFC_DEP_OVERLAP)
+	    return false;
+
+	  break;
+
+	default:
+	  gcc_unreachable ();
+	}
+      lref = lref->next;
+      rref = rref->next;
+    }
+
+  return true;
+}
diff --git a/gcc/fortran/dependency.h b/gcc/fortran/dependency.h
index fbbede8b22c..a2458f79034 100644
--- a/gcc/fortran/dependency.h
+++ b/gcc/fortran/dependency.h
@@ -40,5 +40,6 @@ int gfc_expr_is_one (gfc_expr *, int);
 bool gfc_dep_resolver (gfc_ref *, gfc_ref *, gfc_reverse *,
 		      bool identical = false);
 bool gfc_are_equivalenced_arrays (gfc_expr *, gfc_expr *);
+bool gfc_omp_expr_prefix_same (gfc_expr *, gfc_expr *);
 
 gfc_expr * gfc_discard_nops (gfc_expr *);
diff --git a/gcc/fortran/gfortran.h b/gcc/fortran/gfortran.h
index aa3f6cb70b4..6be0f853253 100644
--- a/gcc/fortran/gfortran.h
+++ b/gcc/fortran/gfortran.h
@@ -1380,6 +1380,7 @@ typedef struct gfc_omp_namelist
       gfc_namespace *ns;
       gfc_expr *allocator;
       struct gfc_symbol *traits_sym;
+      struct gfc_omp_namelist *duplicate_of;
     } u2;
   struct gfc_omp_namelist *next;
   locus where;
diff --git a/gcc/fortran/trans-openmp.cc b/gcc/fortran/trans-openmp.cc
index 33216f5c7f2..b9ab8860bd1 100644
--- a/gcc/fortran/trans-openmp.cc
+++ b/gcc/fortran/trans-openmp.cc
@@ -40,6 +40,7 @@ along with GCC; see the file COPYING3.  If not see
 #include "omp-general.h"
 #include "omp-low.h"
 #include "memmodel.h"  /* For MEMMODEL_ enums.  */
+#include "dependency.h"
 
 #undef GCC_DIAG_STYLE
 #define GCC_DIAG_STYLE __gcc_tdiag__
@@ -2491,36 +2492,24 @@ gfc_trans_omp_array_section (stmtblock_t *block, gfc_exec_op op,
     }
   if (GFC_DESCRIPTOR_TYPE_P (TREE_TYPE (decl)))
     {
-      tree desc_node;
       tree type = TREE_TYPE (decl);
       ptr2 = gfc_conv_descriptor_data_get (decl);
-      desc_node = build_omp_clause (input_location, OMP_CLAUSE_MAP);
-      OMP_CLAUSE_DECL (desc_node) = decl;
-      OMP_CLAUSE_SIZE (desc_node) = TYPE_SIZE_UNIT (type);
-      if (OMP_CLAUSE_MAP_KIND (node) == GOMP_MAP_DELETE)
+      node2 = build_omp_clause (input_location, OMP_CLAUSE_MAP);
+      OMP_CLAUSE_DECL (node2) = decl;
+      OMP_CLAUSE_SIZE (node2) = TYPE_SIZE_UNIT (type);
+      if (OMP_CLAUSE_MAP_KIND (node) == GOMP_MAP_DELETE
+	  || OMP_CLAUSE_MAP_KIND (node) == GOMP_MAP_RELEASE
+	  || op == EXEC_OMP_TARGET_EXIT_DATA
+	  || op == EXEC_OACC_EXIT_DATA)
 	{
-	  OMP_CLAUSE_SET_MAP_KIND (desc_node, GOMP_MAP_DELETE);
-	  node2 = desc_node;
-	}
-      else if (OMP_CLAUSE_MAP_KIND (node) == GOMP_MAP_RELEASE
-	       || op == EXEC_OMP_TARGET_EXIT_DATA)
-	{
-	  OMP_CLAUSE_SET_MAP_KIND (desc_node, GOMP_MAP_RELEASE);
-	  node2 = desc_node;
-	}
-      else if (ptr_kind == GOMP_MAP_ALWAYS_POINTER)
-	{
-	  OMP_CLAUSE_SET_MAP_KIND (desc_node, GOMP_MAP_TO);
-	  node2 = node;
-	  node = desc_node;  /* Needs to come first.  */
+	  gomp_map_kind map_kind
+	    = OMP_CLAUSE_MAP_KIND (node) == GOMP_MAP_DELETE ? GOMP_MAP_DELETE
+							    : GOMP_MAP_RELEASE;
+	  OMP_CLAUSE_SET_MAP_KIND (node2, map_kind);
+	  OMP_CLAUSE_RELEASE_DESCRIPTOR (node2) = 1;
 	}
       else
-	{
-	  OMP_CLAUSE_SET_MAP_KIND (desc_node, GOMP_MAP_TO_PSET);
-	  node2 = desc_node;
-	}
-      if (op == EXEC_OMP_TARGET_EXIT_DATA)
-	return;
+	OMP_CLAUSE_SET_MAP_KIND (node2, GOMP_MAP_TO_PSET);
       node3 = build_omp_clause (input_location, OMP_CLAUSE_MAP);
       OMP_CLAUSE_SET_MAP_KIND (node3, ptr_kind);
       OMP_CLAUSE_DECL (node3) = gfc_conv_descriptor_data_get (decl);
@@ -2624,6 +2613,73 @@ handle_iterator (gfc_namespace *ns, stmtblock_t *iter_block, tree block)
   return list;
 }
 
+/* To alleviate quadratic behaviour in checking each entry of a
+   gfc_omp_namelist against every other entry, we build a hashtable indexed by
+   gfc_symbol pointer, which we can use in the usual case that a map
+   expression has a symbol as its root term.  Return a namelist based on the
+   root symbol used by N, building a new table in SYM_ROOTED_NL using the
+   gfc_omp_namelist N2 (all clauses) if we haven't done so already.  */
+
+static gfc_omp_namelist *
+get_symbol_rooted_namelist (hash_map<gfc_symbol *,
+				     gfc_omp_namelist *> *&sym_rooted_nl,
+			    gfc_omp_namelist *n,
+			    gfc_omp_namelist *n2, bool *sym_based)
+{
+  /* Early-out if we have a NULL clause list (e.g. for OpenACC).  */
+  if (!n2)
+    return NULL;
+
+  gfc_symbol *use_sym = NULL;
+
+  /* We're only interested in cases where we have an expression, e.g. a
+     component access.  */
+  if (n->expr && n->expr->expr_type == EXPR_VARIABLE && n->expr->symtree)
+    use_sym = n->expr->symtree->n.sym;
+
+  *sym_based = false;
+
+  if (!use_sym)
+    return n2;
+
+  if (!sym_rooted_nl)
+    {
+      sym_rooted_nl = new hash_map<gfc_symbol *, gfc_omp_namelist *> ();
+
+      for (; n2 != NULL; n2 = n2->next)
+	{
+	  if (!n2->expr
+	      || n2->expr->expr_type != EXPR_VARIABLE
+	      || !n2->expr->symtree)
+	    continue;
+
+	  gfc_omp_namelist *nl_copy = gfc_get_omp_namelist ();
+	  memcpy (nl_copy, n2, sizeof *nl_copy);
+	  nl_copy->u2.duplicate_of = n2;
+	  nl_copy->next = NULL;
+
+	  gfc_symbol *idx_sym = n2->expr->symtree->n.sym;
+
+	  bool existed;
+	  gfc_omp_namelist *&entry
+	    = sym_rooted_nl->get_or_insert (idx_sym, &existed);
+	  if (existed)
+	    nl_copy->next = entry;
+	  entry = nl_copy;
+	}
+    }
+
+  gfc_omp_namelist **n2_sym = sym_rooted_nl->get (use_sym);
+
+  if (n2_sym)
+    {
+      *sym_based = true;
+      return *n2_sym;
+    }
+
+  return NULL;
+}
+
 static tree
 gfc_trans_omp_clauses (stmtblock_t *block, gfc_omp_clauses *clauses,
 		       locus where, bool declare_simd = false,
@@ -2641,6 +2697,8 @@ gfc_trans_omp_clauses (stmtblock_t *block, gfc_omp_clauses *clauses,
   if (clauses == NULL)
     return NULL_TREE;
 
+  hash_map<gfc_symbol *, gfc_omp_namelist *> *sym_rooted_nl = NULL;
+
   for (list = 0; list < OMP_LIST_NUM; list++)
     {
       gfc_omp_namelist *n = clauses->lists[list];
@@ -3664,6 +3722,54 @@ gfc_trans_omp_clauses (stmtblock_t *block, gfc_omp_clauses *clauses,
 			      goto finalize_map_clause;
 			    }
 
+			  gfc_omp_namelist *n2
+			    = openacc ? NULL : clauses->lists[OMP_LIST_MAP];
+
+			  bool sym_based;
+			  n2 = get_symbol_rooted_namelist (sym_rooted_nl, n,
+							   n2, &sym_based);
+
+			  /* If the last reference is a pointer to a derived
+			     type ("foo%dt_ptr"), check if any subcomponents
+			     of the same derived type member are being mapped
+			     elsewhere in the clause list ("foo%dt_ptr%x",
+			     etc.).  If we have such subcomponent mappings,
+			     we only create an ALLOC node for the pointer
+			     itself, and inhibit mapping the whole derived
+			     type.  */
+
+			  for (; n2 != NULL; n2 = n2->next)
+			    {
+			      if ((!sym_based && n == n2)
+				  || (sym_based && n == n2->u2.duplicate_of)
+				  || !n2->expr)
+				continue;
+
+			      if (!gfc_omp_expr_prefix_same (n->expr,
+							     n2->expr))
+				continue;
+
+			      gfc_ref *ref1 = n->expr->ref;
+			      gfc_ref *ref2 = n2->expr->ref;
+
+			      while (ref1->next && ref2->next)
+				{
+				  ref1 = ref1->next;
+				  ref2 = ref2->next;
+				}
+
+			      if (ref2->next)
+				{
+				  inner = build_fold_addr_expr (inner);
+				  OMP_CLAUSE_SET_MAP_KIND (node,
+							   GOMP_MAP_ALLOC);
+				  OMP_CLAUSE_DECL (node) = inner;
+				  OMP_CLAUSE_SIZE (node)
+				    = TYPE_SIZE_UNIT (TREE_TYPE (inner));
+				  goto finalize_map_clause;
+				}
+			    }
+
 			  tree data, size;
 
 			  if (lastref->u.c.component->ts.type == BT_CLASS)
@@ -3719,7 +3825,6 @@ gfc_trans_omp_clauses (stmtblock_t *block, gfc_omp_clauses *clauses,
 		      if (GFC_DESCRIPTOR_TYPE_P (TREE_TYPE (inner)))
 			{
 			  gomp_map_kind map_kind;
-			  tree desc_node;
 			  tree type = TREE_TYPE (inner);
 			  tree ptr = gfc_conv_descriptor_data_get (inner);
 			  ptr = build_fold_indirect_ref (ptr);
@@ -3738,7 +3843,8 @@ gfc_trans_omp_clauses (stmtblock_t *block, gfc_omp_clauses *clauses,
 			  else if (n->u.map_op == OMP_MAP_RELEASE
 				   || n->u.map_op == OMP_MAP_DELETE)
 			    ;
-			  else if (op == EXEC_OMP_TARGET_EXIT_DATA)
+			  else if (op == EXEC_OMP_TARGET_EXIT_DATA
+				   || op == EXEC_OACC_EXIT_DATA)
 			    map_kind = GOMP_MAP_RELEASE;
 			  else
 			    map_kind = GOMP_MAP_ALLOC;
@@ -3764,24 +3870,78 @@ gfc_trans_omp_clauses (stmtblock_t *block, gfc_omp_clauses *clauses,
 			  OMP_CLAUSE_SIZE (node)
 			    = fold_build2 (MULT_EXPR, gfc_array_index_type,
 					   OMP_CLAUSE_SIZE (node), elemsz);
-			  desc_node = build_omp_clause (input_location,
-							OMP_CLAUSE_MAP);
-			  if (openacc)
-			    OMP_CLAUSE_SET_MAP_KIND (desc_node,
-						     GOMP_MAP_TO_PSET);
-			  else
-			    OMP_CLAUSE_SET_MAP_KIND (desc_node, map_kind);
-			  OMP_CLAUSE_DECL (desc_node) = inner;
-			  OMP_CLAUSE_SIZE (desc_node) = TYPE_SIZE_UNIT (type);
-			  if (openacc)
-			    node2 = desc_node;
-			  else
+			  node2 = build_omp_clause (input_location,
+						    OMP_CLAUSE_MAP);
+			  if (map_kind == GOMP_MAP_RELEASE
+			      || map_kind == GOMP_MAP_DELETE)
 			    {
-			      node2 = node;
-			      node = desc_node;  /* Put first.  */
+			      OMP_CLAUSE_SET_MAP_KIND (node2, map_kind);
+			      OMP_CLAUSE_RELEASE_DESCRIPTOR (node2) = 1;
+			    }
+			  else
+			    OMP_CLAUSE_SET_MAP_KIND (node2,
+						     GOMP_MAP_TO_PSET);
+			  OMP_CLAUSE_DECL (node2) = inner;
+			  OMP_CLAUSE_SIZE (node2) = TYPE_SIZE_UNIT (type);
+			  if (!openacc)
+			    {
+			      gfc_omp_namelist *n2
+				= clauses->lists[OMP_LIST_MAP];
+
+			      /* If we don't have a mapping of a smaller part
+				 of the array -- or we can't prove that we do
+				 statically -- set this flag.  If there is a
+				 mapping of a smaller part of the array after
+				 all, this will turn into a no-op at
+				 runtime.  */
+			      OMP_CLAUSE_MAP_RUNTIME_IMPLICIT_P (node) = 1;
+
+			      bool sym_based;
+			      n2 = get_symbol_rooted_namelist (sym_rooted_nl,
+							       n, n2,
+							       &sym_based);
+
+			      bool drop_mapping = false;
+
+			      for (; n2 != NULL; n2 = n2->next)
+				{
+				  if ((!sym_based && n == n2)
+				      || (sym_based && n == n2->u2.duplicate_of)
+				      || !n2->expr)
+				    continue;
+
+				  if (!gfc_omp_expr_prefix_same (n->expr,
+								 n2->expr))
+				    continue;
+
+				  gfc_ref *ref1 = n->expr->ref;
+				  gfc_ref *ref2 = n2->expr->ref;
+
+				  /* We know ref1 and ref2 overlap.  We're
+				     interested in whether ref2 describes a
+				     smaller part of the array than ref1, which
+				     we already know refers to the full
+				     array.  */
+
+				  while (ref1->next && ref2->next)
+				    {
+				      ref1 = ref1->next;
+				      ref2 = ref2->next;
+				    }
+
+				  if (ref2->next
+				      || (ref2->type == REF_ARRAY
+					  && (ref2->u.ar.type == AR_ELEMENT
+					      || (ref2->u.ar.type
+						  == AR_SECTION))))
+				    {
+				      drop_mapping = true;
+				      break;
+				    }
+				}
+			      if (drop_mapping)
+				continue;
 			    }
-			  if (op == EXEC_OMP_TARGET_EXIT_DATA)
-			    goto finalize_map_clause;
 			  node3 = build_omp_clause (input_location,
 						    OMP_CLAUSE_MAP);
 			  OMP_CLAUSE_SET_MAP_KIND (node3,
@@ -3945,6 +4105,23 @@ gfc_trans_omp_clauses (stmtblock_t *block, gfc_omp_clauses *clauses,
 	}
     }
 
+  /* Free hashmap if we built it.  */
+  if (sym_rooted_nl)
+    {
+      typedef hash_map<gfc_symbol *, gfc_omp_namelist *>::iterator hti;
+      for (hti it = sym_rooted_nl->begin (); it != sym_rooted_nl->end (); ++it)
+	{
+	  gfc_omp_namelist *&nl = (*it).second;
+	  while (nl)
+	    {
+	      gfc_omp_namelist *next = nl->next;
+	      free (nl);
+	      nl = next;
+	    }
+	}
+      delete sym_rooted_nl;
+    }
+
   if (clauses->if_expr)
     {
       tree if_var;
@@ -4787,7 +4964,7 @@ gfc_trans_oacc_executable_directive (gfc_code *code)
 
   gfc_start_block (&block);
   oacc_clauses = gfc_trans_omp_clauses (&block, code->ext.omp_clauses,
-					code->loc, false, true);
+					code->loc, false, true, code->op);
   stmt = build1_loc (input_location, construct_code, void_type_node, 
 		     oacc_clauses);
   gfc_add_expr_to_block (&block, stmt);
diff --git a/gcc/gimplify.cc b/gcc/gimplify.cc
index 1e34bde19be..57e3252608a 100644
--- a/gcc/gimplify.cc
+++ b/gcc/gimplify.cc
@@ -9146,6 +9146,25 @@ gimplify_omp_depend (tree *list_p, gimple_seq *pre_p)
   return 1;
 }
 
+/* True if mapping node C maps, or unmaps, a (Fortran) array descriptor.  */
+
+static bool
+omp_map_clause_descriptor_p (tree c)
+{
+  if (OMP_CLAUSE_CODE (c) != OMP_CLAUSE_MAP)
+    return false;
+
+  if (OMP_CLAUSE_MAP_KIND (c) == GOMP_MAP_TO_PSET)
+    return true;
+
+  if ((OMP_CLAUSE_MAP_KIND (c) == GOMP_MAP_RELEASE
+       || OMP_CLAUSE_MAP_KIND (c) == GOMP_MAP_DELETE)
+      && OMP_CLAUSE_RELEASE_DESCRIPTOR (c))
+    return true;
+
+  return false;
+}
+
 /* For a set of mappings describing an array section pointed to by a struct
    (or derived type, etc.) component, create an "alloc" or "release" node to
    insert into a list following a GOMP_MAP_STRUCT node.  For some types of
@@ -9181,9 +9200,7 @@ build_omp_struct_comp_nodes (enum tree_code code, tree grp_start, tree grp_end,
   if (OMP_CLAUSE_CHAIN (grp_start) != grp_end)
     grp_mid = OMP_CLAUSE_CHAIN (grp_start);
 
-  if (grp_mid
-      && OMP_CLAUSE_CODE (grp_mid) == OMP_CLAUSE_MAP
-      && OMP_CLAUSE_MAP_KIND (grp_mid) == GOMP_MAP_TO_PSET)
+  if (grp_mid && omp_map_clause_descriptor_p (grp_mid))
     OMP_CLAUSE_SIZE (c2) = OMP_CLAUSE_SIZE (grp_mid);
   else
     OMP_CLAUSE_SIZE (c2) = TYPE_SIZE_UNIT (ptr_type_node);
@@ -9369,7 +9386,7 @@ omp_get_attachment (omp_mapping_group *grp)
 	return NULL_TREE;
 
       node = OMP_CLAUSE_CHAIN (node);
-      if (node && OMP_CLAUSE_MAP_KIND (node) == GOMP_MAP_TO_PSET)
+      if (node && omp_map_clause_descriptor_p (node))
 	{
 	  gcc_assert (node != grp->grp_end);
 	  node = OMP_CLAUSE_CHAIN (node);
@@ -9464,7 +9481,7 @@ omp_group_last (tree *start_p)
 		     == GOMP_MAP_ATTACH_ZERO_LENGTH_ARRAY_SECTION)
 		 || OMP_CLAUSE_MAP_KIND (nc) == GOMP_MAP_DETACH
 		 || OMP_CLAUSE_MAP_KIND (nc) == GOMP_MAP_ALWAYS_POINTER
-		 || OMP_CLAUSE_MAP_KIND (nc) == GOMP_MAP_TO_PSET))
+		 || omp_map_clause_descriptor_p (nc)))
 	{
 	  tree nc2 = OMP_CLAUSE_CHAIN (nc);
 	  if (OMP_CLAUSE_MAP_KIND (nc) == GOMP_MAP_DETACH)
@@ -9631,33 +9648,32 @@ omp_group_base (omp_mapping_group *grp, unsigned int *chained,
 	return node;
 
       node = OMP_CLAUSE_CHAIN (node);
-      if (node && OMP_CLAUSE_MAP_KIND (node) == GOMP_MAP_TO_PSET)
+      if (!node)
+	internal_error ("unexpected mapping node");
+      if (omp_map_clause_descriptor_p (node))
 	{
 	  if (node == grp->grp_end)
 	    return *grp->grp_start;
 	  node = OMP_CLAUSE_CHAIN (node);
 	}
-      if (node)
-	switch (OMP_CLAUSE_MAP_KIND (node))
-	  {
-	  case GOMP_MAP_POINTER:
-	  case GOMP_MAP_FIRSTPRIVATE_POINTER:
-	  case GOMP_MAP_FIRSTPRIVATE_REFERENCE:
-	  case GOMP_MAP_POINTER_TO_ZERO_LENGTH_ARRAY_SECTION:
-	    *firstprivate = OMP_CLAUSE_DECL (node);
-	    return *grp->grp_start;
+      switch (OMP_CLAUSE_MAP_KIND (node))
+	{
+	case GOMP_MAP_POINTER:
+	case GOMP_MAP_FIRSTPRIVATE_POINTER:
+	case GOMP_MAP_FIRSTPRIVATE_REFERENCE:
+	case GOMP_MAP_POINTER_TO_ZERO_LENGTH_ARRAY_SECTION:
+	  *firstprivate = OMP_CLAUSE_DECL (node);
+	  return *grp->grp_start;
 
-	  case GOMP_MAP_ALWAYS_POINTER:
-	  case GOMP_MAP_ATTACH_DETACH:
-	  case GOMP_MAP_ATTACH_ZERO_LENGTH_ARRAY_SECTION:
-	  case GOMP_MAP_DETACH:
-	    return *grp->grp_start;
+	case GOMP_MAP_ALWAYS_POINTER:
+	case GOMP_MAP_ATTACH_DETACH:
+	case GOMP_MAP_ATTACH_ZERO_LENGTH_ARRAY_SECTION:
+	case GOMP_MAP_DETACH:
+	  return *grp->grp_start;
 
-	  default:
-	    internal_error ("unexpected mapping node");
-	  }
-      else
-	internal_error ("unexpected mapping node");
+	default:
+	  internal_error ("unexpected mapping node");
+	}
       return error_mark_node;
 
     case GOMP_MAP_TO_PSET:
@@ -10005,18 +10021,45 @@ omp_tsort_mapping_groups_1 (omp_mapping_group ***outlist,
 static omp_mapping_group *
 omp_tsort_mapping_groups (vec<omp_mapping_group> *groups,
 			  hash_map<tree_operand_hash_no_se, omp_mapping_group *>
-			    *grpmap)
+			    *grpmap,
+			  bool enter_exit_data)
 {
   omp_mapping_group *grp, *outlist = NULL, **cursor;
   unsigned int i;
+  bool saw_runtime_implicit = false;
 
   cursor = &outlist;
 
   FOR_EACH_VEC_ELT (*groups, i, grp)
     {
       if (grp->mark != PERMANENT)
-	if (!omp_tsort_mapping_groups_1 (&cursor, groups, grpmap, grp))
-	  return NULL;
+	{
+	  if (OMP_CLAUSE_MAP_RUNTIME_IMPLICIT_P (*grp->grp_start))
+	    {
+	      saw_runtime_implicit = true;
+	      continue;
+	    }
+	  if (!omp_tsort_mapping_groups_1 (&cursor, groups, grpmap, grp))
+	    return NULL;
+	}
+    }
+
+  if (!saw_runtime_implicit)
+    return outlist;
+
+  FOR_EACH_VEC_ELT (*groups, i, grp)
+    {
+      if (grp->mark != PERMANENT
+	  && OMP_CLAUSE_MAP_RUNTIME_IMPLICIT_P (*grp->grp_start))
+	{
+	  /* Clear the flag for enter/exit data because it is currently
+	     meaningless for those operations in libgomp.  */
+	  if (enter_exit_data)
+	    OMP_CLAUSE_MAP_RUNTIME_IMPLICIT_P (*grp->grp_start) = 0;
+
+	  if (!omp_tsort_mapping_groups_1 (&cursor, groups, grpmap, grp))
+	    return NULL;
+	}
     }
 
   return outlist;
@@ -10419,6 +10462,11 @@ omp_check_mapping_compatibility (location_t loc,
    mapping.  However, if we have a reference to pointer, make other appropriate
    adjustments to the mapping nodes instead.
 
+   If we have an ATTACH_DETACH node with a Fortran pointer-set (array
+   descriptor) mapping for a derived-type component, and we're also mapping the
+   whole of the derived-type variable on another clause, the pointer-set
+   mapping is removed.
+
    If we have a component access but we're also mapping the whole of the
    containing struct, drop the former access.
 
@@ -10598,6 +10646,17 @@ omp_resolve_clause_dependencies (enum tree_code code,
 		   GOMP_MAP_ATTACH_ZLAS for it.  */
 		if (!base_mapped_to && referenced_ptr_node)
 		  OMP_CLAUSE_SET_MAP_KIND (referenced_ptr_node, zlas_kind);
+
+		omp_mapping_group *struct_group;
+		tree desc;
+		if ((desc = OMP_CLAUSE_CHAIN (*grp->grp_start))
+		    && omp_map_clause_descriptor_p (desc)
+		    && omp_mapped_by_containing_struct (grpmap, decl,
+							&struct_group))
+		  /* If we have a pointer set but we're mapping (or unmapping)
+		     the whole of the containing struct, we can remove the
+		     pointer set mapping.  */
+		  OMP_CLAUSE_CHAIN (*grp->grp_start) = OMP_CLAUSE_CHAIN (desc);
 	      }
 	    else if (TREE_CODE (TREE_TYPE (base_ptr)) == REFERENCE_TYPE
 		     && (TREE_CODE (TREE_TYPE (TREE_TYPE (base_ptr)))
@@ -10996,11 +11055,17 @@ omp_accumulate_sibling_list (enum omp_region_type region_type,
      for the purposes of gathering sibling lists, etc.  */
   /* gcc_assert (base == addr_tokens[base_token]->expr);  */
 
-  bool ptr = (OMP_CLAUSE_MAP_KIND (grp_end) == GOMP_MAP_ALWAYS_POINTER);
   bool attach_detach = ((OMP_CLAUSE_MAP_KIND (grp_end)
 			 == GOMP_MAP_ATTACH_DETACH)
 			|| (OMP_CLAUSE_MAP_KIND (grp_end)
 			    == GOMP_MAP_ATTACH_ZERO_LENGTH_ARRAY_SECTION));
+  bool has_descriptor = false;
+  if (OMP_CLAUSE_CHAIN (*grp_start_p) != grp_end)
+    {
+      tree grp_mid = OMP_CLAUSE_CHAIN (*grp_start_p);
+      if (grp_mid && omp_map_clause_descriptor_p (grp_mid))
+	has_descriptor = true;
+    }
 
   if (!struct_map_to_clause || struct_map_to_clause->get (base) == NULL)
     {
@@ -11023,7 +11088,18 @@ omp_accumulate_sibling_list (enum omp_region_type region_type,
 	 GOMP_MAP_STRUCT into the middle of the old one.  */
       tree *insert_node_pos = reprocessing_struct ? *added_tail : grp_start_p;
 
-      if (ptr || attach_detach)
+      if (has_descriptor)
+	{
+	  tree desc = OMP_CLAUSE_CHAIN (*grp_start_p);
+	  if (code == OMP_TARGET_EXIT_DATA || code == OACC_EXIT_DATA)
+	    OMP_CLAUSE_SET_MAP_KIND (desc, GOMP_MAP_RELEASE);
+	  tree sc = *insert_node_pos;
+	  OMP_CLAUSE_CHAIN (l) = desc;
+	  OMP_CLAUSE_CHAIN (*grp_start_p) = OMP_CLAUSE_CHAIN (desc);
+	  OMP_CLAUSE_CHAIN (desc) = sc;
+	  *insert_node_pos = l;
+	}
+      else if (attach_detach)
 	{
 	  tree extra_node;
 	  tree alloc_node
@@ -11254,7 +11330,7 @@ omp_accumulate_sibling_list (enum omp_region_type region_type,
 	  || OMP_CLAUSE_MAP_KIND (*sc) == GOMP_MAP_ATTACH_DETACH)
 	sc = &OMP_CLAUSE_CHAIN (*sc);
       for (i = 0; i < elems; i++, sc = &OMP_CLAUSE_CHAIN (*sc))
-	if ((ptr || attach_detach) && sc == grp_start_p)
+	if (attach_detach && sc == grp_start_p)
 	  break;
 	else if (TREE_CODE (OMP_CLAUSE_DECL (*sc)) != COMPONENT_REF
 		 && TREE_CODE (OMP_CLAUSE_DECL (*sc)) != INDIRECT_REF
@@ -11310,7 +11386,7 @@ omp_accumulate_sibling_list (enum omp_region_type region_type,
 		|| (known_eq (coffset, offset)
 		    && maybe_lt (cbitpos, bitpos)))
 	      {
-		if (ptr || attach_detach)
+		if (attach_detach)
 		  scp = sc;
 		else
 		  break;
@@ -11326,7 +11402,9 @@ omp_accumulate_sibling_list (enum omp_region_type region_type,
 	     the list manipulation below.  We only need to handle the (pointer
 	     or reference) attach/detach case.  */
 	  tree extra_node, alloc_node;
-	  if (attach_detach)
+	  if (has_descriptor)
+	    gcc_unreachable ();
+	  else if (attach_detach)
 	    alloc_node = build_omp_struct_comp_nodes (code, *grp_start_p,
 						      grp_end, &extra_node);
 	  else
@@ -11359,7 +11437,17 @@ omp_accumulate_sibling_list (enum omp_region_type region_type,
 	  return NULL;
 	}
 
-      if (ptr || attach_detach)
+      if (has_descriptor)
+	{
+	  tree desc = OMP_CLAUSE_CHAIN (*grp_start_p);
+	  if (code == OMP_TARGET_EXIT_DATA
+	      || code == OACC_EXIT_DATA)
+	    OMP_CLAUSE_SET_MAP_KIND (desc, GOMP_MAP_RELEASE);
+	  omp_siblist_move_node_after (desc,
+				       &OMP_CLAUSE_CHAIN (*grp_start_p),
+				       scp ? scp : sc);
+	}
+      else if (attach_detach)
 	{
 	  tree cl = NULL_TREE, extra_node;
 	  tree alloc_node = build_omp_struct_comp_nodes (code, *grp_start_p,
@@ -11504,8 +11592,7 @@ omp_build_struct_sibling_lists (enum tree_code code,
 	     as a struct (the GOMP_MAP_POINTER following will have the form
 	     "var.data", but such mappings are handled specially).  */
 	  tree grpmid = OMP_CLAUSE_CHAIN (*grp_start_p);
-	  if (OMP_CLAUSE_CODE (grpmid) == OMP_CLAUSE_MAP
-	      && OMP_CLAUSE_MAP_KIND (grpmid) == GOMP_MAP_TO_PSET
+	  if (omp_map_clause_descriptor_p (grpmid)
 	      && DECL_P (OMP_CLAUSE_DECL (grpmid)))
 	    continue;
 	}
@@ -11781,6 +11868,8 @@ gimplify_scan_omp_clauses (tree *list_p, gimple_seq *pre_p,
 					  list_p);
 
 	  omp_mapping_group *outlist = NULL;
+	  bool enter_exit = (code == OMP_TARGET_ENTER_DATA
+			     || code == OMP_TARGET_EXIT_DATA);
 
 	  /* Topological sorting may fail if we have duplicate nodes, which
 	     we should have detected and shown an error for already.  Skip
@@ -11795,7 +11884,7 @@ gimplify_scan_omp_clauses (tree *list_p, gimple_seq *pre_p,
 	  groups = omp_gather_mapping_groups (list_p);
 	  grpmap = omp_index_mapping_groups (groups);
 
-	  outlist = omp_tsort_mapping_groups (groups, grpmap);
+	  outlist = omp_tsort_mapping_groups (groups, grpmap, enter_exit);
 	  outlist = omp_segregate_mapping_groups (outlist);
 	  list_p = omp_reorder_mapping_groups (groups, outlist, list_p);
 
diff --git a/gcc/testsuite/gfortran.dg/goacc/enter-exit-data-2.f90 b/gcc/testsuite/gfortran.dg/goacc/enter-exit-data-2.f90
new file mode 100644
index 00000000000..6a16c8a89ea
--- /dev/null
+++ b/gcc/testsuite/gfortran.dg/goacc/enter-exit-data-2.f90
@@ -0,0 +1,38 @@
+! { dg-additional-options "-fdump-tree-original" }
+
+type t
+integer, pointer :: arr(:)
+end type t
+
+type(t) :: var
+
+allocate (var%arr(1:100))
+
+!$acc enter data copyin(var%arr(10:20))
+! { dg-final { scan-tree-dump-times {(?n)#pragma acc enter data map\(to:\*\(integer\(kind=[0-9]+\)\[0:\] \*\) parm\.[0-9]+\.data \[len: D.[0-9]+ \* [0-9]+\]\) map\(to:var\.arr \[pointer set, len: [0-9]+\]\) map\(attach_detach:var\.arr\.data \[bias: \(integer\(kind=[0-9]+\)\) parm\.[0-9]+\.data - \(integer\(kind=[0-9]+\)\) var\.arr\.data\]\);$} 1 "original" } }
+
+!$acc exit data delete(var%arr(10:20))
+! { dg-final { scan-tree-dump-times {(?n)#pragma acc exit data map\(release:\*\(integer\(kind=[0-9]+\)\[0:\] \*\) parm\.[0-9]+\.data \[len: D\.[0-9]+ \* [0-9]+\]\) map\(release:var\.arr \[pointer set, len: [0-9]+\]\) map\(attach_detach:var\.arr\.data \[bias: \(integer\(kind=[0-9]+\)\) parm\.[0-9]+\.data - \(integer\(kind=[0-9]+\)\) var\.arr\.data\]\);$} 1 "original" } }
+
+
+!$acc enter data create(var%arr(20:30))
+! { dg-final { scan-tree-dump-times {(?n)#pragma acc enter data map\(alloc:\*\(integer\(kind=[0-9]+\)\[0:\] \*\) parm\.[0-9]+\.data \[len: D\.[0-9]+ \* [0-9]+\]\) map\(to:var\.arr \[pointer set, len: [0-9]+\]\) map\(attach_detach:var\.arr\.data \[bias: \(integer\(kind=[0-9]+\)\) parm\.[0-9]+\.data - \(integer\(kind=[0-9]+\)\) var\.arr\.data\]\);$} 1 "original" } }
+
+!$acc exit data finalize delete(var%arr(20:30))
+! { dg-final { scan-tree-dump-times {(?n)#pragma acc exit data map\(release:\*\(integer\(kind=[0-9]+\)\[0:\] \*\) parm\.[0-9]+\.data \[len: D\.[0-9]+ \* [0-9]+\]\) map\(release:var\.arr \[pointer set, len: [0-9]+\]\) map\(attach_detach:var\.arr\.data \[bias: \(integer\(kind=[0-9]+\)\) parm\.[0-9]+\.data - \(integer\(kind=[0-9]+\)\) var\.arr\.data\]\) finalize;$} 1 "original" } }
+
+
+!$acc enter data copyin(var%arr)
+! { dg-final { scan-tree-dump-times {(?n)#pragma acc enter data map\(to:\*\(integer\(kind=[0-9]+\)\[0:\] \*\) var\.arr\.data \[len: D\.[0-9]+ \* [0-9]+\]\) map\(to:var\.arr \[pointer set, len: [0-9]+\]\) map\(attach_detach:var\.arr\.data \[bias: 0\]\);$} 1 "original" } }
+
+!$acc exit data delete(var%arr)
+! { dg-final { scan-tree-dump-times {(?n)#pragma acc exit data map\(release:\*\(integer\(kind=[0-9]+\)\[0:\] \*\) var\.arr\.data \[len: D\.[0-9]+ \* [0-9]+\]\) map\(release:var\.arr \[pointer set, len: [0-9]+\]\) map\(attach_detach:var\.arr\.data \[bias: 0\]\);$} 1 "original" } }
+
+
+!$acc enter data create(var%arr)
+! { dg-final { scan-tree-dump-times {(?n)#pragma acc enter data map\(alloc:\*\(integer\(kind=[0-9]+\)\[0:\] \*\) var\.arr\.data \[len: D\.[0-9]+ \* [0-9]+\]\) map\(to:var\.arr \[pointer set, len: [0-9]+\]\) map\(attach_detach:var\.arr\.data \[bias: 0\]\);$} 1 "original" } }
+
+!$acc exit data finalize delete(var%arr)
+! { dg-final { scan-tree-dump-times {(?n)#pragma acc exit data map\(release:\*\(integer\(kind=[0-9]+\)\[0:\] \*\) var\.arr\.data \[len: D\.[0-9]+ \* [0-9]+\]\) map\(release:var\.arr \[pointer set, len: [0-9]+\]\) map\(attach_detach:var\.arr\.data \[bias: 0\]\) finalize;$} 1 "original" } }
+
+end
diff --git a/gcc/testsuite/gfortran.dg/goacc/finalize-1.f b/gcc/testsuite/gfortran.dg/goacc/finalize-1.f
index 23f0ffc627e..63beb479453 100644
--- a/gcc/testsuite/gfortran.dg/goacc/finalize-1.f
+++ b/gcc/testsuite/gfortran.dg/goacc/finalize-1.f
@@ -20,8 +20,8 @@
 ! { dg-final { scan-tree-dump-times "(?n)#pragma omp target oacc_exit_data map\\(delete:del_f \\\[len: \[0-9\]+\\\]\\) finalize$" 1 "gimple" } }
 
 !$ACC EXIT DATA FINALIZE DELETE (del_f_p(2:5))
-! { dg-final { scan-tree-dump-times "(?n)#pragma acc exit data map\\(release:\\*\\(integer\\(kind=.\\)\\\[0:\\\] \\*\\) parm\\.0\\.data \\\[len: \[^\\\]\]+\\\]\\) map\\(release:del_f_p \\\[len: \[0-9\]+\\\]\\) map\\(alloc:\\(integer\\(kind=1\\)\\\[0:\\\] \\* restrict\\) del_f_p\\.data \\\[pointer assign, bias: \\(.*int.*\\) parm\\.0\\.data - \\(.*int.*\\) del_f_p\\.data\\\]\\) finalize;$" 1 "original" } }
-! { dg-final { scan-tree-dump-times "(?n)#pragma omp target oacc_exit_data map\\(delete:MEM <\[^>\]+> \\\[\\(integer\\(kind=.\\)\\\[0:\\\] \\*\\)_\[0-9\]+\\\] \\\[len: \[^\\\]\]+\\\]\\) map\\(delete:del_f_p \\\[len: \[0-9\]+\\\]\\) map\\(alloc:del_f_p\\.data \\\[pointer assign, bias: \[^\\\]\]+\\\]\\) finalize$" 1 "gimple" } }
+! { dg-final { scan-tree-dump-times "(?n)#pragma acc exit data map\\(release:\\*\\(integer\\(kind=.\\)\\\[0:\\\] \\*\\) parm\\.0\\.data \\\[len: \[^\\\]\]+\\\]\\) map\\(release:del_f_p \\\[pointer set, len: \[0-9\]+\\\]\\) map\\(alloc:\\(integer\\(kind=1\\)\\\[0:\\\] \\* restrict\\) del_f_p\\.data \\\[pointer assign, bias: \\(.*int.*\\) parm\\.0\\.data - \\(.*int.*\\) del_f_p\\.data\\\]\\) finalize;$" 1 "original" } }
+! { dg-final { scan-tree-dump-times "(?n)#pragma omp target oacc_exit_data map\\(delete:MEM <\[^>\]+> \\\[\\(integer\\(kind=.\\)\\\[0:\\\] \\*\\)_\[0-9\]+\\\] \\\[len: \[^\\\]\]+\\\]\\) map\\(delete:del_f_p \\\[pointer set, len: \[0-9\]+\\\]\\) map\\(alloc:del_f_p\\.data \\\[pointer assign, bias: \[^\\\]\]+\\\]\\) finalize$" 1 "gimple" } }
 
 !$ACC EXIT DATA COPYOUT (cpo_r)
 ! { dg-final { scan-tree-dump-times "(?n)#pragma acc exit data map\\(from:cpo_r\\);$" 1 "original" } }
@@ -32,6 +32,6 @@
 ! { dg-final { scan-tree-dump-times "(?n)#pragma omp target oacc_exit_data map\\(force_from:cpo_f \\\[len: \[0-9\]+\\\]\\) finalize$" 1 "gimple" } }
 
 !$ACC EXIT DATA COPYOUT (cpo_f_p(4:10)) FINALIZE
-! { dg-final { scan-tree-dump-times "(?n)#pragma acc exit data map\\(from:\\*\\(integer\\(kind=.\\)\\\[0:\\\] \\*\\) parm\\.1\\.data \\\[len: \[^\\\]\]+\\\]\\) map\\(to:cpo_f_p \\\[pointer set, len: \[0-9\]+\\\]\\) map\\(alloc:\\(integer\\(kind=1\\)\\\[0:\\\] \\* restrict\\) cpo_f_p\\.data \\\[pointer assign, bias: \\(.*int.*\\) parm\\.1\\.data - \\(.*int.*\\) cpo_f_p\\.data\\\]\\) finalize;$" 1 "original" } }
-! { dg-final { scan-tree-dump-times "(?n)#pragma omp target oacc_exit_data map\\(force_from:MEM <\[^>\]+> \\\[\\(integer\\(kind=.\\)\\\[0:\\\] \\*\\)_\[0-9\]+\\\] \\\[len: \[^\\\]\]+\\\]\\) map\\(to:cpo_f_p \\\[pointer set, len: \[0-9\]+\\\]\\) map\\(alloc:cpo_f_p\\.data \\\[pointer assign, bias: \[^\\\]\]+\\\]\\) finalize$" 1 "gimple" } }
+! { dg-final { scan-tree-dump-times "(?n)#pragma acc exit data map\\(from:\\*\\(integer\\(kind=.\\)\\\[0:\\\] \\*\\) parm\\.1\\.data \\\[len: \[^\\\]\]+\\\]\\) map\\(release:cpo_f_p \\\[pointer set, len: \[0-9\]+\\\]\\) map\\(alloc:\\(integer\\(kind=1\\)\\\[0:\\\] \\* restrict\\) cpo_f_p\\.data \\\[pointer assign, bias: \\(.*int.*\\) parm\\.1\\.data - \\(.*int.*\\) cpo_f_p\\.data\\\]\\) finalize;$" 1 "original" } }
+! { dg-final { scan-tree-dump-times "(?n)#pragma omp target oacc_exit_data map\\(force_from:MEM <\[^>\]+> \\\[\\(integer\\(kind=.\\)\\\[0:\\\] \\*\\)_\[0-9\]+\\\] \\\[len: \[^\\\]\]+\\\]\\) map\\(delete:cpo_f_p \\\[pointer set, len: \[0-9\]+\\\]\\) map\\(alloc:cpo_f_p\\.data \\\[pointer assign, bias: \[^\\\]\]+\\\]\\) finalize$" 1 "gimple" } }
       END SUBROUTINE f
diff --git a/gcc/testsuite/gfortran.dg/gomp/map-9.f90 b/gcc/testsuite/gfortran.dg/gomp/map-9.f90
index f930a49d9ff..8c8d4f7c57c 100644
--- a/gcc/testsuite/gfortran.dg/gomp/map-9.f90
+++ b/gcc/testsuite/gfortran.dg/gomp/map-9.f90
@@ -2,7 +2,7 @@
 
 ! PR fortran/108545
 
-! { dg-final { scan-tree-dump "#pragma omp target enter data map\\(struct:x \\\[len: 1\\\]\\) map\\(always,to:x\.a \\\[len: \[0-9\]+\\\]\\) map\\(to:MEM <integer\\(kind=4\\)\\\[0:\\\]> \\\[\\(integer\\(kind=4\\)\\\[0:\\\] \\*\\)_\[0-9\]+] \\\[len: _\[0-9\]+\\\]\\) map\\(attach:x\.a\.data \\\[bias: 0\\\]\\)" "omplower" } }
+! { dg-final { scan-tree-dump "#pragma omp target enter data map\\(struct:x \\\[len: 1\\\]\\) map\\(to:x\.a \\\[pointer set, len: \[0-9\]+\\\]\\) map\\(to:MEM <integer\\(kind=4\\)\\\[0:\\\]> \\\[\\(integer\\(kind=4\\)\\\[0:\\\] \\*\\)_\[0-9\]+] \\\[len: _\[0-9\]+\\\]\\) map\\(attach:x\.a\.data \\\[bias: 0\\\]\\)" "omplower" } }
 
 program p
    type t
diff --git a/gcc/testsuite/gfortran.dg/gomp/map-subarray-2.f90 b/gcc/testsuite/gfortran.dg/gomp/map-subarray-2.f90
new file mode 100644
index 00000000000..26e113f4fdd
--- /dev/null
+++ b/gcc/testsuite/gfortran.dg/gomp/map-subarray-2.f90
@@ -0,0 +1,57 @@
+! { dg-do compile }
+! { dg-additional-options "-fdump-tree-gimple" }
+
+type T
+integer, pointer :: arr1(:)
+integer, pointer :: arr2(:)
+integer, pointer :: arr3(:)
+integer, pointer :: arr4(:)
+end type T
+
+type(T) :: tv
+integer, allocatable, target, dimension(:) :: arr
+
+allocate(arr(1:20))
+
+tv%arr1 => arr
+tv%arr2 => arr
+tv%arr3 => arr
+tv%arr4 => arr
+
+!$omp target enter data map(to: tv%arr1)
+
+! { dg-final { scan-tree-dump {(?n)#pragma omp target enter data map\(struct:tv \[len: 1\]\) map\(to:tv\.arr1 \[pointer set, len: [0-9]+\]\) map\(to:MEM <integer\(kind=4\)\[0:\]> \[\(integer\(kind=4\)\[0:\] \*\)_[0-9]+\] \[len: _[0-9]+\]\) map\(attach:tv\.arr1\.data \[bias: 0\]\)} "gimple" } }
+
+!$omp target exit data map(from: tv%arr1)
+
+! { dg-final { scan-tree-dump {(?n)#pragma omp target exit data map\(release:tv\.arr1 \[pointer set, len: [0-9]+\]\) map\(from:MEM <integer\(kind=4\)\[0:\]> \[\(integer\(kind=4\)\[0:\] \*\)_[0-9]+\] \[len: _[0-9]+\]\) map\(detach:tv\.arr1\.data \[bias: 0\]\)} "gimple" } }
+
+
+!$omp target enter data map(to: tv%arr2) map(to: tv%arr2(1:10))
+
+! { dg-final { scan-tree-dump {(?n)#pragma omp target enter data map\(struct:tv \[len: 1\]\) map\(to:tv\.arr2 \[pointer set, len: [0-9]+\]\) map\(to:MEM <integer\(kind=4\)\[0:\]> \[\(integer\(kind=4\)\[0:\] \*\)_[0-9]+\] \[len: _[0-9]+\]\) map\(attach:tv\.arr2\.data \[bias: [^\]]+\]\)} "gimple" } }
+
+!$omp target exit data map(from: tv%arr2) map(from: tv%arr2(1:10))
+
+! { dg-final { scan-tree-dump {(?n)#pragma omp target exit data map\(release:tv\.arr2 \[pointer set, len: [0-9]+\]\) map\(from:MEM <integer\(kind=4\)\[0:\]> \[\(integer\(kind=4\)\[0:\] \*\)_[0-9]+\] \[len: _[0-9]+\]\) map\(detach:tv\.arr2\.data \[bias: [^\]]+\]\)} "gimple" } }
+
+
+!$omp target enter data map(to: tv, tv%arr3(1:10))
+
+! { dg-final { scan-tree-dump {(?n)#pragma omp target enter data map\(to:tv \[len: [0-9]+\]\) map\(to:MEM <integer\(kind=4\)\[0:\]> \[\(integer\(kind=4\)\[0:\] \*\)_[0-9]+\] \[len: _[0-9]+\]\) map\(attach:tv\.arr3\.data \[bias: [^\]]+\]\)} "gimple" } }
+
+!$omp target exit data map(from: tv, tv%arr3(1:10))
+
+! { dg-final { scan-tree-dump {(?n)#pragma omp target exit data map\(from:tv \[len: [0-9]+\]\) map\(from:MEM <integer\(kind=4\)\[0:\]> \[\(integer\(kind=4\)\[0:\] \*\)[_[0-9]+\] \[len: _[0-9]+\]\) map\(detach:tv\.arr3\.data \[bias: [^\]]+\]\)} "gimple" } }
+
+
+!$omp target enter data map(to: tv%arr4(1:10))
+
+! { dg-final { scan-tree-dump {(?n)#pragma omp target enter data map\(struct:tv \[len: 1\]\) map\(to:tv\.arr4 \[pointer set, len: [0-9]+\]\) map\(to:MEM <integer\(kind=4\)\[0:\]> \[\(integer\(kind=4\)\[0:\] \*\)_[0-9]+\] \[len: _[0-9]+\]\) map\(attach:tv\.arr4\.data \[bias: [^\]]+\]\)} "gimple" } }
+
+!$omp target exit data map(from: tv%arr4(1:10))
+
+! { dg-final { scan-tree-dump {(?n)#pragma omp target exit data map\(release:tv\.arr4 \[pointer set, len: [0-9]+\]\) map\(from:MEM <integer\(kind=4\)\[0:\]> \[\(integer\(kind=4\)\[0:\] \*\)_[0-9]+\] \[len: _[0-9]+\]\) map\(detach:tv\.arr4\.data \[bias: [^\]]+\]\)} "gimple" } }
+
+end
+
diff --git a/gcc/testsuite/gfortran.dg/gomp/map-subarray.f90 b/gcc/testsuite/gfortran.dg/gomp/map-subarray.f90
new file mode 100644
index 00000000000..197888a4336
--- /dev/null
+++ b/gcc/testsuite/gfortran.dg/gomp/map-subarray.f90
@@ -0,0 +1,40 @@
+! { dg-do compile }
+! { dg-additional-options "-fdump-tree-gimple" }
+
+type T
+integer, pointer :: arr1(:)
+integer, pointer :: arr2(:)
+end type T
+
+type(T) :: tv
+integer, allocatable, target, dimension(:) :: arr
+
+allocate(arr(1:20))
+
+tv%arr1 => arr
+tv%arr2 => arr
+
+!$omp target map(tv%arr1)
+tv%arr1(1) = tv%arr1(1) + 1
+!$omp end target
+
+! { dg-final { scan-tree-dump {(?n)#pragma omp target.* map\(struct:tv \[len: 1\]\) map\(to:tv\.arr1 \[pointer set, len: [0-9]+\]\) map\(tofrom:MEM <integer\(kind=4\)\[0:\]> \[\(integer\(kind=4\)\[0:\] \*\)_[0-9]+\] \[len: _[0-9]+\]\[implicit\]\) map\(attach:tv\.arr1\.data \[bias: 0\]\)} "gimple" } }
+
+!$omp target map(tv%arr2) map(tv%arr2(1:10))
+tv%arr2(1) = tv%arr2(1) + 1
+!$omp end target
+
+!$omp target map(tv%arr2(1:10))
+tv%arr2(1) = tv%arr2(1) + 1
+!$omp end target
+
+! { dg-final { scan-tree-dump-times {(?n)#pragma omp target.* map\(struct:tv \[len: 1\]\) map\(to:tv\.arr2 \[pointer set, len: [0-9]+\]\) map\(tofrom:MEM <integer\(kind=4\)\[0:\]> \[\(integer\(kind=4\)\[0:\] \*\)_[0-9]+\] \[len: _[0-9]+\]\) map\(attach:tv\.arr2\.data \[bias: [^\]]+\]\)} 2 "gimple" } }
+
+!$omp target map(tv, tv%arr2(1:10))
+tv%arr2(1) = tv%arr2(1) + 1
+!$omp end target
+
+! { dg-final { scan-tree-dump {(?n)#pragma omp target.* map\(tofrom:tv \[len: [0-9]+\]\) map\(tofrom:MEM <integer\(kind=4\)\[0:\]> \[\(integer\(kind=4\)\[0:\] \*\)_[0-9]+\] \[len: _[0-9]+\]\) map\(attach:tv\.arr2\.data \[bias: [^\]]+\]\)} "gimple" } }
+
+end
+
diff --git a/gcc/testsuite/gfortran.dg/gomp/target-enter-exit-data.f90 b/gcc/testsuite/gfortran.dg/gomp/target-enter-exit-data.f90
new file mode 100644
index 00000000000..c14a11dacd9
--- /dev/null
+++ b/gcc/testsuite/gfortran.dg/gomp/target-enter-exit-data.f90
@@ -0,0 +1,39 @@
+! { dg-additional-options "-fdump-tree-original" }
+
+type t
+integer, pointer :: arr(:)
+end type t
+
+type(t) :: var
+
+allocate (var%arr(1:100))
+
+!$omp target enter data map(to: var%arr(10:20))
+! { dg-final { scan-tree-dump-times {(?n)#pragma omp target enter data map\(to:\*\(integer\(kind=[0-9]+\)\[0:\] \*\) parm\.[0-9]+\.data \[len: D\.[0-9]+ \* [0-9]+\]\) map\(to:var\.arr \[pointer set, len: [0-9]+\]\) map\(attach_detach:\(integer\(kind=[0-9]+\)\[0:\] \*\) var\.arr\.data \[bias: \(integer\(kind=[0-9]+\)\) parm\.[0-9]+\.data - \(integer\(kind=[0-9]+\)\) var\.arr\.data\]\)$} 1 "original" } }
+
+!$omp target exit data map(release: var%arr(10:20))
+! { dg-final { scan-tree-dump-times {(?n)#pragma omp target exit data map\(release:\*\(integer\(kind=[0-9]+\)\[0:\] \*\) parm\.[0-9]+\.data \[len: D\.[0-9]+ \* [0-9]+\]\) map\(release:var\.arr \[pointer set, len: [0-9]+\]\) map\(attach_detach:\(integer\(kind=[0-9]+\)\[0:\] \*\) var\.arr\.data \[bias: \(integer\(kind=[0-9]+\)\) parm\.[0-9]+\.data - \(integer\(kind=[0-9]+\)\) var\.arr\.data\]\)$} 1 "original" } }
+
+
+!$omp target enter data map(alloc: var%arr(20:30))
+! { dg-final { scan-tree-dump-times {(?n)#pragma omp target enter data map\(alloc:\*\(integer\(kind=[0-9]+\)\[0:\] \*\) parm\.[0-9]+\.data \[len: D\.[0-9]+ \* [0-9]+\]\) map\(to:var\.arr \[pointer set, len: [0-9]+\]\) map\(attach_detach:\(integer\(kind=[0-9]+\)\[0:\] \*\) var\.arr\.data \[bias: \(integer\(kind=[0-9]+\)\) parm\.[0-9]+\.data - \(integer\(kind=[0-9]+\)\) var\.arr\.data\]\)$} 1 "original" } }
+
+!$omp target exit data map(delete: var%arr(20:30))
+! { dg-final { scan-tree-dump-times {(?n)#pragma omp target exit data map\(delete:\*\(integer\(kind=[0-9]+\)\[0:\] \*\) parm\.[0-9]+\.data \[len: D\.[0-9]+ \* [0-9]+\]\) map\(delete:var\.arr \[pointer set, len: [0-9]+\]\) map\(attach_detach:\(integer\(kind=[0-9]+\)\[0:\] \*\) var\.arr\.data \[bias: \(integer\(kind=[0-9]+\)\) parm\.[0-9]+\.data - \(integer\(kind=[0-9]+\)\) var\.arr\.data\]\)$} 1 "original" } }
+
+
+!$omp target enter data map(to: var%arr)
+! { dg-final { scan-tree-dump-times {(?n)#pragma omp target enter data map\(to:\*\(integer\(kind=[0-9]+\)\[0:\] \*\) var\.arr\.data \[len: D\.[0-9]+ \* [0-9]+\]\[implicit\]\) map\(to:var\.arr \[pointer set, len: [0-9]+\]\) map\(attach_detach:\(integer\(kind=[0-9]+\)\[0:\] \*\) var\.arr\.data \[bias: 0\]\)$} 1 "original" } }
+
+!$omp target exit data map(release: var%arr)
+! { dg-final { scan-tree-dump-times {(?n)#pragma omp target exit data map\(release:\*\(integer\(kind=[0-9]+\)\[0:\] \*\) var\.arr\.data \[len: D\.[0-9]+ \* [0-9]+\]\[implicit\]\) map\(release:var\.arr \[pointer set, len: [0-9]+\]\) map\(attach_detach:\(integer\(kind=[0-9]+\)\[0:\] \*\) var\.arr\.data \[bias: 0\]\)$} 1 "original" } }
+
+
+!$omp target enter data map(alloc: var%arr)
+! { dg-final { scan-tree-dump-times {(?n)#pragma omp target enter data map\(alloc:\*\(integer\(kind=[0-9]+\)\[0:\] \*\) var\.arr\.data \[len: D\.[0-9]+ \* [0-9]+\]\[implicit\]\) map\(to:var\.arr \[pointer set, len: [0-9]+\]\) map\(attach_detach:\(integer\(kind=[0-9]+\)\[0:\] \*\) var\.arr\.data \[bias: 0\]\)$} 1 "original" } }
+
+!$omp target exit data map(delete: var%arr)
+! { dg-final { scan-tree-dump-times {(?n)#pragma omp target exit data map\(delete:\*\(integer\(kind=[0-9]+\)\[0:\] \*\) var\.arr\.data \[len: D\.[0-9]+ \* [0-9]+\]\[implicit\]\) map\(delete:var\.arr \[pointer set, len: [0-9]+\]\) map\(attach_detach:\(integer\(kind=[0-9]+\)\[0:\] \*\) var\.arr\.data \[bias: 0\]\)$} 1 "original" } }
+
+
+end
diff --git a/gcc/tree-pretty-print.cc b/gcc/tree-pretty-print.cc
index 1fadd752d05..6e1c386cb02 100644
--- a/gcc/tree-pretty-print.cc
+++ b/gcc/tree-pretty-print.cc
@@ -1050,6 +1050,15 @@ dump_omp_clause (pretty_printer *pp, tree clause, int spc, dump_flags_t flags)
 	    case GOMP_MAP_ATTACH_ZERO_LENGTH_ARRAY_SECTION:
 	      pp_string (pp, " [bias: ");
 	      break;
+	    case GOMP_MAP_RELEASE:
+	    case GOMP_MAP_DELETE:
+	      if (OMP_CLAUSE_CODE (clause) == OMP_CLAUSE_MAP
+		  && OMP_CLAUSE_RELEASE_DESCRIPTOR (clause))
+		{
+		  pp_string (pp, " [pointer set, len: ");
+		  break;
+		}
+	      /* Fallthrough.  */
 	    default:
 	      pp_string (pp, " [len: ");
 	      break;
diff --git a/gcc/tree.h b/gcc/tree.h
index 4fd75e6131b..2b69d84d0a6 100644
--- a/gcc/tree.h
+++ b/gcc/tree.h
@@ -1829,6 +1829,10 @@ class auto_suppress_location_wrappers
    same directive.  */
 #define OMP_CLAUSE_ATTACHMENT_MAPPING_ERASED(NODE) \
   TREE_STATIC (OMP_CLAUSE_SUBCODE_CHECK (NODE, OMP_CLAUSE_MAP))
+/* Nonzero if this is a release/delete node which refers to a (Fortran) array
+   descriptor.  */
+#define OMP_CLAUSE_RELEASE_DESCRIPTOR(NODE) \
+  TREE_NOTHROW (OMP_CLAUSE_SUBCODE_CHECK (NODE, OMP_CLAUSE_MAP))
 
 /* Flag that 'OMP_CLAUSE_DECL (NODE)' is to be made addressable during OMP
    lowering.  */
diff --git a/libgomp/testsuite/libgomp.fortran/map-subarray-2.f90 b/libgomp/testsuite/libgomp.fortran/map-subarray-2.f90
new file mode 100644
index 00000000000..02f08c52a8c
--- /dev/null
+++ b/libgomp/testsuite/libgomp.fortran/map-subarray-2.f90
@@ -0,0 +1,108 @@
+! { dg-do run }
+
+program myprog
+type u
+  integer, dimension (:), pointer :: tarr1
+  integer, dimension (:), pointer :: tarr2
+  integer, dimension (:), pointer :: tarr3
+end type u
+
+type(u) :: myu1, myu2, myu3
+
+integer, dimension (12), target :: myarray1
+integer, dimension (12), target :: myarray2
+integer, dimension (12), target :: myarray3
+integer, dimension (12), target :: myarray4
+integer, dimension (12), target :: myarray5
+integer, dimension (12), target :: myarray6
+integer, dimension (12), target :: myarray7
+integer, dimension (12), target :: myarray8
+integer, dimension (12), target :: myarray9
+
+myu1%tarr1 => myarray1
+myu1%tarr2 => myarray2
+myu1%tarr3 => myarray3
+myu2%tarr1 => myarray4
+myu2%tarr2 => myarray5
+myu2%tarr3 => myarray6
+myu3%tarr1 => myarray7
+myu3%tarr2 => myarray8
+myu3%tarr3 => myarray9
+
+myu1%tarr1 = 0
+myu1%tarr2 = 0
+myu1%tarr3 = 0
+myu2%tarr1 = 0
+myu2%tarr2 = 0
+myu2%tarr3 = 0
+myu3%tarr1 = 0
+myu3%tarr2 = 0
+myu3%tarr3 = 0
+
+!$omp target map(to:myu1%tarr1) map(tofrom:myu1%tarr1(:)) &
+!$omp&       map(to:myu1%tarr2) map(tofrom:myu1%tarr2(:)) &
+!$omp&       map(to:myu1%tarr3) map(tofrom:myu1%tarr3(:)) &
+!$omp&       map(to:myu2%tarr1) map(tofrom:myu2%tarr1(:)) &
+!$omp&       map(to:myu2%tarr2) map(tofrom:myu2%tarr2(:)) &
+!$omp&       map(to:myu2%tarr3) map(tofrom:myu2%tarr3(:)) &
+!$omp&       map(to:myu3%tarr1) map(tofrom:myu3%tarr1(:)) &
+!$omp&       map(to:myu3%tarr2) map(tofrom:myu3%tarr2(:)) &
+!$omp&       map(to:myu3%tarr3) map(tofrom:myu3%tarr3(:))
+myu1%tarr1(1) = myu1%tarr1(1) + 1
+myu2%tarr1(1) = myu2%tarr1(1) + 1
+myu3%tarr1(1) = myu3%tarr1(1) + 1
+!$omp end target
+
+!$omp target map(to:myu1%tarr1) map(tofrom:myu1%tarr1(1:2)) &
+!$omp&       map(to:myu1%tarr2) map(tofrom:myu1%tarr2(1:2)) &
+!$omp&       map(to:myu1%tarr3) map(tofrom:myu1%tarr3(1:2)) &
+!$omp&       map(to:myu2%tarr1) map(tofrom:myu2%tarr1(1:2)) &
+!$omp&       map(to:myu2%tarr2) map(tofrom:myu2%tarr2(1:2)) &
+!$omp&       map(to:myu2%tarr3) map(tofrom:myu2%tarr3(1:2)) &
+!$omp&       map(to:myu3%tarr1) map(tofrom:myu3%tarr1(1:2)) &
+!$omp&       map(to:myu3%tarr2) map(tofrom:myu3%tarr2(1:2)) &
+!$omp&       map(to:myu3%tarr3) map(tofrom:myu3%tarr3(1:2))
+myu1%tarr2(1) = myu1%tarr2(1) + 1
+myu2%tarr2(1) = myu2%tarr2(1) + 1
+myu3%tarr2(1) = myu3%tarr2(1) + 1
+!$omp end target
+
+!$omp target map(to:myu1%tarr1) map(tofrom:myu1%tarr1(1)) &
+!$omp&       map(to:myu1%tarr2) map(tofrom:myu1%tarr2(1)) &
+!$omp&       map(to:myu1%tarr3) map(tofrom:myu1%tarr3(1)) &
+!$omp&       map(to:myu2%tarr1) map(tofrom:myu2%tarr1(1)) &
+!$omp&       map(to:myu2%tarr2) map(tofrom:myu2%tarr2(1)) &
+!$omp&       map(to:myu2%tarr3) map(tofrom:myu2%tarr3(1)) &
+!$omp&       map(to:myu3%tarr1) map(tofrom:myu3%tarr1(1)) &
+!$omp&       map(to:myu3%tarr2) map(tofrom:myu3%tarr2(1)) &
+!$omp&       map(to:myu3%tarr3) map(tofrom:myu3%tarr3(1))
+myu1%tarr3(1) = myu1%tarr3(1) + 1
+myu2%tarr3(1) = myu2%tarr3(1) + 1
+myu3%tarr3(1) = myu3%tarr3(1) + 1
+!$omp end target
+
+!$omp target map(tofrom:myu1%tarr1) &
+!$omp&       map(tofrom:myu1%tarr2) &
+!$omp&       map(tofrom:myu1%tarr3) &
+!$omp&       map(tofrom:myu2%tarr1) &
+!$omp&       map(tofrom:myu2%tarr2) &
+!$omp&       map(tofrom:myu2%tarr3) &
+!$omp&       map(tofrom:myu3%tarr1) &
+!$omp&       map(tofrom:myu3%tarr2) &
+!$omp&       map(tofrom:myu3%tarr3)
+myu1%tarr2(1) = myu1%tarr2(1) + 1
+myu2%tarr2(1) = myu2%tarr2(1) + 1
+myu3%tarr2(1) = myu3%tarr2(1) + 1
+!$omp end target
+
+if (myu1%tarr1(1).ne.1) stop 1
+if (myu2%tarr1(1).ne.1) stop 2
+if (myu3%tarr1(1).ne.1) stop 3
+if (myu1%tarr2(1).ne.2) stop 4
+if (myu2%tarr2(1).ne.2) stop 5
+if (myu3%tarr2(1).ne.2) stop 6
+if (myu1%tarr3(1).ne.1) stop 7
+if (myu2%tarr3(1).ne.1) stop 8
+if (myu3%tarr3(1).ne.1) stop 9
+
+end program myprog
diff --git a/libgomp/testsuite/libgomp.fortran/map-subarray-3.f90 b/libgomp/testsuite/libgomp.fortran/map-subarray-3.f90
new file mode 100644
index 00000000000..318e77ea44f
--- /dev/null
+++ b/libgomp/testsuite/libgomp.fortran/map-subarray-3.f90
@@ -0,0 +1,62 @@
+! { dg-do run }
+
+module mymod
+type G
+integer :: x, y
+integer, pointer :: arr(:)
+integer :: z
+end type G
+end module mymod
+
+program myprog
+use mymod
+
+integer, target :: arr1(10)
+integer, target :: arr2(10)
+integer, target :: arr3(10)
+type(G), dimension(3) :: gvar
+
+integer :: i, j
+
+gvar(1)%arr => arr1
+gvar(2)%arr => arr2
+gvar(3)%arr => arr3
+
+gvar(1)%arr = 0
+gvar(2)%arr = 0
+gvar(3)%arr = 0
+
+i = 1
+j = 1
+
+! Here 'gvar(i)' and 'gvar(j)' are the same element, so this should work.
+! This generates a whole-array mapping for gvar(i)%arr, but with the
+! "runtime implicit" bit set so the smaller subarray gvar(j)%arr(1:5) takes
+! precedence.
+
+!$omp target map(gvar(i)%arr, gvar(j)%arr(1:5))
+gvar(i)%arr(1) = gvar(i)%arr(1) + 1
+gvar(j)%arr(1) = gvar(j)%arr(1) + 2
+!$omp end target
+
+!$omp target map(gvar(i)%arr(1:5), gvar(j)%arr)
+gvar(i)%arr(1) = gvar(i)%arr(1) + 3
+gvar(j)%arr(1) = gvar(j)%arr(1) + 4
+!$omp end target
+
+! For these ones, we know the array index is the same, so we can just
+! drop the whole-array mapping.
+
+!$omp target map(gvar(i)%arr, gvar(i)%arr(1:5))
+gvar(i)%arr(1) = gvar(i)%arr(1) + 1
+gvar(i)%arr(1) = gvar(j)%arr(1) + 2
+!$omp end target
+
+!$omp target map(gvar(i)%arr(1:5), gvar(i)%arr)
+gvar(i)%arr(1) = gvar(i)%arr(1) + 3
+gvar(i)%arr(1) = gvar(j)%arr(1) + 4
+!$omp end target
+
+if (gvar(1)%arr(1).ne.20) stop 1
+
+end program myprog
diff --git a/libgomp/testsuite/libgomp.fortran/map-subarray-4.f90 b/libgomp/testsuite/libgomp.fortran/map-subarray-4.f90
new file mode 100644
index 00000000000..5d15808f0da
--- /dev/null
+++ b/libgomp/testsuite/libgomp.fortran/map-subarray-4.f90
@@ -0,0 +1,35 @@
+! { dg-do run }
+
+type t
+  integer, pointer :: p(:)
+end type t
+
+type(t) :: var(2)
+
+allocate (var(1)%p, source=[1,2,3,5])
+allocate (var(2)%p, source=[2,3,5])
+
+!$omp target map(var(1)%p, var(2)%p)
+var(1)%p(1) = 5
+var(2)%p(2) = 7
+!$omp end target
+
+!$omp target map(var(1)%p(1:3), var(1)%p, var(2)%p)
+var(1)%p(1) = var(1)%p(1) + 1
+var(2)%p(2) = var(2)%p(2) + 1
+!$omp end target
+
+!$omp target map(var(1)%p, var(2)%p, var(2)%p(1:3))
+var(1)%p(1) = var(1)%p(1) + 1
+var(2)%p(2) = var(2)%p(2) + 1
+!$omp end target
+
+!$omp target map(var(1)%p, var(1)%p(1:3), var(2)%p, var(2)%p(2))
+var(1)%p(1) = var(1)%p(1) + 1
+var(2)%p(2) = var(2)%p(2) + 1
+!$omp end target
+
+if (var(1)%p(1).ne.8) stop 1
+if (var(2)%p(2).ne.10) stop 2
+
+end
diff --git a/libgomp/testsuite/libgomp.fortran/map-subarray-6.f90 b/libgomp/testsuite/libgomp.fortran/map-subarray-6.f90
new file mode 100644
index 00000000000..9f0edf70890
--- /dev/null
+++ b/libgomp/testsuite/libgomp.fortran/map-subarray-6.f90
@@ -0,0 +1,26 @@
+! { dg-do run }
+
+type t
+  integer, pointer :: p(:)
+  integer, pointer :: p2(:)
+end type t
+
+type(t) :: var
+integer, target :: tgt(5), tgt2(1000)
+var%p => tgt
+var%p2 => tgt2
+
+p = 0
+p2 = 0
+
+!$omp target map(tgt, tgt2(4:6), var)
+  var%p(1) = 5
+  var%p2(5) = 7
+!$omp end target
+
+if (var%p(1).ne.5) stop 1
+if (var%p2(5).ne.7) stop 2
+
+end
+
+! { dg-shouldfail "" { offload_device_nonshared_as } }
diff --git a/libgomp/testsuite/libgomp.fortran/map-subarray-7.f90 b/libgomp/testsuite/libgomp.fortran/map-subarray-7.f90
new file mode 100644
index 00000000000..42da7296106
--- /dev/null
+++ b/libgomp/testsuite/libgomp.fortran/map-subarray-7.f90
@@ -0,0 +1,29 @@
+type t
+integer, pointer :: p2(:)
+end type t
+
+integer, target :: A(5)
+integer, pointer :: p(:), p2(:)
+type(t) :: var
+
+allocate(p2(1:20))
+p => A
+var%p2 => p2
+
+A = 0
+p2 = 0
+
+! These arrays "share original storage", so are unsupported.  This will
+! (correctly) fail with a non-shared address space.
+
+!$omp target map(A(3:4), p2(4:8), p, var%p2)
+A(3) = A(3) + 1
+p2(4) = p2(4) + 2
+!$omp end target
+
+if (A(3).ne.1) stop 1
+if (p2(4).ne.2) stop 2
+
+end program
+
+! { dg-shouldfail "" { offload_device_nonshared_as } }
diff --git a/libgomp/testsuite/libgomp.fortran/map-subarray-8.f90 b/libgomp/testsuite/libgomp.fortran/map-subarray-8.f90
new file mode 100644
index 00000000000..a47360e10ec
--- /dev/null
+++ b/libgomp/testsuite/libgomp.fortran/map-subarray-8.f90
@@ -0,0 +1,47 @@
+! { dg-do run }
+
+type F
+integer, pointer :: mem(:)
+end type F
+
+type(F) :: fv
+integer, allocatable, target :: arr(:)
+
+allocate(arr(1:20))
+
+fv%mem => arr
+fv%mem = 0
+
+!$omp target enter data map(to: fv%mem(1:10))
+!$omp target map(alloc: fv%mem)
+fv%mem(1) = fv%mem(1) + 1
+!$omp end target
+!$omp target exit data map(from: fv%mem(1:10))
+
+if (fv%mem(1).ne.1) stop 1
+
+!$omp target enter data map(to: fv, fv%mem(1:10))
+!$omp target
+fv%mem(1) = fv%mem(1) + 1
+!$omp end target
+!$omp target exit data map(from: fv, fv%mem(1:10))
+
+if (fv%mem(1).ne.2) stop 2
+
+!$omp target enter data map(to: fv%mem, fv%mem(1:10))
+!$omp target
+fv%mem(1) = fv%mem(1) + 1
+!$omp end target
+!$omp target exit data map(from: fv%mem, fv%mem(1:10))
+
+if (fv%mem(1).ne.3) stop 3
+
+!$omp target enter data map(to: fv%mem)
+!$omp target
+fv%mem(1) = fv%mem(1) + 1
+!$omp end target
+!$omp target exit data map(from: fv%mem)
+
+if (fv%mem(1).ne.4) stop 4
+
+end
diff --git a/libgomp/testsuite/libgomp.fortran/map-subarray.f90 b/libgomp/testsuite/libgomp.fortran/map-subarray.f90
new file mode 100644
index 00000000000..85f5af3a2a6
--- /dev/null
+++ b/libgomp/testsuite/libgomp.fortran/map-subarray.f90
@@ -0,0 +1,33 @@
+! { dg-do run }
+
+program myprog
+type u
+  integer, dimension (:), pointer :: tarr
+end type u
+
+type(u) :: myu
+integer, dimension (12), target :: myarray
+
+myu%tarr => myarray
+
+myu%tarr = 0
+
+!$omp target map(to:myu%tarr) map(tofrom:myu%tarr(:))
+myu%tarr(1) = myu%tarr(1) + 1
+!$omp end target
+
+!$omp target map(to:myu%tarr) map(tofrom:myu%tarr(1:2))
+myu%tarr(1) = myu%tarr(1) + 1
+!$omp end target
+
+!$omp target map(to:myu%tarr) map(tofrom:myu%tarr(1))
+myu%tarr(1) = myu%tarr(1) + 1
+!$omp end target
+
+!$omp target map(tofrom:myu%tarr)
+myu%tarr(1) = myu%tarr(1) + 1
+!$omp end target
+
+if (myu%tarr(1).ne.4) stop 1
+
+end program myprog
diff --git a/libgomp/testsuite/libgomp.fortran/map-subcomponents.f90 b/libgomp/testsuite/libgomp.fortran/map-subcomponents.f90
new file mode 100644
index 00000000000..c7f90131cba
--- /dev/null
+++ b/libgomp/testsuite/libgomp.fortran/map-subcomponents.f90
@@ -0,0 +1,32 @@
+! { dg-do run }
+
+module mymod
+type F
+integer :: a, b, c
+integer, dimension(10) :: d
+end type F
+
+type G
+integer :: x, y
+type(F), pointer :: myf
+integer :: z
+end type G
+end module mymod
+
+program myprog
+use mymod
+
+type(F), target :: ftmp
+type(G) :: gvar
+
+gvar%myf => ftmp
+
+gvar%myf%d = 0
+
+!$omp target map(to:gvar%myf) map(tofrom: gvar%myf%b, gvar%myf%d)
+gvar%myf%d(1) = gvar%myf%d(1) + 1
+!$omp end target
+
+if (gvar%myf%d(1).ne.1) stop 1
+
+end program myprog
diff --git a/libgomp/testsuite/libgomp.fortran/struct-elem-map-1.f90 b/libgomp/testsuite/libgomp.fortran/struct-elem-map-1.f90
index 7f3d8174f97..b1d696656c0 100644
--- a/libgomp/testsuite/libgomp.fortran/struct-elem-map-1.f90
+++ b/libgomp/testsuite/libgomp.fortran/struct-elem-map-1.f90
@@ -36,6 +36,10 @@ program main
   call six ()
   call seven ()
   call eight ()
+  call nine ()
+  call ten ()
+  call eleven ()
+  call twelve ()
 
 contains
   ! Implicitly mapped – but no pointers are mapped
@@ -408,7 +412,180 @@ contains
     !$omp end target
   end subroutine eight
 
-end program main
+  ! This is "subroutine four" but with explicit base-pointer mappings
+  ! (var%f, etc.).
+  subroutine nine()
+    type(t2) :: var
 
-! Fixed by the "Fortran pointers and member mappings" patch
-! { dg-xfail-run-if TODO { offload_device_nonshared_as } }
+    print '(g0)', '==== TESTCASE "nine" ===='
+
+    var = t2(a = 1, &
+             b = 2, c = cmplx(-1.0_8, 2.0_8,kind=8), &
+             d = [(-3*i, i = 1, 10)], &
+             str1 = "abcde", &
+             str2 = ["12345", "67890", "ABCDE", "FGHIJ"], &
+             uni1 = 4_"abcde", &
+             uni2 = [4_"12345", 4_"67890", 4_"ABCDE", 4_"FGHIJ"])
+    allocate (var%f, source=[22, 33, 44, 55])
+    allocate (var%str4, source=["Let's", "Go!!!"])
+    allocate (var%uni4, source=[4_"Let's", 4_"Go!!!"])
+
+!   !$omp target map(tofrom: var%d(4:7), var%f(2:3), var%str2(2:3)) &
+!   !$omp&       map(tofrom: var%str4(2:2), var%uni2(2:3), var%uni4(2:2))
+    !$omp target map(to: var%f) map(tofrom: var%d(4:7), var%f(2:3), &
+    !$omp&       var%str2(2:3), var%uni2(2:3))
+      if (any (var%d(4:7) /= [(-3*i, i = 4, 7)])) stop 4
+      if (any (var%str2(2:3) /= ["67890", "ABCDE"])) stop 6
+
+      if (.not. associated (var%f)) stop 9
+      if (size (var%f) /= 4) stop 10
+      if (any (var%f(2:3) /= [33, 44])) stop 11
+!     if (.not. associated (var%str4)) stop 15
+!     if (len (var%str4) /= 5) stop 16
+!     if (size (var%str4) /= 2) stop 17
+!     if (var%str4(2) /= "Go!!!") stop 18
+
+      if (any (var%uni2(2:3) /= [4_"67890", 4_"ABCDE"])) stop 19
+!     if (.not. associated (var%uni4)) stop 20
+!     if (len (var%uni4) /= 5) stop 21
+!     if (size (var%uni4) /= 2) stop 22
+!     if (var%uni4(2) /= "Go!!!") stop 23
+    !$omp end target
+
+    deallocate(var%f, var%str4)
+  end subroutine nine
+
+  ! This is "subroutine five" but with explicit base-pointer mappings.
+  subroutine ten()
+    type(t2) :: var
+
+    print '(g0)', '==== TESTCASE "ten" ===='
+
+    var = t2(a = 1, &
+             b = 2, c = cmplx(-1.0_8, 2.0_8,kind=8), &
+             d = [(-3*i, i = 1, 10)], &
+             str1 = "abcde", &
+             str2 = ["12345", "67890", "ABCDE", "FGHIJ"], &
+             uni1 = 4_"abcde", &
+             uni2 = [4_"12345", 4_"67890", 4_"ABCDE", 4_"FGHIJ"])
+    allocate (var%f, source=[22, 33, 44, 55])
+    allocate (var%str4, source=["Let's", "Go!!!"])
+
+    !$omp target map(tofrom: var%d(4:7))
+      if (any (var%d(4:7) /= [(-3*i, i = 4, 7)])) stop 4
+    !$omp end target
+    !$omp target map(tofrom: var%str2(2:3))
+      if (any (var%str2(2:3) /= ["67890", "ABCDE"])) stop 6
+    !$omp end target
+
+    !$omp target map(to: var%f) map(tofrom: var%f(2:3))
+     if (.not. associated (var%f)) stop 9
+     if (size (var%f) /= 4) stop 10
+     if (any (var%f(2:3) /= [33, 44])) stop 11
+    !$omp end target
+!  !$omp target map(tofrom: var%str4(2:2))
+!     if (.not. associated (var%str4)) stop 15
+!     if (len (var%str4) /= 5) stop 16
+!     if (size (var%str4) /= 2) stop 17
+!     if (var%str4(2) /= "Go!!!") stop 18
+!   !$omp end target
+!  !$omp target map(tofrom: var%uni4(2:2))
+!     if (.not. associated (var%uni4)) stop 15
+!     if (len (var%uni4) /= 5) stop 16
+!     if (size (var%uni4) /= 2) stop 17
+!     if (var%uni4(2) /= 4_"Go!!!") stop 18
+!  !$omp end target
+
+    deallocate(var%f, var%str4)
+  end subroutine ten
+
+  ! This is "subroutine six" but with explicit base pointer mappings.
+  subroutine eleven()
+    type(t2) :: var
+
+    print '(g0)', '==== TESTCASE "eleven" ===='
+
+    var = t2(a = 1, &
+             b = 2, c = cmplx(-1.0_8, 2.0_8,kind=8), &
+             d = [(-3*i, i = 1, 10)], &
+             str1 = "abcde", &
+             str2 = ["12345", "67890", "ABCDE", "FGHIJ"], &
+             uni1 = 4_"abcde", &
+             uni2 = [4_"12345", 4_"67890", 4_"ABCDE", 4_"FGHIJ"])
+    allocate (var%f, source=[22, 33, 44, 55])
+    allocate (var%str4, source=["Let's", "Go!!!"])
+    allocate (var%uni4, source=[4_"Let's", 4_"Go!!!"])
+
+!   !$omp target map(tofrom: var%d(5), var%f(3), var%str2(3), &
+!   !$omp                    var%str4(2), var%uni2(3), var%uni4(2))
+    !$omp target map(to: var%f) map(tofrom: var%d(5), var%f(3), &
+    !$omp&                                  var%str2(3), var%uni2(3))
+      if (var%d(5) /= -3*5) stop 4
+      if (var%str2(3) /= "ABCDE") stop 6
+      if (var%uni2(3) /= 4_"ABCDE") stop 7
+
+     if (.not. associated (var%f)) stop 9
+     if (size (var%f) /= 4) stop 10
+     if (var%f(3) /= 44) stop 11
+!     if (.not. associated (var%str4)) stop 15
+!     if (len (var%str4) /= 5) stop 16
+!     if (size (var%str4) /= 2) stop 17
+!     if (var%str4(2) /= "Go!!!") stop 18
+!     if (.not. associated (var%uni4)) stop 19
+!     if (len (var%uni4) /= 5) stop 20
+!     if (size (var%uni4) /= 2) stop 21
+!     if (var%uni4(2) /= 4_"Go!!!") stop 22
+    !$omp end target
+
+    deallocate(var%f, var%str4, var%uni4)
+  end subroutine eleven
+
+  ! This is "subroutine seven" but with explicit base-pointer mappings.
+  subroutine twelve()
+    type(t2) :: var
+
+    print '(g0)', '==== TESTCASE "twelve" ===='
+
+    var = t2(a = 1, &
+             b = 2, c = cmplx(-1.0_8, 2.0_8,kind=8), &
+             d = [(-3*i, i = 1, 10)], &
+             str1 = "abcde", &
+             str2 = ["12345", "67890", "ABCDE", "FGHIJ"], &
+             uni1 = 4_"abcde", &
+             uni2 = [4_"12345", 4_"67890", 4_"ABCDE", 4_"FGHIJ"])
+    allocate (var%f, source=[22, 33, 44, 55])
+    allocate (var%str4, source=["Let's", "Go!!!"])
+    allocate (var%uni4, source=[4_"Let's", 4_"Go!!!"])
+
+    !$omp target map(tofrom: var%d(5))
+      if (var%d(5) /= (-3*5)) stop 4
+    !$omp end target
+    !$omp target map(tofrom: var%str2(2:3))
+      if (any (var%str2(2:3) /= ["67890", "ABCDE"])) stop 6
+    !$omp end target
+    !$omp target map(tofrom: var%uni2(2:3))
+      if (any (var%uni2(2:3) /= [4_"67890", 4_"ABCDE"])) stop 7
+    !$omp end target
+
+    !$omp target map(to: var%f) map(tofrom: var%f(2:3))
+     if (.not. associated (var%f)) stop 9
+     if (size (var%f) /= 4) stop 10
+     if (any (var%f(2:3) /= [33, 44])) stop 11
+    !$omp end target
+!   !$omp target map(tofrom: var%str4(2:2))
+!     if (.not. associated (var%str4)) stop 15
+!     if (len (var%str4) /= 5) stop 16
+!     if (size (var%str4) /= 2) stop 17
+!     if (var%str4(2) /= "Go!!!") stop 18
+!   !$omp end target
+!   !$omp target map(tofrom: var%uni4(2:2))
+!     if (.not. associated (var%uni4)) stop 15
+!     if (len (var%uni4) /= 5) stop 16
+!     if (size (var%uni4) /= 2) stop 17
+!     if (var%uni4(2) /= 4_"Go!!!") stop 18
+!   !$omp end target
+
+    deallocate(var%f, var%str4, var%uni4)
+  end subroutine twelve
+
+end program main

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

* Re: [PATCH v7 3/5] OpenMP: Pointers and member mappings
  2023-12-07 17:24     ` Julian Brown
@ 2023-12-11 11:44       ` Tobias Burnus
  0 siblings, 0 replies; 19+ messages in thread
From: Tobias Burnus @ 2023-12-11 11:44 UTC (permalink / raw)
  To: Julian Brown; +Cc: gcc-patches, fortran, jakub

Hi Julian,

On 07.12.23 18:24, Julian Brown wrote:
> On Wed, 6 Dec 2023 12:36:34 +0100
> Tobias Burnus<tobias@codesourcery.com>  wrote:
>
>> LGTM, except for:
>>
>> * The 'target exit data' handling - comments below - looks a bit
>> fishy/inconsistent.
...
>> Thus, I wonder whether that shouldn't be instead
>>     OMP_CLAUSE_MAP_KIND (node) == GOMP_MAP_DELETE
>>     ? GOMP_MAP_DELETE : GOMP_MAP_RELEASE;
> I've fixed that as you suggest.  Actually I've made OpenACC use the new
> node layout as well, since (a) it works and (b) it was weirdly
> inconsistent before.  That is, exit data directives will no longer use
> e.g.:
>
>    GOMP_MAP_FROM
>    GOMP_MAP_TO_PSET
>    GOMP_MAP_ATTACH_DETACH
>
> but instead,
>
>    GOMP_MAP_FROM
>    GOMP_MAP_RELEASE (with OMP_CLAUSE_RELEASE_DESCRIPTOR set)
>    GOMP_MAP_ATTACH_DETACH
>
> actually the current state is that GOMP_MAP_TO_PSET will be used for
> the descriptor on an "exit data" directive if you refer to the whole
> array, but GOMP_MAP_RELEASE (etc.) will be used if you refer to an array
> section (without the flag newly added in this patch, of course). I
> don't think there's any reason to maintain that inconsistency.
...
> I've re-tested this version. Does it look better now?

Yes, LGTM as well.

Tobias

-----------------
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] 19+ messages in thread

* [committed] testsuite: Fix up target-enter-data-1.c on 32-bit targets
  2023-11-29 11:43     ` Julian Brown
  2023-11-29 16:03       ` Tobias Burnus
@ 2023-12-14  7:14       ` Jakub Jelinek
  2023-12-14 10:09         ` Julian Brown
  1 sibling, 1 reply; 19+ messages in thread
From: Jakub Jelinek @ 2023-12-14  7:14 UTC (permalink / raw)
  To: Julian Brown; +Cc: Tobias Burnus, gcc-patches, fortran

On Wed, Nov 29, 2023 at 11:43:05AM +0000, Julian Brown wrote:
> 	* c-c++-common/gomp/target-enter-data-1.c: Adjust scan output.

struct bar { int num_vectors; double *vectors; };

is 16 bytes only on 64-bit targets, on 32-bit ones it is just 8 bytes,
so the explicit matching of the * 16 multiplication only works on the
former.

Tested on x86_64-linux -m32/-m64, committed to trunk.

2023-12-14  Jakub Jelinek  <jakub@redhat.com>

	* c-c++-common/gomp/target-enter-data-1.c: Match also sizeof bar on
	32-bit targets - 8 bytes - rather than just 16 bytes.

--- gcc/testsuite/c-c++-common/gomp/target-enter-data-1.c.jj	2023-12-14 07:49:53.364577887 +0100
+++ gcc/testsuite/c-c++-common/gomp/target-enter-data-1.c	2023-12-14 08:08:59.680964433 +0100
@@ -22,4 +22,4 @@ void func (struct foo *f, int n, int m)
 }
 
 /* { dg-final { scan-tree-dump-times {map\(struct:\*f \[len: 1\]\) map\(alloc:[a-z0-9\._]+->vectors \[len: 0\]\) map\(to:\*_[0-9]+ \[len: _[0-9]+\]\) map\(attach:[a-z0-9\._]+->vectors \[bias: [^\]]+\]\) map\(attach:\*_[0-9]+ \[bias: _[0-9]+\]\)} 1 "gimple" } } */
-/* { dg-final { scan-tree-dump-times {map\(struct:\*\(f->bars \+ \(sizetype\) \(\([^\)]+\) n \* 16\)\) \[len: 1\]\) map\(alloc:[a-z0-9\._]+->vectors \[len: 0\]\) map\(to:\*_[0-9]+ \[len: _[0-9]+\]\) map\(attach:[a-z0-9\._]+->vectors \[bias: [^\]]+\]\)} 2 "gimple" } } */
+/* { dg-final { scan-tree-dump-times {map\(struct:\*\(f->bars \+ \(sizetype\) \(\([^\)]+\) n \* (?:16|8)\)\) \[len: 1\]\) map\(alloc:[a-z0-9\._]+->vectors \[len: 0\]\) map\(to:\*_[0-9]+ \[len: _[0-9]+\]\) map\(attach:[a-z0-9\._]+->vectors \[bias: [^\]]+\]\)} 2 "gimple" } } */


	Jakub


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

* Re: [committed] testsuite: Fix up target-enter-data-1.c on 32-bit targets
  2023-12-14  7:14       ` [committed] testsuite: Fix up target-enter-data-1.c on 32-bit targets Jakub Jelinek
@ 2023-12-14 10:09         ` Julian Brown
  0 siblings, 0 replies; 19+ messages in thread
From: Julian Brown @ 2023-12-14 10:09 UTC (permalink / raw)
  To: Jakub Jelinek; +Cc: Tobias Burnus, gcc-patches, fortran

On Thu, 14 Dec 2023 08:14:56 +0100
Jakub Jelinek <jakub@redhat.com> wrote:

> On Wed, Nov 29, 2023 at 11:43:05AM +0000, Julian Brown wrote:
> > 	* c-c++-common/gomp/target-enter-data-1.c: Adjust scan
> > output.  
> 
> struct bar { int num_vectors; double *vectors; };
> 
> is 16 bytes only on 64-bit targets, on 32-bit ones it is just 8 bytes,
> so the explicit matching of the * 16 multiplication only works on the
> former.
> 
> Tested on x86_64-linux -m32/-m64, committed to trunk.

That was quick, thank you! Apologies for the breakage.

Julian

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

* Re: [PATCH v7 4/5] OpenMP/OpenACC: Unordered/non-constant component offset runtime diagnostic
  2023-08-18 22:47 ` [PATCH v7 4/5] OpenMP/OpenACC: Unordered/non-constant component offset runtime diagnostic Julian Brown
@ 2023-12-14 14:26   ` Tobias Burnus
  2023-12-15 13:00     ` Thomas Schwinge
  0 siblings, 1 reply; 19+ messages in thread
From: Tobias Burnus @ 2023-12-14 14:26 UTC (permalink / raw)
  To: Julian Brown, gcc-patches; +Cc: fortran, jakub

On 19.08.23 00:47, Julian Brown wrote:
> This patch adds support for non-constant component offsets in "map"
> clauses for OpenMP (and the equivalants for OpenACC), which are not able
> to be sorted into order at compile time.  Normally struct accesses in
> such clauses are gathered together and sorted into increasing address
> order after a "GOMP_MAP_STRUCT" node: if we have variable indices,
> that is no longer possible.
>
> This version of the patch scales back the previously-posted version to
> merely add a diagnostic for incorrect usage of component accesses with
> variably-indexed arrays of structs: the only permitted variant is where
> we have multiple indices that are the same, but we could not prove so
> at compile time.  Rather than silently producing the wrong result for
> cases where the indices are in fact different, we error out (e.g.,
> "map(dtarr(i)%arrptr, dtarr(j)%arrptr(4:8))", for different i/j).
>
> For now, multiple *constant* array indices are still supported (see
> map-arrayofstruct-1.c).  That could perhaps be addressed with a follow-up
> patch, if necessary.
>
> This version of the patch renumbers the GOMP_MAP_STRUCT_UNORD kind to
> avoid clashing with the OpenACC "non-contiguous" dynamic array support
> (though that is not yet applied to mainline).

LGTM with:

- inclusion of your follow-up fix for shared-memory systems (see email
of August 21)

- adding a comment to map-arrayofstruct-1.c indicating that this usage
is an extension, violating a restriction (be a bit more explicit that
just that)

See https://gcc.gnu.org/pipermail/gcc-patches/2022-October/603126.html
for a quote of the specification or (same wording, newer spec) in TR12
under "Restrictions to the map clause are as follows:" in "6.8.3 map
Clause" [218+219:36-37+1-3]

Thanks,

Tobias

> 2023-08-18  Julian Brown  <julian@codesourcery.com>
>
> gcc/
>       * gimplify.cc (extract_base_bit_offset): Add VARIABLE_OFFSET parameter.
>       (omp_get_attachment, omp_group_last, omp_group_base,
>       omp_directive_maps_explicitly): Add GOMP_MAP_STRUCT_UNORD support.
>       (omp_accumulate_sibling_list): Update calls to extract_base_bit_offset.
>       Support GOMP_MAP_STRUCT_UNORD.
>       (omp_build_struct_sibling_lists, gimplify_scan_omp_clauses,
>       gimplify_adjust_omp_clauses, gimplify_omp_target_update): Add
>       GOMP_MAP_STRUCT_UNORD support.
>       * omp-low.cc (lower_omp_target): Add GOMP_MAP_STRUCT_UNORD support.
>       * tree-pretty-print.cc (dump_omp_clause): Likewise.
>
> include/
>       * gomp-constants.h (gomp_map_kind): Add GOMP_MAP_STRUCT_UNORD.
>
> libgomp/
>       * oacc-mem.c (find_group_last, goacc_enter_data_internal,
>       goacc_exit_data_internal, GOACC_enter_exit_data): Add
>       GOMP_MAP_STRUCT_UNORD support.
>       * target.c (gomp_map_vars_internal): Add GOMP_MAP_STRUCT_UNORD support.
>       Detect incorrect use of variable indexing of arrays of structs.
>       (GOMP_target_enter_exit_data, gomp_target_task_fn): Add
>       GOMP_MAP_STRUCT_UNORD support.
>       * testsuite/libgomp.c-c++-common/map-arrayofstruct-1.c: New test.
>       * testsuite/libgomp.c-c++-common/map-arrayofstruct-2.c: New test.
>       * testsuite/libgomp.c-c++-common/map-arrayofstruct-3.c: New test.
>       * testsuite/libgomp.fortran/map-subarray-5.f90: New test.
> ---
>   gcc/gimplify.cc                               | 110 ++++++++++++++----
>   gcc/omp-low.cc                                |   1 +
>   gcc/tree-pretty-print.cc                      |   3 +
>   include/gomp-constants.h                      |   6 +
>   libgomp/oacc-mem.c                            |   6 +-
>   libgomp/target.c                              |  60 +++++++++-
>   .../map-arrayofstruct-1.c                     |  38 ++++++
>   .../map-arrayofstruct-2.c                     |  58 +++++++++
>   .../map-arrayofstruct-3.c                     |  68 +++++++++++
>   .../libgomp.fortran/map-subarray-5.f90        |  54 +++++++++
>   10 files changed, 377 insertions(+), 27 deletions(-)
>   create mode 100644 libgomp/testsuite/libgomp.c-c++-common/map-arrayofstruct-1.c
>   create mode 100644 libgomp/testsuite/libgomp.c-c++-common/map-arrayofstruct-2.c
>   create mode 100644 libgomp/testsuite/libgomp.c-c++-common/map-arrayofstruct-3.c
>   create mode 100644 libgomp/testsuite/libgomp.fortran/map-subarray-5.f90
>
> diff --git a/gcc/gimplify.cc b/gcc/gimplify.cc
> index fad4308a0eb4..e682583054b0 100644
> --- a/gcc/gimplify.cc
> +++ b/gcc/gimplify.cc
> @@ -8965,7 +8965,8 @@ build_omp_struct_comp_nodes (enum tree_code code, tree grp_start, tree grp_end,
>
>   static tree
>   extract_base_bit_offset (tree base, poly_int64 *bitposp,
> -                      poly_offset_int *poffsetp)
> +                      poly_offset_int *poffsetp,
> +                      bool *variable_offset)
>   {
>     tree offset;
>     poly_int64 bitsize, bitpos;
> @@ -8983,10 +8984,13 @@ extract_base_bit_offset (tree base, poly_int64 *bitposp,
>     if (offset && poly_int_tree_p (offset))
>       {
>         poffset = wi::to_poly_offset (offset);
> -      offset = NULL_TREE;
> +      *variable_offset = false;
>       }
>     else
> -    poffset = 0;
> +    {
> +      poffset = 0;
> +      *variable_offset = (offset != NULL_TREE);
> +    }
>
>     if (maybe_ne (bitpos, 0))
>       poffset += bits_to_bytes_round_down (bitpos);
> @@ -9166,6 +9170,7 @@ omp_get_attachment (omp_mapping_group *grp)
>         return error_mark_node;
>
>       case GOMP_MAP_STRUCT:
> +    case GOMP_MAP_STRUCT_UNORD:
>       case GOMP_MAP_FORCE_DEVICEPTR:
>       case GOMP_MAP_DEVICE_RESIDENT:
>       case GOMP_MAP_LINK:
> @@ -9271,6 +9276,7 @@ omp_group_last (tree *start_p)
>         break;
>
>       case GOMP_MAP_STRUCT:
> +    case GOMP_MAP_STRUCT_UNORD:
>         {
>       unsigned HOST_WIDE_INT num_mappings
>         = tree_to_uhwi (OMP_CLAUSE_SIZE (c));
> @@ -9437,6 +9443,7 @@ omp_group_base (omp_mapping_group *grp, unsigned int *chained,
>         return error_mark_node;
>
>       case GOMP_MAP_STRUCT:
> +    case GOMP_MAP_STRUCT_UNORD:
>         {
>       unsigned HOST_WIDE_INT num_mappings
>         = tree_to_uhwi (OMP_CLAUSE_SIZE (node));
> @@ -10079,7 +10086,8 @@ omp_directive_maps_explicitly (hash_map<tree_operand_hash_no_se,
>         /* We might be called during omp_build_struct_sibling_lists, when
>        GOMP_MAP_STRUCT might have been inserted at the start of the group.
>        Skip over that, and also possibly the node after it.  */
> -      if (OMP_CLAUSE_MAP_KIND (grp_first) == GOMP_MAP_STRUCT)
> +      if (OMP_CLAUSE_MAP_KIND (grp_first) == GOMP_MAP_STRUCT
> +       || OMP_CLAUSE_MAP_KIND (grp_first) == GOMP_MAP_STRUCT_UNORD)
>       {
>         grp_first = OMP_CLAUSE_CHAIN (grp_first);
>         if (OMP_CLAUSE_MAP_KIND (grp_first) == GOMP_MAP_FIRSTPRIVATE_POINTER
> @@ -10816,7 +10824,9 @@ omp_accumulate_sibling_list (enum omp_region_type region_type,
>       }
>       }
>
> -  tree base = extract_base_bit_offset (ocd, &cbitpos, &coffset);
> +  bool variable_offset;
> +  tree base
> +    = extract_base_bit_offset (ocd, &cbitpos, &coffset, &variable_offset);
>
>     int base_token;
>     for (base_token = addr_tokens.length () - 1; base_token >= 0; base_token--)
> @@ -10850,14 +10860,20 @@ omp_accumulate_sibling_list (enum omp_region_type region_type,
>
>     if (!struct_map_to_clause || struct_map_to_clause->get (base) == NULL)
>       {
> -      tree l = build_omp_clause (OMP_CLAUSE_LOCATION (grp_end), OMP_CLAUSE_MAP);
> -
> -      OMP_CLAUSE_SET_MAP_KIND (l, GOMP_MAP_STRUCT);
> -      OMP_CLAUSE_DECL (l) = unshare_expr (base);
> -      OMP_CLAUSE_SIZE (l) = size_int (1);
> +      enum gomp_map_kind str_kind = GOMP_MAP_STRUCT;
>
>         if (struct_map_to_clause == NULL)
>       struct_map_to_clause = new hash_map<tree_operand_hash, tree>;
> +
> +      if (variable_offset)
> +     str_kind = GOMP_MAP_STRUCT_UNORD;
> +
> +      tree l = build_omp_clause (OMP_CLAUSE_LOCATION (grp_end), OMP_CLAUSE_MAP);
> +
> +      OMP_CLAUSE_SET_MAP_KIND (l, str_kind);
> +      OMP_CLAUSE_DECL (l) = unshare_expr (base);
> +      OMP_CLAUSE_SIZE (l) = size_int (1);
> +
>         struct_map_to_clause->put (base, l);
>
>         /* On first iterating through the clause list, we insert the struct node
> @@ -11097,6 +11113,11 @@ omp_accumulate_sibling_list (enum omp_region_type region_type,
>       {
>         tree *osc = struct_map_to_clause->get (base);
>         tree *sc = NULL, *scp = NULL;
> +      bool unordered = false;
> +
> +      if (osc && OMP_CLAUSE_MAP_KIND (*osc) == GOMP_MAP_STRUCT_UNORD)
> +     unordered = true;
> +
>         unsigned HOST_WIDE_INT i, elems = tree_to_uhwi (OMP_CLAUSE_SIZE (*osc));
>         sc = &OMP_CLAUSE_CHAIN (*osc);
>         /* The struct mapping might be immediately followed by a
> @@ -11137,12 +11158,20 @@ omp_accumulate_sibling_list (enum omp_region_type region_type,
>                        == REFERENCE_TYPE))
>             sc_decl = TREE_OPERAND (sc_decl, 0);
>
> -         tree base2 = extract_base_bit_offset (sc_decl, &bitpos, &offset);
> +         bool variable_offset2;
> +         tree base2 = extract_base_bit_offset (sc_decl, &bitpos, &offset,
> +                                               &variable_offset2);
>           if (!base2 || !operand_equal_p (base2, base, 0))
>             break;
>           if (scp)
>             continue;
> -         if ((region_type & ORT_ACC) != 0)
> +         if (variable_offset2)
> +           {
> +             OMP_CLAUSE_SET_MAP_KIND (*osc, GOMP_MAP_STRUCT_UNORD);
> +             unordered = true;
> +             break;
> +           }
> +         else if ((region_type & ORT_ACC) != 0)
>             {
>               /* For OpenACC, allow (ignore) duplicate struct accesses in
>                  the middle of a mapping clause, e.g. "mystruct->foo" in:
> @@ -11174,6 +11203,15 @@ omp_accumulate_sibling_list (enum omp_region_type region_type,
>             }
>         }
>
> +      /* If this is an unordered struct, just insert the new element at the
> +      end of the list.  */
> +      if (unordered)
> +     {
> +       for (; i < elems; i++)
> +         sc = &OMP_CLAUSE_CHAIN (*sc);
> +       scp = NULL;
> +     }
> +
>         OMP_CLAUSE_SIZE (*osc)
>       = size_binop (PLUS_EXPR, OMP_CLAUSE_SIZE (*osc), size_one_node);
>
> @@ -11565,14 +11603,42 @@ omp_build_struct_sibling_lists (enum tree_code code,
>
>       /* This is the first sorted node in the struct sibling list.  Use it
>          to recalculate the correct bias to use.
> -        (&first_node - attach_decl).  */
> -     tree first_node = OMP_CLAUSE_DECL (OMP_CLAUSE_CHAIN (attach));
> -     first_node = build_fold_addr_expr (first_node);
> -     first_node = fold_convert (ptrdiff_type_node, first_node);
> +        (&first_node - attach_decl).
> +        For GOMP_MAP_STRUCT_UNORD, we need e.g. the
> +        min(min(min(first,second),third),fourth) element, because the
> +        elements aren't in any particular order.  */
> +     tree lowest_addr;
> +     if (OMP_CLAUSE_MAP_KIND (struct_node) == GOMP_MAP_STRUCT_UNORD)
> +       {
> +         tree first_node = OMP_CLAUSE_CHAIN (attach);
> +         unsigned HOST_WIDE_INT num_mappings
> +           = tree_to_uhwi (OMP_CLAUSE_SIZE (struct_node));
> +         lowest_addr = OMP_CLAUSE_DECL (first_node);
> +         lowest_addr = build_fold_addr_expr (lowest_addr);
> +         lowest_addr = fold_convert (pointer_sized_int_node, lowest_addr);
> +         tree next_node = OMP_CLAUSE_CHAIN (first_node);
> +         while (num_mappings > 1)
> +           {
> +             tree tmp = OMP_CLAUSE_DECL (next_node);
> +             tmp = build_fold_addr_expr (tmp);
> +             tmp = fold_convert (pointer_sized_int_node, tmp);
> +             lowest_addr = fold_build2 (MIN_EXPR, pointer_sized_int_node,
> +                                        lowest_addr, tmp);
> +             next_node = OMP_CLAUSE_CHAIN (next_node);
> +             num_mappings--;
> +           }
> +         lowest_addr = fold_convert (ptrdiff_type_node, lowest_addr);
> +       }
> +     else
> +       {
> +         tree first_node = OMP_CLAUSE_DECL (OMP_CLAUSE_CHAIN (attach));
> +         first_node = build_fold_addr_expr (first_node);
> +         lowest_addr = fold_convert (ptrdiff_type_node, first_node);
> +       }
>       tree attach_decl = OMP_CLAUSE_DECL (attach);
>       attach_decl = fold_convert (ptrdiff_type_node, attach_decl);
>       OMP_CLAUSE_SIZE (attach)
> -       = fold_build2 (MINUS_EXPR, ptrdiff_type_node, first_node,
> +       = fold_build2 (MINUS_EXPR, ptrdiff_type_node, lowest_addr,
>                        attach_decl);
>
>       /* Remove GOMP_MAP_ATTACH node from after struct node.  */
> @@ -12129,7 +12195,8 @@ gimplify_scan_omp_clauses (tree *list_p, gimple_seq *pre_p,
>                                 GOVD_FIRSTPRIVATE | GOVD_SEEN);
>           }
>
> -       if (OMP_CLAUSE_MAP_KIND (c) == GOMP_MAP_STRUCT
> +       if ((OMP_CLAUSE_MAP_KIND (c) == GOMP_MAP_STRUCT
> +            || OMP_CLAUSE_MAP_KIND (c) == GOMP_MAP_STRUCT_UNORD)
>             && (addr_tokens[0]->type == STRUCTURE_BASE
>                 || addr_tokens[0]->type == ARRAY_BASE)
>             && addr_tokens[0]->u.structure_base_kind == BASE_DECL)
> @@ -13718,7 +13785,8 @@ gimplify_adjust_omp_clauses (gimple_seq *pre_p, gimple_seq body, tree *list_p,
>                   }
>               }
>           }
> -       if (OMP_CLAUSE_MAP_KIND (c) == GOMP_MAP_STRUCT
> +       if ((OMP_CLAUSE_MAP_KIND (c) == GOMP_MAP_STRUCT
> +            || OMP_CLAUSE_MAP_KIND (c) == GOMP_MAP_STRUCT_UNORD)
>             && (code == OMP_TARGET_EXIT_DATA || code == OACC_EXIT_DATA))
>           {
>             remove = true;
> @@ -13762,7 +13830,8 @@ gimplify_adjust_omp_clauses (gimple_seq *pre_p, gimple_seq body, tree *list_p,
>                in target block and none of the mapping has always modifier,
>                remove all the struct element mappings, which immediately
>                follow the GOMP_MAP_STRUCT map clause.  */
> -           if (OMP_CLAUSE_MAP_KIND (c) == GOMP_MAP_STRUCT)
> +           if (OMP_CLAUSE_MAP_KIND (c) == GOMP_MAP_STRUCT
> +               || OMP_CLAUSE_MAP_KIND (c) == GOMP_MAP_STRUCT_UNORD)
>               {
>                 HOST_WIDE_INT cnt = tree_to_shwi (OMP_CLAUSE_SIZE (c));
>                 while (cnt--)
> @@ -16541,6 +16610,7 @@ gimplify_omp_target_update (tree *expr_p, gimple_seq *pre_p)
>             have_clause = false;
>             break;
>           case GOMP_MAP_STRUCT:
> +         case GOMP_MAP_STRUCT_UNORD:
>             have_clause = false;
>             break;
>           default:
> diff --git a/gcc/omp-low.cc b/gcc/omp-low.cc
> index 2b2aa7f13146..3e2c984f8815 100644
> --- a/gcc/omp-low.cc
> +++ b/gcc/omp-low.cc
> @@ -12811,6 +12811,7 @@ lower_omp_target (gimple_stmt_iterator *gsi_p, omp_context *ctx)
>         case GOMP_MAP_FIRSTPRIVATE_POINTER:
>         case GOMP_MAP_FIRSTPRIVATE_REFERENCE:
>         case GOMP_MAP_STRUCT:
> +       case GOMP_MAP_STRUCT_UNORD:
>         case GOMP_MAP_ALWAYS_POINTER:
>         case GOMP_MAP_ATTACH:
>         case GOMP_MAP_DETACH:
> diff --git a/gcc/tree-pretty-print.cc b/gcc/tree-pretty-print.cc
> index 25d191b10fd7..0c1d6722c5ca 100644
> --- a/gcc/tree-pretty-print.cc
> +++ b/gcc/tree-pretty-print.cc
> @@ -967,6 +967,9 @@ dump_omp_clause (pretty_printer *pp, tree clause, int spc, dump_flags_t flags)
>       case GOMP_MAP_STRUCT:
>         pp_string (pp, "struct");
>         break;
> +     case GOMP_MAP_STRUCT_UNORD:
> +       pp_string (pp, "struct_unord");
> +       break;
>       case GOMP_MAP_ALWAYS_POINTER:
>         pp_string (pp, "always_pointer");
>         break;
> diff --git a/include/gomp-constants.h b/include/gomp-constants.h
> index 8d4e8e813031..20c722665680 100644
> --- a/include/gomp-constants.h
> +++ b/include/gomp-constants.h
> @@ -153,6 +153,12 @@ enum gomp_map_kind
>          (address of the last adjacent entry plus its size).  */
>       GOMP_MAP_STRUCT =                       (GOMP_MAP_FLAG_SPECIAL_2
>                                        | GOMP_MAP_FLAG_SPECIAL | 0),
> +    /* As above, but followed by an unordered list of adjacent entries.
> +       At present, this is used only to diagnose incorrect usage of variable
> +       indices into arrays of structs.  */
> +    GOMP_MAP_STRUCT_UNORD =          (GOMP_MAP_FLAG_SPECIAL_4
> +                                      | GOMP_MAP_FLAG_SPECIAL_2
> +                                      | GOMP_MAP_FLAG_SPECIAL | 0),
>       /* On a location of a pointer/reference that is assumed to be already mapped
>          earlier, store the translated address of the preceeding mapping.
>          No refcount is bumped by this, and the store is done unconditionally.  */
> diff --git a/libgomp/oacc-mem.c b/libgomp/oacc-mem.c
> index fe6327407693..79d6e32c0b4e 100644
> --- a/libgomp/oacc-mem.c
> +++ b/libgomp/oacc-mem.c
> @@ -1028,6 +1028,7 @@ find_group_last (int pos, size_t mapnum, size_t *sizes, unsigned short *kinds)
>         break;
>
>       case GOMP_MAP_STRUCT:
> +    case GOMP_MAP_STRUCT_UNORD:
>         pos += sizes[pos];
>         break;
>
> @@ -1088,6 +1089,7 @@ goacc_enter_data_internal (struct gomp_device_descr *acc_dev, size_t mapnum,
>         switch (kinds[i] & 0xff)
>       {
>       case GOMP_MAP_STRUCT:
> +     case GOMP_MAP_STRUCT_UNORD:
>         {
>           size = (uintptr_t) hostaddrs[group_last] + sizes[group_last]
>                  - (uintptr_t) hostaddrs[i];
> @@ -1334,6 +1336,7 @@ goacc_exit_data_internal (struct gomp_device_descr *acc_dev, size_t mapnum,
>         break;
>
>       case GOMP_MAP_STRUCT:
> +     case GOMP_MAP_STRUCT_UNORD:
>         /* Skip the 'GOMP_MAP_STRUCT' itself, and use the regular processing
>            for all its entries.  This special handling exists for GCC 10.1
>            compatibility; afterwards, we're not generating these no-op
> @@ -1472,7 +1475,8 @@ GOACC_enter_exit_data (int flags_m, size_t mapnum, void **hostaddrs,
>
>         if (kind == GOMP_MAP_POINTER
>         || kind == GOMP_MAP_TO_PSET
> -       || kind == GOMP_MAP_STRUCT)
> +       || kind == GOMP_MAP_STRUCT
> +       || kind == GOMP_MAP_STRUCT_UNORD)
>       continue;
>
>         if (kind == GOMP_MAP_FORCE_ALLOC
> diff --git a/libgomp/target.c b/libgomp/target.c
> index b9137e703045..a94fcea154f9 100644
> --- a/libgomp/target.c
> +++ b/libgomp/target.c
> @@ -1083,7 +1083,8 @@ gomp_map_vars_internal (struct gomp_device_descr *devicep,
>           tgt->list[i].offset = 0;
>         continue;
>       }
> -      else if ((kind & typemask) == GOMP_MAP_STRUCT)
> +      else if ((kind & typemask) == GOMP_MAP_STRUCT
> +            || (kind & typemask) == GOMP_MAP_STRUCT_UNORD)
>       {
>         size_t first = i + 1;
>         size_t last = i + sizes[i];
> @@ -1467,6 +1468,20 @@ gomp_map_vars_internal (struct gomp_device_descr *devicep,
>                   tgt->list[i].offset = OFFSET_INLINED;
>                 }
>               continue;
> +           case GOMP_MAP_STRUCT_UNORD:
> +             if (sizes[i] > 1)
> +               {
> +                 void *first = hostaddrs[i + 1];
> +                 for (size_t j = i + 1; j < i + sizes[i]; j++)
> +                   if (hostaddrs[j + 1] != first)
> +                     {
> +                       gomp_mutex_unlock (&devicep->lock);
> +                       gomp_fatal ("Mapped array elements must be the "
> +                                   "same (%p vs %p)", first,
> +                                   hostaddrs[j + 1]);
> +                     }
> +               }
> +             /* Fallthrough.  */
>             case GOMP_MAP_STRUCT:
>               first = i + 1;
>               last = i + sizes[i];
> @@ -1585,9 +1600,40 @@ gomp_map_vars_internal (struct gomp_device_descr *devicep,
>             k->host_end = k->host_start + sizeof (void *);
>           splay_tree_key n = splay_tree_lookup (mem_map, k);
>           if (n && n->refcount != REFCOUNT_LINK)
> -           gomp_map_vars_existing (devicep, aq, n, k, &tgt->list[i],
> -                                   kind & typemask, false, implicit, cbufp,
> -                                   refcount_set);
> +           {
> +             if (field_tgt_clear != FIELD_TGT_EMPTY)
> +               {
> +                 /* For this condition to be true, there must be a
> +                    duplicate struct element mapping.  This can happen with
> +                    GOMP_MAP_STRUCT_UNORD mappings, for example.  */
> +                 tgt->list[i].key = n;
> +                 if (openmp_p)
> +                   {
> +                     assert ((n->refcount & REFCOUNT_STRUCTELEM) != 0);
> +                     assert (field_tgt_structelem_first != NULL);
> +
> +                     if (i == field_tgt_clear)
> +                       {
> +                         n->refcount |= REFCOUNT_STRUCTELEM_FLAG_LAST;
> +                         field_tgt_structelem_first = NULL;
> +                       }
> +                   }
> +                 if (i == field_tgt_clear)
> +                   field_tgt_clear = FIELD_TGT_EMPTY;
> +                 gomp_increment_refcount (n, refcount_set);
> +                 tgt->list[i].copy_from
> +                   = GOMP_MAP_COPY_FROM_P (kind & typemask);
> +                 tgt->list[i].always_copy_from
> +                   = GOMP_MAP_ALWAYS_FROM_P (kind & typemask);
> +                 tgt->list[i].is_attach = false;
> +                 tgt->list[i].offset = 0;
> +                 tgt->list[i].length = k->host_end - k->host_start;
> +               }
> +             else
> +               gomp_map_vars_existing (devicep, aq, n, k, &tgt->list[i],
> +                                       kind & typemask, false, implicit,
> +                                       cbufp, refcount_set);
> +           }
>           else
>             {
>               k->aux = NULL;
> @@ -4154,7 +4200,8 @@ GOMP_target_enter_exit_data (int device, size_t mapnum, void **hostaddrs,
>     size_t i, j;
>     if ((flags & GOMP_TARGET_FLAG_EXIT_DATA) == 0)
>       for (i = 0; i < mapnum; i++)
> -      if ((kinds[i] & 0xff) == GOMP_MAP_STRUCT)
> +      if ((kinds[i] & 0xff) == GOMP_MAP_STRUCT
> +       || (kinds[i] & 0xff) == GOMP_MAP_STRUCT_UNORD)
>       {
>         gomp_map_vars (devicep, sizes[i] + 1, &hostaddrs[i], NULL, &sizes[i],
>                        &kinds[i], true, &refcount_set,
> @@ -4252,7 +4299,8 @@ gomp_target_task_fn (void *data)
>         htab_t refcount_set = htab_create (ttask->mapnum);
>         if ((ttask->flags & GOMP_TARGET_FLAG_EXIT_DATA) == 0)
>       for (i = 0; i < ttask->mapnum; i++)
> -       if ((ttask->kinds[i] & 0xff) == GOMP_MAP_STRUCT)
> +       if ((ttask->kinds[i] & 0xff) == GOMP_MAP_STRUCT
> +           || (ttask->kinds[i] & 0xff) == GOMP_MAP_STRUCT_UNORD)
>           {
>             gomp_map_vars (devicep, ttask->sizes[i] + 1, &ttask->hostaddrs[i],
>                            NULL, &ttask->sizes[i], &ttask->kinds[i], true,
> diff --git a/libgomp/testsuite/libgomp.c-c++-common/map-arrayofstruct-1.c b/libgomp/testsuite/libgomp.c-c++-common/map-arrayofstruct-1.c
> new file mode 100644
> index 000000000000..b0994c0a7bb4
> --- /dev/null
> +++ b/libgomp/testsuite/libgomp.c-c++-common/map-arrayofstruct-1.c
> @@ -0,0 +1,38 @@
> +#include <stdlib.h>
> +#include <assert.h>
> +
> +struct st {
> +  int *p;
> +};
> +
> +int main (void)
> +{
> +  struct st s[2];
> +  s[0].p = (int *) calloc (5, sizeof (int));
> +  s[1].p = (int *) calloc (5, sizeof (int));
> +
> +#pragma omp target map(s[0].p, s[1].p, s[0].p[0:2], s[1].p[1:3])
> +  {
> +    s[0].p[0] = 5;
> +    s[1].p[1] = 7;
> +  }
> +
> +#pragma omp target map(s, s[0].p[0:2], s[1].p[1:3])
> +  {
> +    s[0].p[0]++;
> +    s[1].p[1]++;
> +  }
> +
> +#pragma omp target map(s[0:2], s[0].p[0:2], s[1].p[1:3])
> +  {
> +    s[0].p[0]++;
> +    s[1].p[1]++;
> +  }
> +
> +  assert (s[0].p[0] == 7);
> +  assert (s[1].p[1] == 9);
> +
> +  free (s[0].p);
> +  free (s[1].p);
> +  return 0;
> +}
> diff --git a/libgomp/testsuite/libgomp.c-c++-common/map-arrayofstruct-2.c b/libgomp/testsuite/libgomp.c-c++-common/map-arrayofstruct-2.c
> new file mode 100644
> index 000000000000..81f7efc27c98
> --- /dev/null
> +++ b/libgomp/testsuite/libgomp.c-c++-common/map-arrayofstruct-2.c
> @@ -0,0 +1,58 @@
> +#include <stdlib.h>
> +#include <assert.h>
> +
> +struct st {
> +  int *p;
> +};
> +
> +int main (void)
> +{
> +  struct st s[10];
> +
> +  for (int i = 0; i < 10; i++)
> +    s[i].p = (int *) calloc (5, sizeof (int));
> +
> +  for (int i = 0; i < 10; i++)
> +    for (int j = 0; j < 10; j++)
> +      for (int k = 0; k < 10; k++)
> +     {
> +       if (i == j || j == k || i == k)
> +         continue;
> +
> +#pragma omp target map(s[i].p, s[j].p, s[k].p, s[i].p[0:2], s[j].p[1:3], \
> +                    s[k].p[2])
> +       {
> +         s[i].p[0]++;
> +         s[j].p[1]++;
> +         s[k].p[2]++;
> +       }
> +
> +#pragma omp target map(s, s[i].p[0:2], s[j].p[1:3], s[k].p[2])
> +       {
> +         s[i].p[0]++;
> +         s[j].p[1]++;
> +         s[k].p[2]++;
> +       }
> +
> +#pragma omp target map(s[0:10], s[i].p[0:2], s[j].p[1:3], s[k].p[2])
> +       {
> +         s[i].p[0]++;
> +         s[j].p[1]++;
> +         s[k].p[2]++;
> +       }
> +     }
> +
> +  for (int i = 0; i < 10; i++)
> +    {
> +      assert (s[i].p[0] == 216);
> +      assert (s[i].p[1] == 216);
> +      assert (s[i].p[2] == 216);
> +      free (s[i].p);
> +    }
> +
> +  return 0;
> +}
> +
> +/* { dg-output "(\n|\r|\r\n)" } */
> +/* { dg-output "libgomp: Mapped array elements must be the same .*(\n|\r|\r\n)+" } */
> +/* { dg-shouldfail "" { offload_device_nonshared_as } } */
> diff --git a/libgomp/testsuite/libgomp.c-c++-common/map-arrayofstruct-3.c b/libgomp/testsuite/libgomp.c-c++-common/map-arrayofstruct-3.c
> new file mode 100644
> index 000000000000..639a0d2bc1e3
> --- /dev/null
> +++ b/libgomp/testsuite/libgomp.c-c++-common/map-arrayofstruct-3.c
> @@ -0,0 +1,68 @@
> +#include <stdlib.h>
> +#include <assert.h>
> +
> +struct st {
> +  int *p;
> +};
> +
> +struct tt {
> +  struct st a[10];
> +};
> +
> +struct ut {
> +  struct tt *t;
> +};
> +
> +int main (void)
> +{
> +  struct tt *t = (struct tt *) malloc (sizeof *t);
> +  struct ut *u = (struct ut *) malloc (sizeof *u);
> +
> +  for (int i = 0; i < 10; i++)
> +    t->a[i].p = (int *) calloc (5, sizeof (int));
> +
> +  u->t = t;
> +
> +  for (int i = 0; i < 10; i++)
> +    for (int j = 0; j < 10; j++)
> +      for (int k = 0; k < 10; k++)
> +     {
> +       if (i == j || j == k || i == k)
> +         continue;
> +
> +       /* This one can use "firstprivate" for T...  */
> +#pragma omp target map(t->a[i].p, t->a[j].p, t->a[k].p, \
> +                    t->a[i].p[0:2], t->a[j].p[1:3], t->a[k].p[2])
> +       {
> +         t->a[i].p[0]++;
> +         t->a[j].p[1]++;
> +         t->a[k].p[2]++;
> +       }
> +
> +       /* ...but this one must use attach/detach for T.  */
> +#pragma omp target map(u->t, u->t->a[i].p, u->t->a[j].p, u->t->a[k].p, \
> +                    u->t->a[i].p[0:2], u->t->a[j].p[1:3], u->t->a[k].p[2])
> +       {
> +         u->t->a[i].p[0]++;
> +         u->t->a[j].p[1]++;
> +         u->t->a[k].p[2]++;
> +       }
> +     }
> +
> +  for (int i = 0; i < 10; i++)
> +    {
> +      assert (t->a[i].p[0] == 144);
> +      assert (t->a[i].p[1] == 144);
> +      assert (t->a[i].p[2] == 144);
> +      free (t->a[i].p);
> +    }
> +
> +  free (u);
> +  free (t);
> +
> +  return 0;
> +}
> +
> +/* { dg-output "(\n|\r|\r\n)" } */
> +/* { dg-output "libgomp: Mapped array elements must be the same .*(\n|\r|\r\n)+" } */
> +/* { dg-shouldfail "" { offload_device_nonshared_as } } */
> diff --git a/libgomp/testsuite/libgomp.fortran/map-subarray-5.f90 b/libgomp/testsuite/libgomp.fortran/map-subarray-5.f90
> new file mode 100644
> index 000000000000..e7cdf11e6108
> --- /dev/null
> +++ b/libgomp/testsuite/libgomp.fortran/map-subarray-5.f90
> @@ -0,0 +1,54 @@
> +! { dg-do run }
> +
> +type t
> +  integer, pointer :: p(:)
> +end type t
> +
> +type(t) :: var(3)
> +integer :: i, j
> +
> +allocate (var(1)%p, source=[1,2,3,5])
> +allocate (var(2)%p, source=[2,3,5])
> +allocate (var(3)%p(1:3))
> +
> +var(3)%p = 0
> +
> +do i = 1, 3
> +  do j = 1, 3
> +!$omp target map(var(i)%p, var(j)%p)
> +    var(i)%p(1) = 5
> +    var(j)%p(2) = 7
> +!$omp end target
> +
> +    if (i.ne.j) then
> +!$omp target map(var(i)%p(1:3), var(i)%p, var(j)%p)
> +      var(i)%p(1) = var(i)%p(1) + 1
> +      var(j)%p(2) = var(j)%p(2) + 1
> +!$omp end target
> +
> +!$omp target map(var(i)%p, var(j)%p, var(j)%p(1:3))
> +      var(i)%p(1) = var(i)%p(1) + 1
> +      var(j)%p(2) = var(j)%p(2) + 1
> +!$omp end target
> +
> +!$omp target map(var(i)%p, var(i)%p(1:3), var(j)%p, var(j)%p(2))
> +      var(i)%p(1) = var(i)%p(1) + 1
> +      var(j)%p(2) = var(j)%p(2) + 1
> +!$omp end target
> +    end if
> +
> +    if (i.eq.j) then
> +      if (var(i)%p(1).ne.5) stop 1
> +      if (var(j)%p(2).ne.7) stop 2
> +    else
> +      if (var(i)%p(1).ne.8) stop 3
> +      if (var(j)%p(2).ne.10) stop 4
> +    end if
> +  end do
> +end do
> +
> +end
> +
> +! { dg-output "(\n|\r|\r\n)" }
> +! { dg-output "libgomp: Mapped array elements must be the same .*(\n|\r|\r\n)+" }
> +! { dg-shouldfail "" { offload_device_nonshared_as } }
-----------------
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] 19+ messages in thread

* Re: [PATCH v7 4/5] OpenMP/OpenACC: Unordered/non-constant component offset runtime diagnostic
  2023-12-14 14:26   ` Tobias Burnus
@ 2023-12-15 13:00     ` Thomas Schwinge
  0 siblings, 0 replies; 19+ messages in thread
From: Thomas Schwinge @ 2023-12-15 13:00 UTC (permalink / raw)
  To: Julian Brown, gcc-patches; +Cc: Tobias Burnus, fortran, jakub

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

Hi!

On 2023-12-14T15:26:38+0100, Tobias Burnus <tobias@codesourcery.com> wrote:
> On 19.08.23 00:47, Julian Brown wrote:
>> This patch adds support for non-constant component offsets in "map"
>> clauses for OpenMP (and the equivalants for OpenACC) [...]

Should eventually also add some OpenACC test cases?


> LGTM with:
>
> - inclusion of your follow-up fix for shared-memory systems (see email
> of August 21)

This was applied here:

>> --- /dev/null
>> +++ b/libgomp/testsuite/libgomp.c-c++-common/map-arrayofstruct-2.c

>> +/* { dg-output "(\n|\r|\r\n)" } */
>> +/* { dg-output "libgomp: Mapped array elements must be the same .*(\n|\r|\r\n)+" } */
>> +/* { dg-shouldfail "" { offload_device_nonshared_as } } */

..., and here:

>> --- /dev/null
>> +++ b/libgomp/testsuite/libgomp.c-c++-common/map-arrayofstruct-3.c

>> +/* { dg-output "(\n|\r|\r\n)" } */
>> +/* { dg-output "libgomp: Mapped array elements must be the same .*(\n|\r|\r\n)+" } */
>> +/* { dg-shouldfail "" { offload_device_nonshared_as } } */

..., but not here:

>> --- /dev/null
>> +++ b/libgomp/testsuite/libgomp.fortran/map-subarray-5.f90

>> +! { dg-output "(\n|\r|\r\n)" }
>> +! { dg-output "libgomp: Mapped array elements must be the same .*(\n|\r|\r\n)+" }
>> +! { dg-shouldfail "" { offload_device_nonshared_as } }

Pushed to master branch commit bc7546e32c5a942e240ef97776352d21105ef291
"In 'libgomp.fortran/map-subarray-5.f90', restrict 'dg-output's to 'target offload_device_nonshared_as'",
see attached.


Grüße
 Thomas


-----------------
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

[-- Warning: decoded text below may be mangled, UTF-8 assumed --]
[-- Attachment #2: 0001-In-libgomp.fortran-map-subarray-5.f90-restrict-dg-ou.patch --]
[-- Type: text/x-diff, Size: 1412 bytes --]

From bc7546e32c5a942e240ef97776352d21105ef291 Mon Sep 17 00:00:00 2001
From: Thomas Schwinge <thomas@codesourcery.com>
Date: Fri, 15 Dec 2023 13:05:24 +0100
Subject: [PATCH] In 'libgomp.fortran/map-subarray-5.f90', restrict
 'dg-output's to 'target offload_device_nonshared_as'

..., as in 'libgomp.c-c++-common/map-arrayofstruct-{2,3}.c'.

Minor fix-up for commit f5745dc1426bdb1a53ebaf7af758b2250ccbff02
"OpenMP/OpenACC: Unordered/non-constant component offset runtime diagnostic".

	libgomp/
	* testsuite/libgomp.fortran/map-subarray-5.f90: Restrict
	'dg-output's to 'target offload_device_nonshared_as'.
---
 libgomp/testsuite/libgomp.fortran/map-subarray-5.f90 | 4 ++--
 1 file changed, 2 insertions(+), 2 deletions(-)

diff --git a/libgomp/testsuite/libgomp.fortran/map-subarray-5.f90 b/libgomp/testsuite/libgomp.fortran/map-subarray-5.f90
index e7cdf11e610..59ad01ab76b 100644
--- a/libgomp/testsuite/libgomp.fortran/map-subarray-5.f90
+++ b/libgomp/testsuite/libgomp.fortran/map-subarray-5.f90
@@ -49,6 +49,6 @@ end do
 
 end
 
-! { dg-output "(\n|\r|\r\n)" }
-! { dg-output "libgomp: Mapped array elements must be the same .*(\n|\r|\r\n)+" }
+! { dg-output "(\n|\r|\r\n)" { target offload_device_nonshared_as } }
+! { dg-output "libgomp: Mapped array elements must be the same .*(\n|\r|\r\n)+" { target offload_device_nonshared_as } }
 ! { dg-shouldfail "" { offload_device_nonshared_as } }
-- 
2.34.1


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

* Re: [PATCH v7 5/5] OpenMP/OpenACC: Reorganise OMP map clause handling in gimplify.cc
       [not found]   ` <20231216132507.5991c79e@squid.athome>
@ 2023-12-19 15:41     ` Tobias Burnus
  2023-12-20 21:29       ` Julian Brown
  0 siblings, 1 reply; 19+ messages in thread
From: Tobias Burnus @ 2023-12-19 15:41 UTC (permalink / raw)
  To: Julian Brown, gcc-patches; +Cc: fortran

Hi Julian,

On 16.12.23 14:25, Julian Brown wrote:
>      OpenMP/OpenACC: Reorganise OMP map clause handling in gimplify.cc
>
>      This patch has been separated out from the C++ "declare mapper"
>      support patch.  It contains just the gimplify.cc rearrangement
>      work, mostly moving gimplification from gimplify_scan_omp_clauses
>      to gimplify_adjust_omp_clauses for map clauses.
>
>      The motivation for doing this was that we don't know if we need to
>      instantiate mappers implicitly until the body of an offload region has
>      been scanned, i.e. in gimplify_adjust_omp_clauses, but we also need the
>      un-gimplified form of clauses to sort by base-pointer dependencies after
>      mapper instantiation has taken place.
>
>      The patch also reimplements the "present" clause sorting code to avoid
>      another sorting pass on mapping nodes.
>
>      This version of the patch is based on the version posted for og13, and
>      additionally incorporates a follow-on fix for DECL_VALUE_EXPR handling
>      in gimplify_adjust_omp_clauses:
>
>      "OpenMP/OpenACC: Reorganise OMP map clause handling in gimplify.cc"
>      https://gcc.gnu.org/pipermail/gcc-patches/2023-June/622223.html
>
>      Parts of:
>      "OpenMP: OpenMP 5.2 semantics for pointers with unmapped target"
>      https://gcc.gnu.org/pipermail/gcc-patches/2023-June/623351.html

I find the patch hard to digest - and I wouldn't be surprised if I
missed something.

However, I think the patch is OK - except for a few minor observations:

>      2023-12-16  Julian Brown<julian@codesourcery.com>
>
>      gcc/
>              * gimplify.cc (omp_segregate_mapping_groups): Handle "present" groups.
>              (gimplify_scan_omp_clauses): Use mapping group functionality to
>              iterate through mapping nodes.  Remove most gimplification of
>              OMP_CLAUSE_MAP nodes from here, but still populate ctx->variables
>              splay tree.
>              (gimplify_adjust_omp_clauses): Move most gimplification of
>              OMP_CLAUSE_MAP nodes here.
>
>      gcc/testsuite/
>              * gfortran.dg/gomp/map-12.f90: Adjust scan output.
>
>      libgomp/
>              * testsuite/libgomp.fortran/target-enter-data-6.f90: Remove XFAIL.

> diff --git a/gcc/gimplify.cc b/gcc/gimplify.cc
> index a6bdceab45d..fa6ddd546f8 100644
> --- a/gcc/gimplify.cc
> +++ b/gcc/gimplify.cc
> @@ -10107,6 +10114,20 @@ omp_segregate_mapping_groups (omp_mapping_group *inlist)
>         ard_tail = &w->next;
>         break;
>
> +     case GOMP_MAP_PRESENT_ALLOC:
> +       *pa_tail = w;
> +       w->next = NULL;
> +       pa_tail = &w->next;
> +       break;
> +
> +     case GOMP_MAP_PRESENT_FROM:
> +     case GOMP_MAP_PRESENT_TO:
> +     case GOMP_MAP_PRESENT_TOFROM:
> +       *ptf_tail = w;
> +       w->next = NULL;
> +       ptf_tail = &w->next;
> +       break;
> +

First, I note that GOMP_MAP_PRESENT_ALLOC and
GOMP_MAP_PRESENT_{FROM,TO,TOFROM} are semantically identical: If the
variable is not present, error termination will happen - otherwise, if
present, no data movement will happen. Hence, they will be changed to
GOMP_MAP_FORCE_PRESENT in gimplify_adjust_omp_clauses.

That's also the reason that the old code handled all of them identical.

However, besides a plain 'present', there is also 'always' + 'present'.
Those are different as after a normal 'present' check (abort if not
present), the data copying will happen: GOMP_MAP_ALWAYS_PRESENT_TO,
GOMP_MAP_ALWAYS_PRESENT_FROM, GOMP_MAP_ALWAYS_PRESENT_TOFROM.

(Note that: always + present + alloc = GOMP_MAP_PRESENT_ALLOC (w/o
'always') as already done in the FE.)

Thus, all 'case' from your patch should go to a single group (possibly
adding a comment about it). The question is what to do with the
'present,always' case. I think leaving them under 'default:' is fine,
but I might have missed something.

>       default:
>         *tf_tail = w;
>         w->next = NULL;
> @@ -10118,8 +10139,10 @@ omp_segregate_mapping_groups (omp_mapping_group *inlist)
* * *
> @@ -11922,119 +11945,30 @@ gimplify_scan_omp_clauses (tree *list_p, gimple_seq *pre_p,
>       break;
>         }
>
> -  if (code == OMP_TARGET
> -      || code == OMP_TARGET_DATA
> -      || code == OMP_TARGET_ENTER_DATA
> -      || code == OMP_TARGET_EXIT_DATA)
> -    {
> -      vec<omp_mapping_group> *groups;
> -      groups = omp_gather_mapping_groups (list_p);
> -      if (groups)
> -     {
> -       hash_map<tree_operand_hash_no_se, omp_mapping_group *> *grpmap;
> -       grpmap = omp_index_mapping_groups (groups);
> +  vec<omp_mapping_group> *groups = omp_gather_mapping_groups (list_p);
> +  hash_map<tree_operand_hash_no_se, omp_mapping_group *> *grpmap = NULL;
> +  unsigned grpnum = 0;
> +  tree *grp_start_p = NULL, grp_end = NULL_TREE;

...

> -  else if (region_type & ORT_ACC)
> -    {
I wonder whether you should not better call 'omp_gather_mapping_groups'
only for the 'code == OMP_TARGET...' and for ORT_ACC (or some subset of
OACC *), given that this function is also called
bygimplify_omp_parallel, gimplify_omp_task, gimplify_omp_for, ...

This avoids some memory allocation and list_p walking, i.e. it is not
too bad - but also not really needed for task, parallel, for, ...

> @@ -14008,26 +13926,73 @@ gimplify_adjust_omp_clauses (gimple_seq *pre_p, gimple_seq body, tree *list_p,
>           default:
>             break;
>           }
> -       if (code == OMP_TARGET_EXIT_DATA
> -           && OMP_CLAUSE_MAP_KIND (c) == GOMP_MAP_ALWAYS_POINTER)
> +       switch (code)
>           {
> +         case OMP_TARGET:
> +           break;
> +         case OACC_DATA:
> +           if (TREE_CODE (TREE_TYPE (decl)) != ARRAY_TYPE)
> +             break;
> +           goto check_firstprivate;
> +         case OACC_ENTER_DATA:
> +         case OACC_EXIT_DATA:
> +         case OMP_TARGET_DATA:
> +         case OMP_TARGET_ENTER_DATA:
> +         case OMP_TARGET_EXIT_DATA:
> +         case OACC_HOST_DATA:
> +         check_firstprivate:
> +           if (OMP_CLAUSE_MAP_KIND (c) == GOMP_MAP_FIRSTPRIVATE_POINTER

I think it looks nicer if the OACC_HOST is before OMP_* such that all
OACC_* are together. (In the old code, oacc_enter/exit was treated
differently than OMP_* and OACC_HOST_DATA; your order is a leftover from
that code movement/change.)

* * *

> +         flags = GOVD_MAP | GOVD_EXPLICIT;
> +         if (OMP_CLAUSE_MAP_KIND (c) == GOMP_MAP_ALWAYS_TO
> +             || OMP_CLAUSE_MAP_KIND (c) == GOMP_MAP_ALWAYS_TOFROM)
> +           flags |= GOVD_MAP_ALWAYS_TO;
I know that the code has only been moved, but I wonder whether that
should also include GOMP_MAP_ALWAYS_PRESENT_{TO,TOFROM} as condition.

Tobias

-----------------
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] 19+ messages in thread

* Re: [PATCH v7 5/5] OpenMP/OpenACC: Reorganise OMP map clause handling in gimplify.cc
  2023-12-19 15:41     ` Tobias Burnus
@ 2023-12-20 21:29       ` Julian Brown
  2023-12-21  8:51         ` Tobias Burnus
  0 siblings, 1 reply; 19+ messages in thread
From: Julian Brown @ 2023-12-20 21:29 UTC (permalink / raw)
  To: Tobias Burnus; +Cc: gcc-patches, fortran

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

Hi Tobias,

Thanks for review! Here's a new version of the patch which hopefully
addresses this round of comments.

On Tue, 19 Dec 2023 16:41:54 +0100
Tobias Burnus <tobias@codesourcery.com> wrote:

> On 16.12.23 14:25, Julian Brown wrote:
> > --- a/gcc/gimplify.cc
> > +++ b/gcc/gimplify.cc
> > @@ -10107,6 +10114,20 @@ omp_segregate_mapping_groups
> > (omp_mapping_group *inlist) ard_tail = &w->next;
> >         break;
> >
> > +     case GOMP_MAP_PRESENT_ALLOC:
> > +       *pa_tail = w;
> > +       w->next = NULL;
> > +       pa_tail = &w->next;
> > +       break;
> > +
> > +     case GOMP_MAP_PRESENT_FROM:
> > +     case GOMP_MAP_PRESENT_TO:
> > +     case GOMP_MAP_PRESENT_TOFROM:
> > +       *ptf_tail = w;
> > +       w->next = NULL;
> > +       ptf_tail = &w->next;
> > +       break;
> > +  
> 
> First, I note that GOMP_MAP_PRESENT_ALLOC and
> GOMP_MAP_PRESENT_{FROM,TO,TOFROM} are semantically identical: If the
> variable is not present, error termination will happen - otherwise, if
> present, no data movement will happen. Hence, they will be changed to
> GOMP_MAP_FORCE_PRESENT in gimplify_adjust_omp_clauses.
> 
> That's also the reason that the old code handled all of them
> identical.
> 
> However, besides a plain 'present', there is also 'always' +
> 'present'. Those are different as after a normal 'present' check
> (abort if not present), the data copying will happen:
> GOMP_MAP_ALWAYS_PRESENT_TO, GOMP_MAP_ALWAYS_PRESENT_FROM,
> GOMP_MAP_ALWAYS_PRESENT_TOFROM.
> 
> (Note that: always + present + alloc = GOMP_MAP_PRESENT_ALLOC (w/o
> 'always') as already done in the FE.)
> 
> Thus, all 'case' from your patch should go to a single group (possibly
> adding a comment about it). The question is what to do with the
> 'present,always' case. I think leaving them under 'default:' is fine,
> but I might have missed something.

I've made this change (i.e.: grouping all "GOMP_MAP_PRESENT_*" nodes
together), and in fact that restores the dump output for the
gfortran.dg/gomp/map-12.f90 that needed to be adjusted for the previous
version of the patch (so that hunk has now disappeared).

> >       default:
> >         *tf_tail = w;
> >         w->next = NULL;
> > @@ -10118,8 +10139,10 @@ omp_segregate_mapping_groups
> > (omp_mapping_group *inlist)  
> * * *
> > @@ -11922,119 +11945,30 @@ gimplify_scan_omp_clauses (tree *list_p,
> > gimple_seq *pre_p, break;
> >         }
> >
> > -  if (code == OMP_TARGET
> > -      || code == OMP_TARGET_DATA
> > -      || code == OMP_TARGET_ENTER_DATA
> > -      || code == OMP_TARGET_EXIT_DATA)
> > -    {
> > -      vec<omp_mapping_group> *groups;
> > -      groups = omp_gather_mapping_groups (list_p);
> > -      if (groups)
> > -     {
> > -       hash_map<tree_operand_hash_no_se, omp_mapping_group *>
> > *grpmap;
> > -       grpmap = omp_index_mapping_groups (groups);
> > +  vec<omp_mapping_group> *groups = omp_gather_mapping_groups
> > (list_p);
> > +  hash_map<tree_operand_hash_no_se, omp_mapping_group *> *grpmap =
> > NULL;
> > +  unsigned grpnum = 0;
> > +  tree *grp_start_p = NULL, grp_end = NULL_TREE;  
> 
> ...
> 
> > -  else if (region_type & ORT_ACC)
> > -    {  
> I wonder whether you should not better call
> 'omp_gather_mapping_groups' only for the 'code == OMP_TARGET...' and
> for ORT_ACC (or some subset of OACC *), given that this function is
> also called bygimplify_omp_parallel, gimplify_omp_task,
> gimplify_omp_for, ...
> 
> This avoids some memory allocation and list_p walking, i.e. it is not
> too bad - but also not really needed for task, parallel, for, ...

I've made that change -- OpenACC uses OMP_CLAUSE_MAP in quite a wide
range of directives, but the new version of the patch lists them
individually anyway, rather than using a catch-all for ORT_ACC regions.
That seems OK, I think.

> > @@ -14008,26 +13926,73 @@ gimplify_adjust_omp_clauses (gimple_seq
> > *pre_p, gimple_seq body, tree *list_p, default:
> >             break;
> >           }
> > -       if (code == OMP_TARGET_EXIT_DATA
> > -           && OMP_CLAUSE_MAP_KIND (c) == GOMP_MAP_ALWAYS_POINTER)
> > +       switch (code)
> >           {
> > +         case OMP_TARGET:
> > +           break;
> > +         case OACC_DATA:
> > +           if (TREE_CODE (TREE_TYPE (decl)) != ARRAY_TYPE)
> > +             break;
> > +           goto check_firstprivate;
> > +         case OACC_ENTER_DATA:
> > +         case OACC_EXIT_DATA:
> > +         case OMP_TARGET_DATA:
> > +         case OMP_TARGET_ENTER_DATA:
> > +         case OMP_TARGET_EXIT_DATA:
> > +         case OACC_HOST_DATA:
> > +         check_firstprivate:
> > +           if (OMP_CLAUSE_MAP_KIND (c) ==
> > GOMP_MAP_FIRSTPRIVATE_POINTER  
> 
> I think it looks nicer if the OACC_HOST is before OMP_* such that all
> OACC_* are together. (In the old code, oacc_enter/exit was treated
> differently than OMP_* and OACC_HOST_DATA; your order is a leftover
> from that code movement/change.)

I've fixed this bit -- which actually doesn't need the goto any more
either, so that's now a fallthrough instead.

> > +         flags = GOVD_MAP | GOVD_EXPLICIT;
> > +         if (OMP_CLAUSE_MAP_KIND (c) == GOMP_MAP_ALWAYS_TO
> > +             || OMP_CLAUSE_MAP_KIND (c) == GOMP_MAP_ALWAYS_TOFROM)
> > +           flags |= GOVD_MAP_ALWAYS_TO;  
> I know that the code has only been moved, but I wonder whether that
> should also include GOMP_MAP_ALWAYS_PRESENT_{TO,TOFROM} as condition.

I've added it (caveat: without any tests).

Re-tested with offloading to NVPTX. OK now?

Thanks,

Julian

[-- Attachment #2: omp-clause-gimplify-reorg-9.diff --]
[-- Type: text/x-patch, Size: 32308 bytes --]

commit 968b0f47c5782a6c0a9f514c14199a97003dfb62
Author: Julian Brown <julian@codesourcery.com>
Date:   Tue May 23 09:37:00 2023 +0000

    OpenMP/OpenACC: Reorganise OMP map clause handling in gimplify.cc
    
    This patch has been separated out from the C++ "declare mapper"
    support patch.  It contains just the gimplify.cc rearrangement
    work, mostly moving gimplification from gimplify_scan_omp_clauses
    to gimplify_adjust_omp_clauses for map clauses.
    
    The motivation for doing this was that we don't know if we need to
    instantiate mappers implicitly until the body of an offload region has
    been scanned, i.e. in gimplify_adjust_omp_clauses, but we also need the
    un-gimplified form of clauses to sort by base-pointer dependencies after
    mapper instantiation has taken place.
    
    The patch also reimplements the "present" clause sorting code to avoid
    another sorting pass on mapping nodes.
    
    This version of the patch is based on the version posted for og13, and
    additionally incorporates a follow-on fix for DECL_VALUE_EXPR handling
    in gimplify_adjust_omp_clauses:
    
    "OpenMP/OpenACC: Reorganise OMP map clause handling in gimplify.cc"
    https://gcc.gnu.org/pipermail/gcc-patches/2023-June/622223.html
    
    Parts of:
    "OpenMP: OpenMP 5.2 semantics for pointers with unmapped target"
    https://gcc.gnu.org/pipermail/gcc-patches/2023-June/623351.html
    
    2023-12-16  Julian Brown  <julian@codesourcery.com>
    
    gcc/
            * gimplify.cc (omp_segregate_mapping_groups): Handle "present" groups.
            (gimplify_scan_omp_clauses): Use mapping group functionality to
            iterate through mapping nodes.  Remove most gimplification of
            OMP_CLAUSE_MAP nodes from here, but still populate ctx->variables
            splay tree.
            (gimplify_adjust_omp_clauses): Move most gimplification of
            OMP_CLAUSE_MAP nodes here.
    
    libgomp/
            * testsuite/libgomp.fortran/target-enter-data-6.f90: Remove XFAIL.

diff --git a/gcc/gimplify.cc b/gcc/gimplify.cc
index 12c06b219b7..f71abedcb30 100644
--- a/gcc/gimplify.cc
+++ b/gcc/gimplify.cc
@@ -10077,10 +10077,14 @@ omp_tsort_mapping_groups (vec<omp_mapping_group> *groups,
   return outlist;
 }
 
-/* Split INLIST into two parts, moving groups corresponding to
-   ALLOC/RELEASE/DELETE mappings to one list, and other mappings to another.
-   The former list is then appended to the latter.  Each sub-list retains the
-   order of the original list.
+/* Split INLIST into three parts:
+
+     - "present" alloc/to/from groups
+     - other to/from groups
+     - other alloc/release/delete groups
+
+   These sub-lists are then concatenated together to form the final list.
+   Each sub-list retains the order of the original list.
    Note that ATTACH nodes are later moved to the end of the list in
    gimplify_adjust_omp_clauses, for target regions.  */
 
@@ -10088,7 +10092,9 @@ static omp_mapping_group *
 omp_segregate_mapping_groups (omp_mapping_group *inlist)
 {
   omp_mapping_group *ard_groups = NULL, *tf_groups = NULL;
+  omp_mapping_group *p_groups = NULL;
   omp_mapping_group **ard_tail = &ard_groups, **tf_tail = &tf_groups;
+  omp_mapping_group **p_tail = &p_groups;
 
   for (omp_mapping_group *w = inlist; w;)
     {
@@ -10107,6 +10113,18 @@ omp_segregate_mapping_groups (omp_mapping_group *inlist)
 	  ard_tail = &w->next;
 	  break;
 
+	/* These map types are all semantically identical, so are moved into a
+	   single group.  They will each be changed into GOMP_MAP_FORCE_PRESENT
+	   in gimplify_adjust_omp_clauses.  */
+	case GOMP_MAP_PRESENT_ALLOC:
+	case GOMP_MAP_PRESENT_FROM:
+	case GOMP_MAP_PRESENT_TO:
+	case GOMP_MAP_PRESENT_TOFROM:
+	  *p_tail = w;
+	  w->next = NULL;
+	  p_tail = &w->next;
+	  break;
+
 	default:
 	  *tf_tail = w;
 	  w->next = NULL;
@@ -10118,8 +10136,9 @@ omp_segregate_mapping_groups (omp_mapping_group *inlist)
 
   /* Now splice the lists together...  */
   *tf_tail = ard_groups;
+  *p_tail = tf_groups;
 
-  return tf_groups;
+  return p_groups;
 }
 
 /* Given a list LIST_P containing groups of mappings given by GROUPS, reorder
@@ -11922,119 +11941,46 @@ gimplify_scan_omp_clauses (tree *list_p, gimple_seq *pre_p,
 	break;
       }
 
+  vec<omp_mapping_group> *groups = NULL;
+  hash_map<tree_operand_hash_no_se, omp_mapping_group *> *grpmap = NULL;
+  unsigned grpnum = 0;
+  tree *grp_start_p = NULL, grp_end = NULL_TREE;
+
   if (code == OMP_TARGET
       || code == OMP_TARGET_DATA
       || code == OMP_TARGET_ENTER_DATA
-      || code == OMP_TARGET_EXIT_DATA)
+      || code == OMP_TARGET_EXIT_DATA
+      || code == OACC_DATA
+      || code == OACC_KERNELS
+      || code == OACC_PARALLEL
+      || code == OACC_SERIAL
+      || code == OACC_ENTER_DATA
+      || code == OACC_EXIT_DATA
+      || code == OACC_UPDATE
+      || code == OACC_DECLARE)
     {
-      vec<omp_mapping_group> *groups;
       groups = omp_gather_mapping_groups (list_p);
+
       if (groups)
-	{
-	  hash_map<tree_operand_hash_no_se, omp_mapping_group *> *grpmap;
-	  grpmap = omp_index_mapping_groups (groups);
-
-	  omp_resolve_clause_dependencies (code, groups, grpmap);
-	  omp_build_struct_sibling_lists (code, region_type, groups, &grpmap,
-					  list_p);
-
-	  omp_mapping_group *outlist = NULL;
-	  bool enter_exit = (code == OMP_TARGET_ENTER_DATA
-			     || code == OMP_TARGET_EXIT_DATA);
-
-	  /* Topological sorting may fail if we have duplicate nodes, which
-	     we should have detected and shown an error for already.  Skip
-	     sorting in that case.  */
-	  if (seen_error ())
-	    goto failure;
-
-	  delete grpmap;
-	  delete groups;
-
-	  /* Rebuild now we have struct sibling lists.  */
-	  groups = omp_gather_mapping_groups (list_p);
-	  grpmap = omp_index_mapping_groups (groups);
-
-	  outlist = omp_tsort_mapping_groups (groups, grpmap, enter_exit);
-	  outlist = omp_segregate_mapping_groups (outlist);
-	  list_p = omp_reorder_mapping_groups (groups, outlist, list_p);
-
-	failure:
-	  delete grpmap;
-	  delete groups;
-	}
-
-      /* OpenMP map clauses with 'present' need to go in front of those
-	 without.  */
-      tree present_map_head = NULL;
-      tree *present_map_tail_p = &present_map_head;
-      tree *first_map_clause_p = NULL;
-
-      for (tree *c_p = list_p; *c_p; )
-	{
-	  tree c = *c_p;
-	  tree *next_c_p = &OMP_CLAUSE_CHAIN (c);
-
-	  if (OMP_CLAUSE_CODE (c) == OMP_CLAUSE_MAP)
-	    {
-	      if (!first_map_clause_p)
-		first_map_clause_p = c_p;
-	      switch (OMP_CLAUSE_MAP_KIND (c))
-		{
-		case GOMP_MAP_PRESENT_ALLOC:
-		case GOMP_MAP_PRESENT_FROM:
-		case GOMP_MAP_PRESENT_TO:
-		case GOMP_MAP_PRESENT_TOFROM:
-		  next_c_p = c_p;
-		  *c_p = OMP_CLAUSE_CHAIN (c);
-
-		  OMP_CLAUSE_CHAIN (c) = NULL;
-		  *present_map_tail_p = c;
-		  present_map_tail_p = &OMP_CLAUSE_CHAIN (c);
-
-		  break;
-
-		default:
-		  break;
-		}
-	    }
-
-	  c_p = next_c_p;
-	}
-      if (first_map_clause_p && present_map_head)
-	{
-	  tree next = *first_map_clause_p;
-	  *first_map_clause_p = present_map_head;
-	  *present_map_tail_p = next;
-	}
-    }
-  else if (region_type & ORT_ACC)
-    {
-      vec<omp_mapping_group> *groups;
-      groups = omp_gather_mapping_groups (list_p);
-      if (groups)
-	{
-	  hash_map<tree_operand_hash_no_se, omp_mapping_group *> *grpmap;
-	  grpmap = omp_index_mapping_groups (groups);
-
-	  oacc_resolve_clause_dependencies (groups, grpmap);
-	  omp_build_struct_sibling_lists (code, region_type, groups, &grpmap,
-					  list_p);
-
-	  delete groups;
-	  delete grpmap;
-	}
+	grpmap = omp_index_mapping_groups (groups);
     }
 
   while ((c = *list_p) != NULL)
     {
       bool remove = false;
       bool notice_outer = true;
+      bool map_descriptor;
       const char *check_non_private = NULL;
       unsigned int flags;
       tree decl;
       auto_vec<omp_addr_token *, 10> addr_tokens;
 
+      if (grp_end && c == OMP_CLAUSE_CHAIN (grp_end))
+	{
+	  grp_start_p = NULL;
+	  grp_end = NULL_TREE;
+	}
+
       switch (OMP_CLAUSE_CODE (c))
 	{
 	case OMP_CLAUSE_PRIVATE:
@@ -12339,45 +12285,26 @@ gimplify_scan_omp_clauses (tree *list_p, gimple_seq *pre_p,
 	  goto do_add;
 
 	case OMP_CLAUSE_MAP:
+	  if (!grp_start_p)
+	    {
+	      grp_start_p = list_p;
+	      grp_end = (*groups)[grpnum].grp_end;
+	      grpnum++;
+	    }
 	  decl = OMP_CLAUSE_DECL (c);
 
+	  if (error_operand_p (decl))
+	    {
+	      remove = true;
+	      break;
+	    }
+
 	  if (!omp_parse_expr (addr_tokens, decl))
 	    {
 	      remove = true;
 	      break;
 	    }
 
-	  if (error_operand_p (decl))
-	    remove = true;
-	  switch (code)
-	    {
-	    case OMP_TARGET:
-	      break;
-	    case OACC_DATA:
-	      if (TREE_CODE (TREE_TYPE (decl)) != ARRAY_TYPE)
-		break;
-	      goto check_firstprivate;
-	    case OACC_ENTER_DATA:
-	    case OACC_EXIT_DATA:
-	      if (OMP_CLAUSE_MAP_KIND (c) == GOMP_MAP_ATTACH_DETACH
-		  && addr_tokens[0]->type == ARRAY_BASE)
-		remove = true;
-	      /* FALLTHRU */
-	    case OMP_TARGET_DATA:
-	    case OMP_TARGET_ENTER_DATA:
-	    case OMP_TARGET_EXIT_DATA:
-	    case OACC_HOST_DATA:
-	    check_firstprivate:
-	      if (OMP_CLAUSE_MAP_KIND (c) == GOMP_MAP_FIRSTPRIVATE_POINTER
-		  || (OMP_CLAUSE_MAP_KIND (c)
-		      == GOMP_MAP_FIRSTPRIVATE_REFERENCE))
-		/* For target {,enter ,exit }data only the array slice is
-		   mapped, but not the pointer to it.  */
-		remove = true;
-	      break;
-	    default:
-	      break;
-	    }
 	  if (remove)
 	    break;
 	  if (DECL_P (decl) && outer_ctx && (region_type & ORT_ACC))
@@ -12396,52 +12323,61 @@ gimplify_scan_omp_clauses (tree *list_p, gimple_seq *pre_p,
 			      DECL_NAME (decl));
 		}
 	    }
-	  if (OMP_CLAUSE_SIZE (c) == NULL_TREE)
-	    {
-	      /* Sanity check: attach/detach map kinds use the size as a bias,
-		 and it's never right to use the decl size for such
-		 mappings.  */
-	      gcc_assert (OMP_CLAUSE_MAP_KIND (c) != GOMP_MAP_ATTACH
-			  && OMP_CLAUSE_MAP_KIND (c) != GOMP_MAP_DETACH
-			  && OMP_CLAUSE_MAP_KIND (c) != GOMP_MAP_FORCE_DETACH
-			  && OMP_CLAUSE_MAP_KIND (c) != GOMP_MAP_ATTACH_DETACH
-			  && (OMP_CLAUSE_MAP_KIND (c)
-			      != GOMP_MAP_ATTACH_ZERO_LENGTH_ARRAY_SECTION));
-	      OMP_CLAUSE_SIZE (c) = DECL_P (decl) ? DECL_SIZE_UNIT (decl)
-				    : TYPE_SIZE_UNIT (TREE_TYPE (decl));
-	    }
-	  if (gimplify_expr (&OMP_CLAUSE_SIZE (c), pre_p,
-			     NULL, is_gimple_val, fb_rvalue) == GS_ERROR)
-	    {
-	      remove = true;
-	      break;
-	    }
-	  else if ((OMP_CLAUSE_MAP_KIND (c) == GOMP_MAP_FIRSTPRIVATE_POINTER
-		    || (OMP_CLAUSE_MAP_KIND (c)
-			== GOMP_MAP_FIRSTPRIVATE_REFERENCE)
-		    || OMP_CLAUSE_MAP_KIND (c) == GOMP_MAP_ATTACH_DETACH)
-		   && TREE_CODE (OMP_CLAUSE_SIZE (c)) != INTEGER_CST)
-	    {
-	      OMP_CLAUSE_SIZE (c)
-		= get_initialized_tmp_var (OMP_CLAUSE_SIZE (c), pre_p, NULL,
-					   false);
-	      if ((region_type & ORT_TARGET) != 0)
-		omp_add_variable (ctx, OMP_CLAUSE_SIZE (c),
-				  GOVD_FIRSTPRIVATE | GOVD_SEEN);
-	    }
 
-	  if ((OMP_CLAUSE_MAP_KIND (c) == GOMP_MAP_STRUCT
-	       || OMP_CLAUSE_MAP_KIND (c) == GOMP_MAP_STRUCT_UNORD)
-	      && (addr_tokens[0]->type == STRUCTURE_BASE
-		  || addr_tokens[0]->type == ARRAY_BASE)
-	      && addr_tokens[0]->u.structure_base_kind == BASE_DECL)
+	  map_descriptor = false;
+
+	  /* This condition checks if we're mapping an array descriptor that
+	     isn't inside a derived type -- these have special handling, and
+	     are not handled as structs in omp_build_struct_sibling_lists.
+	     See that function for further details.  */
+	  if (*grp_start_p != grp_end
+	      && OMP_CLAUSE_CHAIN (*grp_start_p)
+	      && OMP_CLAUSE_CHAIN (*grp_start_p) != grp_end)
+	    {
+	      tree grp_mid = OMP_CLAUSE_CHAIN (*grp_start_p);
+	      if (omp_map_clause_descriptor_p (grp_mid)
+		  && DECL_P (OMP_CLAUSE_DECL (grp_mid)))
+		map_descriptor = true;
+	    }
+	  else if (OMP_CLAUSE_CODE (grp_end) == OMP_CLAUSE_MAP
+		   && (OMP_CLAUSE_MAP_KIND (grp_end) == GOMP_MAP_RELEASE
+		       || OMP_CLAUSE_MAP_KIND (grp_end) == GOMP_MAP_DELETE)
+		   && OMP_CLAUSE_RELEASE_DESCRIPTOR (grp_end))
+	    map_descriptor = true;
+
+	  /* Adding the decl for a struct access: we haven't created
+	     GOMP_MAP_STRUCT nodes yet, so this statement needs to predict
+	     whether they will be created in gimplify_adjust_omp_clauses.
+	     NOTE: Technically we should probably look through DECL_VALUE_EXPR
+	     here because something that looks like a DECL_P may actually be a
+	     struct access, e.g. variables in a lambda closure
+	     (__closure->__foo) or class members (this->foo). Currently in both
+	     those cases we map the whole of the containing object (directly in
+	     the C++ FE) though, so struct nodes are not created.  */
+	  if (c == grp_end
+	      && addr_tokens[0]->type == STRUCTURE_BASE
+	      && addr_tokens[0]->u.structure_base_kind == BASE_DECL
+	      && !map_descriptor)
 	    {
 	      gcc_assert (addr_tokens[1]->type == ACCESS_METHOD);
 	      /* If we got to this struct via a chain of pointers, maybe we
 		 want to map it implicitly instead.  */
 	      if (omp_access_chain_p (addr_tokens, 1))
 		break;
+	      omp_mapping_group *wholestruct;
+	      if (!(region_type & ORT_ACC)
+		  && omp_mapped_by_containing_struct (grpmap,
+						      OMP_CLAUSE_DECL (c),
+						      &wholestruct))
+		break;
 	      decl = addr_tokens[1]->expr;
+	      if (splay_tree_lookup (ctx->variables, (splay_tree_key) decl))
+		break;
+	      /* Standalone attach or detach clauses for a struct element
+		 should not inhibit implicit mapping of the whole struct.  */
+	      if (OMP_CLAUSE_MAP_KIND (c) == GOMP_MAP_ATTACH
+		  || OMP_CLAUSE_MAP_KIND (c) == GOMP_MAP_DETACH)
+		break;
 	      flags = GOVD_MAP | GOVD_EXPLICIT;
 
 	      gcc_assert (addr_tokens[1]->u.access_kind != ACCESS_DIRECT
@@ -12449,14 +12385,7 @@ gimplify_scan_omp_clauses (tree *list_p, gimple_seq *pre_p,
 	      goto do_add_decl;
 	    }
 
-	  if (TREE_CODE (decl) == TARGET_EXPR)
-	    {
-	      if (gimplify_expr (&OMP_CLAUSE_DECL (c), pre_p, NULL,
-				 is_gimple_lvalue, fb_lvalue)
-		  == GS_ERROR)
-		remove = true;
-	    }
-	  else if (!DECL_P (decl))
+	  if (!DECL_P (decl))
 	    {
 	      tree d = decl, *pd;
 	      if (TREE_CODE (d) == ARRAY_REF)
@@ -12479,56 +12408,20 @@ gimplify_scan_omp_clauses (tree *list_p, gimple_seq *pre_p,
 		  pd = &TREE_OPERAND (decl, 0);
 		  decl = TREE_OPERAND (decl, 0);
 		}
-	      /* An "attach/detach" operation on an update directive should
-		 behave as a GOMP_MAP_ALWAYS_POINTER.  Beware that
-		 unlike attach or detach map kinds, GOMP_MAP_ALWAYS_POINTER
-		 depends on the previous mapping.  */
-	      if (code == OACC_UPDATE
-		  && OMP_CLAUSE_MAP_KIND (c) == GOMP_MAP_ATTACH_DETACH)
-		OMP_CLAUSE_SET_MAP_KIND (c, GOMP_MAP_ALWAYS_POINTER);
 
-	      if (OMP_CLAUSE_MAP_KIND (c) == GOMP_MAP_ATTACH_DETACH)
+	      if (addr_tokens[0]->type == STRUCTURE_BASE
+		  && addr_tokens[0]->u.structure_base_kind == BASE_DECL
+		  && addr_tokens[1]->type == ACCESS_METHOD
+		  && (addr_tokens[1]->u.access_kind == ACCESS_POINTER
+		      || (addr_tokens[1]->u.access_kind
+			  == ACCESS_POINTER_OFFSET))
+		  && GOMP_MAP_ALWAYS_P (OMP_CLAUSE_MAP_KIND (c)))
 		{
-		  if (TREE_CODE (TREE_TYPE (OMP_CLAUSE_DECL (c)))
-		      == ARRAY_TYPE)
-		    remove = true;
-		  else
-		    {
-		      gomp_map_kind k = ((code == OACC_EXIT_DATA
-					  || code == OMP_TARGET_EXIT_DATA)
-					 ? GOMP_MAP_DETACH : GOMP_MAP_ATTACH);
-		      OMP_CLAUSE_SET_MAP_KIND (c, k);
-		    }
-		}
-
-	      tree cref = decl;
-
-	      while (TREE_CODE (cref) == ARRAY_REF)
-		cref = TREE_OPERAND (cref, 0);
-
-	      if (TREE_CODE (cref) == INDIRECT_REF)
-		cref = TREE_OPERAND (cref, 0);
-
-	      if (TREE_CODE (cref) == COMPONENT_REF)
-		{
-		  tree base = cref;
-		  while (base && !DECL_P (base))
-		    {
-		      tree innerbase = omp_get_base_pointer (base);
-		      if (!innerbase)
-			break;
-		      base = innerbase;
-		    }
-		  if (base
-		      && DECL_P (base)
-		      && GOMP_MAP_ALWAYS_P (OMP_CLAUSE_MAP_KIND (c))
-		      && POINTER_TYPE_P (TREE_TYPE (base)))
-		    {
-		      splay_tree_node n
-			= splay_tree_lookup (ctx->variables,
-					     (splay_tree_key) base);
-		      n->value |= GOVD_SEEN;
-		    }
+		  tree base = addr_tokens[1]->expr;
+		  splay_tree_node n
+		    = splay_tree_lookup (ctx->variables,
+					 (splay_tree_key) base);
+		  n->value |= GOVD_SEEN;
 		}
 
 	      if (code == OMP_TARGET && OMP_CLAUSE_MAP_IN_REDUCTION (c))
@@ -12639,18 +12532,8 @@ gimplify_scan_omp_clauses (tree *list_p, gimple_seq *pre_p,
 			}
 		    }
 		}
-	      else if (gimplify_expr (pd, pre_p, NULL, is_gimple_lvalue,
-				      fb_lvalue) == GS_ERROR)
-		{
-		  remove = true;
-		  break;
-		}
 	      break;
 	    }
-	  flags = GOVD_MAP | GOVD_EXPLICIT;
-	  if (OMP_CLAUSE_MAP_KIND (c) == GOMP_MAP_ALWAYS_TO
-	      || OMP_CLAUSE_MAP_KIND (c) == GOMP_MAP_ALWAYS_TOFROM)
-	    flags |= GOVD_MAP_ALWAYS_TO;
 
 	  if ((code == OMP_TARGET
 	       || code == OMP_TARGET_DATA
@@ -12658,26 +12541,6 @@ gimplify_scan_omp_clauses (tree *list_p, gimple_seq *pre_p,
 	       || code == OMP_TARGET_EXIT_DATA)
 	      && OMP_CLAUSE_MAP_KIND (c) == GOMP_MAP_ATTACH_DETACH)
 	    {
-	      for (struct gimplify_omp_ctx *octx = outer_ctx; octx;
-		   octx = octx->outer_context)
-		{
-		  splay_tree_node n
-		    = splay_tree_lookup (octx->variables,
-					 (splay_tree_key) OMP_CLAUSE_DECL (c));
-		  /* If this is contained in an outer OpenMP region as a
-		     firstprivate value, remove the attach/detach.  */
-		  if (n && (n->value & GOVD_FIRSTPRIVATE))
-		    {
-		      OMP_CLAUSE_SET_MAP_KIND (c, GOMP_MAP_FIRSTPRIVATE_POINTER);
-		      goto do_add;
-		    }
-		}
-
-	      enum gomp_map_kind map_kind = (code == OMP_TARGET_EXIT_DATA
-					     ? GOMP_MAP_DETACH
-					     : GOMP_MAP_ATTACH);
-	      OMP_CLAUSE_SET_MAP_KIND (c, map_kind);
-
 	      /* If we have attach/detach but the decl we have is a pointer to
 		 pointer, we're probably mapping the "base level" array
 		 implicitly.  Make sure we don't add the decl as if we mapped
@@ -12704,16 +12567,13 @@ gimplify_scan_omp_clauses (tree *list_p, gimple_seq *pre_p,
 		  && TREE_CODE (TREE_TYPE (basetype)) == POINTER_TYPE)
 		break;
 	    }
-	  else if ((code == OACC_ENTER_DATA
-		    || code == OACC_EXIT_DATA
-		    || code == OACC_PARALLEL)
-		   && OMP_CLAUSE_MAP_KIND (c) == GOMP_MAP_ATTACH_DETACH)
-	    {
-	      enum gomp_map_kind map_kind = (code == OACC_EXIT_DATA
-					     ? GOMP_MAP_DETACH
-					     : GOMP_MAP_ATTACH);
-	      OMP_CLAUSE_SET_MAP_KIND (c, map_kind);
-	    }
+
+	  flags = GOVD_MAP | GOVD_EXPLICIT;
+	  if (OMP_CLAUSE_MAP_KIND (c) == GOMP_MAP_ALWAYS_TO
+	      || OMP_CLAUSE_MAP_KIND (c) == GOMP_MAP_ALWAYS_TOFROM
+	      || OMP_CLAUSE_MAP_KIND (c) == GOMP_MAP_ALWAYS_PRESENT_TO
+	      || OMP_CLAUSE_MAP_KIND (c) == GOMP_MAP_ALWAYS_PRESENT_TOFROM)
+	    flags |= GOVD_MAP_ALWAYS_TO;
 
 	  goto do_add;
 
@@ -13355,6 +13215,12 @@ gimplify_scan_omp_clauses (tree *list_p, gimple_seq *pre_p,
 	list_p = &OMP_CLAUSE_CHAIN (c);
     }
 
+  if (groups)
+    {
+      delete grpmap;
+      delete groups;
+    }
+
   ctx->clauses = *orig_list_p;
   gimplify_omp_ctxp = ctx;
 }
@@ -13834,15 +13700,75 @@ gimplify_adjust_omp_clauses (gimple_seq *pre_p, gimple_seq body, tree *list_p,
 	  }
     }
 
+  if (code == OMP_TARGET
+      || code == OMP_TARGET_DATA
+      || code == OMP_TARGET_ENTER_DATA
+      || code == OMP_TARGET_EXIT_DATA)
+    {
+      vec<omp_mapping_group> *groups;
+      groups = omp_gather_mapping_groups (list_p);
+      hash_map<tree_operand_hash_no_se, omp_mapping_group *> *grpmap = NULL;
+
+      if (groups)
+	{
+	  grpmap = omp_index_mapping_groups (groups);
+
+	  omp_resolve_clause_dependencies (code, groups, grpmap);
+	  omp_build_struct_sibling_lists (code, ctx->region_type, groups,
+					  &grpmap, list_p);
+
+	  omp_mapping_group *outlist = NULL;
+
+	  delete grpmap;
+	  delete groups;
+
+	  /* Rebuild now we have struct sibling lists.  */
+	  groups = omp_gather_mapping_groups (list_p);
+	  grpmap = omp_index_mapping_groups (groups);
+
+	  bool enter_exit = (code == OMP_TARGET_ENTER_DATA
+			     || code == OMP_TARGET_EXIT_DATA);
+
+	  outlist = omp_tsort_mapping_groups (groups, grpmap, enter_exit);
+	  outlist = omp_segregate_mapping_groups (outlist);
+	  list_p = omp_reorder_mapping_groups (groups, outlist, list_p);
+
+	  delete grpmap;
+	  delete groups;
+	}
+    }
+  else if (ctx->region_type & ORT_ACC)
+    {
+      vec<omp_mapping_group> *groups;
+      groups = omp_gather_mapping_groups (list_p);
+      if (groups)
+	{
+	  hash_map<tree_operand_hash_no_se, omp_mapping_group *> *grpmap;
+	  grpmap = omp_index_mapping_groups (groups);
+
+	  oacc_resolve_clause_dependencies (groups, grpmap);
+	  omp_build_struct_sibling_lists (code, ctx->region_type, groups,
+					  &grpmap, list_p);
+
+	  delete groups;
+	  delete grpmap;
+	}
+    }
+
   tree attach_list = NULL_TREE;
   tree *attach_tail = &attach_list;
 
+  tree *grp_start_p = NULL, grp_end = NULL_TREE;
+
   while ((c = *list_p) != NULL)
     {
       splay_tree_node n;
       bool remove = false;
       bool move_attach = false;
 
+      if (grp_end && c == OMP_CLAUSE_CHAIN (grp_end))
+	grp_end = NULL_TREE;
+
       switch (OMP_CLAUSE_CODE (c))
 	{
 	case OMP_CLAUSE_FIRSTPRIVATE:
@@ -13997,6 +13923,12 @@ gimplify_adjust_omp_clauses (gimple_seq *pre_p, gimple_seq body, tree *list_p,
 	  break;
 
 	case OMP_CLAUSE_MAP:
+	  decl = OMP_CLAUSE_DECL (c);
+	  if (!grp_end)
+	    {
+	      grp_start_p = list_p;
+	      grp_end = *omp_group_last (grp_start_p);
+	    }
 	  switch (OMP_CLAUSE_MAP_KIND (c))
 	    {
 	    case GOMP_MAP_PRESENT_ALLOC:
@@ -14008,26 +13940,72 @@ gimplify_adjust_omp_clauses (gimple_seq *pre_p, gimple_seq body, tree *list_p,
 	    default:
 	      break;
 	    }
-	  if (code == OMP_TARGET_EXIT_DATA
-	      && OMP_CLAUSE_MAP_KIND (c) == GOMP_MAP_ALWAYS_POINTER)
+	  switch (code)
 	    {
+	    case OACC_DATA:
+	      if (TREE_CODE (TREE_TYPE (decl)) != ARRAY_TYPE)
+		break;
+	      /* Fallthrough.  */
+	    case OACC_HOST_DATA:
+	    case OACC_ENTER_DATA:
+	    case OACC_EXIT_DATA:
+	    case OMP_TARGET_DATA:
+	    case OMP_TARGET_ENTER_DATA:
+	    case OMP_TARGET_EXIT_DATA:
+	      if (OMP_CLAUSE_MAP_KIND (c) == GOMP_MAP_FIRSTPRIVATE_POINTER
+		  || (OMP_CLAUSE_MAP_KIND (c)
+		      == GOMP_MAP_FIRSTPRIVATE_REFERENCE))
+		/* For target {,enter ,exit }data only the array slice is
+		   mapped, but not the pointer to it.  */
+		remove = true;
+	      if (code == OMP_TARGET_EXIT_DATA
+		  && (OMP_CLAUSE_MAP_KIND (c) == GOMP_MAP_ALWAYS_POINTER
+		      || OMP_CLAUSE_MAP_KIND (c) == GOMP_MAP_POINTER))
+		remove = true;
+	      break;
+	    case OMP_TARGET:
+	      break;
+	    default:
+	      break;
+	    }
+	  if (remove)
+	    break;
+	  if (OMP_CLAUSE_SIZE (c) == NULL_TREE)
+	    {
+	      /* Sanity check: attach/detach map kinds use the size as a bias,
+		 and it's never right to use the decl size for such
+		 mappings.  */
+	      gcc_assert (OMP_CLAUSE_MAP_KIND (c) != GOMP_MAP_ATTACH
+			  && OMP_CLAUSE_MAP_KIND (c) != GOMP_MAP_DETACH
+			  && OMP_CLAUSE_MAP_KIND (c) != GOMP_MAP_FORCE_DETACH
+			  && OMP_CLAUSE_MAP_KIND (c) != GOMP_MAP_ATTACH_DETACH
+			  && (OMP_CLAUSE_MAP_KIND (c)
+			      != GOMP_MAP_ATTACH_ZERO_LENGTH_ARRAY_SECTION));
+	      OMP_CLAUSE_SIZE (c) = DECL_P (decl) ? DECL_SIZE_UNIT (decl)
+				    : TYPE_SIZE_UNIT (TREE_TYPE (decl));
+	    }
+	  gimplify_omp_ctxp = ctx->outer_context;
+	  if (gimplify_expr (&OMP_CLAUSE_SIZE (c), pre_p, NULL,
+				  is_gimple_val, fb_rvalue) == GS_ERROR)
+	    {
+	      gimplify_omp_ctxp = ctx;
 	      remove = true;
 	      break;
 	    }
-	  /* If we have a target region, we can push all the attaches to the
-	     end of the list (we may have standalone "attach" operations
-	     synthesized for GOMP_MAP_STRUCT nodes that must be processed after
-	     the attachment point AND the pointed-to block have been mapped).
-	     If we have something else, e.g. "enter data", we need to keep
-	     "attach" nodes together with the previous node they attach to so
-	     that separate "exit data" operations work properly (see
-	     libgomp/target.c).  */
-	  if ((ctx->region_type & ORT_TARGET) != 0
-	      && (OMP_CLAUSE_MAP_KIND (c) == GOMP_MAP_ATTACH
-		  || (OMP_CLAUSE_MAP_KIND (c)
-		      == GOMP_MAP_ATTACH_ZERO_LENGTH_ARRAY_SECTION)))
-	    move_attach = true;
-	  decl = OMP_CLAUSE_DECL (c);
+	  else if ((OMP_CLAUSE_MAP_KIND (c) == GOMP_MAP_FIRSTPRIVATE_POINTER
+		    || (OMP_CLAUSE_MAP_KIND (c)
+			== GOMP_MAP_FIRSTPRIVATE_REFERENCE)
+		    || OMP_CLAUSE_MAP_KIND (c) == GOMP_MAP_ATTACH_DETACH)
+		   && TREE_CODE (OMP_CLAUSE_SIZE (c)) != INTEGER_CST)
+	    {
+	      OMP_CLAUSE_SIZE (c)
+		= get_initialized_tmp_var (OMP_CLAUSE_SIZE (c), pre_p, NULL,
+					   false);
+	      if ((ctx->region_type & ORT_TARGET) != 0)
+		omp_add_variable (ctx, OMP_CLAUSE_SIZE (c),
+				  GOVD_FIRSTPRIVATE | GOVD_SEEN);
+	    }
+	  gimplify_omp_ctxp = ctx;
 	  /* Data clauses associated with reductions must be
 	     compatible with present_or_copy.  Warn and adjust the clause
 	     if that is not the case.  */
@@ -14064,7 +14042,25 @@ gimplify_adjust_omp_clauses (gimple_seq *pre_p, gimple_seq body, tree *list_p,
 	      remove = true;
 	      break;
 	    }
-	  if (!DECL_P (decl))
+	  /* If we have a DECL_VALUE_EXPR (e.g. this is a class member and/or
+	     a variable captured in a lambda closure), look through that now
+	     before the DECL_P check below.  (A code other than COMPONENT_REF,
+	     i.e. INDIRECT_REF, will be a VLA/variable-length array
+	     section.  A global var may be a variable in a common block.  We
+	     don't want to do this here for either of those.)  */
+	  if ((ctx->region_type & ORT_ACC) == 0
+	      && DECL_P (decl)
+	      && !is_global_var (decl)
+	      && DECL_HAS_VALUE_EXPR_P (decl)
+	      && TREE_CODE (DECL_VALUE_EXPR (decl)) == COMPONENT_REF)
+	    decl = OMP_CLAUSE_DECL (c) = DECL_VALUE_EXPR (decl);
+	  if (TREE_CODE (decl) == TARGET_EXPR)
+	    {
+	      if (gimplify_expr (&OMP_CLAUSE_DECL (c), pre_p, NULL,
+				 is_gimple_lvalue, fb_lvalue) == GS_ERROR)
+		remove = true;
+	    }
+	  else if (!DECL_P (decl))
 	    {
 	      if ((ctx->region_type & ORT_TARGET) != 0
 		  && OMP_CLAUSE_MAP_KIND (c) == GOMP_MAP_FIRSTPRIVATE_POINTER)
@@ -14087,8 +14083,122 @@ gimplify_adjust_omp_clauses (gimple_seq *pre_p, gimple_seq body, tree *list_p,
 			}
 		    }
 		}
+
+	      tree d = decl, *pd;
+	      if (TREE_CODE (d) == ARRAY_REF)
+		{
+		  while (TREE_CODE (d) == ARRAY_REF)
+		    d = TREE_OPERAND (d, 0);
+		  if (TREE_CODE (d) == COMPONENT_REF
+		      && TREE_CODE (TREE_TYPE (d)) == ARRAY_TYPE)
+		    decl = d;
+		}
+	      pd = &OMP_CLAUSE_DECL (c);
+	      if (d == decl
+		  && TREE_CODE (decl) == INDIRECT_REF
+		  && TREE_CODE (TREE_OPERAND (decl, 0)) == COMPONENT_REF
+		  && (TREE_CODE (TREE_TYPE (TREE_OPERAND (decl, 0)))
+		      == REFERENCE_TYPE)
+		  && (OMP_CLAUSE_MAP_KIND (c)
+		      != GOMP_MAP_POINTER_TO_ZERO_LENGTH_ARRAY_SECTION))
+		{
+		  pd = &TREE_OPERAND (decl, 0);
+		  decl = TREE_OPERAND (decl, 0);
+		}
+
+	      if (OMP_CLAUSE_MAP_KIND (c) == GOMP_MAP_ATTACH_DETACH)
+		switch (code)
+		  {
+		  case OACC_ENTER_DATA:
+		  case OACC_EXIT_DATA:
+		    if (TREE_CODE (TREE_TYPE (OMP_CLAUSE_DECL (c)))
+			== ARRAY_TYPE)
+		      remove = true;
+		    else if (code == OACC_ENTER_DATA)
+		      goto change_to_attach;
+		    /* Fallthrough.  */
+		  case OMP_TARGET_EXIT_DATA:
+		    OMP_CLAUSE_SET_MAP_KIND (c, GOMP_MAP_DETACH);
+		    break;
+		  case OACC_UPDATE:
+		    /* An "attach/detach" operation on an update directive
+		       should behave as a GOMP_MAP_ALWAYS_POINTER.  Note that
+		       both GOMP_MAP_ATTACH_DETACH and GOMP_MAP_ALWAYS_POINTER
+		       kinds depend on the previous mapping (for non-TARGET
+		       regions).  */
+		    OMP_CLAUSE_SET_MAP_KIND (c, GOMP_MAP_ALWAYS_POINTER);
+		    break;
+		  default:
+		  change_to_attach:
+		    OMP_CLAUSE_SET_MAP_KIND (c, GOMP_MAP_ATTACH);
+		    if ((ctx->region_type & ORT_TARGET) != 0)
+		      move_attach = true;
+		  }
+	      else if ((ctx->region_type & ORT_TARGET) != 0
+		       && (OMP_CLAUSE_MAP_KIND (c) == GOMP_MAP_ATTACH
+			   || (OMP_CLAUSE_MAP_KIND (c)
+			       == GOMP_MAP_ATTACH_ZERO_LENGTH_ARRAY_SECTION)))
+		move_attach = true;
+
+	      /* If we have e.g. map(struct: *var), don't gimplify the
+		 argument since omp-low.cc wants to see the decl itself.  */
+	      if (OMP_CLAUSE_MAP_KIND (c) == GOMP_MAP_STRUCT)
+		break;
+
+	      /* 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))
+		break;
+
+	      gimplify_omp_ctxp = ctx->outer_context;
+	      if (gimplify_expr (pd, pre_p, NULL, is_gimple_lvalue,
+				 fb_lvalue) == GS_ERROR)
+		remove = true;
+	      gimplify_omp_ctxp = ctx;
 	      break;
 	    }
+
+	 if ((code == OMP_TARGET
+	      || code == OMP_TARGET_DATA
+	      || code == OMP_TARGET_ENTER_DATA
+	      || code == OMP_TARGET_EXIT_DATA)
+	     && OMP_CLAUSE_MAP_KIND (c) == GOMP_MAP_ATTACH_DETACH)
+	   {
+	     bool firstprivatize = false;
+
+	     for (struct gimplify_omp_ctx *octx = ctx->outer_context; octx;
+		  octx = octx->outer_context)
+	       {
+		 splay_tree_node n
+		   = splay_tree_lookup (octx->variables,
+					(splay_tree_key) OMP_CLAUSE_DECL (c));
+		 /* If this is contained in an outer OpenMP region as a
+		    firstprivate value, remove the attach/detach.  */
+		 if (n && (n->value & GOVD_FIRSTPRIVATE))
+		   {
+		     firstprivatize = true;
+		     break;
+		   }
+	       }
+
+	     enum gomp_map_kind map_kind;
+	     if (firstprivatize)
+	       map_kind = GOMP_MAP_FIRSTPRIVATE_POINTER;
+	     else if (code == OMP_TARGET_EXIT_DATA)
+	       map_kind = GOMP_MAP_DETACH;
+	     else
+	       map_kind = GOMP_MAP_ATTACH;
+	     OMP_CLAUSE_SET_MAP_KIND (c, map_kind);
+	   }
+	 else if ((ctx->region_type & ORT_ACC) != 0
+		  && OMP_CLAUSE_MAP_KIND (c) == GOMP_MAP_ATTACH_DETACH)
+	   {
+	     enum gomp_map_kind map_kind = (code == OACC_EXIT_DATA
+					    ? GOMP_MAP_DETACH
+					    : GOMP_MAP_ATTACH);
+	     OMP_CLAUSE_SET_MAP_KIND (c, map_kind);
+	   }
+
 	  n = splay_tree_lookup (ctx->variables, (splay_tree_key) decl);
 	  if ((ctx->region_type & ORT_TARGET) != 0
 	      && !(n->value & GOVD_SEEN)
@@ -14163,6 +14273,21 @@ gimplify_adjust_omp_clauses (gimple_seq *pre_p, gimple_seq body, tree *list_p,
 			  || ((n->value & (GOVD_PRIVATE | GOVD_FIRSTPRIVATE))
 			      == 0));
 	    }
+
+	  /* If we have a target region, we can push all the attaches to the
+	     end of the list (we may have standalone "attach" operations
+	     synthesized for GOMP_MAP_STRUCT nodes that must be processed after
+	     the attachment point AND the pointed-to block have been mapped).
+	     If we have something else, e.g. "enter data", we need to keep
+	     "attach" nodes together with the previous node they attach to so
+	     that separate "exit data" operations work properly (see
+	     libgomp/target.c).  */
+	  if ((ctx->region_type & ORT_TARGET) != 0
+	      && (OMP_CLAUSE_MAP_KIND (c) == GOMP_MAP_ATTACH
+		  || (OMP_CLAUSE_MAP_KIND (c)
+		      == GOMP_MAP_ATTACH_ZERO_LENGTH_ARRAY_SECTION)))
+	    move_attach = true;
+
 	  break;
 
 	case OMP_CLAUSE_TO:
diff --git a/libgomp/testsuite/libgomp.fortran/target-enter-data-6.f90 b/libgomp/testsuite/libgomp.fortran/target-enter-data-6.f90
index b55d0b268d4..80d30edbfc5 100644
--- a/libgomp/testsuite/libgomp.fortran/target-enter-data-6.f90
+++ b/libgomp/testsuite/libgomp.fortran/target-enter-data-6.f90
@@ -3,16 +3,6 @@
 ! - arrays with array descriptors
 ! For those, the array descriptor / string length must be mapped with 'to:'
 
-! This test fails without the following additional patches:
-!
-! "OpenMP: Pointers and member mappings":
-! https://gcc.gnu.org/pipermail/gcc-patches/2023-August/627898.html
-!
-! "OpenMP/OpenACC: Reorganise OMP map clause handling in gimplify.cc":
-! https://gcc.gnu.org/pipermail/gcc-patches/2023-August/627900.html
-!
-! { dg-xfail-run-if TODO { offload_device_nonshared_as } }
-
 program main
 implicit none
 

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

* Re: [PATCH v7 5/5] OpenMP/OpenACC: Reorganise OMP map clause handling in gimplify.cc
  2023-12-20 21:29       ` Julian Brown
@ 2023-12-21  8:51         ` Tobias Burnus
  0 siblings, 0 replies; 19+ messages in thread
From: Tobias Burnus @ 2023-12-21  8:51 UTC (permalink / raw)
  To: Julian Brown; +Cc: gcc-patches, fortran

Hi Julian,

On 20.12.23 22:29, Julian Brown wrote:
> Thanks for review! Here's a new version of the patch which hopefully
> addresses this round of comments.

Thanks for the patch. LGTM now.

Tobias

> On Tue, 19 Dec 2023 16:41:54 +0100
> Tobias Burnus <tobias@codesourcery.com> wrote:
>
>> On 16.12.23 14:25, Julian Brown wrote:
>>> --- a/gcc/gimplify.cc
>>> +++ b/gcc/gimplify.cc
>>> @@ -10107,6 +10114,20 @@ omp_segregate_mapping_groups
>>> (omp_mapping_group *inlist) ard_tail = &w->next;
>>>          break;
>>>
>>> +     case GOMP_MAP_PRESENT_ALLOC:
>>> +       *pa_tail = w;
>>> +       w->next = NULL;
>>> +       pa_tail = &w->next;
>>> +       break;
>>> +
>>> +     case GOMP_MAP_PRESENT_FROM:
>>> +     case GOMP_MAP_PRESENT_TO:
>>> +     case GOMP_MAP_PRESENT_TOFROM:
>>> +       *ptf_tail = w;
>>> +       w->next = NULL;
>>> +       ptf_tail = &w->next;
>>> +       break;
>>> +
>> First, I note that GOMP_MAP_PRESENT_ALLOC and
>> GOMP_MAP_PRESENT_{FROM,TO,TOFROM} are semantically identical: If the
>> variable is not present, error termination will happen - otherwise, if
>> present, no data movement will happen. Hence, they will be changed to
>> GOMP_MAP_FORCE_PRESENT in gimplify_adjust_omp_clauses.
>>
>> That's also the reason that the old code handled all of them
>> identical.
>>
>> However, besides a plain 'present', there is also 'always' +
>> 'present'. Those are different as after a normal 'present' check
>> (abort if not present), the data copying will happen:
>> GOMP_MAP_ALWAYS_PRESENT_TO, GOMP_MAP_ALWAYS_PRESENT_FROM,
>> GOMP_MAP_ALWAYS_PRESENT_TOFROM.
>>
>> (Note that: always + present + alloc = GOMP_MAP_PRESENT_ALLOC (w/o
>> 'always') as already done in the FE.)
>>
>> Thus, all 'case' from your patch should go to a single group (possibly
>> adding a comment about it). The question is what to do with the
>> 'present,always' case. I think leaving them under 'default:' is fine,
>> but I might have missed something.
> I've made this change (i.e.: grouping all "GOMP_MAP_PRESENT_*" nodes
> together), and in fact that restores the dump output for the
> gfortran.dg/gomp/map-12.f90 that needed to be adjusted for the previous
> version of the patch (so that hunk has now disappeared).
>
>>>        default:
>>>          *tf_tail = w;
>>>          w->next = NULL;
>>> @@ -10118,8 +10139,10 @@ omp_segregate_mapping_groups
>>> (omp_mapping_group *inlist)
>> * * *
>>> @@ -11922,119 +11945,30 @@ gimplify_scan_omp_clauses (tree *list_p,
>>> gimple_seq *pre_p, break;
>>>          }
>>>
>>> -  if (code == OMP_TARGET
>>> -      || code == OMP_TARGET_DATA
>>> -      || code == OMP_TARGET_ENTER_DATA
>>> -      || code == OMP_TARGET_EXIT_DATA)
>>> -    {
>>> -      vec<omp_mapping_group> *groups;
>>> -      groups = omp_gather_mapping_groups (list_p);
>>> -      if (groups)
>>> -     {
>>> -       hash_map<tree_operand_hash_no_se, omp_mapping_group *>
>>> *grpmap;
>>> -       grpmap = omp_index_mapping_groups (groups);
>>> +  vec<omp_mapping_group> *groups = omp_gather_mapping_groups
>>> (list_p);
>>> +  hash_map<tree_operand_hash_no_se, omp_mapping_group *> *grpmap =
>>> NULL;
>>> +  unsigned grpnum = 0;
>>> +  tree *grp_start_p = NULL, grp_end = NULL_TREE;
>> ...
>>
>>> -  else if (region_type & ORT_ACC)
>>> -    {
>> I wonder whether you should not better call
>> 'omp_gather_mapping_groups' only for the 'code == OMP_TARGET...' and
>> for ORT_ACC (or some subset of OACC *), given that this function is
>> also called bygimplify_omp_parallel, gimplify_omp_task,
>> gimplify_omp_for, ...
>>
>> This avoids some memory allocation and list_p walking, i.e. it is not
>> too bad - but also not really needed for task, parallel, for, ...
> I've made that change -- OpenACC uses OMP_CLAUSE_MAP in quite a wide
> range of directives, but the new version of the patch lists them
> individually anyway, rather than using a catch-all for ORT_ACC regions.
> That seems OK, I think.
>
>>> @@ -14008,26 +13926,73 @@ gimplify_adjust_omp_clauses (gimple_seq
>>> *pre_p, gimple_seq body, tree *list_p, default:
>>>              break;
>>>            }
>>> -       if (code == OMP_TARGET_EXIT_DATA
>>> -           && OMP_CLAUSE_MAP_KIND (c) == GOMP_MAP_ALWAYS_POINTER)
>>> +       switch (code)
>>>            {
>>> +         case OMP_TARGET:
>>> +           break;
>>> +         case OACC_DATA:
>>> +           if (TREE_CODE (TREE_TYPE (decl)) != ARRAY_TYPE)
>>> +             break;
>>> +           goto check_firstprivate;
>>> +         case OACC_ENTER_DATA:
>>> +         case OACC_EXIT_DATA:
>>> +         case OMP_TARGET_DATA:
>>> +         case OMP_TARGET_ENTER_DATA:
>>> +         case OMP_TARGET_EXIT_DATA:
>>> +         case OACC_HOST_DATA:
>>> +         check_firstprivate:
>>> +           if (OMP_CLAUSE_MAP_KIND (c) ==
>>> GOMP_MAP_FIRSTPRIVATE_POINTER
>> I think it looks nicer if the OACC_HOST is before OMP_* such that all
>> OACC_* are together. (In the old code, oacc_enter/exit was treated
>> differently than OMP_* and OACC_HOST_DATA; your order is a leftover
>> from that code movement/change.)
> I've fixed this bit -- which actually doesn't need the goto any more
> either, so that's now a fallthrough instead.
>
>>> +         flags = GOVD_MAP | GOVD_EXPLICIT;
>>> +         if (OMP_CLAUSE_MAP_KIND (c) == GOMP_MAP_ALWAYS_TO
>>> +             || OMP_CLAUSE_MAP_KIND (c) == GOMP_MAP_ALWAYS_TOFROM)
>>> +           flags |= GOVD_MAP_ALWAYS_TO;
>> I know that the code has only been moved, but I wonder whether that
>> should also include GOMP_MAP_ALWAYS_PRESENT_{TO,TOFROM} as condition.
> I've added it (caveat: without any tests).
>
> Re-tested with offloading to NVPTX. OK now?
-----------------
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] 19+ messages in thread

end of thread, other threads:[~2023-12-21  8:51 UTC | newest]

Thread overview: 19+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2023-08-18 22:47 [PATCH v7 0/5] OpenMP/OpenACC: map clause and OMP gimplify rework Julian Brown
2023-08-18 22:47 ` [PATCH v7 1/5] OpenMP/OpenACC: Reindent TO/FROM/_CACHE_ stanza in {c_}finish_omp_clause Julian Brown
2023-08-18 22:47 ` [PATCH v7 2/5] OpenMP/OpenACC: Rework clause expansion and nested struct handling Julian Brown
2023-11-14 10:21   ` Tobias Burnus
2023-11-29 11:43     ` Julian Brown
2023-11-29 16:03       ` Tobias Burnus
2023-12-14  7:14       ` [committed] testsuite: Fix up target-enter-data-1.c on 32-bit targets Jakub Jelinek
2023-12-14 10:09         ` Julian Brown
2023-08-18 22:47 ` [PATCH v7 3/5] OpenMP: Pointers and member mappings Julian Brown
2023-12-06 11:36   ` Tobias Burnus
2023-12-07 17:24     ` Julian Brown
2023-12-11 11:44       ` Tobias Burnus
2023-08-18 22:47 ` [PATCH v7 4/5] OpenMP/OpenACC: Unordered/non-constant component offset runtime diagnostic Julian Brown
2023-12-14 14:26   ` Tobias Burnus
2023-12-15 13:00     ` Thomas Schwinge
2023-08-18 22:47 ` [PATCH v7 5/5] OpenMP/OpenACC: Reorganise OMP map clause handling in gimplify.cc Julian Brown
     [not found]   ` <20231216132507.5991c79e@squid.athome>
2023-12-19 15:41     ` Tobias Burnus
2023-12-20 21:29       ` Julian Brown
2023-12-21  8:51         ` Tobias Burnus

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).