[OpenMP,C/C++] Handle array reference base-pointers in array sections

Message ID 65e1462e-d17f-2975-1401-358fe9c69e28@codesourcery.com
State New
Headers
Series [OpenMP,C/C++] Handle array reference base-pointers in array sections |

Commit Message

Chung-Lin Tang Feb. 21, 2022, 3:18 p.m. UTC
  Hi Jakub,
as encountered in cases where a program constructs its own deep-copying
for arrays-of-pointers, e.g:

    #pragma omp target enter data map(to:level->vectors[:N])
    for (i = 0; i < N; i++)
      #pragma omp target enter data map(to:level->vectors[i][:N])

We need to treat the part of the array reference before the array section
as a base-pointer (here 'level->vectors[i]'), providing pointer-attachment behavior.

This patch adds this inside handle_omp_array_sections(), tracing the whole
sequence of array dimensions, creating a whole base-pointer reference
iteratively using build_array_ref(). The conditions are that each of the
"absorbed" dimensions must be length==1, and the final reference must be
of pointer-type (so that pointer attachment makes sense).

There's also a little patch in gimplify_scan_omp_clauses(), to make sure
the array-ref base-pointer goes down the right path.

This case was encountered when working to make 534.hpgmgfv_t from
SPEChpc 2021 properly compile. Tested without regressions on trunk.
Okay to go in once stage1 opens?

Thanks,
Chung-Lin

2022-02-21  Chung-Lin Tang  <cltang@codesourcery.com>

gcc/c/ChangeLog:

	* c-typeck.cc (handle_omp_array_sections): Add handling for
	creating array-reference base-pointer attachment clause.

gcc/cp/ChangeLog:

	* semantics.cc (handle_omp_array_sections): Add handling for
	creating array-reference base-pointer attachment clause.

gcc/ChangeLog:

	* gimplify.cc (gimplify_scan_omp_clauses): Add case for
	attach/detach map kind for ARRAY_REF of POINTER_TYPE.

gcc/testsuite/ChangeLog:

	* c-c++-common/gomp/target-enter-data-1.c: Adjust testcase.

libgomp/testsuite/ChangeLog:

	* libgomp.c-c++-common/ptr-attach-2.c: New test.
  

Comments

Jakub Jelinek May 5, 2022, 8:52 a.m. UTC | #1
On Mon, Feb 21, 2022 at 11:18:57PM +0800, Chung-Lin Tang wrote:
> as encountered in cases where a program constructs its own deep-copying
> for arrays-of-pointers, e.g:
> 
>    #pragma omp target enter data map(to:level->vectors[:N])
>    for (i = 0; i < N; i++)
>      #pragma omp target enter data map(to:level->vectors[i][:N])
> 
> We need to treat the part of the array reference before the array section
> as a base-pointer (here 'level->vectors[i]'), providing pointer-attachment behavior.
> 
> This patch adds this inside handle_omp_array_sections(), tracing the whole
> sequence of array dimensions, creating a whole base-pointer reference
> iteratively using build_array_ref(). The conditions are that each of the
> "absorbed" dimensions must be length==1, and the final reference must be
> of pointer-type (so that pointer attachment makes sense).
> 
> There's also a little patch in gimplify_scan_omp_clauses(), to make sure
> the array-ref base-pointer goes down the right path.
> 
> This case was encountered when working to make 534.hpgmgfv_t from
> SPEChpc 2021 properly compile. Tested without regressions on trunk.
> Okay to go in once stage1 opens?

I'm afraid this is going in the wrong direction.  The OpenMP 5.0 change
that:
"A locator list item is any lvalue expression, including variables, or an array
section."
is much more general than just allowing -> in the expressions, there can be
function calls and many other.  So, the more code like this patch we add,
the more we'll need to throw away again.  And as it is in OpenMP 5.0, we
really need to throw it away in the GCC 13 cycle.

So, what we really need is add OMP_ARRAY_SECTION tree code, some parser flag
to know that we are inside of an OpenMP clause that allows array sections,
and just where we normally parse ARRAY_REFs if that flag is on also parse
array sections and parse the map/to/from clauses just as normal expressions.
At least for the C FE maybe we'll need to arrange for less folding to be
done because C still folds too much stuff prematurely.
Then when finishing clauses verify that OMP_ARRAY_SECTION trees appear only
where we allow them and not elsewhere (say
foo (1, 2, 3)[:36]
would be ok if foo returns a pointer, but
foo (ptr[0:13], 2, 3)
would not) and then need to differentiate between the cases listed in the
standard which we handle for each . -> [idx] when starting from a var
(in such a case I vaguely recall there are rules for pointer attachments
etc.) or other arbitrary expressions (in that case we just evaluate those
expressions and e.g. in the foo (1, 2, 3)[:36] case basically do
tmp = foo (1, 2, 3);
and mapping of tmp[:36].

> 2022-02-21  Chung-Lin Tang  <cltang@codesourcery.com>
> 
> gcc/c/ChangeLog:
> 
> 	* c-typeck.cc (handle_omp_array_sections): Add handling for
> 	creating array-reference base-pointer attachment clause.
> 
> gcc/cp/ChangeLog:
> 
> 	* semantics.cc (handle_omp_array_sections): Add handling for
> 	creating array-reference base-pointer attachment clause.
> 
> gcc/ChangeLog:
> 
> 	* gimplify.cc (gimplify_scan_omp_clauses): Add case for
> 	attach/detach map kind for ARRAY_REF of POINTER_TYPE.
> 
> gcc/testsuite/ChangeLog:
> 
> 	* c-c++-common/gomp/target-enter-data-1.c: Adjust testcase.
> 
> libgomp/testsuite/ChangeLog:
> 
> 	* libgomp.c-c++-common/ptr-attach-2.c: New test.

	Jakub
  
Julian Brown May 5, 2022, 11:46 a.m. UTC | #2
On Thu, 5 May 2022 10:52:57 +0200
Jakub Jelinek via Gcc-patches <gcc-patches@gcc.gnu.org> wrote:

> On Mon, Feb 21, 2022 at 11:18:57PM +0800, Chung-Lin Tang wrote:
> > as encountered in cases where a program constructs its own
> > deep-copying for arrays-of-pointers, e.g:
> > 
> >    #pragma omp target enter data map(to:level->vectors[:N])
> >    for (i = 0; i < N; i++)
> >      #pragma omp target enter data map(to:level->vectors[i][:N])
> > 
> > We need to treat the part of the array reference before the array
> > section as a base-pointer (here 'level->vectors[i]'), providing
> > pointer-attachment behavior.
> > 
> > This patch adds this inside handle_omp_array_sections(), tracing
> > the whole sequence of array dimensions, creating a whole
> > base-pointer reference iteratively using build_array_ref(). The
> > conditions are that each of the "absorbed" dimensions must be
> > length==1, and the final reference must be of pointer-type (so that
> > pointer attachment makes sense).
> > 
> > There's also a little patch in gimplify_scan_omp_clauses(), to make
> > sure the array-ref base-pointer goes down the right path.
> > 
> > This case was encountered when working to make 534.hpgmgfv_t from
> > SPEChpc 2021 properly compile. Tested without regressions on trunk.
> > Okay to go in once stage1 opens?  
> 
> I'm afraid this is going in the wrong direction.  The OpenMP 5.0
> change that:
> "A locator list item is any lvalue expression, including variables,
> or an array section."
> is much more general than just allowing -> in the expressions, there
> can be function calls and many other.  So, the more code like this
> patch we add, the more we'll need to throw away again.  And as it is
> in OpenMP 5.0, we really need to throw it away in the GCC 13 cycle.
> 
> So, what we really need is add OMP_ARRAY_SECTION tree code, some
> parser flag to know that we are inside of an OpenMP clause that
> allows array sections, and just where we normally parse ARRAY_REFs if
> that flag is on also parse array sections and parse the map/to/from
> clauses just as normal expressions.

All the above (at least) has been done as part of the patch series
posted here:

https://gcc.gnu.org/pipermail/gcc-patches/2022-March/591973.html

> At least for the C FE maybe we'll
> need to arrange for less folding to be done because C still folds too
> much stuff prematurely. Then when finishing clauses verify that
> OMP_ARRAY_SECTION trees appear only where we allow them and not
> elsewhere (say foo (1, 2, 3)[:36]
> would be ok if foo returns a pointer, but
> foo (ptr[0:13], 2, 3)
> would not) and then need to differentiate between the cases listed in
> the standard which we handle for each . -> [idx] when starting from a
> var (in such a case I vaguely recall there are rules for pointer
> attachments etc.) or other arbitrary expressions (in that case we
> just evaluate those expressions and e.g. in the foo (1, 2, 3)[:36]
> case basically do tmp = foo (1, 2, 3);
> and mapping of tmp[:36].

...which also changes/refactors quite a lot regarding how lowering
clauses into mapping nodes works (the "address inspector" bits).
"Weird" cases like mapping the return value from functions doesn't
necessarily DTRT yet -- it wasn't entirely clear how that should/could
work, I don't think.

HTH,

Julian
  
Jakub Jelinek May 5, 2022, 12:40 p.m. UTC | #3
On Thu, May 05, 2022 at 12:46:29PM +0100, Julian Brown wrote:
> All the above (at least) has been done as part of the patch series
> posted here:
> 
> https://gcc.gnu.org/pipermail/gcc-patches/2022-March/591973.html

Ah, ok, so is this patch superseded by that series, or do you want to apply
it just to be removed again?

> > At least for the C FE maybe we'll
> > need to arrange for less folding to be done because C still folds too
> > much stuff prematurely. Then when finishing clauses verify that
> > OMP_ARRAY_SECTION trees appear only where we allow them and not
> > elsewhere (say foo (1, 2, 3)[:36]
> > would be ok if foo returns a pointer, but
> > foo (ptr[0:13], 2, 3)
> > would not) and then need to differentiate between the cases listed in
> > the standard which we handle for each . -> [idx] when starting from a
> > var (in such a case I vaguely recall there are rules for pointer
> > attachments etc.) or other arbitrary expressions (in that case we
> > just evaluate those expressions and e.g. in the foo (1, 2, 3)[:36]
> > case basically do tmp = foo (1, 2, 3);
> > and mapping of tmp[:36].
> 
> ...which also changes/refactors quite a lot regarding how lowering
> clauses into mapping nodes works (the "address inspector" bits).
> "Weird" cases like mapping the return value from functions doesn't
> necessarily DTRT yet -- it wasn't entirely clear how that should/could
> work, I don't think.

I vaguely remember that the ./-/[] handling applies only when it starts
from a variable and as soon as one triggers something else, perhaps
including just *& or similar stuff then only the final lvalue is mapped and
nothing else, but dunno if it is from just discussions about the topic or
what is actually written in the spec, will need to look it up.

	Jakub
  
Julian Brown May 5, 2022, 3:59 p.m. UTC | #4
On Thu, 5 May 2022 14:40:38 +0200
Jakub Jelinek via Gcc-patches <gcc-patches@gcc.gnu.org> wrote:

> On Thu, May 05, 2022 at 12:46:29PM +0100, Julian Brown wrote:
> > All the above (at least) has been done as part of the patch series
> > posted here:
> > 
> > https://gcc.gnu.org/pipermail/gcc-patches/2022-March/591973.html  
> 
> Ah, ok, so is this patch superseded by that series, or do you want to
> apply it just to be removed again?

Regarding this one (Chung-Lin's, not mine), I'm not sure -- I don't
know if it addresses a problem that is still present with my patch
series applied, nor do I know if my series and this patch have been
tested together. We might need to do some work to integrate the bits,
one way or the other.

Thanks,

Julian
  

Patch

diff --git a/gcc/c/c-typeck.cc b/gcc/c/c-typeck.cc
index 3075c883548..4257e373557 100644
--- a/gcc/c/c-typeck.cc
+++ b/gcc/c/c-typeck.cc
@@ -13649,6 +13649,10 @@  handle_omp_array_sections (tree c, enum c_omp_region_type ort)
       if (int_size_in_bytes (TREE_TYPE (first)) <= 0)
 	maybe_zero_len = true;
 
+      struct dim { tree low_bound, length; };
+      auto_vec<dim> dims (num);
+      dims.safe_grow (num);
+
       for (i = num, t = OMP_CLAUSE_DECL (c); i > 0;
 	   t = TREE_CHAIN (t))
 	{
@@ -13763,6 +13767,9 @@  handle_omp_array_sections (tree c, enum c_omp_region_type ort)
 	      else
 		size = size_binop (MULT_EXPR, size, l);
 	    }
+
+	  dim d = { low_bound, length };
+	  dims[i] = d;
 	}
       if (side_effects)
 	size = build2 (COMPOUND_EXPR, sizetype, side_effects, size);
@@ -13802,6 +13809,23 @@  handle_omp_array_sections (tree c, enum c_omp_region_type ort)
 	  OMP_CLAUSE_DECL (c) = t;
 	  return false;
 	}
+
+      tree aref = t;
+      for (i = 0; i < dims.length (); i++)
+	{
+	  if (dims[i].length && integer_onep (dims[i].length))
+	    {
+	      tree lb = dims[i].low_bound;
+	      aref = build_array_ref (OMP_CLAUSE_LOCATION (c), aref, lb);
+	    }
+	  else
+	    {
+	      if (TREE_CODE (TREE_TYPE (aref)) == POINTER_TYPE)
+		t = aref;
+	      break;
+	    }
+	}
+
       first = c_fully_fold (first, false, NULL);
       OMP_CLAUSE_DECL (c) = first;
       if (OMP_CLAUSE_CODE (c) == OMP_CLAUSE_HAS_DEVICE_ADDR)
@@ -13836,7 +13860,8 @@  handle_omp_array_sections (tree c, enum c_omp_region_type ort)
 	  break;
 	}
       tree c2 = build_omp_clause (OMP_CLAUSE_LOCATION (c), OMP_CLAUSE_MAP);
-      if (TREE_CODE (t) == COMPONENT_REF)
+      if (TREE_CODE (t) == COMPONENT_REF || TREE_CODE (t) == ARRAY_REF
+	  || TREE_CODE (t) == INDIRECT_REF)
 	OMP_CLAUSE_SET_MAP_KIND (c2, GOMP_MAP_ATTACH_DETACH);
       else
 	OMP_CLAUSE_SET_MAP_KIND (c2, GOMP_MAP_FIRSTPRIVATE_POINTER);
diff --git a/gcc/cp/semantics.cc b/gcc/cp/semantics.cc
index 0cb17a6a8ab..646f4883d66 100644
--- a/gcc/cp/semantics.cc
+++ b/gcc/cp/semantics.cc
@@ -5497,6 +5497,10 @@  handle_omp_array_sections (tree c, enum c_omp_region_type ort)
       if (processing_template_decl && maybe_zero_len)
 	return false;
 
+      struct dim { tree low_bound, length; };
+      auto_vec<dim> dims (num);
+      dims.safe_grow (num);
+
       for (i = num, t = OMP_CLAUSE_DECL (c); i > 0;
 	   t = TREE_CHAIN (t))
 	{
@@ -5604,6 +5608,9 @@  handle_omp_array_sections (tree c, enum c_omp_region_type ort)
 	      else
 		size = size_binop (MULT_EXPR, size, l);
 	    }
+
+	  dim d = { low_bound, length };
+	  dims[i] = d;
 	}
       if (!processing_template_decl)
 	{
@@ -5647,6 +5654,24 @@  handle_omp_array_sections (tree c, enum c_omp_region_type ort)
 	      OMP_CLAUSE_DECL (c) = t;
 	      return false;
 	    }
+
+	  tree aref = t;
+	  for (i = 0; i < dims.length (); i++)
+	    {
+	      if (dims[i].length && integer_onep (dims[i].length))
+		{
+		  tree lb = dims[i].low_bound;
+		  aref = convert_from_reference (aref);
+		  aref = build_array_ref (OMP_CLAUSE_LOCATION (c), aref, lb);
+		}
+	      else
+		{
+		  if (TREE_CODE (TREE_TYPE (aref)) == POINTER_TYPE)
+		    t = aref;
+		  break;
+		}
+	    }
+
 	  OMP_CLAUSE_DECL (c) = first;
 	  if (OMP_CLAUSE_CODE (c) == OMP_CLAUSE_HAS_DEVICE_ADDR)
 	    return false;
@@ -5681,7 +5706,8 @@  handle_omp_array_sections (tree c, enum c_omp_region_type ort)
 	  bool reference_always_pointer = true;
 	  tree c2 = build_omp_clause (OMP_CLAUSE_LOCATION (c),
 				      OMP_CLAUSE_MAP);
-	  if (TREE_CODE (t) == COMPONENT_REF)
+	  if (TREE_CODE (t) == COMPONENT_REF || TREE_CODE (t) == ARRAY_REF
+	      || (TREE_CODE (t) == INDIRECT_REF && !REFERENCE_REF_P (t)))
 	    {
 	      OMP_CLAUSE_SET_MAP_KIND (c2, GOMP_MAP_ATTACH_DETACH);
 
diff --git a/gcc/gimplify.cc b/gcc/gimplify.cc
index f570daa015a..77b95cd8000 100644
--- a/gcc/gimplify.cc
+++ b/gcc/gimplify.cc
@@ -9626,7 +9626,10 @@  gimplify_scan_omp_clauses (tree *list_p, gimple_seq *pre_p,
 		   || (component_ref_p
 		       && (INDIRECT_REF_P (decl)
 			   || TREE_CODE (decl) == MEM_REF
-			   || TREE_CODE (decl) == ARRAY_REF)))
+			   || TREE_CODE (decl) == ARRAY_REF))
+		   || (TREE_CODE (decl) == ARRAY_REF
+		       && TREE_CODE (TREE_TYPE (decl)) == POINTER_TYPE
+		       && OMP_CLAUSE_MAP_KIND (c) == GOMP_MAP_ATTACH_DETACH))
 		  && OMP_CLAUSE_MAP_KIND (c) != GOMP_MAP_TO_PSET
 		  && OMP_CLAUSE_MAP_KIND (c) != GOMP_MAP_ATTACH
 		  && OMP_CLAUSE_MAP_KIND (c) != GOMP_MAP_DETACH
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..3a1b488fa1f 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\\(to:\\*_\[0-9\]+ \\\[len: _\[0-9\]+\\\]\\) map\\(attach:\\*_\[0-9\]+ \\\[bias: \[^\]\]+\\\]\\)" 1 "gimple" } } */
+/* { dg-final { scan-tree-dump-times "map\\(to:\\*_\[0-9\]+ \\\[len: _\[0-9\]+\\\]\\) map\\(attach:\[^-\]+->vectors \\\[bias: \[^\]\]+\\\]\\)" 2 "gimple" } } */
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;
+}