[v2,OpenMP,Fortran] Support in_reduction for Fortran

Message ID 6f7d3bb0-4771-fc39-be30-bcedafc988df@codesourcery.com
State Accepted
Headers
Series [v2,OpenMP,Fortran] Support in_reduction for Fortran |

Commit Message

Chung-Lin Tang Oct. 19, 2021, 1:03 p.m. UTC
  Hi Jakub,

On 2021/9/18 12:11 AM, Jakub Jelinek wrote:
>> @@ -3496,7 +3509,8 @@ static match
>>   match_omp (gfc_exec_op op, const omp_mask mask)
>>   {
>>     gfc_omp_clauses *c;
>> -  if (gfc_match_omp_clauses (&c, mask) != MATCH_YES)
>> +  if (gfc_match_omp_clauses (&c, mask, true, true, false,
>> +			     (op == EXEC_OMP_TARGET)) != MATCH_YES)
> 
> The ()s around op == EXEC_OMP_TARGET are unnecessary.

Fixed.

>> --- a/gcc/fortran/trans-openmp.c
>> +++ b/gcc/fortran/trans-openmp.c
>> @@ -6391,12 +6391,17 @@ gfc_trans_omp_task (gfc_code *code)
>>   static tree
>>   gfc_trans_omp_taskgroup (gfc_code *code)
>>   {
>> +  stmtblock_t block;
>> +  gfc_start_block (&block);
>>     tree body = gfc_trans_code (code->block->next);
>>     tree stmt = make_node (OMP_TASKGROUP);
>>     TREE_TYPE (stmt) = void_type_node;
>>     OMP_TASKGROUP_BODY (stmt) = body;
>> -  OMP_TASKGROUP_CLAUSES (stmt) = NULL_TREE;
>> -  return stmt;
>> +  OMP_TASKGROUP_CLAUSES (stmt) = gfc_trans_omp_clauses (&block,
>> +							code->ext.omp_clauses,
>> +							code->loc);
>> +  gfc_add_expr_to_block (&block, stmt);
> 
> If this was missing, then I'm afraid we lack a lot of testsuite coverage for
> Fortran task reductions.  It doesn't need to be covered in this patch, but would be
> good to cover it incrementally.  Because the above means nothing with
> taskgroup with task_reduction clause(s) could work properly at runtime.

Actually, the testcases do somewhat exercise taskgroup task_reductions, but like you
said, only lightly.

>> --- a/gcc/omp-low.c
>> +++ b/gcc/omp-low.c
>> @@ -1317,9 +1317,13 @@ scan_sharing_clauses (tree clauses, omp_context *ctx)
>>   	  if (is_omp_target (ctx->stmt))
>>   	    {
>>   	      tree at = decl;
>> +	      omp_context *scan_ctx = ctx;
>>   	      if (ctx->outer)
>> -		scan_omp_op (&at, ctx->outer);
>> -	      tree nt = omp_copy_decl_1 (at, ctx);
>> +		{
>> +		  scan_omp_op (&at, ctx->outer);
>> +		  scan_ctx = ctx->outer;
>> +		}
>> +	      tree nt = omp_copy_decl_1 (at, scan_ctx);
>>   	      splay_tree_insert (ctx->field_map,
>>   				 (splay_tree_key) &DECL_CONTEXT (decl),
>>   				 (splay_tree_value) nt);
> 
> You're right that the var remembered with &DECL_CONTEXT (whatever) key is
> used outside of the target construct rather than inside of it.
> So, if ctx->outer is non-NULL, it seems right to create the var in that
> outer context.  But, if ctx->outer is NULL, which can happen if the
> target construct is orphaned, consider e.g.
> extern int &x;
> extern int &y;
> 
> void
> foo ()
> {
>    #pragma omp target in_reduction (+: x, y)
>    {
>      x = x + 8;
>      y = y + 16;
>    }
> }
> 
> void
> bar ()
> {
>    #pragma omp taskgroup task_reduction (+: x, y)
>    foo ();
> }
> then those artificial decls (copies of x and y) should appear
> to be at the function scope and not inside of the target region.
> 
> Therefore, I wonder if omp_copy_decl_2 shouldn't do the
>    DECL_CONTEXT (copy) = current_function_decl;
>    DECL_CHAIN (copy) = ctx->block_vars;
>    ctx->block_vars = copy;
> (the last one can be moved next to the others) only if ctx != NULL
> and otherwise call gimple_add_tmp_var (copy); instead
> and then just call omp_copy_decl_1 at that spot with unconditional
> ctx->outer.

I see what you mean. I tried gimple_add_tmp_var but didn't work due to a
!DECL_SEEN_IN_BIND_EXPR_P() assert fail, but record_vars() appears to work.

> Also, this isn't the only place that should have such a change,
> there is also
>                    if (ctx->outer)
>                      scan_omp_op (&at, ctx->outer);
>                    tree nt = omp_copy_decl_1 (at, ctx);
>                    splay_tree_insert (ctx->field_map,
>                                       (splay_tree_key) &DECL_CONTEXT (t),
>                                       (splay_tree_value) nt);
> a few lines above this and I'd expect that it should be (at, ctx->outer)
> as well.

Fixed.

>> @@ -1339,7 +1343,9 @@ scan_sharing_clauses (tree clauses, omp_context *ctx)
>>   	      if (!is_global_var (maybe_lookup_decl_in_outer_ctx (decl, ctx)))
>>   		{
>>   		  by_ref = use_pointer_for_field (decl, ctx);
>> -		  if (OMP_CLAUSE_CODE (c) == OMP_CLAUSE_IN_REDUCTION)
>> +		  if (OMP_CLAUSE_CODE (c) == OMP_CLAUSE_IN_REDUCTION
>> +		      && !splay_tree_lookup (ctx->field_map,
>> +					     (splay_tree_key) decl))
>>   		    install_var_field (decl, by_ref, 3, ctx);
>>   		}
>>   	      install_var_local (decl, ctx);
> 
> When exactly do you need this?  It doesn't trigger on the new libgomp
> testcase...

I remember there was a testcase with triggered an ICE without this, but for some
reason can't find it anymore. I don't have any more evidence this is needed, so
removed now.

>> --- /dev/null
>> +++ b/libgomp/testsuite/libgomp.fortran/target-in-reduction-1.f90
>> @@ -0,0 +1,33 @@
>> +! { dg-do run }
>> +
>> +subroutine foo (x, y)
...
>> +  if (x .ne. 11) stop 1
>> +  if (y .ne. 21) stop 2
>> +
>> +end program main
> 
> Again, something that can be dealt incrementally, but the
> testsuite coverage of
> https://gcc.gnu.org/pipermail/gcc-patches/2021-June/573600.html
> was larger than this.  Would be nice e.g. to cover both scalar vars
> and array sections/arrays, parameters passed by reference as in the
> above testcase, but also something that isn't a reference (either a local
> variable or dummy parameter with VALUE, etc.
> 
> 	Jakub

I have expanded target-in-reduction-1.f90 to cover local variables and
VALUE passed parameters. Array sections in reductions appear to be still
not supported by the Fortran FE in general (Tobias plans to work on that later).

I also added another target-in-reduction-2.f90 testcase that tests the "orphaned"
case in Fortran, where the task/target-in_reduction is in another separate subroutine.

Tested without regressions on trunk, is this okay to commit?

Thanks,
Chung-Lin

2021-10-19  Chung-Lin Tang  <cltang@codesourcery.com>

gcc/fortran/ChangeLog:

	* openmp.c (gfc_match_omp_clause_reduction): Add 'openmp_target' default
	false parameter. Add 'always,tofrom' map for OMP_LIST_IN_REDUCTION case.
	(gfc_match_omp_clauses): Add 'openmp_target' default false parameter,
	adjust call to gfc_match_omp_clause_reduction.
	(match_omp): Adjust call to gfc_match_omp_clauses
	* trans-openmp.c (gfc_trans_omp_taskgroup): Add call to
	gfc_match_omp_clause, create and return block.

gcc/ChangeLog:

	* omp-low.c (omp_copy_decl_2): For !ctx, use record_vars to add new copy
	as local variable.
	(scan_sharing_clauses): Place copy of OMP_CLAUSE_IN_REDUCTION decl in
	ctx->outer instead of ctx.

gcc/testsuite/ChangeLog:

	* gfortran.dg/gomp/reduction4.f90: Adjust omp target in_reduction' scan
	pattern.

libgomp/ChangeLog:

	* testsuite/libgomp.fortran/target-in-reduction-1.f90: New test.
	* testsuite/libgomp.fortran/target-in-reduction-2.f90: New test.
  

Comments

Jakub Jelinek Oct. 19, 2021, 2:05 p.m. UTC | #1
On Tue, Oct 19, 2021 at 09:03:06PM +0800, Chung-Lin Tang wrote:
> 2021-10-19  Chung-Lin Tang  <cltang@codesourcery.com>
> 
> gcc/fortran/ChangeLog:
> 
> 	* openmp.c (gfc_match_omp_clause_reduction): Add 'openmp_target' default
> 	false parameter. Add 'always,tofrom' map for OMP_LIST_IN_REDUCTION case.
> 	(gfc_match_omp_clauses): Add 'openmp_target' default false parameter,
> 	adjust call to gfc_match_omp_clause_reduction.
> 	(match_omp): Adjust call to gfc_match_omp_clauses
> 	* trans-openmp.c (gfc_trans_omp_taskgroup): Add call to
> 	gfc_match_omp_clause, create and return block.
> 
> gcc/ChangeLog:
> 
> 	* omp-low.c (omp_copy_decl_2): For !ctx, use record_vars to add new copy
> 	as local variable.
> 	(scan_sharing_clauses): Place copy of OMP_CLAUSE_IN_REDUCTION decl in
> 	ctx->outer instead of ctx.
> 
> gcc/testsuite/ChangeLog:
> 
> 	* gfortran.dg/gomp/reduction4.f90: Adjust omp target in_reduction' scan
> 	pattern.
> 
> libgomp/ChangeLog:
> 
> 	* testsuite/libgomp.fortran/target-in-reduction-1.f90: New test.
> 	* testsuite/libgomp.fortran/target-in-reduction-2.f90: New test.

LGTM, thanks.

	Jakub
  

Patch

diff --git a/gcc/fortran/openmp.c b/gcc/fortran/openmp.c
index 6a4ca2868f8..210fb06dbec 100644
--- a/gcc/fortran/openmp.c
+++ b/gcc/fortran/openmp.c
@@ -1138,7 +1138,7 @@  failed:
 
 static match
 gfc_match_omp_clause_reduction (char pc, gfc_omp_clauses *c, bool openacc,
-				bool allow_derived)
+				bool allow_derived, bool openmp_target = false)
 {
   if (pc == 'r' && gfc_match ("reduction ( ") != MATCH_YES)
     return MATCH_NO;
@@ -1285,6 +1285,19 @@  gfc_match_omp_clause_reduction (char pc, gfc_omp_clauses *c, bool openacc,
 	    n->u2.udr = gfc_get_omp_namelist_udr ();
 	    n->u2.udr->udr = udr;
 	  }
+	if (openmp_target && list_idx == OMP_LIST_IN_REDUCTION)
+	  {
+	    gfc_omp_namelist *p = gfc_get_omp_namelist (), **tl;
+	    p->sym = n->sym;
+	    p->where = p->where;
+	    p->u.map_op = OMP_MAP_ALWAYS_TOFROM;
+
+	    tl = &c->lists[OMP_LIST_MAP];
+	    while (*tl)
+	      tl = &((*tl)->next);
+	    *tl = p;
+	    p->next = NULL;
+	  }
      }
   return MATCH_YES;
 }
@@ -1353,7 +1366,7 @@  gfc_match_dupl_atomic (bool not_dupl, const char *name)
 static match
 gfc_match_omp_clauses (gfc_omp_clauses **cp, const omp_mask mask,
 		       bool first = true, bool needs_space = true,
-		       bool openacc = false)
+		       bool openacc = false, bool openmp_target = false)
 {
   bool error = false;
   gfc_omp_clauses *c = gfc_get_omp_clauses ();
@@ -2057,8 +2070,8 @@  gfc_match_omp_clauses (gfc_omp_clauses **cp, const omp_mask mask,
 	      goto error;
 	    }
 	  if ((mask & OMP_CLAUSE_IN_REDUCTION)
-	      && gfc_match_omp_clause_reduction (pc, c, openacc,
-						 allow_derived) == MATCH_YES)
+	      && gfc_match_omp_clause_reduction (pc, c, openacc, allow_derived,
+						 openmp_target) == MATCH_YES)
 	    continue;
 	  if ((mask & OMP_CLAUSE_INBRANCH)
 	      && (m = gfc_match_dupl_check (!c->inbranch && !c->notinbranch,
@@ -3512,7 +3525,8 @@  static match
 match_omp (gfc_exec_op op, const omp_mask mask)
 {
   gfc_omp_clauses *c;
-  if (gfc_match_omp_clauses (&c, mask) != MATCH_YES)
+  if (gfc_match_omp_clauses (&c, mask, true, true, false,
+			     op == EXEC_OMP_TARGET) != MATCH_YES)
     return MATCH_ERROR;
   new_st.op = op;
   new_st.ext.omp_clauses = c;
diff --git a/gcc/fortran/trans-openmp.c b/gcc/fortran/trans-openmp.c
index d234d1b070f..56efe195257 100644
--- a/gcc/fortran/trans-openmp.c
+++ b/gcc/fortran/trans-openmp.c
@@ -6405,12 +6405,17 @@  gfc_trans_omp_task (gfc_code *code)
 static tree
 gfc_trans_omp_taskgroup (gfc_code *code)
 {
+  stmtblock_t block;
+  gfc_start_block (&block);
   tree body = gfc_trans_code (code->block->next);
   tree stmt = make_node (OMP_TASKGROUP);
   TREE_TYPE (stmt) = void_type_node;
   OMP_TASKGROUP_BODY (stmt) = body;
-  OMP_TASKGROUP_CLAUSES (stmt) = NULL_TREE;
-  return stmt;
+  OMP_TASKGROUP_CLAUSES (stmt) = gfc_trans_omp_clauses (&block,
+							code->ext.omp_clauses,
+							code->loc);
+  gfc_add_expr_to_block (&block, stmt);
+  return gfc_finish_block (&block);
 }
 
 static tree
diff --git a/gcc/omp-low.c b/gcc/omp-low.c
index 057b7ae4866..15e4424b0bc 100644
--- a/gcc/omp-low.c
+++ b/gcc/omp-low.c
@@ -591,7 +591,15 @@  omp_copy_decl_2 (tree var, tree name, tree type, omp_context *ctx)
   tree copy = copy_var_decl (var, name, type);
 
   DECL_CONTEXT (copy) = current_function_decl;
-  DECL_CHAIN (copy) = ctx->block_vars;
+
+  if (ctx)
+    {
+      DECL_CHAIN (copy) = ctx->block_vars;
+      ctx->block_vars = copy;
+    }
+  else
+    record_vars (copy);
+
   /* If VAR is listed in task_shared_vars, it means it wasn't
      originally addressable and is just because task needs to take
      it's address.  But we don't need to take address of privatizations
@@ -602,7 +610,6 @@  omp_copy_decl_2 (tree var, tree name, tree type, omp_context *ctx)
 	  || (global_nonaddressable_vars
 	      && bitmap_bit_p (global_nonaddressable_vars, DECL_UID (var)))))
     TREE_ADDRESSABLE (copy) = 0;
-  ctx->block_vars = copy;
 
   return copy;
 }
@@ -1281,7 +1288,7 @@  scan_sharing_clauses (tree clauses, omp_context *ctx)
 		  tree at = t;
 		  if (ctx->outer)
 		    scan_omp_op (&at, ctx->outer);
-		  tree nt = omp_copy_decl_1 (at, ctx);
+		  tree nt = omp_copy_decl_1 (at, ctx->outer);
 		  splay_tree_insert (ctx->field_map,
 				     (splay_tree_key) &DECL_CONTEXT (t),
 				     (splay_tree_value) nt);
@@ -1322,7 +1329,7 @@  scan_sharing_clauses (tree clauses, omp_context *ctx)
 	      tree at = decl;
 	      if (ctx->outer)
 		scan_omp_op (&at, ctx->outer);
-	      tree nt = omp_copy_decl_1 (at, ctx);
+	      tree nt = omp_copy_decl_1 (at, ctx->outer);
 	      splay_tree_insert (ctx->field_map,
 				 (splay_tree_key) &DECL_CONTEXT (decl),
 				 (splay_tree_value) nt);
diff --git a/gcc/testsuite/gfortran.dg/gomp/reduction4.f90 b/gcc/testsuite/gfortran.dg/gomp/reduction4.f90
index 52d504bac71..71b4231f315 100644
--- a/gcc/testsuite/gfortran.dg/gomp/reduction4.f90
+++ b/gcc/testsuite/gfortran.dg/gomp/reduction4.f90
@@ -137,7 +137,7 @@  end
 ! { dg-final { scan-tree-dump-times "#pragma omp sections reduction\\(task,\\\+:a\\)" 1 "original" } }
 ! { dg-final { scan-tree-dump-times "#pragma omp simd linear\\(i:1\\) reduction\\(\\\+:a\\)" 2 "original" } }
 ! { dg-final { scan-tree-dump-times "#pragma omp simd linear\\(i:1\\) reduction\\(task,\\\+:a\\)" 1 "original" } }
-! { dg-final { scan-tree-dump-times "#pragma omp target in_reduction\\(\\\+:b\\)" 1 "original" } }
+! { dg-final { scan-tree-dump-times "#pragma omp target map\\(always,tofrom:b\\) in_reduction\\(\\\+:b\\)" 1 "original" } }
 ! { dg-final { scan-tree-dump-times "#pragma omp task in_reduction\\(\\\+:a\\)" 1 "original" } }
 ! { dg-final { scan-tree-dump-times "#pragma omp teams reduction\\(\\\+:b\\)" 2 "original" } }
 ! { dg-final { scan-tree-dump-times "#pragma omp taskloop reduction\\(\\\+:a\\) in_reduction\\(\\\+:b\\)" 2 "original" } }
diff --git a/libgomp/testsuite/libgomp.fortran/target-in-reduction-1.f90 b/libgomp/testsuite/libgomp.fortran/target-in-reduction-1.f90
new file mode 100644
index 00000000000..f9acb711e67
--- /dev/null
+++ b/libgomp/testsuite/libgomp.fortran/target-in-reduction-1.f90
@@ -0,0 +1,78 @@ 
+! { dg-do run }
+
+module mod1
+  contains
+
+    subroutine foo (x, y)
+      integer :: x, y
+
+      !$omp taskgroup task_reduction (+: x, y)
+
+      !$omp target in_reduction (+: x, y)
+      x = x + 8
+      y = y + 16
+      !$omp end target
+
+      !$omp task in_reduction (+: x, y)
+      x = x + 2
+      y = y + 4
+      !$omp end task
+
+      !$omp end taskgroup
+    end subroutine foo
+
+    integer function bar (x)
+      integer, value :: x
+
+      !$omp taskgroup task_reduction (+: x)
+
+      !$omp target in_reduction (+: x)
+      x = x + 16
+      !$omp end target
+
+      !$omp task in_reduction (+: x)
+      x = x + 32
+      !$omp end task
+
+      !$omp end taskgroup
+
+      bar = x
+    end function bar
+  end module mod1
+
+program main
+  use mod1
+  integer :: x, y
+  real :: f;
+
+  x = 1
+  y = 1
+
+  call foo (x, y)
+
+  if (x .ne. 11) stop 1
+  if (y .ne. 21) stop 2
+
+  y = bar (8)
+  if (y .ne. 56) stop 3
+
+  x = 0
+  f = 0.0
+
+  !$omp taskgroup task_reduction (+: x, f)
+  !$omp target in_reduction (+: x, f)
+  x = x + 1
+  f = f + 2.0
+  !$omp end target
+
+  !$omp task in_reduction (+: x, f)
+  x = x + 2
+  f = f + 3.0
+  !$omp end task
+
+  !$omp end taskgroup
+
+  if (x .ne. 3) stop 4
+  if (f .ne. 5.0) stop 5
+
+end program main
diff --git a/libgomp/testsuite/libgomp.fortran/target-in-reduction-2.f90 b/libgomp/testsuite/libgomp.fortran/target-in-reduction-2.f90
new file mode 100644
index 00000000000..7f2e16b534b
--- /dev/null
+++ b/libgomp/testsuite/libgomp.fortran/target-in-reduction-2.f90
@@ -0,0 +1,30 @@ 
+! { dg-do run }
+
+program main
+  integer :: x
+
+  x = 0
+  !$omp taskgroup task_reduction (+: x)
+  call foo (x)
+  call bar (x)
+  !$omp end taskgroup
+
+  if (x .ne. 3) stop 1
+
+contains
+
+  subroutine foo (x)
+    integer :: x
+    !$omp task in_reduction (+: x)
+    x = x + 1
+    !$omp end task
+  end subroutine foo
+
+  subroutine bar (x)
+    integer :: x
+    !$omp target in_reduction (+: x)
+    x = x + 2
+    !$omp end target
+  end subroutine bar
+
+end program main