public inbox for gcc-patches@gcc.gnu.org
 help / color / mirror / Atom feed
* [PATCH 10/16] OpenMP: Fix non-zero attach/detach bias for struct dereferences
@ 2021-11-25 14:10 Julian Brown
  2021-11-25 14:10 ` [PATCH 11/16] OpenMP: Handle reference-typed struct members Julian Brown
                   ` (3 more replies)
  0 siblings, 4 replies; 5+ messages in thread
From: Julian Brown @ 2021-11-25 14:10 UTC (permalink / raw)
  To: gcc-patches; +Cc: Jakub Jelinek, Thomas Schwinge

This patch fixes attach/detach operations for OpenMP that have a non-zero
bias: these can occur if we have a mapping such as:

  #pragma omp target map(mystruct->a.b[idx].c[:arrsz])

i.e. where there is an offset between the attachment point ("mystruct"
here) and the pointed-to data.  (The "b" and "c" members would be array
types here, not pointers themselves).  In this example the difference
(thus bias encoded in the attach/detach node) will be something like:

  (uintptr_t) &mystruct->a.b[idx].c[0] - (uintptr_t) &mystruct->a

OK?

Thanks,

Julian

2021-09-29  Julian Brown  <julian@codesourcery.com>

gcc/c-family/
	* c-common.h (c_omp_decompose_attachable_address): Add prototype.
	* c-omp.c (c_omp_decompose_attachable_address): New function.

gcc/c/
	* c-typeck.c (handle_omp_array_sections): Handle attach/detach for
	struct dereferences with non-zero bias.

gcc/cp/
	* semantics.c (handle_omp_array_section): Handle attach/detach for
	struct dereferences with non-zero bias.

libgomp/
	* testsuite/libgomp.c++/baseptrs-3.C: Add test (XFAILed for now).
	* testsuite/libgomp.c-c++-common/baseptrs-1.c: Add test.
	* testsuite/libgomp.c-c++-common/baseptrs-2.c: Add test.
---
 gcc/c-family/c-common.h                       |   1 +
 gcc/c-family/c-omp.c                          |  42 ++++
 gcc/c/c-typeck.c                              |  12 +-
 gcc/cp/semantics.c                            |  14 +-
 libgomp/testsuite/libgomp.c++/baseptrs-3.C    | 182 ++++++++++++++++++
 .../libgomp.c-c++-common/baseptrs-1.c         |  50 +++++
 .../libgomp.c-c++-common/baseptrs-2.c         |  70 +++++++
 7 files changed, 364 insertions(+), 7 deletions(-)
 create mode 100644 libgomp/testsuite/libgomp.c++/baseptrs-3.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

diff --git a/gcc/c-family/c-common.h b/gcc/c-family/c-common.h
index d5dad99ff97..dd103d8eecd 100644
--- a/gcc/c-family/c-common.h
+++ b/gcc/c-family/c-common.h
@@ -1251,6 +1251,7 @@ extern tree c_omp_check_context_selector (location_t, tree);
 extern void c_omp_mark_declare_variant (location_t, tree, tree);
 extern const char *c_omp_map_clause_name (tree, bool);
 extern void c_omp_adjust_map_clauses (tree, bool);
+extern tree c_omp_decompose_attachable_address (tree t, tree *virtbase);
 
 enum c_omp_directive_kind {
   C_OMP_DIR_STANDALONE,
diff --git a/gcc/c-family/c-omp.c b/gcc/c-family/c-omp.c
index 3f84fd1b5cb..a90696fe706 100644
--- a/gcc/c-family/c-omp.c
+++ b/gcc/c-family/c-omp.c
@@ -3113,6 +3113,48 @@ c_omp_adjust_map_clauses (tree clauses, bool is_target)
     }
 }
 
+tree
+c_omp_decompose_attachable_address (tree t, tree *virtbase)
+{
+  *virtbase = t;
+
+  /* It's already a pointer.  Just use that.  */
+  if (POINTER_TYPE_P (TREE_TYPE (t)))
+    return NULL_TREE;
+
+  /* Otherwise, look for a base pointer deeper within the expression.  */
+
+  while (TREE_CODE (t) == COMPONENT_REF
+	 && (TREE_CODE (TREE_OPERAND (t, 0)) == COMPONENT_REF
+	     || TREE_CODE (TREE_OPERAND (t, 0)) == ARRAY_REF))
+    {
+      t = TREE_OPERAND (t, 0);
+      while (TREE_CODE (t) == ARRAY_REF)
+	t = TREE_OPERAND (t, 0);
+    }
+
+
+  *virtbase = t;
+
+  if (TREE_CODE (t) != COMPONENT_REF)
+    return NULL_TREE;
+
+  t = TREE_OPERAND (t, 0);
+
+  tree attach_pt = NULL_TREE;
+
+  if ((TREE_CODE (t) == INDIRECT_REF
+       || TREE_CODE (t) == MEM_REF)
+      && TREE_CODE (TREE_TYPE (TREE_OPERAND (t, 0))) == POINTER_TYPE)
+    {
+      attach_pt = TREE_OPERAND (t, 0);
+      if (TREE_CODE (attach_pt) == POINTER_PLUS_EXPR)
+	attach_pt = TREE_OPERAND (attach_pt, 0);
+    }
+
+  return attach_pt;
+}
+
 static const struct c_omp_directive omp_directives[] = {
   /* Keep this alphabetically sorted by the first word.  Non-null second/third
      if any should precede null ones.  */
diff --git a/gcc/c/c-typeck.c b/gcc/c/c-typeck.c
index 4d156f6d3ec..cfac7d0a2b5 100644
--- a/gcc/c/c-typeck.c
+++ b/gcc/c/c-typeck.c
@@ -13799,9 +13799,15 @@ 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;
+      tree virtbase = t;
+      tree attach_pt
+	= ((ort != C_ORT_ACC)
+	   ? c_omp_decompose_attachable_address (t, &virtbase)
+	   : NULL_TREE);
       if (OMP_CLAUSE_CODE (c) != OMP_CLAUSE_MAP
 	  || (TREE_CODE (t) == COMPONENT_REF
-	      && TREE_CODE (TREE_TYPE (t)) == ARRAY_TYPE))
+	      && TREE_CODE (TREE_TYPE (t)) == ARRAY_TYPE
+	      && !attach_pt))
 	return false;
       gcc_assert (OMP_CLAUSE_MAP_KIND (c) != GOMP_MAP_FORCE_DEVICEPTR);
       switch (OMP_CLAUSE_MAP_KIND (c))
@@ -13834,10 +13840,10 @@ handle_omp_array_sections (tree c, enum c_omp_region_type ort)
       if (OMP_CLAUSE_MAP_KIND (c2) != GOMP_MAP_FIRSTPRIVATE_POINTER
 	  && !c_mark_addressable (t))
 	return false;
-      OMP_CLAUSE_DECL (c2) = t;
+      OMP_CLAUSE_DECL (c2) = attach_pt ? attach_pt : 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);
+      tree ptr = virtbase;
       if (!POINTER_TYPE_P (TREE_TYPE (ptr)))
 	ptr = build_fold_addr_expr (ptr);
       t = fold_build2_loc (OMP_CLAUSE_LOCATION (c), MINUS_EXPR,
diff --git a/gcc/cp/semantics.c b/gcc/cp/semantics.c
index e882c302f31..068c0c69e58 100644
--- a/gcc/cp/semantics.c
+++ b/gcc/cp/semantics.c
@@ -5620,9 +5620,16 @@ 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);
+
+	  tree virtbase = t;
+	  tree attach_pt
+	    = ((ort != C_ORT_ACC)
+	       ? c_omp_decompose_attachable_address (t, &virtbase)
+	       : NULL_TREE);
 	  if (OMP_CLAUSE_CODE (c) != OMP_CLAUSE_MAP
 	      || (TREE_CODE (t) == COMPONENT_REF
-		  && TREE_CODE (TREE_TYPE (t)) == ARRAY_TYPE))
+		  && TREE_CODE (TREE_TYPE (t)) == ARRAY_TYPE
+		  && !attach_pt))
 	    return false;
 	  switch (OMP_CLAUSE_MAP_KIND (c))
 	    {
@@ -5684,12 +5691,11 @@ handle_omp_array_sections (tree c, enum c_omp_region_type ort)
 	  if (OMP_CLAUSE_MAP_KIND (c2) != GOMP_MAP_FIRSTPRIVATE_POINTER
 	      && !cxx_mark_addressable (t))
 	    return false;
-	  OMP_CLAUSE_DECL (c2) = t;
+	  OMP_CLAUSE_DECL (c2) = attach_pt ? attach_pt : 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);
+	  tree ptr = convert_from_reference (virtbase);
 	  if (!INDIRECT_TYPE_P (TREE_TYPE (ptr)))
 	    ptr = build_fold_addr_expr (ptr);
 	  t = fold_build2_loc (OMP_CLAUSE_LOCATION (c), MINUS_EXPR,
diff --git a/libgomp/testsuite/libgomp.c++/baseptrs-3.C b/libgomp/testsuite/libgomp.c++/baseptrs-3.C
new file mode 100644
index 00000000000..cabeb7c2b7a
--- /dev/null
+++ b/libgomp/testsuite/libgomp.c++/baseptrs-3.C
@@ -0,0 +1,182 @@
+/* { dg-xfail-if "fails to parse correctly" { *-*-* } } */
+
+#include <cstdlib>
+#include <cstring>
+#include <cassert>
+
+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 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->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);
+
+/* FIXME: This currently ICEs.  */
+/*  #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);
+
+  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->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);
+
+/* FIXME: This currently ICEs.  */
+/*  #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);
+
+  delete my_a->ptr;
+  delete my_a;
+  delete my_b;
+}
+
+int main (int argc, char *argv[])
+{
+  foo ();
+  bar ();
+  foop ();
+  barp ();
+  return 0;
+}
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;
+}
-- 
2.29.2


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

* [PATCH 11/16] OpenMP: Handle reference-typed struct members
  2021-11-25 14:10 [PATCH 10/16] OpenMP: Fix non-zero attach/detach bias for struct dereferences Julian Brown
@ 2021-11-25 14:10 ` Julian Brown
  2021-11-25 14:10 ` [PATCH 12/16] OpenACC: Make deep-copy-arrayofstruct.c a libgomp/runtime test Julian Brown
                   ` (2 subsequent siblings)
  3 siblings, 0 replies; 5+ messages in thread
From: Julian Brown @ 2021-11-25 14:10 UTC (permalink / raw)
  To: gcc-patches; +Cc: Jakub Jelinek, Thomas Schwinge

This patch fixes the baseptrs-3.C test case introduced in the patch:

  https://gcc.gnu.org/pipermail/gcc-patches/2021-October/580729.html

The problematic case concerns OpenMP mapping clauses containing struct
members of reference type, e.g. "mystruct.myref.myptr[:N]".  To be able
to access the array slice through the reference in the middle, we need
to perform an attach action for that reference, since it is represented
internally as a pointer.

I don't think the spec allows for this case explicitly.  The closest
clause is (OpenMP 5.0, "2.19.7.1 map Clause"):

  "If the type of a list item is a reference to a type T then the
   reference in the device data environment is initialized to refer to
   the object in the device data environment that corresponds to the
   object referenced by the list item. If mapping occurs, it occurs as
   though the object were mapped through a pointer with an array section
   of type T and length one."

The patch as is allows the mapping to work with just
"mystruct.myref.myptr[:N]", without an explicit "mystruct.myref"
mapping also (because, would that refer to the hidden pointer used by
the reference, or the automatically-dereferenced data itself?). An
attach/detach operation is thus synthesised for the reference.

OK?

Thanks,

Julian

2021-10-11  Julian Brown  <julian@codesourcery.com>

gcc/cp/
	* semantics.c (finish_omp_clauses): Handle reference-typed members.

gcc/
	* gimplify.c (build_struct_group): Arrange for attach/detach nodes to
	be created for reference-typed struct members for OpenMP.  Only create
	firstprivate_pointer/firstprivate_reference nodes for innermost struct
	accesses, those with an optionally-indirected DECL_P base.
	(omp_build_struct_sibling_lists): Handle two-element chain for inner
	struct component returned from build_struct_group.

libgomp/
	* testsuite/libgomp.c++/baseptrs-3.C: Remove XFAILs and extend test.
---
 gcc/cp/semantics.c                         |   4 +
 gcc/gimplify.c                             |  56 +++++++++--
 libgomp/testsuite/libgomp.c++/baseptrs-3.C | 109 +++++++++++++++++++--
 3 files changed, 154 insertions(+), 15 deletions(-)

diff --git a/gcc/cp/semantics.c b/gcc/cp/semantics.c
index 068c0c69e58..6d30a9ed97d 100644
--- a/gcc/cp/semantics.c
+++ b/gcc/cp/semantics.c
@@ -7923,6 +7923,8 @@ finish_omp_clauses (tree clauses, enum c_omp_region_type ort)
 			      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);
 			    }
 			}
 		      while (TREE_CODE (t) == COMPONENT_REF);
@@ -8021,6 +8023,8 @@ finish_omp_clauses (tree clauses, enum c_omp_region_type ort)
 	    {
 	      t = TREE_OPERAND (TREE_OPERAND (t, 0), 0);
 	      indir_component_ref_p = true;
+	      if (REFERENCE_REF_P (t))
+		t = TREE_OPERAND (t, 0);
 	      STRIP_NOPS (t);
 	      if (TREE_CODE (t) == POINTER_PLUS_EXPR)
 		t = TREE_OPERAND (t, 0);
diff --git a/gcc/gimplify.c b/gcc/gimplify.c
index 56f0aaaf979..8f07da8a991 100644
--- a/gcc/gimplify.c
+++ b/gcc/gimplify.c
@@ -9802,7 +9802,10 @@ build_struct_group (enum omp_region_type region_type, enum tree_code code,
   /* 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)
+  if (openmp
+      && attach_detach
+      && !(TREE_CODE (TREE_TYPE (ocd)) == REFERENCE_TYPE
+	   && TREE_CODE (TREE_TYPE (TREE_TYPE (ocd))) != POINTER_TYPE))
     return NULL;
 
   if (!struct_map_to_clause || struct_map_to_clause->get (base) == NULL)
@@ -9851,9 +9854,32 @@ build_struct_group (enum omp_region_type region_type, enum tree_code code,
 
       tree noind = strip_indirections (base);
 
-      if (!openmp
+      if (openmp
+	  && TREE_CODE (TREE_TYPE (noind)) == REFERENCE_TYPE
 	  && (region_type & ORT_TARGET)
 	  && TREE_CODE (noind) == COMPONENT_REF)
+	{
+	  tree c2 = build_omp_clause (OMP_CLAUSE_LOCATION (grp_end),
+				      OMP_CLAUSE_MAP);
+	  OMP_CLAUSE_SET_MAP_KIND (c2, GOMP_MAP_TO);
+	  OMP_CLAUSE_DECL (c2) = unshare_expr (base);
+	  OMP_CLAUSE_SIZE (c2) = TYPE_SIZE_UNIT (TREE_TYPE (noind));
+
+	  tree c3 = build_omp_clause (OMP_CLAUSE_LOCATION (grp_end),
+				      OMP_CLAUSE_MAP);
+	  OMP_CLAUSE_SET_MAP_KIND (c3, GOMP_MAP_ATTACH_DETACH);
+	  OMP_CLAUSE_DECL (c3) = unshare_expr (noind);
+	  OMP_CLAUSE_SIZE (c3) = size_zero_node;
+
+	  OMP_CLAUSE_CHAIN (c2) = c3;
+	  OMP_CLAUSE_CHAIN (c3) = NULL_TREE;
+
+	  *inner = c2;
+	  return NULL;
+	}
+      else if (!openmp
+	       && (region_type & ORT_TARGET)
+	       && TREE_CODE (noind) == COMPONENT_REF)
 	{
 	  /* The base for this component access is a struct component access
 	     itself.  Insert a node to be processed on the next iteration of
@@ -9867,13 +9893,30 @@ build_struct_group (enum omp_region_type region_type, enum tree_code code,
 	  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));
+	  OMP_CLAUSE_CHAIN (c2) = NULL_TREE;
 	  *inner = c2;
 	  return NULL;
 	}
 
-      tree sdecl = strip_components_and_deref (base);
+      tree sdecl = base;
+      /* There are too many places we need to do things like this.  */
+      if (TREE_CODE (sdecl) == INDIRECT_REF
+	  || TREE_CODE (sdecl) == MEM_REF)
+	{
+	  sdecl = TREE_OPERAND (sdecl, 0);
+	  if (TREE_CODE (sdecl) == INDIRECT_REF
+	      && (TREE_CODE (TREE_TYPE (TREE_OPERAND (sdecl, 0)))
+		  == REFERENCE_TYPE))
+	    sdecl = TREE_OPERAND (sdecl, 0);
+	}
 
-      if (POINTER_TYPE_P (TREE_TYPE (sdecl)) && (region_type & ORT_TARGET))
+      while (TREE_CODE (sdecl) == COMPONENT_REF
+	     || TREE_CODE (sdecl) == POINTER_PLUS_EXPR)
+	sdecl = TREE_OPERAND (sdecl, 0);
+
+      if (DECL_P (sdecl)
+	  && POINTER_TYPE_P (TREE_TYPE (sdecl))
+	  && (region_type & ORT_TARGET))
 	{
 	  tree c2 = build_omp_clause (OMP_CLAUSE_LOCATION (grp_end),
 				      OMP_CLAUSE_MAP);
@@ -10212,11 +10255,10 @@ omp_build_struct_sibling_lists (enum tree_code code,
 	      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_end = (OMP_CLAUSE_CHAIN (inner)
+				? OMP_CLAUSE_CHAIN (inner) : inner);
 	      newgrp.mark = UNVISITED;
 	      newgrp.sibling = NULL;
 	      newgrp.next = NULL;
diff --git a/libgomp/testsuite/libgomp.c++/baseptrs-3.C b/libgomp/testsuite/libgomp.c++/baseptrs-3.C
index cabeb7c2b7a..39a48a40920 100644
--- a/libgomp/testsuite/libgomp.c++/baseptrs-3.C
+++ b/libgomp/testsuite/libgomp.c++/baseptrs-3.C
@@ -1,9 +1,58 @@
-/* { dg-xfail-if "fails to parse correctly" { *-*-* } } */
-
 #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;
@@ -90,6 +139,49 @@ bar ()
   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;
@@ -108,7 +200,7 @@ foop ()
 
   memset (my_c->a->ptr, 0, sizeof (int) * 10);
 
-  #pragma omp target map (my_c->a->ptr, my_c->a->ptr[: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;
@@ -119,8 +211,7 @@ foop ()
 
   memset (my_c->b->arr, 0, sizeof (int) * 10);
 
-/* FIXME: This currently ICEs.  */
-/*  #pragma omp target map (my_c->b->arr[: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;
@@ -146,7 +237,8 @@ barp ()
 
   memset (my_cref->a->ptr, 0, sizeof (int) * 10);
 
-  #pragma omp target map (my_cref->a->ptr, my_cref->a->ptr[: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;
@@ -157,8 +249,7 @@ barp ()
 
   memset (my_cref->b->arr, 0, sizeof (int) * 10);
 
-/* FIXME: This currently ICEs.  */
-/*  #pragma omp target map (my_cref->b->arr[: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;
@@ -174,8 +265,10 @@ barp ()
 
 int main (int argc, char *argv[])
 {
+  foo0 ();
   foo ();
   bar ();
+  foop0 ();
   foop ();
   barp ();
   return 0;
-- 
2.29.2


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

* [PATCH 12/16] OpenACC: Make deep-copy-arrayofstruct.c a libgomp/runtime test
  2021-11-25 14:10 [PATCH 10/16] OpenMP: Fix non-zero attach/detach bias for struct dereferences Julian Brown
  2021-11-25 14:10 ` [PATCH 11/16] OpenMP: Handle reference-typed struct members Julian Brown
@ 2021-11-25 14:10 ` Julian Brown
  2021-11-25 14:10 ` [PATCH 13/16] Add debug_omp_expr Julian Brown
  2021-11-25 14:10 ` [PATCH 14/16] OpenMP: Add inspector class to unify mapped address analysis Julian Brown
  3 siblings, 0 replies; 5+ messages in thread
From: Julian Brown @ 2021-11-25 14:10 UTC (permalink / raw)
  To: gcc-patches; +Cc: Jakub Jelinek, Thomas Schwinge

I noticed that the test in question now compiles properly, and in fact
runs properly too.  Thus it's more useful as a runtime test than a
passing compilation test that otherwise doesn't do much.  This patch
moves it to libgomp.

OK?

Thanks,

Julian

2021-10-11  Julian Brown  <julian@codesourcery.com>

gcc/testsuite/
	* c-c++-common/goacc/deep-copy-arrayofstruct.c: Move test from here.

libgomp/
	* testsuite/libgomp.oacc-c-c++-common/deep-copy-arrayofstruct.c: Move
	test to here.
---
 .../libgomp.oacc-c-c++-common}/deep-copy-arrayofstruct.c        | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)
 rename {gcc/testsuite/c-c++-common/goacc => libgomp/testsuite/libgomp.oacc-c-c++-common}/deep-copy-arrayofstruct.c (98%)

diff --git a/gcc/testsuite/c-c++-common/goacc/deep-copy-arrayofstruct.c b/libgomp/testsuite/libgomp.oacc-c-c++-common/deep-copy-arrayofstruct.c
similarity index 98%
rename from gcc/testsuite/c-c++-common/goacc/deep-copy-arrayofstruct.c
rename to libgomp/testsuite/libgomp.oacc-c-c++-common/deep-copy-arrayofstruct.c
index 4247607b61c..a11c64749cc 100644
--- a/gcc/testsuite/c-c++-common/goacc/deep-copy-arrayofstruct.c
+++ b/libgomp/testsuite/libgomp.oacc-c-c++-common/deep-copy-arrayofstruct.c
@@ -1,4 +1,4 @@
-/* { dg-do compile } */
+/* { dg-do run } */
 
 #include <stdlib.h>
 #include <stdio.h>
-- 
2.29.2


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

* [PATCH 13/16] Add debug_omp_expr
  2021-11-25 14:10 [PATCH 10/16] OpenMP: Fix non-zero attach/detach bias for struct dereferences Julian Brown
  2021-11-25 14:10 ` [PATCH 11/16] OpenMP: Handle reference-typed struct members Julian Brown
  2021-11-25 14:10 ` [PATCH 12/16] OpenACC: Make deep-copy-arrayofstruct.c a libgomp/runtime test Julian Brown
@ 2021-11-25 14:10 ` Julian Brown
  2021-11-25 14:10 ` [PATCH 14/16] OpenMP: Add inspector class to unify mapped address analysis Julian Brown
  3 siblings, 0 replies; 5+ messages in thread
From: Julian Brown @ 2021-11-25 14:10 UTC (permalink / raw)
  To: gcc-patches; +Cc: Jakub Jelinek, Thomas Schwinge

The C and C++ front-ends use a TREE_LIST as a 3-tuple representing an
OpenMP array section, which tends to crash debug_generic_expr if one
wants to print such an expression in the debugger.  This little helper
function works around that.

We might want to adjust the representation of array sections to use the
soon-to-be-introduced OMP_ARRAY_SECTION tree code throughout instead,
at which point this patch will no longer be necessary.

OK?

Thanks,

Julian

2021-11-15  Julian Brown  <julian@codesourcery.com>

gcc/
	* tree-pretty-print.c (print_omp_expr, debug_omp_expr): New functions.
	* tree-pretty-print.h (debug_omp_expr): Add prototype.
---
 gcc/tree-pretty-print.c | 31 +++++++++++++++++++++++++++++++
 gcc/tree-pretty-print.h |  1 +
 2 files changed, 32 insertions(+)

diff --git a/gcc/tree-pretty-print.c b/gcc/tree-pretty-print.c
index a81ba401ef9..13b64fd52e1 100644
--- a/gcc/tree-pretty-print.c
+++ b/gcc/tree-pretty-print.c
@@ -103,6 +103,37 @@ debug_generic_stmt (tree t)
   fprintf (stderr, "\n");
 }
 
+static void
+print_omp_expr (tree t)
+{
+  if (TREE_CODE (t) == TREE_LIST)
+    {
+      tree low = TREE_PURPOSE (t);
+      tree len = TREE_VALUE (t);
+      tree base = TREE_CHAIN (t);
+      if (TREE_CODE (base) == TREE_LIST)
+	print_omp_expr (base);
+      else
+	print_generic_expr (stderr, base, TDF_VOPS|TDF_MEMSYMS);
+      fprintf (stderr, "[");
+      if (low)
+	print_generic_expr (stderr, low, TDF_VOPS|TDF_MEMSYMS);
+      fprintf (stderr, ":");
+      if (len)
+	print_generic_expr (stderr, len, TDF_VOPS|TDF_MEMSYMS);
+      fprintf (stderr, "]");
+    }
+  else
+    print_generic_expr (stderr, t, TDF_VOPS|TDF_MEMSYMS);
+}
+
+DEBUG_FUNCTION void
+debug_omp_expr (tree t)
+{
+  print_omp_expr (t);
+  fprintf (stderr, "\n");
+}
+
 /* Debugging function to print out a chain of trees .  */
 
 DEBUG_FUNCTION void
diff --git a/gcc/tree-pretty-print.h b/gcc/tree-pretty-print.h
index dacd256302b..bc910f9a1b1 100644
--- a/gcc/tree-pretty-print.h
+++ b/gcc/tree-pretty-print.h
@@ -33,6 +33,7 @@ along with GCC; see the file COPYING3.  If not see
 
 extern void debug_generic_expr (tree);
 extern void debug_generic_stmt (tree);
+extern void debug_omp_expr (tree);
 extern void debug_tree_chain (tree);
 extern void print_generic_decl (FILE *, tree, dump_flags_t);
 extern void print_generic_stmt (FILE *, tree, dump_flags_t = TDF_NONE);
-- 
2.29.2


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

* [PATCH 14/16] OpenMP: Add inspector class to unify mapped address analysis
  2021-11-25 14:10 [PATCH 10/16] OpenMP: Fix non-zero attach/detach bias for struct dereferences Julian Brown
                   ` (2 preceding siblings ...)
  2021-11-25 14:10 ` [PATCH 13/16] Add debug_omp_expr Julian Brown
@ 2021-11-25 14:10 ` Julian Brown
  3 siblings, 0 replies; 5+ messages in thread
From: Julian Brown @ 2021-11-25 14:10 UTC (permalink / raw)
  To: gcc-patches; +Cc: Jakub Jelinek, Thomas Schwinge

Several places in the C and C++ front-ends dig through OpenMP addresses
from "map" clauses (etc.) in order to determine whether they are component
accesses that need "attach" operations, check duplicate mapping clauses,
and so on.  When we're extending support for more kinds of lvalues in
map clauses, it seems helpful to bring these all into one place in order
to keep all the analyses in sync, and to make it easier to reason about
which kinds of expressions are supported.

This patch introduces an "address inspector" class for that purpose,
and adjusts the C and C++ front-ends to use it.

(The adjacent "c_omp_decompose_attachable_address" function could also
be moved into the address inspector class, perhaps.  That's not been
done yet.)

OK?

Thanks,

Julian

2021-11-15  Julian Brown  <julian@codesourcery.com>

gcc/c-family/
	* c-common.h (c_omp_address_inspector): New class.
	* c-omp.c (c_omp_address_inspector::init,
	c_omp_address_inspector::analyze_components,
	c_omp_address_inspector::map_supported_p,
	c_omp_address_inspector::mappable_type): New methods.

gcc/c/
	* c-typeck.c (handle_omp_array_sections_1,
	c_finish_omp_clauses): Use c_omp_address_inspector class.

gcc/cp/
	* semantics.c (cp_omp_address_inspector): New class, derived from
	c_omp_address_inspector.
	(handle_omp_array_sections_1): Use cp_omp_address_inspector class to
	analyze OpenMP map clause expressions.  Support POINTER_PLUS_EXPR.
	(finish_omp_clauses): Likewise.  Support some additional kinds of
	lvalues in map clauses.

gcc/testsuite/
	* g++.dg/gomp/unmappable-component-1.C: New test.
---
 gcc/c-family/c-common.h                       |  44 +++
 gcc/c-family/c-omp.c                          | 147 ++++++++++
 gcc/c/c-typeck.c                              | 198 +++-----------
 gcc/cp/semantics.c                            | 252 ++++++------------
 .../g++.dg/gomp/unmappable-component-1.C      |  21 ++
 5 files changed, 338 insertions(+), 324 deletions(-)
 create mode 100644 gcc/testsuite/g++.dg/gomp/unmappable-component-1.C

diff --git a/gcc/c-family/c-common.h b/gcc/c-family/c-common.h
index dd103d8eecd..05d479e4d2f 100644
--- a/gcc/c-family/c-common.h
+++ b/gcc/c-family/c-common.h
@@ -1253,6 +1253,50 @@ extern const char *c_omp_map_clause_name (tree, bool);
 extern void c_omp_adjust_map_clauses (tree, bool);
 extern tree c_omp_decompose_attachable_address (tree t, tree *virtbase);
 
+class c_omp_address_inspector
+{
+  tree clause;
+  tree orig;
+  tree deref_toplevel;
+  tree outer_virtual_base;
+  tree root_term;
+  bool component_access;
+  bool indirections;
+  int map_supported;
+
+protected:
+  virtual bool reference_ref_p (tree) { return false; }
+  virtual bool processing_template_decl_p () { return false; }
+  virtual bool mappable_type (tree t);
+  virtual void emit_unmappable_type_notes (tree) { }
+
+public:
+  c_omp_address_inspector (tree c, tree t)
+    : clause (c), orig (t), deref_toplevel (NULL_TREE),
+      outer_virtual_base (NULL_TREE), root_term (NULL_TREE),
+      component_access (false), indirections (false), map_supported (-1)
+  { }
+
+  ~c_omp_address_inspector () {}
+
+  virtual void init ();
+
+  tree analyze_components (bool);
+
+  tree get_deref_toplevel () { return deref_toplevel; }
+  tree get_outer_virtual_base () { return outer_virtual_base; }
+  tree get_root_term () { gcc_assert (root_term); return root_term; }
+  bool component_access_p () { return component_access; }
+
+  bool indir_component_ref_p ()
+    {
+      gcc_assert (!component_access || root_term != NULL_TREE);
+      return component_access && indirections;
+    }
+
+  bool map_supported_p ();
+};
+
 enum c_omp_directive_kind {
   C_OMP_DIR_STANDALONE,
   C_OMP_DIR_CONSTRUCT,
diff --git a/gcc/c-family/c-omp.c b/gcc/c-family/c-omp.c
index a90696fe706..5b2fbf6809b 100644
--- a/gcc/c-family/c-omp.c
+++ b/gcc/c-family/c-omp.c
@@ -3113,6 +3113,153 @@ c_omp_adjust_map_clauses (tree clauses, bool is_target)
     }
 }
 
+/* This could just be done in the constructor, but we need to call the
+   subclass's version of reference_ref_p, etc.  */
+
+void
+c_omp_address_inspector::init ()
+{
+  tree t = orig;
+
+  gcc_assert (TREE_CODE (t) != ARRAY_REF);
+
+  /* We may have a reference-typed component access at the outermost level
+     that has had convert_from_reference called on it.  Look through that
+     access.  */
+  if (reference_ref_p (t)
+      && TREE_CODE (TREE_OPERAND (t, 0)) == COMPONENT_REF)
+    {
+      t = TREE_OPERAND (t, 0);
+      deref_toplevel = t;
+    }
+  else
+    deref_toplevel = t;
+
+  /* Strip off expression nodes that may enclose a COMPONENT_REF.  Look through
+     references, but not indirections through pointers.  */
+  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 (reference_ref_p (t))
+	t = TREE_OPERAND (t, 0);
+      else
+	break;
+    }
+
+  outer_virtual_base = t;
+
+  if (TREE_CODE (t) == COMPONENT_REF)
+    component_access = true;
+}
+
+tree
+c_omp_address_inspector::analyze_components (bool checking)
+{
+  if (root_term)
+    return root_term;
+
+  gcc_assert (TREE_CODE (outer_virtual_base) == COMPONENT_REF);
+
+  tree t = outer_virtual_base;
+
+  if (checking
+      && 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 error_mark_node;
+    }
+  else if (checking
+	   && !processing_template_decl_p ()
+	   && !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 error_mark_node;
+    }
+  else if (checking && 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 error_mark_node;
+    }
+
+  while (TREE_CODE (t) == COMPONENT_REF)
+    {
+      if (checking
+	  && TREE_TYPE (TREE_OPERAND (t, 0))
+	  && TREE_CODE (TREE_TYPE (TREE_OPERAND (t, 0))) == UNION_TYPE)
+	{
+	  error_at (OMP_CLAUSE_LOCATION (clause),
+		    "%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;
+}
+
+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;
+
+  /* Get the innermost point recorded so far.  */
+  tree t = root_term ? root_term : outer_virtual_base;
+
+  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)
+    if (TREE_CODE (t) == COMPOUND_EXPR)
+      t = TREE_OPERAND (t, 1);
+    else
+      t = TREE_OPERAND (t, 0);
+
+  map_supported = DECL_P (t);
+
+  return map_supported;
+}
+
+bool
+c_omp_address_inspector::mappable_type (tree t)
+{
+  return lang_hooks.types.omp_mappable_type (t);
+}
+
 tree
 c_omp_decompose_attachable_address (tree t, tree *virtbase)
 {
diff --git a/gcc/c/c-typeck.c b/gcc/c/c-typeck.c
index cfac7d0a2b5..3a9f129181b 100644
--- a/gcc/c/c-typeck.c
+++ b/gcc/c/c-typeck.c
@@ -13208,6 +13208,8 @@ handle_omp_array_sections_1 (tree c, tree t, vec<tree> &types,
     {
       if (error_operand_p (t))
 	return error_mark_node;
+      c_omp_address_inspector t_insp (c, t);
+      t_insp.init ();
       ret = t;
       if (OMP_CLAUSE_CODE (c) != OMP_CLAUSE_AFFINITY
 	  && OMP_CLAUSE_CODE (c) != OMP_CLAUSE_DEPEND
@@ -13217,59 +13219,15 @@ 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 (TREE_CODE (t) == INDIRECT_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
+      if (t_insp.component_access_p ()
 	  && (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
-		     || TREE_CODE (t) == INDIRECT_REF
-		     || 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);
-		}
-	    }
-	}
+	t = t_insp.analyze_components (true);
+      else
+	t = t_insp.get_outer_virtual_base ();
+      if (t == error_mark_node)
+	return error_mark_node;
       if (!VAR_P (t) && TREE_CODE (t) != PARM_DECL)
 	{
 	  if (DECL_P (t))
@@ -14917,23 +14875,16 @@ 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 t_insp (c, t);
+		  t_insp.init ();
+
+		  tree ovb = t_insp.get_outer_virtual_base ();
+
+		  if (TREE_CODE (ovb) == COMPONENT_REF
+		      && TREE_CODE (TREE_TYPE (ovb)) == ARRAY_TYPE)
 		    {
-		      do
-			{
-			  t = TREE_OPERAND (t, 0);
-			  if (TREE_CODE (t) == MEM_REF
-			      || TREE_CODE (t) == INDIRECT_REF
-			      || 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) == COMPONENT_REF);
+		      t = t_insp.analyze_components (false);
 
 		      if (OMP_CLAUSE_CODE (c) == OMP_CLAUSE_MAP
 			  && OMP_CLAUSE_MAP_IMPLICIT (c)
@@ -15001,96 +14952,35 @@ 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.c.  */
 	    OMP_CLAUSE_SIZE (c) = size_zero_node;
-	  while (TREE_CODE (t) == INDIRECT_REF
-		 || 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
-		  || TREE_CODE (TREE_OPERAND (t, 0)) == INDIRECT_REF
-		  || 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 (!lang_hooks.types.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
-			 || TREE_CODE (t) == INDIRECT_REF
-			 || 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)
+	  {
+	    c_omp_address_inspector t_insp (c, t);
+	    t_insp.init ();
+
+	    if (t_insp.component_access_p ()
+		&& OMP_CLAUSE_CODE (c) != OMP_CLAUSE__CACHE_)
+	      t = t_insp.analyze_components (true);
+	    else
+	      t = t_insp.get_outer_virtual_base ();
+
+	    if (t == error_mark_node)
+	      {
+		remove = true;
 		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;
-		}
-	    }
+	      }
+
+	    indir_component_ref_p = t_insp.indir_component_ref_p ();
+
+	    if (t_insp.component_access_p ()
+		&& (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),
diff --git a/gcc/cp/semantics.c b/gcc/cp/semantics.c
index 6d30a9ed97d..a95eb1adcb5 100644
--- a/gcc/cp/semantics.c
+++ b/gcc/cp/semantics.c
@@ -4996,6 +4996,39 @@ 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
+{
+protected:
+  bool reference_ref_p (tree t)
+  {
+    return REFERENCE_REF_P (t);
+  }
+
+  bool processing_template_decl_p ()
+  {
+    return processing_template_decl;
+  }
+
+  bool mappable_type (tree t)
+  {
+    return cp_omp_mappable_type (t);
+  }
+
+  void emit_unmappable_type_notes (tree t)
+  {
+    cp_omp_emit_unmappable_type_notes (t);
+  }
+
+public:
+  cp_omp_address_inspector (tree c, tree t) : c_omp_address_inspector (c, t)
+  { }
+
+  ~cp_omp_address_inspector ()
+  { }
+};
+
 /* 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
@@ -5026,59 +5059,18 @@ handle_omp_array_sections_1 (tree c, tree t, vec<tree> &types,
     {
       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 (TREE_CODE (t) == INDIRECT_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
+      cp_omp_address_inspector t_insp (c, t);
+      t_insp.init ();
+      if (t_insp.component_access_p ()
 	  && (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
-		     || TREE_CODE (t) == INDIRECT_REF
-		     || 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);
-	}
+	      || OMP_CLAUSE_CODE (c) == OMP_CLAUSE_FROM))
+	t = t_insp.analyze_components (true);
+      else
+	t = t_insp.get_outer_virtual_base ();
+      if (t == error_mark_node)
+	return error_mark_node;
+      ret = t_insp.get_deref_toplevel ();
       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)
@@ -7907,27 +7899,15 @@ 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)
+
+		  cp_omp_address_inspector t_insp (c, t);
+		  t_insp.init ();
+		  tree ovb = t_insp.get_outer_virtual_base ();
+
+		  if (TREE_CODE (ovb) == COMPONENT_REF
+		      && TREE_CODE (TREE_TYPE (ovb)) == ARRAY_TYPE)
 		    {
-		      do
-			{
-			  t = TREE_OPERAND (t, 0);
-			  if (REFERENCE_REF_P (t))
-			    t = TREE_OPERAND (t, 0);
-			  if (TREE_CODE (t) == MEM_REF
-			      || TREE_CODE (t) == INDIRECT_REF
-			      || 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);
-			    }
-			}
-		      while (TREE_CODE (t) == COMPONENT_REF);
+		      t = t_insp.analyze_components (false);
 
 		      if (OMP_CLAUSE_CODE (c) == OMP_CLAUSE_MAP
 			  && OMP_CLAUSE_MAP_IMPLICIT (c)
@@ -7995,106 +7975,38 @@ 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.c.  */
 	    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 (TREE_CODE (t) == INDIRECT_REF
-		 || 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)) == INDIRECT_REF
-		  || TREE_CODE (TREE_OPERAND (t, 0)) == ARRAY_REF))
-	    {
-	      t = TREE_OPERAND (TREE_OPERAND (t, 0), 0);
-	      indir_component_ref_p = true;
-	      if (REFERENCE_REF_P (t))
-		t = TREE_OPERAND (t, 0);
-	      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))
+
+	  {
+	    cp_omp_address_inspector t_insp (c, t);
+	    t_insp.init ();
+
+	    if (t_insp.component_access_p ()
+		&& OMP_CLAUSE_CODE (c) != OMP_CLAUSE__CACHE_)
+	      t = t_insp.analyze_components (true);
+	    else
+	      t = t_insp.get_outer_virtual_base ();
+	    if (OMP_CLAUSE_CODE (c) == OMP_CLAUSE_MAP
+		&& OMP_CLAUSE_MAP_KIND (c) != GOMP_MAP_ATTACH_DETACH)
+	      OMP_CLAUSE_DECL (c) = t_insp.get_deref_toplevel ();
+	    if (type_dependent_expression_p (t_insp.get_deref_toplevel ()))
+	      break;
+	    if (t == error_mark_node)
+	      {
+		remove = true;
 		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 (!cp_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)]);
-		  cp_omp_emit_unmappable_type_notes (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
-			 || TREE_CODE (t) == INDIRECT_REF
-			 || 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)
+	      }
+	    indir_component_ref_p = t_insp.indir_component_ref_p ();
+	    if (t_insp.component_access_p ()
+		&& (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);
diff --git a/gcc/testsuite/g++.dg/gomp/unmappable-component-1.C b/gcc/testsuite/g++.dg/gomp/unmappable-component-1.C
new file mode 100644
index 00000000000..6fbc49616b1
--- /dev/null
+++ b/gcc/testsuite/g++.dg/gomp/unmappable-component-1.C
@@ -0,0 +1,21 @@
+/* { dg-do compile } */
+
+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) // { dg-error "'b->B::a' does not have a mappable type in 'map' clause" }
+  ;
+  B bb;
+#pragma omp target map(bb.a) // { dg-error "'bb\.B::a' does not have a mappable type in 'map' clause" }
+  ;
+  delete b;
+}
-- 
2.29.2


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

end of thread, other threads:[~2021-11-25 14:10 UTC | newest]

Thread overview: 5+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2021-11-25 14:10 [PATCH 10/16] OpenMP: Fix non-zero attach/detach bias for struct dereferences Julian Brown
2021-11-25 14:10 ` [PATCH 11/16] OpenMP: Handle reference-typed struct members Julian Brown
2021-11-25 14:10 ` [PATCH 12/16] OpenACC: Make deep-copy-arrayofstruct.c a libgomp/runtime test Julian Brown
2021-11-25 14:10 ` [PATCH 13/16] Add debug_omp_expr Julian Brown
2021-11-25 14:10 ` [PATCH 14/16] OpenMP: Add inspector class to unify mapped address analysis Julian Brown

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