[committed] Fix lambda handling in OpenMP declare reduction (PR c++/60228)

Message ID 20191129091932.GH10088@tucnak
State New
Headers show
Series
  • [committed] Fix lambda handling in OpenMP declare reduction (PR c++/60228)
Related show

Commit Message

Jakub Jelinek Nov. 29, 2019, 9:19 a.m.
Hi!

When OpenMP declare reduction appears outside of block scope (i.e. namespace
scope or class scope), an artificial function that is actually never called
and should be never emitted is created to hold the statements of the
combiner and initializer from which then OpenMP reduction clause handling
picks the statements.  Unfortunately, with lambdas there can be various
issues as the testcases show.
One is in templates, where lambda parsing can add extra DECL_EXPRs and
popscope doesn't add a BIND_EXPR around so we can end up with multiple
statements returned from finish_omp_structured_block, while the reduction
handling code requires exactly one to be able to figure out what is what.
The rest of the fixes is making sure that the artificial functions aren't
actually genericized/gimplified which with the lambdas apparently happened
in some cases, that they don't show up in the mangled names (the functions
are artificially added and furthermore their names contain spaces etc.,
so assemblers aren't happy about that either).  Some of the changes handle
those like consteval functions, but something more is needed in two spots.

Bootstrapped/regtested on x86_64-linux and i686-linux, committed to trunk.

2019-11-29  Jakub Jelinek  <jakub@redhat.com>

	PR c++/60228
	* parser.c (cp_parser_omp_declare_reduction_exprs): If
	processing_template_decl, wrap the combiner or initializer
	into EXPR_STMT.
	* decl.c (start_preparsed_function): Don't start a lambda scope
	for DECL_OMP_DECLARE_REDUCTION_P functions.
	(finish_function): Don't finish a lambda scope for
	DECL_OMP_DECLARE_REDUCTION_P functions, nor cp_fold_function
	them nor cp_genericize them.
	* mangle.c (decl_mangling_context): Look through
	DECL_OMP_DECLARE_REDUCTION_P functions.
	* semantics.c (expand_or_defer_fn_1): For DECL_OMP_DECLARE_REDUCTION_P
	functions, use tentative linkage, don't keep their bodies with
	-fkeep-inline-functions and return false at the end.

	* g++.dg/gomp/openmp-simd-2.C: Don't expect bodies for
	DECL_OMP_DECLARE_REDUCTION_P functions.

	* testsuite/libgomp.c++/udr-20.C: New test.
	* testsuite/libgomp.c++/udr-21.C: New test.


	Jakub

Patch

--- gcc/cp/parser.c.jj	2019-11-28 09:02:26.931819871 +0100
+++ gcc/cp/parser.c	2019-11-28 11:30:22.273670201 +0100
@@ -41244,6 +41244,8 @@  cp_parser_omp_declare_reduction_exprs (t
   combiner = cp_parser_expression (parser);
   finish_expr_stmt (combiner);
   block = finish_omp_structured_block (block);
+  if (processing_template_decl)
+    block = build_stmt (input_location, EXPR_STMT, block);
   add_stmt (block);
 
   if (!cp_parser_require (parser, CPP_CLOSE_PAREN, RT_CLOSE_PAREN))
@@ -41348,6 +41350,8 @@  cp_parser_omp_declare_reduction_exprs (t
 
       block = finish_omp_structured_block (block);
       cp_walk_tree (&block, cp_remove_omp_priv_cleanup_stmt, omp_priv, NULL);
+      if (processing_template_decl)
+	block = build_stmt (input_location, EXPR_STMT, block);
       add_stmt (block);
 
       if (ctor)
--- gcc/cp/decl.c.jj	2019-11-28 09:02:26.470826954 +0100
+++ gcc/cp/decl.c	2019-11-28 13:09:24.748799556 +0100
@@ -16318,7 +16318,8 @@  start_preparsed_function (tree decl1, tr
       && !implicit_default_ctor_p (decl1))
     cp_ubsan_maybe_initialize_vtbl_ptrs (current_class_ptr);
 
-  start_lambda_scope (decl1);
+  if (!DECL_OMP_DECLARE_REDUCTION_P (decl1))
+    start_lambda_scope (decl1);
 
   return true;
 }
@@ -16703,7 +16704,8 @@  finish_function (bool inline_p)
   if (fndecl == NULL_TREE)
     return error_mark_node;
 
-  finish_lambda_scope ();
+  if (!DECL_OMP_DECLARE_REDUCTION_P (fndecl))
+    finish_lambda_scope ();
 
   if (c_dialect_objc ())
     objc_finish_function ();
@@ -16845,7 +16847,9 @@  finish_function (bool inline_p)
     invoke_plugin_callbacks (PLUGIN_PRE_GENERICIZE, fndecl);
 
   /* Perform delayed folding before NRV transformation.  */
-  if (!processing_template_decl && !DECL_IMMEDIATE_FUNCTION_P (fndecl))
+  if (!processing_template_decl
+      && !DECL_IMMEDIATE_FUNCTION_P (fndecl)
+      && !DECL_OMP_DECLARE_REDUCTION_P (fndecl))
     cp_fold_function (fndecl);
 
   /* Set up the named return value optimization, if we can.  Candidate
@@ -16958,7 +16962,9 @@  finish_function (bool inline_p)
     do_warn_unused_parameter (fndecl);
 
   /* Genericize before inlining.  */
-  if (!processing_template_decl && !DECL_IMMEDIATE_FUNCTION_P (fndecl))
+  if (!processing_template_decl
+      && !DECL_IMMEDIATE_FUNCTION_P (fndecl)
+      && !DECL_OMP_DECLARE_REDUCTION_P (fndecl))
     cp_genericize (fndecl);
 
  cleanup:
--- gcc/cp/mangle.c.jj	2019-11-05 08:40:43.705298108 +0100
+++ gcc/cp/mangle.c	2019-11-28 13:22:35.489587821 +0100
@@ -873,7 +873,16 @@  decl_mangling_context (tree decl)
   else if (template_type_parameter_p (decl))
      /* template type parms have no mangling context.  */
       return NULL_TREE;
-  return CP_DECL_CONTEXT (decl);
+
+  tcontext = CP_DECL_CONTEXT (decl);
+
+  /* Ignore the artificial declare reduction functions.  */
+  if (tcontext
+      && TREE_CODE (tcontext) == FUNCTION_DECL
+      && DECL_OMP_DECLARE_REDUCTION_P (tcontext))
+    return decl_mangling_context (tcontext);
+
+  return tcontext;
 }
 
 /* <name> ::= <unscoped-name>
--- gcc/cp/semantics.c.jj	2019-11-27 17:26:57.262013502 +0100
+++ gcc/cp/semantics.c	2019-11-28 12:23:39.357290552 +0100
@@ -4417,7 +4417,9 @@  expand_or_defer_fn_1 (tree fn)
       if (DECL_INTERFACE_KNOWN (fn))
 	/* We've already made a decision as to how this function will
 	   be handled.  */;
-      else if (!at_eof || DECL_IMMEDIATE_FUNCTION_P (fn))
+      else if (!at_eof
+	       || DECL_IMMEDIATE_FUNCTION_P (fn)
+	       || DECL_OMP_DECLARE_REDUCTION_P (fn))
 	tentative_decl_linkage (fn);
       else
 	import_export_decl (fn);
@@ -4429,6 +4431,7 @@  expand_or_defer_fn_1 (tree fn)
       if (DECL_DECLARED_INLINE_P (fn)
 	  && !DECL_REALLY_EXTERN (fn)
 	  && !DECL_IMMEDIATE_FUNCTION_P (fn)
+	  && !DECL_OMP_DECLARE_REDUCTION_P (fn)
 	  && (flag_keep_inline_functions
 	      || (flag_keep_inline_dllexport
 		  && lookup_attribute ("dllexport", DECL_ATTRIBUTES (fn)))))
@@ -4461,6 +4464,9 @@  expand_or_defer_fn_1 (tree fn)
       return false;
     }
 
+  if (DECL_OMP_DECLARE_REDUCTION_P (fn))
+    return false;
+
   return true;
 }
 
--- gcc/testsuite/g++.dg/gomp/openmp-simd-2.C.jj	2015-05-29 15:04:31.982819751 +0200
+++ gcc/testsuite/g++.dg/gomp/openmp-simd-2.C	2019-11-28 13:46:30.556419116 +0100
@@ -36,8 +36,6 @@  void bar(int n, float *a, float *b)
     a[i] = b[i];
 }
 
-/* { dg-final { scan-tree-dump-times "Function void omp declare reduction operator\\+" 1 "original" } } */
-/* { dg-final { scan-tree-dump-times "Function void omp declare reduction foo" 2 "original" } } */
 /* { dg-final { scan-tree-dump-times "pragma omp simd reduction\\(u\\) reduction\\(t\\) reduction\\(\\+:s\\) aligned\\(a:32\\)" 1 "original" } } */
 /* { dg-final { scan-tree-dump-times "pragma omp simd safelen\\(64\\)" 1 "original" } } */
 /* { dg-final { scan-tree-dump-not "omp parallel" "original" } } */
--- libgomp/testsuite/libgomp.c++/udr-20.C.jj	2019-11-28 13:43:04.424604245 +0100
+++ libgomp/testsuite/libgomp.c++/udr-20.C	2019-11-28 13:42:59.568679274 +0100
@@ -0,0 +1,54 @@ 
+// PR c++/60228
+// { dg-additional-options "-std=c++11" }
+
+extern "C" void abort ();
+
+struct A
+{
+  typedef int T;
+  #pragma omp declare reduction (x : T : omp_out += omp_in + [](){ return 0; }()) initializer (omp_priv = [](){ return 0; }())
+  static void foo ();
+};
+
+template <typename T>
+struct B
+{
+  #pragma omp declare reduction (x : T : omp_out += omp_in + [](){ return T (0); }()) initializer (omp_priv = [](){ return T (0); }())
+  static void foo ();
+};
+
+void
+A::foo ()
+{
+  int r = 0, s = 0;
+  #pragma omp parallel for reduction (x : r, s)
+  for (int i = 0; i < 64; i++)
+    {
+      r++;
+      s += i;
+    }
+  if (r != 64 || s != (64 * 63) / 2)
+    abort ();
+}
+
+template <typename T>
+void
+B<T>::foo ()
+{
+  T r = 0, s = 0;
+  #pragma omp parallel for reduction (x : r, s)
+  for (int i = 0; i < 64; i++)
+    {
+      r++;
+      s += i;
+    }
+  if (r != 64 || s != (64 * 63) / 2)
+    abort ();
+}
+
+int
+main ()
+{
+  A::foo ();
+  B<long>::foo ();
+}
--- libgomp/testsuite/libgomp.c++/udr-21.C.jj	2019-11-28 13:43:19.783366923 +0100
+++ libgomp/testsuite/libgomp.c++/udr-21.C	2019-11-28 13:44:28.861299539 +0100
@@ -0,0 +1,54 @@ 
+// PR c++/60228
+// { dg-additional-options "-std=c++11" }
+
+extern "C" void abort ();
+
+struct A
+{
+  typedef int T;
+  #pragma omp declare reduction (y : T : [&omp_out, &omp_in]() { omp_out += omp_in; return 0; }()) initializer (omp_priv = [omp_orig]() { return omp_orig; }())
+  static void foo ();
+};
+
+template <typename T>
+struct B
+{
+  #pragma omp declare reduction (y : T : [&omp_out, &omp_in]() { omp_out += omp_in; return 0; }()) initializer (omp_priv = [omp_orig]() { return omp_orig; }())
+  static void foo ();
+};
+
+void
+A::foo ()
+{
+  int r = 0, s = 0;
+  #pragma omp parallel for reduction (y : r, s)
+  for (int i = 0; i < 64; i++)
+    {
+      r++;
+      s += i;
+    }
+  if (r != 64 || s != (64 * 63) / 2)
+    abort ();
+}
+
+template <typename T>
+void
+B<T>::foo ()
+{
+  T r = 0, s = 0;
+  #pragma omp parallel for reduction (y : r, s)
+  for (int i = 0; i < 64; i++)
+    {
+      r++;
+      s += i;
+    }
+  if (r != 64 || s != (64 * 63) / 2)
+    abort ();
+}
+
+int
+main ()
+{
+  A::foo ();
+  B<short>::foo ();
+}