public inbox for gcc-patches@gcc.gnu.org
 help / color / mirror / Atom feed
* Use verify_oacc_routine_clauses for C/C++
@ 2016-08-01 15:21 Thomas Schwinge
  2016-08-01 15:30 ` Use verify_oacc_routine_clauses for Fortran (was: Use verify_oacc_routine_clauses for C/C++) Thomas Schwinge
                   ` (2 more replies)
  0 siblings, 3 replies; 16+ messages in thread
From: Thomas Schwinge @ 2016-08-01 15:21 UTC (permalink / raw)
  To: gcc-patches

Hi!

Some checking of OpenACC clauses currently done in the front ends should
be moved later, and be unified.  (Also, I suppose, for supporting of the
device_type clause, such checking actually *must* be moved later, into
the oaccdevlow pass, or similar.)  Here is a first preparatory patch.  OK
for trunk?

commit e02a9b65c505b404f8d985b0ec6ccb99d73515d3
Author: Thomas Schwinge <thomas@codesourcery.com>
Date:   Wed Jul 27 15:54:38 2016 +0200

    Use verify_oacc_routine_clauses for C/C++
    
    	gcc/
    	* omp-low.c (build_oacc_routine_dims): Move some of its processing
    	into...
    	(verify_oacc_routine_clauses): ... this new function.
    	* omp-low.h (verify_oacc_routine_clauses): New prototype.
    	gcc/c/
    	* c-parser.c (c_parser_oacc_routine): Normalize order of clauses.
    	(c_finish_oacc_routine): Call verify_oacc_routine_clauses.
    	gcc/cp/
    	* parser.c (cp_parser_oacc_routine)
    	(cp_parser_late_parsing_oacc_routine): Normalize order of clauses.
    	(cp_finalize_oacc_routine): Call verify_oacc_routine_clauses.
    	gcc/testsuite/
    	* c-c++-common/goacc/routine-2.c: Update, and move some test
    	into...
    	* c-c++-common/goacc/routine-level-of-parallelism-1.c: ... this
    	new file.
---
 gcc/c/c-parser.c                                   |   8 +
 gcc/cp/parser.c                                    |   9 +
 gcc/omp-low.c                                      |  67 +++++-
 gcc/omp-low.h                                      |   1 +
 gcc/testsuite/c-c++-common/goacc/routine-2.c       |  20 +-
 .../goacc/routine-level-of-parallelism-1.c         | 265 +++++++++++++++++++++
 6 files changed, 341 insertions(+), 29 deletions(-)

diff --git gcc/c/c-parser.c gcc/c/c-parser.c
index 795842f..24f60cf 100644
--- gcc/c/c-parser.c
+++ gcc/c/c-parser.c
@@ -14072,6 +14072,9 @@ c_parser_oacc_routine (c_parser *parser, enum pragma_context context)
       data.clauses
 	= c_parser_oacc_all_clauses (parser, OACC_ROUTINE_CLAUSE_MASK,
 				     "#pragma acc routine");
+      /* The clauses are in reverse order; fix that to make later diagnostic
+	 emission easier.  */
+      data.clauses = nreverse (data.clauses);
 
       if (TREE_CODE (decl) != FUNCTION_DECL)
 	{
@@ -14086,6 +14089,9 @@ c_parser_oacc_routine (c_parser *parser, enum pragma_context context)
       data.clauses
 	= c_parser_oacc_all_clauses (parser, OACC_ROUTINE_CLAUSE_MASK,
 				     "#pragma acc routine");
+      /* The clauses are in reverse order; fix that to make later diagnostic
+	 emission easier.  */
+      data.clauses = nreverse (data.clauses);
 
       /* Emit a helpful diagnostic if there's another pragma following this
 	 one.  Also don't allow a static assertion declaration, as in the
@@ -14149,6 +14155,8 @@ c_finish_oacc_routine (struct oacc_routine_data *data, tree fndecl,
       return;
     }
 
+  verify_oacc_routine_clauses (&data->clauses, data->loc);
+
   if (get_oacc_fn_attrib (fndecl))
     {
       error_at (data->loc,
diff --git gcc/cp/parser.c gcc/cp/parser.c
index 1008725..6197fc9 100644
--- gcc/cp/parser.c
+++ gcc/cp/parser.c
@@ -36614,6 +36614,9 @@ cp_parser_oacc_routine (cp_parser *parser, cp_token *pragma_tok,
 	= cp_parser_oacc_all_clauses (parser, OACC_ROUTINE_CLAUSE_MASK,
 				      "#pragma acc routine",
 				      cp_lexer_peek_token (parser->lexer));
+      /* The clauses are in reverse order; fix that to make later diagnostic
+	 emission easier.  */
+      data.clauses = nreverse (data.clauses);
 
       if (decl && is_overloaded_fn (decl)
 	  && (TREE_CODE (decl) != FUNCTION_DECL
@@ -36710,6 +36713,9 @@ cp_parser_late_parsing_oacc_routine (cp_parser *parser, tree attrs)
   parser->oacc_routine->clauses
     = cp_parser_oacc_all_clauses (parser, OACC_ROUTINE_CLAUSE_MASK,
 				  "#pragma acc routine", pragma_tok);
+  /* The clauses are in reverse order; fix that to make later diagnostic
+     emission easier.  */
+  parser->oacc_routine->clauses = nreverse (parser->oacc_routine->clauses);
   cp_parser_pop_lexer (parser);
   /* Later, cp_finalize_oacc_routine will process the clauses, and then set
      fndecl_seen.  */
@@ -36744,6 +36750,9 @@ cp_finalize_oacc_routine (cp_parser *parser, tree fndecl, bool is_defn)
 	  return;
 	}
 
+      verify_oacc_routine_clauses (&parser->oacc_routine->clauses,
+				   parser->oacc_routine->loc);
+
       if (get_oacc_fn_attrib (fndecl))
 	{
 	  error_at (parser->oacc_routine->loc,
diff --git gcc/omp-low.c gcc/omp-low.c
index c75452c..ec5704c 100644
--- gcc/omp-low.c
+++ gcc/omp-low.c
@@ -12599,9 +12599,62 @@ set_oacc_fn_attrib (tree fn, tree clauses, bool is_kernel, vec<tree> *args)
     }
 }
 
-/*  Process the routine's dimension clauess to generate an attribute
-    value.  Issue diagnostics as appropriate.  We default to SEQ
-    (OpenACC 2.5 clarifies this). All dimensions have a size of zero
+/* Verify OpenACC routine clauses.
+
+   Upon returning, the chain of clauses will contain exactly one clause
+   specifying the level of parallelism.  */
+
+void
+verify_oacc_routine_clauses (tree *clauses, location_t loc)
+{
+  tree c_level = NULL_TREE;
+  tree c_p = NULL_TREE;
+  for (tree c = *clauses; c; c_p = c, c = OMP_CLAUSE_CHAIN (c))
+    switch (OMP_CLAUSE_CODE (c))
+      {
+      case OMP_CLAUSE_GANG:
+      case OMP_CLAUSE_WORKER:
+      case OMP_CLAUSE_VECTOR:
+      case OMP_CLAUSE_SEQ:
+	if (c_level == NULL_TREE)
+	  c_level = c;
+	else if (OMP_CLAUSE_CODE (c) == OMP_CLAUSE_CODE (c_level))
+	  {
+	    /* This has already been diagnosed in the front ends.  */
+	    /* Drop the duplicate clause.  */
+	    gcc_checking_assert (c_p != NULL_TREE);
+	    OMP_CLAUSE_CHAIN (c_p) = OMP_CLAUSE_CHAIN (c);
+	    c = c_p;
+	  }
+	else
+	  {
+	    error_at (OMP_CLAUSE_LOCATION (c),
+		      "%qs specifies a conflicting level of parallelism",
+		      omp_clause_code_name[OMP_CLAUSE_CODE (c)]);
+	    inform (OMP_CLAUSE_LOCATION (c_level),
+		    "... to the previous %qs clause here",
+		    omp_clause_code_name[OMP_CLAUSE_CODE (c_level)]);
+	    /* Drop the conflicting clause.  */
+	    gcc_checking_assert (c_p != NULL_TREE);
+	    OMP_CLAUSE_CHAIN (c_p) = OMP_CLAUSE_CHAIN (c);
+	    c = c_p;
+	  }
+	break;
+      default:
+	gcc_unreachable ();
+      }
+  if (c_level == NULL_TREE)
+    {
+      /* OpenACC 2.5 makes this an error; for the current OpenACC 2.0a
+	 implementation add an implicit "seq" clause.  */
+      c_level = build_omp_clause (loc, OMP_CLAUSE_SEQ);
+      OMP_CLAUSE_CHAIN (c_level) = *clauses;
+      *clauses = c_level;
+    }
+}
+
+/*  Process the OpenACC routine's clauses to generate an attribute
+    for the level of parallelism.  All dimensions have a size of zero
     (dynamic).  TREE_PURPOSE is set to indicate whether that dimension
     can have a loop partitioned on it.  non-zero indicates
     yes, zero indicates no.  By construction once a non-zero has been
@@ -12623,17 +12676,11 @@ build_oacc_routine_dims (tree clauses)
     for (ix = GOMP_DIM_MAX + 1; ix--;)
       if (OMP_CLAUSE_CODE (clauses) == ids[ix])
 	{
-	  if (level >= 0)
-	    error_at (OMP_CLAUSE_LOCATION (clauses),
-		      "multiple loop axes specified for routine");
 	  level = ix;
 	  break;
 	}
+  gcc_checking_assert (level >= 0);
 
-  /* Default to SEQ.  */
-  if (level < 0)
-    level = GOMP_DIM_MAX;
-  
   tree dims = NULL_TREE;
 
   for (ix = GOMP_DIM_MAX; ix--;)
diff --git gcc/omp-low.h gcc/omp-low.h
index b1f7885..c7b7dcb 100644
--- gcc/omp-low.h
+++ gcc/omp-low.h
@@ -31,6 +31,7 @@ extern bool make_gimple_omp_edges (basic_block, struct omp_region **, int *);
 extern void omp_finish_file (void);
 extern tree omp_member_access_dummy_var (tree);
 extern void replace_oacc_fn_attrib (tree, tree);
+extern void verify_oacc_routine_clauses (tree *, location_t);
 extern tree build_oacc_routine_dims (tree);
 extern tree get_oacc_fn_attrib (tree);
 extern void set_oacc_fn_attrib (tree, tree, bool, vec<tree> *);
diff --git gcc/testsuite/c-c++-common/goacc/routine-2.c gcc/testsuite/c-c++-common/goacc/routine-2.c
index fc5eb11..1e6bc48 100644
--- gcc/testsuite/c-c++-common/goacc/routine-2.c
+++ gcc/testsuite/c-c++-common/goacc/routine-2.c
@@ -1,21 +1,3 @@
-#pragma acc routine gang worker /* { dg-error "multiple loop axes" } */
-void gang (void)
-{
-}
-
-#pragma acc routine worker vector /* { dg-error "multiple loop axes" } */
-void worker (void)
-{
-}
-
-#pragma acc routine vector seq /* { dg-error "multiple loop axes" } */
-void vector (void)
-{
-}
-
-#pragma acc routine seq gang /* { dg-error "multiple loop axes" } */
-void seq (void)
-{
-}
+/* Test invalid use of clauses with OpenACC routine.  */
 
 #pragma acc routine (nothing) gang /* { dg-error "not been declared" } */
diff --git gcc/testsuite/c-c++-common/goacc/routine-level-of-parallelism-1.c gcc/testsuite/c-c++-common/goacc/routine-level-of-parallelism-1.c
new file mode 100644
index 0000000..fc8bc3c
--- /dev/null
+++ gcc/testsuite/c-c++-common/goacc/routine-level-of-parallelism-1.c
@@ -0,0 +1,265 @@
+/* Test various aspects of clauses specifying incompatible levels of
+   parallelism with the OpenACC routine directive.  The Fortran counterpart is
+   ../../gfortran.dg/goacc/routine-level-of-parallelism-1.f.  */
+
+extern void g_1 (void);
+#pragma acc routine (g_1) gang gang /* { dg-error "too many 'gang' clauses" } */
+
+#pragma acc routine worker worker /* { dg-error "too many 'worker' clauses" } */
+void w_1 (void)
+{
+}
+
+#pragma acc routine vector vector /* { dg-error "too many 'vector' clauses" } */
+void v_1 (void)
+{
+}
+
+#pragma acc routine seq seq /* { dg-error "too many 'seq' clauses" } */
+extern void s_1 (void);
+
+
+#pragma acc routine gang gang gang /* { dg-error "too many 'gang' clauses" } */
+void g_2 (void)
+{
+}
+
+#pragma acc routine worker worker worker /* { dg-error "too many 'worker' clauses" } */
+extern void w_2 (void);
+
+extern void v_2 (void);
+#pragma acc routine (v_2) vector vector vector /* { dg-error "too many 'vector' clauses" } */
+
+#pragma acc routine seq seq seq /* { dg-error "too many 'seq' clauses" } */
+void s_2 (void)
+{
+}
+
+
+#pragma acc routine \
+  gang \
+  worker /* { dg-error ".worker. specifies a conflicting level of parallelism" } */
+void g_3 (void)
+{
+}
+#pragma acc routine (g_3) /* { dg-error ".#pragma acc routine. already applied to .\[void \]*g_3." } */ \
+  gang \
+  seq /* { dg-error ".seq. specifies a conflicting level of parallelism" } */
+#pragma acc routine (g_3) /* { dg-error ".#pragma acc routine. already applied to .\[void \]*g_3." } */ \
+  gang \
+  vector /* { dg-error ".vector. specifies a conflicting level of parallelism" } */
+
+extern void w_3 (void);
+#pragma acc routine (w_3) \
+  worker \
+  vector /* { dg-error ".vector. specifies a conflicting level of parallelism" } */
+#pragma acc routine (w_3) /* { dg-error ".#pragma acc routine. already applied to .\[void \]*w_3." } */ \
+  worker \
+  gang /* { dg-error ".gang. specifies a conflicting level of parallelism" } */
+#pragma acc routine (w_3) /* { dg-error ".#pragma acc routine. already applied to .\[void \]*w_3." } */ \
+  worker \
+  seq /* { dg-error ".seq. specifies a conflicting level of parallelism" } */
+
+#pragma acc routine \
+  vector \
+  seq /* { dg-error ".seq. specifies a conflicting level of parallelism" } */
+void v_3 (void)
+{
+}
+#pragma acc routine (v_3) /* { dg-error ".#pragma acc routine. already applied to .\[void \]*v_3." } */ \
+  vector \
+  worker /* { dg-error ".worker. specifies a conflicting level of parallelism" } */
+#pragma acc routine (v_3) /* { dg-error ".#pragma acc routine. already applied to .\[void \]*v_3." } */ \
+  vector \
+  gang /* { dg-error ".gang. specifies a conflicting level of parallelism" } */
+
+extern void s_3 (void);
+#pragma acc routine (s_3) \
+  seq \
+  gang /* { dg-error ".gang. specifies a conflicting level of parallelism" } */
+#pragma acc routine (s_3) /* { dg-error ".#pragma acc routine. already applied to .\[void \]*s_3." } */ \
+  seq \
+  vector /* { dg-error ".vector. specifies a conflicting level of parallelism" } */
+#pragma acc routine (s_3) /* { dg-error ".#pragma acc routine. already applied to .\[void \]*s_3." } */ \
+  seq \
+  worker /* { dg-error ".worker. specifies a conflicting level of parallelism" } */
+
+
+#pragma acc routine \
+  gang gang gang /* { dg-error "too many 'gang' clauses" } */ \
+  worker /* { dg-error ".worker. specifies a conflicting level of parallelism" } */ \
+  vector /* { dg-error ".vector. specifies a conflicting level of parallelism" } */ \
+  seq /* { dg-error ".seq. specifies a conflicting level of parallelism" } */
+extern void g_4 (void);
+#pragma acc routine (g_4) /* { dg-error ".#pragma acc routine. already applied to .\[void \]*g_4." } */ \
+  gang gang gang /* { dg-error "too many 'gang' clauses" } */ \
+  vector /* { dg-error ".vector. specifies a conflicting level of parallelism" } */ \
+  worker /* { dg-error ".worker. specifies a conflicting level of parallelism" } */ \
+  seq /* { dg-error ".seq. specifies a conflicting level of parallelism" } */
+#pragma acc routine (g_4) /* { dg-error ".#pragma acc routine. already applied to .\[void \]*g_4." } */ \
+  gang gang gang /* { dg-error "too many 'gang' clauses" } */ \
+  seq /* { dg-error ".seq. specifies a conflicting level of parallelism" } */ \
+  vector /* { dg-error ".vector. specifies a conflicting level of parallelism" } */ \
+  worker /* { dg-error ".worker. specifies a conflicting level of parallelism" } */
+
+extern void w_4 (void);
+#pragma acc routine (w_4) \
+  worker worker worker /* { dg-error "too many 'worker' clauses" } */ \
+  seq /* { dg-error ".seq. specifies a conflicting level of parallelism" } */ \
+  vector /* { dg-error ".vector. specifies a conflicting level of parallelism" } */ \
+  gang /* { dg-error ".gang. specifies a conflicting level of parallelism" } */
+#pragma acc routine (w_4) /* { dg-error ".#pragma acc routine. already applied to .\[void \]*w_4." } */ \
+  worker worker worker /* { dg-error "too many 'worker' clauses" } */ \
+  vector /* { dg-error ".vector. specifies a conflicting level of parallelism" } */ \
+  seq /* { dg-error ".seq. specifies a conflicting level of parallelism" } */ \
+  gang /* { dg-error ".gang. specifies a conflicting level of parallelism" } */
+#pragma acc routine (w_4) /* { dg-error ".#pragma acc routine. already applied to .\[void \]*w_4." } */ \
+  worker worker worker /* { dg-error "too many 'worker' clauses" } */ \
+  seq /* { dg-error ".seq. specifies a conflicting level of parallelism" } */ \
+  gang /* { dg-error ".gang. specifies a conflicting level of parallelism" } */ \
+  vector /* { dg-error ".vector. specifies a conflicting level of parallelism" } */
+
+#pragma acc routine \
+  vector vector vector /* { dg-error "too many 'vector' clauses" } */ \
+  worker /* { dg-error ".worker. specifies a conflicting level of parallelism" } */ \
+  seq /* { dg-error ".seq. specifies a conflicting level of parallelism" } */ \
+  gang /* { dg-error ".gang. specifies a conflicting level of parallelism" } */
+void v_4 (void)
+{
+}
+#pragma acc routine (v_4) /* { dg-error ".#pragma acc routine. already applied to .\[void \]*v_4." } */ \
+  vector vector vector /* { dg-error "too many 'vector' clauses" } */ \
+  seq /* { dg-error ".seq. specifies a conflicting level of parallelism" } */ \
+  worker /* { dg-error ".worker. specifies a conflicting level of parallelism" } */ \
+  gang /* { dg-error ".gang. specifies a conflicting level of parallelism" } */
+#pragma acc routine (v_4) /* { dg-error ".#pragma acc routine. already applied to .\[void \]*v_4." } */ \
+  vector vector vector /* { dg-error "too many 'vector' clauses" } */ \
+  gang /* { dg-error ".gang. specifies a conflicting level of parallelism" } */ \
+  seq /* { dg-error ".seq. specifies a conflicting level of parallelism" } */ \
+  worker /* { dg-error ".worker. specifies a conflicting level of parallelism" } */
+
+#pragma acc routine \
+  seq seq seq /* { dg-error "too many 'seq' clauses" } */ \
+  vector /* { dg-error ".vector. specifies a conflicting level of parallelism" } */ \
+  worker /* { dg-error ".worker. specifies a conflicting level of parallelism" } */ \
+  gang /* { dg-error ".gang. specifies a conflicting level of parallelism" } */
+void s_4 (void)
+{
+}
+#pragma acc routine (s_4) /* { dg-error ".#pragma acc routine. already applied to .\[void \]*s_4." } */ \
+  seq seq seq /* { dg-error "too many 'seq' clauses" } */ \
+  vector /* { dg-error ".vector. specifies a conflicting level of parallelism" } */ \
+  gang /* { dg-error ".gang. specifies a conflicting level of parallelism" } */ \
+  worker /* { dg-error ".worker. specifies a conflicting level of parallelism" } */
+#pragma acc routine (s_4) /* { dg-error ".#pragma acc routine. already applied to .\[void \]*s_4." } */ \
+  seq seq seq /* { dg-error "too many 'seq' clauses" } */ \
+  worker /* { dg-error ".worker. specifies a conflicting level of parallelism" } */ \
+  vector /* { dg-error ".vector. specifies a conflicting level of parallelism" } */ \
+  gang /* { dg-error ".gang. specifies a conflicting level of parallelism" } */
+
+
+#pragma acc routine \
+  gang gang gang /* { dg-error "too many 'gang' clauses" } */ \
+  worker worker /* { dg-error "too many 'worker' clauses" } */ \
+  /* { dg-error ".worker. specifies a conflicting level of parallelism" "" { target *-*-* } 163 } */ \
+  vector vector vector /* { dg-error "too many 'vector' clauses" } */ \
+  /* { dg-error ".vector. specifies a conflicting level of parallelism" "" { target *-*-* } 165 } */ \
+  seq seq seq seq seq /* { dg-error "too many 'seq' clauses" } */ \
+  /* { dg-error ".seq. specifies a conflicting level of parallelism" "" { target *-*-* } 167 } */
+void g_5 (void)
+{
+}
+#pragma acc routine (g_5) /* { dg-error ".#pragma acc routine. already applied to .\[void \]*g_5." } */ \
+  gang gang gang /* { dg-error "too many 'gang' clauses" } */ \
+  vector vector vector vector /* { dg-error "too many 'vector' clauses" } */ \
+  /* { dg-error ".vector. specifies a conflicting level of parallelism" "" { target *-*-* } 174 } */ \
+  worker worker worker /* { dg-error "too many 'worker' clauses" } */ \
+  /* { dg-error ".worker. specifies a conflicting level of parallelism" "" { target *-*-* } 176 } */ \
+  seq seq seq seq seq seq /* { dg-error "too many 'seq' clauses" } */ \
+  /* { dg-error ".seq. specifies a conflicting level of parallelism" "" { target *-*-* } 178 } */
+#pragma acc routine (g_5) /* { dg-error ".#pragma acc routine. already applied to .\[void \]*g_5." } */ \
+  gang gang gang /* { dg-error "too many 'gang' clauses" } */ \
+  seq seq seq seq seq /* { dg-error "too many 'seq' clauses" } */ \
+  /* { dg-error ".seq. specifies a conflicting level of parallelism" "" { target *-*-* } 182 } */ \
+  vector vector vector vector /* { dg-error "too many 'vector' clauses" } */ \
+  /* { dg-error ".vector. specifies a conflicting level of parallelism" "" { target *-*-* } 184 } */ \
+  worker worker worker worker /* { dg-error "too many 'worker' clauses" } */ \
+  /* { dg-error ".worker. specifies a conflicting level of parallelism" "" { target *-*-* } 186 } */
+
+#pragma acc routine \
+  worker worker worker /* { dg-error "too many 'worker' clauses" } */ \
+  seq seq seq seq /* { dg-error "too many 'seq' clauses" } */ \
+  /* { dg-error ".seq. specifies a conflicting level of parallelism" "" { target *-*-* } 191 } */ \
+  vector vector vector vector vector /* { dg-error "too many 'vector' clauses" } */ \
+  /* { dg-error ".vector. specifies a conflicting level of parallelism" "" { target *-*-* } 193 } */ \
+  gang gang /* { dg-error "too many 'gang' clauses" } */ \
+  /* { dg-error ".gang. specifies a conflicting level of parallelism" "" { target *-*-* } 195 } */
+extern void w_5 (void);
+#pragma acc routine (w_5) /* { dg-error ".#pragma acc routine. already applied to .\[void \]*w_5." } */ \
+  worker worker worker /* { dg-error "too many 'worker' clauses" } */ \
+  vector vector vector /* { dg-error "too many 'vector' clauses" } */ \
+  /* { dg-error ".vector. specifies a conflicting level of parallelism" "" { target *-*-* } 200 } */ \
+  seq seq seq /* { dg-error "too many 'seq' clauses" } */ \
+  /* { dg-error ".seq. specifies a conflicting level of parallelism" "" { target *-*-* } 202 } */ \
+  gang gang /* { dg-error "too many 'gang' clauses" } */ \
+  /* { dg-error ".gang. specifies a conflicting level of parallelism" "" { target *-*-* } 204 } */
+#pragma acc routine (w_5) /* { dg-error ".#pragma acc routine. already applied to .\[void \]*w_5." } */ \
+  worker worker worker /* { dg-error "too many 'worker' clauses" } */ \
+  seq seq /* { dg-error "too many 'seq' clauses" } */ \
+  /* { dg-error ".seq. specifies a conflicting level of parallelism" "" { target *-*-* } 208 } */ \
+  gang gang /* { dg-error "too many 'gang' clauses" } */ \
+  /* { dg-error ".gang. specifies a conflicting level of parallelism" "" { target *-*-* } 210 } */ \
+  vector vector vector vector /* { dg-error "too many 'vector' clauses" } */ \
+  /* { dg-error ".vector. specifies a conflicting level of parallelism" "" { target *-*-* } 212 } */
+
+#pragma acc routine \
+  vector vector vector /* { dg-error "too many 'vector' clauses" } */ \
+  worker worker worker /* { dg-error "too many 'worker' clauses" } */ \
+  /* { dg-error ".worker. specifies a conflicting level of parallelism" "" { target *-*-* } 217 } */ \
+  seq seq seq seq seq seq /* { dg-error "too many 'seq' clauses" } */ \
+  /* { dg-error ".seq. specifies a conflicting level of parallelism" "" { target *-*-* } 219 } */ \
+  gang gang /* { dg-error "too many 'gang' clauses" } */ \
+  /* { dg-error ".gang. specifies a conflicting level of parallelism" "" { target *-*-* } 221 } */
+extern void v_5 (void);
+#pragma acc routine (v_5) /* { dg-error ".#pragma acc routine. already applied to .\[void \]*v_5." } */ \
+  vector vector vector /* { dg-error "too many 'vector' clauses" } */ \
+  seq seq seq seq seq seq /* { dg-error "too many 'seq' clauses" } */ \
+  /* { dg-error ".seq. specifies a conflicting level of parallelism" "" { target *-*-* } 226 } */ \
+  worker worker /* { dg-error "too many 'worker' clauses" } */ \
+  /* { dg-error ".worker. specifies a conflicting level of parallelism" "" { target *-*-* } 228 } */ \
+  gang gang /* { dg-error "too many 'gang' clauses" } */ \
+  /* { dg-error ".gang. specifies a conflicting level of parallelism" "" { target *-*-* } 230 } */
+#pragma acc routine (v_5) /* { dg-error ".#pragma acc routine. already applied to .\[void \]*v_5." } */ \
+  vector vector vector /* { dg-error "too many 'vector' clauses" } */ \
+  gang gang /* { dg-error "too many 'gang' clauses" } */ \
+  /* { dg-error ".gang. specifies a conflicting level of parallelism" "" { target *-*-* } 234 } */ \
+  seq seq seq seq seq /* { dg-error "too many 'seq' clauses" } */ \
+  /* { dg-error ".seq. specifies a conflicting level of parallelism" "" { target *-*-* } 236 } */ \
+  worker worker /* { dg-error "too many 'worker' clauses" } */ \
+  /* { dg-error ".worker. specifies a conflicting level of parallelism" "" { target *-*-* } 238 } */
+
+extern void s_5 (void);
+#pragma acc routine (s_5) \
+  seq seq seq /* { dg-error "too many 'seq' clauses" } */ \
+  vector vector vector vector /* { dg-error "too many 'vector' clauses" } */ \
+  /* { dg-error ".vector. specifies a conflicting level of parallelism" "" { target *-*-* } 244 } */ \
+  worker worker worker worker worker /* { dg-error "too many 'worker' clauses" } */ \
+  /* { dg-error ".worker. specifies a conflicting level of parallelism" "" { target *-*-* } 246 } */ \
+  gang gang /* { dg-error "too many 'gang' clauses" } */ \
+  /* { dg-error ".gang. specifies a conflicting level of parallelism" "" { target *-*-* } 248 } */
+#pragma acc routine (s_5) /* { dg-error ".#pragma acc routine. already applied to .\[void \]*s_5." } */ \
+  seq seq seq /* { dg-error "too many 'seq' clauses" } */ \
+  vector vector vector vector /* { dg-error "too many 'vector' clauses" } */ \
+  /* { dg-error ".vector. specifies a conflicting level of parallelism" "" { target *-*-* } 252 } */ \
+  gang gang /* { dg-error "too many 'gang' clauses" } */ \
+  /* { dg-error ".gang. specifies a conflicting level of parallelism" "" { target *-*-* } 254 } */ \
+  worker worker worker /* { dg-error "too many 'worker' clauses" } */ \
+  /* { dg-error ".worker. specifies a conflicting level of parallelism" "" { target *-*-* } 256 } */
+#pragma acc routine (s_5) /* { dg-error ".#pragma acc routine. already applied to .\[void \]*s_5." } */ \
+  seq seq seq /* { dg-error "too many 'seq' clauses" } */ \
+  worker worker /* { dg-error "too many 'worker' clauses" } */ \
+  /* { dg-error ".worker. specifies a conflicting level of parallelism" "" { target *-*-* } 260 } */ \
+  vector vector vector vector /* { dg-error "too many 'vector' clauses" } */ \
+  /* { dg-error ".vector. specifies a conflicting level of parallelism" "" { target *-*-* } 262 } */ \
+  gang gang /* { dg-error "too many 'gang' clauses" } */ \
+  /* { dg-error ".gang. specifies a conflicting level of parallelism" "" { target *-*-* } 264 } */


Grüße
 Thomas

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

* Use verify_oacc_routine_clauses for Fortran (was: Use verify_oacc_routine_clauses for C/C++)
  2016-08-01 15:21 Use verify_oacc_routine_clauses for C/C++ Thomas Schwinge
@ 2016-08-01 15:30 ` Thomas Schwinge
  2016-08-09 15:55   ` Cesar Philippidis
  2016-08-17  1:02 ` Use verify_oacc_routine_clauses for C/C++ Thomas Schwinge
  2018-10-02 14:12 ` [patch,openacc] Use oacc_verify_routine_clauses " Cesar Philippidis
  2 siblings, 1 reply; 16+ messages in thread
From: Thomas Schwinge @ 2016-08-01 15:30 UTC (permalink / raw)
  To: gcc-patches, fortran

Hi!

On Mon, 01 Aug 2016 17:21:37 +0200, I wrote:
> Some checking of OpenACC clauses currently done in the front ends should
> be moved later, and be unified.  (Also, I suppose, for supporting of the
> device_type clause, such checking actually *must* be moved later, into
> the oaccdevlow pass, or similar.)  Here is a first preparatory patch.  OK
> for trunk?
> 
> commit e02a9b65c505b404f8d985b0ec6ccb99d73515d3
> Author: Thomas Schwinge <thomas@codesourcery.com>
> Date:   Wed Jul 27 15:54:38 2016 +0200
> 
>     Use verify_oacc_routine_clauses for C/C++

Here is a Fortran patch.  This depends on other Fortran patches in flight
(such as Cesar's), and on PR72741 "Fortran OpenACC routine directive
doesn't properly handle clauses specifying the level of parallelism" be
resolved, and thereabouts, but I'm posting it anyway, in case anyone has
any review comments already.  I suppose, to begin with, the call of
gfc_oacc_routine_dims will move later into the Fortran front end
pipeline, to the point then function declarations' attributes are set, or
similar.  Also, as discussed already, the Fortran front end currently is
very "forgetful" in regards to OpenACC/OpenMP clauses' specific location
information, so we're not able at present to produce diagnostics with
precise location information.

commit 6480b966af617e61b35b59bb089dd009064743e5
Author: Thomas Schwinge <thomas@codesourcery.com>
Date:   Fri Jul 29 13:12:49 2016 +0200

    Use verify_oacc_routine_clauses for Fortran
    
    	gcc/fortran/
    	* openmp.c: Include "trans-stmt.h".
    	(gfc_oacc_routine_dims): Move function...
    	* trans-openmp.c: ... here, and re-implement it.  Adjust all
    	users.
    	* trans-stmt.h (gfc_oacc_routine_dims): New prototype.
    	gcc/testsuite/
    	* gfortran.dg/goacc/pr72741.f90: Update.
    	* gfortran.dg/goacc/routine-level-of-parallelism-1.f: New file.
---
 gcc/fortran/openmp.c                               |  47 +---------
 gcc/fortran/trans-openmp.c                         |  67 +++++++++++++
 gcc/fortran/trans-stmt.h                           |   7 +-
 gcc/testsuite/gfortran.dg/goacc/pr72741.f90        |  10 +-
 .../goacc/routine-level-of-parallelism-1.f         | 104 +++++++++++++++++++++
 5 files changed, 181 insertions(+), 54 deletions(-)

diff --git gcc/fortran/openmp.c gcc/fortran/openmp.c
index f7a907d..b0a10a8 100644
--- gcc/fortran/openmp.c
+++ gcc/fortran/openmp.c
@@ -27,6 +27,7 @@ along with GCC; see the file COPYING3.  If not see
 #include "parse.h"
 #include "diagnostic.h"
 #include "gomp-constants.h"
+#include "trans-stmt.h"
 
 /* Match an end of OpenMP directive.  End of OpenMP directive is optional
    whitespace, followed by '\n' or comment '!'.  */
@@ -1714,44 +1715,6 @@ gfc_match_oacc_cache (void)
   return MATCH_YES;
 }
 
-/* Determine the loop level for a routine.  Returns OACC_FUNCTION_NONE if
-   any error is detected.  */
-
-static oacc_function
-gfc_oacc_routine_dims (gfc_omp_clauses *clauses)
-{
-  int level = -1;
-  oacc_function ret = OACC_FUNCTION_SEQ;
-
-  if (clauses)
-    {
-      unsigned mask = 0;
-
-      if (clauses->gang)
-	{
-	  level = GOMP_DIM_GANG, mask |= GOMP_DIM_MASK (level);
-	  ret = OACC_FUNCTION_GANG;
-	}
-      if (clauses->worker)
-	{
-	  level = GOMP_DIM_WORKER, mask |= GOMP_DIM_MASK (level);
-	  ret = OACC_FUNCTION_WORKER;
-	}
-      if (clauses->vector)
-	{
-	  level = GOMP_DIM_VECTOR, mask |= GOMP_DIM_MASK (level);
-	  ret = OACC_FUNCTION_VECTOR;
-	}
-      if (clauses->seq)
-	level = GOMP_DIM_MAX, mask |= GOMP_DIM_MASK (level);
-
-      if (mask != (mask & -mask))
-	ret = OACC_FUNCTION_NONE;
-    }
-
-  return ret;
-}
-
 match
 gfc_match_oacc_routine (void)
 {
@@ -1828,13 +1791,7 @@ gfc_match_oacc_routine (void)
 	  != MATCH_YES))
     return MATCH_ERROR;
 
-  dims = gfc_oacc_routine_dims (c);
-  if (dims == OACC_FUNCTION_NONE)
-    {
-      gfc_error ("Multiple loop axes specified for routine %C");
-      gfc_current_locus = old_loc;
-      return MATCH_ERROR;
-    }
+  dims = gfc_oacc_routine_dims (c, old_loc);
 
   if (isym != NULL)
     /* There is nothing to do for intrinsic procedures.  */
diff --git gcc/fortran/trans-openmp.c gcc/fortran/trans-openmp.c
index 0d646ed..254732c 100644
--- gcc/fortran/trans-openmp.c
+++ gcc/fortran/trans-openmp.c
@@ -4570,3 +4570,70 @@ gfc_trans_omp_declare_simd (gfc_namespace *ns)
       DECL_ATTRIBUTES (fndecl) = c;
     }
 }
+
+/* Determine and verify the level of parallelism for an OpenACC routine.  */
+
+oacc_function
+gfc_oacc_routine_dims (gfc_omp_clauses *clauses, locus location)
+{
+  /* This is implemented in terms of OMP_CLAUSE trees, so that we can use the
+     generic functions for checking validity.  This has a little bit of
+     overhead, but as the number of clauses on OpenACC routine directives as
+     well as the number of OpenACC routine directives will both be rather
+     small, this is acceptable.  */
+  tree clauses_t = NULL_TREE;
+  /* We don't have specific location information available for the individual
+     clauses...  */
+  location_t loc = location.lb->location;
+  if (clauses)
+    {
+      if (clauses->gang)
+	{
+	  tree c = build_omp_clause (loc, OMP_CLAUSE_GANG);
+	  OMP_CLAUSE_CHAIN (c) = clauses_t;
+	  clauses_t = c;
+	}
+      if (clauses->worker)
+	{
+	  tree c = build_omp_clause (loc, OMP_CLAUSE_WORKER);
+	  OMP_CLAUSE_CHAIN (c) = clauses_t;
+	  clauses_t = c;
+	}
+      if (clauses->vector)
+	{
+	  tree c = build_omp_clause (loc, OMP_CLAUSE_VECTOR);
+	  OMP_CLAUSE_CHAIN (c) = clauses_t;
+	  clauses_t = c;
+	}
+      if (clauses->seq)
+	{
+	  tree c = build_omp_clause (loc, OMP_CLAUSE_SEQ);
+	  OMP_CLAUSE_CHAIN (c) = clauses_t;
+	  clauses_t = c;
+	}
+    }
+  verify_oacc_routine_clauses (&clauses_t, loc);
+
+  gcc_checking_assert (clauses_t != NULL_TREE
+		       && OMP_CLAUSE_CHAIN (clauses_t) == NULL_TREE);
+  oacc_function ret;
+  switch (OMP_CLAUSE_CODE (clauses_t))
+    {
+    case OMP_CLAUSE_GANG:
+      ret = OACC_FUNCTION_GANG;
+      break;
+    case OMP_CLAUSE_WORKER:
+      ret = OACC_FUNCTION_WORKER;
+      break;
+    case OMP_CLAUSE_VECTOR:
+      ret = OACC_FUNCTION_VECTOR;
+      break;
+    case OMP_CLAUSE_SEQ:
+      ret = OACC_FUNCTION_SEQ;
+      break;
+    default:
+      gcc_unreachable ();
+    }
+
+  return ret;
+}
diff --git gcc/fortran/trans-stmt.h gcc/fortran/trans-stmt.h
index f9c8e74..1e50298 100644
--- gcc/fortran/trans-stmt.h
+++ gcc/fortran/trans-stmt.h
@@ -63,12 +63,11 @@ tree gfc_trans_deallocate (gfc_code *);
 tree gfc_trans_deallocate_array (tree);
 
 /* trans-openmp.c */
+tree gfc_trans_oacc_declare (gfc_namespace *);
+tree gfc_trans_oacc_directive (gfc_code *);
 tree gfc_trans_omp_directive (gfc_code *);
 void gfc_trans_omp_declare_simd (gfc_namespace *);
-
-/* trans-openacc.c */
-tree gfc_trans_oacc_directive (gfc_code *);
-tree gfc_trans_oacc_declare (gfc_namespace *);
+oacc_function gfc_oacc_routine_dims (gfc_omp_clauses *, locus);
 
 /* trans-io.c */
 tree gfc_trans_open (gfc_code *);
diff --git gcc/testsuite/gfortran.dg/goacc/pr72741.f90 gcc/testsuite/gfortran.dg/goacc/pr72741.f90
index cf89727..a5cb218 100644
--- gcc/testsuite/gfortran.dg/goacc/pr72741.f90
+++ gcc/testsuite/gfortran.dg/goacc/pr72741.f90
@@ -1,12 +1,12 @@
 SUBROUTINE v_1
-  !$ACC ROUTINE VECTOR WORKER ! { dg-error "Multiple loop axes" }
+  !$ACC ROUTINE VECTOR WORKER ! { dg-error ".worker. specifies a conflicting level of parallelism" }
 END SUBROUTINE v_1
 
 SUBROUTINE sub_1
   IMPLICIT NONE
   EXTERNAL :: g_1
-  !$ACC ROUTINE (g_1) GANG WORKER ! { dg-error "Multiple loop axes" }
-  !$ACC ROUTINE (ABORT) SEQ VECTOR ! { dg-error "Multiple loop axes" }
+  !$ACC ROUTINE (g_1) GANG WORKER ! { dg-error ".gang. specifies a conflicting level of parallelism" }
+  !$ACC ROUTINE (ABORT) SEQ VECTOR ! { dg-error ".vector. specifies a conflicting level of parallelism" }
 
   CALL v_1
   CALL g_1
@@ -16,8 +16,8 @@ END SUBROUTINE sub_1
 MODULE m_w_1
   IMPLICIT NONE
   EXTERNAL :: w_1
-  !$ACC ROUTINE (w_1) WORKER SEQ ! { dg-error "Multiple loop axes" }
-  !$ACC ROUTINE (ABORT) VECTOR GANG ! { dg-error "Multiple loop axes" }
+  !$ACC ROUTINE (w_1) WORKER SEQ ! { dg-error ".worker. specifies a conflicting level of parallelism" }
+  !$ACC ROUTINE (ABORT) VECTOR GANG ! { dg-error ".gang. specifies a conflicting level of parallelism" }
 
 CONTAINS
   SUBROUTINE sub_2
diff --git gcc/testsuite/gfortran.dg/goacc/routine-level-of-parallelism-1.f gcc/testsuite/gfortran.dg/goacc/routine-level-of-parallelism-1.f
new file mode 100644
index 0000000..e2f3c61
--- /dev/null
+++ gcc/testsuite/gfortran.dg/goacc/routine-level-of-parallelism-1.f
@@ -0,0 +1,104 @@
+! Test various aspects of clauses specifying incompatible levels of
+! parallelism with the OpenACC routine directive.  The C/C++ counterpart
+! is ../../c-c++-common/goacc/routine-level-of-parallelism-1.c.
+
+! We currently don't have precise location information for these, so can't
+! match them properly.
+! { dg-prune-output "specifies a conflicting level of parallelism" }
+
+      MODULE m_g_1
+      EXTERNAL :: g_1
+!$ACC ROUTINE (g_1) GANG GANG ! { dg-error "Unclassifiable OpenACC directive" }
+      END
+
+      SUBROUTINE w_1
+!$ACC ROUTINE WORKER WORKER ! { dg-error "Unclassifiable OpenACC directive" }
+      END
+
+      SUBROUTINE v_1
+!$ACC ROUTINE VECTOR VECTOR ! { dg-error "Unclassifiable OpenACC directive" }
+      END
+
+      MODULE m_s_1
+      EXTERNAL :: s_1
+!$ACC ROUTINE (s_1) SEQ SEQ ! { dg-error "Unclassifiable OpenACC directive" }
+      END
+
+
+      SUBROUTINE g_2
+!$ACC ROUTINE GANG GANG GANG ! { dg-error "Unclassifiable OpenACC directive" }
+      END
+
+      MODULE m_w_2
+      EXTERNAL :: w_2
+!$ACC ROUTINE (w_2) WORKER WORKER WORKER ! { dg-error "Unclassifiable OpenACC directive" }
+      END
+
+      MODULE m_v_2
+      EXTERNAL :: v_2
+!$ACC ROUTINE (v_2) VECTOR VECTOR VECTOR ! { dg-error "Unclassifiable OpenACC directive" }
+      END
+
+      SUBROUTINE s_2
+!$ACC ROUTINE SEQ SEQ SEQ ! { dg-error "Unclassifiable OpenACC directive" }
+      END
+
+
+      SUBROUTINE g_3
+!$ACC ROUTINE
+!$ACC& GANG
+!$ACC& WORKER ! { dg-error ".worker. specifies a conflicting level of parallelism" "" { xfail *-*-* } }
+      END
+
+      MODULE m_g_3
+      EXTERNAL :: g_3
+!$ACC ROUTINE (g_3) ! { dg-error ".#pragma acc routine. already applied to .\[void \]*g_3." "" { xfail *-*-* } }
+!$ACC& GANG
+!$ACC& SEQ ! { dg-error ".seq. specifies a conflicting level of parallelism" "" { xfail *-*-* } }
+     
+!$ACC ROUTINE (g_3) ! { dg-error ".#pragma acc routine. already applied to .\[void \]*g_3." "" { xfail *-*-* } }
+!$ACC& GANG
+!$ACC& VECTOR ! { dg-error ".vector. specifies a conflicting level of parallelism" "" { xfail *-*-* } }
+      END
+
+      MODULE m_w_3
+      EXTERNAL :: w_3
+!$ACC ROUTINE (w_3)
+!$ACC& WORKER
+!$ACC& VECTOR ! { dg-error ".vector. specifies a conflicting level of parallelism" "" { xfail *-*-* } }
+!$ACC ROUTINE (w_3) ! { dg-error ".#pragma acc routine. already applied to .\[void \]*w_3." "" { xfail *-*-* } }
+!$ACC& WORKER
+!$ACC& GANG ! { dg-error ".gang. specifies a conflicting level of parallelism" "" { xfail *-*-* } }
+!$ACC ROUTINE (w_3) ! { dg-error ".#pragma acc routine. already applied to .\[void \]*w_3." "" { xfail *-*-* } }
+!$ACC& WORKER
+!$ACC& SEQ ! { dg-error ".seq. specifies a conflicting level of parallelism" "" { xfail *-*-* } }
+      END
+
+      SUBROUTINE v_3
+!$ACC ROUTINE (v_3)
+!$ACC& VECTOR
+!$ACC& SEQ ! { dg-error ".seq. specifies a conflicting level of parallelism" "" { xfail *-*-* } }
+      END
+
+      MODULE m_v_3
+      EXTERNAL :: v_3
+!$ACC ROUTINE (v_3) ! { dg-error ".#pragma acc routine. already applied to .\[void \]*v_3." "" { xfail *-*-* } }
+!$ACC& VECTOR
+!$ACC& WORKER ! { dg-error ".worker. specifies a conflicting level of parallelism" "" { xfail *-*-* } }
+!$ACC ROUTINE (v_3) ! { dg-error ".#pragma acc routine. already applied to .\[void \]*v_3." "" { xfail *-*-* } }
+!$ACC& VECTOR
+!$ACC& GANG ! { dg-error ".gang. specifies a conflicting level of parallelism" "" { xfail *-*-* } }
+      END
+
+      MODULE m_s_3
+      EXTERNAL :: s_3
+!$ACC ROUTINE (s_3)
+!$ACC& SEQ
+!$ACC& GANG ! { dg-error ".gang. specifies a conflicting level of parallelism" "" { xfail *-*-* } }
+!$ACC ROUTINE (s_3) ! { dg-error ".#pragma acc routine. already applied to .\[void \]*s_3." "" { xfail *-*-* } }
+!$ACC& SEQ
+!$ACC& VECTOR ! { dg-error ".vector. specifies a conflicting level of parallelism" "" { xfail *-*-* } }
+!$ACC ROUTINE (s_3) ! { dg-error ".#pragma acc routine. already applied to .\[void \]*s_3." "" { xfail *-*-* } }
+!$ACC& SEQ
+!$ACC& WORKER ! { dg-error ".worker. specifies a conflicting level of parallelism" "" { xfail *-*-* } }
+      END


Grüße
 Thomas

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

* Repeated use of the OpenACC routine directive
@ 2016-08-01 15:51   ` Thomas Schwinge
  2016-08-17  1:05     ` Thomas Schwinge
  2018-10-02 14:57     ` [patch,openacc] " Cesar Philippidis
  0 siblings, 2 replies; 16+ messages in thread
From: Thomas Schwinge @ 2016-08-01 15:51 UTC (permalink / raw)
  To: gcc-patches

Hi!

We found that it's not correct that we currently unconditionally diagnose
an error for repeated use of the OpenACC routine directive on one
function/declaration.  (For reference, it is also permissible for an
"ordinary" function to have several declarations plus a definition, as
long as these are compatible.)  This is, the following shall be valid:

    #pragma acc routine worker
    void f(void)
    {
    }
    #pragma acc routine (f) worker
    #pragma acc routine worker
    extern void f(void);

As it is not detailed in the specification, the semantics of repeated use
of OpenACC routine will be implementation-defined.  Within one
translation unit, we just remove the existing diagnostic, and declare it
user error if any two usages are not compatible, but we try to be
helpful, and produce a compile-time diagnostic.  For incompatible use
spanning over multiple translation units, we're not currently attempting
to produce meaningful diagnostics/semantics; this would be more difficult
(out of scope at present).

Depending on other patches posted (such as "Use
verify_oacc_routine_clauses for C/C++"), here is a patch to implement
this for C/C++ -- Fortran is blocked on the issues raised in the "Use
verify_oacc_routine_clauses for Fortran" patch submission.

One change is to attach an OpenACC routine directive's clauses to the
"omp declare target" attribute (in spirit similar to how they're attached
to OMP_*/GIMPLE_OMP_* codes).  (It will then also be possible to get rid
of the "oacc function" attribute and instead have these represented as
OpenACC gang/worker/vector/seq clauses attached to "omp declare target".)
While currently not used for OpenMP, it won't do any harm: continue using
NULL_TREE there, the "empty" clause list.  As discussed months ago, on
gomp-4_0-branch we're already using that for implementing the OpenACC
bind and nohost clauses.

Is this conceptually OK for trunk?

commit 91f0742f64f9f0671480023c9949a596691e8476
Author: Thomas Schwinge <thomas@codesourcery.com>
Date:   Fri Jul 29 17:47:47 2016 +0200

    Repeated use of the OpenACC routine directive
    
    	gcc/
    	* omp-low.c (verify_oacc_routine_clauses): Change formal
    	parameters.  Add checking if already marked as an accelerator
    	routine.  Adjust all users.
    	gcc/c-family/
    	* c-common.c (c_common_attribute_table): Set -1 max_len for "omp
    	declare target".
    	gcc/c/
    	* c-parser.c (c_finish_oacc_routine): Rework checking if already
    	marked as an accelerator routine.
    	gcc/cp/
    	* parser.c (cp_finalize_oacc_routine): Rework checking if already
    	marked as an accelerator routine.
    	gcc/fortran/
    	* f95-lang.c (gfc_attribute_table): Set -1 max_len for "omp
    	declare target".
    	gcc/testsuite/
    	* c-c++-common/goacc/oaccdevlow-routine.c: Update.
    	* c-c++-common/goacc/routine-5.c: Likewise.
    	* c-c++-common/goacc/routine-level-of-parallelism-1.c: Likewise.
    	* c-c++-common/goacc/routine-level-of-parallelism-2.c: New file.
---
 gcc/c-family/c-common.c                            |   2 +-
 gcc/c/c-parser.c                                   |  42 ++--
 gcc/cp/parser.c                                    |  47 +++--
 gcc/fortran/f95-lang.c                             |   2 +-
 gcc/fortran/trans-openmp.c                         |   4 +-
 gcc/omp-low.c                                      |  61 +++++-
 gcc/omp-low.h                                      |   2 +-
 .../c-c++-common/goacc/oaccdevlow-routine.c        |   2 +-
 gcc/testsuite/c-c++-common/goacc/routine-5.c       |  46 +---
 .../goacc/routine-level-of-parallelism-1.c         | 233 ++++++++++++++++++---
 .../goacc/routine-level-of-parallelism-2.c         |  71 +++++++
 11 files changed, 395 insertions(+), 117 deletions(-)

diff --git gcc/c-family/c-common.c gcc/c-family/c-common.c
index 16e3965..63ed75a 100644
--- gcc/c-family/c-common.c
+++ gcc/c-family/c-common.c
@@ -822,7 +822,7 @@ const struct attribute_spec c_common_attribute_table[] =
 			      handle_omp_declare_simd_attribute, false },
   { "simd",		      0, 1, true,  false, false,
 			      handle_simd_attribute, false },
-  { "omp declare target",     0, 0, true, false, false,
+  { "omp declare target",     0, -1, true, false, false,
 			      handle_omp_declare_target_attribute, false },
   { "omp declare target link", 0, 0, true, false, false,
 			      handle_omp_declare_target_attribute, false },
diff --git gcc/c/c-parser.c gcc/c/c-parser.c
index 24f60cf..8e67ed8 100644
--- gcc/c/c-parser.c
+++ gcc/c/c-parser.c
@@ -14155,33 +14155,37 @@ c_finish_oacc_routine (struct oacc_routine_data *data, tree fndecl,
       return;
     }
 
-  verify_oacc_routine_clauses (&data->clauses, data->loc);
-
-  if (get_oacc_fn_attrib (fndecl))
+  int compatible
+    = verify_oacc_routine_clauses (fndecl, &data->clauses, data->loc,
+				   "#pragma acc routine");
+  if (compatible < 0)
     {
-      error_at (data->loc,
-		"%<#pragma acc routine%> already applied to %qD", fndecl);
       data->error_seen = true;
       return;
     }
-
-  if (TREE_USED (fndecl) || (!is_defn && DECL_SAVED_TREE (fndecl)))
+  if (compatible > 0)
     {
-      error_at (data->loc,
-		"%<#pragma acc routine%> must be applied before %s",
-		TREE_USED (fndecl) ? "use" : "definition");
-      data->error_seen = true;
-      return;
     }
+  else
+    {
+      if (TREE_USED (fndecl) || (!is_defn && DECL_SAVED_TREE (fndecl)))
+	{
+	  error_at (data->loc,
+		    "%<#pragma acc routine%> must be applied before %s",
+		    TREE_USED (fndecl) ? "use" : "definition");
+	  data->error_seen = true;
+	  return;
+	}
 
-  /* Process the routine's dimension clauses.  */
-  tree dims = build_oacc_routine_dims (data->clauses);
-  replace_oacc_fn_attrib (fndecl, dims);
+      /* Set the routine's level of parallelism.  */
+      tree dims = build_oacc_routine_dims (data->clauses);
+      replace_oacc_fn_attrib (fndecl, dims);
 
-  /* Add an "omp declare target" attribute.  */
-  DECL_ATTRIBUTES (fndecl)
-    = tree_cons (get_identifier ("omp declare target"),
-		 NULL_TREE, DECL_ATTRIBUTES (fndecl));
+      /* Add an "omp declare target" attribute.  */
+      DECL_ATTRIBUTES (fndecl)
+	= tree_cons (get_identifier ("omp declare target"),
+		     data->clauses, DECL_ATTRIBUTES (fndecl));
+    }
 
   /* Remember that we've used this "#pragma acc routine".  */
   data->fndecl_seen = true;
diff --git gcc/cp/parser.c gcc/cp/parser.c
index 6197fc9..34afa17 100644
--- gcc/cp/parser.c
+++ gcc/cp/parser.c
@@ -36750,34 +36750,39 @@ cp_finalize_oacc_routine (cp_parser *parser, tree fndecl, bool is_defn)
 	  return;
 	}
 
-      verify_oacc_routine_clauses (&parser->oacc_routine->clauses,
-				   parser->oacc_routine->loc);
-
-      if (get_oacc_fn_attrib (fndecl))
+      int compatible
+	= verify_oacc_routine_clauses (fndecl, &parser->oacc_routine->clauses,
+				       parser->oacc_routine->loc,
+				       "#pragma acc routine");
+      if (compatible < 0)
 	{
-	  error_at (parser->oacc_routine->loc,
-		    "%<#pragma acc routine%> already applied to %qD", fndecl);
 	  parser->oacc_routine = NULL;
 	  return;
 	}
-
-      if (TREE_USED (fndecl) || (!is_defn && DECL_SAVED_TREE (fndecl)))
+      if (compatible > 0)
 	{
-	  error_at (parser->oacc_routine->loc,
-		    "%<#pragma acc routine%> must be applied before %s",
-		    TREE_USED (fndecl) ? "use" : "definition");
-	  parser->oacc_routine = NULL;
-	  return;
 	}
+      else
+	{
+	  if (TREE_USED (fndecl) || (!is_defn && DECL_SAVED_TREE (fndecl)))
+	    {
+	      error_at (parser->oacc_routine->loc,
+			"%<#pragma acc routine%> must be applied before %s",
+			TREE_USED (fndecl) ? "use" : "definition");
+	      parser->oacc_routine = NULL;
+	      return;
+	    }
+
+	  /* Set the routine's level of parallelism.  */
+	  tree dims = build_oacc_routine_dims (parser->oacc_routine->clauses);
+	  replace_oacc_fn_attrib (fndecl, dims);
 
-      /* Process the routine's dimension clauses.  */
-      tree dims = build_oacc_routine_dims (parser->oacc_routine->clauses);
-      replace_oacc_fn_attrib (fndecl, dims);
-      
-      /* Add an "omp declare target" attribute.  */
-      DECL_ATTRIBUTES (fndecl)
-	= tree_cons (get_identifier ("omp declare target"),
-		     NULL_TREE, DECL_ATTRIBUTES (fndecl));
+	  /* Add an "omp declare target" attribute.  */
+	  DECL_ATTRIBUTES (fndecl)
+	    = tree_cons (get_identifier ("omp declare target"),
+			 parser->oacc_routine->clauses,
+			 DECL_ATTRIBUTES (fndecl));
+	}
 
       /* Don't unset parser->oacc_routine here: we may still need it to
 	 diagnose wrong usage.  But, remember that we've used this "#pragma acc
diff --git gcc/fortran/f95-lang.c gcc/fortran/f95-lang.c
index 2b58173..5f6e54f 100644
--- gcc/fortran/f95-lang.c
+++ gcc/fortran/f95-lang.c
@@ -90,7 +90,7 @@ static const struct attribute_spec gfc_attribute_table[] =
 {
   /* { name, min_len, max_len, decl_req, type_req, fn_type_req, handler,
        affects_type_identity } */
-  { "omp declare target", 0, 0, true,  false, false,
+  { "omp declare target", 0, -1, true,  false, false,
     gfc_handle_omp_declare_target_attribute, false },
   { "oacc function", 0, -1, true,  false, false,
     gfc_handle_omp_declare_target_attribute, false },
diff --git gcc/fortran/trans-openmp.c gcc/fortran/trans-openmp.c
index 254732c..0239871 100644
--- gcc/fortran/trans-openmp.c
+++ gcc/fortran/trans-openmp.c
@@ -4612,7 +4612,9 @@ gfc_oacc_routine_dims (gfc_omp_clauses *clauses, locus location)
 	  clauses_t = c;
 	}
     }
-  verify_oacc_routine_clauses (&clauses_t, loc);
+  (void) verify_oacc_routine_clauses (/* TODO */ NULL_TREE,
+				      &clauses_t, loc,
+				      "!$ACC ROUTINE");
 
   gcc_checking_assert (clauses_t != NULL_TREE
 		       && OMP_CLAUSE_CHAIN (clauses_t) == NULL_TREE);
diff --git gcc/omp-low.c gcc/omp-low.c
index 74b8a99..8a8f222 100644
--- gcc/omp-low.c
+++ gcc/omp-low.c
@@ -12597,11 +12597,14 @@ set_oacc_fn_attrib (tree fn, tree clauses, vec<tree> *args)
 
 /* Verify OpenACC routine clauses.
 
-   Upon returning, the chain of clauses will contain exactly one clause
-   specifying the level of parallelism.  */
+   Returns 0 if FNDECL should be marked as an accelerator routine, 1 if it has
+   already been marked in compatible way, and -1 if incompatible.  Upon
+   returning, the chain of clauses will contain exactly one clause specifying
+   the level of parallelism.  */
 
-void
-verify_oacc_routine_clauses (tree *clauses, location_t loc)
+int
+verify_oacc_routine_clauses (tree fndecl, tree *clauses, location_t loc,
+			     const char *routine_str)
 {
   tree c_level = NULL_TREE;
   tree c_p = NULL_TREE;
@@ -12647,6 +12650,56 @@ verify_oacc_routine_clauses (tree *clauses, location_t loc)
       OMP_CLAUSE_CHAIN (c_level) = *clauses;
       *clauses = c_level;
     }
+  /* In *clauses, we now have exactly one clause specifying the level of
+     parallelism.  */
+
+  /* Still got some work to do for Fortran...  */
+  if (fndecl == NULL_TREE)
+    return 0;
+
+  tree attr
+    = lookup_attribute ("omp declare target", DECL_ATTRIBUTES (fndecl));
+  if (attr != NULL_TREE)
+    {
+      /* If a "#pragma acc routine" has already been applied, just verify
+	 this one for compatibility.  */
+      /* Collect previous directive's clauses.  */
+      tree c_level_p = NULL_TREE;
+      for (tree c = TREE_VALUE (attr); c; c = OMP_CLAUSE_CHAIN (c))
+	switch (OMP_CLAUSE_CODE (c))
+	  {
+	  case OMP_CLAUSE_GANG:
+	  case OMP_CLAUSE_WORKER:
+	  case OMP_CLAUSE_VECTOR:
+	  case OMP_CLAUSE_SEQ:
+	    gcc_checking_assert (c_level_p == NULL_TREE);
+	    c_level_p = c;
+	    break;
+	  default:
+	    gcc_unreachable ();
+	  }
+      gcc_checking_assert (c_level_p != NULL_TREE);
+      /* ..., and compare to current directive's, which we've already collected
+	 above.  */
+      if (OMP_CLAUSE_CODE (c_level) != OMP_CLAUSE_CODE (c_level_p))
+	{
+	  error_at (OMP_CLAUSE_LOCATION (c_level),
+		    "incompatible %qs clause when applying"
+		    " %<%s%> to %qD, which has already been"
+		    " marked as an accelerator routine",
+		    omp_clause_code_name[OMP_CLAUSE_CODE (c_level)],
+		    routine_str, fndecl);
+	  inform (OMP_CLAUSE_LOCATION (c_level_p),
+		  "... with %qs clause here",
+		  omp_clause_code_name[OMP_CLAUSE_CODE (c_level_p)]);
+	  /* Incompatible.  */
+	  return -1;
+	}
+      /* Compatible.  */
+      return 1;
+    }
+
+  return 0;
 }
 
 /*  Process the OpenACC routine's clauses to generate an attribute
diff --git gcc/omp-low.h gcc/omp-low.h
index f02d990..0ea5308 100644
--- gcc/omp-low.h
+++ gcc/omp-low.h
@@ -31,7 +31,7 @@ extern bool make_gimple_omp_edges (basic_block, struct omp_region **, int *);
 extern void omp_finish_file (void);
 extern tree omp_member_access_dummy_var (tree);
 extern void replace_oacc_fn_attrib (tree, tree);
-extern void verify_oacc_routine_clauses (tree *, location_t);
+extern int verify_oacc_routine_clauses (tree, tree *, location_t, const char *);
 extern tree build_oacc_routine_dims (tree);
 extern tree get_oacc_fn_attrib (tree);
 extern void set_oacc_fn_attrib (tree, tree, vec<tree> *);
diff --git gcc/testsuite/c-c++-common/goacc/oaccdevlow-routine.c gcc/testsuite/c-c++-common/goacc/oaccdevlow-routine.c
index bb02fc7..771f3b1 100644
--- gcc/testsuite/c-c++-common/goacc/oaccdevlow-routine.c
+++ gcc/testsuite/c-c++-common/goacc/oaccdevlow-routine.c
@@ -21,7 +21,7 @@ void ROUTINE ()
 }
 
 /* Check the offloaded function's attributes.
-   { dg-final { scan-tree-dump-times "(?n)__attribute__\\(\\(omp declare target, oacc function \\(0 1, 1 0, 1 0\\)\\)\\)" 1 "ompexp" } } */
+   { dg-final { scan-tree-dump-times "(?n)__attribute__\\(\\(omp declare target \\(worker\\), oacc function \\(0 1, 1 0, 1 0\\)\\)\\)" 1 "ompexp" } } */
 
 /* Check the offloaded function's classification and compute dimensions (will
    always be [1, 1, 1] for target compilation).
diff --git gcc/testsuite/c-c++-common/goacc/routine-5.c gcc/testsuite/c-c++-common/goacc/routine-5.c
index 17fe67c..f10651d 100644
--- gcc/testsuite/c-c++-common/goacc/routine-5.c
+++ gcc/testsuite/c-c++-common/goacc/routine-5.c
@@ -150,61 +150,19 @@ void f_static_assert();
 
 #pragma acc routine
 __extension__ extern void ex1();
-#pragma acc routine (ex1) /* { dg-error ".#pragma acc routine. already applied to .\[void \]*ex1" } */
+#pragma acc routine (ex1) worker /* { dg-error "has already been marked as an accelerator routine" } */
 
 #pragma acc routine
 __extension__ __extension__ __extension__ __extension__ __extension__ void ex2()
 {
 }
-#pragma acc routine (ex2) /* { dg-error ".#pragma acc routine. already applied to .\[void \]*ex2" } */
+#pragma acc routine (ex2) worker /* { dg-error "has already been marked as an accelerator routine" } */
 
 #pragma acc routine /* { dg-error ".#pragma acc routine. not immediately followed by function declaration or definition" } */
 __extension__ int ex3;
 #pragma acc routine (ex3) /* { dg-error ".ex3. does not refer to a function" } */
 
 
-/* "#pragma acc routine" already applied.  */
-
-extern void fungsi_1();
-#pragma acc routine(fungsi_1) gang
-#pragma acc routine(fungsi_1) gang /* { dg-error ".#pragma acc routine. already applied to .\[void \]*fungsi_1" } */
-#pragma acc routine(fungsi_1) worker /* { dg-error ".#pragma acc routine. already applied to .\[void \]*fungsi_1" } */
-#pragma acc routine(fungsi_1) vector /* { dg-error ".#pragma acc routine. already applied to .\[void \]*fungsi_1" } */
-
-#pragma acc routine seq
-extern void fungsi_2();
-#pragma acc routine(fungsi_2) seq /* { dg-error ".#pragma acc routine. already applied to .\[void \]*fungsi_2." } */
-#pragma acc routine(fungsi_2) worker /* { dg-error ".#pragma acc routine. already applied to .\[void \]*fungsi_2." } */
-#pragma acc routine(fungsi_2) /* { dg-error ".#pragma acc routine. already applied to .\[void \]*fungsi_2." } */
-
-#pragma acc routine vector
-extern void fungsi_3();
-#pragma acc routine vector /* { dg-error ".#pragma acc routine. already applied to .\[void \]*fungsi_3." } */
-void fungsi_3()
-{
-}
-
-extern void fungsi_4();
-#pragma acc routine (fungsi_4) worker
-#pragma acc routine gang /* { dg-error ".#pragma acc routine. already applied to .\[void \]*fungsi_4." } */
-void fungsi_4()
-{
-}
-
-#pragma acc routine gang
-void fungsi_5()
-{
-}
-#pragma acc routine (fungsi_5) worker /* { dg-error ".#pragma acc routine. already applied to .\[void \]*fungsi_5." } */
-
-#pragma acc routine seq
-void fungsi_6()
-{
-}
-#pragma acc routine seq /* { dg-error ".#pragma acc routine. already applied to .\[void \]*fungsi_6." } */
-extern void fungsi_6();
-
-
 /* "#pragma acc routine" must be applied before.  */
 
 void Bar ();
diff --git gcc/testsuite/c-c++-common/goacc/routine-level-of-parallelism-1.c gcc/testsuite/c-c++-common/goacc/routine-level-of-parallelism-1.c
index fc8bc3c..8f45499 100644
--- gcc/testsuite/c-c++-common/goacc/routine-level-of-parallelism-1.c
+++ gcc/testsuite/c-c++-common/goacc/routine-level-of-parallelism-1.c
@@ -42,10 +42,10 @@ void s_2 (void)
 void g_3 (void)
 {
 }
-#pragma acc routine (g_3) /* { dg-error ".#pragma acc routine. already applied to .\[void \]*g_3." } */ \
+#pragma acc routine (g_3) \
   gang \
   seq /* { dg-error ".seq. specifies a conflicting level of parallelism" } */
-#pragma acc routine (g_3) /* { dg-error ".#pragma acc routine. already applied to .\[void \]*g_3." } */ \
+#pragma acc routine (g_3) \
   gang \
   vector /* { dg-error ".vector. specifies a conflicting level of parallelism" } */
 
@@ -53,10 +53,10 @@ extern void w_3 (void);
 #pragma acc routine (w_3) \
   worker \
   vector /* { dg-error ".vector. specifies a conflicting level of parallelism" } */
-#pragma acc routine (w_3) /* { dg-error ".#pragma acc routine. already applied to .\[void \]*w_3." } */ \
+#pragma acc routine (w_3) \
   worker \
   gang /* { dg-error ".gang. specifies a conflicting level of parallelism" } */
-#pragma acc routine (w_3) /* { dg-error ".#pragma acc routine. already applied to .\[void \]*w_3." } */ \
+#pragma acc routine (w_3) \
   worker \
   seq /* { dg-error ".seq. specifies a conflicting level of parallelism" } */
 
@@ -66,10 +66,10 @@ extern void w_3 (void);
 void v_3 (void)
 {
 }
-#pragma acc routine (v_3) /* { dg-error ".#pragma acc routine. already applied to .\[void \]*v_3." } */ \
+#pragma acc routine (v_3) \
   vector \
   worker /* { dg-error ".worker. specifies a conflicting level of parallelism" } */
-#pragma acc routine (v_3) /* { dg-error ".#pragma acc routine. already applied to .\[void \]*v_3." } */ \
+#pragma acc routine (v_3) \
   vector \
   gang /* { dg-error ".gang. specifies a conflicting level of parallelism" } */
 
@@ -77,10 +77,10 @@ extern void s_3 (void);
 #pragma acc routine (s_3) \
   seq \
   gang /* { dg-error ".gang. specifies a conflicting level of parallelism" } */
-#pragma acc routine (s_3) /* { dg-error ".#pragma acc routine. already applied to .\[void \]*s_3." } */ \
+#pragma acc routine (s_3) \
   seq \
   vector /* { dg-error ".vector. specifies a conflicting level of parallelism" } */
-#pragma acc routine (s_3) /* { dg-error ".#pragma acc routine. already applied to .\[void \]*s_3." } */ \
+#pragma acc routine (s_3) \
   seq \
   worker /* { dg-error ".worker. specifies a conflicting level of parallelism" } */
 
@@ -91,12 +91,12 @@ extern void s_3 (void);
   vector /* { dg-error ".vector. specifies a conflicting level of parallelism" } */ \
   seq /* { dg-error ".seq. specifies a conflicting level of parallelism" } */
 extern void g_4 (void);
-#pragma acc routine (g_4) /* { dg-error ".#pragma acc routine. already applied to .\[void \]*g_4." } */ \
+#pragma acc routine (g_4) \
   gang gang gang /* { dg-error "too many 'gang' clauses" } */ \
   vector /* { dg-error ".vector. specifies a conflicting level of parallelism" } */ \
   worker /* { dg-error ".worker. specifies a conflicting level of parallelism" } */ \
   seq /* { dg-error ".seq. specifies a conflicting level of parallelism" } */
-#pragma acc routine (g_4) /* { dg-error ".#pragma acc routine. already applied to .\[void \]*g_4." } */ \
+#pragma acc routine (g_4) \
   gang gang gang /* { dg-error "too many 'gang' clauses" } */ \
   seq /* { dg-error ".seq. specifies a conflicting level of parallelism" } */ \
   vector /* { dg-error ".vector. specifies a conflicting level of parallelism" } */ \
@@ -108,12 +108,12 @@ extern void w_4 (void);
   seq /* { dg-error ".seq. specifies a conflicting level of parallelism" } */ \
   vector /* { dg-error ".vector. specifies a conflicting level of parallelism" } */ \
   gang /* { dg-error ".gang. specifies a conflicting level of parallelism" } */
-#pragma acc routine (w_4) /* { dg-error ".#pragma acc routine. already applied to .\[void \]*w_4." } */ \
+#pragma acc routine (w_4) \
   worker worker worker /* { dg-error "too many 'worker' clauses" } */ \
   vector /* { dg-error ".vector. specifies a conflicting level of parallelism" } */ \
   seq /* { dg-error ".seq. specifies a conflicting level of parallelism" } */ \
   gang /* { dg-error ".gang. specifies a conflicting level of parallelism" } */
-#pragma acc routine (w_4) /* { dg-error ".#pragma acc routine. already applied to .\[void \]*w_4." } */ \
+#pragma acc routine (w_4) \
   worker worker worker /* { dg-error "too many 'worker' clauses" } */ \
   seq /* { dg-error ".seq. specifies a conflicting level of parallelism" } */ \
   gang /* { dg-error ".gang. specifies a conflicting level of parallelism" } */ \
@@ -127,12 +127,12 @@ extern void w_4 (void);
 void v_4 (void)
 {
 }
-#pragma acc routine (v_4) /* { dg-error ".#pragma acc routine. already applied to .\[void \]*v_4." } */ \
+#pragma acc routine (v_4) \
   vector vector vector /* { dg-error "too many 'vector' clauses" } */ \
   seq /* { dg-error ".seq. specifies a conflicting level of parallelism" } */ \
   worker /* { dg-error ".worker. specifies a conflicting level of parallelism" } */ \
   gang /* { dg-error ".gang. specifies a conflicting level of parallelism" } */
-#pragma acc routine (v_4) /* { dg-error ".#pragma acc routine. already applied to .\[void \]*v_4." } */ \
+#pragma acc routine (v_4) \
   vector vector vector /* { dg-error "too many 'vector' clauses" } */ \
   gang /* { dg-error ".gang. specifies a conflicting level of parallelism" } */ \
   seq /* { dg-error ".seq. specifies a conflicting level of parallelism" } */ \
@@ -146,12 +146,12 @@ void v_4 (void)
 void s_4 (void)
 {
 }
-#pragma acc routine (s_4) /* { dg-error ".#pragma acc routine. already applied to .\[void \]*s_4." } */ \
+#pragma acc routine (s_4) \
   seq seq seq /* { dg-error "too many 'seq' clauses" } */ \
   vector /* { dg-error ".vector. specifies a conflicting level of parallelism" } */ \
   gang /* { dg-error ".gang. specifies a conflicting level of parallelism" } */ \
   worker /* { dg-error ".worker. specifies a conflicting level of parallelism" } */
-#pragma acc routine (s_4) /* { dg-error ".#pragma acc routine. already applied to .\[void \]*s_4." } */ \
+#pragma acc routine (s_4) \
   seq seq seq /* { dg-error "too many 'seq' clauses" } */ \
   worker /* { dg-error ".worker. specifies a conflicting level of parallelism" } */ \
   vector /* { dg-error ".vector. specifies a conflicting level of parallelism" } */ \
@@ -169,7 +169,7 @@ void s_4 (void)
 void g_5 (void)
 {
 }
-#pragma acc routine (g_5) /* { dg-error ".#pragma acc routine. already applied to .\[void \]*g_5." } */ \
+#pragma acc routine (g_5) \
   gang gang gang /* { dg-error "too many 'gang' clauses" } */ \
   vector vector vector vector /* { dg-error "too many 'vector' clauses" } */ \
   /* { dg-error ".vector. specifies a conflicting level of parallelism" "" { target *-*-* } 174 } */ \
@@ -177,7 +177,7 @@ void g_5 (void)
   /* { dg-error ".worker. specifies a conflicting level of parallelism" "" { target *-*-* } 176 } */ \
   seq seq seq seq seq seq /* { dg-error "too many 'seq' clauses" } */ \
   /* { dg-error ".seq. specifies a conflicting level of parallelism" "" { target *-*-* } 178 } */
-#pragma acc routine (g_5) /* { dg-error ".#pragma acc routine. already applied to .\[void \]*g_5." } */ \
+#pragma acc routine (g_5) \
   gang gang gang /* { dg-error "too many 'gang' clauses" } */ \
   seq seq seq seq seq /* { dg-error "too many 'seq' clauses" } */ \
   /* { dg-error ".seq. specifies a conflicting level of parallelism" "" { target *-*-* } 182 } */ \
@@ -195,7 +195,7 @@ void g_5 (void)
   gang gang /* { dg-error "too many 'gang' clauses" } */ \
   /* { dg-error ".gang. specifies a conflicting level of parallelism" "" { target *-*-* } 195 } */
 extern void w_5 (void);
-#pragma acc routine (w_5) /* { dg-error ".#pragma acc routine. already applied to .\[void \]*w_5." } */ \
+#pragma acc routine (w_5) \
   worker worker worker /* { dg-error "too many 'worker' clauses" } */ \
   vector vector vector /* { dg-error "too many 'vector' clauses" } */ \
   /* { dg-error ".vector. specifies a conflicting level of parallelism" "" { target *-*-* } 200 } */ \
@@ -203,7 +203,7 @@ extern void w_5 (void);
   /* { dg-error ".seq. specifies a conflicting level of parallelism" "" { target *-*-* } 202 } */ \
   gang gang /* { dg-error "too many 'gang' clauses" } */ \
   /* { dg-error ".gang. specifies a conflicting level of parallelism" "" { target *-*-* } 204 } */
-#pragma acc routine (w_5) /* { dg-error ".#pragma acc routine. already applied to .\[void \]*w_5." } */ \
+#pragma acc routine (w_5) \
   worker worker worker /* { dg-error "too many 'worker' clauses" } */ \
   seq seq /* { dg-error "too many 'seq' clauses" } */ \
   /* { dg-error ".seq. specifies a conflicting level of parallelism" "" { target *-*-* } 208 } */ \
@@ -221,7 +221,7 @@ extern void w_5 (void);
   gang gang /* { dg-error "too many 'gang' clauses" } */ \
   /* { dg-error ".gang. specifies a conflicting level of parallelism" "" { target *-*-* } 221 } */
 extern void v_5 (void);
-#pragma acc routine (v_5) /* { dg-error ".#pragma acc routine. already applied to .\[void \]*v_5." } */ \
+#pragma acc routine (v_5) \
   vector vector vector /* { dg-error "too many 'vector' clauses" } */ \
   seq seq seq seq seq seq /* { dg-error "too many 'seq' clauses" } */ \
   /* { dg-error ".seq. specifies a conflicting level of parallelism" "" { target *-*-* } 226 } */ \
@@ -229,7 +229,7 @@ extern void v_5 (void);
   /* { dg-error ".worker. specifies a conflicting level of parallelism" "" { target *-*-* } 228 } */ \
   gang gang /* { dg-error "too many 'gang' clauses" } */ \
   /* { dg-error ".gang. specifies a conflicting level of parallelism" "" { target *-*-* } 230 } */
-#pragma acc routine (v_5) /* { dg-error ".#pragma acc routine. already applied to .\[void \]*v_5." } */ \
+#pragma acc routine (v_5) \
   vector vector vector /* { dg-error "too many 'vector' clauses" } */ \
   gang gang /* { dg-error "too many 'gang' clauses" } */ \
   /* { dg-error ".gang. specifies a conflicting level of parallelism" "" { target *-*-* } 234 } */ \
@@ -247,7 +247,7 @@ extern void s_5 (void);
   /* { dg-error ".worker. specifies a conflicting level of parallelism" "" { target *-*-* } 246 } */ \
   gang gang /* { dg-error "too many 'gang' clauses" } */ \
   /* { dg-error ".gang. specifies a conflicting level of parallelism" "" { target *-*-* } 248 } */
-#pragma acc routine (s_5) /* { dg-error ".#pragma acc routine. already applied to .\[void \]*s_5." } */ \
+#pragma acc routine (s_5) \
   seq seq seq /* { dg-error "too many 'seq' clauses" } */ \
   vector vector vector vector /* { dg-error "too many 'vector' clauses" } */ \
   /* { dg-error ".vector. specifies a conflicting level of parallelism" "" { target *-*-* } 252 } */ \
@@ -255,7 +255,7 @@ extern void s_5 (void);
   /* { dg-error ".gang. specifies a conflicting level of parallelism" "" { target *-*-* } 254 } */ \
   worker worker worker /* { dg-error "too many 'worker' clauses" } */ \
   /* { dg-error ".worker. specifies a conflicting level of parallelism" "" { target *-*-* } 256 } */
-#pragma acc routine (s_5) /* { dg-error ".#pragma acc routine. already applied to .\[void \]*s_5." } */ \
+#pragma acc routine (s_5) \
   seq seq seq /* { dg-error "too many 'seq' clauses" } */ \
   worker worker /* { dg-error "too many 'worker' clauses" } */ \
   /* { dg-error ".worker. specifies a conflicting level of parallelism" "" { target *-*-* } 260 } */ \
@@ -263,3 +263,188 @@ extern void s_5 (void);
   /* { dg-error ".vector. specifies a conflicting level of parallelism" "" { target *-*-* } 262 } */ \
   gang gang /* { dg-error "too many 'gang' clauses" } */ \
   /* { dg-error ".gang. specifies a conflicting level of parallelism" "" { target *-*-* } 264 } */
+
+
+/* Like the *_5 tests, but with the order of clauses changed in the second and
+   following routine directives for the specific *_5 function.  */
+
+#pragma acc routine \
+  gang gang gang /* { dg-error "too many 'gang' clauses" } */ \
+  worker worker /* { dg-error "too many 'worker' clauses" } */ \
+  /* { dg-error ".worker. specifies a conflicting level of parallelism" "" { target *-*-* } 273 } */ \
+  vector vector vector /* { dg-error "too many 'vector' clauses" } */ \
+  /* { dg-error ".vector. specifies a conflicting level of parallelism" "" { target *-*-* } 275 } */ \
+  seq seq seq seq seq /* { dg-error "too many 'seq' clauses" } */ \
+  /* { dg-error ".seq. specifies a conflicting level of parallelism" "" { target *-*-* } 277 } */
+void g_6 (void)
+{
+}
+#pragma acc routine (g_6) \
+  vector vector vector vector /* { dg-error "too many 'vector' clauses" } */ \
+  /* { dg-error "incompatible .vector. clause when applying .#pragma acc routine. to .\[void \]*g_6\[\\(\\)\]*., which has already been marked as an accelerator routine" "" { target *-*-* } 283 } */ \
+  gang gang gang /* { dg-error "too many 'gang' clauses" } */ \
+  /* { dg-error ".gang. specifies a conflicting level of parallelism" "" { target *-*-* } 285 } */ \
+  worker worker worker /* { dg-error "too many 'worker' clauses" } */ \
+  /* { dg-error ".worker. specifies a conflicting level of parallelism" "" { target *-*-* } 287 } */ \
+  seq seq seq seq seq seq /* { dg-error "too many 'seq' clauses" } */ \
+  /* { dg-error ".seq. specifies a conflicting level of parallelism" "" { target *-*-* } 289 } */
+#pragma acc routine (g_6) \
+  seq seq seq seq seq /* { dg-error "too many 'seq' clauses" } */ \
+  /* { dg-error "incompatible .seq. clause when applying .#pragma acc routine. to .\[void \]*g_6\[\\(\\)\]*., which has already been marked as an accelerator routine" "" { target *-*-* } 292 } */ \
+  gang gang gang /* { dg-error "too many 'gang' clauses" } */ \
+  /* { dg-error ".gang. specifies a conflicting level of parallelism" "" { target *-*-* } 294 } */ \
+  vector vector vector vector /* { dg-error "too many 'vector' clauses" } */ \
+  /* { dg-error ".vector. specifies a conflicting level of parallelism" "" { target *-*-* } 296 } */ \
+  worker worker worker worker /* { dg-error "too many 'worker' clauses" } */ \
+  /* { dg-error ".worker. specifies a conflicting level of parallelism" "" { target *-*-* } 298 } */
+
+#pragma acc routine \
+  worker worker worker /* { dg-error "too many 'worker' clauses" } */ \
+  seq seq seq seq /* { dg-error "too many 'seq' clauses" } */ \
+  /* { dg-error ".seq. specifies a conflicting level of parallelism" "" { target *-*-* } 303 } */ \
+  vector vector vector vector vector /* { dg-error "too many 'vector' clauses" } */ \
+  /* { dg-error ".vector. specifies a conflicting level of parallelism" "" { target *-*-* } 305 } */ \
+  gang gang /* { dg-error "too many 'gang' clauses" } */ \
+  /* { dg-error ".gang. specifies a conflicting level of parallelism" "" { target *-*-* } 307 } */
+extern void w_6 (void);
+#pragma acc routine (w_6) \
+  vector vector vector /* { dg-error "too many 'vector' clauses" } */ \
+  /* { dg-error "incompatible .vector. clause when applying .#pragma acc routine. to .\[void \]*w_6\[\\(\\)\]*., which has already been marked as an accelerator routine" "" { target *-*-* } 311 } */ \
+  worker worker worker /* { dg-error "too many 'worker' clauses" } */ \
+  /* { dg-error ".worker. specifies a conflicting level of parallelism" "" { target *-*-* } 313 } */ \
+  seq seq seq /* { dg-error "too many 'seq' clauses" } */ \
+  /* { dg-error ".seq. specifies a conflicting level of parallelism" "" { target *-*-* } 315 } */ \
+  gang gang /* { dg-error "too many 'gang' clauses" } */ \
+  /* { dg-error ".gang. specifies a conflicting level of parallelism" "" { target *-*-* } 317 } */
+#pragma acc routine (w_6) \
+  seq seq /* { dg-error "too many 'seq' clauses" } */ \
+  /* { dg-error "incompatible .seq. clause when applying .#pragma acc routine. to .\[void \]*w_6\[\\(\\)\]*., which has already been marked as an accelerator routine" "" { target *-*-* } 320 } */ \
+  worker worker worker /* { dg-error "too many 'worker' clauses" } */ \
+  /* { dg-error ".worker. specifies a conflicting level of parallelism" "" { target *-*-* } 322 } */ \
+  gang gang /* { dg-error "too many 'gang' clauses" } */ \
+  /* { dg-error ".gang. specifies a conflicting level of parallelism" "" { target *-*-* } 324 } */ \
+  vector vector vector vector /* { dg-error "too many 'vector' clauses" } */ \
+  /* { dg-error ".vector. specifies a conflicting level of parallelism" "" { target *-*-* } 326 } */
+
+#pragma acc routine \
+  vector vector vector /* { dg-error "too many 'vector' clauses" } */ \
+  worker worker worker /* { dg-error "too many 'worker' clauses" } */ \
+  /* { dg-error ".worker. specifies a conflicting level of parallelism" "" { target *-*-* } 331 } */ \
+  seq seq seq seq seq seq /* { dg-error "too many 'seq' clauses" } */ \
+  /* { dg-error ".seq. specifies a conflicting level of parallelism" "" { target *-*-* } 333 } */ \
+  gang gang /* { dg-error "too many 'gang' clauses" } */ \
+  /* { dg-error ".gang. specifies a conflicting level of parallelism" "" { target *-*-* } 335 } */
+extern void v_6 (void);
+#pragma acc routine (v_6) \
+  seq seq seq seq seq seq /* { dg-error "too many 'seq' clauses" } */ \
+  /* { dg-error "incompatible .seq. clause when applying .#pragma acc routine. to .\[void \]*v_6\[\\(\\)\]*., which has already been marked as an accelerator routine" "" { target *-*-* } 339 } */ \
+  vector vector vector /* { dg-error "too many 'vector' clauses" } */ \
+  /* { dg-error ".vector. specifies a conflicting level of parallelism" "" { target *-*-* } 341 } */ \
+  worker worker /* { dg-error "too many 'worker' clauses" } */ \
+  /* { dg-error ".worker. specifies a conflicting level of parallelism" "" { target *-*-* } 343 } */ \
+  gang gang /* { dg-error "too many 'gang' clauses" } */ \
+  /* { dg-error ".gang. specifies a conflicting level of parallelism" "" { target *-*-* } 345 } */
+#pragma acc routine (v_6) \
+  gang gang /* { dg-error "too many 'gang' clauses" } */ \
+  /* { dg-error "incompatible .gang. clause when applying .#pragma acc routine. to .\[void \]*v_6\[\\(\\)\]*., which has already been marked as an accelerator routine" "" { target *-*-* } 348 } */ \
+  vector vector vector /* { dg-error "too many 'vector' clauses" } */ \
+  /* { dg-error ".vector. specifies a conflicting level of parallelism" "" { target *-*-* } 350 } */ \
+  seq seq seq seq seq /* { dg-error "too many 'seq' clauses" } */ \
+  /* { dg-error ".seq. specifies a conflicting level of parallelism" "" { target *-*-* } 352 } */ \
+  worker worker /* { dg-error "too many 'worker' clauses" } */ \
+  /* { dg-error ".worker. specifies a conflicting level of parallelism" "" { target *-*-* } 354 } */
+
+extern void s_6 (void);
+#pragma acc routine (s_6) \
+  seq seq seq /* { dg-error "too many 'seq' clauses" } */ \
+  vector vector vector vector /* { dg-error "too many 'vector' clauses" } */ \
+  /* { dg-error ".vector. specifies a conflicting level of parallelism" "" { target *-*-* } 360 } */ \
+  worker worker worker worker worker /* { dg-error "too many 'worker' clauses" } */ \
+  /* { dg-error ".worker. specifies a conflicting level of parallelism" "" { target *-*-* } 362 } */ \
+  gang gang /* { dg-error "too many 'gang' clauses" } */ \
+  /* { dg-error ".gang. specifies a conflicting level of parallelism" "" { target *-*-* } 364 } */
+#pragma acc routine (s_6) \
+  vector vector vector vector /* { dg-error "too many 'vector' clauses" } */ \
+  /* { dg-error "incompatible .vector. clause when applying .#pragma acc routine. to .\[void \]*s_6\[\\(\\)\]*., which has already been marked as an accelerator routine" "" { target *-*-* } 367 } */ \
+  seq seq seq /* { dg-error "too many 'seq' clauses" } */ \
+  /* { dg-error ".seq. specifies a conflicting level of parallelism" "" { target *-*-* } 369 } */ \
+  gang gang /* { dg-error "too many 'gang' clauses" } */ \
+  /* { dg-error ".gang. specifies a conflicting level of parallelism" "" { target *-*-* } 371 } */ \
+  worker worker worker /* { dg-error "too many 'worker' clauses" } */ \
+  /* { dg-error ".worker. specifies a conflicting level of parallelism" "" { target *-*-* } 373 } */
+#pragma acc routine (s_6) \
+  worker worker /* { dg-error "too many 'worker' clauses" } */ \
+  /* { dg-error "incompatible .worker. clause when applying .#pragma acc routine. to .\[void \]*s_6\[\\(\\)\]*., which has already been marked as an accelerator routine" "" { target *-*-* } 376 } */ \
+  seq seq seq /* { dg-error "too many 'seq' clauses" } */ \
+  /* { dg-error ".seq. specifies a conflicting level of parallelism" "" { target *-*-* } 378 } */ \
+  vector vector vector vector /* { dg-error "too many 'vector' clauses" } */ \
+  /* { dg-error ".vector. specifies a conflicting level of parallelism" "" { target *-*-* } 380 } */ \
+  gang gang /* { dg-error "too many 'gang' clauses" } */ \
+  /* { dg-error ".gang. specifies a conflicting level of parallelism" "" { target *-*-* } 382 } */
+
+
+/* Like the *_6 tests, but without all the duplicate clauses, so that the
+   routine directives are valid in isolation.  */
+
+#pragma acc routine \
+  gang
+void g_7 (void)
+{
+}
+#pragma acc routine (g_7) \
+  vector /* { dg-error "incompatible .vector. clause when applying .#pragma acc routine. to .\[void \]*g_7\[\\(\\)\]*., which has already been marked as an accelerator routine" } */
+#pragma acc routine (g_7) \
+  seq /* { dg-error "incompatible .seq. clause when applying .#pragma acc routine. to .\[void \]*g_7\[\\(\\)\]*., which has already been marked as an accelerator routine" } */
+
+#pragma acc routine \
+  worker
+extern void w_7 (void);
+#pragma acc routine (w_7) \
+  vector /* { dg-error "incompatible .vector. clause when applying .#pragma acc routine. to .\[void \]*w_7\[\\(\\)\]*., which has already been marked as an accelerator routine" } */
+#pragma acc routine (w_7) \
+  seq /* { dg-error "incompatible .seq. clause when applying .#pragma acc routine. to .\[void \]*w_7\[\\(\\)\]*., which has already been marked as an accelerator routine" } */
+
+#pragma acc routine \
+  vector
+extern void v_7 (void);
+#pragma acc routine (v_7) \
+  seq /* { dg-error "incompatible .seq. clause when applying .#pragma acc routine. to .\[void \]*v_7\[\\(\\)\]*., which has already been marked as an accelerator routine" } */
+#pragma acc routine (v_7) \
+  gang /* { dg-error "incompatible .gang. clause when applying .#pragma acc routine. to .\[void \]*v_7\[\\(\\)\]*., which has already been marked as an accelerator routine" } */
+
+extern void s_7 (void);
+#pragma acc routine (s_7) \
+  seq
+#pragma acc routine (s_7) \
+  vector /* { dg-error "incompatible .vector. clause when applying .#pragma acc routine. to .\[void \]*s_7\[\\(\\)\]*., which has already been marked as an accelerator routine" } */
+#pragma acc routine (s_7) \
+  worker /* { dg-error "incompatible .worker. clause when applying .#pragma acc routine. to .\[void \]*s_7\[\\(\\)\]*., which has already been marked as an accelerator routine" } */
+
+
+/* Test cases for implicit seq clause.  */
+
+#pragma acc routine \
+  gang
+void g_8 (void)
+{
+}
+#pragma acc routine (g_8) /* { dg-error "incompatible .seq. clause when applying .#pragma acc routine. to .\[void \]*g_8\[\\(\\)\]*., which has already been marked as an accelerator routine" } */
+
+#pragma acc routine \
+  worker
+extern void w_8 (void);
+#pragma acc routine (w_8) /* { dg-error "incompatible .seq. clause when applying .#pragma acc routine. to .\[void \]*w_8\[\\(\\)\]*., which has already been marked as an accelerator routine" } */
+
+#pragma acc routine \
+  vector
+extern void v_8 (void);
+#pragma acc routine (v_8) /* { dg-error "incompatible .seq. clause when applying .#pragma acc routine. to .\[void \]*v_8\[\\(\\)\]*., which has already been marked as an accelerator routine" } */
+
+extern void s_8 (void);
+#pragma acc routine (s_8)
+#pragma acc routine (s_8) \
+  vector /* { dg-error "incompatible .vector. clause when applying .#pragma acc routine. to .\[void \]*s_8\[\\(\\)\]*., which has already been marked as an accelerator routine" } */
+#pragma acc routine (s_8) \
+  gang /* { dg-error "incompatible .gang. clause when applying .#pragma acc routine. to .\[void \]*s_8\[\\(\\)\]*., which has already been marked as an accelerator routine" } */
+#pragma acc routine (s_8) \
+  worker /* { dg-error "incompatible .worker. clause when applying .#pragma acc routine. to .\[void \]*s_8\[\\(\\)\]*., which has already been marked as an accelerator routine" } */
diff --git gcc/testsuite/c-c++-common/goacc/routine-level-of-parallelism-2.c gcc/testsuite/c-c++-common/goacc/routine-level-of-parallelism-2.c
new file mode 100644
index 0000000..1217397
--- /dev/null
+++ gcc/testsuite/c-c++-common/goacc/routine-level-of-parallelism-2.c
@@ -0,0 +1,71 @@
+/* Test various aspects of clauses specifying compatible levels of
+   parallelism with the OpenACC routine directive.  The Fortran counterpart is
+   ../../gfortran.dg/goacc/routine-level-of-parallelism-2.f.  */
+
+#pragma acc routine gang
+void g_1 (void)
+{
+}
+#pragma acc routine (g_1) gang
+#pragma acc routine (g_1) gang
+
+
+extern void w_1 (void);
+#pragma acc routine (w_1) worker
+#pragma acc routine (w_1) worker
+#pragma acc routine (w_1) worker
+
+
+#pragma acc routine vector
+extern void v_1 (void);
+#pragma acc routine (v_1) vector
+#pragma acc routine (v_1) vector
+
+
+/* Also test the implicit seq clause.  */
+
+#pragma acc routine seq
+extern void s_1_1 (void);
+#pragma acc routine (s_1_1)
+#pragma acc routine (s_1_1) seq
+#pragma acc routine (s_1_1)
+#pragma acc routine (s_1_1) seq
+
+#pragma acc routine
+extern void s_1_2 (void);
+#pragma acc routine (s_1_2)
+#pragma acc routine (s_1_2) seq
+#pragma acc routine (s_1_2)
+#pragma acc routine (s_1_2) seq
+
+extern void s_2_1 (void);
+#pragma acc routine (s_2_1) seq
+#pragma acc routine (s_2_1)
+#pragma acc routine (s_2_1) seq
+#pragma acc routine (s_2_1)
+#pragma acc routine (s_2_1) seq
+
+extern void s_2_2 (void);
+#pragma acc routine (s_2_2)
+#pragma acc routine (s_2_2)
+#pragma acc routine (s_2_2) seq
+#pragma acc routine (s_2_2)
+#pragma acc routine (s_2_2) seq
+
+#pragma acc routine seq
+void s_3_1 (void)
+{
+}
+#pragma acc routine (s_3_1)
+#pragma acc routine (s_3_1) seq
+#pragma acc routine (s_3_1)
+#pragma acc routine (s_3_1) seq
+
+#pragma acc routine
+void s_3_2 (void)
+{
+}
+#pragma acc routine (s_3_2)
+#pragma acc routine (s_3_2) seq
+#pragma acc routine (s_3_2)
+#pragma acc routine (s_3_2) seq


Grüße
 Thomas

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

* Re: Use verify_oacc_routine_clauses for Fortran (was: Use verify_oacc_routine_clauses for C/C++)
  2016-08-01 15:30 ` Use verify_oacc_routine_clauses for Fortran (was: Use verify_oacc_routine_clauses for C/C++) Thomas Schwinge
@ 2016-08-09 15:55   ` Cesar Philippidis
  2016-08-10 11:46     ` Thomas Schwinge
  0 siblings, 1 reply; 16+ messages in thread
From: Cesar Philippidis @ 2016-08-09 15:55 UTC (permalink / raw)
  To: Thomas Schwinge, gcc-patches, fortran

On 08/01/2016 08:29 AM, Thomas Schwinge wrote:

> On Mon, 01 Aug 2016 17:21:37 +0200, I wrote:
>> Some checking of OpenACC clauses currently done in the front ends should
>> be moved later, and be unified.  (Also, I suppose, for supporting of the
>> device_type clause, such checking actually *must* be moved later, into
>> the oaccdevlow pass, or similar.)  Here is a first preparatory patch.  OK
>> for trunk?
>>
>> commit e02a9b65c505b404f8d985b0ec6ccb99d73515d3
>> Author: Thomas Schwinge <thomas@codesourcery.com>
>> Date:   Wed Jul 27 15:54:38 2016 +0200
>>
>>     Use verify_oacc_routine_clauses for C/C++
> 
> Here is a Fortran patch.  This depends on other Fortran patches in flight
> (such as Cesar's), and on PR72741 "Fortran OpenACC routine directive
> doesn't properly handle clauses specifying the level of parallelism" be
> resolved, and thereabouts, but I'm posting it anyway, in case anyone has
> any review comments already.  I suppose, to begin with, the call of
> gfc_oacc_routine_dims will move later into the Fortran front end
> pipeline, to the point then function declarations' attributes are set, or
> similar.  Also, as discussed already, the Fortran front end currently is
> very "forgetful" in regards to OpenACC/OpenMP clauses' specific location
> information, so we're not able at present to produce diagnostics with
> precise location information.

Are you planning on staging this patch in gomp4? I know it's a work in
progress, but is this function ...


> diff --git gcc/fortran/trans-openmp.c gcc/fortran/trans-openmp.c
> index 0d646ed..254732c 100644
> --- gcc/fortran/trans-openmp.c
> +++ gcc/fortran/trans-openmp.c
> @@ -4570,3 +4570,70 @@ gfc_trans_omp_declare_simd (gfc_namespace *ns)
>        DECL_ATTRIBUTES (fndecl) =;
>      }
>  }
> +
> +/* Determine and verify the level of parallelism for an OpenACC routine.  */
> +
> +oacc_function
> +gfc_oacc_routine_dims (gfc_omp_clauses *clauses, locus location)
> +{
> +  /* This is implemented in terms of OMP_CLAUSE trees, so that we can use the
> +     generic functions for checking validity.  This has a little bit of
> +     overhead, but as the number of clauses on OpenACC routine directives as
> +     well as the number of OpenACC routine directives will both be rather
> +     small, this is acceptable.  */
> +  tree clauses_t =ULL_TREE;
> +  /* We don't have specific location information available for the individual
> +     clauses...  */
> +  location_t loc =ocation.lb->location;
> +  if (clauses)
> +    {
> +      if (clauses->gang)
> +	{
> +	  tree c =uild_omp_clause (loc, OMP_CLAUSE_GANG);
> +	  OMP_CLAUSE_CHAIN (c) =lauses_t;
> +	  clauses_t =;
> +	}
> +      if (clauses->worker)
> +	{
> +	  tree c =uild_omp_clause (loc, OMP_CLAUSE_WORKER);
> +	  OMP_CLAUSE_CHAIN (c) =lauses_t;
> +	  clauses_t =;
> +	}
> +      if (clauses->vector)
> +	{
> +	  tree c =uild_omp_clause (loc, OMP_CLAUSE_VECTOR);
> +	  OMP_CLAUSE_CHAIN (c) =lauses_t;
> +	  clauses_t =;
> +	}
> +      if (clauses->seq)
> +	{
> +	  tree c =uild_omp_clause (loc, OMP_CLAUSE_SEQ);
> +	  OMP_CLAUSE_CHAIN (c) =lauses_t;
> +	  clauses_t =;
> +	}
> +    }
> +  verify_oacc_routine_clauses (&clauses_t, loc);

included elsewhere in your patch set? I'll take over this patch as I
work on PR72741.

Cesar

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

* Re: Use verify_oacc_routine_clauses for Fortran (was: Use verify_oacc_routine_clauses for C/C++)
  2016-08-09 15:55   ` Cesar Philippidis
@ 2016-08-10 11:46     ` Thomas Schwinge
  0 siblings, 0 replies; 16+ messages in thread
From: Thomas Schwinge @ 2016-08-10 11:46 UTC (permalink / raw)
  To: Cesar Philippidis; +Cc: gcc-patches, fortran

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

Hi Cesar!

On Tue, 9 Aug 2016 08:54:46 -0700, Cesar Philippidis <cesar@codesourcery.com> wrote:
> On 08/01/2016 08:29 AM, Thomas Schwinge wrote:
> 
> > On Mon, 01 Aug 2016 17:21:37 +0200, I wrote:
> >> Some checking of OpenACC clauses currently done in the front ends should
> >> be moved later, and be unified.  (Also, I suppose, for supporting of the
> >> device_type clause, such checking actually *must* be moved later, into
> >> the oaccdevlow pass, or similar.)  Here is a first preparatory patch.  OK
> >> for trunk?
> >>
> >> commit e02a9b65c505b404f8d985b0ec6ccb99d73515d3
> >> Author: Thomas Schwinge <thomas@codesourcery.com>
> >> Date:   Wed Jul 27 15:54:38 2016 +0200
> >>
> >>     Use verify_oacc_routine_clauses for C/C++
> > 
> > Here is a Fortran patch.  This depends on other Fortran patches in flight
> > (such as Cesar's), and on PR72741 "Fortran OpenACC routine directive
> > doesn't properly handle clauses specifying the level of parallelism" be
> > resolved, and thereabouts, but I'm posting it anyway, in case anyone has
> > any review comments already.  I suppose, to begin with, the call of
> > gfc_oacc_routine_dims will move later into the Fortran front end
> > pipeline, to the point then function declarations' attributes are set, or
> > similar.  Also, as discussed already, the Fortran front end currently is
> > very "forgetful" in regards to OpenACC/OpenMP clauses' specific location
> > information, so we're not able at present to produce diagnostics with
> > precise location information.
> 
> Are you planning on staging this patch in gomp4?

As we discussed yesterday, I was planning to wait for the corresponding
Fortran front end changes to materialize, and then handle it all
together, for C, C++, and Fortran.

> I know it's a work in
> progress, but is this function ...

> > --- gcc/fortran/trans-openmp.c
> > +++ gcc/fortran/trans-openmp.c

> > +/* Determine and verify the level of parallelism for an OpenACC routine.  */
> > +
> > +oacc_function
> > +gfc_oacc_routine_dims (gfc_omp_clauses *clauses, locus location)
> > +{
> > +  [...]
> 
> included elsewhere in your patch set? I'll take over this patch as I
> work on PR72741.

I don't know what exactly you mean with "included"?  Also, as I
elaborated in <https://gcc.gnu.org/bugzilla/show_bug.cgi?id=72741#c3>,
actually this function should "ideally just call
gcc/fortran/trans-openmp.c:gfc_trans_omp_clauses to translate the Fortran
OMP clauses representation into OMP_CLAUSE trees" (if that makes sense).
Anyway, feel free to take over this function/patch as required.


Grüße
 Thomas

[-- Attachment #2: signature.asc --]
[-- Type: application/pgp-signature, Size: 472 bytes --]

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

* Re: Use verify_oacc_routine_clauses for C/C++
  2016-08-01 15:21 Use verify_oacc_routine_clauses for C/C++ Thomas Schwinge
  2016-08-01 15:30 ` Use verify_oacc_routine_clauses for Fortran (was: Use verify_oacc_routine_clauses for C/C++) Thomas Schwinge
@ 2016-08-17  1:02 ` Thomas Schwinge
  2018-10-02 14:12 ` [patch,openacc] Use oacc_verify_routine_clauses " Cesar Philippidis
  2 siblings, 0 replies; 16+ messages in thread
From: Thomas Schwinge @ 2016-08-17  1:02 UTC (permalink / raw)
  To: gcc-patches; +Cc: cesar

Hi!

On Mon, 01 Aug 2016 17:21:37 +0200, I wrote:
> Some checking of OpenACC clauses currently done in the front ends should
> be moved later, and be unified.  (Also, I suppose, for supporting of the
> device_type clause, such checking actually *must* be moved later, into
> the oaccdevlow pass, or similar.)  Here is a first preparatory patch.  OK
> for trunk?

In r239520 committed to gomp-4_0-branch:

commit 57c46e92eb368443dbe6c28115f3aeff94fae0dc
Author: tschwinge <tschwinge@138bc75d-0d04-0410-961f-82ee72b054a4>
Date:   Wed Aug 17 00:54:50 2016 +0000

    Use verify_oacc_routine_clauses for C/C++
    
    	gcc/
    	* omp-low.c (build_oacc_routine_dims): Move some of its processing
    	into...
    	(verify_oacc_routine_clauses): ... this new function.
    	* omp-low.h (verify_oacc_routine_clauses): New prototype.
    	gcc/c/
    	* c-parser.c (c_parser_oacc_routine): Normalize order of clauses.
    	(c_finish_oacc_routine): Call verify_oacc_routine_clauses.
    	gcc/cp/
    	* parser.c (cp_parser_oacc_routine)
    	(cp_parser_late_parsing_oacc_routine): Normalize order of clauses.
    	(cp_finalize_oacc_routine): Call verify_oacc_routine_clauses.
    	gcc/testsuite/
    	* c-c++-common/goacc/routine-2.c: Update, and move some test
    	into...
    	* c-c++-common/goacc/routine-level-of-parallelism-1.c: ... this
    	new file.
    
    git-svn-id: svn+ssh://gcc.gnu.org/svn/gcc/branches/gomp-4_0-branch@239520 138bc75d-0d04-0410-961f-82ee72b054a4
---
 gcc/ChangeLog.gomp                                 |   7 +
 gcc/c/ChangeLog.gomp                               |   3 +
 gcc/c/c-parser.c                                   |   8 +
 gcc/cp/ChangeLog.gomp                              |   4 +
 gcc/cp/parser.c                                    |  43 ++--
 gcc/omp-low.c                                      |  67 +++++-
 gcc/omp-low.h                                      |   1 +
 gcc/testsuite/ChangeLog.gomp                       |   7 +
 gcc/testsuite/c-c++-common/goacc/routine-2.c       |  14 +-
 .../goacc/routine-level-of-parallelism-1.c         | 265 +++++++++++++++++++++
 10 files changed, 379 insertions(+), 40 deletions(-)

diff --git gcc/ChangeLog.gomp gcc/ChangeLog.gomp
index f9564d6..bcd011d 100644
--- gcc/ChangeLog.gomp
+++ gcc/ChangeLog.gomp
@@ -1,3 +1,10 @@
+2016-08-17  Thomas Schwinge  <thomas@codesourcery.com>
+
+	* omp-low.c (build_oacc_routine_dims): Move some of its processing
+	into...
+	(verify_oacc_routine_clauses): ... this new function.
+	* omp-low.h (verify_oacc_routine_clauses): New prototype.
+
 2016-08-15  Chung-Lin Tang  <cltang@codesourcery.com>
 
 	* omp-low.c (lower_oacc_reductions): Adjust variable lookup to use
diff --git gcc/c/ChangeLog.gomp gcc/c/ChangeLog.gomp
index d9725b0..37f5602 100644
--- gcc/c/ChangeLog.gomp
+++ gcc/c/ChangeLog.gomp
@@ -1,5 +1,8 @@
 2016-08-17  Thomas Schwinge  <thomas@codesourcery.com>
 
+	* c-parser.c (c_parser_oacc_routine): Normalize order of clauses.
+	(c_finish_oacc_routine): Call verify_oacc_routine_clauses.
+
 	* c-parser.c (c_parser_oacc_shape_clause)
 	(c_parser_oacc_simple_clause): Add loc formal parameter.  Adjust
 	all users.
diff --git gcc/c/c-parser.c gcc/c/c-parser.c
index 2e38807..3d50676 100644
--- gcc/c/c-parser.c
+++ gcc/c/c-parser.c
@@ -14205,6 +14205,9 @@ c_parser_oacc_routine (c_parser *parser, enum pragma_context context)
       data.clauses
 	= c_parser_oacc_all_clauses (parser, OACC_ROUTINE_CLAUSE_MASK,
 				     "#pragma acc routine");
+      /* The clauses are in reverse order; fix that to make later diagnostic
+	 emission easier.  */
+      data.clauses = nreverse (data.clauses);
 
       if (TREE_CODE (decl) != FUNCTION_DECL)
 	{
@@ -14220,6 +14223,9 @@ c_parser_oacc_routine (c_parser *parser, enum pragma_context context)
 	= c_parser_oacc_all_clauses (parser, OACC_ROUTINE_CLAUSE_MASK,
 				     "#pragma acc routine",
 				     OACC_ROUTINE_CLAUSE_DEVICE_TYPE_MASK);
+      /* The clauses are in reverse order; fix that to make later diagnostic
+	 emission easier.  */
+      data.clauses = nreverse (data.clauses);
 
       /* Emit a helpful diagnostic if there's another pragma following this
 	 one.  Also don't allow a static assertion declaration, as in the
@@ -14283,6 +14289,8 @@ c_finish_oacc_routine (struct oacc_routine_data *data, tree fndecl,
       return;
     }
 
+  verify_oacc_routine_clauses (&data->clauses, data->loc);
+
   if (get_oacc_fn_attrib (fndecl))
     {
       error_at (data->loc,
diff --git gcc/cp/ChangeLog.gomp gcc/cp/ChangeLog.gomp
index feb186a..7f634d9 100644
--- gcc/cp/ChangeLog.gomp
+++ gcc/cp/ChangeLog.gomp
@@ -1,5 +1,9 @@
 2016-08-17  Thomas Schwinge  <thomas@codesourcery.com>
 
+	* parser.c (cp_parser_oacc_routine)
+	(cp_parser_late_parsing_oacc_routine): Normalize order of clauses.
+	(cp_finalize_oacc_routine): Call verify_oacc_routine_clauses.
+
 	* parser.c (cp_parser_oacc_shape_clause): Add loc formal
 	parameter.  Adjust all users.
 
diff --git gcc/cp/parser.c gcc/cp/parser.c
index d3d673b..8558a6f 100644
--- gcc/cp/parser.c
+++ gcc/cp/parser.c
@@ -36680,6 +36680,9 @@ cp_parser_oacc_routine (cp_parser *parser, cp_token *pragma_tok,
 	= cp_parser_oacc_all_clauses (parser, OACC_ROUTINE_CLAUSE_MASK,
 				      "#pragma acc routine",
 				      cp_lexer_peek_token (parser->lexer));
+      /* The clauses are in reverse order; fix that to make later diagnostic
+	 emission easier.  */
+      data.clauses = nreverse (data.clauses);
 
       if (decl && is_overloaded_fn (decl)
 	  && (TREE_CODE (decl) != FUNCTION_DECL
@@ -36767,6 +36770,9 @@ cp_parser_late_parsing_oacc_routine (cp_parser *parser, tree attrs)
     = cp_parser_oacc_all_clauses (parser, OACC_ROUTINE_CLAUSE_MASK,
 				  "#pragma acc routine", pragma_tok,
 				   OACC_ROUTINE_CLAUSE_DEVICE_TYPE_MASK);
+  /* The clauses are in reverse order; fix that to make later diagnostic
+     emission easier.  */
+  parser->oacc_routine->clauses = nreverse (parser->oacc_routine->clauses);
   cp_parser_pop_lexer (parser);
   /* Later, cp_finalize_oacc_routine will process the clauses, and then set
      fndecl_seen.  */
@@ -36801,23 +36807,6 @@ cp_finalize_oacc_routine (cp_parser *parser, tree fndecl, bool is_defn)
 	  return;
 	}
 
-      if (get_oacc_fn_attrib (fndecl))
-	{
-	  error_at (parser->oacc_routine->loc,
-		    "%<#pragma acc routine%> already applied to %qD", fndecl);
-	  parser->oacc_routine = NULL;
-	  return;
-	}
-
-      if (TREE_USED (fndecl) || (!is_defn && DECL_SAVED_TREE (fndecl)))
-	{
-	  error_at (parser->oacc_routine->loc,
-		    "%<#pragma acc routine%> must be applied before %s",
-		    TREE_USED (fndecl) ? "use" : "definition");
-	  parser->oacc_routine = NULL;
-	  return;
-	}
-
       /* Process the bind clause, if present.  */
       for (tree c = parser->oacc_routine->clauses; c; c = OMP_CLAUSE_CHAIN (c))
 	{
@@ -36847,6 +36836,26 @@ cp_finalize_oacc_routine (cp_parser *parser, tree fndecl, bool is_defn)
 	  break;
 	}
 
+      verify_oacc_routine_clauses (&parser->oacc_routine->clauses,
+				   parser->oacc_routine->loc);
+
+      if (get_oacc_fn_attrib (fndecl))
+	{
+	  error_at (parser->oacc_routine->loc,
+		    "%<#pragma acc routine%> already applied to %qD", fndecl);
+	  parser->oacc_routine = NULL;
+	  return;
+	}
+
+      if (TREE_USED (fndecl) || (!is_defn && DECL_SAVED_TREE (fndecl)))
+	{
+	  error_at (parser->oacc_routine->loc,
+		    "%<#pragma acc routine%> must be applied before %s",
+		    TREE_USED (fndecl) ? "use" : "definition");
+	  parser->oacc_routine = NULL;
+	  return;
+	}
+
       /* Process the routine's dimension clauses.  */
       tree dims = build_oacc_routine_dims (parser->oacc_routine->clauses);
       replace_oacc_fn_attrib (fndecl, dims);
diff --git gcc/omp-low.c gcc/omp-low.c
index 2e725d6..6604897 100644
--- gcc/omp-low.c
+++ gcc/omp-low.c
@@ -12699,9 +12699,62 @@ set_oacc_fn_attrib (tree fn, tree clauses, bool is_kernel, vec<tree> *args)
     }
 }
 
-/*  Process the routine's dimension clauess to generate an attribute
-    value.  Issue diagnostics as appropriate.  We default to SEQ
-    (OpenACC 2.5 clarifies this). All dimensions have a size of zero
+/* Verify OpenACC routine clauses.
+
+   The chain of clauses returned will contain exactly one clause specifying the
+   level of parallelism.  */
+
+void
+verify_oacc_routine_clauses (tree *clauses, location_t loc)
+{
+  tree c_level = NULL_TREE;
+  tree c_p = NULL_TREE;
+  for (tree c = *clauses; c; c_p = c, c = OMP_CLAUSE_CHAIN (c))
+    switch (OMP_CLAUSE_CODE (c))
+      {
+      case OMP_CLAUSE_GANG:
+      case OMP_CLAUSE_WORKER:
+      case OMP_CLAUSE_VECTOR:
+      case OMP_CLAUSE_SEQ:
+	if (c_level == NULL_TREE)
+	  c_level = c;
+	else if (OMP_CLAUSE_CODE (c) == OMP_CLAUSE_CODE (c_level))
+	  {
+	    /* This has already been diagnosed in the front ends.  */
+	    /* Drop the duplicate clause.  */
+	    gcc_checking_assert (c_p != NULL_TREE);
+	    OMP_CLAUSE_CHAIN (c_p) = OMP_CLAUSE_CHAIN (c);
+	    c = c_p;
+	  }
+	else
+	  {
+	    error_at (OMP_CLAUSE_LOCATION (c),
+		      "%qs specifies a conflicting level of parallelism",
+		      omp_clause_code_name[OMP_CLAUSE_CODE (c)]);
+	    inform (OMP_CLAUSE_LOCATION (c_level),
+		    "... to the previous %qs clause here",
+		    omp_clause_code_name[OMP_CLAUSE_CODE (c_level)]);
+	    /* Drop the conflicting clause.  */
+	    gcc_checking_assert (c_p != NULL_TREE);
+	    OMP_CLAUSE_CHAIN (c_p) = OMP_CLAUSE_CHAIN (c);
+	    c = c_p;
+	  }
+	break;
+      default:
+	gcc_unreachable ();
+      }
+  if (c_level == NULL_TREE)
+    {
+      /* OpenACC 2.5 makes this an error; for the current OpenACC 2.0a
+	 implementation add an implicit "seq" clause.  */
+      c_level = build_omp_clause (loc, OMP_CLAUSE_SEQ);
+      OMP_CLAUSE_CHAIN (c_level) = *clauses;
+      *clauses = c_level;
+    }
+}
+
+/*  Process the OpenACC routine's clauses to generate an attribute
+    for the level of parallelism.  All dimensions have a size of zero
     (dynamic).  TREE_PURPOSE is set to indicate whether that dimension
     can have a loop partitioned on it.  non-zero indicates
     yes, zero indicates no.  By construction once a non-zero has been
@@ -12723,17 +12776,11 @@ build_oacc_routine_dims (tree clauses)
     for (ix = GOMP_DIM_MAX + 1; ix--;)
       if (OMP_CLAUSE_CODE (clauses) == ids[ix])
 	{
-	  if (level >= 0)
-	    error_at (OMP_CLAUSE_LOCATION (clauses),
-		      "multiple loop axes specified for routine");
 	  level = ix;
 	  break;
 	}
+  gcc_checking_assert (level >= 0);
 
-  /* Default to SEQ.  */
-  if (level < 0)
-    level = GOMP_DIM_MAX;
-  
   tree dims = NULL_TREE;
 
   for (ix = GOMP_DIM_MAX; ix--;)
diff --git gcc/omp-low.h gcc/omp-low.h
index b1f7885..c7b7dcb 100644
--- gcc/omp-low.h
+++ gcc/omp-low.h
@@ -31,6 +31,7 @@ extern bool make_gimple_omp_edges (basic_block, struct omp_region **, int *);
 extern void omp_finish_file (void);
 extern tree omp_member_access_dummy_var (tree);
 extern void replace_oacc_fn_attrib (tree, tree);
+extern void verify_oacc_routine_clauses (tree *, location_t);
 extern tree build_oacc_routine_dims (tree);
 extern tree get_oacc_fn_attrib (tree);
 extern void set_oacc_fn_attrib (tree, tree, bool, vec<tree> *);
diff --git gcc/testsuite/ChangeLog.gomp gcc/testsuite/ChangeLog.gomp
index efaa360..0870821 100644
--- gcc/testsuite/ChangeLog.gomp
+++ gcc/testsuite/ChangeLog.gomp
@@ -1,3 +1,10 @@
+2016-08-17  Thomas Schwinge  <thomas@codesourcery.com>
+
+	* c-c++-common/goacc/routine-2.c: Update, and move some test
+	into...
+	* c-c++-common/goacc/routine-level-of-parallelism-1.c: ... this
+	new file.
+
 2016-08-16  Cesar Philippidis  <cesar@codesourcery.com>
 	    Thomas Schwinge  <thomas@codesourcery.com>
 
diff --git gcc/testsuite/c-c++-common/goacc/routine-2.c gcc/testsuite/c-c++-common/goacc/routine-2.c
index 35857ea..7582c86 100644
--- gcc/testsuite/c-c++-common/goacc/routine-2.c
+++ gcc/testsuite/c-c++-common/goacc/routine-2.c
@@ -1,16 +1,4 @@
-/* Test invalid use of clauses with routine.  */
-
-#pragma acc routine gang worker /* { dg-error "multiple loop axes" } */
-extern void gang (void);
-
-#pragma acc routine worker vector /* { dg-error "multiple loop axes" } */
-extern void worker (void);
-
-#pragma acc routine vector seq /* { dg-error "multiple loop axes" } */
-extern void vector (void);
-
-#pragma acc routine seq gang /* { dg-error "multiple loop axes" } */
-extern void seq (void);
+/* Test invalid use of clauses with OpenACC routine.  */
 
 extern float F;
 #pragma acc routine bind (F) /* { dg-error ".F. does not refer to a function" } */
diff --git gcc/testsuite/c-c++-common/goacc/routine-level-of-parallelism-1.c gcc/testsuite/c-c++-common/goacc/routine-level-of-parallelism-1.c
new file mode 100644
index 0000000..fc8bc3c
--- /dev/null
+++ gcc/testsuite/c-c++-common/goacc/routine-level-of-parallelism-1.c
@@ -0,0 +1,265 @@
+/* Test various aspects of clauses specifying incompatible levels of
+   parallelism with the OpenACC routine directive.  The Fortran counterpart is
+   ../../gfortran.dg/goacc/routine-level-of-parallelism-1.f.  */
+
+extern void g_1 (void);
+#pragma acc routine (g_1) gang gang /* { dg-error "too many 'gang' clauses" } */
+
+#pragma acc routine worker worker /* { dg-error "too many 'worker' clauses" } */
+void w_1 (void)
+{
+}
+
+#pragma acc routine vector vector /* { dg-error "too many 'vector' clauses" } */
+void v_1 (void)
+{
+}
+
+#pragma acc routine seq seq /* { dg-error "too many 'seq' clauses" } */
+extern void s_1 (void);
+
+
+#pragma acc routine gang gang gang /* { dg-error "too many 'gang' clauses" } */
+void g_2 (void)
+{
+}
+
+#pragma acc routine worker worker worker /* { dg-error "too many 'worker' clauses" } */
+extern void w_2 (void);
+
+extern void v_2 (void);
+#pragma acc routine (v_2) vector vector vector /* { dg-error "too many 'vector' clauses" } */
+
+#pragma acc routine seq seq seq /* { dg-error "too many 'seq' clauses" } */
+void s_2 (void)
+{
+}
+
+
+#pragma acc routine \
+  gang \
+  worker /* { dg-error ".worker. specifies a conflicting level of parallelism" } */
+void g_3 (void)
+{
+}
+#pragma acc routine (g_3) /* { dg-error ".#pragma acc routine. already applied to .\[void \]*g_3." } */ \
+  gang \
+  seq /* { dg-error ".seq. specifies a conflicting level of parallelism" } */
+#pragma acc routine (g_3) /* { dg-error ".#pragma acc routine. already applied to .\[void \]*g_3." } */ \
+  gang \
+  vector /* { dg-error ".vector. specifies a conflicting level of parallelism" } */
+
+extern void w_3 (void);
+#pragma acc routine (w_3) \
+  worker \
+  vector /* { dg-error ".vector. specifies a conflicting level of parallelism" } */
+#pragma acc routine (w_3) /* { dg-error ".#pragma acc routine. already applied to .\[void \]*w_3." } */ \
+  worker \
+  gang /* { dg-error ".gang. specifies a conflicting level of parallelism" } */
+#pragma acc routine (w_3) /* { dg-error ".#pragma acc routine. already applied to .\[void \]*w_3." } */ \
+  worker \
+  seq /* { dg-error ".seq. specifies a conflicting level of parallelism" } */
+
+#pragma acc routine \
+  vector \
+  seq /* { dg-error ".seq. specifies a conflicting level of parallelism" } */
+void v_3 (void)
+{
+}
+#pragma acc routine (v_3) /* { dg-error ".#pragma acc routine. already applied to .\[void \]*v_3." } */ \
+  vector \
+  worker /* { dg-error ".worker. specifies a conflicting level of parallelism" } */
+#pragma acc routine (v_3) /* { dg-error ".#pragma acc routine. already applied to .\[void \]*v_3." } */ \
+  vector \
+  gang /* { dg-error ".gang. specifies a conflicting level of parallelism" } */
+
+extern void s_3 (void);
+#pragma acc routine (s_3) \
+  seq \
+  gang /* { dg-error ".gang. specifies a conflicting level of parallelism" } */
+#pragma acc routine (s_3) /* { dg-error ".#pragma acc routine. already applied to .\[void \]*s_3." } */ \
+  seq \
+  vector /* { dg-error ".vector. specifies a conflicting level of parallelism" } */
+#pragma acc routine (s_3) /* { dg-error ".#pragma acc routine. already applied to .\[void \]*s_3." } */ \
+  seq \
+  worker /* { dg-error ".worker. specifies a conflicting level of parallelism" } */
+
+
+#pragma acc routine \
+  gang gang gang /* { dg-error "too many 'gang' clauses" } */ \
+  worker /* { dg-error ".worker. specifies a conflicting level of parallelism" } */ \
+  vector /* { dg-error ".vector. specifies a conflicting level of parallelism" } */ \
+  seq /* { dg-error ".seq. specifies a conflicting level of parallelism" } */
+extern void g_4 (void);
+#pragma acc routine (g_4) /* { dg-error ".#pragma acc routine. already applied to .\[void \]*g_4." } */ \
+  gang gang gang /* { dg-error "too many 'gang' clauses" } */ \
+  vector /* { dg-error ".vector. specifies a conflicting level of parallelism" } */ \
+  worker /* { dg-error ".worker. specifies a conflicting level of parallelism" } */ \
+  seq /* { dg-error ".seq. specifies a conflicting level of parallelism" } */
+#pragma acc routine (g_4) /* { dg-error ".#pragma acc routine. already applied to .\[void \]*g_4." } */ \
+  gang gang gang /* { dg-error "too many 'gang' clauses" } */ \
+  seq /* { dg-error ".seq. specifies a conflicting level of parallelism" } */ \
+  vector /* { dg-error ".vector. specifies a conflicting level of parallelism" } */ \
+  worker /* { dg-error ".worker. specifies a conflicting level of parallelism" } */
+
+extern void w_4 (void);
+#pragma acc routine (w_4) \
+  worker worker worker /* { dg-error "too many 'worker' clauses" } */ \
+  seq /* { dg-error ".seq. specifies a conflicting level of parallelism" } */ \
+  vector /* { dg-error ".vector. specifies a conflicting level of parallelism" } */ \
+  gang /* { dg-error ".gang. specifies a conflicting level of parallelism" } */
+#pragma acc routine (w_4) /* { dg-error ".#pragma acc routine. already applied to .\[void \]*w_4." } */ \
+  worker worker worker /* { dg-error "too many 'worker' clauses" } */ \
+  vector /* { dg-error ".vector. specifies a conflicting level of parallelism" } */ \
+  seq /* { dg-error ".seq. specifies a conflicting level of parallelism" } */ \
+  gang /* { dg-error ".gang. specifies a conflicting level of parallelism" } */
+#pragma acc routine (w_4) /* { dg-error ".#pragma acc routine. already applied to .\[void \]*w_4." } */ \
+  worker worker worker /* { dg-error "too many 'worker' clauses" } */ \
+  seq /* { dg-error ".seq. specifies a conflicting level of parallelism" } */ \
+  gang /* { dg-error ".gang. specifies a conflicting level of parallelism" } */ \
+  vector /* { dg-error ".vector. specifies a conflicting level of parallelism" } */
+
+#pragma acc routine \
+  vector vector vector /* { dg-error "too many 'vector' clauses" } */ \
+  worker /* { dg-error ".worker. specifies a conflicting level of parallelism" } */ \
+  seq /* { dg-error ".seq. specifies a conflicting level of parallelism" } */ \
+  gang /* { dg-error ".gang. specifies a conflicting level of parallelism" } */
+void v_4 (void)
+{
+}
+#pragma acc routine (v_4) /* { dg-error ".#pragma acc routine. already applied to .\[void \]*v_4." } */ \
+  vector vector vector /* { dg-error "too many 'vector' clauses" } */ \
+  seq /* { dg-error ".seq. specifies a conflicting level of parallelism" } */ \
+  worker /* { dg-error ".worker. specifies a conflicting level of parallelism" } */ \
+  gang /* { dg-error ".gang. specifies a conflicting level of parallelism" } */
+#pragma acc routine (v_4) /* { dg-error ".#pragma acc routine. already applied to .\[void \]*v_4." } */ \
+  vector vector vector /* { dg-error "too many 'vector' clauses" } */ \
+  gang /* { dg-error ".gang. specifies a conflicting level of parallelism" } */ \
+  seq /* { dg-error ".seq. specifies a conflicting level of parallelism" } */ \
+  worker /* { dg-error ".worker. specifies a conflicting level of parallelism" } */
+
+#pragma acc routine \
+  seq seq seq /* { dg-error "too many 'seq' clauses" } */ \
+  vector /* { dg-error ".vector. specifies a conflicting level of parallelism" } */ \
+  worker /* { dg-error ".worker. specifies a conflicting level of parallelism" } */ \
+  gang /* { dg-error ".gang. specifies a conflicting level of parallelism" } */
+void s_4 (void)
+{
+}
+#pragma acc routine (s_4) /* { dg-error ".#pragma acc routine. already applied to .\[void \]*s_4." } */ \
+  seq seq seq /* { dg-error "too many 'seq' clauses" } */ \
+  vector /* { dg-error ".vector. specifies a conflicting level of parallelism" } */ \
+  gang /* { dg-error ".gang. specifies a conflicting level of parallelism" } */ \
+  worker /* { dg-error ".worker. specifies a conflicting level of parallelism" } */
+#pragma acc routine (s_4) /* { dg-error ".#pragma acc routine. already applied to .\[void \]*s_4." } */ \
+  seq seq seq /* { dg-error "too many 'seq' clauses" } */ \
+  worker /* { dg-error ".worker. specifies a conflicting level of parallelism" } */ \
+  vector /* { dg-error ".vector. specifies a conflicting level of parallelism" } */ \
+  gang /* { dg-error ".gang. specifies a conflicting level of parallelism" } */
+
+
+#pragma acc routine \
+  gang gang gang /* { dg-error "too many 'gang' clauses" } */ \
+  worker worker /* { dg-error "too many 'worker' clauses" } */ \
+  /* { dg-error ".worker. specifies a conflicting level of parallelism" "" { target *-*-* } 163 } */ \
+  vector vector vector /* { dg-error "too many 'vector' clauses" } */ \
+  /* { dg-error ".vector. specifies a conflicting level of parallelism" "" { target *-*-* } 165 } */ \
+  seq seq seq seq seq /* { dg-error "too many 'seq' clauses" } */ \
+  /* { dg-error ".seq. specifies a conflicting level of parallelism" "" { target *-*-* } 167 } */
+void g_5 (void)
+{
+}
+#pragma acc routine (g_5) /* { dg-error ".#pragma acc routine. already applied to .\[void \]*g_5." } */ \
+  gang gang gang /* { dg-error "too many 'gang' clauses" } */ \
+  vector vector vector vector /* { dg-error "too many 'vector' clauses" } */ \
+  /* { dg-error ".vector. specifies a conflicting level of parallelism" "" { target *-*-* } 174 } */ \
+  worker worker worker /* { dg-error "too many 'worker' clauses" } */ \
+  /* { dg-error ".worker. specifies a conflicting level of parallelism" "" { target *-*-* } 176 } */ \
+  seq seq seq seq seq seq /* { dg-error "too many 'seq' clauses" } */ \
+  /* { dg-error ".seq. specifies a conflicting level of parallelism" "" { target *-*-* } 178 } */
+#pragma acc routine (g_5) /* { dg-error ".#pragma acc routine. already applied to .\[void \]*g_5." } */ \
+  gang gang gang /* { dg-error "too many 'gang' clauses" } */ \
+  seq seq seq seq seq /* { dg-error "too many 'seq' clauses" } */ \
+  /* { dg-error ".seq. specifies a conflicting level of parallelism" "" { target *-*-* } 182 } */ \
+  vector vector vector vector /* { dg-error "too many 'vector' clauses" } */ \
+  /* { dg-error ".vector. specifies a conflicting level of parallelism" "" { target *-*-* } 184 } */ \
+  worker worker worker worker /* { dg-error "too many 'worker' clauses" } */ \
+  /* { dg-error ".worker. specifies a conflicting level of parallelism" "" { target *-*-* } 186 } */
+
+#pragma acc routine \
+  worker worker worker /* { dg-error "too many 'worker' clauses" } */ \
+  seq seq seq seq /* { dg-error "too many 'seq' clauses" } */ \
+  /* { dg-error ".seq. specifies a conflicting level of parallelism" "" { target *-*-* } 191 } */ \
+  vector vector vector vector vector /* { dg-error "too many 'vector' clauses" } */ \
+  /* { dg-error ".vector. specifies a conflicting level of parallelism" "" { target *-*-* } 193 } */ \
+  gang gang /* { dg-error "too many 'gang' clauses" } */ \
+  /* { dg-error ".gang. specifies a conflicting level of parallelism" "" { target *-*-* } 195 } */
+extern void w_5 (void);
+#pragma acc routine (w_5) /* { dg-error ".#pragma acc routine. already applied to .\[void \]*w_5." } */ \
+  worker worker worker /* { dg-error "too many 'worker' clauses" } */ \
+  vector vector vector /* { dg-error "too many 'vector' clauses" } */ \
+  /* { dg-error ".vector. specifies a conflicting level of parallelism" "" { target *-*-* } 200 } */ \
+  seq seq seq /* { dg-error "too many 'seq' clauses" } */ \
+  /* { dg-error ".seq. specifies a conflicting level of parallelism" "" { target *-*-* } 202 } */ \
+  gang gang /* { dg-error "too many 'gang' clauses" } */ \
+  /* { dg-error ".gang. specifies a conflicting level of parallelism" "" { target *-*-* } 204 } */
+#pragma acc routine (w_5) /* { dg-error ".#pragma acc routine. already applied to .\[void \]*w_5." } */ \
+  worker worker worker /* { dg-error "too many 'worker' clauses" } */ \
+  seq seq /* { dg-error "too many 'seq' clauses" } */ \
+  /* { dg-error ".seq. specifies a conflicting level of parallelism" "" { target *-*-* } 208 } */ \
+  gang gang /* { dg-error "too many 'gang' clauses" } */ \
+  /* { dg-error ".gang. specifies a conflicting level of parallelism" "" { target *-*-* } 210 } */ \
+  vector vector vector vector /* { dg-error "too many 'vector' clauses" } */ \
+  /* { dg-error ".vector. specifies a conflicting level of parallelism" "" { target *-*-* } 212 } */
+
+#pragma acc routine \
+  vector vector vector /* { dg-error "too many 'vector' clauses" } */ \
+  worker worker worker /* { dg-error "too many 'worker' clauses" } */ \
+  /* { dg-error ".worker. specifies a conflicting level of parallelism" "" { target *-*-* } 217 } */ \
+  seq seq seq seq seq seq /* { dg-error "too many 'seq' clauses" } */ \
+  /* { dg-error ".seq. specifies a conflicting level of parallelism" "" { target *-*-* } 219 } */ \
+  gang gang /* { dg-error "too many 'gang' clauses" } */ \
+  /* { dg-error ".gang. specifies a conflicting level of parallelism" "" { target *-*-* } 221 } */
+extern void v_5 (void);
+#pragma acc routine (v_5) /* { dg-error ".#pragma acc routine. already applied to .\[void \]*v_5." } */ \
+  vector vector vector /* { dg-error "too many 'vector' clauses" } */ \
+  seq seq seq seq seq seq /* { dg-error "too many 'seq' clauses" } */ \
+  /* { dg-error ".seq. specifies a conflicting level of parallelism" "" { target *-*-* } 226 } */ \
+  worker worker /* { dg-error "too many 'worker' clauses" } */ \
+  /* { dg-error ".worker. specifies a conflicting level of parallelism" "" { target *-*-* } 228 } */ \
+  gang gang /* { dg-error "too many 'gang' clauses" } */ \
+  /* { dg-error ".gang. specifies a conflicting level of parallelism" "" { target *-*-* } 230 } */
+#pragma acc routine (v_5) /* { dg-error ".#pragma acc routine. already applied to .\[void \]*v_5." } */ \
+  vector vector vector /* { dg-error "too many 'vector' clauses" } */ \
+  gang gang /* { dg-error "too many 'gang' clauses" } */ \
+  /* { dg-error ".gang. specifies a conflicting level of parallelism" "" { target *-*-* } 234 } */ \
+  seq seq seq seq seq /* { dg-error "too many 'seq' clauses" } */ \
+  /* { dg-error ".seq. specifies a conflicting level of parallelism" "" { target *-*-* } 236 } */ \
+  worker worker /* { dg-error "too many 'worker' clauses" } */ \
+  /* { dg-error ".worker. specifies a conflicting level of parallelism" "" { target *-*-* } 238 } */
+
+extern void s_5 (void);
+#pragma acc routine (s_5) \
+  seq seq seq /* { dg-error "too many 'seq' clauses" } */ \
+  vector vector vector vector /* { dg-error "too many 'vector' clauses" } */ \
+  /* { dg-error ".vector. specifies a conflicting level of parallelism" "" { target *-*-* } 244 } */ \
+  worker worker worker worker worker /* { dg-error "too many 'worker' clauses" } */ \
+  /* { dg-error ".worker. specifies a conflicting level of parallelism" "" { target *-*-* } 246 } */ \
+  gang gang /* { dg-error "too many 'gang' clauses" } */ \
+  /* { dg-error ".gang. specifies a conflicting level of parallelism" "" { target *-*-* } 248 } */
+#pragma acc routine (s_5) /* { dg-error ".#pragma acc routine. already applied to .\[void \]*s_5." } */ \
+  seq seq seq /* { dg-error "too many 'seq' clauses" } */ \
+  vector vector vector vector /* { dg-error "too many 'vector' clauses" } */ \
+  /* { dg-error ".vector. specifies a conflicting level of parallelism" "" { target *-*-* } 252 } */ \
+  gang gang /* { dg-error "too many 'gang' clauses" } */ \
+  /* { dg-error ".gang. specifies a conflicting level of parallelism" "" { target *-*-* } 254 } */ \
+  worker worker worker /* { dg-error "too many 'worker' clauses" } */ \
+  /* { dg-error ".worker. specifies a conflicting level of parallelism" "" { target *-*-* } 256 } */
+#pragma acc routine (s_5) /* { dg-error ".#pragma acc routine. already applied to .\[void \]*s_5." } */ \
+  seq seq seq /* { dg-error "too many 'seq' clauses" } */ \
+  worker worker /* { dg-error "too many 'worker' clauses" } */ \
+  /* { dg-error ".worker. specifies a conflicting level of parallelism" "" { target *-*-* } 260 } */ \
+  vector vector vector vector /* { dg-error "too many 'vector' clauses" } */ \
+  /* { dg-error ".vector. specifies a conflicting level of parallelism" "" { target *-*-* } 262 } */ \
+  gang gang /* { dg-error "too many 'gang' clauses" } */ \
+  /* { dg-error ".gang. specifies a conflicting level of parallelism" "" { target *-*-* } 264 } */


Grüße
 Thomas

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

* Re: Repeated use of the OpenACC routine directive
  2016-08-01 15:51   ` Repeated use of the OpenACC routine directive Thomas Schwinge
@ 2016-08-17  1:05     ` Thomas Schwinge
  2016-08-18 22:08       ` Cesar Philippidis
  2016-08-19 14:39       ` Cesar Philippidis
  2018-10-02 14:57     ` [patch,openacc] " Cesar Philippidis
  1 sibling, 2 replies; 16+ messages in thread
From: Thomas Schwinge @ 2016-08-17  1:05 UTC (permalink / raw)
  To: gcc-patches; +Cc: cesar

Hi!

On Mon, 01 Aug 2016 17:51:24 +0200, I wrote:
> We found that it's not correct that we currently unconditionally diagnose
> an error for repeated use of the OpenACC routine directive on one
> function/declaration.  (For reference, it is also permissible for an
> "ordinary" function to have several declarations plus a definition, as
> long as these are compatible.)  This is, the following shall be valid:
> 
>     #pragma acc routine worker
>     void f(void)
>     {
>     }
>     #pragma acc routine (f) worker
>     #pragma acc routine worker
>     extern void f(void);
> 
> [...]

In r239521 committed to gomp-4_0-branch:

commit bffb0ee6c0a83b8c85cd919e1172086b51fdc452
Author: tschwinge <tschwinge@138bc75d-0d04-0410-961f-82ee72b054a4>
Date:   Wed Aug 17 00:55:02 2016 +0000

    Repeated use of the C/C++ OpenACC routine directive
    
    	gcc/
    	* omp-low.c (verify_oacc_routine_clauses): Change formal
    	parameters.  Add checking if already marked as an accelerator
    	routine.  Adjust all users.
    	gcc/c/
    	* c-parser.c (c_finish_oacc_routine): Rework checking if already
    	marked as an accelerator routine.
    	gcc/cp/
    	* parser.c (cp_finalize_oacc_routine): Rework checking if already
    	marked as an accelerator routine.
    	gcc/testsuite/
    	* c-c++-common/goacc/oaccdevlow-routine.c: Update.
    	* c-c++-common/goacc/routine-5.c: Likewise.
    	* c-c++-common/goacc/routine-level-of-parallelism-1.c: Likewise.
    	* c-c++-common/goacc/routine-level-of-parallelism-2.c: New file.
    
    	gcc/testsuite/
    	* c-c++-common/goacc/routine-1.c: Update.
    	* c-c++-common/goacc/routine-2.c: Likewise.
    	* c-c++-common/goacc/routine-nohost-1.c: Likewise.
    	* g++.dg/goacc/routine-2.C: Likewise.
    	* c-c++-common/goacc/routine-nohost-2.c: New file.
    
    git-svn-id: svn+ssh://gcc.gnu.org/svn/gcc/branches/gomp-4_0-branch@239521 138bc75d-0d04-0410-961f-82ee72b054a4
---
 gcc/ChangeLog.gomp                                 |   4 +
 gcc/c/ChangeLog.gomp                               |   3 +
 gcc/c/c-parser.c                                   |  42 ++--
 gcc/cp/ChangeLog.gomp                              |   3 +
 gcc/cp/parser.c                                    |  51 +++--
 gcc/omp-low.c                                      | 146 ++++++++++++-
 gcc/omp-low.h                                      |   2 +-
 gcc/testsuite/ChangeLog.gomp                       |  11 +
 gcc/testsuite/c-c++-common/goacc/routine-1.c       |  51 ++++-
 gcc/testsuite/c-c++-common/goacc/routine-2.c       | 143 +++++++++++++
 gcc/testsuite/c-c++-common/goacc/routine-5.c       |  46 +---
 .../goacc/routine-level-of-parallelism-1.c         | 233 ++++++++++++++++++---
 .../goacc/routine-level-of-parallelism-2.c         |  73 +++++++
 .../c-c++-common/goacc/routine-nohost-1.c          |  20 ++
 .../c-c++-common/goacc/routine-nohost-2.c          |  97 +++++++++
 gcc/testsuite/g++.dg/goacc/routine-2.C             |   9 +
 16 files changed, 816 insertions(+), 118 deletions(-)

diff --git gcc/ChangeLog.gomp gcc/ChangeLog.gomp
index bcd011d..f2e8f0d 100644
--- gcc/ChangeLog.gomp
+++ gcc/ChangeLog.gomp
@@ -1,5 +1,9 @@
 2016-08-17  Thomas Schwinge  <thomas@codesourcery.com>
 
+	* omp-low.c (verify_oacc_routine_clauses): Change formal
+	parameters.  Add checking if already marked as an accelerator
+	routine.  Adjust all users.
+
 	* omp-low.c (build_oacc_routine_dims): Move some of its processing
 	into...
 	(verify_oacc_routine_clauses): ... this new function.
diff --git gcc/c/ChangeLog.gomp gcc/c/ChangeLog.gomp
index 37f5602..57b8dc9 100644
--- gcc/c/ChangeLog.gomp
+++ gcc/c/ChangeLog.gomp
@@ -1,5 +1,8 @@
 2016-08-17  Thomas Schwinge  <thomas@codesourcery.com>
 
+	* c-parser.c (c_finish_oacc_routine): Rework checking if already
+	marked as an accelerator routine.
+
 	* c-parser.c (c_parser_oacc_routine): Normalize order of clauses.
 	(c_finish_oacc_routine): Call verify_oacc_routine_clauses.
 
diff --git gcc/c/c-parser.c gcc/c/c-parser.c
index 3d50676..824ee14 100644
--- gcc/c/c-parser.c
+++ gcc/c/c-parser.c
@@ -14289,33 +14289,37 @@ c_finish_oacc_routine (struct oacc_routine_data *data, tree fndecl,
       return;
     }
 
-  verify_oacc_routine_clauses (&data->clauses, data->loc);
-
-  if (get_oacc_fn_attrib (fndecl))
+  int compatible
+    = verify_oacc_routine_clauses (fndecl, &data->clauses, data->loc,
+				   "#pragma acc routine");
+  if (compatible < 0)
     {
-      error_at (data->loc,
-		"%<#pragma acc routine%> already applied to %qD", fndecl);
       data->error_seen = true;
       return;
     }
-
-  if (TREE_USED (fndecl) || (!is_defn && DECL_SAVED_TREE (fndecl)))
+  if (compatible > 0)
     {
-      error_at (data->loc,
-		"%<#pragma acc routine%> must be applied before %s",
-		TREE_USED (fndecl) ? "use" : "definition");
-      data->error_seen = true;
-      return;
     }
+  else
+    {
+      if (TREE_USED (fndecl) || (!is_defn && DECL_SAVED_TREE (fndecl)))
+	{
+	  error_at (data->loc,
+		    "%<#pragma acc routine%> must be applied before %s",
+		    TREE_USED (fndecl) ? "use" : "definition");
+	  data->error_seen = true;
+	  return;
+	}
 
-  /* Process the routine's dimension clauses.  */
-  tree dims = build_oacc_routine_dims (data->clauses);
-  replace_oacc_fn_attrib (fndecl, dims);
+      /* Set the routine's level of parallelism.  */
+      tree dims = build_oacc_routine_dims (data->clauses);
+      replace_oacc_fn_attrib (fndecl, dims);
 
-  /* Add an "omp declare target" attribute.  */
-  DECL_ATTRIBUTES (fndecl)
-    = tree_cons (get_identifier ("omp declare target"),
-		 data->clauses, DECL_ATTRIBUTES (fndecl));
+      /* Add an "omp declare target" attribute.  */
+      DECL_ATTRIBUTES (fndecl)
+	= tree_cons (get_identifier ("omp declare target"),
+		     data->clauses, DECL_ATTRIBUTES (fndecl));
+    }
 
   /* Remember that we've used this "#pragma acc routine".  */
   data->fndecl_seen = true;
diff --git gcc/cp/ChangeLog.gomp gcc/cp/ChangeLog.gomp
index 7f634d9..bdb4502 100644
--- gcc/cp/ChangeLog.gomp
+++ gcc/cp/ChangeLog.gomp
@@ -1,5 +1,8 @@
 2016-08-17  Thomas Schwinge  <thomas@codesourcery.com>
 
+	* parser.c (cp_finalize_oacc_routine): Rework checking if already
+	marked as an accelerator routine.
+
 	* parser.c (cp_parser_oacc_routine)
 	(cp_parser_late_parsing_oacc_routine): Normalize order of clauses.
 	(cp_finalize_oacc_routine): Call verify_oacc_routine_clauses.
diff --git gcc/cp/parser.c gcc/cp/parser.c
index 8558a6f..902197b 100644
--- gcc/cp/parser.c
+++ gcc/cp/parser.c
@@ -36808,7 +36808,9 @@ cp_finalize_oacc_routine (cp_parser *parser, tree fndecl, bool is_defn)
 	}
 
       /* Process the bind clause, if present.  */
-      for (tree c = parser->oacc_routine->clauses; c; c = OMP_CLAUSE_CHAIN (c))
+      for (tree c = parser->oacc_routine->clauses;
+	   c;
+	   c = OMP_CLAUSE_CHAIN (c))
 	{
 	  if (OMP_CLAUSE_CODE (c) != OMP_CLAUSE_BIND)
 	    continue;
@@ -36836,34 +36838,39 @@ cp_finalize_oacc_routine (cp_parser *parser, tree fndecl, bool is_defn)
 	  break;
 	}
 
-      verify_oacc_routine_clauses (&parser->oacc_routine->clauses,
-				   parser->oacc_routine->loc);
-
-      if (get_oacc_fn_attrib (fndecl))
+      int compatible
+	= verify_oacc_routine_clauses (fndecl, &parser->oacc_routine->clauses,
+				       parser->oacc_routine->loc,
+				       "#pragma acc routine");
+      if (compatible < 0)
 	{
-	  error_at (parser->oacc_routine->loc,
-		    "%<#pragma acc routine%> already applied to %qD", fndecl);
 	  parser->oacc_routine = NULL;
 	  return;
 	}
-
-      if (TREE_USED (fndecl) || (!is_defn && DECL_SAVED_TREE (fndecl)))
+      if (compatible > 0)
 	{
-	  error_at (parser->oacc_routine->loc,
-		    "%<#pragma acc routine%> must be applied before %s",
-		    TREE_USED (fndecl) ? "use" : "definition");
-	  parser->oacc_routine = NULL;
-	  return;
 	}
+      else
+	{
+	  if (TREE_USED (fndecl) || (!is_defn && DECL_SAVED_TREE (fndecl)))
+	    {
+	      error_at (parser->oacc_routine->loc,
+			"%<#pragma acc routine%> must be applied before %s",
+			TREE_USED (fndecl) ? "use" : "definition");
+	      parser->oacc_routine = NULL;
+	      return;
+	    }
+
+	  /* Set the routine's level of parallelism.  */
+	  tree dims = build_oacc_routine_dims (parser->oacc_routine->clauses);
+	  replace_oacc_fn_attrib (fndecl, dims);
 
-      /* Process the routine's dimension clauses.  */
-      tree dims = build_oacc_routine_dims (parser->oacc_routine->clauses);
-      replace_oacc_fn_attrib (fndecl, dims);
-      
-      /* Add an "omp declare target" attribute.  */
-      DECL_ATTRIBUTES (fndecl)
-	= tree_cons (get_identifier ("omp declare target"),
-		     parser->oacc_routine->clauses, DECL_ATTRIBUTES (fndecl));
+	  /* Add an "omp declare target" attribute.  */
+	  DECL_ATTRIBUTES (fndecl)
+	    = tree_cons (get_identifier ("omp declare target"),
+			 parser->oacc_routine->clauses,
+			 DECL_ATTRIBUTES (fndecl));
+	}
 
       /* Don't unset parser->oacc_routine here: we may still need it to
 	 diagnose wrong usage.  But, remember that we've used this "#pragma acc
diff --git gcc/omp-low.c gcc/omp-low.c
index 6604897..b314523 100644
--- gcc/omp-low.c
+++ gcc/omp-low.c
@@ -12701,13 +12701,18 @@ set_oacc_fn_attrib (tree fn, tree clauses, bool is_kernel, vec<tree> *args)
 
 /* Verify OpenACC routine clauses.
 
-   The chain of clauses returned will contain exactly one clause specifying the
-   level of parallelism.  */
+   Returns 0 if FNDECL should be marked as an accelerator routine, 1 if it has
+   already been marked in compatible way, and -1 if incompatible.  Upon
+   returning, the chain of clauses will contain exactly one clause specifying
+   the level of parallelism.  */
 
-void
-verify_oacc_routine_clauses (tree *clauses, location_t loc)
+int
+verify_oacc_routine_clauses (tree fndecl, tree *clauses, location_t loc,
+			     const char *routine_str)
 {
   tree c_level = NULL_TREE;
+  tree c_bind = NULL_TREE;
+  tree c_nohost = NULL_TREE;
   tree c_p = NULL_TREE;
   for (tree c = *clauses; c; c_p = c, c = OMP_CLAUSE_CHAIN (c))
     switch (OMP_CLAUSE_CODE (c))
@@ -12740,6 +12745,19 @@ verify_oacc_routine_clauses (tree *clauses, location_t loc)
 	    c = c_p;
 	  }
 	break;
+      case OMP_CLAUSE_BIND:
+	/* Don't bother with duplicate clauses at this point.  */
+	c_bind = c;
+	break;
+      case OMP_CLAUSE_NOHOST:
+	/* Don't bother with duplicate clauses at this point.  */
+	c_nohost = c;
+	break;
+      case OMP_CLAUSE_DEVICE_TYPE:
+	/* TODO */
+	sorry ("%<device_type%> clause not yet supported with %qs",
+	       routine_str);
+	break;
       default:
 	gcc_unreachable ();
       }
@@ -12751,6 +12769,126 @@ verify_oacc_routine_clauses (tree *clauses, location_t loc)
       OMP_CLAUSE_CHAIN (c_level) = *clauses;
       *clauses = c_level;
     }
+  /* In *clauses, we now have exactly one clause specifying the level of
+     parallelism.  */
+
+  /* Still got some work to do for Fortran...  */
+  if (fndecl == NULL_TREE)
+    return 0;
+
+  tree attr
+    = lookup_attribute ("omp declare target", DECL_ATTRIBUTES (fndecl));
+  if (attr != NULL_TREE)
+    {
+      /* If a "#pragma acc routine" has already been applied, just verify
+	 this one for compatibility.  */
+      /* Collect previous directive's clauses.  */
+      tree c_level_p = NULL_TREE;
+      tree c_bind_p = NULL_TREE;
+      tree c_nohost_p = NULL_TREE;
+      for (tree c = TREE_VALUE (attr); c; c = OMP_CLAUSE_CHAIN (c))
+	switch (OMP_CLAUSE_CODE (c))
+	  {
+	  case OMP_CLAUSE_GANG:
+	  case OMP_CLAUSE_WORKER:
+	  case OMP_CLAUSE_VECTOR:
+	  case OMP_CLAUSE_SEQ:
+	    gcc_checking_assert (c_level_p == NULL_TREE);
+	    c_level_p = c;
+	    break;
+	  case OMP_CLAUSE_BIND:
+	    /* Don't bother with duplicate clauses at this point.  */
+	    c_bind_p = c;
+	    break;
+	  case OMP_CLAUSE_NOHOST:
+	    /* Don't bother with duplicate clauses at this point.  */
+	    c_nohost_p = c;
+	    break;
+	  case OMP_CLAUSE_DEVICE_TYPE:
+	    /* TODO */
+	    break;
+	  default:
+	    gcc_unreachable ();
+	  }
+      gcc_checking_assert (c_level_p != NULL_TREE);
+      /* ..., and compare to current directive's, which we've already collected
+	 above.  */
+      tree c_diag;
+      tree c_diag_p;
+      /* Matching level of parallelism?  */
+      if (OMP_CLAUSE_CODE (c_level) != OMP_CLAUSE_CODE (c_level_p))
+	{
+	  c_diag = c_level;
+	  c_diag_p = c_level_p;
+	  goto incompatible;
+	}
+      /* Matching bind clauses?  */
+      if ((c_bind == NULL_TREE) != (c_bind_p == NULL_TREE))
+	{
+	  c_diag = c_bind;
+	  c_diag_p = c_bind_p;
+	  goto incompatible;
+	}
+      /* Matching bind clauses' names?  */
+      if ((c_bind != NULL_TREE) && (c_bind_p != NULL_TREE))
+	{
+	  tree c_bind_name = OMP_CLAUSE_BIND_NAME (c_bind);
+	  tree c_bind_name_p = OMP_CLAUSE_BIND_NAME (c_bind_p);
+	  /* TODO: will/should actually be the trees/strings/string pointers be
+	     identical?  */
+	  if (strcmp (TREE_STRING_POINTER (c_bind_name),
+		      TREE_STRING_POINTER (c_bind_name_p)) != 0)
+	    {
+	      c_diag = c_bind;
+	      c_diag_p = c_bind_p;
+	      goto incompatible;
+	    }
+	}
+      /* Matching nohost clauses?  */
+      if ((c_nohost == NULL_TREE) != (c_nohost_p == NULL_TREE))
+	{
+	  c_diag = c_nohost;
+	  c_diag_p = c_nohost_p;
+	  goto incompatible;
+	}
+      /* Compatible.  */
+      return 1;
+
+    incompatible:
+      if (c_diag != NULL_TREE)
+	error_at (OMP_CLAUSE_LOCATION (c_diag),
+		  "incompatible %qs clause when applying"
+		  " %<%s%> to %qD, which has already been"
+		  " marked as an accelerator routine",
+		  omp_clause_code_name[OMP_CLAUSE_CODE (c_diag)],
+		  routine_str, fndecl);
+      else if (c_diag_p != NULL_TREE)
+	error_at (loc,
+		  "missing %qs clause when applying"
+		  " %<%s%> to %qD, which has already been"
+		  " marked as an accelerator routine",
+		  omp_clause_code_name[OMP_CLAUSE_CODE (c_diag_p)],
+		  routine_str, fndecl);
+      else
+	gcc_unreachable ();
+      if (c_diag_p != NULL_TREE)
+	inform (OMP_CLAUSE_LOCATION (c_diag_p),
+		"... with %qs clause here",
+		omp_clause_code_name[OMP_CLAUSE_CODE (c_diag_p)]);
+      else
+	{
+	  /* In the front ends, we don't preserve location information for the
+	     OpenACC routine directive itself.  However, that of c_level_p
+	     should be close.  */
+	  location_t loc_routine = OMP_CLAUSE_LOCATION (c_level_p);
+	  inform (loc_routine, "... without %qs clause near to here",
+		  omp_clause_code_name[OMP_CLAUSE_CODE (c_diag)]);
+	}
+      /* Incompatible.  */
+      return -1;
+    }
+
+  return 0;
 }
 
 /*  Process the OpenACC routine's clauses to generate an attribute
diff --git gcc/omp-low.h gcc/omp-low.h
index c7b7dcb..2602a12 100644
--- gcc/omp-low.h
+++ gcc/omp-low.h
@@ -31,7 +31,7 @@ extern bool make_gimple_omp_edges (basic_block, struct omp_region **, int *);
 extern void omp_finish_file (void);
 extern tree omp_member_access_dummy_var (tree);
 extern void replace_oacc_fn_attrib (tree, tree);
-extern void verify_oacc_routine_clauses (tree *, location_t);
+extern int verify_oacc_routine_clauses (tree, tree *, location_t, const char *);
 extern tree build_oacc_routine_dims (tree);
 extern tree get_oacc_fn_attrib (tree);
 extern void set_oacc_fn_attrib (tree, tree, bool, vec<tree> *);
diff --git gcc/testsuite/ChangeLog.gomp gcc/testsuite/ChangeLog.gomp
index 0870821..1e12d8f 100644
--- gcc/testsuite/ChangeLog.gomp
+++ gcc/testsuite/ChangeLog.gomp
@@ -1,5 +1,16 @@
 2016-08-17  Thomas Schwinge  <thomas@codesourcery.com>
 
+	* c-c++-common/goacc/routine-1.c: Update.
+	* c-c++-common/goacc/routine-2.c: Likewise.
+	* c-c++-common/goacc/routine-nohost-1.c: Likewise.
+	* g++.dg/goacc/routine-2.C: Likewise.
+	* c-c++-common/goacc/routine-nohost-2.c: New file.
+
+	* c-c++-common/goacc/oaccdevlow-routine.c: Update.
+	* c-c++-common/goacc/routine-5.c: Likewise.
+	* c-c++-common/goacc/routine-level-of-parallelism-1.c: Likewise.
+	* c-c++-common/goacc/routine-level-of-parallelism-2.c: New file.
+
 	* c-c++-common/goacc/routine-2.c: Update, and move some test
 	into...
 	* c-c++-common/goacc/routine-level-of-parallelism-1.c: ... this
diff --git gcc/testsuite/c-c++-common/goacc/routine-1.c gcc/testsuite/c-c++-common/goacc/routine-1.c
index 7d57921..0b56661 100644
--- gcc/testsuite/c-c++-common/goacc/routine-1.c
+++ gcc/testsuite/c-c++-common/goacc/routine-1.c
@@ -20,32 +20,75 @@ void seq (void)
 {
 }
 
+
 #pragma acc routine
 void bind_f_1 (void)
 {
 }
 
-#pragma acc routine bind (bind_f_1)
+#pragma acc routine
+extern void bind_f_1 (void);
+
+#pragma acc routine (bind_f_1)
+
+
+#pragma acc routine \
+  bind (bind_f_1)
 void bind_f_1_1 (void)
 {
 }
 
+#pragma acc routine \
+  bind (bind_f_1)
+extern void bind_f_1_1 (void);
+
+#pragma acc routine (bind_f_1_1) \
+  bind (bind_f_1)
+
+
 /* Non-sensical bind clause, but permitted.  */
-#pragma acc routine bind ("bind_f_2")
+#pragma acc routine \
+  bind ("bind_f_2")
 void bind_f_2 (void)
 {
 }
 
-#pragma acc routine bind ("bind_f_2")
+#pragma acc routine \
+  bind ("bind_f_2")
+extern void bind_f_2 (void);
+
+#pragma acc routine (bind_f_2) \
+  bind ("bind_f_2")
+
+
+#pragma acc routine \
+  bind ("bind_f_2")
 void bind_f_2_1 (void)
 {
 }
 
-#pragma acc routine nohost
+#pragma acc routine \
+  bind ("bind_f_2")
+extern void bind_f_2_1 (void);
+
+#pragma acc routine (bind_f_2_1) \
+  bind ("bind_f_2")
+
+
+#pragma acc routine \
+  nohost
 void nohost (void)
 {
 }
 
+#pragma acc routine \
+  nohost
+extern void nohost (void);
+
+#pragma acc routine (nohost) \
+  nohost
+
+
 int main ()
 {
 #pragma acc parallel num_gangs (32) num_workers (32) vector_length (32)
diff --git gcc/testsuite/c-c++-common/goacc/routine-2.c gcc/testsuite/c-c++-common/goacc/routine-2.c
index 7582c86..debf6d7 100644
--- gcc/testsuite/c-c++-common/goacc/routine-2.c
+++ gcc/testsuite/c-c++-common/goacc/routine-2.c
@@ -26,3 +26,146 @@ extern void bind_3 (void);
 
 #pragma acc routine nohost nohost /* { dg-error "too many .nohost. clauses" } */
 extern void nohost (void);
+
+
+/* bind clause on first OpenACC routine directive but not on following.  */
+
+extern void a_bind_f_1 (void);
+#pragma acc routine (a_bind_f_1)
+
+
+#pragma acc routine \
+  bind (a_bind_f_1)
+void a_bind_f_1_1 (void)
+{
+}
+
+#pragma acc routine /* { dg-error "missing .bind. clause when applying .#pragma acc routine. to .\[void \]*a_bind_f_1_1\[\\(\\)\]*., which has already been marked as an accelerator routine" } */
+extern void a_bind_f_1_1 (void);
+
+#pragma acc routine (a_bind_f_1_1) /* { dg-error "missing .bind. clause when applying .#pragma acc routine. to .\[void \]*a_bind_f_1_1\[\\(\\)\]*., which has already been marked as an accelerator routine" } */
+
+
+/* Non-sensical bind clause, but permitted.  */
+#pragma acc routine \
+  bind ("a_bind_f_2")
+void a_bind_f_2 (void)
+{
+}
+
+#pragma acc routine /* { dg-error "missing .bind. clause when applying .#pragma acc routine. to .\[void \]*a_bind_f_2\[\\(\\)\]*., which has already been marked as an accelerator routine" } */
+extern void a_bind_f_2 (void);
+
+#pragma acc routine (a_bind_f_2) /* { dg-error "missing .bind. clause when applying .#pragma acc routine. to .\[void \]*a_bind_f_2\[\\(\\)\]*., which has already been marked as an accelerator routine" } */
+
+
+#pragma acc routine \
+  bind ("a_bind_f_2")
+void a_bind_f_2_1 (void)
+{
+}
+
+#pragma acc routine /* { dg-error "missing .bind. clause when applying .#pragma acc routine. to .\[void \]*a_bind_f_2_1\[\\(\\)\]*., which has already been marked as an accelerator routine" } */
+extern void a_bind_f_2_1 (void);
+
+#pragma acc routine (a_bind_f_2_1) /* { dg-error "missing .bind. clause when applying .#pragma acc routine. to .\[void \]*a_bind_f_2_1\[\\(\\)\]*., which has already been marked as an accelerator routine" } */
+
+
+/* No bind clause on first OpenACC routine directive, but on following.  */
+
+#pragma acc routine
+extern void b_bind_f_1 (void);
+
+
+#pragma acc routine
+void b_bind_f_1_1 (void)
+{
+}
+
+#pragma acc routine \
+  bind (b_bind_f_1) /* { dg-error "incompatible .bind. clause when applying .#pragma acc routine. to .\[void \]*b_bind_f_1_1\[\\(\\)\]*., which has already been marked as an accelerator routine" } */
+extern void b_bind_f_1_1 (void);
+
+#pragma acc routine (b_bind_f_1_1) \
+  bind (b_bind_f_1) /* { dg-error "incompatible .bind. clause when applying .#pragma acc routine. to .\[void \]*b_bind_f_1_1\[\\(\\)\]*., which has already been marked as an accelerator routine" } */
+
+
+/* Non-sensical bind clause, but permitted.  */
+#pragma acc routine
+void b_bind_f_2 (void)
+{
+}
+
+#pragma acc routine \
+  bind ("b_bind_f_2") /* { dg-error "incompatible .bind. clause when applying .#pragma acc routine. to .\[void \]*b_bind_f_2\[\\(\\)\]*., which has already been marked as an accelerator routine" } */
+extern void b_bind_f_2 (void);
+
+#pragma acc routine (b_bind_f_2) \
+  bind ("b_bind_f_2") /* { dg-error "incompatible .bind. clause when applying .#pragma acc routine. to .\[void \]*b_bind_f_2\[\\(\\)\]*., which has already been marked as an accelerator routine" } */
+
+
+#pragma acc routine
+void b_bind_f_2_1 (void)
+{
+}
+
+#pragma acc routine \
+  bind ("b_bind_f_2") /* { dg-error "incompatible .bind. clause when applying .#pragma acc routine. to .\[void \]*b_bind_f_2_1\[\\(\\)\]*., which has already been marked as an accelerator routine" } */
+extern void b_bind_f_2_1 (void);
+
+#pragma acc routine (b_bind_f_2_1) \
+  bind ("b_bind_f_2") /* { dg-error "incompatible .bind. clause when applying .#pragma acc routine. to .\[void \]*b_bind_f_2_1\[\\(\\)\]*., which has already been marked as an accelerator routine" } */
+
+
+/* Non-matching bind clauses.  */
+
+#pragma acc routine
+void c_bind_f_1a (void)
+{
+}
+
+#pragma acc routine
+extern void c_bind_f_1b (void);
+
+
+#pragma acc routine \
+  bind (c_bind_f_1a)
+void c_bind_f_1_1 (void)
+{
+}
+
+#pragma acc routine \
+  bind (c_bind_f_1b) /* { dg-error "incompatible .bind. clause when applying .#pragma acc routine. to .\[void \]*c_bind_f_1_1\[\\(\\)\]*., which has already been marked as an accelerator routine" } */
+extern void c_bind_f_1_1 (void);
+
+#pragma acc routine (c_bind_f_1_1) \
+  bind (c_bind_f_1b) /* { dg-error "incompatible .bind. clause when applying .#pragma acc routine. to .\[void \]*c_bind_f_1_1\[\\(\\)\]*., which has already been marked as an accelerator routine" } */
+
+
+/* Non-sensical bind clause, but permitted.  */
+#pragma acc routine \
+  bind ("c_bind_f_2")
+void c_bind_f_2 (void)
+{
+}
+
+#pragma acc routine \
+  bind ("C_BIND_F_2") /* { dg-error "incompatible .bind. clause when applying .#pragma acc routine. to .\[void \]*c_bind_f_2\[\\(\\)\]*., which has already been marked as an accelerator routine" } */
+extern void c_bind_f_2 (void);
+
+#pragma acc routine (c_bind_f_2) \
+  bind ("C_BIND_F_2") /* { dg-error "incompatible .bind. clause when applying .#pragma acc routine. to .\[void \]*c_bind_f_2\[\\(\\)\]*., which has already been marked as an accelerator routine" } */
+
+
+#pragma acc routine \
+  bind ("c_bind_f_2")
+void c_bind_f_2_1 (void)
+{
+}
+
+#pragma acc routine \
+  bind ("C_BIND_F_2") /* { dg-error "incompatible .bind. clause when applying .#pragma acc routine. to .\[void \]*c_bind_f_2_1\[\\(\\)\]*., which has already been marked as an accelerator routine" } */
+extern void c_bind_f_2_1 (void);
+
+#pragma acc routine (c_bind_f_2_1) \
+  bind ("C_BIND_F_2") /* { dg-error "incompatible .bind. clause when applying .#pragma acc routine. to .\[void \]*c_bind_f_2_1\[\\(\\)\]*., which has already been marked as an accelerator routine" } */
diff --git gcc/testsuite/c-c++-common/goacc/routine-5.c gcc/testsuite/c-c++-common/goacc/routine-5.c
index 17fe67c..f10651d 100644
--- gcc/testsuite/c-c++-common/goacc/routine-5.c
+++ gcc/testsuite/c-c++-common/goacc/routine-5.c
@@ -150,61 +150,19 @@ void f_static_assert();
 
 #pragma acc routine
 __extension__ extern void ex1();
-#pragma acc routine (ex1) /* { dg-error ".#pragma acc routine. already applied to .\[void \]*ex1" } */
+#pragma acc routine (ex1) worker /* { dg-error "has already been marked as an accelerator routine" } */
 
 #pragma acc routine
 __extension__ __extension__ __extension__ __extension__ __extension__ void ex2()
 {
 }
-#pragma acc routine (ex2) /* { dg-error ".#pragma acc routine. already applied to .\[void \]*ex2" } */
+#pragma acc routine (ex2) worker /* { dg-error "has already been marked as an accelerator routine" } */
 
 #pragma acc routine /* { dg-error ".#pragma acc routine. not immediately followed by function declaration or definition" } */
 __extension__ int ex3;
 #pragma acc routine (ex3) /* { dg-error ".ex3. does not refer to a function" } */
 
 
-/* "#pragma acc routine" already applied.  */
-
-extern void fungsi_1();
-#pragma acc routine(fungsi_1) gang
-#pragma acc routine(fungsi_1) gang /* { dg-error ".#pragma acc routine. already applied to .\[void \]*fungsi_1" } */
-#pragma acc routine(fungsi_1) worker /* { dg-error ".#pragma acc routine. already applied to .\[void \]*fungsi_1" } */
-#pragma acc routine(fungsi_1) vector /* { dg-error ".#pragma acc routine. already applied to .\[void \]*fungsi_1" } */
-
-#pragma acc routine seq
-extern void fungsi_2();
-#pragma acc routine(fungsi_2) seq /* { dg-error ".#pragma acc routine. already applied to .\[void \]*fungsi_2." } */
-#pragma acc routine(fungsi_2) worker /* { dg-error ".#pragma acc routine. already applied to .\[void \]*fungsi_2." } */
-#pragma acc routine(fungsi_2) /* { dg-error ".#pragma acc routine. already applied to .\[void \]*fungsi_2." } */
-
-#pragma acc routine vector
-extern void fungsi_3();
-#pragma acc routine vector /* { dg-error ".#pragma acc routine. already applied to .\[void \]*fungsi_3." } */
-void fungsi_3()
-{
-}
-
-extern void fungsi_4();
-#pragma acc routine (fungsi_4) worker
-#pragma acc routine gang /* { dg-error ".#pragma acc routine. already applied to .\[void \]*fungsi_4." } */
-void fungsi_4()
-{
-}
-
-#pragma acc routine gang
-void fungsi_5()
-{
-}
-#pragma acc routine (fungsi_5) worker /* { dg-error ".#pragma acc routine. already applied to .\[void \]*fungsi_5." } */
-
-#pragma acc routine seq
-void fungsi_6()
-{
-}
-#pragma acc routine seq /* { dg-error ".#pragma acc routine. already applied to .\[void \]*fungsi_6." } */
-extern void fungsi_6();
-
-
 /* "#pragma acc routine" must be applied before.  */
 
 void Bar ();
diff --git gcc/testsuite/c-c++-common/goacc/routine-level-of-parallelism-1.c gcc/testsuite/c-c++-common/goacc/routine-level-of-parallelism-1.c
index fc8bc3c..8f45499 100644
--- gcc/testsuite/c-c++-common/goacc/routine-level-of-parallelism-1.c
+++ gcc/testsuite/c-c++-common/goacc/routine-level-of-parallelism-1.c
@@ -42,10 +42,10 @@ void s_2 (void)
 void g_3 (void)
 {
 }
-#pragma acc routine (g_3) /* { dg-error ".#pragma acc routine. already applied to .\[void \]*g_3." } */ \
+#pragma acc routine (g_3) \
   gang \
   seq /* { dg-error ".seq. specifies a conflicting level of parallelism" } */
-#pragma acc routine (g_3) /* { dg-error ".#pragma acc routine. already applied to .\[void \]*g_3." } */ \
+#pragma acc routine (g_3) \
   gang \
   vector /* { dg-error ".vector. specifies a conflicting level of parallelism" } */
 
@@ -53,10 +53,10 @@ extern void w_3 (void);
 #pragma acc routine (w_3) \
   worker \
   vector /* { dg-error ".vector. specifies a conflicting level of parallelism" } */
-#pragma acc routine (w_3) /* { dg-error ".#pragma acc routine. already applied to .\[void \]*w_3." } */ \
+#pragma acc routine (w_3) \
   worker \
   gang /* { dg-error ".gang. specifies a conflicting level of parallelism" } */
-#pragma acc routine (w_3) /* { dg-error ".#pragma acc routine. already applied to .\[void \]*w_3." } */ \
+#pragma acc routine (w_3) \
   worker \
   seq /* { dg-error ".seq. specifies a conflicting level of parallelism" } */
 
@@ -66,10 +66,10 @@ extern void w_3 (void);
 void v_3 (void)
 {
 }
-#pragma acc routine (v_3) /* { dg-error ".#pragma acc routine. already applied to .\[void \]*v_3." } */ \
+#pragma acc routine (v_3) \
   vector \
   worker /* { dg-error ".worker. specifies a conflicting level of parallelism" } */
-#pragma acc routine (v_3) /* { dg-error ".#pragma acc routine. already applied to .\[void \]*v_3." } */ \
+#pragma acc routine (v_3) \
   vector \
   gang /* { dg-error ".gang. specifies a conflicting level of parallelism" } */
 
@@ -77,10 +77,10 @@ extern void s_3 (void);
 #pragma acc routine (s_3) \
   seq \
   gang /* { dg-error ".gang. specifies a conflicting level of parallelism" } */
-#pragma acc routine (s_3) /* { dg-error ".#pragma acc routine. already applied to .\[void \]*s_3." } */ \
+#pragma acc routine (s_3) \
   seq \
   vector /* { dg-error ".vector. specifies a conflicting level of parallelism" } */
-#pragma acc routine (s_3) /* { dg-error ".#pragma acc routine. already applied to .\[void \]*s_3." } */ \
+#pragma acc routine (s_3) \
   seq \
   worker /* { dg-error ".worker. specifies a conflicting level of parallelism" } */
 
@@ -91,12 +91,12 @@ extern void s_3 (void);
   vector /* { dg-error ".vector. specifies a conflicting level of parallelism" } */ \
   seq /* { dg-error ".seq. specifies a conflicting level of parallelism" } */
 extern void g_4 (void);
-#pragma acc routine (g_4) /* { dg-error ".#pragma acc routine. already applied to .\[void \]*g_4." } */ \
+#pragma acc routine (g_4) \
   gang gang gang /* { dg-error "too many 'gang' clauses" } */ \
   vector /* { dg-error ".vector. specifies a conflicting level of parallelism" } */ \
   worker /* { dg-error ".worker. specifies a conflicting level of parallelism" } */ \
   seq /* { dg-error ".seq. specifies a conflicting level of parallelism" } */
-#pragma acc routine (g_4) /* { dg-error ".#pragma acc routine. already applied to .\[void \]*g_4." } */ \
+#pragma acc routine (g_4) \
   gang gang gang /* { dg-error "too many 'gang' clauses" } */ \
   seq /* { dg-error ".seq. specifies a conflicting level of parallelism" } */ \
   vector /* { dg-error ".vector. specifies a conflicting level of parallelism" } */ \
@@ -108,12 +108,12 @@ extern void w_4 (void);
   seq /* { dg-error ".seq. specifies a conflicting level of parallelism" } */ \
   vector /* { dg-error ".vector. specifies a conflicting level of parallelism" } */ \
   gang /* { dg-error ".gang. specifies a conflicting level of parallelism" } */
-#pragma acc routine (w_4) /* { dg-error ".#pragma acc routine. already applied to .\[void \]*w_4." } */ \
+#pragma acc routine (w_4) \
   worker worker worker /* { dg-error "too many 'worker' clauses" } */ \
   vector /* { dg-error ".vector. specifies a conflicting level of parallelism" } */ \
   seq /* { dg-error ".seq. specifies a conflicting level of parallelism" } */ \
   gang /* { dg-error ".gang. specifies a conflicting level of parallelism" } */
-#pragma acc routine (w_4) /* { dg-error ".#pragma acc routine. already applied to .\[void \]*w_4." } */ \
+#pragma acc routine (w_4) \
   worker worker worker /* { dg-error "too many 'worker' clauses" } */ \
   seq /* { dg-error ".seq. specifies a conflicting level of parallelism" } */ \
   gang /* { dg-error ".gang. specifies a conflicting level of parallelism" } */ \
@@ -127,12 +127,12 @@ extern void w_4 (void);
 void v_4 (void)
 {
 }
-#pragma acc routine (v_4) /* { dg-error ".#pragma acc routine. already applied to .\[void \]*v_4." } */ \
+#pragma acc routine (v_4) \
   vector vector vector /* { dg-error "too many 'vector' clauses" } */ \
   seq /* { dg-error ".seq. specifies a conflicting level of parallelism" } */ \
   worker /* { dg-error ".worker. specifies a conflicting level of parallelism" } */ \
   gang /* { dg-error ".gang. specifies a conflicting level of parallelism" } */
-#pragma acc routine (v_4) /* { dg-error ".#pragma acc routine. already applied to .\[void \]*v_4." } */ \
+#pragma acc routine (v_4) \
   vector vector vector /* { dg-error "too many 'vector' clauses" } */ \
   gang /* { dg-error ".gang. specifies a conflicting level of parallelism" } */ \
   seq /* { dg-error ".seq. specifies a conflicting level of parallelism" } */ \
@@ -146,12 +146,12 @@ void v_4 (void)
 void s_4 (void)
 {
 }
-#pragma acc routine (s_4) /* { dg-error ".#pragma acc routine. already applied to .\[void \]*s_4." } */ \
+#pragma acc routine (s_4) \
   seq seq seq /* { dg-error "too many 'seq' clauses" } */ \
   vector /* { dg-error ".vector. specifies a conflicting level of parallelism" } */ \
   gang /* { dg-error ".gang. specifies a conflicting level of parallelism" } */ \
   worker /* { dg-error ".worker. specifies a conflicting level of parallelism" } */
-#pragma acc routine (s_4) /* { dg-error ".#pragma acc routine. already applied to .\[void \]*s_4." } */ \
+#pragma acc routine (s_4) \
   seq seq seq /* { dg-error "too many 'seq' clauses" } */ \
   worker /* { dg-error ".worker. specifies a conflicting level of parallelism" } */ \
   vector /* { dg-error ".vector. specifies a conflicting level of parallelism" } */ \
@@ -169,7 +169,7 @@ void s_4 (void)
 void g_5 (void)
 {
 }
-#pragma acc routine (g_5) /* { dg-error ".#pragma acc routine. already applied to .\[void \]*g_5." } */ \
+#pragma acc routine (g_5) \
   gang gang gang /* { dg-error "too many 'gang' clauses" } */ \
   vector vector vector vector /* { dg-error "too many 'vector' clauses" } */ \
   /* { dg-error ".vector. specifies a conflicting level of parallelism" "" { target *-*-* } 174 } */ \
@@ -177,7 +177,7 @@ void g_5 (void)
   /* { dg-error ".worker. specifies a conflicting level of parallelism" "" { target *-*-* } 176 } */ \
   seq seq seq seq seq seq /* { dg-error "too many 'seq' clauses" } */ \
   /* { dg-error ".seq. specifies a conflicting level of parallelism" "" { target *-*-* } 178 } */
-#pragma acc routine (g_5) /* { dg-error ".#pragma acc routine. already applied to .\[void \]*g_5." } */ \
+#pragma acc routine (g_5) \
   gang gang gang /* { dg-error "too many 'gang' clauses" } */ \
   seq seq seq seq seq /* { dg-error "too many 'seq' clauses" } */ \
   /* { dg-error ".seq. specifies a conflicting level of parallelism" "" { target *-*-* } 182 } */ \
@@ -195,7 +195,7 @@ void g_5 (void)
   gang gang /* { dg-error "too many 'gang' clauses" } */ \
   /* { dg-error ".gang. specifies a conflicting level of parallelism" "" { target *-*-* } 195 } */
 extern void w_5 (void);
-#pragma acc routine (w_5) /* { dg-error ".#pragma acc routine. already applied to .\[void \]*w_5." } */ \
+#pragma acc routine (w_5) \
   worker worker worker /* { dg-error "too many 'worker' clauses" } */ \
   vector vector vector /* { dg-error "too many 'vector' clauses" } */ \
   /* { dg-error ".vector. specifies a conflicting level of parallelism" "" { target *-*-* } 200 } */ \
@@ -203,7 +203,7 @@ extern void w_5 (void);
   /* { dg-error ".seq. specifies a conflicting level of parallelism" "" { target *-*-* } 202 } */ \
   gang gang /* { dg-error "too many 'gang' clauses" } */ \
   /* { dg-error ".gang. specifies a conflicting level of parallelism" "" { target *-*-* } 204 } */
-#pragma acc routine (w_5) /* { dg-error ".#pragma acc routine. already applied to .\[void \]*w_5." } */ \
+#pragma acc routine (w_5) \
   worker worker worker /* { dg-error "too many 'worker' clauses" } */ \
   seq seq /* { dg-error "too many 'seq' clauses" } */ \
   /* { dg-error ".seq. specifies a conflicting level of parallelism" "" { target *-*-* } 208 } */ \
@@ -221,7 +221,7 @@ extern void w_5 (void);
   gang gang /* { dg-error "too many 'gang' clauses" } */ \
   /* { dg-error ".gang. specifies a conflicting level of parallelism" "" { target *-*-* } 221 } */
 extern void v_5 (void);
-#pragma acc routine (v_5) /* { dg-error ".#pragma acc routine. already applied to .\[void \]*v_5." } */ \
+#pragma acc routine (v_5) \
   vector vector vector /* { dg-error "too many 'vector' clauses" } */ \
   seq seq seq seq seq seq /* { dg-error "too many 'seq' clauses" } */ \
   /* { dg-error ".seq. specifies a conflicting level of parallelism" "" { target *-*-* } 226 } */ \
@@ -229,7 +229,7 @@ extern void v_5 (void);
   /* { dg-error ".worker. specifies a conflicting level of parallelism" "" { target *-*-* } 228 } */ \
   gang gang /* { dg-error "too many 'gang' clauses" } */ \
   /* { dg-error ".gang. specifies a conflicting level of parallelism" "" { target *-*-* } 230 } */
-#pragma acc routine (v_5) /* { dg-error ".#pragma acc routine. already applied to .\[void \]*v_5." } */ \
+#pragma acc routine (v_5) \
   vector vector vector /* { dg-error "too many 'vector' clauses" } */ \
   gang gang /* { dg-error "too many 'gang' clauses" } */ \
   /* { dg-error ".gang. specifies a conflicting level of parallelism" "" { target *-*-* } 234 } */ \
@@ -247,7 +247,7 @@ extern void s_5 (void);
   /* { dg-error ".worker. specifies a conflicting level of parallelism" "" { target *-*-* } 246 } */ \
   gang gang /* { dg-error "too many 'gang' clauses" } */ \
   /* { dg-error ".gang. specifies a conflicting level of parallelism" "" { target *-*-* } 248 } */
-#pragma acc routine (s_5) /* { dg-error ".#pragma acc routine. already applied to .\[void \]*s_5." } */ \
+#pragma acc routine (s_5) \
   seq seq seq /* { dg-error "too many 'seq' clauses" } */ \
   vector vector vector vector /* { dg-error "too many 'vector' clauses" } */ \
   /* { dg-error ".vector. specifies a conflicting level of parallelism" "" { target *-*-* } 252 } */ \
@@ -255,7 +255,7 @@ extern void s_5 (void);
   /* { dg-error ".gang. specifies a conflicting level of parallelism" "" { target *-*-* } 254 } */ \
   worker worker worker /* { dg-error "too many 'worker' clauses" } */ \
   /* { dg-error ".worker. specifies a conflicting level of parallelism" "" { target *-*-* } 256 } */
-#pragma acc routine (s_5) /* { dg-error ".#pragma acc routine. already applied to .\[void \]*s_5." } */ \
+#pragma acc routine (s_5) \
   seq seq seq /* { dg-error "too many 'seq' clauses" } */ \
   worker worker /* { dg-error "too many 'worker' clauses" } */ \
   /* { dg-error ".worker. specifies a conflicting level of parallelism" "" { target *-*-* } 260 } */ \
@@ -263,3 +263,188 @@ extern void s_5 (void);
   /* { dg-error ".vector. specifies a conflicting level of parallelism" "" { target *-*-* } 262 } */ \
   gang gang /* { dg-error "too many 'gang' clauses" } */ \
   /* { dg-error ".gang. specifies a conflicting level of parallelism" "" { target *-*-* } 264 } */
+
+
+/* Like the *_5 tests, but with the order of clauses changed in the second and
+   following routine directives for the specific *_5 function.  */
+
+#pragma acc routine \
+  gang gang gang /* { dg-error "too many 'gang' clauses" } */ \
+  worker worker /* { dg-error "too many 'worker' clauses" } */ \
+  /* { dg-error ".worker. specifies a conflicting level of parallelism" "" { target *-*-* } 273 } */ \
+  vector vector vector /* { dg-error "too many 'vector' clauses" } */ \
+  /* { dg-error ".vector. specifies a conflicting level of parallelism" "" { target *-*-* } 275 } */ \
+  seq seq seq seq seq /* { dg-error "too many 'seq' clauses" } */ \
+  /* { dg-error ".seq. specifies a conflicting level of parallelism" "" { target *-*-* } 277 } */
+void g_6 (void)
+{
+}
+#pragma acc routine (g_6) \
+  vector vector vector vector /* { dg-error "too many 'vector' clauses" } */ \
+  /* { dg-error "incompatible .vector. clause when applying .#pragma acc routine. to .\[void \]*g_6\[\\(\\)\]*., which has already been marked as an accelerator routine" "" { target *-*-* } 283 } */ \
+  gang gang gang /* { dg-error "too many 'gang' clauses" } */ \
+  /* { dg-error ".gang. specifies a conflicting level of parallelism" "" { target *-*-* } 285 } */ \
+  worker worker worker /* { dg-error "too many 'worker' clauses" } */ \
+  /* { dg-error ".worker. specifies a conflicting level of parallelism" "" { target *-*-* } 287 } */ \
+  seq seq seq seq seq seq /* { dg-error "too many 'seq' clauses" } */ \
+  /* { dg-error ".seq. specifies a conflicting level of parallelism" "" { target *-*-* } 289 } */
+#pragma acc routine (g_6) \
+  seq seq seq seq seq /* { dg-error "too many 'seq' clauses" } */ \
+  /* { dg-error "incompatible .seq. clause when applying .#pragma acc routine. to .\[void \]*g_6\[\\(\\)\]*., which has already been marked as an accelerator routine" "" { target *-*-* } 292 } */ \
+  gang gang gang /* { dg-error "too many 'gang' clauses" } */ \
+  /* { dg-error ".gang. specifies a conflicting level of parallelism" "" { target *-*-* } 294 } */ \
+  vector vector vector vector /* { dg-error "too many 'vector' clauses" } */ \
+  /* { dg-error ".vector. specifies a conflicting level of parallelism" "" { target *-*-* } 296 } */ \
+  worker worker worker worker /* { dg-error "too many 'worker' clauses" } */ \
+  /* { dg-error ".worker. specifies a conflicting level of parallelism" "" { target *-*-* } 298 } */
+
+#pragma acc routine \
+  worker worker worker /* { dg-error "too many 'worker' clauses" } */ \
+  seq seq seq seq /* { dg-error "too many 'seq' clauses" } */ \
+  /* { dg-error ".seq. specifies a conflicting level of parallelism" "" { target *-*-* } 303 } */ \
+  vector vector vector vector vector /* { dg-error "too many 'vector' clauses" } */ \
+  /* { dg-error ".vector. specifies a conflicting level of parallelism" "" { target *-*-* } 305 } */ \
+  gang gang /* { dg-error "too many 'gang' clauses" } */ \
+  /* { dg-error ".gang. specifies a conflicting level of parallelism" "" { target *-*-* } 307 } */
+extern void w_6 (void);
+#pragma acc routine (w_6) \
+  vector vector vector /* { dg-error "too many 'vector' clauses" } */ \
+  /* { dg-error "incompatible .vector. clause when applying .#pragma acc routine. to .\[void \]*w_6\[\\(\\)\]*., which has already been marked as an accelerator routine" "" { target *-*-* } 311 } */ \
+  worker worker worker /* { dg-error "too many 'worker' clauses" } */ \
+  /* { dg-error ".worker. specifies a conflicting level of parallelism" "" { target *-*-* } 313 } */ \
+  seq seq seq /* { dg-error "too many 'seq' clauses" } */ \
+  /* { dg-error ".seq. specifies a conflicting level of parallelism" "" { target *-*-* } 315 } */ \
+  gang gang /* { dg-error "too many 'gang' clauses" } */ \
+  /* { dg-error ".gang. specifies a conflicting level of parallelism" "" { target *-*-* } 317 } */
+#pragma acc routine (w_6) \
+  seq seq /* { dg-error "too many 'seq' clauses" } */ \
+  /* { dg-error "incompatible .seq. clause when applying .#pragma acc routine. to .\[void \]*w_6\[\\(\\)\]*., which has already been marked as an accelerator routine" "" { target *-*-* } 320 } */ \
+  worker worker worker /* { dg-error "too many 'worker' clauses" } */ \
+  /* { dg-error ".worker. specifies a conflicting level of parallelism" "" { target *-*-* } 322 } */ \
+  gang gang /* { dg-error "too many 'gang' clauses" } */ \
+  /* { dg-error ".gang. specifies a conflicting level of parallelism" "" { target *-*-* } 324 } */ \
+  vector vector vector vector /* { dg-error "too many 'vector' clauses" } */ \
+  /* { dg-error ".vector. specifies a conflicting level of parallelism" "" { target *-*-* } 326 } */
+
+#pragma acc routine \
+  vector vector vector /* { dg-error "too many 'vector' clauses" } */ \
+  worker worker worker /* { dg-error "too many 'worker' clauses" } */ \
+  /* { dg-error ".worker. specifies a conflicting level of parallelism" "" { target *-*-* } 331 } */ \
+  seq seq seq seq seq seq /* { dg-error "too many 'seq' clauses" } */ \
+  /* { dg-error ".seq. specifies a conflicting level of parallelism" "" { target *-*-* } 333 } */ \
+  gang gang /* { dg-error "too many 'gang' clauses" } */ \
+  /* { dg-error ".gang. specifies a conflicting level of parallelism" "" { target *-*-* } 335 } */
+extern void v_6 (void);
+#pragma acc routine (v_6) \
+  seq seq seq seq seq seq /* { dg-error "too many 'seq' clauses" } */ \
+  /* { dg-error "incompatible .seq. clause when applying .#pragma acc routine. to .\[void \]*v_6\[\\(\\)\]*., which has already been marked as an accelerator routine" "" { target *-*-* } 339 } */ \
+  vector vector vector /* { dg-error "too many 'vector' clauses" } */ \
+  /* { dg-error ".vector. specifies a conflicting level of parallelism" "" { target *-*-* } 341 } */ \
+  worker worker /* { dg-error "too many 'worker' clauses" } */ \
+  /* { dg-error ".worker. specifies a conflicting level of parallelism" "" { target *-*-* } 343 } */ \
+  gang gang /* { dg-error "too many 'gang' clauses" } */ \
+  /* { dg-error ".gang. specifies a conflicting level of parallelism" "" { target *-*-* } 345 } */
+#pragma acc routine (v_6) \
+  gang gang /* { dg-error "too many 'gang' clauses" } */ \
+  /* { dg-error "incompatible .gang. clause when applying .#pragma acc routine. to .\[void \]*v_6\[\\(\\)\]*., which has already been marked as an accelerator routine" "" { target *-*-* } 348 } */ \
+  vector vector vector /* { dg-error "too many 'vector' clauses" } */ \
+  /* { dg-error ".vector. specifies a conflicting level of parallelism" "" { target *-*-* } 350 } */ \
+  seq seq seq seq seq /* { dg-error "too many 'seq' clauses" } */ \
+  /* { dg-error ".seq. specifies a conflicting level of parallelism" "" { target *-*-* } 352 } */ \
+  worker worker /* { dg-error "too many 'worker' clauses" } */ \
+  /* { dg-error ".worker. specifies a conflicting level of parallelism" "" { target *-*-* } 354 } */
+
+extern void s_6 (void);
+#pragma acc routine (s_6) \
+  seq seq seq /* { dg-error "too many 'seq' clauses" } */ \
+  vector vector vector vector /* { dg-error "too many 'vector' clauses" } */ \
+  /* { dg-error ".vector. specifies a conflicting level of parallelism" "" { target *-*-* } 360 } */ \
+  worker worker worker worker worker /* { dg-error "too many 'worker' clauses" } */ \
+  /* { dg-error ".worker. specifies a conflicting level of parallelism" "" { target *-*-* } 362 } */ \
+  gang gang /* { dg-error "too many 'gang' clauses" } */ \
+  /* { dg-error ".gang. specifies a conflicting level of parallelism" "" { target *-*-* } 364 } */
+#pragma acc routine (s_6) \
+  vector vector vector vector /* { dg-error "too many 'vector' clauses" } */ \
+  /* { dg-error "incompatible .vector. clause when applying .#pragma acc routine. to .\[void \]*s_6\[\\(\\)\]*., which has already been marked as an accelerator routine" "" { target *-*-* } 367 } */ \
+  seq seq seq /* { dg-error "too many 'seq' clauses" } */ \
+  /* { dg-error ".seq. specifies a conflicting level of parallelism" "" { target *-*-* } 369 } */ \
+  gang gang /* { dg-error "too many 'gang' clauses" } */ \
+  /* { dg-error ".gang. specifies a conflicting level of parallelism" "" { target *-*-* } 371 } */ \
+  worker worker worker /* { dg-error "too many 'worker' clauses" } */ \
+  /* { dg-error ".worker. specifies a conflicting level of parallelism" "" { target *-*-* } 373 } */
+#pragma acc routine (s_6) \
+  worker worker /* { dg-error "too many 'worker' clauses" } */ \
+  /* { dg-error "incompatible .worker. clause when applying .#pragma acc routine. to .\[void \]*s_6\[\\(\\)\]*., which has already been marked as an accelerator routine" "" { target *-*-* } 376 } */ \
+  seq seq seq /* { dg-error "too many 'seq' clauses" } */ \
+  /* { dg-error ".seq. specifies a conflicting level of parallelism" "" { target *-*-* } 378 } */ \
+  vector vector vector vector /* { dg-error "too many 'vector' clauses" } */ \
+  /* { dg-error ".vector. specifies a conflicting level of parallelism" "" { target *-*-* } 380 } */ \
+  gang gang /* { dg-error "too many 'gang' clauses" } */ \
+  /* { dg-error ".gang. specifies a conflicting level of parallelism" "" { target *-*-* } 382 } */
+
+
+/* Like the *_6 tests, but without all the duplicate clauses, so that the
+   routine directives are valid in isolation.  */
+
+#pragma acc routine \
+  gang
+void g_7 (void)
+{
+}
+#pragma acc routine (g_7) \
+  vector /* { dg-error "incompatible .vector. clause when applying .#pragma acc routine. to .\[void \]*g_7\[\\(\\)\]*., which has already been marked as an accelerator routine" } */
+#pragma acc routine (g_7) \
+  seq /* { dg-error "incompatible .seq. clause when applying .#pragma acc routine. to .\[void \]*g_7\[\\(\\)\]*., which has already been marked as an accelerator routine" } */
+
+#pragma acc routine \
+  worker
+extern void w_7 (void);
+#pragma acc routine (w_7) \
+  vector /* { dg-error "incompatible .vector. clause when applying .#pragma acc routine. to .\[void \]*w_7\[\\(\\)\]*., which has already been marked as an accelerator routine" } */
+#pragma acc routine (w_7) \
+  seq /* { dg-error "incompatible .seq. clause when applying .#pragma acc routine. to .\[void \]*w_7\[\\(\\)\]*., which has already been marked as an accelerator routine" } */
+
+#pragma acc routine \
+  vector
+extern void v_7 (void);
+#pragma acc routine (v_7) \
+  seq /* { dg-error "incompatible .seq. clause when applying .#pragma acc routine. to .\[void \]*v_7\[\\(\\)\]*., which has already been marked as an accelerator routine" } */
+#pragma acc routine (v_7) \
+  gang /* { dg-error "incompatible .gang. clause when applying .#pragma acc routine. to .\[void \]*v_7\[\\(\\)\]*., which has already been marked as an accelerator routine" } */
+
+extern void s_7 (void);
+#pragma acc routine (s_7) \
+  seq
+#pragma acc routine (s_7) \
+  vector /* { dg-error "incompatible .vector. clause when applying .#pragma acc routine. to .\[void \]*s_7\[\\(\\)\]*., which has already been marked as an accelerator routine" } */
+#pragma acc routine (s_7) \
+  worker /* { dg-error "incompatible .worker. clause when applying .#pragma acc routine. to .\[void \]*s_7\[\\(\\)\]*., which has already been marked as an accelerator routine" } */
+
+
+/* Test cases for implicit seq clause.  */
+
+#pragma acc routine \
+  gang
+void g_8 (void)
+{
+}
+#pragma acc routine (g_8) /* { dg-error "incompatible .seq. clause when applying .#pragma acc routine. to .\[void \]*g_8\[\\(\\)\]*., which has already been marked as an accelerator routine" } */
+
+#pragma acc routine \
+  worker
+extern void w_8 (void);
+#pragma acc routine (w_8) /* { dg-error "incompatible .seq. clause when applying .#pragma acc routine. to .\[void \]*w_8\[\\(\\)\]*., which has already been marked as an accelerator routine" } */
+
+#pragma acc routine \
+  vector
+extern void v_8 (void);
+#pragma acc routine (v_8) /* { dg-error "incompatible .seq. clause when applying .#pragma acc routine. to .\[void \]*v_8\[\\(\\)\]*., which has already been marked as an accelerator routine" } */
+
+extern void s_8 (void);
+#pragma acc routine (s_8)
+#pragma acc routine (s_8) \
+  vector /* { dg-error "incompatible .vector. clause when applying .#pragma acc routine. to .\[void \]*s_8\[\\(\\)\]*., which has already been marked as an accelerator routine" } */
+#pragma acc routine (s_8) \
+  gang /* { dg-error "incompatible .gang. clause when applying .#pragma acc routine. to .\[void \]*s_8\[\\(\\)\]*., which has already been marked as an accelerator routine" } */
+#pragma acc routine (s_8) \
+  worker /* { dg-error "incompatible .worker. clause when applying .#pragma acc routine. to .\[void \]*s_8\[\\(\\)\]*., which has already been marked as an accelerator routine" } */
diff --git gcc/testsuite/c-c++-common/goacc/routine-level-of-parallelism-2.c gcc/testsuite/c-c++-common/goacc/routine-level-of-parallelism-2.c
new file mode 100644
index 0000000..1803997
--- /dev/null
+++ gcc/testsuite/c-c++-common/goacc/routine-level-of-parallelism-2.c
@@ -0,0 +1,73 @@
+/* Test various aspects of clauses specifying compatible levels of
+   parallelism with the OpenACC routine directive.  The Fortran counterpart is
+   ../../gfortran.dg/goacc/routine-level-of-parallelism-2.f.  */
+
+#pragma acc routine gang
+void g_1 (void) /* { dg-warning "region is gang partitioned but does not contain gang partitioned code" } */
+/* { dg-bogus "region is worker partitioned but does not contain worker partitioned code" "worker partitioned" { xfail *-*-* } 6 } */
+/* { dg-bogus "region is vector partitioned but does not contain vector partitioned code" "worker partitioned" { xfail *-*-* } 6 } */
+{
+}
+#pragma acc routine (g_1) gang
+#pragma acc routine (g_1) gang
+
+
+extern void w_1 (void);
+#pragma acc routine (w_1) worker
+#pragma acc routine (w_1) worker
+#pragma acc routine (w_1) worker
+
+
+#pragma acc routine vector
+extern void v_1 (void);
+#pragma acc routine (v_1) vector
+#pragma acc routine (v_1) vector
+
+
+/* Also test the implicit seq clause.  */
+
+#pragma acc routine seq
+extern void s_1_1 (void);
+#pragma acc routine (s_1_1)
+#pragma acc routine (s_1_1) seq
+#pragma acc routine (s_1_1)
+#pragma acc routine (s_1_1) seq
+
+#pragma acc routine
+extern void s_1_2 (void);
+#pragma acc routine (s_1_2)
+#pragma acc routine (s_1_2) seq
+#pragma acc routine (s_1_2)
+#pragma acc routine (s_1_2) seq
+
+extern void s_2_1 (void);
+#pragma acc routine (s_2_1) seq
+#pragma acc routine (s_2_1)
+#pragma acc routine (s_2_1) seq
+#pragma acc routine (s_2_1)
+#pragma acc routine (s_2_1) seq
+
+extern void s_2_2 (void);
+#pragma acc routine (s_2_2)
+#pragma acc routine (s_2_2)
+#pragma acc routine (s_2_2) seq
+#pragma acc routine (s_2_2)
+#pragma acc routine (s_2_2) seq
+
+#pragma acc routine seq
+void s_3_1 (void)
+{
+}
+#pragma acc routine (s_3_1)
+#pragma acc routine (s_3_1) seq
+#pragma acc routine (s_3_1)
+#pragma acc routine (s_3_1) seq
+
+#pragma acc routine
+void s_3_2 (void)
+{
+}
+#pragma acc routine (s_3_2)
+#pragma acc routine (s_3_2) seq
+#pragma acc routine (s_3_2)
+#pragma acc routine (s_3_2) seq
diff --git gcc/testsuite/c-c++-common/goacc/routine-nohost-1.c gcc/testsuite/c-c++-common/goacc/routine-nohost-1.c
index 9baa56c..17d6b03 100644
--- gcc/testsuite/c-c++-common/goacc/routine-nohost-1.c
+++ gcc/testsuite/c-c++-common/goacc/routine-nohost-1.c
@@ -9,13 +9,27 @@ int THREE(void)
   return 3;
 }
 
+#pragma acc routine (THREE) nohost
+
+#pragma acc routine nohost
+extern int THREE(void);
+
+
 #pragma acc routine nohost
 extern void NOTHING(void);
 
+#pragma acc routine (NOTHING) nohost
+
 void NOTHING(void)
 {
 }
 
+#pragma acc routine nohost
+extern void NOTHING(void);
+
+#pragma acc routine (NOTHING) nohost
+
+
 extern float ADD(float, float);
 
 #pragma acc routine (ADD) nohost
@@ -25,4 +39,10 @@ float ADD(float x, float y)
   return x + y;
 }
 
+#pragma acc routine nohost
+extern float ADD(float, float);
+
+#pragma acc routine (ADD) nohost
+
+
 /* { dg-final { scan-tree-dump-times "Discarding function" 3 "oaccdevlow" } } */
diff --git gcc/testsuite/c-c++-common/goacc/routine-nohost-2.c gcc/testsuite/c-c++-common/goacc/routine-nohost-2.c
new file mode 100644
index 0000000..7402bfc
--- /dev/null
+++ gcc/testsuite/c-c++-common/goacc/routine-nohost-2.c
@@ -0,0 +1,97 @@
+/* Test invalid usage of the nohost clause for OpenACC routine directive.
+   Exercising different variants for declaring routines.  */
+
+#pragma acc routine
+int THREE_1(void)
+{
+  return 3;
+}
+
+#pragma acc routine (THREE_1) \
+  nohost /* { dg-error "incompatible .nohost. clause when applying .#pragma acc routine. to .\[int \]*THREE_1\[\\(\\)\]*., which has already been marked as an accelerator routine" } */
+
+#pragma acc routine \
+  nohost /* { dg-error "incompatible .nohost. clause when applying .#pragma acc routine. to .\[int \]*THREE_1\[\\(\\)\]*., which has already been marked as an accelerator routine" } */
+extern int THREE_1(void);
+
+
+#pragma acc routine
+extern void NOTHING_1(void);
+
+#pragma acc routine (NOTHING_1) \
+  nohost /* { dg-error "incompatible .nohost. clause when applying .#pragma acc routine. to .\[void \]*NOTHING_1\[\\(\\)\]*., which has already been marked as an accelerator routine" } */
+
+void NOTHING_1(void)
+{
+}
+
+#pragma acc routine \
+  nohost /* { dg-error "incompatible .nohost. clause when applying .#pragma acc routine. to .\[void \]*NOTHING_1\[\\(\\)\]*., which has already been marked as an accelerator routine" } */
+extern void NOTHING_1(void);
+
+#pragma acc routine (NOTHING_1) \
+  nohost /* { dg-error "incompatible .nohost. clause when applying .#pragma acc routine. to .\[void \]*NOTHING_1\[\\(\\)\]*., which has already been marked as an accelerator routine" } */
+
+
+extern float ADD_1(float, float);
+
+#pragma acc routine (ADD_1)
+
+float ADD_1(float x, float y)
+{
+  return x + y;
+}
+
+#pragma acc routine \
+  nohost /* { dg-error "incompatible .nohost. clause when applying .#pragma acc routine. to .\[float \]*ADD_1\[\\(float, \\)\]*., which has already been marked as an accelerator routine" } */
+extern float ADD_1(float, float);
+
+#pragma acc routine (ADD_1) \
+  nohost /* { dg-error "incompatible .nohost. clause when applying .#pragma acc routine. to .\[float \]*ADD_1\[\\(float, \\)\]*., which has already been marked as an accelerator routine" } */
+
+
+/* The same again, but with/without nohost reversed.  */
+
+#pragma acc routine \
+  nohost
+int THREE_2(void)
+{
+  return 3;
+}
+
+#pragma acc routine (THREE_2) /* { dg-error "missing .nohost. clause when applying .#pragma acc routine. to .\[int \]*THREE_2\[\\(\\)\]*., which has already been marked as an accelerator routine" } */
+
+#pragma acc routine /* { dg-error "missing .nohost. clause when applying .#pragma acc routine. to .\[int \]*THREE_2\[\\(\\)\]*., which has already been marked as an accelerator routine" } */
+extern int THREE_2(void);
+
+
+#pragma acc routine \
+  nohost
+extern void NOTHING_2(void);
+
+#pragma acc routine (NOTHING_2) /* { dg-error "missing .nohost. clause when applying .#pragma acc routine. to .\[void \]*NOTHING_2\[\\(\\)\]*., which has already been marked as an accelerator routine" } */
+
+void NOTHING_2(void)
+{
+}
+
+#pragma acc routine /* { dg-error "missing .nohost. clause when applying .#pragma acc routine. to .\[void \]*NOTHING_2\[\\(\\)\]*., which has already been marked as an accelerator routine" } */
+extern void NOTHING_2(void);
+
+#pragma acc routine (NOTHING_2) /* { dg-error "missing .nohost. clause when applying .#pragma acc routine. to .\[void \]*NOTHING_2\[\\(\\)\]*., which has already been marked as an accelerator routine" } */
+
+
+extern float ADD_2(float, float);
+
+#pragma acc routine (ADD_2) \
+  nohost
+
+float ADD_2(float x, float y)
+{
+  return x + y;
+}
+
+#pragma acc routine /* { dg-error "missing .nohost. clause when applying .#pragma acc routine. to .\[float \]*ADD_2\[\\(float, \\)\]*., which has already been marked as an accelerator routine" } */
+extern float ADD_2(float, float);
+
+#pragma acc routine (ADD_2) /* { dg-error "missing .nohost. clause when applying .#pragma acc routine. to .\[float \]*ADD_2\[\\(float, \\)\]*., which has already been marked as an accelerator routine" } */
diff --git gcc/testsuite/g++.dg/goacc/routine-2.C gcc/testsuite/g++.dg/goacc/routine-2.C
index cbee487..939dfba 100644
--- gcc/testsuite/g++.dg/goacc/routine-2.C
+++ gcc/testsuite/g++.dg/goacc/routine-2.C
@@ -58,12 +58,18 @@ broken_mult2 (float a, float b)
 {
   return a + b;
 }
+#pragma acc routine (broken_mult2) bind("mult")
+#pragma acc routine bind("mult")
+extern float broken_mult2 (float, float);
 
 #pragma acc routine bind(sum2) /* { dg-error "'sum2' has not been declared" } */
 int broken_mult3 (int a, int b);
 
 #pragma acc routine bind(foo::sub)
 int broken_mult4 (int a, int b);
+#pragma acc routine (broken_mult4) bind(foo::sub)
+#pragma acc routine bind(foo::sub)
+extern int broken_mult4 (int a, int b);
 
 namespace bar
 {
@@ -72,6 +78,9 @@ namespace bar
   {
     return a * b;
   }
+#pragma acc routine (working_mult) bind (mult)
+#pragma acc routine bind (mult)
+  float working_mult (float, float);
 }
 
 #pragma acc routine


Grüße
 Thomas

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

* Re: Repeated use of the OpenACC routine directive
  2016-08-17  1:05     ` Thomas Schwinge
@ 2016-08-18 22:08       ` Cesar Philippidis
  2016-08-19 14:39       ` Cesar Philippidis
  1 sibling, 0 replies; 16+ messages in thread
From: Cesar Philippidis @ 2016-08-18 22:08 UTC (permalink / raw)
  To: Thomas Schwinge, gcc-patches

On 08/16/2016 06:05 PM, Thomas Schwinge wrote:

> commit bffb0ee6c0a83b8c85cd919e1172086b51fdc452
> Author: tschwinge <tschwinge@138bc75d-0d04-0410-961f-82ee72b054a4>
> Date:   Wed Aug 17 00:55:02 2016 +0000
> 
>     Repeated use of the C/C++ OpenACC routine directive
>     
>     	gcc/
>     	* omp-low.c (verify_oacc_routine_clauses): Change formal
>     	parameters.  Add checking if already marked as an accelerator
>     	routine.  Adjust all users.
>     	gcc/c/
>     	* c-parser.c (c_finish_oacc_routine): Rework checking if already
>     	marked as an accelerator routine.
>     	gcc/cp/
>     	* parser.c (cp_finalize_oacc_routine): Rework checking if already
>     	marked as an accelerator routine.
>     	gcc/testsuite/
>     	* c-c++-common/goacc/oaccdevlow-routine.c: Update.

This test case is missing in both gomp4 and the git diff --stat below.

Cesar

>     	* c-c++-common/goacc/routine-5.c: Likewise.
>     	* c-c++-common/goacc/routine-level-of-parallelism-1.c: Likewise.
>     	* c-c++-common/goacc/routine-level-of-parallelism-2.c: New file.
>     
>     	gcc/testsuite/
>     	* c-c++-common/goacc/routine-1.c: Update.
>     	* c-c++-common/goacc/routine-2.c: Likewise.
>     	* c-c++-common/goacc/routine-nohost-1.c: Likewise.
>     	* g++.dg/goacc/routine-2.C: Likewise.
>     	* c-c++-common/goacc/routine-nohost-2.c: New file.
>     
>     git-svn-id: svn+ssh://gcc.gnu.org/svn/gcc/branches/gomp-4_0-branch@239521 138bc75d-0d04-0410-961f-82ee72b054a4
> ---
>  gcc/ChangeLog.gomp                                 |   4 +
>  gcc/c/ChangeLog.gomp                               |   3 +
>  gcc/c/c-parser.c                                   |  42 ++--
>  gcc/cp/ChangeLog.gomp                              |   3 +
>  gcc/cp/parser.c                                    |  51 +++--
>  gcc/omp-low.c                                      | 146 ++++++++++++-
>  gcc/omp-low.h                                      |   2 +-
>  gcc/testsuite/ChangeLog.gomp                       |  11 +
>  gcc/testsuite/c-c++-common/goacc/routine-1.c       |  51 ++++-
>  gcc/testsuite/c-c++-common/goacc/routine-2.c       | 143 +++++++++++++
>  gcc/testsuite/c-c++-common/goacc/routine-5.c       |  46 +---
>  .../goacc/routine-level-of-parallelism-1.c         | 233 ++++++++++++++++++---
>  .../goacc/routine-level-of-parallelism-2.c         |  73 +++++++
>  .../c-c++-common/goacc/routine-nohost-1.c          |  20 ++
>  .../c-c++-common/goacc/routine-nohost-2.c          |  97 +++++++++
>  gcc/testsuite/g++.dg/goacc/routine-2.C             |   9 +
>  16 files changed, 816 insertions(+), 118 deletions(-)

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

* Re: Repeated use of the OpenACC routine directive
  2016-08-17  1:05     ` Thomas Schwinge
  2016-08-18 22:08       ` Cesar Philippidis
@ 2016-08-19 14:39       ` Cesar Philippidis
  1 sibling, 0 replies; 16+ messages in thread
From: Cesar Philippidis @ 2016-08-19 14:39 UTC (permalink / raw)
  To: Thomas Schwinge, gcc-patches

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

On 08/16/2016 06:05 PM, Thomas Schwinge wrote:
> On Mon, 01 Aug 2016 17:51:24 +0200, I wrote:
>> > We found that it's not correct that we currently unconditionally diagnose
>> > an error for repeated use of the OpenACC routine directive on one
>> > function/declaration.  (For reference, it is also permissible for an
>> > "ordinary" function to have several declarations plus a definition, as
>> > long as these are compatible.)  This is, the following shall be valid:
>> > 
>> >     #pragma acc routine worker
>> >     void f(void)
>> >     {
>> >     }
>> >     #pragma acc routine (f) worker
>> >     #pragma acc routine worker
>> >     extern void f(void);
>> > 
>> > [...]

Because of the different scoping rules, I don't think it makes sense to
allow users to repeat acc routine directives in fortran. Consequently, I
couldn't create a 1:1 of your c tests to fortran. However, after
inspecting your patch I did find some areas where fortran had
insufficient test coverage, specifically with routines inside interface
blocks and that warning for unutilized parallelism.

I've applied this patch, which addresses the acc routine test coverage
deficiencies in fortran, to gomp-4_0-branch.

Cesar

[-- Attachment #2: more-routine-tests.diff --]
[-- Type: text/x-patch, Size: 3193 bytes --]

2016-08-19  Cesar Philippidis  <cesar@codesourcery.com>

	gcc/testsuite/
	* gfortran.dg/goacc/routine-8.f90: New test.
	* gfortran.dg/goacc/routine-level-of-parallelism-1.f90: New test.


diff --git a/gcc/testsuite/gfortran.dg/goacc/routine-8.f90 b/gcc/testsuite/gfortran.dg/goacc/routine-8.f90
new file mode 100644
index 0000000..c903915
--- /dev/null
+++ b/gcc/testsuite/gfortran.dg/goacc/routine-8.f90
@@ -0,0 +1,32 @@
+! Test ACC ROUTINE inside an interface block.
+
+program main
+  interface
+     function s_1 (a)
+       integer a
+       !$acc routine
+     end function s_1
+  end interface
+
+  interface
+     function s_2 (a)
+       integer a
+       !$acc routine seq
+     end function s_2
+  end interface
+
+  interface
+     function s_3 (a)
+       integer a
+       !$acc routine (s_3) ! { dg-error "Only the ..ACC ROUTINE form without list is allowed in interface block" }
+     end function s_3
+  end interface
+
+  interface
+     function s_4 (a)
+       integer a
+         !$acc routine (s_4) seq ! { dg-error "Only the ..ACC ROUTINE form without list is allowed in interface block" }
+     end function s_4
+  end interface
+end program main
+
diff --git a/gcc/testsuite/gfortran.dg/goacc/routine-level-of-parallelism-1.f90 b/gcc/testsuite/gfortran.dg/goacc/routine-level-of-parallelism-1.f90
new file mode 100644
index 0000000..364a058
--- /dev/null
+++ b/gcc/testsuite/gfortran.dg/goacc/routine-level-of-parallelism-1.f90
@@ -0,0 +1,74 @@
+! Test various aspects of clauses specifying compatible levels of
+! parallelism with the OpenACC routine directive.  The Fortran counterpart is
+! c-c++-common/goacc/routine-level-of-parallelism-2.c
+
+subroutine g_1 ! { dg-warning "region is gang partitioned but does not contain gang partitioned code" }
+  !$acc routine gang
+! { dg-bogus "region is worker partitioned but does not contain worker partitioned code" "worker partitioned" { xfail *-*-* } 5 }
+! { dg-bogus "region is vector partitioned but does not contain vector partitioned code" "worker partitioned" { xfail *-*-* } 5 }
+end subroutine g_1
+
+subroutine s_1_2a
+  !$acc routine
+end subroutine s_1_2a
+
+subroutine s_1_2b
+  !$acc routine seq
+end subroutine s_1_2b
+
+subroutine s_1_2c
+  !$acc routine (s_1_2c)
+end subroutine s_1_2c
+
+subroutine s_1_2d
+  !$acc routine (s_1_2d) seq
+end subroutine s_1_2d
+
+module s_2
+contains
+  subroutine s_2_1a
+    !$acc routine
+  end subroutine s_2_1a
+
+  subroutine s_2_1b
+    !$acc routine seq
+  end subroutine s_2_1b
+
+  subroutine s_2_1c
+    !$acc routine (s_2_1c)
+  end subroutine s_2_1c
+
+  subroutine s_2_1d
+    !$acc routine (s_2_1d) seq
+  end subroutine s_2_1d
+end module s_2
+
+subroutine test
+  external g_1, w_1, v_1
+  external s_1_1, s_1_2
+
+  interface
+     function s_3_1a (a)
+       integer a
+       !$acc routine
+     end function s_3_1a
+  end interface
+
+  interface
+     function s_3_1b (a)
+       integer a
+       !$acc routine seq
+     end function s_3_1b
+  end interface
+
+  !$acc routine(g_1) gang
+
+  !$acc routine(w_1) worker
+
+  !$acc routine(v_1) worker
+
+  ! Also test the implicit seq clause.
+
+  !$acc routine (s_1_1) seq
+
+end subroutine test

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

* [patch,openacc] Use oacc_verify_routine_clauses for C/C++
@ 2018-10-02 14:12 ` Cesar Philippidis
  2016-08-01 15:51   ` Repeated use of the OpenACC routine directive Thomas Schwinge
  0 siblings, 1 reply; 16+ messages in thread
From: Cesar Philippidis @ 2018-10-02 14:12 UTC (permalink / raw)
  To: gcc-patches, Thomas Schwinge

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

This patch introduces a new oacc_verify_routine_clauses function that
reports errors if the user abuses the gang, worker and vector clauses
for acc routine directives in C/C++. Fortran is a little different,
because the FE has it's own IR. So, while it would be possible to defer
checking for gang, worker, vector parallelism until a tree node is
created for a function, we'd still have problems of verifying the
parallelism for functions and subroutines defined and declared inside
modules. The C and C++ FE's are similar enough were they can share a
common function.

Is this OK for trunk? I bootstrapped and regression tested it for x86_64
Linux with nvptx offloading. This is only touches the OpenACC code path.

Cesar

[-- Attachment #2: 0003-Use-oacc_verify_routine_clauses-for-C-C.patch --]
[-- Type: text/x-patch, Size: 24091 bytes --]

[OpenACC] Use oacc_verify_routine_clauses for C/C++

2018-XX-YY  Thomas Schwinge  <thomas@codesourcery.com>
	    Cesar Philippidis  <cesar@codesourcery.com>

	gcc/
	* omp-general.c (oacc_build_routine_dims): Move some of its
	processing into...
	(oacc_verify_routine_clauses): ... this new function.
	* omp-general.h (oacc_verify_routine_clauses): New prototype.

	gcc/c/
	* c-parser.c (c_parser_oacc_routine): Normalize order of clauses.
	(c_finish_oacc_routine): Call oacc_verify_routine_clauses.

	gcc/cp/
	* parser.c (cp_parser_oacc_routine)
	(cp_parser_late_parsing_oacc_routine): Normalize order of clauses.
	(cp_finalize_oacc_routine): Call oacc_verify_routine_clauses.

	gcc/testsuite/
	* c-c++-common/goacc/routine-level-of-parallelism-1.c: New test.

(cherry picked from gomp-4_0-branch r239520)
---
 gcc/c/c-parser.c                              |   8 +
 gcc/cp/parser.c                               |   9 +
 gcc/omp-general.c                             |  69 ++++-
 gcc/omp-general.h                             |   1 +
 .../goacc/routine-level-of-parallelism-1.c    | 265 ++++++++++++++++++
 5 files changed, 342 insertions(+), 10 deletions(-)
 create mode 100644 gcc/testsuite/c-c++-common/goacc/routine-level-of-parallelism-1.c

diff --git a/gcc/c/c-parser.c b/gcc/c/c-parser.c
index 3ca8fe71cc4..3517cb783d9 100644
--- a/gcc/c/c-parser.c
+++ b/gcc/c/c-parser.c
@@ -14999,6 +14999,9 @@ c_parser_oacc_routine (c_parser *parser, enum pragma_context context)
       data.clauses
 	= c_parser_oacc_all_clauses (parser, OACC_ROUTINE_CLAUSE_MASK,
 				     "#pragma acc routine");
+      /* The clauses are in reverse order; fix that to make later diagnostic
+	 emission easier.  */
+      data.clauses = nreverse (data.clauses);
 
       if (TREE_CODE (decl) != FUNCTION_DECL)
 	{
@@ -15013,6 +15016,9 @@ c_parser_oacc_routine (c_parser *parser, enum pragma_context context)
       data.clauses
 	= c_parser_oacc_all_clauses (parser, OACC_ROUTINE_CLAUSE_MASK,
 				     "#pragma acc routine");
+      /* The clauses are in reverse order; fix that to make later diagnostic
+	 emission easier.  */
+      data.clauses = nreverse (data.clauses);
 
       /* Emit a helpful diagnostic if there's another pragma following this
 	 one.  Also don't allow a static assertion declaration, as in the
@@ -15076,6 +15082,8 @@ c_finish_oacc_routine (struct oacc_routine_data *data, tree fndecl,
       return;
     }
 
+  oacc_verify_routine_clauses (&data->clauses, data->loc);
+
   if (oacc_get_fn_attrib (fndecl))
     {
       error_at (data->loc,
diff --git a/gcc/cp/parser.c b/gcc/cp/parser.c
index 241226d8c21..fa7ee7798ae 100644
--- a/gcc/cp/parser.c
+++ b/gcc/cp/parser.c
@@ -38117,6 +38117,9 @@ cp_parser_oacc_routine (cp_parser *parser, cp_token *pragma_tok,
 	= cp_parser_oacc_all_clauses (parser, OACC_ROUTINE_CLAUSE_MASK,
 				      "#pragma acc routine",
 				      cp_lexer_peek_token (parser->lexer));
+      /* The clauses are in reverse order; fix that to make later diagnostic
+	 emission easier.  */
+      data.clauses = nreverse (data.clauses);
 
       if (decl && is_overloaded_fn (decl)
 	  && (TREE_CODE (decl) != FUNCTION_DECL
@@ -38213,6 +38216,9 @@ cp_parser_late_parsing_oacc_routine (cp_parser *parser, tree attrs)
   parser->oacc_routine->clauses
     = cp_parser_oacc_all_clauses (parser, OACC_ROUTINE_CLAUSE_MASK,
 				  "#pragma acc routine", pragma_tok);
+  /* The clauses are in reverse order; fix that to make later diagnostic
+     emission easier.  */
+  parser->oacc_routine->clauses = nreverse (parser->oacc_routine->clauses);
   cp_parser_pop_lexer (parser);
   /* Later, cp_finalize_oacc_routine will process the clauses, and then set
      fndecl_seen.  */
@@ -38247,6 +38253,9 @@ cp_finalize_oacc_routine (cp_parser *parser, tree fndecl, bool is_defn)
 	  return;
 	}
 
+      oacc_verify_routine_clauses (&parser->oacc_routine->clauses,
+				   parser->oacc_routine->loc);
+
       if (oacc_get_fn_attrib (fndecl))
 	{
 	  error_at (parser->oacc_routine->loc,
diff --git a/gcc/omp-general.c b/gcc/omp-general.c
index cabbbbc6de2..3ea2224957d 100644
--- a/gcc/omp-general.c
+++ b/gcc/omp-general.c
@@ -559,9 +559,64 @@ oacc_set_fn_attrib (tree fn, tree clauses, vec<tree> *args)
     }
 }
 
-/*  Process the routine's dimension clauess to generate an attribute
-    value.  Issue diagnostics as appropriate.  We default to SEQ
-    (OpenACC 2.5 clarifies this). All dimensions have a size of zero
+/* Verify OpenACC routine clauses.
+
+   The chain of clauses returned will contain exactly one clause specifying the
+   level of parallelism.  */
+
+void
+oacc_verify_routine_clauses (tree *clauses, location_t loc)
+{
+  tree c_level = NULL_TREE;
+  tree c_p = NULL_TREE;
+  for (tree c = *clauses; c; c_p = c, c = OMP_CLAUSE_CHAIN (c))
+    switch (OMP_CLAUSE_CODE (c))
+      {
+      case OMP_CLAUSE_GANG:
+      case OMP_CLAUSE_WORKER:
+      case OMP_CLAUSE_VECTOR:
+      case OMP_CLAUSE_SEQ:
+	if (c_level == NULL_TREE)
+	  c_level = c;
+	else if (OMP_CLAUSE_CODE (c) == OMP_CLAUSE_CODE (c_level))
+	  {
+	    /* This has already been diagnosed in the front ends.  */
+	    /* Drop the duplicate clause.  */
+	    gcc_checking_assert (c_p != NULL_TREE);
+	    OMP_CLAUSE_CHAIN (c_p) = OMP_CLAUSE_CHAIN (c);
+	    c = c_p;
+	  }
+	else
+	  {
+	    error_at (OMP_CLAUSE_LOCATION (c),
+		      "%qs specifies a conflicting level of parallelism",
+		      omp_clause_code_name[OMP_CLAUSE_CODE (c)]);
+	    inform (OMP_CLAUSE_LOCATION (c_level),
+		    "... to the previous %qs clause here",
+		    omp_clause_code_name[OMP_CLAUSE_CODE (c_level)]);
+	    /* Drop the conflicting clause.  */
+	    gcc_checking_assert (c_p != NULL_TREE);
+	    OMP_CLAUSE_CHAIN (c_p) = OMP_CLAUSE_CHAIN (c);
+	    c = c_p;
+	  }
+	break;
+      case OMP_CLAUSE_NOHOST:
+	break;
+      default:
+	gcc_unreachable ();
+      }
+  if (c_level == NULL_TREE)
+    {
+      /* OpenACC 2.5 makes this an error; for the current OpenACC 2.0a
+	 implementation add an implicit "seq" clause.  */
+      c_level = build_omp_clause (loc, OMP_CLAUSE_SEQ);
+      OMP_CLAUSE_CHAIN (c_level) = *clauses;
+      *clauses = c_level;
+    }
+}
+
+/*  Process the OpenACC routine's clauses to generate an attribute
+    for the level of parallelism.  All dimensions have a size of zero
     (dynamic).  TREE_PURPOSE is set to indicate whether that dimension
     can have a loop partitioned on it.  non-zero indicates
     yes, zero indicates no.  By construction once a non-zero has been
@@ -583,16 +638,10 @@ oacc_build_routine_dims (tree clauses)
     for (ix = GOMP_DIM_MAX + 1; ix--;)
       if (OMP_CLAUSE_CODE (clauses) == ids[ix])
 	{
-	  if (level >= 0)
-	    error_at (OMP_CLAUSE_LOCATION (clauses),
-		      "multiple loop axes specified for routine");
 	  level = ix;
 	  break;
 	}
-
-  /* Default to SEQ.  */
-  if (level < 0)
-    level = GOMP_DIM_MAX;
+  gcc_checking_assert (level >= 0);
 
   tree dims = NULL_TREE;
 
diff --git a/gcc/omp-general.h b/gcc/omp-general.h
index 66f0a33c2e2..e0de288dd9a 100644
--- a/gcc/omp-general.h
+++ b/gcc/omp-general.h
@@ -83,6 +83,7 @@ extern int omp_max_simt_vf (void);
 extern tree oacc_launch_pack (unsigned code, tree device, unsigned op);
 extern void oacc_replace_fn_attrib (tree fn, tree dims);
 extern void oacc_set_fn_attrib (tree fn, tree clauses, vec<tree> *args);
+extern void oacc_verify_routine_clauses (tree *, location_t);
 extern tree oacc_build_routine_dims (tree clauses);
 extern tree oacc_get_fn_attrib (tree fn);
 extern bool offloading_function_p (tree fn);
diff --git a/gcc/testsuite/c-c++-common/goacc/routine-level-of-parallelism-1.c b/gcc/testsuite/c-c++-common/goacc/routine-level-of-parallelism-1.c
new file mode 100644
index 00000000000..d71d6e088f7
--- /dev/null
+++ b/gcc/testsuite/c-c++-common/goacc/routine-level-of-parallelism-1.c
@@ -0,0 +1,265 @@
+/* Test various aspects of clauses specifying incompatible levels of
+   parallelism with the OpenACC routine directive.  The Fortran counterpart is
+   ../../gfortran.dg/goacc/routine-level-of-parallelism-1.f.  */
+
+extern void g_1 (void);
+#pragma acc routine (g_1) gang gang /* { dg-error "too many 'gang' clauses" } */
+
+#pragma acc routine worker worker /* { dg-error "too many 'worker' clauses" } */
+void w_1 (void)
+{
+}
+
+#pragma acc routine vector vector /* { dg-error "too many 'vector' clauses" } */
+void v_1 (void)
+{
+}
+
+#pragma acc routine seq seq /* { dg-error "too many 'seq' clauses" } */
+extern void s_1 (void);
+
+
+#pragma acc routine gang gang gang /* { dg-error "too many 'gang' clauses" } */
+void g_2 (void)
+{
+}
+
+#pragma acc routine worker worker worker /* { dg-error "too many 'worker' clauses" } */
+extern void w_2 (void);
+
+extern void v_2 (void);
+#pragma acc routine (v_2) vector vector vector /* { dg-error "too many 'vector' clauses" } */
+
+#pragma acc routine seq seq seq /* { dg-error "too many 'seq' clauses" } */
+void s_2 (void)
+{
+}
+
+
+#pragma acc routine \
+  gang \
+  worker /* { dg-error ".worker. specifies a conflicting level of parallelism" } */
+void g_3 (void)
+{
+}
+#pragma acc routine (g_3) /* { dg-error ".#pragma acc routine. already applied to .\[void \]*g_3." } */ \
+  gang \
+  seq /* { dg-error ".seq. specifies a conflicting level of parallelism" } */
+#pragma acc routine (g_3) /* { dg-error ".#pragma acc routine. already applied to .\[void \]*g_3." } */ \
+  gang \
+  vector /* { dg-error ".vector. specifies a conflicting level of parallelism" } */
+
+extern void w_3 (void);
+#pragma acc routine (w_3) \
+  worker \
+  vector /* { dg-error ".vector. specifies a conflicting level of parallelism" } */
+#pragma acc routine (w_3) /* { dg-error ".#pragma acc routine. already applied to .\[void \]*w_3." } */ \
+  worker \
+  gang /* { dg-error ".gang. specifies a conflicting level of parallelism" } */
+#pragma acc routine (w_3) /* { dg-error ".#pragma acc routine. already applied to .\[void \]*w_3." } */ \
+  worker \
+  seq /* { dg-error ".seq. specifies a conflicting level of parallelism" } */
+
+#pragma acc routine \
+  vector \
+  seq /* { dg-error ".seq. specifies a conflicting level of parallelism" } */
+void v_3 (void)
+{
+}
+#pragma acc routine (v_3) /* { dg-error ".#pragma acc routine. already applied to .\[void \]*v_3." } */ \
+  vector \
+  worker /* { dg-error ".worker. specifies a conflicting level of parallelism" } */
+#pragma acc routine (v_3) /* { dg-error ".#pragma acc routine. already applied to .\[void \]*v_3." } */ \
+  vector \
+  gang /* { dg-error ".gang. specifies a conflicting level of parallelism" } */
+
+extern void s_3 (void);
+#pragma acc routine (s_3) \
+  seq \
+  gang /* { dg-error ".gang. specifies a conflicting level of parallelism" } */
+#pragma acc routine (s_3) /* { dg-error ".#pragma acc routine. already applied to .\[void \]*s_3." } */ \
+  seq \
+  vector /* { dg-error ".vector. specifies a conflicting level of parallelism" } */
+#pragma acc routine (s_3) /* { dg-error ".#pragma acc routine. already applied to .\[void \]*s_3." } */ \
+  seq \
+  worker /* { dg-error ".worker. specifies a conflicting level of parallelism" } */
+
+
+#pragma acc routine \
+  gang gang gang /* { dg-error "too many 'gang' clauses" } */ \
+  worker /* { dg-error ".worker. specifies a conflicting level of parallelism" } */ \
+  vector /* { dg-error ".vector. specifies a conflicting level of parallelism" } */ \
+  seq /* { dg-error ".seq. specifies a conflicting level of parallelism" } */
+extern void g_4 (void);
+#pragma acc routine (g_4) /* { dg-error ".#pragma acc routine. already applied to .\[void \]*g_4." } */ \
+  gang gang gang /* { dg-error "too many 'gang' clauses" } */ \
+  vector /* { dg-error ".vector. specifies a conflicting level of parallelism" } */ \
+  worker /* { dg-error ".worker. specifies a conflicting level of parallelism" } */ \
+  seq /* { dg-error ".seq. specifies a conflicting level of parallelism" } */
+#pragma acc routine (g_4) /* { dg-error ".#pragma acc routine. already applied to .\[void \]*g_4." } */ \
+  gang gang gang /* { dg-error "too many 'gang' clauses" } */ \
+  seq /* { dg-error ".seq. specifies a conflicting level of parallelism" } */ \
+  vector /* { dg-error ".vector. specifies a conflicting level of parallelism" } */ \
+  worker /* { dg-error ".worker. specifies a conflicting level of parallelism" } */
+
+extern void w_4 (void);
+#pragma acc routine (w_4) \
+  worker worker worker /* { dg-error "too many 'worker' clauses" } */ \
+  seq /* { dg-error ".seq. specifies a conflicting level of parallelism" } */ \
+  vector /* { dg-error ".vector. specifies a conflicting level of parallelism" } */ \
+  gang /* { dg-error ".gang. specifies a conflicting level of parallelism" } */
+#pragma acc routine (w_4) /* { dg-error ".#pragma acc routine. already applied to .\[void \]*w_4." } */ \
+  worker worker worker /* { dg-error "too many 'worker' clauses" } */ \
+  vector /* { dg-error ".vector. specifies a conflicting level of parallelism" } */ \
+  seq /* { dg-error ".seq. specifies a conflicting level of parallelism" } */ \
+  gang /* { dg-error ".gang. specifies a conflicting level of parallelism" } */
+#pragma acc routine (w_4) /* { dg-error ".#pragma acc routine. already applied to .\[void \]*w_4." } */ \
+  worker worker worker /* { dg-error "too many 'worker' clauses" } */ \
+  seq /* { dg-error ".seq. specifies a conflicting level of parallelism" } */ \
+  gang /* { dg-error ".gang. specifies a conflicting level of parallelism" } */ \
+  vector /* { dg-error ".vector. specifies a conflicting level of parallelism" } */
+
+#pragma acc routine \
+  vector vector vector /* { dg-error "too many 'vector' clauses" } */ \
+  worker /* { dg-error ".worker. specifies a conflicting level of parallelism" } */ \
+  seq /* { dg-error ".seq. specifies a conflicting level of parallelism" } */ \
+  gang /* { dg-error ".gang. specifies a conflicting level of parallelism" } */
+void v_4 (void)
+{
+}
+#pragma acc routine (v_4) /* { dg-error ".#pragma acc routine. already applied to .\[void \]*v_4." } */ \
+  vector vector vector /* { dg-error "too many 'vector' clauses" } */ \
+  seq /* { dg-error ".seq. specifies a conflicting level of parallelism" } */ \
+  worker /* { dg-error ".worker. specifies a conflicting level of parallelism" } */ \
+  gang /* { dg-error ".gang. specifies a conflicting level of parallelism" } */
+#pragma acc routine (v_4) /* { dg-error ".#pragma acc routine. already applied to .\[void \]*v_4." } */ \
+  vector vector vector /* { dg-error "too many 'vector' clauses" } */ \
+  gang /* { dg-error ".gang. specifies a conflicting level of parallelism" } */ \
+  seq /* { dg-error ".seq. specifies a conflicting level of parallelism" } */ \
+  worker /* { dg-error ".worker. specifies a conflicting level of parallelism" } */
+
+#pragma acc routine \
+  seq seq seq /* { dg-error "too many 'seq' clauses" } */ \
+  vector /* { dg-error ".vector. specifies a conflicting level of parallelism" } */ \
+  worker /* { dg-error ".worker. specifies a conflicting level of parallelism" } */ \
+  gang /* { dg-error ".gang. specifies a conflicting level of parallelism" } */
+void s_4 (void)
+{
+}
+#pragma acc routine (s_4) /* { dg-error ".#pragma acc routine. already applied to .\[void \]*s_4." } */ \
+  seq seq seq /* { dg-error "too many 'seq' clauses" } */ \
+  vector /* { dg-error ".vector. specifies a conflicting level of parallelism" } */ \
+  gang /* { dg-error ".gang. specifies a conflicting level of parallelism" } */ \
+  worker /* { dg-error ".worker. specifies a conflicting level of parallelism" } */
+#pragma acc routine (s_4) /* { dg-error ".#pragma acc routine. already applied to .\[void \]*s_4." } */ \
+  seq seq seq /* { dg-error "too many 'seq' clauses" } */ \
+  worker /* { dg-error ".worker. specifies a conflicting level of parallelism" } */ \
+  vector /* { dg-error ".vector. specifies a conflicting level of parallelism" } */ \
+  gang /* { dg-error ".gang. specifies a conflicting level of parallelism" } */
+
+
+#pragma acc routine \
+  gang gang gang /* { dg-error "too many 'gang' clauses" } */ \
+  worker worker /* { dg-error "too many 'worker' clauses" } */ \
+  /* { dg-error ".worker. specifies a conflicting level of parallelism" "" { target *-*-* } .-1 } */ \
+  vector vector vector /* { dg-error "too many 'vector' clauses" } */ \
+  /* { dg-error ".vector. specifies a conflicting level of parallelism" "" { target *-*-* } .-1 } */ \
+  seq seq seq seq seq /* { dg-error "too many 'seq' clauses" } */ \
+  /* { dg-error ".seq. specifies a conflicting level of parallelism" "" { target *-*-* } .-1 } */
+void g_5 (void)
+{
+}
+#pragma acc routine (g_5) /* { dg-error ".#pragma acc routine. already applied to .\[void \]*g_5." } */ \
+  gang gang gang /* { dg-error "too many 'gang' clauses" } */ \
+  vector vector vector vector /* { dg-error "too many 'vector' clauses" } */ \
+  /* { dg-error ".vector. specifies a conflicting level of parallelism" "" { target *-*-* } .-1 } */ \
+  worker worker worker /* { dg-error "too many 'worker' clauses" } */ \
+  /* { dg-error ".worker. specifies a conflicting level of parallelism" "" { target *-*-* } .-1 } */ \
+  seq seq seq seq seq seq /* { dg-error "too many 'seq' clauses" } */ \
+  /* { dg-error ".seq. specifies a conflicting level of parallelism" "" { target *-*-* } .-1 } */
+#pragma acc routine (g_5) /* { dg-error ".#pragma acc routine. already applied to .\[void \]*g_5." } */ \
+  gang gang gang /* { dg-error "too many 'gang' clauses" } */ \
+  seq seq seq seq seq /* { dg-error "too many 'seq' clauses" } */ \
+  /* { dg-error ".seq. specifies a conflicting level of parallelism" "" { target *-*-* } .-1 } */ \
+  vector vector vector vector /* { dg-error "too many 'vector' clauses" } */ \
+  /* { dg-error ".vector. specifies a conflicting level of parallelism" "" { target *-*-* } .-1 } */ \
+  worker worker worker worker /* { dg-error "too many 'worker' clauses" } */ \
+  /* { dg-error ".worker. specifies a conflicting level of parallelism" "" { target *-*-* } .-1 } */
+
+#pragma acc routine \
+  worker worker worker /* { dg-error "too many 'worker' clauses" } */ \
+  seq seq seq seq /* { dg-error "too many 'seq' clauses" } */ \
+  /* { dg-error ".seq. specifies a conflicting level of parallelism" "" { target *-*-* } .-1 } */ \
+  vector vector vector vector vector /* { dg-error "too many 'vector' clauses" } */ \
+  /* { dg-error ".vector. specifies a conflicting level of parallelism" "" { target *-*-* } .-1 } */ \
+  gang gang /* { dg-error "too many 'gang' clauses" } */ \
+  /* { dg-error ".gang. specifies a conflicting level of parallelism" "" { target *-*-* } .-1 } */
+extern void w_5 (void);
+#pragma acc routine (w_5) /* { dg-error ".#pragma acc routine. already applied to .\[void \]*w_5." } */ \
+  worker worker worker /* { dg-error "too many 'worker' clauses" } */ \
+  vector vector vector /* { dg-error "too many 'vector' clauses" } */ \
+  /* { dg-error ".vector. specifies a conflicting level of parallelism" "" { target *-*-* } .-1 } */ \
+  seq seq seq /* { dg-error "too many 'seq' clauses" } */ \
+  /* { dg-error ".seq. specifies a conflicting level of parallelism" "" { target *-*-* } .-1 } */ \
+  gang gang /* { dg-error "too many 'gang' clauses" } */ \
+  /* { dg-error ".gang. specifies a conflicting level of parallelism" "" { target *-*-* } .-1 } */
+#pragma acc routine (w_5) /* { dg-error ".#pragma acc routine. already applied to .\[void \]*w_5." } */ \
+  worker worker worker /* { dg-error "too many 'worker' clauses" } */ \
+  seq seq /* { dg-error "too many 'seq' clauses" } */ \
+  /* { dg-error ".seq. specifies a conflicting level of parallelism" "" { target *-*-* } .-1 } */ \
+  gang gang /* { dg-error "too many 'gang' clauses" } */ \
+  /* { dg-error ".gang. specifies a conflicting level of parallelism" "" { target *-*-* } .-1 } */ \
+  vector vector vector vector /* { dg-error "too many 'vector' clauses" } */ \
+  /* { dg-error ".vector. specifies a conflicting level of parallelism" "" { target *-*-* } .-1 } */
+
+#pragma acc routine \
+  vector vector vector /* { dg-error "too many 'vector' clauses" } */ \
+  worker worker worker /* { dg-error "too many 'worker' clauses" } */ \
+  /* { dg-error ".worker. specifies a conflicting level of parallelism" "" { target *-*-* } .-1 } */ \
+  seq seq seq seq seq seq /* { dg-error "too many 'seq' clauses" } */ \
+  /* { dg-error ".seq. specifies a conflicting level of parallelism" "" { target *-*-* } .-1 } */ \
+  gang gang /* { dg-error "too many 'gang' clauses" } */ \
+  /* { dg-error ".gang. specifies a conflicting level of parallelism" "" { target *-*-* } .-1 } */
+extern void v_5 (void);
+#pragma acc routine (v_5) /* { dg-error ".#pragma acc routine. already applied to .\[void \]*v_5." } */ \
+  vector vector vector /* { dg-error "too many 'vector' clauses" } */ \
+  seq seq seq seq seq seq /* { dg-error "too many 'seq' clauses" } */ \
+  /* { dg-error ".seq. specifies a conflicting level of parallelism" "" { target *-*-* } .-1 } */ \
+  worker worker /* { dg-error "too many 'worker' clauses" } */ \
+  /* { dg-error ".worker. specifies a conflicting level of parallelism" "" { target *-*-* } .-1 } */ \
+  gang gang /* { dg-error "too many 'gang' clauses" } */ \
+  /* { dg-error ".gang. specifies a conflicting level of parallelism" "" { target *-*-* } .-1 } */
+#pragma acc routine (v_5) /* { dg-error ".#pragma acc routine. already applied to .\[void \]*v_5." } */ \
+  vector vector vector /* { dg-error "too many 'vector' clauses" } */ \
+  gang gang /* { dg-error "too many 'gang' clauses" } */ \
+  /* { dg-error ".gang. specifies a conflicting level of parallelism" "" { target *-*-* } .-1 } */ \
+  seq seq seq seq seq /* { dg-error "too many 'seq' clauses" } */ \
+  /* { dg-error ".seq. specifies a conflicting level of parallelism" "" { target *-*-* } .-1 } */ \
+  worker worker /* { dg-error "too many 'worker' clauses" } */ \
+  /* { dg-error ".worker. specifies a conflicting level of parallelism" "" { target *-*-* } .-1 } */
+
+extern void s_5 (void);
+#pragma acc routine (s_5) \
+  seq seq seq /* { dg-error "too many 'seq' clauses" } */ \
+  vector vector vector vector /* { dg-error "too many 'vector' clauses" } */ \
+  /* { dg-error ".vector. specifies a conflicting level of parallelism" "" { target *-*-* } .-1 } */ \
+  worker worker worker worker worker /* { dg-error "too many 'worker' clauses" } */ \
+  /* { dg-error ".worker. specifies a conflicting level of parallelism" "" { target *-*-* } .-1 } */ \
+  gang gang /* { dg-error "too many 'gang' clauses" } */ \
+  /* { dg-error ".gang. specifies a conflicting level of parallelism" "" { target *-*-* } .-1 } */
+#pragma acc routine (s_5) /* { dg-error ".#pragma acc routine. already applied to .\[void \]*s_5." } */ \
+  seq seq seq /* { dg-error "too many 'seq' clauses" } */ \
+  vector vector vector vector /* { dg-error "too many 'vector' clauses" } */ \
+  /* { dg-error ".vector. specifies a conflicting level of parallelism" "" { target *-*-* } .-1 } */ \
+  gang gang /* { dg-error "too many 'gang' clauses" } */ \
+  /* { dg-error ".gang. specifies a conflicting level of parallelism" "" { target *-*-* } .-1 } */ \
+  worker worker worker /* { dg-error "too many 'worker' clauses" } */ \
+  /* { dg-error ".worker. specifies a conflicting level of parallelism" "" { target *-*-* } .-1 } */
+#pragma acc routine (s_5) /* { dg-error ".#pragma acc routine. already applied to .\[void \]*s_5." } */ \
+  seq seq seq /* { dg-error "too many 'seq' clauses" } */ \
+  worker worker /* { dg-error "too many 'worker' clauses" } */ \
+  /* { dg-error ".worker. specifies a conflicting level of parallelism" "" { target *-*-* } .-1 } */ \
+  vector vector vector vector /* { dg-error "too many 'vector' clauses" } */ \
+  /* { dg-error ".vector. specifies a conflicting level of parallelism" "" { target *-*-* } .-1 } */ \
+  gang gang /* { dg-error "too many 'gang' clauses" } */ \
+  /* { dg-error ".gang. specifies a conflicting level of parallelism" "" { target *-*-* } .-1 } */
-- 
2.17.1


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

* [patch,openacc] Repeated use of the OpenACC routine directive
@ 2018-10-02 14:57     ` Cesar Philippidis
  2019-02-22 12:25       ` PR89433 "Repeated use of the OpenACC 'routine' directive" Thomas Schwinge
  0 siblings, 1 reply; 16+ messages in thread
From: Cesar Philippidis @ 2018-10-02 14:57 UTC (permalink / raw)
  To: gcc-patches, Schwinge, Thomas

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

This is another patch that teaches the C and C++ to emit more errors
involving acc routine clauses. In retrospect, I should have merged it
together with the patch I posted here
<https://gcc.gnu.org/ml/gcc-patches/2018-10/msg00089.html>, however at
the time I thought it would make the patch too large.

Is this patch OK for trunk? I bootstrapped and regtested it for x86_64
Linux with nvptx offloading. This patch is also self-contained to the
OpenACC code path.

Thanks,
Cesar

[-- Attachment #2: 0005-C-C-Repeated-use-of-the-OpenACC-routine-directive.patch --]
[-- Type: text/x-patch, Size: 45264 bytes --]

[OpenACC] Repeated use of the OpenACC routine directive

2018-XX-YY  Thomas Schwinge  <thomas@codesourcery.com>
	    Cesar Philippidis  <cesar@codesourcery.com>

	gcc/
	* omp-general.h (oacc_verify_routine_clauses): Declare.
	* omp-general.c (oacc_verify_routine_clauses): Change formal
	parameters.  Add checking if already marked as an accelerator
	routine.  Adjust all users.

	gcc/c/
	* c-parser.c (c_finish_oacc_routine): Rework checking if already
	marked as an accelerator routine.

	gcc/cp/
	* parser.c (cp_finalize_oacc_routine): Rework checking if already
	marked as an accelerator routine.

	gcc/testsuite/
	* c-c++-common/goacc/routine-1.c: Update tests.
	* c-c++-common/goacc/routine-5.c: Likewise.
	* c-c++-common/goacc/routine-level-of-parallelism-1.c: Likewise.
	* c-c++-common/goacc/routine-level-of-parallelism-2.c: New test.
	* c-c++-common/goacc/routine-nohost-1.c: Update tests.
	* c-c++-common/goacc/routine-nohost-2.c: New test.


(cherry picked from gomp-4_0-branch r239521)

remove bind clause support
---
 gcc/c/c-parser.c                              |  46 ++--
 gcc/cp/parser.c                               |  50 ++--
 gcc/omp-general.c                             | 105 +++++++-
 gcc/omp-general.h                             |   3 +-
 gcc/testsuite/c-c++-common/goacc/routine-1.c  |  10 +-
 gcc/testsuite/c-c++-common/goacc/routine-5.c  |   4 +-
 .../goacc/routine-level-of-parallelism-1.c    | 233 ++++++++++++++++--
 .../goacc/routine-level-of-parallelism-2.c    |  73 ++++++
 .../c-c++-common/goacc/routine-nohost-1.c     |  20 ++
 .../c-c++-common/goacc/routine-nohost-2.c     |  97 ++++++++
 10 files changed, 566 insertions(+), 75 deletions(-)
 create mode 100644 gcc/testsuite/c-c++-common/goacc/routine-level-of-parallelism-2.c
 create mode 100644 gcc/testsuite/c-c++-common/goacc/routine-nohost-2.c

diff --git a/gcc/c/c-parser.c b/gcc/c/c-parser.c
index 187a2dec999..3d5cbe76acf 100644
--- a/gcc/c/c-parser.c
+++ b/gcc/c/c-parser.c
@@ -15090,35 +15090,39 @@ c_finish_oacc_routine (struct oacc_routine_data *data, tree fndecl,
       return;
     }
 
-  oacc_verify_routine_clauses (&data->clauses, data->loc);
-
-  if (oacc_get_fn_attrib (fndecl))
+  int compatible
+    = oacc_verify_routine_clauses (fndecl, &data->clauses, data->loc,
+				   "#pragma acc routine");
+  if (compatible < 0)
     {
-      error_at (data->loc,
-		"%<#pragma acc routine%> already applied to %qD", fndecl);
       data->error_seen = true;
       return;
     }
-
-  if (TREE_USED (fndecl) || (!is_defn && DECL_SAVED_TREE (fndecl)))
+  if (compatible > 0)
     {
-      error_at (data->loc,
-		TREE_USED (fndecl)
-		? G_("%<#pragma acc routine%> must be applied before use")
-		: G_("%<#pragma acc routine%> must be applied before "
-		     "definition"));
-      data->error_seen = true;
-      return;
     }
+  else
+    {
+      if (TREE_USED (fndecl) || (!is_defn && DECL_SAVED_TREE (fndecl)))
+	{
+	  error_at (data->loc,
+		    TREE_USED (fndecl)
+		    ? G_("%<#pragma acc routine%> must be applied before use")
+		    : G_("%<#pragma acc routine%> must be applied before"
+			 " definition"));
+	  data->error_seen = true;
+	  return;
+	}
 
-  /* Process the routine's dimension clauses.  */
-  tree dims = oacc_build_routine_dims (data->clauses);
-  oacc_replace_fn_attrib (fndecl, dims);
+      /* Set the routine's level of parallelism.  */
+      tree dims = oacc_build_routine_dims (data->clauses);
+      oacc_replace_fn_attrib (fndecl, dims);
 
-  /* Add an "omp declare target" attribute.  */
-  DECL_ATTRIBUTES (fndecl)
-    = tree_cons (get_identifier ("omp declare target"),
-		 data->clauses, DECL_ATTRIBUTES (fndecl));
+      /* Add an "omp declare target" attribute.  */
+      DECL_ATTRIBUTES (fndecl)
+	= tree_cons (get_identifier ("omp declare target"),
+		     data->clauses, DECL_ATTRIBUTES (fndecl));
+    }
 
   /* Remember that we've used this "#pragma acc routine".  */
   data->fndecl_seen = true;
diff --git a/gcc/cp/parser.c b/gcc/cp/parser.c
index d56105ca177..0d314d63cfd 100644
--- a/gcc/cp/parser.c
+++ b/gcc/cp/parser.c
@@ -38260,36 +38260,42 @@ cp_finalize_oacc_routine (cp_parser *parser, tree fndecl, bool is_defn)
 	  return;
 	}
 
-      oacc_verify_routine_clauses (&parser->oacc_routine->clauses,
-				   parser->oacc_routine->loc);
-
-      if (oacc_get_fn_attrib (fndecl))
+      int compatible
+	= oacc_verify_routine_clauses (fndecl, &parser->oacc_routine->clauses,
+				       parser->oacc_routine->loc,
+				       "#pragma acc routine");
+      if (compatible < 0)
 	{
-	  error_at (parser->oacc_routine->loc,
-		    "%<#pragma acc routine%> already applied to %qD", fndecl);
 	  parser->oacc_routine = NULL;
 	  return;
 	}
-
-      if (TREE_USED (fndecl) || (!is_defn && DECL_SAVED_TREE (fndecl)))
+      if (compatible > 0)
 	{
-	  error_at (parser->oacc_routine->loc,
-		    TREE_USED (fndecl)
-		    ? G_("%<#pragma acc routine%> must be applied before use")
-		    : G_("%<#pragma acc routine%> must be applied before "
-			 "definition"));
-	  parser->oacc_routine = NULL;
-	  return;
 	}
+      else
+	{
+	  if (TREE_USED (fndecl) || (!is_defn && DECL_SAVED_TREE (fndecl)))
+	    {
+	      error_at (parser->oacc_routine->loc,
+			TREE_USED (fndecl)
+			? G_("%<#pragma acc routine%> must be applied before"
+			     " use")
+			: G_("%<#pragma acc routine%> must be applied before"
+			     " definition"));
+	      parser->oacc_routine = NULL;
+	      return;
+	    }
 
-      /* Process the routine's dimension clauses.  */
-      tree dims = oacc_build_routine_dims (parser->oacc_routine->clauses);
-      oacc_replace_fn_attrib (fndecl, dims);
+	  /* Set the routine's level of parallelism.  */
+	  tree dims = oacc_build_routine_dims (parser->oacc_routine->clauses);
+	  oacc_replace_fn_attrib (fndecl, dims);
 
-      /* Add an "omp declare target" attribute.  */
-      DECL_ATTRIBUTES (fndecl)
-	= tree_cons (get_identifier ("omp declare target"),
-		     parser->oacc_routine->clauses, DECL_ATTRIBUTES (fndecl));
+	  /* Add an "omp declare target" attribute.  */
+	  DECL_ATTRIBUTES (fndecl)
+	    = tree_cons (get_identifier ("omp declare target"),
+			 parser->oacc_routine->clauses,
+			 DECL_ATTRIBUTES (fndecl));
+	}
 
       /* Don't unset parser->oacc_routine here: we may still need it to
 	 diagnose wrong usage.  But, remember that we've used this "#pragma acc
diff --git a/gcc/omp-general.c b/gcc/omp-general.c
index 3ea2224957d..5c91ce73a50 100644
--- a/gcc/omp-general.c
+++ b/gcc/omp-general.c
@@ -561,13 +561,17 @@ oacc_set_fn_attrib (tree fn, tree clauses, vec<tree> *args)
 
 /* Verify OpenACC routine clauses.
 
-   The chain of clauses returned will contain exactly one clause specifying the
-   level of parallelism.  */
+   Returns 0 if FNDECL should be marked as an accelerator routine, 1 if it has
+   already been marked in compatible way, and -1 if incompatible.  Upon
+   returning, the chain of clauses will contain exactly one clause specifying
+   the level of parallelism.  */
 
-void
-oacc_verify_routine_clauses (tree *clauses, location_t loc)
+int
+oacc_verify_routine_clauses (tree fndecl, tree *clauses, location_t loc,
+			     const char *routine_str)
 {
   tree c_level = NULL_TREE;
+  tree c_nohost = NULL_TREE;
   tree c_p = NULL_TREE;
   for (tree c = *clauses; c; c_p = c, c = OMP_CLAUSE_CHAIN (c))
     switch (OMP_CLAUSE_CODE (c))
@@ -601,6 +605,8 @@ oacc_verify_routine_clauses (tree *clauses, location_t loc)
 	  }
 	break;
       case OMP_CLAUSE_NOHOST:
+	/* Don't bother with duplicate clauses at this point.  */
+	c_nohost = c;
 	break;
       default:
 	gcc_unreachable ();
@@ -613,6 +619,97 @@ oacc_verify_routine_clauses (tree *clauses, location_t loc)
       OMP_CLAUSE_CHAIN (c_level) = *clauses;
       *clauses = c_level;
     }
+  /* In *clauses, we now have exactly one clause specifying the level of
+     parallelism.  */
+
+  /* Still got some work to do for Fortran...  */
+  if (fndecl == NULL_TREE)
+    return 0;
+
+  tree attr
+    = lookup_attribute ("omp declare target", DECL_ATTRIBUTES (fndecl));
+  if (attr != NULL_TREE)
+    {
+      /* If a "#pragma acc routine" has already been applied, just verify
+	 this one for compatibility.  */
+      /* Collect previous directive's clauses.  */
+      tree c_level_p = NULL_TREE;
+      tree c_nohost_p = NULL_TREE;
+      for (tree c = TREE_VALUE (attr); c; c = OMP_CLAUSE_CHAIN (c))
+	switch (OMP_CLAUSE_CODE (c))
+	  {
+	  case OMP_CLAUSE_GANG:
+	  case OMP_CLAUSE_WORKER:
+	  case OMP_CLAUSE_VECTOR:
+	  case OMP_CLAUSE_SEQ:
+	    gcc_checking_assert (c_level_p == NULL_TREE);
+	    c_level_p = c;
+	    break;
+	  case OMP_CLAUSE_NOHOST:
+	    /* Don't bother with duplicate clauses at this point.  */
+	    c_nohost_p = c;
+	    break;
+	    break;
+	  default:
+	    gcc_unreachable ();
+	  }
+      gcc_checking_assert (c_level_p != NULL_TREE);
+      /* ..., and compare to current directive's, which we've already collected
+	 above.  */
+      tree c_diag;
+      tree c_diag_p;
+      /* Matching level of parallelism?  */
+      if (OMP_CLAUSE_CODE (c_level) != OMP_CLAUSE_CODE (c_level_p))
+	{
+	  c_diag = c_level;
+	  c_diag_p = c_level_p;
+	  goto incompatible;
+	}
+      /* Matching nohost clauses?  */
+      if ((c_nohost == NULL_TREE) != (c_nohost_p == NULL_TREE))
+	{
+	  c_diag = c_nohost;
+	  c_diag_p = c_nohost_p;
+	  goto incompatible;
+	}
+      /* Compatible.  */
+      return 1;
+
+    incompatible:
+      if (c_diag != NULL_TREE)
+	error_at (OMP_CLAUSE_LOCATION (c_diag),
+		  "incompatible %qs clause when applying"
+		  " %<%s%> to %qD, which has already been"
+		  " marked as an accelerator routine",
+		  omp_clause_code_name[OMP_CLAUSE_CODE (c_diag)],
+		  routine_str, fndecl);
+      else if (c_diag_p != NULL_TREE)
+	error_at (loc,
+		  "missing %qs clause when applying"
+		  " %<%s%> to %qD, which has already been"
+		  " marked as an accelerator routine",
+		  omp_clause_code_name[OMP_CLAUSE_CODE (c_diag_p)],
+		  routine_str, fndecl);
+      else
+	gcc_unreachable ();
+      if (c_diag_p != NULL_TREE)
+	inform (OMP_CLAUSE_LOCATION (c_diag_p),
+		"... with %qs clause here",
+		omp_clause_code_name[OMP_CLAUSE_CODE (c_diag_p)]);
+      else
+	{
+	  /* In the front ends, we don't preserve location information for the
+	     OpenACC routine directive itself.  However, that of c_level_p
+	     should be close.  */
+	  location_t loc_routine = OMP_CLAUSE_LOCATION (c_level_p);
+	  inform (loc_routine, "... without %qs clause near to here",
+		  omp_clause_code_name[OMP_CLAUSE_CODE (c_diag)]);
+	}
+      /* Incompatible.  */
+      return -1;
+    }
+
+  return 0;
 }
 
 /*  Process the OpenACC routine's clauses to generate an attribute
diff --git a/gcc/omp-general.h b/gcc/omp-general.h
index e0de288dd9a..1419b66bebb 100644
--- a/gcc/omp-general.h
+++ b/gcc/omp-general.h
@@ -83,7 +83,8 @@ extern int omp_max_simt_vf (void);
 extern tree oacc_launch_pack (unsigned code, tree device, unsigned op);
 extern void oacc_replace_fn_attrib (tree fn, tree dims);
 extern void oacc_set_fn_attrib (tree fn, tree clauses, vec<tree> *args);
-extern void oacc_verify_routine_clauses (tree *, location_t);
+extern int oacc_verify_routine_clauses (tree, tree *, location_t,
+					const char *);
 extern tree oacc_build_routine_dims (tree clauses);
 extern tree oacc_get_fn_attrib (tree fn);
 extern bool offloading_function_p (tree fn);
diff --git a/gcc/testsuite/c-c++-common/goacc/routine-1.c b/gcc/testsuite/c-c++-common/goacc/routine-1.c
index db1322e11ca..23e273c58f4 100644
--- a/gcc/testsuite/c-c++-common/goacc/routine-1.c
+++ b/gcc/testsuite/c-c++-common/goacc/routine-1.c
@@ -20,11 +20,19 @@ void seq (void)
 {
 }
 
-#pragma acc routine nohost
+#pragma acc routine \
+  nohost
 void nohost (void)
 {
 }
 
+#pragma acc routine \
+  nohost
+extern void nohost (void);
+
+#pragma acc routine (nohost) \
+  nohost
+
 int main ()
 {
 #pragma acc kernels num_gangs (32) num_workers (32) vector_length (32)
diff --git a/gcc/testsuite/c-c++-common/goacc/routine-5.c b/gcc/testsuite/c-c++-common/goacc/routine-5.c
index b759db3292d..8b7da7dd453 100644
--- a/gcc/testsuite/c-c++-common/goacc/routine-5.c
+++ b/gcc/testsuite/c-c++-common/goacc/routine-5.c
@@ -150,13 +150,13 @@ void f_static_assert();
 
 #pragma acc routine seq
 __extension__ extern void ex1();
-#pragma acc routine (ex1) /* { dg-error ".#pragma acc routine. already applied to .\[void \]*ex1" } */
+#pragma acc routine (ex1) worker /* { dg-error "has already been marked as an accelerator routine" } */
 
 #pragma acc routine seq
 __extension__ __extension__ __extension__ __extension__ __extension__ void ex2()
 {
 }
-#pragma acc routine (ex2) /* { dg-error ".#pragma acc routine. already applied to .\[void \]*ex2" } */
+#pragma acc routine (ex2) worker /* { dg-error "has already been marked as an accelerator routine" } */
 
 #pragma acc routine seq /* { dg-error ".#pragma acc routine. not immediately followed by function declaration or definition" } */
 __extension__ int ex3;
diff --git a/gcc/testsuite/c-c++-common/goacc/routine-level-of-parallelism-1.c b/gcc/testsuite/c-c++-common/goacc/routine-level-of-parallelism-1.c
index d71d6e088f7..d1258750ae0 100644
--- a/gcc/testsuite/c-c++-common/goacc/routine-level-of-parallelism-1.c
+++ b/gcc/testsuite/c-c++-common/goacc/routine-level-of-parallelism-1.c
@@ -42,10 +42,10 @@ void s_2 (void)
 void g_3 (void)
 {
 }
-#pragma acc routine (g_3) /* { dg-error ".#pragma acc routine. already applied to .\[void \]*g_3." } */ \
+#pragma acc routine (g_3) \
   gang \
   seq /* { dg-error ".seq. specifies a conflicting level of parallelism" } */
-#pragma acc routine (g_3) /* { dg-error ".#pragma acc routine. already applied to .\[void \]*g_3." } */ \
+#pragma acc routine (g_3) \
   gang \
   vector /* { dg-error ".vector. specifies a conflicting level of parallelism" } */
 
@@ -53,10 +53,10 @@ extern void w_3 (void);
 #pragma acc routine (w_3) \
   worker \
   vector /* { dg-error ".vector. specifies a conflicting level of parallelism" } */
-#pragma acc routine (w_3) /* { dg-error ".#pragma acc routine. already applied to .\[void \]*w_3." } */ \
+#pragma acc routine (w_3) \
   worker \
   gang /* { dg-error ".gang. specifies a conflicting level of parallelism" } */
-#pragma acc routine (w_3) /* { dg-error ".#pragma acc routine. already applied to .\[void \]*w_3." } */ \
+#pragma acc routine (w_3) \
   worker \
   seq /* { dg-error ".seq. specifies a conflicting level of parallelism" } */
 
@@ -66,10 +66,10 @@ extern void w_3 (void);
 void v_3 (void)
 {
 }
-#pragma acc routine (v_3) /* { dg-error ".#pragma acc routine. already applied to .\[void \]*v_3." } */ \
+#pragma acc routine (v_3) \
   vector \
   worker /* { dg-error ".worker. specifies a conflicting level of parallelism" } */
-#pragma acc routine (v_3) /* { dg-error ".#pragma acc routine. already applied to .\[void \]*v_3." } */ \
+#pragma acc routine (v_3) \
   vector \
   gang /* { dg-error ".gang. specifies a conflicting level of parallelism" } */
 
@@ -77,10 +77,10 @@ extern void s_3 (void);
 #pragma acc routine (s_3) \
   seq \
   gang /* { dg-error ".gang. specifies a conflicting level of parallelism" } */
-#pragma acc routine (s_3) /* { dg-error ".#pragma acc routine. already applied to .\[void \]*s_3." } */ \
+#pragma acc routine (s_3) \
   seq \
   vector /* { dg-error ".vector. specifies a conflicting level of parallelism" } */
-#pragma acc routine (s_3) /* { dg-error ".#pragma acc routine. already applied to .\[void \]*s_3." } */ \
+#pragma acc routine (s_3) \
   seq \
   worker /* { dg-error ".worker. specifies a conflicting level of parallelism" } */
 
@@ -91,12 +91,12 @@ extern void s_3 (void);
   vector /* { dg-error ".vector. specifies a conflicting level of parallelism" } */ \
   seq /* { dg-error ".seq. specifies a conflicting level of parallelism" } */
 extern void g_4 (void);
-#pragma acc routine (g_4) /* { dg-error ".#pragma acc routine. already applied to .\[void \]*g_4." } */ \
+#pragma acc routine (g_4) \
   gang gang gang /* { dg-error "too many 'gang' clauses" } */ \
   vector /* { dg-error ".vector. specifies a conflicting level of parallelism" } */ \
   worker /* { dg-error ".worker. specifies a conflicting level of parallelism" } */ \
   seq /* { dg-error ".seq. specifies a conflicting level of parallelism" } */
-#pragma acc routine (g_4) /* { dg-error ".#pragma acc routine. already applied to .\[void \]*g_4." } */ \
+#pragma acc routine (g_4) \
   gang gang gang /* { dg-error "too many 'gang' clauses" } */ \
   seq /* { dg-error ".seq. specifies a conflicting level of parallelism" } */ \
   vector /* { dg-error ".vector. specifies a conflicting level of parallelism" } */ \
@@ -108,12 +108,12 @@ extern void w_4 (void);
   seq /* { dg-error ".seq. specifies a conflicting level of parallelism" } */ \
   vector /* { dg-error ".vector. specifies a conflicting level of parallelism" } */ \
   gang /* { dg-error ".gang. specifies a conflicting level of parallelism" } */
-#pragma acc routine (w_4) /* { dg-error ".#pragma acc routine. already applied to .\[void \]*w_4." } */ \
+#pragma acc routine (w_4) \
   worker worker worker /* { dg-error "too many 'worker' clauses" } */ \
   vector /* { dg-error ".vector. specifies a conflicting level of parallelism" } */ \
   seq /* { dg-error ".seq. specifies a conflicting level of parallelism" } */ \
   gang /* { dg-error ".gang. specifies a conflicting level of parallelism" } */
-#pragma acc routine (w_4) /* { dg-error ".#pragma acc routine. already applied to .\[void \]*w_4." } */ \
+#pragma acc routine (w_4) \
   worker worker worker /* { dg-error "too many 'worker' clauses" } */ \
   seq /* { dg-error ".seq. specifies a conflicting level of parallelism" } */ \
   gang /* { dg-error ".gang. specifies a conflicting level of parallelism" } */ \
@@ -127,12 +127,12 @@ extern void w_4 (void);
 void v_4 (void)
 {
 }
-#pragma acc routine (v_4) /* { dg-error ".#pragma acc routine. already applied to .\[void \]*v_4." } */ \
+#pragma acc routine (v_4) \
   vector vector vector /* { dg-error "too many 'vector' clauses" } */ \
   seq /* { dg-error ".seq. specifies a conflicting level of parallelism" } */ \
   worker /* { dg-error ".worker. specifies a conflicting level of parallelism" } */ \
   gang /* { dg-error ".gang. specifies a conflicting level of parallelism" } */
-#pragma acc routine (v_4) /* { dg-error ".#pragma acc routine. already applied to .\[void \]*v_4." } */ \
+#pragma acc routine (v_4) \
   vector vector vector /* { dg-error "too many 'vector' clauses" } */ \
   gang /* { dg-error ".gang. specifies a conflicting level of parallelism" } */ \
   seq /* { dg-error ".seq. specifies a conflicting level of parallelism" } */ \
@@ -146,12 +146,12 @@ void v_4 (void)
 void s_4 (void)
 {
 }
-#pragma acc routine (s_4) /* { dg-error ".#pragma acc routine. already applied to .\[void \]*s_4." } */ \
+#pragma acc routine (s_4) \
   seq seq seq /* { dg-error "too many 'seq' clauses" } */ \
   vector /* { dg-error ".vector. specifies a conflicting level of parallelism" } */ \
   gang /* { dg-error ".gang. specifies a conflicting level of parallelism" } */ \
   worker /* { dg-error ".worker. specifies a conflicting level of parallelism" } */
-#pragma acc routine (s_4) /* { dg-error ".#pragma acc routine. already applied to .\[void \]*s_4." } */ \
+#pragma acc routine (s_4) \
   seq seq seq /* { dg-error "too many 'seq' clauses" } */ \
   worker /* { dg-error ".worker. specifies a conflicting level of parallelism" } */ \
   vector /* { dg-error ".vector. specifies a conflicting level of parallelism" } */ \
@@ -169,7 +169,7 @@ void s_4 (void)
 void g_5 (void)
 {
 }
-#pragma acc routine (g_5) /* { dg-error ".#pragma acc routine. already applied to .\[void \]*g_5." } */ \
+#pragma acc routine (g_5) \
   gang gang gang /* { dg-error "too many 'gang' clauses" } */ \
   vector vector vector vector /* { dg-error "too many 'vector' clauses" } */ \
   /* { dg-error ".vector. specifies a conflicting level of parallelism" "" { target *-*-* } .-1 } */ \
@@ -177,7 +177,7 @@ void g_5 (void)
   /* { dg-error ".worker. specifies a conflicting level of parallelism" "" { target *-*-* } .-1 } */ \
   seq seq seq seq seq seq /* { dg-error "too many 'seq' clauses" } */ \
   /* { dg-error ".seq. specifies a conflicting level of parallelism" "" { target *-*-* } .-1 } */
-#pragma acc routine (g_5) /* { dg-error ".#pragma acc routine. already applied to .\[void \]*g_5." } */ \
+#pragma acc routine (g_5) \
   gang gang gang /* { dg-error "too many 'gang' clauses" } */ \
   seq seq seq seq seq /* { dg-error "too many 'seq' clauses" } */ \
   /* { dg-error ".seq. specifies a conflicting level of parallelism" "" { target *-*-* } .-1 } */ \
@@ -195,7 +195,7 @@ void g_5 (void)
   gang gang /* { dg-error "too many 'gang' clauses" } */ \
   /* { dg-error ".gang. specifies a conflicting level of parallelism" "" { target *-*-* } .-1 } */
 extern void w_5 (void);
-#pragma acc routine (w_5) /* { dg-error ".#pragma acc routine. already applied to .\[void \]*w_5." } */ \
+#pragma acc routine (w_5) \
   worker worker worker /* { dg-error "too many 'worker' clauses" } */ \
   vector vector vector /* { dg-error "too many 'vector' clauses" } */ \
   /* { dg-error ".vector. specifies a conflicting level of parallelism" "" { target *-*-* } .-1 } */ \
@@ -203,7 +203,7 @@ extern void w_5 (void);
   /* { dg-error ".seq. specifies a conflicting level of parallelism" "" { target *-*-* } .-1 } */ \
   gang gang /* { dg-error "too many 'gang' clauses" } */ \
   /* { dg-error ".gang. specifies a conflicting level of parallelism" "" { target *-*-* } .-1 } */
-#pragma acc routine (w_5) /* { dg-error ".#pragma acc routine. already applied to .\[void \]*w_5." } */ \
+#pragma acc routine (w_5) \
   worker worker worker /* { dg-error "too many 'worker' clauses" } */ \
   seq seq /* { dg-error "too many 'seq' clauses" } */ \
   /* { dg-error ".seq. specifies a conflicting level of parallelism" "" { target *-*-* } .-1 } */ \
@@ -221,7 +221,7 @@ extern void w_5 (void);
   gang gang /* { dg-error "too many 'gang' clauses" } */ \
   /* { dg-error ".gang. specifies a conflicting level of parallelism" "" { target *-*-* } .-1 } */
 extern void v_5 (void);
-#pragma acc routine (v_5) /* { dg-error ".#pragma acc routine. already applied to .\[void \]*v_5." } */ \
+#pragma acc routine (v_5) \
   vector vector vector /* { dg-error "too many 'vector' clauses" } */ \
   seq seq seq seq seq seq /* { dg-error "too many 'seq' clauses" } */ \
   /* { dg-error ".seq. specifies a conflicting level of parallelism" "" { target *-*-* } .-1 } */ \
@@ -229,7 +229,7 @@ extern void v_5 (void);
   /* { dg-error ".worker. specifies a conflicting level of parallelism" "" { target *-*-* } .-1 } */ \
   gang gang /* { dg-error "too many 'gang' clauses" } */ \
   /* { dg-error ".gang. specifies a conflicting level of parallelism" "" { target *-*-* } .-1 } */
-#pragma acc routine (v_5) /* { dg-error ".#pragma acc routine. already applied to .\[void \]*v_5." } */ \
+#pragma acc routine (v_5) \
   vector vector vector /* { dg-error "too many 'vector' clauses" } */ \
   gang gang /* { dg-error "too many 'gang' clauses" } */ \
   /* { dg-error ".gang. specifies a conflicting level of parallelism" "" { target *-*-* } .-1 } */ \
@@ -247,7 +247,7 @@ extern void s_5 (void);
   /* { dg-error ".worker. specifies a conflicting level of parallelism" "" { target *-*-* } .-1 } */ \
   gang gang /* { dg-error "too many 'gang' clauses" } */ \
   /* { dg-error ".gang. specifies a conflicting level of parallelism" "" { target *-*-* } .-1 } */
-#pragma acc routine (s_5) /* { dg-error ".#pragma acc routine. already applied to .\[void \]*s_5." } */ \
+#pragma acc routine (s_5) \
   seq seq seq /* { dg-error "too many 'seq' clauses" } */ \
   vector vector vector vector /* { dg-error "too many 'vector' clauses" } */ \
   /* { dg-error ".vector. specifies a conflicting level of parallelism" "" { target *-*-* } .-1 } */ \
@@ -255,7 +255,7 @@ extern void s_5 (void);
   /* { dg-error ".gang. specifies a conflicting level of parallelism" "" { target *-*-* } .-1 } */ \
   worker worker worker /* { dg-error "too many 'worker' clauses" } */ \
   /* { dg-error ".worker. specifies a conflicting level of parallelism" "" { target *-*-* } .-1 } */
-#pragma acc routine (s_5) /* { dg-error ".#pragma acc routine. already applied to .\[void \]*s_5." } */ \
+#pragma acc routine (s_5) \
   seq seq seq /* { dg-error "too many 'seq' clauses" } */ \
   worker worker /* { dg-error "too many 'worker' clauses" } */ \
   /* { dg-error ".worker. specifies a conflicting level of parallelism" "" { target *-*-* } .-1 } */ \
@@ -263,3 +263,188 @@ extern void s_5 (void);
   /* { dg-error ".vector. specifies a conflicting level of parallelism" "" { target *-*-* } .-1 } */ \
   gang gang /* { dg-error "too many 'gang' clauses" } */ \
   /* { dg-error ".gang. specifies a conflicting level of parallelism" "" { target *-*-* } .-1 } */
+
+
+/* Like the *_5 tests, but with the order of clauses changed in the second and
+   following routine directives for the specific *_5 function.  */
+
+#pragma acc routine \
+  gang gang gang /* { dg-error "too many 'gang' clauses" } */ \
+  worker worker /* { dg-error "too many 'worker' clauses" } */ \
+  /* { dg-error ".worker. specifies a conflicting level of parallelism" "" { target *-*-* } .-1 } */ \
+  vector vector vector /* { dg-error "too many 'vector' clauses" } */ \
+  /* { dg-error ".vector. specifies a conflicting level of parallelism" "" { target *-*-* } .-1 } */ \
+  seq seq seq seq seq /* { dg-error "too many 'seq' clauses" } */ \
+  /* { dg-error ".seq. specifies a conflicting level of parallelism" "" { target *-*-* } .-1 } */
+void g_6 (void)
+{
+}
+#pragma acc routine (g_6) \
+  vector vector vector vector /* { dg-error "too many 'vector' clauses" } */ \
+  /* { dg-error "incompatible .vector. clause when applying .#pragma acc routine. to .\[void \]*g_6\[\\(\\)\]*., which has already been marked as an accelerator routine" "" { target *-*-* } .-1 } */ \
+  gang gang gang /* { dg-error "too many 'gang' clauses" } */ \
+  /* { dg-error ".gang. specifies a conflicting level of parallelism" "" { target *-*-* } .-1 } */ \
+  worker worker worker /* { dg-error "too many 'worker' clauses" } */ \
+  /* { dg-error ".worker. specifies a conflicting level of parallelism" "" { target *-*-* } .-1 } */ \
+  seq seq seq seq seq seq /* { dg-error "too many 'seq' clauses" } */ \
+  /* { dg-error ".seq. specifies a conflicting level of parallelism" "" { target *-*-* } .-1 } */
+#pragma acc routine (g_6) \
+  seq seq seq seq seq /* { dg-error "too many 'seq' clauses" } */ \
+  /* { dg-error "incompatible .seq. clause when applying .#pragma acc routine. to .\[void \]*g_6\[\\(\\)\]*., which has already been marked as an accelerator routine" "" { target *-*-* } .-1 } */ \
+  gang gang gang /* { dg-error "too many 'gang' clauses" } */ \
+  /* { dg-error ".gang. specifies a conflicting level of parallelism" "" { target *-*-* } .-1 } */ \
+  vector vector vector vector /* { dg-error "too many 'vector' clauses" } */ \
+  /* { dg-error ".vector. specifies a conflicting level of parallelism" "" { target *-*-* } .-1 } */ \
+  worker worker worker worker /* { dg-error "too many 'worker' clauses" } */ \
+  /* { dg-error ".worker. specifies a conflicting level of parallelism" "" { target *-*-* } .-1 } */
+
+#pragma acc routine \
+  worker worker worker /* { dg-error "too many 'worker' clauses" } */ \
+  seq seq seq seq /* { dg-error "too many 'seq' clauses" } */ \
+  /* { dg-error ".seq. specifies a conflicting level of parallelism" "" { target *-*-* } .-1 } */ \
+  vector vector vector vector vector /* { dg-error "too many 'vector' clauses" } */ \
+  /* { dg-error ".vector. specifies a conflicting level of parallelism" "" { target *-*-* } .-1 } */ \
+  gang gang /* { dg-error "too many 'gang' clauses" } */ \
+  /* { dg-error ".gang. specifies a conflicting level of parallelism" "" { target *-*-* } .-1 } */
+extern void w_6 (void);
+#pragma acc routine (w_6) \
+  vector vector vector /* { dg-error "too many 'vector' clauses" } */ \
+  /* { dg-error "incompatible .vector. clause when applying .#pragma acc routine. to .\[void \]*w_6\[\\(\\)\]*., which has already been marked as an accelerator routine" "" { target *-*-* } .-1 } */ \
+  worker worker worker /* { dg-error "too many 'worker' clauses" } */ \
+  /* { dg-error ".worker. specifies a conflicting level of parallelism" "" { target *-*-* } .-1 } */ \
+  seq seq seq /* { dg-error "too many 'seq' clauses" } */ \
+  /* { dg-error ".seq. specifies a conflicting level of parallelism" "" { target *-*-* } .-1 } */ \
+  gang gang /* { dg-error "too many 'gang' clauses" } */ \
+  /* { dg-error ".gang. specifies a conflicting level of parallelism" "" { target *-*-* } .-1 } */
+#pragma acc routine (w_6) \
+  seq seq /* { dg-error "too many 'seq' clauses" } */ \
+  /* { dg-error "incompatible .seq. clause when applying .#pragma acc routine. to .\[void \]*w_6\[\\(\\)\]*., which has already been marked as an accelerator routine" "" { target *-*-* } .-1 } */ \
+  worker worker worker /* { dg-error "too many 'worker' clauses" } */ \
+  /* { dg-error ".worker. specifies a conflicting level of parallelism" "" { target *-*-* } .-1 } */ \
+  gang gang /* { dg-error "too many 'gang' clauses" } */ \
+  /* { dg-error ".gang. specifies a conflicting level of parallelism" "" { target *-*-* } .-1 } */ \
+  vector vector vector vector /* { dg-error "too many 'vector' clauses" } */ \
+  /* { dg-error ".vector. specifies a conflicting level of parallelism" "" { target *-*-* } .-1 } */
+
+#pragma acc routine \
+  vector vector vector /* { dg-error "too many 'vector' clauses" } */ \
+  worker worker worker /* { dg-error "too many 'worker' clauses" } */ \
+  /* { dg-error ".worker. specifies a conflicting level of parallelism" "" { target *-*-* } .-1 } */ \
+  seq seq seq seq seq seq /* { dg-error "too many 'seq' clauses" } */ \
+  /* { dg-error ".seq. specifies a conflicting level of parallelism" "" { target *-*-* } .-1 } */ \
+  gang gang /* { dg-error "too many 'gang' clauses" } */ \
+  /* { dg-error ".gang. specifies a conflicting level of parallelism" "" { target *-*-* } .-1 } */
+extern void v_6 (void);
+#pragma acc routine (v_6) \
+  seq seq seq seq seq seq /* { dg-error "too many 'seq' clauses" } */ \
+  /* { dg-error "incompatible .seq. clause when applying .#pragma acc routine. to .\[void \]*v_6\[\\(\\)\]*., which has already been marked as an accelerator routine" "" { target *-*-* } .-1 } */ \
+  vector vector vector /* { dg-error "too many 'vector' clauses" } */ \
+  /* { dg-error ".vector. specifies a conflicting level of parallelism" "" { target *-*-* } .-1 } */ \
+  worker worker /* { dg-error "too many 'worker' clauses" } */ \
+  /* { dg-error ".worker. specifies a conflicting level of parallelism" "" { target *-*-* } .-1 } */ \
+  gang gang /* { dg-error "too many 'gang' clauses" } */ \
+  /* { dg-error ".gang. specifies a conflicting level of parallelism" "" { target *-*-* } .-1 } */
+#pragma acc routine (v_6) \
+  gang gang /* { dg-error "too many 'gang' clauses" } */ \
+  /* { dg-error "incompatible .gang. clause when applying .#pragma acc routine. to .\[void \]*v_6\[\\(\\)\]*., which has already been marked as an accelerator routine" "" { target *-*-* } .-1 } */ \
+  vector vector vector /* { dg-error "too many 'vector' clauses" } */ \
+  /* { dg-error ".vector. specifies a conflicting level of parallelism" "" { target *-*-* } .-1 } */ \
+  seq seq seq seq seq /* { dg-error "too many 'seq' clauses" } */ \
+  /* { dg-error ".seq. specifies a conflicting level of parallelism" "" { target *-*-* } .-1 } */ \
+  worker worker /* { dg-error "too many 'worker' clauses" } */ \
+  /* { dg-error ".worker. specifies a conflicting level of parallelism" "" { target *-*-* } .-1 } */
+
+extern void s_6 (void);
+#pragma acc routine (s_6) \
+  seq seq seq /* { dg-error "too many 'seq' clauses" } */ \
+  vector vector vector vector /* { dg-error "too many 'vector' clauses" } */ \
+  /* { dg-error ".vector. specifies a conflicting level of parallelism" "" { target *-*-* } .-1 } */ \
+  worker worker worker worker worker /* { dg-error "too many 'worker' clauses" } */ \
+  /* { dg-error ".worker. specifies a conflicting level of parallelism" "" { target *-*-* } .-1 } */ \
+  gang gang /* { dg-error "too many 'gang' clauses" } */ \
+  /* { dg-error ".gang. specifies a conflicting level of parallelism" "" { target *-*-* } .-1 } */
+#pragma acc routine (s_6) \
+  vector vector vector vector /* { dg-error "too many 'vector' clauses" } */ \
+  /* { dg-error "incompatible .vector. clause when applying .#pragma acc routine. to .\[void \]*s_6\[\\(\\)\]*., which has already been marked as an accelerator routine" "" { target *-*-* } .-1 } */ \
+  seq seq seq /* { dg-error "too many 'seq' clauses" } */ \
+  /* { dg-error ".seq. specifies a conflicting level of parallelism" "" { target *-*-* } .-1 } */ \
+  gang gang /* { dg-error "too many 'gang' clauses" } */ \
+  /* { dg-error ".gang. specifies a conflicting level of parallelism" "" { target *-*-* } .-1 } */ \
+  worker worker worker /* { dg-error "too many 'worker' clauses" } */ \
+  /* { dg-error ".worker. specifies a conflicting level of parallelism" "" { target *-*-* } .-1 } */
+#pragma acc routine (s_6) \
+  worker worker /* { dg-error "too many 'worker' clauses" } */ \
+  /* { dg-error "incompatible .worker. clause when applying .#pragma acc routine. to .\[void \]*s_6\[\\(\\)\]*., which has already been marked as an accelerator routine" "" { target *-*-* } .-1 } */ \
+  seq seq seq /* { dg-error "too many 'seq' clauses" } */ \
+  /* { dg-error ".seq. specifies a conflicting level of parallelism" "" { target *-*-* } .-1 } */ \
+  vector vector vector vector /* { dg-error "too many 'vector' clauses" } */ \
+  /* { dg-error ".vector. specifies a conflicting level of parallelism" "" { target *-*-* } .-1 } */ \
+  gang gang /* { dg-error "too many 'gang' clauses" } */ \
+  /* { dg-error ".gang. specifies a conflicting level of parallelism" "" { target *-*-* } .-1 } */
+
+
+/* Like the *_6 tests, but without all the duplicate clauses, so that the
+   routine directives are valid in isolation.  */
+
+#pragma acc routine \
+  gang
+void g_7 (void)
+{
+}
+#pragma acc routine (g_7) \
+  vector /* { dg-error "incompatible .vector. clause when applying .#pragma acc routine. to .\[void \]*g_7\[\\(\\)\]*., which has already been marked as an accelerator routine" } */
+#pragma acc routine (g_7) \
+  seq /* { dg-error "incompatible .seq. clause when applying .#pragma acc routine. to .\[void \]*g_7\[\\(\\)\]*., which has already been marked as an accelerator routine" } */
+
+#pragma acc routine \
+  worker
+extern void w_7 (void);
+#pragma acc routine (w_7) \
+  vector /* { dg-error "incompatible .vector. clause when applying .#pragma acc routine. to .\[void \]*w_7\[\\(\\)\]*., which has already been marked as an accelerator routine" } */
+#pragma acc routine (w_7) \
+  seq /* { dg-error "incompatible .seq. clause when applying .#pragma acc routine. to .\[void \]*w_7\[\\(\\)\]*., which has already been marked as an accelerator routine" } */
+
+#pragma acc routine \
+  vector
+extern void v_7 (void);
+#pragma acc routine (v_7) \
+  seq /* { dg-error "incompatible .seq. clause when applying .#pragma acc routine. to .\[void \]*v_7\[\\(\\)\]*., which has already been marked as an accelerator routine" } */
+#pragma acc routine (v_7) \
+  gang /* { dg-error "incompatible .gang. clause when applying .#pragma acc routine. to .\[void \]*v_7\[\\(\\)\]*., which has already been marked as an accelerator routine" } */
+
+extern void s_7 (void);
+#pragma acc routine (s_7) \
+  seq
+#pragma acc routine (s_7) \
+  vector /* { dg-error "incompatible .vector. clause when applying .#pragma acc routine. to .\[void \]*s_7\[\\(\\)\]*., which has already been marked as an accelerator routine" } */
+#pragma acc routine (s_7) \
+  worker /* { dg-error "incompatible .worker. clause when applying .#pragma acc routine. to .\[void \]*s_7\[\\(\\)\]*., which has already been marked as an accelerator routine" } */
+
+
+/* Test cases for implicit seq clause.  */
+
+#pragma acc routine \
+  gang
+void g_8 (void)
+{
+}
+#pragma acc routine (g_8) /* { dg-error "incompatible .seq. clause when applying .#pragma acc routine. to .\[void \]*g_8\[\\(\\)\]*., which has already been marked as an accelerator routine" } */
+
+#pragma acc routine \
+  worker
+extern void w_8 (void);
+#pragma acc routine (w_8) /* { dg-error "incompatible .seq. clause when applying .#pragma acc routine. to .\[void \]*w_8\[\\(\\)\]*., which has already been marked as an accelerator routine" } */
+
+#pragma acc routine \
+  vector
+extern void v_8 (void);
+#pragma acc routine (v_8) /* { dg-error "incompatible .seq. clause when applying .#pragma acc routine. to .\[void \]*v_8\[\\(\\)\]*., which has already been marked as an accelerator routine" } */
+
+extern void s_8 (void);
+#pragma acc routine (s_8)
+#pragma acc routine (s_8) \
+  vector /* { dg-error "incompatible .vector. clause when applying .#pragma acc routine. to .\[void \]*s_8\[\\(\\)\]*., which has already been marked as an accelerator routine" } */
+#pragma acc routine (s_8) \
+  gang /* { dg-error "incompatible .gang. clause when applying .#pragma acc routine. to .\[void \]*s_8\[\\(\\)\]*., which has already been marked as an accelerator routine" } */
+#pragma acc routine (s_8) \
+  worker /* { dg-error "incompatible .worker. clause when applying .#pragma acc routine. to .\[void \]*s_8\[\\(\\)\]*., which has already been marked as an accelerator routine" } */
diff --git a/gcc/testsuite/c-c++-common/goacc/routine-level-of-parallelism-2.c b/gcc/testsuite/c-c++-common/goacc/routine-level-of-parallelism-2.c
new file mode 100644
index 00000000000..36626229299
--- /dev/null
+++ b/gcc/testsuite/c-c++-common/goacc/routine-level-of-parallelism-2.c
@@ -0,0 +1,73 @@
+/* Test various aspects of clauses specifying compatible levels of
+   parallelism with the OpenACC routine directive.  The Fortran counterpart is
+   ../../gfortran.dg/goacc/routine-level-of-parallelism-2.f.  */
+
+#pragma acc routine gang
+void g_1 (void) /* { dg-warning "region is gang partitioned but does not contain gang partitioned code" } */
+/* { dg-bogus "region is worker partitioned but does not contain worker partitioned code" "worker partitioned" { xfail *-*-* } .-1 } */
+/* { dg-bogus "region is vector partitioned but does not contain vector partitioned code" "worker partitioned" { xfail *-*-* } .-2 } */
+{
+}
+#pragma acc routine (g_1) gang
+#pragma acc routine (g_1) gang
+
+
+extern void w_1 (void);
+#pragma acc routine (w_1) worker
+#pragma acc routine (w_1) worker
+#pragma acc routine (w_1) worker
+
+
+#pragma acc routine vector
+extern void v_1 (void);
+#pragma acc routine (v_1) vector
+#pragma acc routine (v_1) vector
+
+
+/* Also test the implicit seq clause.  */
+
+#pragma acc routine seq
+extern void s_1_1 (void);
+#pragma acc routine (s_1_1)
+#pragma acc routine (s_1_1) seq
+#pragma acc routine (s_1_1)
+#pragma acc routine (s_1_1) seq
+
+#pragma acc routine
+extern void s_1_2 (void);
+#pragma acc routine (s_1_2)
+#pragma acc routine (s_1_2) seq
+#pragma acc routine (s_1_2)
+#pragma acc routine (s_1_2) seq
+
+extern void s_2_1 (void);
+#pragma acc routine (s_2_1) seq
+#pragma acc routine (s_2_1)
+#pragma acc routine (s_2_1) seq
+#pragma acc routine (s_2_1)
+#pragma acc routine (s_2_1) seq
+
+extern void s_2_2 (void);
+#pragma acc routine (s_2_2)
+#pragma acc routine (s_2_2)
+#pragma acc routine (s_2_2) seq
+#pragma acc routine (s_2_2)
+#pragma acc routine (s_2_2) seq
+
+#pragma acc routine seq
+void s_3_1 (void)
+{
+}
+#pragma acc routine (s_3_1)
+#pragma acc routine (s_3_1) seq
+#pragma acc routine (s_3_1)
+#pragma acc routine (s_3_1) seq
+
+#pragma acc routine
+void s_3_2 (void)
+{
+}
+#pragma acc routine (s_3_2)
+#pragma acc routine (s_3_2) seq
+#pragma acc routine (s_3_2)
+#pragma acc routine (s_3_2) seq
diff --git a/gcc/testsuite/c-c++-common/goacc/routine-nohost-1.c b/gcc/testsuite/c-c++-common/goacc/routine-nohost-1.c
index 9baa56cb206..17d6b0387e3 100644
--- a/gcc/testsuite/c-c++-common/goacc/routine-nohost-1.c
+++ b/gcc/testsuite/c-c++-common/goacc/routine-nohost-1.c
@@ -9,13 +9,27 @@ int THREE(void)
   return 3;
 }
 
+#pragma acc routine (THREE) nohost
+
+#pragma acc routine nohost
+extern int THREE(void);
+
+
 #pragma acc routine nohost
 extern void NOTHING(void);
 
+#pragma acc routine (NOTHING) nohost
+
 void NOTHING(void)
 {
 }
 
+#pragma acc routine nohost
+extern void NOTHING(void);
+
+#pragma acc routine (NOTHING) nohost
+
+
 extern float ADD(float, float);
 
 #pragma acc routine (ADD) nohost
@@ -25,4 +39,10 @@ float ADD(float x, float y)
   return x + y;
 }
 
+#pragma acc routine nohost
+extern float ADD(float, float);
+
+#pragma acc routine (ADD) nohost
+
+
 /* { dg-final { scan-tree-dump-times "Discarding function" 3 "oaccdevlow" } } */
diff --git a/gcc/testsuite/c-c++-common/goacc/routine-nohost-2.c b/gcc/testsuite/c-c++-common/goacc/routine-nohost-2.c
new file mode 100644
index 00000000000..7402bfc1b41
--- /dev/null
+++ b/gcc/testsuite/c-c++-common/goacc/routine-nohost-2.c
@@ -0,0 +1,97 @@
+/* Test invalid usage of the nohost clause for OpenACC routine directive.
+   Exercising different variants for declaring routines.  */
+
+#pragma acc routine
+int THREE_1(void)
+{
+  return 3;
+}
+
+#pragma acc routine (THREE_1) \
+  nohost /* { dg-error "incompatible .nohost. clause when applying .#pragma acc routine. to .\[int \]*THREE_1\[\\(\\)\]*., which has already been marked as an accelerator routine" } */
+
+#pragma acc routine \
+  nohost /* { dg-error "incompatible .nohost. clause when applying .#pragma acc routine. to .\[int \]*THREE_1\[\\(\\)\]*., which has already been marked as an accelerator routine" } */
+extern int THREE_1(void);
+
+
+#pragma acc routine
+extern void NOTHING_1(void);
+
+#pragma acc routine (NOTHING_1) \
+  nohost /* { dg-error "incompatible .nohost. clause when applying .#pragma acc routine. to .\[void \]*NOTHING_1\[\\(\\)\]*., which has already been marked as an accelerator routine" } */
+
+void NOTHING_1(void)
+{
+}
+
+#pragma acc routine \
+  nohost /* { dg-error "incompatible .nohost. clause when applying .#pragma acc routine. to .\[void \]*NOTHING_1\[\\(\\)\]*., which has already been marked as an accelerator routine" } */
+extern void NOTHING_1(void);
+
+#pragma acc routine (NOTHING_1) \
+  nohost /* { dg-error "incompatible .nohost. clause when applying .#pragma acc routine. to .\[void \]*NOTHING_1\[\\(\\)\]*., which has already been marked as an accelerator routine" } */
+
+
+extern float ADD_1(float, float);
+
+#pragma acc routine (ADD_1)
+
+float ADD_1(float x, float y)
+{
+  return x + y;
+}
+
+#pragma acc routine \
+  nohost /* { dg-error "incompatible .nohost. clause when applying .#pragma acc routine. to .\[float \]*ADD_1\[\\(float, \\)\]*., which has already been marked as an accelerator routine" } */
+extern float ADD_1(float, float);
+
+#pragma acc routine (ADD_1) \
+  nohost /* { dg-error "incompatible .nohost. clause when applying .#pragma acc routine. to .\[float \]*ADD_1\[\\(float, \\)\]*., which has already been marked as an accelerator routine" } */
+
+
+/* The same again, but with/without nohost reversed.  */
+
+#pragma acc routine \
+  nohost
+int THREE_2(void)
+{
+  return 3;
+}
+
+#pragma acc routine (THREE_2) /* { dg-error "missing .nohost. clause when applying .#pragma acc routine. to .\[int \]*THREE_2\[\\(\\)\]*., which has already been marked as an accelerator routine" } */
+
+#pragma acc routine /* { dg-error "missing .nohost. clause when applying .#pragma acc routine. to .\[int \]*THREE_2\[\\(\\)\]*., which has already been marked as an accelerator routine" } */
+extern int THREE_2(void);
+
+
+#pragma acc routine \
+  nohost
+extern void NOTHING_2(void);
+
+#pragma acc routine (NOTHING_2) /* { dg-error "missing .nohost. clause when applying .#pragma acc routine. to .\[void \]*NOTHING_2\[\\(\\)\]*., which has already been marked as an accelerator routine" } */
+
+void NOTHING_2(void)
+{
+}
+
+#pragma acc routine /* { dg-error "missing .nohost. clause when applying .#pragma acc routine. to .\[void \]*NOTHING_2\[\\(\\)\]*., which has already been marked as an accelerator routine" } */
+extern void NOTHING_2(void);
+
+#pragma acc routine (NOTHING_2) /* { dg-error "missing .nohost. clause when applying .#pragma acc routine. to .\[void \]*NOTHING_2\[\\(\\)\]*., which has already been marked as an accelerator routine" } */
+
+
+extern float ADD_2(float, float);
+
+#pragma acc routine (ADD_2) \
+  nohost
+
+float ADD_2(float x, float y)
+{
+  return x + y;
+}
+
+#pragma acc routine /* { dg-error "missing .nohost. clause when applying .#pragma acc routine. to .\[float \]*ADD_2\[\\(float, \\)\]*., which has already been marked as an accelerator routine" } */
+extern float ADD_2(float, float);
+
+#pragma acc routine (ADD_2) /* { dg-error "missing .nohost. clause when applying .#pragma acc routine. to .\[float \]*ADD_2\[\\(float, \\)\]*., which has already been marked as an accelerator routine" } */
-- 
2.17.1


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

* PR89433 "Repeated use of the OpenACC 'routine' directive"
  2018-10-02 14:57     ` [patch,openacc] " Cesar Philippidis
@ 2019-02-22 12:25       ` Thomas Schwinge
  2019-05-17 19:17         ` [committed] " Thomas Schwinge
  0 siblings, 1 reply; 16+ messages in thread
From: Thomas Schwinge @ 2019-02-22 12:25 UTC (permalink / raw)
  To: gcc-patches; +Cc: Jakub Jelinek

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

Hi!

To resolve PR89433 "Repeated use of the OpenACC 'routine' directive" at
least for C/C++, I intend to push the attached patches in next GCC
development stage 1 (unless that should be addressed right now, and also
on other GCC release branches?).

The corresponding Fortran changes will require additional effort.


Grüße
 Thomas



[-- Warning: decoded text below may be mangled, UTF-8 assumed --]
[-- Attachment #2: 0001-Refer-to-OpenACC-routine-clauses-from-omp-declare-ta.patch --]
[-- Type: text/x-diff, Size: 7701 bytes --]

From 15a0bacff7c75891ceb11a81112b48114c7014a4 Mon Sep 17 00:00:00 2001
From: Thomas Schwinge <thomas@codesourcery.com>
Date: Thu, 21 Feb 2019 15:52:20 +0100
Subject: [PATCH 1/3] Refer to OpenACC 'routine' clauses from "omp declare
 target" attribute

TODO
	gcc/c-family/
	* c-attribs.c (c_common_attribute_table): Set min_len to -1 for
	"omp declare target".
	gcc/c/
	* c-parser.c (c_finish_oacc_routine): Refer to OpenACC 'routine'
	clauses from "omp declare target" attribute.
	gcc/cp/
	* parser.c (cp_finalize_oacc_routine): Refer to OpenACC 'routine'
	clauses from "omp declare target" attribute.
	gcc/testsuite/
	* c-c++-common/goacc/classify-routine.c: Update.
---
 gcc/c-family/c-attribs.c                       |  2 +-
 gcc/c/c-parser.c                               |  2 +-
 gcc/cp/parser.c                                |  2 +-
 gcc/fortran/f95-lang.c                         |  2 +-
 gcc/fortran/trans-decl.c                       | 18 +++++++++++-------
 .../c-c++-common/goacc/classify-routine.c      |  4 ++--
 .../gfortran.dg/goacc/classify-routine.f95     |  4 ++--
 7 files changed, 19 insertions(+), 15 deletions(-)

diff --git a/gcc/c-family/c-attribs.c b/gcc/c-family/c-attribs.c
index 20584593c6a..f108ce6ca6a 100644
--- a/gcc/c-family/c-attribs.c
+++ b/gcc/c-family/c-attribs.c
@@ -437,7 +437,7 @@ const struct attribute_spec c_common_attribute_table[] =
 			      handle_omp_declare_simd_attribute, NULL },
   { "simd",		      0, 1, true,  false, false, false,
 			      handle_simd_attribute, NULL },
-  { "omp declare target",     0, 0, true, false, false, false,
+  { "omp declare target",     0, -1, true, false, false, false,
 			      handle_omp_declare_target_attribute, NULL },
   { "omp declare target link", 0, 0, true, false, false, false,
 			      handle_omp_declare_target_attribute, NULL },
diff --git a/gcc/c/c-parser.c b/gcc/c/c-parser.c
index 22c7416ac94..c8110faa948 100644
--- a/gcc/c/c-parser.c
+++ b/gcc/c/c-parser.c
@@ -15882,7 +15882,7 @@ c_finish_oacc_routine (struct oacc_routine_data *data, tree fndecl,
   /* Add an "omp declare target" attribute.  */
   DECL_ATTRIBUTES (fndecl)
     = tree_cons (get_identifier ("omp declare target"),
-		 NULL_TREE, DECL_ATTRIBUTES (fndecl));
+		 data->clauses, DECL_ATTRIBUTES (fndecl));
 
   /* Remember that we've used this "#pragma acc routine".  */
   data->fndecl_seen = true;
diff --git a/gcc/cp/parser.c b/gcc/cp/parser.c
index 545047c91af..418cc507822 100644
--- a/gcc/cp/parser.c
+++ b/gcc/cp/parser.c
@@ -40181,7 +40181,7 @@ cp_finalize_oacc_routine (cp_parser *parser, tree fndecl, bool is_defn)
       /* Add an "omp declare target" attribute.  */
       DECL_ATTRIBUTES (fndecl)
 	= tree_cons (get_identifier ("omp declare target"),
-		     NULL_TREE, DECL_ATTRIBUTES (fndecl));
+		     parser->oacc_routine->clauses, DECL_ATTRIBUTES (fndecl));
 
       /* Don't unset parser->oacc_routine here: we may still need it to
 	 diagnose wrong usage.  But, remember that we've used this "#pragma acc
diff --git a/gcc/fortran/f95-lang.c b/gcc/fortran/f95-lang.c
index ad81f7d93a0..19739792c8f 100644
--- a/gcc/fortran/f95-lang.c
+++ b/gcc/fortran/f95-lang.c
@@ -91,7 +91,7 @@ static const struct attribute_spec gfc_attribute_table[] =
 {
   /* { name, min_len, max_len, decl_req, type_req, fn_type_req,
        affects_type_identity, handler, exclude } */
-  { "omp declare target", 0, 0, true,  false, false, false,
+  { "omp declare target", 0, -1, true,  false, false, false,
     gfc_handle_omp_declare_target_attribute, NULL },
   { "omp declare target link", 0, 0, true,  false, false, false,
     gfc_handle_omp_declare_target_attribute, NULL },
diff --git a/gcc/fortran/trans-decl.c b/gcc/fortran/trans-decl.c
index 20d453051a2..23b4f98845a 100644
--- a/gcc/fortran/trans-decl.c
+++ b/gcc/fortran/trans-decl.c
@@ -1400,12 +1400,7 @@ add_attributes_to_decl (symbol_attribute sym_attr, tree list)
 	list = chainon (list, attr);
       }
 
-  if (sym_attr.omp_declare_target_link)
-    list = tree_cons (get_identifier ("omp declare target link"),
-		      NULL_TREE, list);
-  else if (sym_attr.omp_declare_target)
-    list = tree_cons (get_identifier ("omp declare target"),
-		      NULL_TREE, list);
+  tree clauses = NULL_TREE;
 
   if (sym_attr.oacc_routine_lop != OACC_ROUTINE_LOP_NONE)
     {
@@ -1429,11 +1424,20 @@ add_attributes_to_decl (symbol_attribute sym_attr, tree list)
 	  gcc_unreachable ();
 	}
       tree c = build_omp_clause (UNKNOWN_LOCATION, code);
+      OMP_CLAUSE_CHAIN (c) = clauses;
+      clauses = c;
 
-      tree dims = oacc_build_routine_dims (c);
+      tree dims = oacc_build_routine_dims (clauses);
       list = oacc_replace_fn_attrib_attr (list, dims);
     }
 
+  if (sym_attr.omp_declare_target_link)
+    list = tree_cons (get_identifier ("omp declare target link"),
+		      NULL_TREE, list);
+  else if (sym_attr.omp_declare_target)
+    list = tree_cons (get_identifier ("omp declare target"),
+		      clauses, list);
+
   return list;
 }
 
diff --git a/gcc/testsuite/c-c++-common/goacc/classify-routine.c b/gcc/testsuite/c-c++-common/goacc/classify-routine.c
index a723d2cdf51..0b9ba6ea69f 100644
--- a/gcc/testsuite/c-c++-common/goacc/classify-routine.c
+++ b/gcc/testsuite/c-c++-common/goacc/classify-routine.c
@@ -22,10 +22,10 @@ void ROUTINE ()
 }
 
 /* Check the offloaded function's attributes.
-   { dg-final { scan-tree-dump-times "(?n)__attribute__\\(\\(omp declare target, oacc function \\(0 1, 1 0, 1 0\\)\\)\\)" 1 "ompexp" } } */
+   { dg-final { scan-tree-dump-times "(?n)__attribute__\\(\\(omp declare target \\(worker\\), oacc function \\(0 1, 1 0, 1 0\\)\\)\\)" 1 "ompexp" } } */
 
 /* Check the offloaded function's classification and compute dimensions (will
    always be 1 x 1 x 1 for non-offloading compilation).
    { dg-final { scan-tree-dump-times "(?n)Function is OpenACC routine level 1" 1 "oaccdevlow" } }
    { dg-final { scan-tree-dump-times "(?n)Compute dimensions \\\[1, 1, 1\\\]" 1 "oaccdevlow" } }
-   { dg-final { scan-tree-dump-times "(?n)__attribute__\\(\\(oacc function \\(0 1, 1 1, 1 1\\), omp declare target, oacc function \\(0 1, 1 0, 1 0\\)\\)\\)" 1 "oaccdevlow" } } */
+   { dg-final { scan-tree-dump-times "(?n)__attribute__\\(\\(oacc function \\(0 1, 1 1, 1 1\\), omp declare target \\(worker\\), oacc function \\(0 1, 1 0, 1 0\\)\\)\\)" 1 "oaccdevlow" } } */
diff --git a/gcc/testsuite/gfortran.dg/goacc/classify-routine.f95 b/gcc/testsuite/gfortran.dg/goacc/classify-routine.f95
index e435f5d7eae..401d5270391 100644
--- a/gcc/testsuite/gfortran.dg/goacc/classify-routine.f95
+++ b/gcc/testsuite/gfortran.dg/goacc/classify-routine.f95
@@ -21,10 +21,10 @@ subroutine ROUTINE
 end subroutine ROUTINE
 
 ! Check the offloaded function's attributes.
-! { dg-final { scan-tree-dump-times "(?n)__attribute__\\(\\(omp declare target, oacc function \\(0 1, 1 0, 1 0\\)\\)\\)" 1 "ompexp" } }
+! { dg-final { scan-tree-dump-times "(?n)__attribute__\\(\\(oacc function \\(0 1, 1 0, 1 0\\), omp declare target \\(worker\\)\\)\\)" 1 "ompexp" } }
 
 ! Check the offloaded function's classification and compute dimensions (will
 ! always be 1 x 1 x 1 for non-offloading compilation).
 ! { dg-final { scan-tree-dump-times "(?n)Function is OpenACC routine level 1" 1 "oaccdevlow" } }
 ! { dg-final { scan-tree-dump-times "(?n)Compute dimensions \\\[1, 1, 1\\\]" 1 "oaccdevlow" } }
-! { dg-final { scan-tree-dump-times "(?n)__attribute__\\(\\(oacc function \\(0 1, 1 1, 1 1\\), omp declare target, oacc function \\(0 1, 1 0, 1 0\\)\\)\\)" 1 "oaccdevlow" } }
+! { dg-final { scan-tree-dump-times "(?n)__attribute__\\(\\(oacc function \\(0 1, 1 1, 1 1\\), omp declare target \\(worker\\)\\)\\)" 1 "oaccdevlow" } }
-- 
2.17.1


[-- Warning: decoded text below may be mangled, UTF-8 assumed --]
[-- Attachment #3: 0002-C-C-PR89433-Use-oacc_verify_routine_clauses-for-Open.patch --]
[-- Type: text/x-diff, Size: 25142 bytes --]

From a904014e12761dbb861a0a20cbbe03f0b24f2229 Mon Sep 17 00:00:00 2001
From: Thomas Schwinge <thomas@codesourcery.com>
Date: Thu, 21 Feb 2019 13:00:34 +0100
Subject: [PATCH 2/3] [C, C++, PR89433] Use 'oacc_verify_routine_clauses' for
 OpenACC 'routine' directives

TODO PR89433 "Repeated use of the OpenACC 'routine' directive"

TODO
	gcc/
	* omp-general.c (oacc_build_routine_dims): Move some of its
	processing into...
	(oacc_verify_routine_clauses): ... this new function.
	* omp-general.h (oacc_verify_routine_clauses): New prototype.
	gcc/c/
	* c-parser.c (c_parser_oacc_routine): Normalize order of clauses.
	(c_finish_oacc_routine): Call oacc_verify_routine_clauses.
	gcc/cp/
	* parser.c (cp_parser_oacc_routine)
	(cp_parser_late_parsing_oacc_routine): Normalize order of clauses.
	(cp_finalize_oacc_routine): Call oacc_verify_routine_clauses.
	gcc/testsuite/
	* c-c++-common/goacc/routine-2.c: Update, and move some test
	into...
	* c-c++-common/goacc/routine-level-of-parallelism-1.c: ... this
	new file.
---
 gcc/c/c-parser.c                              |   8 +
 gcc/cp/parser.c                               |   9 +
 gcc/omp-general.c                             |  67 ++++-
 gcc/omp-general.h                             |   1 +
 gcc/testsuite/c-c++-common/goacc/routine-2.c  |  20 +-
 .../goacc/routine-level-of-parallelism-1.c    | 265 ++++++++++++++++++
 6 files changed, 341 insertions(+), 29 deletions(-)
 create mode 100644 gcc/testsuite/c-c++-common/goacc/routine-level-of-parallelism-1.c

diff --git a/gcc/c/c-parser.c b/gcc/c/c-parser.c
index c8110faa948..e2d9e973e09 100644
--- a/gcc/c/c-parser.c
+++ b/gcc/c/c-parser.c
@@ -15779,6 +15779,9 @@ c_parser_oacc_routine (c_parser *parser, enum pragma_context context)
       data.clauses
 	= c_parser_oacc_all_clauses (parser, OACC_ROUTINE_CLAUSE_MASK,
 				     "#pragma acc routine");
+      /* The clauses are in reverse order; fix that to make later diagnostic
+	 emission easier.  */
+      data.clauses = nreverse (data.clauses);
 
       if (TREE_CODE (decl) != FUNCTION_DECL)
 	{
@@ -15793,6 +15796,9 @@ c_parser_oacc_routine (c_parser *parser, enum pragma_context context)
       data.clauses
 	= c_parser_oacc_all_clauses (parser, OACC_ROUTINE_CLAUSE_MASK,
 				     "#pragma acc routine");
+      /* The clauses are in reverse order; fix that to make later diagnostic
+	 emission easier.  */
+      data.clauses = nreverse (data.clauses);
 
       /* Emit a helpful diagnostic if there's another pragma following this
 	 one.  Also don't allow a static assertion declaration, as in the
@@ -15856,6 +15862,8 @@ c_finish_oacc_routine (struct oacc_routine_data *data, tree fndecl,
       return;
     }
 
+  oacc_verify_routine_clauses (&data->clauses, data->loc);
+
   if (oacc_get_fn_attrib (fndecl))
     {
       error_at (data->loc,
diff --git a/gcc/cp/parser.c b/gcc/cp/parser.c
index 418cc507822..8b02912fec2 100644
--- a/gcc/cp/parser.c
+++ b/gcc/cp/parser.c
@@ -40025,6 +40025,9 @@ cp_parser_oacc_routine (cp_parser *parser, cp_token *pragma_tok,
 	= cp_parser_oacc_all_clauses (parser, OACC_ROUTINE_CLAUSE_MASK,
 				      "#pragma acc routine",
 				      cp_lexer_peek_token (parser->lexer));
+      /* The clauses are in reverse order; fix that to make later diagnostic
+	 emission easier.  */
+      data.clauses = nreverse (data.clauses);
 
       if (decl && is_overloaded_fn (decl)
 	  && (TREE_CODE (decl) != FUNCTION_DECL
@@ -40121,6 +40124,9 @@ cp_parser_late_parsing_oacc_routine (cp_parser *parser, tree attrs)
   parser->oacc_routine->clauses
     = cp_parser_oacc_all_clauses (parser, OACC_ROUTINE_CLAUSE_MASK,
 				  "#pragma acc routine", pragma_tok);
+  /* The clauses are in reverse order; fix that to make later diagnostic
+     emission easier.  */
+  parser->oacc_routine->clauses = nreverse (parser->oacc_routine->clauses);
   cp_parser_pop_lexer (parser);
   /* Later, cp_finalize_oacc_routine will process the clauses, and then set
      fndecl_seen.  */
@@ -40155,6 +40161,9 @@ cp_finalize_oacc_routine (cp_parser *parser, tree fndecl, bool is_defn)
 	  return;
 	}
 
+      oacc_verify_routine_clauses (&parser->oacc_routine->clauses,
+				   parser->oacc_routine->loc);
+
       if (oacc_get_fn_attrib (fndecl))
 	{
 	  error_at (parser->oacc_routine->loc,
diff --git a/gcc/omp-general.c b/gcc/omp-general.c
index 356772ff458..07b2df6f8f8 100644
--- a/gcc/omp-general.c
+++ b/gcc/omp-general.c
@@ -608,9 +608,62 @@ oacc_set_fn_attrib (tree fn, tree clauses, vec<tree> *args)
     }
 }
 
-/*  Process the routine's dimension clauess to generate an attribute
-    value.  Issue diagnostics as appropriate.  We default to SEQ
-    (OpenACC 2.5 clarifies this). All dimensions have a size of zero
+/* Verify OpenACC routine clauses.
+
+   The chain of clauses returned will contain exactly one clause specifying the
+   level of parallelism.  */
+
+void
+oacc_verify_routine_clauses (tree *clauses, location_t loc)
+{
+  tree c_level = NULL_TREE;
+  tree c_p = NULL_TREE;
+  for (tree c = *clauses; c; c_p = c, c = OMP_CLAUSE_CHAIN (c))
+    switch (OMP_CLAUSE_CODE (c))
+      {
+      case OMP_CLAUSE_GANG:
+      case OMP_CLAUSE_WORKER:
+      case OMP_CLAUSE_VECTOR:
+      case OMP_CLAUSE_SEQ:
+	if (c_level == NULL_TREE)
+	  c_level = c;
+	else if (OMP_CLAUSE_CODE (c) == OMP_CLAUSE_CODE (c_level))
+	  {
+	    /* This has already been diagnosed in the front ends.  */
+	    /* Drop the duplicate clause.  */
+	    gcc_checking_assert (c_p != NULL_TREE);
+	    OMP_CLAUSE_CHAIN (c_p) = OMP_CLAUSE_CHAIN (c);
+	    c = c_p;
+	  }
+	else
+	  {
+	    error_at (OMP_CLAUSE_LOCATION (c),
+		      "%qs specifies a conflicting level of parallelism",
+		      omp_clause_code_name[OMP_CLAUSE_CODE (c)]);
+	    inform (OMP_CLAUSE_LOCATION (c_level),
+		    "... to the previous %qs clause here",
+		    omp_clause_code_name[OMP_CLAUSE_CODE (c_level)]);
+	    /* Drop the conflicting clause.  */
+	    gcc_checking_assert (c_p != NULL_TREE);
+	    OMP_CLAUSE_CHAIN (c_p) = OMP_CLAUSE_CHAIN (c);
+	    c = c_p;
+	  }
+	break;
+      default:
+	gcc_unreachable ();
+      }
+  if (c_level == NULL_TREE)
+    {
+      /* OpenACC 2.5 makes this an error; for the current OpenACC 2.0a
+	 implementation add an implicit "seq" clause.  */
+      c_level = build_omp_clause (loc, OMP_CLAUSE_SEQ);
+      OMP_CLAUSE_CHAIN (c_level) = *clauses;
+      *clauses = c_level;
+    }
+}
+
+/*  Process the OpenACC routine's clauses to generate an attribute
+    for the level of parallelism.  All dimensions have a size of zero
     (dynamic).  TREE_PURPOSE is set to indicate whether that dimension
     can have a loop partitioned on it.  non-zero indicates
     yes, zero indicates no.  By construction once a non-zero has been
@@ -632,16 +685,10 @@ oacc_build_routine_dims (tree clauses)
     for (ix = GOMP_DIM_MAX + 1; ix--;)
       if (OMP_CLAUSE_CODE (clauses) == ids[ix])
 	{
-	  if (level >= 0)
-	    error_at (OMP_CLAUSE_LOCATION (clauses),
-		      "multiple loop axes specified for routine");
 	  level = ix;
 	  break;
 	}
-
-  /* Default to SEQ.  */
-  if (level < 0)
-    level = GOMP_DIM_MAX;
+  gcc_checking_assert (level >= 0);
 
   tree dims = NULL_TREE;
 
diff --git a/gcc/omp-general.h b/gcc/omp-general.h
index 60faa5213a2..4241c33d99e 100644
--- a/gcc/omp-general.h
+++ b/gcc/omp-general.h
@@ -84,6 +84,7 @@ extern tree oacc_launch_pack (unsigned code, tree device, unsigned op);
 extern tree oacc_replace_fn_attrib_attr (tree attribs, tree dims);
 extern void oacc_replace_fn_attrib (tree fn, tree dims);
 extern void oacc_set_fn_attrib (tree fn, tree clauses, vec<tree> *args);
+extern void oacc_verify_routine_clauses (tree *, location_t);
 extern tree oacc_build_routine_dims (tree clauses);
 extern tree oacc_get_fn_attrib (tree fn);
 extern bool offloading_function_p (tree fn);
diff --git a/gcc/testsuite/c-c++-common/goacc/routine-2.c b/gcc/testsuite/c-c++-common/goacc/routine-2.c
index fc5eb11bb54..be1510a369c 100644
--- a/gcc/testsuite/c-c++-common/goacc/routine-2.c
+++ b/gcc/testsuite/c-c++-common/goacc/routine-2.c
@@ -1,21 +1,3 @@
-#pragma acc routine gang worker /* { dg-error "multiple loop axes" } */
-void gang (void)
-{
-}
-
-#pragma acc routine worker vector /* { dg-error "multiple loop axes" } */
-void worker (void)
-{
-}
-
-#pragma acc routine vector seq /* { dg-error "multiple loop axes" } */
-void vector (void)
-{
-}
-
-#pragma acc routine seq gang /* { dg-error "multiple loop axes" } */
-void seq (void)
-{
-}
+/* Test invalid use of the OpenACC 'routine' directive.  */
 
 #pragma acc routine (nothing) gang /* { dg-error "not been declared" } */
diff --git a/gcc/testsuite/c-c++-common/goacc/routine-level-of-parallelism-1.c b/gcc/testsuite/c-c++-common/goacc/routine-level-of-parallelism-1.c
new file mode 100644
index 00000000000..d71d6e088f7
--- /dev/null
+++ b/gcc/testsuite/c-c++-common/goacc/routine-level-of-parallelism-1.c
@@ -0,0 +1,265 @@
+/* Test various aspects of clauses specifying incompatible levels of
+   parallelism with the OpenACC routine directive.  The Fortran counterpart is
+   ../../gfortran.dg/goacc/routine-level-of-parallelism-1.f.  */
+
+extern void g_1 (void);
+#pragma acc routine (g_1) gang gang /* { dg-error "too many 'gang' clauses" } */
+
+#pragma acc routine worker worker /* { dg-error "too many 'worker' clauses" } */
+void w_1 (void)
+{
+}
+
+#pragma acc routine vector vector /* { dg-error "too many 'vector' clauses" } */
+void v_1 (void)
+{
+}
+
+#pragma acc routine seq seq /* { dg-error "too many 'seq' clauses" } */
+extern void s_1 (void);
+
+
+#pragma acc routine gang gang gang /* { dg-error "too many 'gang' clauses" } */
+void g_2 (void)
+{
+}
+
+#pragma acc routine worker worker worker /* { dg-error "too many 'worker' clauses" } */
+extern void w_2 (void);
+
+extern void v_2 (void);
+#pragma acc routine (v_2) vector vector vector /* { dg-error "too many 'vector' clauses" } */
+
+#pragma acc routine seq seq seq /* { dg-error "too many 'seq' clauses" } */
+void s_2 (void)
+{
+}
+
+
+#pragma acc routine \
+  gang \
+  worker /* { dg-error ".worker. specifies a conflicting level of parallelism" } */
+void g_3 (void)
+{
+}
+#pragma acc routine (g_3) /* { dg-error ".#pragma acc routine. already applied to .\[void \]*g_3." } */ \
+  gang \
+  seq /* { dg-error ".seq. specifies a conflicting level of parallelism" } */
+#pragma acc routine (g_3) /* { dg-error ".#pragma acc routine. already applied to .\[void \]*g_3." } */ \
+  gang \
+  vector /* { dg-error ".vector. specifies a conflicting level of parallelism" } */
+
+extern void w_3 (void);
+#pragma acc routine (w_3) \
+  worker \
+  vector /* { dg-error ".vector. specifies a conflicting level of parallelism" } */
+#pragma acc routine (w_3) /* { dg-error ".#pragma acc routine. already applied to .\[void \]*w_3." } */ \
+  worker \
+  gang /* { dg-error ".gang. specifies a conflicting level of parallelism" } */
+#pragma acc routine (w_3) /* { dg-error ".#pragma acc routine. already applied to .\[void \]*w_3." } */ \
+  worker \
+  seq /* { dg-error ".seq. specifies a conflicting level of parallelism" } */
+
+#pragma acc routine \
+  vector \
+  seq /* { dg-error ".seq. specifies a conflicting level of parallelism" } */
+void v_3 (void)
+{
+}
+#pragma acc routine (v_3) /* { dg-error ".#pragma acc routine. already applied to .\[void \]*v_3." } */ \
+  vector \
+  worker /* { dg-error ".worker. specifies a conflicting level of parallelism" } */
+#pragma acc routine (v_3) /* { dg-error ".#pragma acc routine. already applied to .\[void \]*v_3." } */ \
+  vector \
+  gang /* { dg-error ".gang. specifies a conflicting level of parallelism" } */
+
+extern void s_3 (void);
+#pragma acc routine (s_3) \
+  seq \
+  gang /* { dg-error ".gang. specifies a conflicting level of parallelism" } */
+#pragma acc routine (s_3) /* { dg-error ".#pragma acc routine. already applied to .\[void \]*s_3." } */ \
+  seq \
+  vector /* { dg-error ".vector. specifies a conflicting level of parallelism" } */
+#pragma acc routine (s_3) /* { dg-error ".#pragma acc routine. already applied to .\[void \]*s_3." } */ \
+  seq \
+  worker /* { dg-error ".worker. specifies a conflicting level of parallelism" } */
+
+
+#pragma acc routine \
+  gang gang gang /* { dg-error "too many 'gang' clauses" } */ \
+  worker /* { dg-error ".worker. specifies a conflicting level of parallelism" } */ \
+  vector /* { dg-error ".vector. specifies a conflicting level of parallelism" } */ \
+  seq /* { dg-error ".seq. specifies a conflicting level of parallelism" } */
+extern void g_4 (void);
+#pragma acc routine (g_4) /* { dg-error ".#pragma acc routine. already applied to .\[void \]*g_4." } */ \
+  gang gang gang /* { dg-error "too many 'gang' clauses" } */ \
+  vector /* { dg-error ".vector. specifies a conflicting level of parallelism" } */ \
+  worker /* { dg-error ".worker. specifies a conflicting level of parallelism" } */ \
+  seq /* { dg-error ".seq. specifies a conflicting level of parallelism" } */
+#pragma acc routine (g_4) /* { dg-error ".#pragma acc routine. already applied to .\[void \]*g_4." } */ \
+  gang gang gang /* { dg-error "too many 'gang' clauses" } */ \
+  seq /* { dg-error ".seq. specifies a conflicting level of parallelism" } */ \
+  vector /* { dg-error ".vector. specifies a conflicting level of parallelism" } */ \
+  worker /* { dg-error ".worker. specifies a conflicting level of parallelism" } */
+
+extern void w_4 (void);
+#pragma acc routine (w_4) \
+  worker worker worker /* { dg-error "too many 'worker' clauses" } */ \
+  seq /* { dg-error ".seq. specifies a conflicting level of parallelism" } */ \
+  vector /* { dg-error ".vector. specifies a conflicting level of parallelism" } */ \
+  gang /* { dg-error ".gang. specifies a conflicting level of parallelism" } */
+#pragma acc routine (w_4) /* { dg-error ".#pragma acc routine. already applied to .\[void \]*w_4." } */ \
+  worker worker worker /* { dg-error "too many 'worker' clauses" } */ \
+  vector /* { dg-error ".vector. specifies a conflicting level of parallelism" } */ \
+  seq /* { dg-error ".seq. specifies a conflicting level of parallelism" } */ \
+  gang /* { dg-error ".gang. specifies a conflicting level of parallelism" } */
+#pragma acc routine (w_4) /* { dg-error ".#pragma acc routine. already applied to .\[void \]*w_4." } */ \
+  worker worker worker /* { dg-error "too many 'worker' clauses" } */ \
+  seq /* { dg-error ".seq. specifies a conflicting level of parallelism" } */ \
+  gang /* { dg-error ".gang. specifies a conflicting level of parallelism" } */ \
+  vector /* { dg-error ".vector. specifies a conflicting level of parallelism" } */
+
+#pragma acc routine \
+  vector vector vector /* { dg-error "too many 'vector' clauses" } */ \
+  worker /* { dg-error ".worker. specifies a conflicting level of parallelism" } */ \
+  seq /* { dg-error ".seq. specifies a conflicting level of parallelism" } */ \
+  gang /* { dg-error ".gang. specifies a conflicting level of parallelism" } */
+void v_4 (void)
+{
+}
+#pragma acc routine (v_4) /* { dg-error ".#pragma acc routine. already applied to .\[void \]*v_4." } */ \
+  vector vector vector /* { dg-error "too many 'vector' clauses" } */ \
+  seq /* { dg-error ".seq. specifies a conflicting level of parallelism" } */ \
+  worker /* { dg-error ".worker. specifies a conflicting level of parallelism" } */ \
+  gang /* { dg-error ".gang. specifies a conflicting level of parallelism" } */
+#pragma acc routine (v_4) /* { dg-error ".#pragma acc routine. already applied to .\[void \]*v_4." } */ \
+  vector vector vector /* { dg-error "too many 'vector' clauses" } */ \
+  gang /* { dg-error ".gang. specifies a conflicting level of parallelism" } */ \
+  seq /* { dg-error ".seq. specifies a conflicting level of parallelism" } */ \
+  worker /* { dg-error ".worker. specifies a conflicting level of parallelism" } */
+
+#pragma acc routine \
+  seq seq seq /* { dg-error "too many 'seq' clauses" } */ \
+  vector /* { dg-error ".vector. specifies a conflicting level of parallelism" } */ \
+  worker /* { dg-error ".worker. specifies a conflicting level of parallelism" } */ \
+  gang /* { dg-error ".gang. specifies a conflicting level of parallelism" } */
+void s_4 (void)
+{
+}
+#pragma acc routine (s_4) /* { dg-error ".#pragma acc routine. already applied to .\[void \]*s_4." } */ \
+  seq seq seq /* { dg-error "too many 'seq' clauses" } */ \
+  vector /* { dg-error ".vector. specifies a conflicting level of parallelism" } */ \
+  gang /* { dg-error ".gang. specifies a conflicting level of parallelism" } */ \
+  worker /* { dg-error ".worker. specifies a conflicting level of parallelism" } */
+#pragma acc routine (s_4) /* { dg-error ".#pragma acc routine. already applied to .\[void \]*s_4." } */ \
+  seq seq seq /* { dg-error "too many 'seq' clauses" } */ \
+  worker /* { dg-error ".worker. specifies a conflicting level of parallelism" } */ \
+  vector /* { dg-error ".vector. specifies a conflicting level of parallelism" } */ \
+  gang /* { dg-error ".gang. specifies a conflicting level of parallelism" } */
+
+
+#pragma acc routine \
+  gang gang gang /* { dg-error "too many 'gang' clauses" } */ \
+  worker worker /* { dg-error "too many 'worker' clauses" } */ \
+  /* { dg-error ".worker. specifies a conflicting level of parallelism" "" { target *-*-* } .-1 } */ \
+  vector vector vector /* { dg-error "too many 'vector' clauses" } */ \
+  /* { dg-error ".vector. specifies a conflicting level of parallelism" "" { target *-*-* } .-1 } */ \
+  seq seq seq seq seq /* { dg-error "too many 'seq' clauses" } */ \
+  /* { dg-error ".seq. specifies a conflicting level of parallelism" "" { target *-*-* } .-1 } */
+void g_5 (void)
+{
+}
+#pragma acc routine (g_5) /* { dg-error ".#pragma acc routine. already applied to .\[void \]*g_5." } */ \
+  gang gang gang /* { dg-error "too many 'gang' clauses" } */ \
+  vector vector vector vector /* { dg-error "too many 'vector' clauses" } */ \
+  /* { dg-error ".vector. specifies a conflicting level of parallelism" "" { target *-*-* } .-1 } */ \
+  worker worker worker /* { dg-error "too many 'worker' clauses" } */ \
+  /* { dg-error ".worker. specifies a conflicting level of parallelism" "" { target *-*-* } .-1 } */ \
+  seq seq seq seq seq seq /* { dg-error "too many 'seq' clauses" } */ \
+  /* { dg-error ".seq. specifies a conflicting level of parallelism" "" { target *-*-* } .-1 } */
+#pragma acc routine (g_5) /* { dg-error ".#pragma acc routine. already applied to .\[void \]*g_5." } */ \
+  gang gang gang /* { dg-error "too many 'gang' clauses" } */ \
+  seq seq seq seq seq /* { dg-error "too many 'seq' clauses" } */ \
+  /* { dg-error ".seq. specifies a conflicting level of parallelism" "" { target *-*-* } .-1 } */ \
+  vector vector vector vector /* { dg-error "too many 'vector' clauses" } */ \
+  /* { dg-error ".vector. specifies a conflicting level of parallelism" "" { target *-*-* } .-1 } */ \
+  worker worker worker worker /* { dg-error "too many 'worker' clauses" } */ \
+  /* { dg-error ".worker. specifies a conflicting level of parallelism" "" { target *-*-* } .-1 } */
+
+#pragma acc routine \
+  worker worker worker /* { dg-error "too many 'worker' clauses" } */ \
+  seq seq seq seq /* { dg-error "too many 'seq' clauses" } */ \
+  /* { dg-error ".seq. specifies a conflicting level of parallelism" "" { target *-*-* } .-1 } */ \
+  vector vector vector vector vector /* { dg-error "too many 'vector' clauses" } */ \
+  /* { dg-error ".vector. specifies a conflicting level of parallelism" "" { target *-*-* } .-1 } */ \
+  gang gang /* { dg-error "too many 'gang' clauses" } */ \
+  /* { dg-error ".gang. specifies a conflicting level of parallelism" "" { target *-*-* } .-1 } */
+extern void w_5 (void);
+#pragma acc routine (w_5) /* { dg-error ".#pragma acc routine. already applied to .\[void \]*w_5." } */ \
+  worker worker worker /* { dg-error "too many 'worker' clauses" } */ \
+  vector vector vector /* { dg-error "too many 'vector' clauses" } */ \
+  /* { dg-error ".vector. specifies a conflicting level of parallelism" "" { target *-*-* } .-1 } */ \
+  seq seq seq /* { dg-error "too many 'seq' clauses" } */ \
+  /* { dg-error ".seq. specifies a conflicting level of parallelism" "" { target *-*-* } .-1 } */ \
+  gang gang /* { dg-error "too many 'gang' clauses" } */ \
+  /* { dg-error ".gang. specifies a conflicting level of parallelism" "" { target *-*-* } .-1 } */
+#pragma acc routine (w_5) /* { dg-error ".#pragma acc routine. already applied to .\[void \]*w_5." } */ \
+  worker worker worker /* { dg-error "too many 'worker' clauses" } */ \
+  seq seq /* { dg-error "too many 'seq' clauses" } */ \
+  /* { dg-error ".seq. specifies a conflicting level of parallelism" "" { target *-*-* } .-1 } */ \
+  gang gang /* { dg-error "too many 'gang' clauses" } */ \
+  /* { dg-error ".gang. specifies a conflicting level of parallelism" "" { target *-*-* } .-1 } */ \
+  vector vector vector vector /* { dg-error "too many 'vector' clauses" } */ \
+  /* { dg-error ".vector. specifies a conflicting level of parallelism" "" { target *-*-* } .-1 } */
+
+#pragma acc routine \
+  vector vector vector /* { dg-error "too many 'vector' clauses" } */ \
+  worker worker worker /* { dg-error "too many 'worker' clauses" } */ \
+  /* { dg-error ".worker. specifies a conflicting level of parallelism" "" { target *-*-* } .-1 } */ \
+  seq seq seq seq seq seq /* { dg-error "too many 'seq' clauses" } */ \
+  /* { dg-error ".seq. specifies a conflicting level of parallelism" "" { target *-*-* } .-1 } */ \
+  gang gang /* { dg-error "too many 'gang' clauses" } */ \
+  /* { dg-error ".gang. specifies a conflicting level of parallelism" "" { target *-*-* } .-1 } */
+extern void v_5 (void);
+#pragma acc routine (v_5) /* { dg-error ".#pragma acc routine. already applied to .\[void \]*v_5." } */ \
+  vector vector vector /* { dg-error "too many 'vector' clauses" } */ \
+  seq seq seq seq seq seq /* { dg-error "too many 'seq' clauses" } */ \
+  /* { dg-error ".seq. specifies a conflicting level of parallelism" "" { target *-*-* } .-1 } */ \
+  worker worker /* { dg-error "too many 'worker' clauses" } */ \
+  /* { dg-error ".worker. specifies a conflicting level of parallelism" "" { target *-*-* } .-1 } */ \
+  gang gang /* { dg-error "too many 'gang' clauses" } */ \
+  /* { dg-error ".gang. specifies a conflicting level of parallelism" "" { target *-*-* } .-1 } */
+#pragma acc routine (v_5) /* { dg-error ".#pragma acc routine. already applied to .\[void \]*v_5." } */ \
+  vector vector vector /* { dg-error "too many 'vector' clauses" } */ \
+  gang gang /* { dg-error "too many 'gang' clauses" } */ \
+  /* { dg-error ".gang. specifies a conflicting level of parallelism" "" { target *-*-* } .-1 } */ \
+  seq seq seq seq seq /* { dg-error "too many 'seq' clauses" } */ \
+  /* { dg-error ".seq. specifies a conflicting level of parallelism" "" { target *-*-* } .-1 } */ \
+  worker worker /* { dg-error "too many 'worker' clauses" } */ \
+  /* { dg-error ".worker. specifies a conflicting level of parallelism" "" { target *-*-* } .-1 } */
+
+extern void s_5 (void);
+#pragma acc routine (s_5) \
+  seq seq seq /* { dg-error "too many 'seq' clauses" } */ \
+  vector vector vector vector /* { dg-error "too many 'vector' clauses" } */ \
+  /* { dg-error ".vector. specifies a conflicting level of parallelism" "" { target *-*-* } .-1 } */ \
+  worker worker worker worker worker /* { dg-error "too many 'worker' clauses" } */ \
+  /* { dg-error ".worker. specifies a conflicting level of parallelism" "" { target *-*-* } .-1 } */ \
+  gang gang /* { dg-error "too many 'gang' clauses" } */ \
+  /* { dg-error ".gang. specifies a conflicting level of parallelism" "" { target *-*-* } .-1 } */
+#pragma acc routine (s_5) /* { dg-error ".#pragma acc routine. already applied to .\[void \]*s_5." } */ \
+  seq seq seq /* { dg-error "too many 'seq' clauses" } */ \
+  vector vector vector vector /* { dg-error "too many 'vector' clauses" } */ \
+  /* { dg-error ".vector. specifies a conflicting level of parallelism" "" { target *-*-* } .-1 } */ \
+  gang gang /* { dg-error "too many 'gang' clauses" } */ \
+  /* { dg-error ".gang. specifies a conflicting level of parallelism" "" { target *-*-* } .-1 } */ \
+  worker worker worker /* { dg-error "too many 'worker' clauses" } */ \
+  /* { dg-error ".worker. specifies a conflicting level of parallelism" "" { target *-*-* } .-1 } */
+#pragma acc routine (s_5) /* { dg-error ".#pragma acc routine. already applied to .\[void \]*s_5." } */ \
+  seq seq seq /* { dg-error "too many 'seq' clauses" } */ \
+  worker worker /* { dg-error "too many 'worker' clauses" } */ \
+  /* { dg-error ".worker. specifies a conflicting level of parallelism" "" { target *-*-* } .-1 } */ \
+  vector vector vector vector /* { dg-error "too many 'vector' clauses" } */ \
+  /* { dg-error ".vector. specifies a conflicting level of parallelism" "" { target *-*-* } .-1 } */ \
+  gang gang /* { dg-error "too many 'gang' clauses" } */ \
+  /* { dg-error ".gang. specifies a conflicting level of parallelism" "" { target *-*-* } .-1 } */
-- 
2.17.1


[-- Warning: decoded text below may be mangled, UTF-8 assumed --]
[-- Attachment #4: 0003-C-C-PR89433-Repeated-use-of-the-OpenACC-routine-dire.patch --]
[-- Type: text/x-diff, Size: 41301 bytes --]

From 4613129e22af350eb0e6854832582947780e1f64 Mon Sep 17 00:00:00 2001
From: Thomas Schwinge <thomas@codesourcery.com>
Date: Thu, 21 Feb 2019 15:42:18 +0100
Subject: [PATCH 3/3] [C, C++, PR89433] Repeated use of the OpenACC 'routine'
 directive

TODO PR89433 - Repeated use of the OpenACC 'routine' directive

TODO
	gcc/
	* omp-general.c (oacc_verify_routine_clauses): Change formal
	parameters.  Add checking if already marked as an accelerator
	routine.  Adjust all users.
	gcc/c/
	* c-parser.c (c_finish_oacc_routine): Rework checking if already
	marked as an accelerator routine.
	gcc/cp/
	* parser.c (cp_finalize_oacc_routine): Rework checking if already
	marked as an accelerator routine.
	gcc/testsuite/
	* c-c++-common/goacc/oaccdevlow-routine.c: Update.
	* c-c++-common/goacc/routine-5.c: Likewise.
	* c-c++-common/goacc/routine-level-of-parallelism-1.c: Likewise.
	* c-c++-common/goacc/routine-level-of-parallelism-2.c: New file.
	gcc/testsuite/
	* c-c++-common/goacc/routine-1.c: Update.
	* c-c++-common/goacc/routine-2.c: Likewise.
	* c-c++-common/goacc/routine-nohost-1.c: Likewise.
	* g++.dg/goacc/routine-2.C: Likewise.
	* c-c++-common/goacc/routine-nohost-2.c: New file.
---
 gcc/c/c-parser.c                              |  46 ++--
 gcc/cp/parser.c                               |  50 ++--
 gcc/omp-general.c                             |  85 ++++++-
 gcc/omp-general.h                             |   3 +-
 gcc/testsuite/c-c++-common/goacc/routine-5.c  |  46 +---
 .../goacc/routine-level-of-parallelism-1.c    | 236 ++++++++++++++++--
 .../goacc/routine-level-of-parallelism-2.c    |  71 ++++++
 .../goacc/routine-level-of-parallelism-1.f90  |   6 +-
 8 files changed, 422 insertions(+), 121 deletions(-)
 create mode 100644 gcc/testsuite/c-c++-common/goacc/routine-level-of-parallelism-2.c

diff --git a/gcc/c/c-parser.c b/gcc/c/c-parser.c
index e2d9e973e09..04a03fa2feb 100644
--- a/gcc/c/c-parser.c
+++ b/gcc/c/c-parser.c
@@ -15862,35 +15862,39 @@ c_finish_oacc_routine (struct oacc_routine_data *data, tree fndecl,
       return;
     }
 
-  oacc_verify_routine_clauses (&data->clauses, data->loc);
-
-  if (oacc_get_fn_attrib (fndecl))
+  int compatible
+    = oacc_verify_routine_clauses (fndecl, &data->clauses, data->loc,
+				   "#pragma acc routine");
+  if (compatible < 0)
     {
-      error_at (data->loc,
-		"%<#pragma acc routine%> already applied to %qD", fndecl);
       data->error_seen = true;
       return;
     }
-
-  if (TREE_USED (fndecl) || (!is_defn && DECL_SAVED_TREE (fndecl)))
+  if (compatible > 0)
     {
-      error_at (data->loc,
-		TREE_USED (fndecl)
-		? G_("%<#pragma acc routine%> must be applied before use")
-		: G_("%<#pragma acc routine%> must be applied before "
-		     "definition"));
-      data->error_seen = true;
-      return;
     }
+  else
+    {
+      if (TREE_USED (fndecl) || (!is_defn && DECL_SAVED_TREE (fndecl)))
+	{
+	  error_at (data->loc,
+		    TREE_USED (fndecl)
+		    ? G_("%<#pragma acc routine%> must be applied before use")
+		    : G_("%<#pragma acc routine%> must be applied before"
+			 " definition"));
+	  data->error_seen = true;
+	  return;
+	}
 
-  /* Process the routine's dimension clauses.  */
-  tree dims = oacc_build_routine_dims (data->clauses);
-  oacc_replace_fn_attrib (fndecl, dims);
+      /* Set the routine's level of parallelism.  */
+      tree dims = oacc_build_routine_dims (data->clauses);
+      oacc_replace_fn_attrib (fndecl, dims);
 
-  /* Add an "omp declare target" attribute.  */
-  DECL_ATTRIBUTES (fndecl)
-    = tree_cons (get_identifier ("omp declare target"),
-		 data->clauses, DECL_ATTRIBUTES (fndecl));
+      /* Add an "omp declare target" attribute.  */
+      DECL_ATTRIBUTES (fndecl)
+	= tree_cons (get_identifier ("omp declare target"),
+		     data->clauses, DECL_ATTRIBUTES (fndecl));
+    }
 
   /* Remember that we've used this "#pragma acc routine".  */
   data->fndecl_seen = true;
diff --git a/gcc/cp/parser.c b/gcc/cp/parser.c
index 8b02912fec2..b8a9078ea3b 100644
--- a/gcc/cp/parser.c
+++ b/gcc/cp/parser.c
@@ -40161,36 +40161,42 @@ cp_finalize_oacc_routine (cp_parser *parser, tree fndecl, bool is_defn)
 	  return;
 	}
 
-      oacc_verify_routine_clauses (&parser->oacc_routine->clauses,
-				   parser->oacc_routine->loc);
-
-      if (oacc_get_fn_attrib (fndecl))
+      int compatible
+	= oacc_verify_routine_clauses (fndecl, &parser->oacc_routine->clauses,
+				       parser->oacc_routine->loc,
+				       "#pragma acc routine");
+      if (compatible < 0)
 	{
-	  error_at (parser->oacc_routine->loc,
-		    "%<#pragma acc routine%> already applied to %qD", fndecl);
 	  parser->oacc_routine = NULL;
 	  return;
 	}
-
-      if (TREE_USED (fndecl) || (!is_defn && DECL_SAVED_TREE (fndecl)))
+      if (compatible > 0)
 	{
-	  error_at (parser->oacc_routine->loc,
-		    TREE_USED (fndecl)
-		    ? G_("%<#pragma acc routine%> must be applied before use")
-		    : G_("%<#pragma acc routine%> must be applied before "
-			 "definition"));
-	  parser->oacc_routine = NULL;
-	  return;
 	}
+      else
+	{
+	  if (TREE_USED (fndecl) || (!is_defn && DECL_SAVED_TREE (fndecl)))
+	    {
+	      error_at (parser->oacc_routine->loc,
+			TREE_USED (fndecl)
+			? G_("%<#pragma acc routine%> must be applied before"
+			     " use")
+			: G_("%<#pragma acc routine%> must be applied before"
+			     " definition"));
+	      parser->oacc_routine = NULL;
+	      return;
+	    }
 
-      /* Process the routine's dimension clauses.  */
-      tree dims = oacc_build_routine_dims (parser->oacc_routine->clauses);
-      oacc_replace_fn_attrib (fndecl, dims);
+	  /* Set the routine's level of parallelism.  */
+	  tree dims = oacc_build_routine_dims (parser->oacc_routine->clauses);
+	  oacc_replace_fn_attrib (fndecl, dims);
 
-      /* Add an "omp declare target" attribute.  */
-      DECL_ATTRIBUTES (fndecl)
-	= tree_cons (get_identifier ("omp declare target"),
-		     parser->oacc_routine->clauses, DECL_ATTRIBUTES (fndecl));
+	  /* Add an "omp declare target" attribute.  */
+	  DECL_ATTRIBUTES (fndecl)
+	    = tree_cons (get_identifier ("omp declare target"),
+			 parser->oacc_routine->clauses,
+			 DECL_ATTRIBUTES (fndecl));
+	}
 
       /* Don't unset parser->oacc_routine here: we may still need it to
 	 diagnose wrong usage.  But, remember that we've used this "#pragma acc
diff --git a/gcc/omp-general.c b/gcc/omp-general.c
index 07b2df6f8f8..7a7c8ead85c 100644
--- a/gcc/omp-general.c
+++ b/gcc/omp-general.c
@@ -610,11 +610,14 @@ oacc_set_fn_attrib (tree fn, tree clauses, vec<tree> *args)
 
 /* Verify OpenACC routine clauses.
 
-   The chain of clauses returned will contain exactly one clause specifying the
-   level of parallelism.  */
+   Returns 0 if FNDECL should be marked as an accelerator routine, 1 if it has
+   already been marked in compatible way, and -1 if incompatible.  Upon
+   returning, the chain of clauses will contain exactly one clause specifying
+   the level of parallelism.  */
 
-void
-oacc_verify_routine_clauses (tree *clauses, location_t loc)
+int
+oacc_verify_routine_clauses (tree fndecl, tree *clauses, location_t loc,
+			     const char *routine_str)
 {
   tree c_level = NULL_TREE;
   tree c_p = NULL_TREE;
@@ -660,6 +663,80 @@ oacc_verify_routine_clauses (tree *clauses, location_t loc)
       OMP_CLAUSE_CHAIN (c_level) = *clauses;
       *clauses = c_level;
     }
+  /* In *clauses, we now have exactly one clause specifying the level of
+     parallelism.  */
+
+  tree attr
+    = lookup_attribute ("omp declare target", DECL_ATTRIBUTES (fndecl));
+  if (attr != NULL_TREE)
+    {
+      /* If a "#pragma acc routine" has already been applied, just verify
+	 this one for compatibility.  */
+      /* Collect previous directive's clauses.  */
+      tree c_level_p = NULL_TREE;
+      for (tree c = TREE_VALUE (attr); c; c = OMP_CLAUSE_CHAIN (c))
+	switch (OMP_CLAUSE_CODE (c))
+	  {
+	  case OMP_CLAUSE_GANG:
+	  case OMP_CLAUSE_WORKER:
+	  case OMP_CLAUSE_VECTOR:
+	  case OMP_CLAUSE_SEQ:
+	    gcc_checking_assert (c_level_p == NULL_TREE);
+	    c_level_p = c;
+	    break;
+	  default:
+	    gcc_unreachable ();
+	  }
+      gcc_checking_assert (c_level_p != NULL_TREE);
+      /* ..., and compare to current directive's, which we've already collected
+	 above.  */
+      tree c_diag;
+      tree c_diag_p;
+      /* Matching level of parallelism?  */
+      if (OMP_CLAUSE_CODE (c_level) != OMP_CLAUSE_CODE (c_level_p))
+	{
+	  c_diag = c_level;
+	  c_diag_p = c_level_p;
+	  goto incompatible;
+	}
+      /* Compatible.  */
+      return 1;
+
+    incompatible:
+      if (c_diag != NULL_TREE)
+	error_at (OMP_CLAUSE_LOCATION (c_diag),
+		  "incompatible %qs clause when applying"
+		  " %<%s%> to %qD, which has already been"
+		  " marked as an accelerator routine",
+		  omp_clause_code_name[OMP_CLAUSE_CODE (c_diag)],
+		  routine_str, fndecl);
+      else if (c_diag_p != NULL_TREE)
+	error_at (loc,
+		  "missing %qs clause when applying"
+		  " %<%s%> to %qD, which has already been"
+		  " marked as an accelerator routine",
+		  omp_clause_code_name[OMP_CLAUSE_CODE (c_diag_p)],
+		  routine_str, fndecl);
+      else
+	gcc_unreachable ();
+      if (c_diag_p != NULL_TREE)
+	inform (OMP_CLAUSE_LOCATION (c_diag_p),
+		"... with %qs clause here",
+		omp_clause_code_name[OMP_CLAUSE_CODE (c_diag_p)]);
+      else
+	{
+	  /* In the front ends, we don't preserve location information for the
+	     OpenACC routine directive itself.  However, that of c_level_p
+	     should be close.  */
+	  location_t loc_routine = OMP_CLAUSE_LOCATION (c_level_p);
+	  inform (loc_routine, "... without %qs clause near to here",
+		  omp_clause_code_name[OMP_CLAUSE_CODE (c_diag)]);
+	}
+      /* Incompatible.  */
+      return -1;
+    }
+
+  return 0;
 }
 
 /*  Process the OpenACC routine's clauses to generate an attribute
diff --git a/gcc/omp-general.h b/gcc/omp-general.h
index 4241c33d99e..f96d3c7768a 100644
--- a/gcc/omp-general.h
+++ b/gcc/omp-general.h
@@ -84,7 +84,8 @@ extern tree oacc_launch_pack (unsigned code, tree device, unsigned op);
 extern tree oacc_replace_fn_attrib_attr (tree attribs, tree dims);
 extern void oacc_replace_fn_attrib (tree fn, tree dims);
 extern void oacc_set_fn_attrib (tree fn, tree clauses, vec<tree> *args);
-extern void oacc_verify_routine_clauses (tree *, location_t);
+extern int oacc_verify_routine_clauses (tree, tree *, location_t,
+					const char *);
 extern tree oacc_build_routine_dims (tree clauses);
 extern tree oacc_get_fn_attrib (tree fn);
 extern bool offloading_function_p (tree fn);
diff --git a/gcc/testsuite/c-c++-common/goacc/routine-5.c b/gcc/testsuite/c-c++-common/goacc/routine-5.c
index b967a7447bd..a8c974cb6bf 100644
--- a/gcc/testsuite/c-c++-common/goacc/routine-5.c
+++ b/gcc/testsuite/c-c++-common/goacc/routine-5.c
@@ -150,61 +150,19 @@ void f_static_assert();
 
 #pragma acc routine
 __extension__ extern void ex1();
-#pragma acc routine (ex1) /* { dg-error ".#pragma acc routine. already applied to .\[void \]*ex1" } */
+#pragma acc routine (ex1) worker /* { dg-error "has already been marked as an accelerator routine" } */
 
 #pragma acc routine
 __extension__ __extension__ __extension__ __extension__ __extension__ void ex2()
 {
 }
-#pragma acc routine (ex2) /* { dg-error ".#pragma acc routine. already applied to .\[void \]*ex2" } */
+#pragma acc routine (ex2) worker /* { dg-error "has already been marked as an accelerator routine" } */
 
 #pragma acc routine /* { dg-error ".#pragma acc routine. not immediately followed by function declaration or definition" } */
 __extension__ int ex3;
 #pragma acc routine (ex3) /* { dg-error ".ex3. does not refer to a function" } */
 
 
-/* "#pragma acc routine" already applied.  */
-
-extern void fungsi_1();
-#pragma acc routine(fungsi_1) gang
-#pragma acc routine(fungsi_1) gang /* { dg-error ".#pragma acc routine. already applied to .\[void \]*fungsi_1" } */
-#pragma acc routine(fungsi_1) worker /* { dg-error ".#pragma acc routine. already applied to .\[void \]*fungsi_1" } */
-#pragma acc routine(fungsi_1) vector /* { dg-error ".#pragma acc routine. already applied to .\[void \]*fungsi_1" } */
-
-#pragma acc routine seq
-extern void fungsi_2();
-#pragma acc routine(fungsi_2) seq /* { dg-error ".#pragma acc routine. already applied to .\[void \]*fungsi_2." } */
-#pragma acc routine(fungsi_2) worker /* { dg-error ".#pragma acc routine. already applied to .\[void \]*fungsi_2." } */
-#pragma acc routine(fungsi_2) /* { dg-error ".#pragma acc routine. already applied to .\[void \]*fungsi_2." } */
-
-#pragma acc routine vector
-extern void fungsi_3();
-#pragma acc routine vector /* { dg-error ".#pragma acc routine. already applied to .\[void \]*fungsi_3." } */
-void fungsi_3()
-{
-}
-
-extern void fungsi_4();
-#pragma acc routine (fungsi_4) worker
-#pragma acc routine gang /* { dg-error ".#pragma acc routine. already applied to .\[void \]*fungsi_4." } */
-void fungsi_4()
-{
-}
-
-#pragma acc routine gang
-void fungsi_5()
-{
-}
-#pragma acc routine (fungsi_5) worker /* { dg-error ".#pragma acc routine. already applied to .\[void \]*fungsi_5." } */
-
-#pragma acc routine seq
-void fungsi_6()
-{
-}
-#pragma acc routine seq /* { dg-error ".#pragma acc routine. already applied to .\[void \]*fungsi_6." } */
-extern void fungsi_6();
-
-
 /* "#pragma acc routine" must be applied before.  */
 
 void Bar ();
diff --git a/gcc/testsuite/c-c++-common/goacc/routine-level-of-parallelism-1.c b/gcc/testsuite/c-c++-common/goacc/routine-level-of-parallelism-1.c
index d71d6e088f7..aa3c1243ba1 100644
--- a/gcc/testsuite/c-c++-common/goacc/routine-level-of-parallelism-1.c
+++ b/gcc/testsuite/c-c++-common/goacc/routine-level-of-parallelism-1.c
@@ -1,6 +1,5 @@
 /* Test various aspects of clauses specifying incompatible levels of
-   parallelism with the OpenACC routine directive.  The Fortran counterpart is
-   ../../gfortran.dg/goacc/routine-level-of-parallelism-1.f.  */
+   parallelism with the OpenACC routine directive.  */
 
 extern void g_1 (void);
 #pragma acc routine (g_1) gang gang /* { dg-error "too many 'gang' clauses" } */
@@ -42,10 +41,10 @@ void s_2 (void)
 void g_3 (void)
 {
 }
-#pragma acc routine (g_3) /* { dg-error ".#pragma acc routine. already applied to .\[void \]*g_3." } */ \
+#pragma acc routine (g_3) \
   gang \
   seq /* { dg-error ".seq. specifies a conflicting level of parallelism" } */
-#pragma acc routine (g_3) /* { dg-error ".#pragma acc routine. already applied to .\[void \]*g_3." } */ \
+#pragma acc routine (g_3) \
   gang \
   vector /* { dg-error ".vector. specifies a conflicting level of parallelism" } */
 
@@ -53,10 +52,10 @@ extern void w_3 (void);
 #pragma acc routine (w_3) \
   worker \
   vector /* { dg-error ".vector. specifies a conflicting level of parallelism" } */
-#pragma acc routine (w_3) /* { dg-error ".#pragma acc routine. already applied to .\[void \]*w_3." } */ \
+#pragma acc routine (w_3) \
   worker \
   gang /* { dg-error ".gang. specifies a conflicting level of parallelism" } */
-#pragma acc routine (w_3) /* { dg-error ".#pragma acc routine. already applied to .\[void \]*w_3." } */ \
+#pragma acc routine (w_3) \
   worker \
   seq /* { dg-error ".seq. specifies a conflicting level of parallelism" } */
 
@@ -66,10 +65,10 @@ extern void w_3 (void);
 void v_3 (void)
 {
 }
-#pragma acc routine (v_3) /* { dg-error ".#pragma acc routine. already applied to .\[void \]*v_3." } */ \
+#pragma acc routine (v_3) \
   vector \
   worker /* { dg-error ".worker. specifies a conflicting level of parallelism" } */
-#pragma acc routine (v_3) /* { dg-error ".#pragma acc routine. already applied to .\[void \]*v_3." } */ \
+#pragma acc routine (v_3) \
   vector \
   gang /* { dg-error ".gang. specifies a conflicting level of parallelism" } */
 
@@ -77,10 +76,10 @@ extern void s_3 (void);
 #pragma acc routine (s_3) \
   seq \
   gang /* { dg-error ".gang. specifies a conflicting level of parallelism" } */
-#pragma acc routine (s_3) /* { dg-error ".#pragma acc routine. already applied to .\[void \]*s_3." } */ \
+#pragma acc routine (s_3) \
   seq \
   vector /* { dg-error ".vector. specifies a conflicting level of parallelism" } */
-#pragma acc routine (s_3) /* { dg-error ".#pragma acc routine. already applied to .\[void \]*s_3." } */ \
+#pragma acc routine (s_3) \
   seq \
   worker /* { dg-error ".worker. specifies a conflicting level of parallelism" } */
 
@@ -91,12 +90,12 @@ extern void s_3 (void);
   vector /* { dg-error ".vector. specifies a conflicting level of parallelism" } */ \
   seq /* { dg-error ".seq. specifies a conflicting level of parallelism" } */
 extern void g_4 (void);
-#pragma acc routine (g_4) /* { dg-error ".#pragma acc routine. already applied to .\[void \]*g_4." } */ \
+#pragma acc routine (g_4) \
   gang gang gang /* { dg-error "too many 'gang' clauses" } */ \
   vector /* { dg-error ".vector. specifies a conflicting level of parallelism" } */ \
   worker /* { dg-error ".worker. specifies a conflicting level of parallelism" } */ \
   seq /* { dg-error ".seq. specifies a conflicting level of parallelism" } */
-#pragma acc routine (g_4) /* { dg-error ".#pragma acc routine. already applied to .\[void \]*g_4." } */ \
+#pragma acc routine (g_4) \
   gang gang gang /* { dg-error "too many 'gang' clauses" } */ \
   seq /* { dg-error ".seq. specifies a conflicting level of parallelism" } */ \
   vector /* { dg-error ".vector. specifies a conflicting level of parallelism" } */ \
@@ -108,12 +107,12 @@ extern void w_4 (void);
   seq /* { dg-error ".seq. specifies a conflicting level of parallelism" } */ \
   vector /* { dg-error ".vector. specifies a conflicting level of parallelism" } */ \
   gang /* { dg-error ".gang. specifies a conflicting level of parallelism" } */
-#pragma acc routine (w_4) /* { dg-error ".#pragma acc routine. already applied to .\[void \]*w_4." } */ \
+#pragma acc routine (w_4) \
   worker worker worker /* { dg-error "too many 'worker' clauses" } */ \
   vector /* { dg-error ".vector. specifies a conflicting level of parallelism" } */ \
   seq /* { dg-error ".seq. specifies a conflicting level of parallelism" } */ \
   gang /* { dg-error ".gang. specifies a conflicting level of parallelism" } */
-#pragma acc routine (w_4) /* { dg-error ".#pragma acc routine. already applied to .\[void \]*w_4." } */ \
+#pragma acc routine (w_4) \
   worker worker worker /* { dg-error "too many 'worker' clauses" } */ \
   seq /* { dg-error ".seq. specifies a conflicting level of parallelism" } */ \
   gang /* { dg-error ".gang. specifies a conflicting level of parallelism" } */ \
@@ -127,12 +126,12 @@ extern void w_4 (void);
 void v_4 (void)
 {
 }
-#pragma acc routine (v_4) /* { dg-error ".#pragma acc routine. already applied to .\[void \]*v_4." } */ \
+#pragma acc routine (v_4) \
   vector vector vector /* { dg-error "too many 'vector' clauses" } */ \
   seq /* { dg-error ".seq. specifies a conflicting level of parallelism" } */ \
   worker /* { dg-error ".worker. specifies a conflicting level of parallelism" } */ \
   gang /* { dg-error ".gang. specifies a conflicting level of parallelism" } */
-#pragma acc routine (v_4) /* { dg-error ".#pragma acc routine. already applied to .\[void \]*v_4." } */ \
+#pragma acc routine (v_4) \
   vector vector vector /* { dg-error "too many 'vector' clauses" } */ \
   gang /* { dg-error ".gang. specifies a conflicting level of parallelism" } */ \
   seq /* { dg-error ".seq. specifies a conflicting level of parallelism" } */ \
@@ -146,12 +145,12 @@ void v_4 (void)
 void s_4 (void)
 {
 }
-#pragma acc routine (s_4) /* { dg-error ".#pragma acc routine. already applied to .\[void \]*s_4." } */ \
+#pragma acc routine (s_4) \
   seq seq seq /* { dg-error "too many 'seq' clauses" } */ \
   vector /* { dg-error ".vector. specifies a conflicting level of parallelism" } */ \
   gang /* { dg-error ".gang. specifies a conflicting level of parallelism" } */ \
   worker /* { dg-error ".worker. specifies a conflicting level of parallelism" } */
-#pragma acc routine (s_4) /* { dg-error ".#pragma acc routine. already applied to .\[void \]*s_4." } */ \
+#pragma acc routine (s_4) \
   seq seq seq /* { dg-error "too many 'seq' clauses" } */ \
   worker /* { dg-error ".worker. specifies a conflicting level of parallelism" } */ \
   vector /* { dg-error ".vector. specifies a conflicting level of parallelism" } */ \
@@ -169,7 +168,7 @@ void s_4 (void)
 void g_5 (void)
 {
 }
-#pragma acc routine (g_5) /* { dg-error ".#pragma acc routine. already applied to .\[void \]*g_5." } */ \
+#pragma acc routine (g_5) \
   gang gang gang /* { dg-error "too many 'gang' clauses" } */ \
   vector vector vector vector /* { dg-error "too many 'vector' clauses" } */ \
   /* { dg-error ".vector. specifies a conflicting level of parallelism" "" { target *-*-* } .-1 } */ \
@@ -177,7 +176,7 @@ void g_5 (void)
   /* { dg-error ".worker. specifies a conflicting level of parallelism" "" { target *-*-* } .-1 } */ \
   seq seq seq seq seq seq /* { dg-error "too many 'seq' clauses" } */ \
   /* { dg-error ".seq. specifies a conflicting level of parallelism" "" { target *-*-* } .-1 } */
-#pragma acc routine (g_5) /* { dg-error ".#pragma acc routine. already applied to .\[void \]*g_5." } */ \
+#pragma acc routine (g_5) \
   gang gang gang /* { dg-error "too many 'gang' clauses" } */ \
   seq seq seq seq seq /* { dg-error "too many 'seq' clauses" } */ \
   /* { dg-error ".seq. specifies a conflicting level of parallelism" "" { target *-*-* } .-1 } */ \
@@ -195,7 +194,7 @@ void g_5 (void)
   gang gang /* { dg-error "too many 'gang' clauses" } */ \
   /* { dg-error ".gang. specifies a conflicting level of parallelism" "" { target *-*-* } .-1 } */
 extern void w_5 (void);
-#pragma acc routine (w_5) /* { dg-error ".#pragma acc routine. already applied to .\[void \]*w_5." } */ \
+#pragma acc routine (w_5) \
   worker worker worker /* { dg-error "too many 'worker' clauses" } */ \
   vector vector vector /* { dg-error "too many 'vector' clauses" } */ \
   /* { dg-error ".vector. specifies a conflicting level of parallelism" "" { target *-*-* } .-1 } */ \
@@ -203,7 +202,7 @@ extern void w_5 (void);
   /* { dg-error ".seq. specifies a conflicting level of parallelism" "" { target *-*-* } .-1 } */ \
   gang gang /* { dg-error "too many 'gang' clauses" } */ \
   /* { dg-error ".gang. specifies a conflicting level of parallelism" "" { target *-*-* } .-1 } */
-#pragma acc routine (w_5) /* { dg-error ".#pragma acc routine. already applied to .\[void \]*w_5." } */ \
+#pragma acc routine (w_5) \
   worker worker worker /* { dg-error "too many 'worker' clauses" } */ \
   seq seq /* { dg-error "too many 'seq' clauses" } */ \
   /* { dg-error ".seq. specifies a conflicting level of parallelism" "" { target *-*-* } .-1 } */ \
@@ -221,7 +220,7 @@ extern void w_5 (void);
   gang gang /* { dg-error "too many 'gang' clauses" } */ \
   /* { dg-error ".gang. specifies a conflicting level of parallelism" "" { target *-*-* } .-1 } */
 extern void v_5 (void);
-#pragma acc routine (v_5) /* { dg-error ".#pragma acc routine. already applied to .\[void \]*v_5." } */ \
+#pragma acc routine (v_5) \
   vector vector vector /* { dg-error "too many 'vector' clauses" } */ \
   seq seq seq seq seq seq /* { dg-error "too many 'seq' clauses" } */ \
   /* { dg-error ".seq. specifies a conflicting level of parallelism" "" { target *-*-* } .-1 } */ \
@@ -229,7 +228,7 @@ extern void v_5 (void);
   /* { dg-error ".worker. specifies a conflicting level of parallelism" "" { target *-*-* } .-1 } */ \
   gang gang /* { dg-error "too many 'gang' clauses" } */ \
   /* { dg-error ".gang. specifies a conflicting level of parallelism" "" { target *-*-* } .-1 } */
-#pragma acc routine (v_5) /* { dg-error ".#pragma acc routine. already applied to .\[void \]*v_5." } */ \
+#pragma acc routine (v_5) \
   vector vector vector /* { dg-error "too many 'vector' clauses" } */ \
   gang gang /* { dg-error "too many 'gang' clauses" } */ \
   /* { dg-error ".gang. specifies a conflicting level of parallelism" "" { target *-*-* } .-1 } */ \
@@ -247,7 +246,7 @@ extern void s_5 (void);
   /* { dg-error ".worker. specifies a conflicting level of parallelism" "" { target *-*-* } .-1 } */ \
   gang gang /* { dg-error "too many 'gang' clauses" } */ \
   /* { dg-error ".gang. specifies a conflicting level of parallelism" "" { target *-*-* } .-1 } */
-#pragma acc routine (s_5) /* { dg-error ".#pragma acc routine. already applied to .\[void \]*s_5." } */ \
+#pragma acc routine (s_5) \
   seq seq seq /* { dg-error "too many 'seq' clauses" } */ \
   vector vector vector vector /* { dg-error "too many 'vector' clauses" } */ \
   /* { dg-error ".vector. specifies a conflicting level of parallelism" "" { target *-*-* } .-1 } */ \
@@ -255,7 +254,7 @@ extern void s_5 (void);
   /* { dg-error ".gang. specifies a conflicting level of parallelism" "" { target *-*-* } .-1 } */ \
   worker worker worker /* { dg-error "too many 'worker' clauses" } */ \
   /* { dg-error ".worker. specifies a conflicting level of parallelism" "" { target *-*-* } .-1 } */
-#pragma acc routine (s_5) /* { dg-error ".#pragma acc routine. already applied to .\[void \]*s_5." } */ \
+#pragma acc routine (s_5) \
   seq seq seq /* { dg-error "too many 'seq' clauses" } */ \
   worker worker /* { dg-error "too many 'worker' clauses" } */ \
   /* { dg-error ".worker. specifies a conflicting level of parallelism" "" { target *-*-* } .-1 } */ \
@@ -263,3 +262,188 @@ extern void s_5 (void);
   /* { dg-error ".vector. specifies a conflicting level of parallelism" "" { target *-*-* } .-1 } */ \
   gang gang /* { dg-error "too many 'gang' clauses" } */ \
   /* { dg-error ".gang. specifies a conflicting level of parallelism" "" { target *-*-* } .-1 } */
+
+
+/* Like the *_5 tests, but with the order of clauses changed in the second and
+   following routine directives for the specific *_5 function.  */
+
+#pragma acc routine \
+  gang gang gang /* { dg-error "too many 'gang' clauses" } */ \
+  worker worker /* { dg-error "too many 'worker' clauses" } */ \
+  /* { dg-error ".worker. specifies a conflicting level of parallelism" "" { target *-*-* } .-1 } */ \
+  vector vector vector /* { dg-error "too many 'vector' clauses" } */ \
+  /* { dg-error ".vector. specifies a conflicting level of parallelism" "" { target *-*-* } .-1 } */ \
+  seq seq seq seq seq /* { dg-error "too many 'seq' clauses" } */ \
+  /* { dg-error ".seq. specifies a conflicting level of parallelism" "" { target *-*-* } .-1 } */
+void g_6 (void)
+{
+}
+#pragma acc routine (g_6) \
+  vector vector vector vector /* { dg-error "too many 'vector' clauses" } */ \
+  /* { dg-error "incompatible .vector. clause when applying .#pragma acc routine. to .\[void \]*g_6\[\\(\\)\]*., which has already been marked as an accelerator routine" "" { target *-*-* } .-1 } */ \
+  gang gang gang /* { dg-error "too many 'gang' clauses" } */ \
+  /* { dg-error ".gang. specifies a conflicting level of parallelism" "" { target *-*-* } .-1 } */ \
+  worker worker worker /* { dg-error "too many 'worker' clauses" } */ \
+  /* { dg-error ".worker. specifies a conflicting level of parallelism" "" { target *-*-* } .-1 } */ \
+  seq seq seq seq seq seq /* { dg-error "too many 'seq' clauses" } */ \
+  /* { dg-error ".seq. specifies a conflicting level of parallelism" "" { target *-*-* } .-1 } */
+#pragma acc routine (g_6) \
+  seq seq seq seq seq /* { dg-error "too many 'seq' clauses" } */ \
+  /* { dg-error "incompatible .seq. clause when applying .#pragma acc routine. to .\[void \]*g_6\[\\(\\)\]*., which has already been marked as an accelerator routine" "" { target *-*-* } .-1 } */ \
+  gang gang gang /* { dg-error "too many 'gang' clauses" } */ \
+  /* { dg-error ".gang. specifies a conflicting level of parallelism" "" { target *-*-* } .-1 } */ \
+  vector vector vector vector /* { dg-error "too many 'vector' clauses" } */ \
+  /* { dg-error ".vector. specifies a conflicting level of parallelism" "" { target *-*-* } .-1 } */ \
+  worker worker worker worker /* { dg-error "too many 'worker' clauses" } */ \
+  /* { dg-error ".worker. specifies a conflicting level of parallelism" "" { target *-*-* } .-1 } */
+
+#pragma acc routine \
+  worker worker worker /* { dg-error "too many 'worker' clauses" } */ \
+  seq seq seq seq /* { dg-error "too many 'seq' clauses" } */ \
+  /* { dg-error ".seq. specifies a conflicting level of parallelism" "" { target *-*-* } .-1 } */ \
+  vector vector vector vector vector /* { dg-error "too many 'vector' clauses" } */ \
+  /* { dg-error ".vector. specifies a conflicting level of parallelism" "" { target *-*-* } .-1 } */ \
+  gang gang /* { dg-error "too many 'gang' clauses" } */ \
+  /* { dg-error ".gang. specifies a conflicting level of parallelism" "" { target *-*-* } .-1 } */
+extern void w_6 (void);
+#pragma acc routine (w_6) \
+  vector vector vector /* { dg-error "too many 'vector' clauses" } */ \
+  /* { dg-error "incompatible .vector. clause when applying .#pragma acc routine. to .\[void \]*w_6\[\\(\\)\]*., which has already been marked as an accelerator routine" "" { target *-*-* } .-1 } */ \
+  worker worker worker /* { dg-error "too many 'worker' clauses" } */ \
+  /* { dg-error ".worker. specifies a conflicting level of parallelism" "" { target *-*-* } .-1 } */ \
+  seq seq seq /* { dg-error "too many 'seq' clauses" } */ \
+  /* { dg-error ".seq. specifies a conflicting level of parallelism" "" { target *-*-* } .-1 } */ \
+  gang gang /* { dg-error "too many 'gang' clauses" } */ \
+  /* { dg-error ".gang. specifies a conflicting level of parallelism" "" { target *-*-* } .-1 } */
+#pragma acc routine (w_6) \
+  seq seq /* { dg-error "too many 'seq' clauses" } */ \
+  /* { dg-error "incompatible .seq. clause when applying .#pragma acc routine. to .\[void \]*w_6\[\\(\\)\]*., which has already been marked as an accelerator routine" "" { target *-*-* } .-1 } */ \
+  worker worker worker /* { dg-error "too many 'worker' clauses" } */ \
+  /* { dg-error ".worker. specifies a conflicting level of parallelism" "" { target *-*-* } .-1 } */ \
+  gang gang /* { dg-error "too many 'gang' clauses" } */ \
+  /* { dg-error ".gang. specifies a conflicting level of parallelism" "" { target *-*-* } .-1 } */ \
+  vector vector vector vector /* { dg-error "too many 'vector' clauses" } */ \
+  /* { dg-error ".vector. specifies a conflicting level of parallelism" "" { target *-*-* } .-1 } */
+
+#pragma acc routine \
+  vector vector vector /* { dg-error "too many 'vector' clauses" } */ \
+  worker worker worker /* { dg-error "too many 'worker' clauses" } */ \
+  /* { dg-error ".worker. specifies a conflicting level of parallelism" "" { target *-*-* } .-1 } */ \
+  seq seq seq seq seq seq /* { dg-error "too many 'seq' clauses" } */ \
+  /* { dg-error ".seq. specifies a conflicting level of parallelism" "" { target *-*-* } .-1 } */ \
+  gang gang /* { dg-error "too many 'gang' clauses" } */ \
+  /* { dg-error ".gang. specifies a conflicting level of parallelism" "" { target *-*-* } .-1 } */
+extern void v_6 (void);
+#pragma acc routine (v_6) \
+  seq seq seq seq seq seq /* { dg-error "too many 'seq' clauses" } */ \
+  /* { dg-error "incompatible .seq. clause when applying .#pragma acc routine. to .\[void \]*v_6\[\\(\\)\]*., which has already been marked as an accelerator routine" "" { target *-*-* } .-1 } */ \
+  vector vector vector /* { dg-error "too many 'vector' clauses" } */ \
+  /* { dg-error ".vector. specifies a conflicting level of parallelism" "" { target *-*-* } .-1 } */ \
+  worker worker /* { dg-error "too many 'worker' clauses" } */ \
+  /* { dg-error ".worker. specifies a conflicting level of parallelism" "" { target *-*-* } .-1 } */ \
+  gang gang /* { dg-error "too many 'gang' clauses" } */ \
+  /* { dg-error ".gang. specifies a conflicting level of parallelism" "" { target *-*-* } .-1 } */
+#pragma acc routine (v_6) \
+  gang gang /* { dg-error "too many 'gang' clauses" } */ \
+  /* { dg-error "incompatible .gang. clause when applying .#pragma acc routine. to .\[void \]*v_6\[\\(\\)\]*., which has already been marked as an accelerator routine" "" { target *-*-* } .-1 } */ \
+  vector vector vector /* { dg-error "too many 'vector' clauses" } */ \
+  /* { dg-error ".vector. specifies a conflicting level of parallelism" "" { target *-*-* } .-1 } */ \
+  seq seq seq seq seq /* { dg-error "too many 'seq' clauses" } */ \
+  /* { dg-error ".seq. specifies a conflicting level of parallelism" "" { target *-*-* } .-1 } */ \
+  worker worker /* { dg-error "too many 'worker' clauses" } */ \
+  /* { dg-error ".worker. specifies a conflicting level of parallelism" "" { target *-*-* } .-1 } */
+
+extern void s_6 (void);
+#pragma acc routine (s_6) \
+  seq seq seq /* { dg-error "too many 'seq' clauses" } */ \
+  vector vector vector vector /* { dg-error "too many 'vector' clauses" } */ \
+  /* { dg-error ".vector. specifies a conflicting level of parallelism" "" { target *-*-* } .-1 } */ \
+  worker worker worker worker worker /* { dg-error "too many 'worker' clauses" } */ \
+  /* { dg-error ".worker. specifies a conflicting level of parallelism" "" { target *-*-* } .-1 } */ \
+  gang gang /* { dg-error "too many 'gang' clauses" } */ \
+  /* { dg-error ".gang. specifies a conflicting level of parallelism" "" { target *-*-* } .-1 } */
+#pragma acc routine (s_6) \
+  vector vector vector vector /* { dg-error "too many 'vector' clauses" } */ \
+  /* { dg-error "incompatible .vector. clause when applying .#pragma acc routine. to .\[void \]*s_6\[\\(\\)\]*., which has already been marked as an accelerator routine" "" { target *-*-* } .-1 } */ \
+  seq seq seq /* { dg-error "too many 'seq' clauses" } */ \
+  /* { dg-error ".seq. specifies a conflicting level of parallelism" "" { target *-*-* } .-1 } */ \
+  gang gang /* { dg-error "too many 'gang' clauses" } */ \
+  /* { dg-error ".gang. specifies a conflicting level of parallelism" "" { target *-*-* } .-1 } */ \
+  worker worker worker /* { dg-error "too many 'worker' clauses" } */ \
+  /* { dg-error ".worker. specifies a conflicting level of parallelism" "" { target *-*-* } .-1 } */
+#pragma acc routine (s_6) \
+  worker worker /* { dg-error "too many 'worker' clauses" } */ \
+  /* { dg-error "incompatible .worker. clause when applying .#pragma acc routine. to .\[void \]*s_6\[\\(\\)\]*., which has already been marked as an accelerator routine" "" { target *-*-* } .-1 } */ \
+  seq seq seq /* { dg-error "too many 'seq' clauses" } */ \
+  /* { dg-error ".seq. specifies a conflicting level of parallelism" "" { target *-*-* } .-1 } */ \
+  vector vector vector vector /* { dg-error "too many 'vector' clauses" } */ \
+  /* { dg-error ".vector. specifies a conflicting level of parallelism" "" { target *-*-* } .-1 } */ \
+  gang gang /* { dg-error "too many 'gang' clauses" } */ \
+  /* { dg-error ".gang. specifies a conflicting level of parallelism" "" { target *-*-* } .-1 } */
+
+
+/* Like the *_6 tests, but without all the duplicate clauses, so that the
+   routine directives are valid in isolation.  */
+
+#pragma acc routine \
+  gang
+void g_7 (void)
+{
+}
+#pragma acc routine (g_7) \
+  vector /* { dg-error "incompatible .vector. clause when applying .#pragma acc routine. to .\[void \]*g_7\[\\(\\)\]*., which has already been marked as an accelerator routine" } */
+#pragma acc routine (g_7) \
+  seq /* { dg-error "incompatible .seq. clause when applying .#pragma acc routine. to .\[void \]*g_7\[\\(\\)\]*., which has already been marked as an accelerator routine" } */
+
+#pragma acc routine \
+  worker
+extern void w_7 (void);
+#pragma acc routine (w_7) \
+  vector /* { dg-error "incompatible .vector. clause when applying .#pragma acc routine. to .\[void \]*w_7\[\\(\\)\]*., which has already been marked as an accelerator routine" } */
+#pragma acc routine (w_7) \
+  seq /* { dg-error "incompatible .seq. clause when applying .#pragma acc routine. to .\[void \]*w_7\[\\(\\)\]*., which has already been marked as an accelerator routine" } */
+
+#pragma acc routine \
+  vector
+extern void v_7 (void);
+#pragma acc routine (v_7) \
+  seq /* { dg-error "incompatible .seq. clause when applying .#pragma acc routine. to .\[void \]*v_7\[\\(\\)\]*., which has already been marked as an accelerator routine" } */
+#pragma acc routine (v_7) \
+  gang /* { dg-error "incompatible .gang. clause when applying .#pragma acc routine. to .\[void \]*v_7\[\\(\\)\]*., which has already been marked as an accelerator routine" } */
+
+extern void s_7 (void);
+#pragma acc routine (s_7) \
+  seq
+#pragma acc routine (s_7) \
+  vector /* { dg-error "incompatible .vector. clause when applying .#pragma acc routine. to .\[void \]*s_7\[\\(\\)\]*., which has already been marked as an accelerator routine" } */
+#pragma acc routine (s_7) \
+  worker /* { dg-error "incompatible .worker. clause when applying .#pragma acc routine. to .\[void \]*s_7\[\\(\\)\]*., which has already been marked as an accelerator routine" } */
+
+
+/* Test cases for implicit seq clause.  */
+
+#pragma acc routine \
+  gang
+void g_8 (void)
+{
+}
+#pragma acc routine (g_8) /* { dg-error "incompatible .seq. clause when applying .#pragma acc routine. to .\[void \]*g_8\[\\(\\)\]*., which has already been marked as an accelerator routine" } */
+
+#pragma acc routine \
+  worker
+extern void w_8 (void);
+#pragma acc routine (w_8) /* { dg-error "incompatible .seq. clause when applying .#pragma acc routine. to .\[void \]*w_8\[\\(\\)\]*., which has already been marked as an accelerator routine" } */
+
+#pragma acc routine \
+  vector
+extern void v_8 (void);
+#pragma acc routine (v_8) /* { dg-error "incompatible .seq. clause when applying .#pragma acc routine. to .\[void \]*v_8\[\\(\\)\]*., which has already been marked as an accelerator routine" } */
+
+extern void s_8 (void);
+#pragma acc routine (s_8)
+#pragma acc routine (s_8) \
+  vector /* { dg-error "incompatible .vector. clause when applying .#pragma acc routine. to .\[void \]*s_8\[\\(\\)\]*., which has already been marked as an accelerator routine" } */
+#pragma acc routine (s_8) \
+  gang /* { dg-error "incompatible .gang. clause when applying .#pragma acc routine. to .\[void \]*s_8\[\\(\\)\]*., which has already been marked as an accelerator routine" } */
+#pragma acc routine (s_8) \
+  worker /* { dg-error "incompatible .worker. clause when applying .#pragma acc routine. to .\[void \]*s_8\[\\(\\)\]*., which has already been marked as an accelerator routine" } */
diff --git a/gcc/testsuite/c-c++-common/goacc/routine-level-of-parallelism-2.c b/gcc/testsuite/c-c++-common/goacc/routine-level-of-parallelism-2.c
new file mode 100644
index 00000000000..db4360a81ec
--- /dev/null
+++ b/gcc/testsuite/c-c++-common/goacc/routine-level-of-parallelism-2.c
@@ -0,0 +1,71 @@
+/* Test various aspects of clauses specifying compatible levels of parallelism
+   with the OpenACC routine directive.  The Fortran counterpart is
+   '../../gfortran.dg/goacc/routine-level-of-parallelism-1.f90'.  */
+
+#pragma acc routine gang
+void g_1 (void)
+{
+}
+#pragma acc routine (g_1) gang
+#pragma acc routine (g_1) gang
+
+
+extern void w_1 (void);
+#pragma acc routine (w_1) worker
+#pragma acc routine (w_1) worker
+#pragma acc routine (w_1) worker
+
+
+#pragma acc routine vector
+extern void v_1 (void);
+#pragma acc routine (v_1) vector
+#pragma acc routine (v_1) vector
+
+
+/* Also test the implicit seq clause.  */
+
+#pragma acc routine seq
+extern void s_1_1 (void);
+#pragma acc routine (s_1_1)
+#pragma acc routine (s_1_1) seq
+#pragma acc routine (s_1_1)
+#pragma acc routine (s_1_1) seq
+
+#pragma acc routine
+extern void s_1_2 (void);
+#pragma acc routine (s_1_2)
+#pragma acc routine (s_1_2) seq
+#pragma acc routine (s_1_2)
+#pragma acc routine (s_1_2) seq
+
+extern void s_2_1 (void);
+#pragma acc routine (s_2_1) seq
+#pragma acc routine (s_2_1)
+#pragma acc routine (s_2_1) seq
+#pragma acc routine (s_2_1)
+#pragma acc routine (s_2_1) seq
+
+extern void s_2_2 (void);
+#pragma acc routine (s_2_2)
+#pragma acc routine (s_2_2)
+#pragma acc routine (s_2_2) seq
+#pragma acc routine (s_2_2)
+#pragma acc routine (s_2_2) seq
+
+#pragma acc routine seq
+void s_3_1 (void)
+{
+}
+#pragma acc routine (s_3_1)
+#pragma acc routine (s_3_1) seq
+#pragma acc routine (s_3_1)
+#pragma acc routine (s_3_1) seq
+
+#pragma acc routine
+void s_3_2 (void)
+{
+}
+#pragma acc routine (s_3_2)
+#pragma acc routine (s_3_2) seq
+#pragma acc routine (s_3_2)
+#pragma acc routine (s_3_2) seq
diff --git a/gcc/testsuite/gfortran.dg/goacc/routine-level-of-parallelism-1.f90 b/gcc/testsuite/gfortran.dg/goacc/routine-level-of-parallelism-1.f90
index 75dd1b01f6f..83b8c24b41d 100644
--- a/gcc/testsuite/gfortran.dg/goacc/routine-level-of-parallelism-1.f90
+++ b/gcc/testsuite/gfortran.dg/goacc/routine-level-of-parallelism-1.f90
@@ -1,6 +1,6 @@
-! Test various aspects of clauses specifying compatible levels of
-! parallelism with the OpenACC routine directive.  The Fortran counterpart is
-! c-c++-common/goacc/routine-level-of-parallelism-2.c
+! Test various aspects of clauses specifying compatible levels of parallelism
+! with the OpenACC routine directive.  The C/C++ counterpart is
+! '../../c-c++-common/goacc/routine-level-of-parallelism-2.c'.
 
 subroutine g_1
   !$acc routine gang
-- 
2.17.1


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

* [committed] PR89433 "Repeated use of the OpenACC 'routine' directive"
  2019-02-22 12:25       ` PR89433 "Repeated use of the OpenACC 'routine' directive" Thomas Schwinge
@ 2019-05-17 19:17         ` Thomas Schwinge
  2020-03-04 19:27           ` [PATCH] Handle 'omp declare target' attribute set for both OpenACC and OpenMP 'target' [PR89433, PR93465] (was: [committed] PR89433 "Repeated use of the OpenACC 'routine' directive") Thomas Schwinge
  0 siblings, 1 reply; 16+ messages in thread
From: Thomas Schwinge @ 2019-05-17 19:17 UTC (permalink / raw)
  To: gcc-patches, fortran; +Cc: Jakub Jelinek

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

Hi!

On Fri, 22 Feb 2019 12:22:38 +0100, I wrote:
> To resolve PR89433 "Repeated use of the OpenACC 'routine' directive" at
> least for C/C++, I intend to push the attached patches in next GCC
> development stage 1 (unless that should be addressed right now, and also
> on other GCC release branches?).

(I still ponder on the question whether to eventually backport all this
to other GCC release branches...)

> The corresponding Fortran changes will require additional effort.

..., which I actually already have committed a while ago.

Now committed to trunk in r271343 "[PR89433] Refer to OpenACC 'routine'
clauses from "omp declare target" attribute", r271344 "[PR89433] Use
'oacc_verify_routine_clauses' for C/C++ OpenACC 'routine' directives",
r271345 "[PR89433] Repeated use of the C/C++ OpenACC 'routine'
directive", see attached.


Grüße
 Thomas



[-- Warning: decoded text below may be mangled, UTF-8 assumed --]
[-- Attachment #2: 0001-PR89433-Refer-to-OpenACC-routine-clauses-from-.trunk.patch --]
[-- Type: text/x-diff, Size: 10906 bytes --]

From 9b5009857b50944d7711f34dd0ac98364b7f1f9b Mon Sep 17 00:00:00 2001
From: tschwinge <tschwinge@138bc75d-0d04-0410-961f-82ee72b054a4>
Date: Fri, 17 May 2019 19:13:04 +0000
Subject: [PATCH 1/3] [PR89433] Refer to OpenACC 'routine' clauses from "omp
 declare target" attribute

	gcc/c-family/
	PR c/89433
	* c-attribs.c (c_common_attribute_table): Set min_len to -1 for
	"omp declare target".
	gcc/c/
	PR c/89433
	* c-parser.c (c_finish_oacc_routine): Refer to OpenACC 'routine'
	clauses from "omp declare target" attribute.
	gcc/cp/
	PR c++/89433
	* parser.c (cp_finalize_oacc_routine): Refer to OpenACC 'routine'
	clauses from "omp declare target" attribute.
	gcc/fortran/
	PR fortran/89433
	* f95-lang.c (gfc_attribute_table): Set min_len to -1 for "omp
	declare target".
	* trans-decl.c (add_attributes_to_decl): Refer to OpenACC
	'routine' clauses from "omp declare target" attribute.
	gcc/testsuite/
	PR testsuite/89433
	* c-c++-common/goacc/classify-routine.c: Update.
	* gfortran.dg/goacc/classify-routine.f95: Likewise.

git-svn-id: svn+ssh://gcc.gnu.org/svn/gcc/trunk@271343 138bc75d-0d04-0410-961f-82ee72b054a4
---
 gcc/c-family/ChangeLog                         |  6 ++++++
 gcc/c-family/c-attribs.c                       |  2 +-
 gcc/c/ChangeLog                                |  6 ++++++
 gcc/c/c-parser.c                               |  2 +-
 gcc/cp/ChangeLog                               |  6 ++++++
 gcc/cp/parser.c                                |  2 +-
 gcc/fortran/ChangeLog                          |  8 ++++++++
 gcc/fortran/f95-lang.c                         |  2 +-
 gcc/fortran/trans-decl.c                       | 18 +++++++++++-------
 gcc/testsuite/ChangeLog                        |  6 ++++++
 .../c-c++-common/goacc/classify-routine.c      |  4 ++--
 .../gfortran.dg/goacc/classify-routine.f95     |  4 ++--
 12 files changed, 51 insertions(+), 15 deletions(-)

diff --git a/gcc/c-family/ChangeLog b/gcc/c-family/ChangeLog
index 47c1d3d51f50..50687764221b 100644
--- a/gcc/c-family/ChangeLog
+++ b/gcc/c-family/ChangeLog
@@ -1,3 +1,9 @@
+2019-05-17  Thomas Schwinge  <thomas@codesourcery.com>
+
+	PR c/89433
+	* c-attribs.c (c_common_attribute_table): Set min_len to -1 for
+	"omp declare target".
+
 2019-05-16  Martin Sebor  <msebor@redhat.com>
 
         * c-attribs.c (handle_no_sanitize_attribute): Quote identifiers,
diff --git a/gcc/c-family/c-attribs.c b/gcc/c-family/c-attribs.c
index 12c0b9bfb543..03203470955a 100644
--- a/gcc/c-family/c-attribs.c
+++ b/gcc/c-family/c-attribs.c
@@ -437,7 +437,7 @@ const struct attribute_spec c_common_attribute_table[] =
 			      handle_omp_declare_simd_attribute, NULL },
   { "simd",		      0, 1, true,  false, false, false,
 			      handle_simd_attribute, NULL },
-  { "omp declare target",     0, 0, true, false, false, false,
+  { "omp declare target",     0, -1, true, false, false, false,
 			      handle_omp_declare_target_attribute, NULL },
   { "omp declare target link", 0, 0, true, false, false, false,
 			      handle_omp_declare_target_attribute, NULL },
diff --git a/gcc/c/ChangeLog b/gcc/c/ChangeLog
index f558d28dc259..f0cab2e65929 100644
--- a/gcc/c/ChangeLog
+++ b/gcc/c/ChangeLog
@@ -1,3 +1,9 @@
+2019-05-17  Thomas Schwinge  <thomas@codesourcery.com>
+
+	PR c/89433
+	* c-parser.c (c_finish_oacc_routine): Refer to OpenACC 'routine'
+	clauses from "omp declare target" attribute.
+
 2019-05-16  Martin Sebor  <msebor@redhat.com>
 
         * c-decl.c (start_decl): Quote keywords, operators, and
diff --git a/gcc/c/c-parser.c b/gcc/c/c-parser.c
index 993cfe05ecb0..3cbbb199bdda 100644
--- a/gcc/c/c-parser.c
+++ b/gcc/c/c-parser.c
@@ -15904,7 +15904,7 @@ c_finish_oacc_routine (struct oacc_routine_data *data, tree fndecl,
   /* Add an "omp declare target" attribute.  */
   DECL_ATTRIBUTES (fndecl)
     = tree_cons (get_identifier ("omp declare target"),
-		 NULL_TREE, DECL_ATTRIBUTES (fndecl));
+		 data->clauses, DECL_ATTRIBUTES (fndecl));
 
   /* Remember that we've used this "#pragma acc routine".  */
   data->fndecl_seen = true;
diff --git a/gcc/cp/ChangeLog b/gcc/cp/ChangeLog
index 08b7d5337071..40622acc0ff2 100644
--- a/gcc/cp/ChangeLog
+++ b/gcc/cp/ChangeLog
@@ -1,3 +1,9 @@
+2019-05-17  Thomas Schwinge  <thomas@codesourcery.com>
+
+	PR c++/89433
+	* parser.c (cp_finalize_oacc_routine): Refer to OpenACC 'routine'
+	clauses from "omp declare target" attribute.
+
 2019-05-16  Martin Sebor  <msebor@redhat.com>
 
         * call.c (print_z_candidate): Wrap diagnostic text in a gettext
diff --git a/gcc/cp/parser.c b/gcc/cp/parser.c
index e6ef5a9bc008..15424b6b6337 100644
--- a/gcc/cp/parser.c
+++ b/gcc/cp/parser.c
@@ -40292,7 +40292,7 @@ cp_finalize_oacc_routine (cp_parser *parser, tree fndecl, bool is_defn)
       /* Add an "omp declare target" attribute.  */
       DECL_ATTRIBUTES (fndecl)
 	= tree_cons (get_identifier ("omp declare target"),
-		     NULL_TREE, DECL_ATTRIBUTES (fndecl));
+		     parser->oacc_routine->clauses, DECL_ATTRIBUTES (fndecl));
 
       /* Don't unset parser->oacc_routine here: we may still need it to
 	 diagnose wrong usage.  But, remember that we've used this "#pragma acc
diff --git a/gcc/fortran/ChangeLog b/gcc/fortran/ChangeLog
index b8851067cf3e..f09e715353bd 100644
--- a/gcc/fortran/ChangeLog
+++ b/gcc/fortran/ChangeLog
@@ -1,3 +1,11 @@
+2019-05-17  Thomas Schwinge  <thomas@codesourcery.com>
+
+	PR fortran/89433
+	* f95-lang.c (gfc_attribute_table): Set min_len to -1 for "omp
+	declare target".
+	* trans-decl.c (add_attributes_to_decl): Refer to OpenACC
+	'routine' clauses from "omp declare target" attribute.
+
 2019-05-16  Martin Sebor  <msebor@redhat.com>
 
 	* gfortranspec.c (append_arg): Spell out the word "argument."
diff --git a/gcc/fortran/f95-lang.c b/gcc/fortran/f95-lang.c
index 3e3d3046bdb5..6b9f490d2bbc 100644
--- a/gcc/fortran/f95-lang.c
+++ b/gcc/fortran/f95-lang.c
@@ -91,7 +91,7 @@ static const struct attribute_spec gfc_attribute_table[] =
 {
   /* { name, min_len, max_len, decl_req, type_req, fn_type_req,
        affects_type_identity, handler, exclude } */
-  { "omp declare target", 0, 0, true,  false, false, false,
+  { "omp declare target", 0, -1, true,  false, false, false,
     gfc_handle_omp_declare_target_attribute, NULL },
   { "omp declare target link", 0, 0, true,  false, false, false,
     gfc_handle_omp_declare_target_attribute, NULL },
diff --git a/gcc/fortran/trans-decl.c b/gcc/fortran/trans-decl.c
index 07d1c33af72c..8420870a6b78 100644
--- a/gcc/fortran/trans-decl.c
+++ b/gcc/fortran/trans-decl.c
@@ -1400,12 +1400,7 @@ add_attributes_to_decl (symbol_attribute sym_attr, tree list)
 	list = chainon (list, attr);
       }
 
-  if (sym_attr.omp_declare_target_link)
-    list = tree_cons (get_identifier ("omp declare target link"),
-		      NULL_TREE, list);
-  else if (sym_attr.omp_declare_target)
-    list = tree_cons (get_identifier ("omp declare target"),
-		      NULL_TREE, list);
+  tree clauses = NULL_TREE;
 
   if (sym_attr.oacc_routine_lop != OACC_ROUTINE_LOP_NONE)
     {
@@ -1430,11 +1425,20 @@ add_attributes_to_decl (symbol_attribute sym_attr, tree list)
 	  gcc_unreachable ();
 	}
       tree c = build_omp_clause (UNKNOWN_LOCATION, code);
+      OMP_CLAUSE_CHAIN (c) = clauses;
+      clauses = c;
 
-      tree dims = oacc_build_routine_dims (c);
+      tree dims = oacc_build_routine_dims (clauses);
       list = oacc_replace_fn_attrib_attr (list, dims);
     }
 
+  if (sym_attr.omp_declare_target_link)
+    list = tree_cons (get_identifier ("omp declare target link"),
+		      NULL_TREE, list);
+  else if (sym_attr.omp_declare_target)
+    list = tree_cons (get_identifier ("omp declare target"),
+		      clauses, list);
+
   return list;
 }
 
diff --git a/gcc/testsuite/ChangeLog b/gcc/testsuite/ChangeLog
index e1aa25736efb..0f44c6d4ab0e 100644
--- a/gcc/testsuite/ChangeLog
+++ b/gcc/testsuite/ChangeLog
@@ -1,3 +1,9 @@
+2019-05-17  Thomas Schwinge  <thomas@codesourcery.com>
+
+	PR testsuite/89433
+	* c-c++-common/goacc/classify-routine.c: Update.
+	* gfortran.dg/goacc/classify-routine.f95: Likewise.
+
 2019-05-16  Martin Sebor  <msebor@redhat.com>
 
         * c-c++-common/Wbool-operation-1.c: Adjust text of expected diagnostics.
diff --git a/gcc/testsuite/c-c++-common/goacc/classify-routine.c b/gcc/testsuite/c-c++-common/goacc/classify-routine.c
index a723d2cdf513..0b9ba6ea69fc 100644
--- a/gcc/testsuite/c-c++-common/goacc/classify-routine.c
+++ b/gcc/testsuite/c-c++-common/goacc/classify-routine.c
@@ -22,10 +22,10 @@ void ROUTINE ()
 }
 
 /* Check the offloaded function's attributes.
-   { dg-final { scan-tree-dump-times "(?n)__attribute__\\(\\(omp declare target, oacc function \\(0 1, 1 0, 1 0\\)\\)\\)" 1 "ompexp" } } */
+   { dg-final { scan-tree-dump-times "(?n)__attribute__\\(\\(omp declare target \\(worker\\), oacc function \\(0 1, 1 0, 1 0\\)\\)\\)" 1 "ompexp" } } */
 
 /* Check the offloaded function's classification and compute dimensions (will
    always be 1 x 1 x 1 for non-offloading compilation).
    { dg-final { scan-tree-dump-times "(?n)Function is OpenACC routine level 1" 1 "oaccdevlow" } }
    { dg-final { scan-tree-dump-times "(?n)Compute dimensions \\\[1, 1, 1\\\]" 1 "oaccdevlow" } }
-   { dg-final { scan-tree-dump-times "(?n)__attribute__\\(\\(oacc function \\(0 1, 1 1, 1 1\\), omp declare target, oacc function \\(0 1, 1 0, 1 0\\)\\)\\)" 1 "oaccdevlow" } } */
+   { dg-final { scan-tree-dump-times "(?n)__attribute__\\(\\(oacc function \\(0 1, 1 1, 1 1\\), omp declare target \\(worker\\), oacc function \\(0 1, 1 0, 1 0\\)\\)\\)" 1 "oaccdevlow" } } */
diff --git a/gcc/testsuite/gfortran.dg/goacc/classify-routine.f95 b/gcc/testsuite/gfortran.dg/goacc/classify-routine.f95
index e435f5d7eaef..401d5270391e 100644
--- a/gcc/testsuite/gfortran.dg/goacc/classify-routine.f95
+++ b/gcc/testsuite/gfortran.dg/goacc/classify-routine.f95
@@ -21,10 +21,10 @@ subroutine ROUTINE
 end subroutine ROUTINE
 
 ! Check the offloaded function's attributes.
-! { dg-final { scan-tree-dump-times "(?n)__attribute__\\(\\(omp declare target, oacc function \\(0 1, 1 0, 1 0\\)\\)\\)" 1 "ompexp" } }
+! { dg-final { scan-tree-dump-times "(?n)__attribute__\\(\\(oacc function \\(0 1, 1 0, 1 0\\), omp declare target \\(worker\\)\\)\\)" 1 "ompexp" } }
 
 ! Check the offloaded function's classification and compute dimensions (will
 ! always be 1 x 1 x 1 for non-offloading compilation).
 ! { dg-final { scan-tree-dump-times "(?n)Function is OpenACC routine level 1" 1 "oaccdevlow" } }
 ! { dg-final { scan-tree-dump-times "(?n)Compute dimensions \\\[1, 1, 1\\\]" 1 "oaccdevlow" } }
-! { dg-final { scan-tree-dump-times "(?n)__attribute__\\(\\(oacc function \\(0 1, 1 1, 1 1\\), omp declare target, oacc function \\(0 1, 1 0, 1 0\\)\\)\\)" 1 "oaccdevlow" } }
+! { dg-final { scan-tree-dump-times "(?n)__attribute__\\(\\(oacc function \\(0 1, 1 1, 1 1\\), omp declare target \\(worker\\)\\)\\)" 1 "oaccdevlow" } }
-- 
2.17.1


[-- Warning: decoded text below may be mangled, UTF-8 assumed --]
[-- Attachment #3: 0002-PR89433-Use-oacc_verify_routine_clauses-for-C-.trunk.patch --]
[-- Type: text/x-diff, Size: 27414 bytes --]

From 5f7ea2eebbef8715d22695470221dff57fe45c42 Mon Sep 17 00:00:00 2001
From: tschwinge <tschwinge@138bc75d-0d04-0410-961f-82ee72b054a4>
Date: Fri, 17 May 2019 19:13:15 +0000
Subject: [PATCH 2/3] [PR89433] Use 'oacc_verify_routine_clauses' for C/C++
 OpenACC 'routine' directives

	gcc/
	PR middle-end/89433
	* omp-general.c (oacc_build_routine_dims): Move some of its
	processing into...
	(oacc_verify_routine_clauses): ... this new function.
	* omp-general.h (oacc_verify_routine_clauses): New prototype.
	gcc/c/
	PR c/89433
	* c-parser.c (c_parser_oacc_routine): Normalize order of clauses.
	(c_finish_oacc_routine): Call oacc_verify_routine_clauses.
	gcc/cp/
	PR c++/89433
	* parser.c (cp_parser_oacc_routine)
	(cp_parser_late_parsing_oacc_routine): Normalize order of clauses.
	(cp_finalize_oacc_routine): Call oacc_verify_routine_clauses.
	gcc/testsuite/
	PR testsuite/89433
	* c-c++-common/goacc/routine-2.c: Update, and move some test
	into...
	* c-c++-common/goacc/routine-level-of-parallelism-1.c: ... this
	new file.

git-svn-id: svn+ssh://gcc.gnu.org/svn/gcc/trunk@271344 138bc75d-0d04-0410-961f-82ee72b054a4
---
 gcc/ChangeLog                                 |   8 +
 gcc/c/ChangeLog                               |   4 +
 gcc/c/c-parser.c                              |   8 +
 gcc/cp/ChangeLog                              |   5 +
 gcc/cp/parser.c                               |   9 +
 gcc/omp-general.c                             |  66 ++++-
 gcc/omp-general.h                             |   1 +
 gcc/testsuite/ChangeLog                       |   6 +
 gcc/testsuite/c-c++-common/goacc/routine-2.c  |  20 +-
 .../goacc/routine-level-of-parallelism-1.c    | 264 ++++++++++++++++++
 10 files changed, 362 insertions(+), 29 deletions(-)
 create mode 100644 gcc/testsuite/c-c++-common/goacc/routine-level-of-parallelism-1.c

diff --git a/gcc/ChangeLog b/gcc/ChangeLog
index 5fc97248662d..b8a435860525 100644
--- a/gcc/ChangeLog
+++ b/gcc/ChangeLog
@@ -1,3 +1,11 @@
+2019-05-17  Thomas Schwinge  <thomas@codesourcery.com>
+
+	PR middle-end/89433
+	* omp-general.c (oacc_build_routine_dims): Move some of its
+	processing into...
+	(oacc_verify_routine_clauses): ... this new function.
+	* omp-general.h (oacc_verify_routine_clauses): New prototype.
+
 2019-05-17  Iain Sandoe  <iain@sandoe.co.uk>
 
 	* config/rs6000/rs6000.c (machopic_output_stub): Adjust the
diff --git a/gcc/c/ChangeLog b/gcc/c/ChangeLog
index f0cab2e65929..1393e8f47fd2 100644
--- a/gcc/c/ChangeLog
+++ b/gcc/c/ChangeLog
@@ -1,5 +1,9 @@
 2019-05-17  Thomas Schwinge  <thomas@codesourcery.com>
 
+	PR c/89433
+	* c-parser.c (c_parser_oacc_routine): Normalize order of clauses.
+	(c_finish_oacc_routine): Call oacc_verify_routine_clauses.
+
 	PR c/89433
 	* c-parser.c (c_finish_oacc_routine): Refer to OpenACC 'routine'
 	clauses from "omp declare target" attribute.
diff --git a/gcc/c/c-parser.c b/gcc/c/c-parser.c
index 3cbbb199bdda..8337f1cce0cf 100644
--- a/gcc/c/c-parser.c
+++ b/gcc/c/c-parser.c
@@ -15801,6 +15801,9 @@ c_parser_oacc_routine (c_parser *parser, enum pragma_context context)
       data.clauses
 	= c_parser_oacc_all_clauses (parser, OACC_ROUTINE_CLAUSE_MASK,
 				     "#pragma acc routine");
+      /* The clauses are in reverse order; fix that to make later diagnostic
+	 emission easier.  */
+      data.clauses = nreverse (data.clauses);
 
       if (TREE_CODE (decl) != FUNCTION_DECL)
 	{
@@ -15815,6 +15818,9 @@ c_parser_oacc_routine (c_parser *parser, enum pragma_context context)
       data.clauses
 	= c_parser_oacc_all_clauses (parser, OACC_ROUTINE_CLAUSE_MASK,
 				     "#pragma acc routine");
+      /* The clauses are in reverse order; fix that to make later diagnostic
+	 emission easier.  */
+      data.clauses = nreverse (data.clauses);
 
       /* Emit a helpful diagnostic if there's another pragma following this
 	 one.  Also don't allow a static assertion declaration, as in the
@@ -15878,6 +15884,8 @@ c_finish_oacc_routine (struct oacc_routine_data *data, tree fndecl,
       return;
     }
 
+  oacc_verify_routine_clauses (&data->clauses, data->loc);
+
   if (oacc_get_fn_attrib (fndecl))
     {
       error_at (data->loc,
diff --git a/gcc/cp/ChangeLog b/gcc/cp/ChangeLog
index 40622acc0ff2..2f1e06ca458f 100644
--- a/gcc/cp/ChangeLog
+++ b/gcc/cp/ChangeLog
@@ -1,5 +1,10 @@
 2019-05-17  Thomas Schwinge  <thomas@codesourcery.com>
 
+	PR c++/89433
+	* parser.c (cp_parser_oacc_routine)
+	(cp_parser_late_parsing_oacc_routine): Normalize order of clauses.
+	(cp_finalize_oacc_routine): Call oacc_verify_routine_clauses.
+
 	PR c++/89433
 	* parser.c (cp_finalize_oacc_routine): Refer to OpenACC 'routine'
 	clauses from "omp declare target" attribute.
diff --git a/gcc/cp/parser.c b/gcc/cp/parser.c
index 15424b6b6337..aa6507e42f41 100644
--- a/gcc/cp/parser.c
+++ b/gcc/cp/parser.c
@@ -40136,6 +40136,9 @@ cp_parser_oacc_routine (cp_parser *parser, cp_token *pragma_tok,
 	= cp_parser_oacc_all_clauses (parser, OACC_ROUTINE_CLAUSE_MASK,
 				      "#pragma acc routine",
 				      cp_lexer_peek_token (parser->lexer));
+      /* The clauses are in reverse order; fix that to make later diagnostic
+	 emission easier.  */
+      data.clauses = nreverse (data.clauses);
 
       if (decl && is_overloaded_fn (decl)
 	  && (TREE_CODE (decl) != FUNCTION_DECL
@@ -40232,6 +40235,9 @@ cp_parser_late_parsing_oacc_routine (cp_parser *parser, tree attrs)
   parser->oacc_routine->clauses
     = cp_parser_oacc_all_clauses (parser, OACC_ROUTINE_CLAUSE_MASK,
 				  "#pragma acc routine", pragma_tok);
+  /* The clauses are in reverse order; fix that to make later diagnostic
+     emission easier.  */
+  parser->oacc_routine->clauses = nreverse (parser->oacc_routine->clauses);
   cp_parser_pop_lexer (parser);
   /* Later, cp_finalize_oacc_routine will process the clauses, and then set
      fndecl_seen.  */
@@ -40266,6 +40272,9 @@ cp_finalize_oacc_routine (cp_parser *parser, tree fndecl, bool is_defn)
 	  return;
 	}
 
+      oacc_verify_routine_clauses (&parser->oacc_routine->clauses,
+				   parser->oacc_routine->loc);
+
       if (oacc_get_fn_attrib (fndecl))
 	{
 	  error_at (parser->oacc_routine->loc,
diff --git a/gcc/omp-general.c b/gcc/omp-general.c
index 356772ff4582..f1d859b0275c 100644
--- a/gcc/omp-general.c
+++ b/gcc/omp-general.c
@@ -608,9 +608,61 @@ oacc_set_fn_attrib (tree fn, tree clauses, vec<tree> *args)
     }
 }
 
-/*  Process the routine's dimension clauess to generate an attribute
-    value.  Issue diagnostics as appropriate.  We default to SEQ
-    (OpenACC 2.5 clarifies this). All dimensions have a size of zero
+/* Verify OpenACC routine clauses.
+
+   Upon returning, the chain of clauses will contain exactly one clause
+   specifying the level of parallelism.  */
+
+void
+oacc_verify_routine_clauses (tree *clauses, location_t loc)
+{
+  tree c_level = NULL_TREE;
+  tree c_p = NULL_TREE;
+  for (tree c = *clauses; c; c_p = c, c = OMP_CLAUSE_CHAIN (c))
+    switch (OMP_CLAUSE_CODE (c))
+      {
+      case OMP_CLAUSE_GANG:
+      case OMP_CLAUSE_WORKER:
+      case OMP_CLAUSE_VECTOR:
+      case OMP_CLAUSE_SEQ:
+	if (c_level == NULL_TREE)
+	  c_level = c;
+	else if (OMP_CLAUSE_CODE (c) == OMP_CLAUSE_CODE (c_level))
+	  {
+	    /* This has already been diagnosed in the front ends.  */
+	    /* Drop the duplicate clause.  */
+	    gcc_checking_assert (c_p != NULL_TREE);
+	    OMP_CLAUSE_CHAIN (c_p) = OMP_CLAUSE_CHAIN (c);
+	    c = c_p;
+	  }
+	else
+	  {
+	    error_at (OMP_CLAUSE_LOCATION (c),
+		      "%qs specifies a conflicting level of parallelism",
+		      omp_clause_code_name[OMP_CLAUSE_CODE (c)]);
+	    inform (OMP_CLAUSE_LOCATION (c_level),
+		    "... to the previous %qs clause here",
+		    omp_clause_code_name[OMP_CLAUSE_CODE (c_level)]);
+	    /* Drop the conflicting clause.  */
+	    gcc_checking_assert (c_p != NULL_TREE);
+	    OMP_CLAUSE_CHAIN (c_p) = OMP_CLAUSE_CHAIN (c);
+	    c = c_p;
+	  }
+	break;
+      default:
+	gcc_unreachable ();
+      }
+  if (c_level == NULL_TREE)
+    {
+      /* Default to an implicit 'seq' clause.  */
+      c_level = build_omp_clause (loc, OMP_CLAUSE_SEQ);
+      OMP_CLAUSE_CHAIN (c_level) = *clauses;
+      *clauses = c_level;
+    }
+}
+
+/*  Process the OpenACC 'routine' directive clauses to generate an attribute
+    for the level of parallelism.  All dimensions have a size of zero
     (dynamic).  TREE_PURPOSE is set to indicate whether that dimension
     can have a loop partitioned on it.  non-zero indicates
     yes, zero indicates no.  By construction once a non-zero has been
@@ -632,16 +684,10 @@ oacc_build_routine_dims (tree clauses)
     for (ix = GOMP_DIM_MAX + 1; ix--;)
       if (OMP_CLAUSE_CODE (clauses) == ids[ix])
 	{
-	  if (level >= 0)
-	    error_at (OMP_CLAUSE_LOCATION (clauses),
-		      "multiple loop axes specified for routine");
 	  level = ix;
 	  break;
 	}
-
-  /* Default to SEQ.  */
-  if (level < 0)
-    level = GOMP_DIM_MAX;
+  gcc_checking_assert (level >= 0);
 
   tree dims = NULL_TREE;
 
diff --git a/gcc/omp-general.h b/gcc/omp-general.h
index 60faa5213a27..4241c33d99e0 100644
--- a/gcc/omp-general.h
+++ b/gcc/omp-general.h
@@ -84,6 +84,7 @@ extern tree oacc_launch_pack (unsigned code, tree device, unsigned op);
 extern tree oacc_replace_fn_attrib_attr (tree attribs, tree dims);
 extern void oacc_replace_fn_attrib (tree fn, tree dims);
 extern void oacc_set_fn_attrib (tree fn, tree clauses, vec<tree> *args);
+extern void oacc_verify_routine_clauses (tree *, location_t);
 extern tree oacc_build_routine_dims (tree clauses);
 extern tree oacc_get_fn_attrib (tree fn);
 extern bool offloading_function_p (tree fn);
diff --git a/gcc/testsuite/ChangeLog b/gcc/testsuite/ChangeLog
index 0f44c6d4ab0e..4b07888dadbc 100644
--- a/gcc/testsuite/ChangeLog
+++ b/gcc/testsuite/ChangeLog
@@ -1,5 +1,11 @@
 2019-05-17  Thomas Schwinge  <thomas@codesourcery.com>
 
+	PR testsuite/89433
+	* c-c++-common/goacc/routine-2.c: Update, and move some test
+	into...
+	* c-c++-common/goacc/routine-level-of-parallelism-1.c: ... this
+	new file.
+
 	PR testsuite/89433
 	* c-c++-common/goacc/classify-routine.c: Update.
 	* gfortran.dg/goacc/classify-routine.f95: Likewise.
diff --git a/gcc/testsuite/c-c++-common/goacc/routine-2.c b/gcc/testsuite/c-c++-common/goacc/routine-2.c
index fc5eb11bb54d..be1510a369ca 100644
--- a/gcc/testsuite/c-c++-common/goacc/routine-2.c
+++ b/gcc/testsuite/c-c++-common/goacc/routine-2.c
@@ -1,21 +1,3 @@
-#pragma acc routine gang worker /* { dg-error "multiple loop axes" } */
-void gang (void)
-{
-}
-
-#pragma acc routine worker vector /* { dg-error "multiple loop axes" } */
-void worker (void)
-{
-}
-
-#pragma acc routine vector seq /* { dg-error "multiple loop axes" } */
-void vector (void)
-{
-}
-
-#pragma acc routine seq gang /* { dg-error "multiple loop axes" } */
-void seq (void)
-{
-}
+/* Test invalid use of the OpenACC 'routine' directive.  */
 
 #pragma acc routine (nothing) gang /* { dg-error "not been declared" } */
diff --git a/gcc/testsuite/c-c++-common/goacc/routine-level-of-parallelism-1.c b/gcc/testsuite/c-c++-common/goacc/routine-level-of-parallelism-1.c
new file mode 100644
index 000000000000..ab0414bfed69
--- /dev/null
+++ b/gcc/testsuite/c-c++-common/goacc/routine-level-of-parallelism-1.c
@@ -0,0 +1,264 @@
+/* Test various aspects of clauses specifying incompatible levels of
+   parallelism with the OpenACC 'routine' directive.  */
+
+extern void g_1 (void);
+#pragma acc routine (g_1) gang gang /* { dg-error "too many 'gang' clauses" } */
+
+#pragma acc routine worker worker /* { dg-error "too many 'worker' clauses" } */
+void w_1 (void)
+{
+}
+
+#pragma acc routine vector vector /* { dg-error "too many 'vector' clauses" } */
+void v_1 (void)
+{
+}
+
+#pragma acc routine seq seq /* { dg-error "too many 'seq' clauses" } */
+extern void s_1 (void);
+
+
+#pragma acc routine gang gang gang /* { dg-error "too many 'gang' clauses" } */
+void g_2 (void)
+{
+}
+
+#pragma acc routine worker worker worker /* { dg-error "too many 'worker' clauses" } */
+extern void w_2 (void);
+
+extern void v_2 (void);
+#pragma acc routine (v_2) vector vector vector /* { dg-error "too many 'vector' clauses" } */
+
+#pragma acc routine seq seq seq /* { dg-error "too many 'seq' clauses" } */
+void s_2 (void)
+{
+}
+
+
+#pragma acc routine \
+  gang \
+  worker /* { dg-error ".worker. specifies a conflicting level of parallelism" } */
+void g_3 (void)
+{
+}
+#pragma acc routine (g_3) /* { dg-error ".#pragma acc routine. already applied to .\[void \]*g_3." } */ \
+  gang \
+  seq /* { dg-error ".seq. specifies a conflicting level of parallelism" } */
+#pragma acc routine (g_3) /* { dg-error ".#pragma acc routine. already applied to .\[void \]*g_3." } */ \
+  gang \
+  vector /* { dg-error ".vector. specifies a conflicting level of parallelism" } */
+
+extern void w_3 (void);
+#pragma acc routine (w_3) \
+  worker \
+  vector /* { dg-error ".vector. specifies a conflicting level of parallelism" } */
+#pragma acc routine (w_3) /* { dg-error ".#pragma acc routine. already applied to .\[void \]*w_3." } */ \
+  worker \
+  gang /* { dg-error ".gang. specifies a conflicting level of parallelism" } */
+#pragma acc routine (w_3) /* { dg-error ".#pragma acc routine. already applied to .\[void \]*w_3." } */ \
+  worker \
+  seq /* { dg-error ".seq. specifies a conflicting level of parallelism" } */
+
+#pragma acc routine \
+  vector \
+  seq /* { dg-error ".seq. specifies a conflicting level of parallelism" } */
+void v_3 (void)
+{
+}
+#pragma acc routine (v_3) /* { dg-error ".#pragma acc routine. already applied to .\[void \]*v_3." } */ \
+  vector \
+  worker /* { dg-error ".worker. specifies a conflicting level of parallelism" } */
+#pragma acc routine (v_3) /* { dg-error ".#pragma acc routine. already applied to .\[void \]*v_3." } */ \
+  vector \
+  gang /* { dg-error ".gang. specifies a conflicting level of parallelism" } */
+
+extern void s_3 (void);
+#pragma acc routine (s_3) \
+  seq \
+  gang /* { dg-error ".gang. specifies a conflicting level of parallelism" } */
+#pragma acc routine (s_3) /* { dg-error ".#pragma acc routine. already applied to .\[void \]*s_3." } */ \
+  seq \
+  vector /* { dg-error ".vector. specifies a conflicting level of parallelism" } */
+#pragma acc routine (s_3) /* { dg-error ".#pragma acc routine. already applied to .\[void \]*s_3." } */ \
+  seq \
+  worker /* { dg-error ".worker. specifies a conflicting level of parallelism" } */
+
+
+#pragma acc routine \
+  gang gang gang /* { dg-error "too many 'gang' clauses" } */ \
+  worker /* { dg-error ".worker. specifies a conflicting level of parallelism" } */ \
+  vector /* { dg-error ".vector. specifies a conflicting level of parallelism" } */ \
+  seq /* { dg-error ".seq. specifies a conflicting level of parallelism" } */
+extern void g_4 (void);
+#pragma acc routine (g_4) /* { dg-error ".#pragma acc routine. already applied to .\[void \]*g_4." } */ \
+  gang gang gang /* { dg-error "too many 'gang' clauses" } */ \
+  vector /* { dg-error ".vector. specifies a conflicting level of parallelism" } */ \
+  worker /* { dg-error ".worker. specifies a conflicting level of parallelism" } */ \
+  seq /* { dg-error ".seq. specifies a conflicting level of parallelism" } */
+#pragma acc routine (g_4) /* { dg-error ".#pragma acc routine. already applied to .\[void \]*g_4." } */ \
+  gang gang gang /* { dg-error "too many 'gang' clauses" } */ \
+  seq /* { dg-error ".seq. specifies a conflicting level of parallelism" } */ \
+  vector /* { dg-error ".vector. specifies a conflicting level of parallelism" } */ \
+  worker /* { dg-error ".worker. specifies a conflicting level of parallelism" } */
+
+extern void w_4 (void);
+#pragma acc routine (w_4) \
+  worker worker worker /* { dg-error "too many 'worker' clauses" } */ \
+  seq /* { dg-error ".seq. specifies a conflicting level of parallelism" } */ \
+  vector /* { dg-error ".vector. specifies a conflicting level of parallelism" } */ \
+  gang /* { dg-error ".gang. specifies a conflicting level of parallelism" } */
+#pragma acc routine (w_4) /* { dg-error ".#pragma acc routine. already applied to .\[void \]*w_4." } */ \
+  worker worker worker /* { dg-error "too many 'worker' clauses" } */ \
+  vector /* { dg-error ".vector. specifies a conflicting level of parallelism" } */ \
+  seq /* { dg-error ".seq. specifies a conflicting level of parallelism" } */ \
+  gang /* { dg-error ".gang. specifies a conflicting level of parallelism" } */
+#pragma acc routine (w_4) /* { dg-error ".#pragma acc routine. already applied to .\[void \]*w_4." } */ \
+  worker worker worker /* { dg-error "too many 'worker' clauses" } */ \
+  seq /* { dg-error ".seq. specifies a conflicting level of parallelism" } */ \
+  gang /* { dg-error ".gang. specifies a conflicting level of parallelism" } */ \
+  vector /* { dg-error ".vector. specifies a conflicting level of parallelism" } */
+
+#pragma acc routine \
+  vector vector vector /* { dg-error "too many 'vector' clauses" } */ \
+  worker /* { dg-error ".worker. specifies a conflicting level of parallelism" } */ \
+  seq /* { dg-error ".seq. specifies a conflicting level of parallelism" } */ \
+  gang /* { dg-error ".gang. specifies a conflicting level of parallelism" } */
+void v_4 (void)
+{
+}
+#pragma acc routine (v_4) /* { dg-error ".#pragma acc routine. already applied to .\[void \]*v_4." } */ \
+  vector vector vector /* { dg-error "too many 'vector' clauses" } */ \
+  seq /* { dg-error ".seq. specifies a conflicting level of parallelism" } */ \
+  worker /* { dg-error ".worker. specifies a conflicting level of parallelism" } */ \
+  gang /* { dg-error ".gang. specifies a conflicting level of parallelism" } */
+#pragma acc routine (v_4) /* { dg-error ".#pragma acc routine. already applied to .\[void \]*v_4." } */ \
+  vector vector vector /* { dg-error "too many 'vector' clauses" } */ \
+  gang /* { dg-error ".gang. specifies a conflicting level of parallelism" } */ \
+  seq /* { dg-error ".seq. specifies a conflicting level of parallelism" } */ \
+  worker /* { dg-error ".worker. specifies a conflicting level of parallelism" } */
+
+#pragma acc routine \
+  seq seq seq /* { dg-error "too many 'seq' clauses" } */ \
+  vector /* { dg-error ".vector. specifies a conflicting level of parallelism" } */ \
+  worker /* { dg-error ".worker. specifies a conflicting level of parallelism" } */ \
+  gang /* { dg-error ".gang. specifies a conflicting level of parallelism" } */
+void s_4 (void)
+{
+}
+#pragma acc routine (s_4) /* { dg-error ".#pragma acc routine. already applied to .\[void \]*s_4." } */ \
+  seq seq seq /* { dg-error "too many 'seq' clauses" } */ \
+  vector /* { dg-error ".vector. specifies a conflicting level of parallelism" } */ \
+  gang /* { dg-error ".gang. specifies a conflicting level of parallelism" } */ \
+  worker /* { dg-error ".worker. specifies a conflicting level of parallelism" } */
+#pragma acc routine (s_4) /* { dg-error ".#pragma acc routine. already applied to .\[void \]*s_4." } */ \
+  seq seq seq /* { dg-error "too many 'seq' clauses" } */ \
+  worker /* { dg-error ".worker. specifies a conflicting level of parallelism" } */ \
+  vector /* { dg-error ".vector. specifies a conflicting level of parallelism" } */ \
+  gang /* { dg-error ".gang. specifies a conflicting level of parallelism" } */
+
+
+#pragma acc routine \
+  gang gang gang /* { dg-error "too many 'gang' clauses" } */ \
+  worker worker /* { dg-error "too many 'worker' clauses" } */ \
+  /* { dg-error ".worker. specifies a conflicting level of parallelism" "" { target *-*-* } .-1 } */ \
+  vector vector vector /* { dg-error "too many 'vector' clauses" } */ \
+  /* { dg-error ".vector. specifies a conflicting level of parallelism" "" { target *-*-* } .-1 } */ \
+  seq seq seq seq seq /* { dg-error "too many 'seq' clauses" } */ \
+  /* { dg-error ".seq. specifies a conflicting level of parallelism" "" { target *-*-* } .-1 } */
+void g_5 (void)
+{
+}
+#pragma acc routine (g_5) /* { dg-error ".#pragma acc routine. already applied to .\[void \]*g_5." } */ \
+  gang gang gang /* { dg-error "too many 'gang' clauses" } */ \
+  vector vector vector vector /* { dg-error "too many 'vector' clauses" } */ \
+  /* { dg-error ".vector. specifies a conflicting level of parallelism" "" { target *-*-* } .-1 } */ \
+  worker worker worker /* { dg-error "too many 'worker' clauses" } */ \
+  /* { dg-error ".worker. specifies a conflicting level of parallelism" "" { target *-*-* } .-1 } */ \
+  seq seq seq seq seq seq /* { dg-error "too many 'seq' clauses" } */ \
+  /* { dg-error ".seq. specifies a conflicting level of parallelism" "" { target *-*-* } .-1 } */
+#pragma acc routine (g_5) /* { dg-error ".#pragma acc routine. already applied to .\[void \]*g_5." } */ \
+  gang gang gang /* { dg-error "too many 'gang' clauses" } */ \
+  seq seq seq seq seq /* { dg-error "too many 'seq' clauses" } */ \
+  /* { dg-error ".seq. specifies a conflicting level of parallelism" "" { target *-*-* } .-1 } */ \
+  vector vector vector vector /* { dg-error "too many 'vector' clauses" } */ \
+  /* { dg-error ".vector. specifies a conflicting level of parallelism" "" { target *-*-* } .-1 } */ \
+  worker worker worker worker /* { dg-error "too many 'worker' clauses" } */ \
+  /* { dg-error ".worker. specifies a conflicting level of parallelism" "" { target *-*-* } .-1 } */
+
+#pragma acc routine \
+  worker worker worker /* { dg-error "too many 'worker' clauses" } */ \
+  seq seq seq seq /* { dg-error "too many 'seq' clauses" } */ \
+  /* { dg-error ".seq. specifies a conflicting level of parallelism" "" { target *-*-* } .-1 } */ \
+  vector vector vector vector vector /* { dg-error "too many 'vector' clauses" } */ \
+  /* { dg-error ".vector. specifies a conflicting level of parallelism" "" { target *-*-* } .-1 } */ \
+  gang gang /* { dg-error "too many 'gang' clauses" } */ \
+  /* { dg-error ".gang. specifies a conflicting level of parallelism" "" { target *-*-* } .-1 } */
+extern void w_5 (void);
+#pragma acc routine (w_5) /* { dg-error ".#pragma acc routine. already applied to .\[void \]*w_5." } */ \
+  worker worker worker /* { dg-error "too many 'worker' clauses" } */ \
+  vector vector vector /* { dg-error "too many 'vector' clauses" } */ \
+  /* { dg-error ".vector. specifies a conflicting level of parallelism" "" { target *-*-* } .-1 } */ \
+  seq seq seq /* { dg-error "too many 'seq' clauses" } */ \
+  /* { dg-error ".seq. specifies a conflicting level of parallelism" "" { target *-*-* } .-1 } */ \
+  gang gang /* { dg-error "too many 'gang' clauses" } */ \
+  /* { dg-error ".gang. specifies a conflicting level of parallelism" "" { target *-*-* } .-1 } */
+#pragma acc routine (w_5) /* { dg-error ".#pragma acc routine. already applied to .\[void \]*w_5." } */ \
+  worker worker worker /* { dg-error "too many 'worker' clauses" } */ \
+  seq seq /* { dg-error "too many 'seq' clauses" } */ \
+  /* { dg-error ".seq. specifies a conflicting level of parallelism" "" { target *-*-* } .-1 } */ \
+  gang gang /* { dg-error "too many 'gang' clauses" } */ \
+  /* { dg-error ".gang. specifies a conflicting level of parallelism" "" { target *-*-* } .-1 } */ \
+  vector vector vector vector /* { dg-error "too many 'vector' clauses" } */ \
+  /* { dg-error ".vector. specifies a conflicting level of parallelism" "" { target *-*-* } .-1 } */
+
+#pragma acc routine \
+  vector vector vector /* { dg-error "too many 'vector' clauses" } */ \
+  worker worker worker /* { dg-error "too many 'worker' clauses" } */ \
+  /* { dg-error ".worker. specifies a conflicting level of parallelism" "" { target *-*-* } .-1 } */ \
+  seq seq seq seq seq seq /* { dg-error "too many 'seq' clauses" } */ \
+  /* { dg-error ".seq. specifies a conflicting level of parallelism" "" { target *-*-* } .-1 } */ \
+  gang gang /* { dg-error "too many 'gang' clauses" } */ \
+  /* { dg-error ".gang. specifies a conflicting level of parallelism" "" { target *-*-* } .-1 } */
+extern void v_5 (void);
+#pragma acc routine (v_5) /* { dg-error ".#pragma acc routine. already applied to .\[void \]*v_5." } */ \
+  vector vector vector /* { dg-error "too many 'vector' clauses" } */ \
+  seq seq seq seq seq seq /* { dg-error "too many 'seq' clauses" } */ \
+  /* { dg-error ".seq. specifies a conflicting level of parallelism" "" { target *-*-* } .-1 } */ \
+  worker worker /* { dg-error "too many 'worker' clauses" } */ \
+  /* { dg-error ".worker. specifies a conflicting level of parallelism" "" { target *-*-* } .-1 } */ \
+  gang gang /* { dg-error "too many 'gang' clauses" } */ \
+  /* { dg-error ".gang. specifies a conflicting level of parallelism" "" { target *-*-* } .-1 } */
+#pragma acc routine (v_5) /* { dg-error ".#pragma acc routine. already applied to .\[void \]*v_5." } */ \
+  vector vector vector /* { dg-error "too many 'vector' clauses" } */ \
+  gang gang /* { dg-error "too many 'gang' clauses" } */ \
+  /* { dg-error ".gang. specifies a conflicting level of parallelism" "" { target *-*-* } .-1 } */ \
+  seq seq seq seq seq /* { dg-error "too many 'seq' clauses" } */ \
+  /* { dg-error ".seq. specifies a conflicting level of parallelism" "" { target *-*-* } .-1 } */ \
+  worker worker /* { dg-error "too many 'worker' clauses" } */ \
+  /* { dg-error ".worker. specifies a conflicting level of parallelism" "" { target *-*-* } .-1 } */
+
+extern void s_5 (void);
+#pragma acc routine (s_5) \
+  seq seq seq /* { dg-error "too many 'seq' clauses" } */ \
+  vector vector vector vector /* { dg-error "too many 'vector' clauses" } */ \
+  /* { dg-error ".vector. specifies a conflicting level of parallelism" "" { target *-*-* } .-1 } */ \
+  worker worker worker worker worker /* { dg-error "too many 'worker' clauses" } */ \
+  /* { dg-error ".worker. specifies a conflicting level of parallelism" "" { target *-*-* } .-1 } */ \
+  gang gang /* { dg-error "too many 'gang' clauses" } */ \
+  /* { dg-error ".gang. specifies a conflicting level of parallelism" "" { target *-*-* } .-1 } */
+#pragma acc routine (s_5) /* { dg-error ".#pragma acc routine. already applied to .\[void \]*s_5." } */ \
+  seq seq seq /* { dg-error "too many 'seq' clauses" } */ \
+  vector vector vector vector /* { dg-error "too many 'vector' clauses" } */ \
+  /* { dg-error ".vector. specifies a conflicting level of parallelism" "" { target *-*-* } .-1 } */ \
+  gang gang /* { dg-error "too many 'gang' clauses" } */ \
+  /* { dg-error ".gang. specifies a conflicting level of parallelism" "" { target *-*-* } .-1 } */ \
+  worker worker worker /* { dg-error "too many 'worker' clauses" } */ \
+  /* { dg-error ".worker. specifies a conflicting level of parallelism" "" { target *-*-* } .-1 } */
+#pragma acc routine (s_5) /* { dg-error ".#pragma acc routine. already applied to .\[void \]*s_5." } */ \
+  seq seq seq /* { dg-error "too many 'seq' clauses" } */ \
+  worker worker /* { dg-error "too many 'worker' clauses" } */ \
+  /* { dg-error ".worker. specifies a conflicting level of parallelism" "" { target *-*-* } .-1 } */ \
+  vector vector vector vector /* { dg-error "too many 'vector' clauses" } */ \
+  /* { dg-error ".vector. specifies a conflicting level of parallelism" "" { target *-*-* } .-1 } */ \
+  gang gang /* { dg-error "too many 'gang' clauses" } */ \
+  /* { dg-error ".gang. specifies a conflicting level of parallelism" "" { target *-*-* } .-1 } */
-- 
2.17.1


[-- Warning: decoded text below may be mangled, UTF-8 assumed --]
[-- Attachment #4: 0003-PR89433-Repeated-use-of-the-C-C-OpenACC-routin.trunk.patch --]
[-- Type: text/x-diff, Size: 43109 bytes --]

From 33dacef99912c0fb7aa832fb241ac32f7130a1e8 Mon Sep 17 00:00:00 2001
From: tschwinge <tschwinge@138bc75d-0d04-0410-961f-82ee72b054a4>
Date: Fri, 17 May 2019 19:13:26 +0000
Subject: [PATCH 3/3] [PR89433] Repeated use of the C/C++ OpenACC 'routine'
 directive

	gcc/
	PR middle-end/89433
	* omp-general.c (oacc_verify_routine_clauses): Change formal
	parameters.  Add checking if already marked with an OpenACC
	'routine' directive.  Adjust all users.
	gcc/c/
	PR c/89433
	* c-parser.c (c_finish_oacc_routine): Rework checking if already
	marked with an OpenACC 'routine' directive.
	gcc/cp/
	PR c++/89433
	* parser.c (cp_finalize_oacc_routine): Rework checking if already
	marked with an OpenACC 'routine' directive.
	gcc/testsuite/
	PR testsuite/89433
	* c-c++-common/goacc/routine-5.c: Update.
	* c-c++-common/goacc/routine-level-of-parallelism-1.c: Likewise.
	* c-c++-common/goacc/routine-level-of-parallelism-2.c: New file.

git-svn-id: svn+ssh://gcc.gnu.org/svn/gcc/trunk@271345 138bc75d-0d04-0410-961f-82ee72b054a4
---
 gcc/ChangeLog                                 |   5 +
 gcc/c/ChangeLog                               |   4 +
 gcc/c/c-parser.c                              |  46 ++--
 gcc/cp/ChangeLog                              |   4 +
 gcc/cp/parser.c                               |  50 ++--
 gcc/omp-general.c                             |  81 +++++-
 gcc/omp-general.h                             |   3 +-
 gcc/testsuite/ChangeLog                       |   5 +
 gcc/testsuite/c-c++-common/goacc/routine-5.c  |  46 +---
 .../goacc/routine-level-of-parallelism-1.c    | 233 ++++++++++++++++--
 .../goacc/routine-level-of-parallelism-2.c    |  71 ++++++
 .../goacc/routine-level-of-parallelism-1.f90  |   6 +-
 12 files changed, 437 insertions(+), 117 deletions(-)
 create mode 100644 gcc/testsuite/c-c++-common/goacc/routine-level-of-parallelism-2.c

diff --git a/gcc/ChangeLog b/gcc/ChangeLog
index b8a435860525..bee1292ac229 100644
--- a/gcc/ChangeLog
+++ b/gcc/ChangeLog
@@ -1,5 +1,10 @@
 2019-05-17  Thomas Schwinge  <thomas@codesourcery.com>
 
+	PR middle-end/89433
+	* omp-general.c (oacc_verify_routine_clauses): Change formal
+	parameters.  Add checking if already marked with an OpenACC
+	'routine' directive.  Adjust all users.
+
 	PR middle-end/89433
 	* omp-general.c (oacc_build_routine_dims): Move some of its
 	processing into...
diff --git a/gcc/c/ChangeLog b/gcc/c/ChangeLog
index 1393e8f47fd2..cfbd164fdc3c 100644
--- a/gcc/c/ChangeLog
+++ b/gcc/c/ChangeLog
@@ -1,5 +1,9 @@
 2019-05-17  Thomas Schwinge  <thomas@codesourcery.com>
 
+	PR c/89433
+	* c-parser.c (c_finish_oacc_routine): Rework checking if already
+	marked with an OpenACC 'routine' directive.
+
 	PR c/89433
 	* c-parser.c (c_parser_oacc_routine): Normalize order of clauses.
 	(c_finish_oacc_routine): Call oacc_verify_routine_clauses.
diff --git a/gcc/c/c-parser.c b/gcc/c/c-parser.c
index 8337f1cce0cf..8f610242435f 100644
--- a/gcc/c/c-parser.c
+++ b/gcc/c/c-parser.c
@@ -15884,35 +15884,39 @@ c_finish_oacc_routine (struct oacc_routine_data *data, tree fndecl,
       return;
     }
 
-  oacc_verify_routine_clauses (&data->clauses, data->loc);
-
-  if (oacc_get_fn_attrib (fndecl))
+  int compatible
+    = oacc_verify_routine_clauses (fndecl, &data->clauses, data->loc,
+				   "#pragma acc routine");
+  if (compatible < 0)
     {
-      error_at (data->loc,
-		"%<#pragma acc routine%> already applied to %qD", fndecl);
       data->error_seen = true;
       return;
     }
-
-  if (TREE_USED (fndecl) || (!is_defn && DECL_SAVED_TREE (fndecl)))
+  if (compatible > 0)
     {
-      error_at (data->loc,
-		TREE_USED (fndecl)
-		? G_("%<#pragma acc routine%> must be applied before use")
-		: G_("%<#pragma acc routine%> must be applied before "
-		     "definition"));
-      data->error_seen = true;
-      return;
     }
+  else
+    {
+      if (TREE_USED (fndecl) || (!is_defn && DECL_SAVED_TREE (fndecl)))
+	{
+	  error_at (data->loc,
+		    TREE_USED (fndecl)
+		    ? G_("%<#pragma acc routine%> must be applied before use")
+		    : G_("%<#pragma acc routine%> must be applied before"
+			 " definition"));
+	  data->error_seen = true;
+	  return;
+	}
 
-  /* Process the routine's dimension clauses.  */
-  tree dims = oacc_build_routine_dims (data->clauses);
-  oacc_replace_fn_attrib (fndecl, dims);
+      /* Set the routine's level of parallelism.  */
+      tree dims = oacc_build_routine_dims (data->clauses);
+      oacc_replace_fn_attrib (fndecl, dims);
 
-  /* Add an "omp declare target" attribute.  */
-  DECL_ATTRIBUTES (fndecl)
-    = tree_cons (get_identifier ("omp declare target"),
-		 data->clauses, DECL_ATTRIBUTES (fndecl));
+      /* Add an "omp declare target" attribute.  */
+      DECL_ATTRIBUTES (fndecl)
+	= tree_cons (get_identifier ("omp declare target"),
+		     data->clauses, DECL_ATTRIBUTES (fndecl));
+    }
 
   /* Remember that we've used this "#pragma acc routine".  */
   data->fndecl_seen = true;
diff --git a/gcc/cp/ChangeLog b/gcc/cp/ChangeLog
index 2f1e06ca458f..39aaab966c90 100644
--- a/gcc/cp/ChangeLog
+++ b/gcc/cp/ChangeLog
@@ -1,5 +1,9 @@
 2019-05-17  Thomas Schwinge  <thomas@codesourcery.com>
 
+	PR c++/89433
+	* parser.c (cp_finalize_oacc_routine): Rework checking if already
+	marked with an OpenACC 'routine' directive.
+
 	PR c++/89433
 	* parser.c (cp_parser_oacc_routine)
 	(cp_parser_late_parsing_oacc_routine): Normalize order of clauses.
diff --git a/gcc/cp/parser.c b/gcc/cp/parser.c
index aa6507e42f41..6705d64389c1 100644
--- a/gcc/cp/parser.c
+++ b/gcc/cp/parser.c
@@ -40272,36 +40272,42 @@ cp_finalize_oacc_routine (cp_parser *parser, tree fndecl, bool is_defn)
 	  return;
 	}
 
-      oacc_verify_routine_clauses (&parser->oacc_routine->clauses,
-				   parser->oacc_routine->loc);
-
-      if (oacc_get_fn_attrib (fndecl))
+      int compatible
+	= oacc_verify_routine_clauses (fndecl, &parser->oacc_routine->clauses,
+				       parser->oacc_routine->loc,
+				       "#pragma acc routine");
+      if (compatible < 0)
 	{
-	  error_at (parser->oacc_routine->loc,
-		    "%<#pragma acc routine%> already applied to %qD", fndecl);
 	  parser->oacc_routine = NULL;
 	  return;
 	}
-
-      if (TREE_USED (fndecl) || (!is_defn && DECL_SAVED_TREE (fndecl)))
+      if (compatible > 0)
 	{
-	  error_at (parser->oacc_routine->loc,
-		    TREE_USED (fndecl)
-		    ? G_("%<#pragma acc routine%> must be applied before use")
-		    : G_("%<#pragma acc routine%> must be applied before "
-			 "definition"));
-	  parser->oacc_routine = NULL;
-	  return;
 	}
+      else
+	{
+	  if (TREE_USED (fndecl) || (!is_defn && DECL_SAVED_TREE (fndecl)))
+	    {
+	      error_at (parser->oacc_routine->loc,
+			TREE_USED (fndecl)
+			? G_("%<#pragma acc routine%> must be applied before"
+			     " use")
+			: G_("%<#pragma acc routine%> must be applied before"
+			     " definition"));
+	      parser->oacc_routine = NULL;
+	      return;
+	    }
 
-      /* Process the routine's dimension clauses.  */
-      tree dims = oacc_build_routine_dims (parser->oacc_routine->clauses);
-      oacc_replace_fn_attrib (fndecl, dims);
+	  /* Set the routine's level of parallelism.  */
+	  tree dims = oacc_build_routine_dims (parser->oacc_routine->clauses);
+	  oacc_replace_fn_attrib (fndecl, dims);
 
-      /* Add an "omp declare target" attribute.  */
-      DECL_ATTRIBUTES (fndecl)
-	= tree_cons (get_identifier ("omp declare target"),
-		     parser->oacc_routine->clauses, DECL_ATTRIBUTES (fndecl));
+	  /* Add an "omp declare target" attribute.  */
+	  DECL_ATTRIBUTES (fndecl)
+	    = tree_cons (get_identifier ("omp declare target"),
+			 parser->oacc_routine->clauses,
+			 DECL_ATTRIBUTES (fndecl));
+	}
 
       /* Don't unset parser->oacc_routine here: we may still need it to
 	 diagnose wrong usage.  But, remember that we've used this "#pragma acc
diff --git a/gcc/omp-general.c b/gcc/omp-general.c
index f1d859b0275c..82f0a04eab01 100644
--- a/gcc/omp-general.c
+++ b/gcc/omp-general.c
@@ -610,11 +610,14 @@ oacc_set_fn_attrib (tree fn, tree clauses, vec<tree> *args)
 
 /* Verify OpenACC routine clauses.
 
+   Returns 0 if FNDECL should be marked with an OpenACC 'routine' directive, 1
+   if it has already been marked in compatible way, and -1 if incompatible.
    Upon returning, the chain of clauses will contain exactly one clause
    specifying the level of parallelism.  */
 
-void
-oacc_verify_routine_clauses (tree *clauses, location_t loc)
+int
+oacc_verify_routine_clauses (tree fndecl, tree *clauses, location_t loc,
+			     const char *routine_str)
 {
   tree c_level = NULL_TREE;
   tree c_p = NULL_TREE;
@@ -659,6 +662,80 @@ oacc_verify_routine_clauses (tree *clauses, location_t loc)
       OMP_CLAUSE_CHAIN (c_level) = *clauses;
       *clauses = c_level;
     }
+  /* In *clauses, we now have exactly one clause specifying the level of
+     parallelism.  */
+
+  tree attr
+    = lookup_attribute ("omp declare target", DECL_ATTRIBUTES (fndecl));
+  if (attr != NULL_TREE)
+    {
+      /* If a "#pragma acc routine" has already been applied, just verify
+	 this one for compatibility.  */
+      /* Collect previous directive's clauses.  */
+      tree c_level_p = NULL_TREE;
+      for (tree c = TREE_VALUE (attr); c; c = OMP_CLAUSE_CHAIN (c))
+	switch (OMP_CLAUSE_CODE (c))
+	  {
+	  case OMP_CLAUSE_GANG:
+	  case OMP_CLAUSE_WORKER:
+	  case OMP_CLAUSE_VECTOR:
+	  case OMP_CLAUSE_SEQ:
+	    gcc_checking_assert (c_level_p == NULL_TREE);
+	    c_level_p = c;
+	    break;
+	  default:
+	    gcc_unreachable ();
+	  }
+      gcc_checking_assert (c_level_p != NULL_TREE);
+      /* ..., and compare to current directive's, which we've already collected
+	 above.  */
+      tree c_diag;
+      tree c_diag_p;
+      /* Matching level of parallelism?  */
+      if (OMP_CLAUSE_CODE (c_level) != OMP_CLAUSE_CODE (c_level_p))
+	{
+	  c_diag = c_level;
+	  c_diag_p = c_level_p;
+	  goto incompatible;
+	}
+      /* Compatible.  */
+      return 1;
+
+    incompatible:
+      if (c_diag != NULL_TREE)
+	error_at (OMP_CLAUSE_LOCATION (c_diag),
+		  "incompatible %qs clause when applying"
+		  " %<%s%> to %qD, which has already been"
+		  " marked with an OpenACC 'routine' directive",
+		  omp_clause_code_name[OMP_CLAUSE_CODE (c_diag)],
+		  routine_str, fndecl);
+      else if (c_diag_p != NULL_TREE)
+	error_at (loc,
+		  "missing %qs clause when applying"
+		  " %<%s%> to %qD, which has already been"
+		  " marked with an OpenACC 'routine' directive",
+		  omp_clause_code_name[OMP_CLAUSE_CODE (c_diag_p)],
+		  routine_str, fndecl);
+      else
+	gcc_unreachable ();
+      if (c_diag_p != NULL_TREE)
+	inform (OMP_CLAUSE_LOCATION (c_diag_p),
+		"... with %qs clause here",
+		omp_clause_code_name[OMP_CLAUSE_CODE (c_diag_p)]);
+      else
+	{
+	  /* In the front ends, we don't preserve location information for the
+	     OpenACC routine directive itself.  However, that of c_level_p
+	     should be close.  */
+	  location_t loc_routine = OMP_CLAUSE_LOCATION (c_level_p);
+	  inform (loc_routine, "... without %qs clause near to here",
+		  omp_clause_code_name[OMP_CLAUSE_CODE (c_diag)]);
+	}
+      /* Incompatible.  */
+      return -1;
+    }
+
+  return 0;
 }
 
 /*  Process the OpenACC 'routine' directive clauses to generate an attribute
diff --git a/gcc/omp-general.h b/gcc/omp-general.h
index 4241c33d99e0..f96d3c7768a0 100644
--- a/gcc/omp-general.h
+++ b/gcc/omp-general.h
@@ -84,7 +84,8 @@ extern tree oacc_launch_pack (unsigned code, tree device, unsigned op);
 extern tree oacc_replace_fn_attrib_attr (tree attribs, tree dims);
 extern void oacc_replace_fn_attrib (tree fn, tree dims);
 extern void oacc_set_fn_attrib (tree fn, tree clauses, vec<tree> *args);
-extern void oacc_verify_routine_clauses (tree *, location_t);
+extern int oacc_verify_routine_clauses (tree, tree *, location_t,
+					const char *);
 extern tree oacc_build_routine_dims (tree clauses);
 extern tree oacc_get_fn_attrib (tree fn);
 extern bool offloading_function_p (tree fn);
diff --git a/gcc/testsuite/ChangeLog b/gcc/testsuite/ChangeLog
index 4b07888dadbc..ed94f19e1dbd 100644
--- a/gcc/testsuite/ChangeLog
+++ b/gcc/testsuite/ChangeLog
@@ -1,5 +1,10 @@
 2019-05-17  Thomas Schwinge  <thomas@codesourcery.com>
 
+	PR testsuite/89433
+	* c-c++-common/goacc/routine-5.c: Update.
+	* c-c++-common/goacc/routine-level-of-parallelism-1.c: Likewise.
+	* c-c++-common/goacc/routine-level-of-parallelism-2.c: New file.
+
 	PR testsuite/89433
 	* c-c++-common/goacc/routine-2.c: Update, and move some test
 	into...
diff --git a/gcc/testsuite/c-c++-common/goacc/routine-5.c b/gcc/testsuite/c-c++-common/goacc/routine-5.c
index b967a7447bdb..a68c6be9be5d 100644
--- a/gcc/testsuite/c-c++-common/goacc/routine-5.c
+++ b/gcc/testsuite/c-c++-common/goacc/routine-5.c
@@ -150,61 +150,19 @@ void f_static_assert();
 
 #pragma acc routine
 __extension__ extern void ex1();
-#pragma acc routine (ex1) /* { dg-error ".#pragma acc routine. already applied to .\[void \]*ex1" } */
+#pragma acc routine (ex1) worker /* { dg-error "has already been marked with an OpenACC 'routine' directive" } */
 
 #pragma acc routine
 __extension__ __extension__ __extension__ __extension__ __extension__ void ex2()
 {
 }
-#pragma acc routine (ex2) /* { dg-error ".#pragma acc routine. already applied to .\[void \]*ex2" } */
+#pragma acc routine (ex2) worker /* { dg-error "has already been marked with an OpenACC 'routine' directive" } */
 
 #pragma acc routine /* { dg-error ".#pragma acc routine. not immediately followed by function declaration or definition" } */
 __extension__ int ex3;
 #pragma acc routine (ex3) /* { dg-error ".ex3. does not refer to a function" } */
 
 
-/* "#pragma acc routine" already applied.  */
-
-extern void fungsi_1();
-#pragma acc routine(fungsi_1) gang
-#pragma acc routine(fungsi_1) gang /* { dg-error ".#pragma acc routine. already applied to .\[void \]*fungsi_1" } */
-#pragma acc routine(fungsi_1) worker /* { dg-error ".#pragma acc routine. already applied to .\[void \]*fungsi_1" } */
-#pragma acc routine(fungsi_1) vector /* { dg-error ".#pragma acc routine. already applied to .\[void \]*fungsi_1" } */
-
-#pragma acc routine seq
-extern void fungsi_2();
-#pragma acc routine(fungsi_2) seq /* { dg-error ".#pragma acc routine. already applied to .\[void \]*fungsi_2." } */
-#pragma acc routine(fungsi_2) worker /* { dg-error ".#pragma acc routine. already applied to .\[void \]*fungsi_2." } */
-#pragma acc routine(fungsi_2) /* { dg-error ".#pragma acc routine. already applied to .\[void \]*fungsi_2." } */
-
-#pragma acc routine vector
-extern void fungsi_3();
-#pragma acc routine vector /* { dg-error ".#pragma acc routine. already applied to .\[void \]*fungsi_3." } */
-void fungsi_3()
-{
-}
-
-extern void fungsi_4();
-#pragma acc routine (fungsi_4) worker
-#pragma acc routine gang /* { dg-error ".#pragma acc routine. already applied to .\[void \]*fungsi_4." } */
-void fungsi_4()
-{
-}
-
-#pragma acc routine gang
-void fungsi_5()
-{
-}
-#pragma acc routine (fungsi_5) worker /* { dg-error ".#pragma acc routine. already applied to .\[void \]*fungsi_5." } */
-
-#pragma acc routine seq
-void fungsi_6()
-{
-}
-#pragma acc routine seq /* { dg-error ".#pragma acc routine. already applied to .\[void \]*fungsi_6." } */
-extern void fungsi_6();
-
-
 /* "#pragma acc routine" must be applied before.  */
 
 void Bar ();
diff --git a/gcc/testsuite/c-c++-common/goacc/routine-level-of-parallelism-1.c b/gcc/testsuite/c-c++-common/goacc/routine-level-of-parallelism-1.c
index ab0414bfed69..4fdeb1461f81 100644
--- a/gcc/testsuite/c-c++-common/goacc/routine-level-of-parallelism-1.c
+++ b/gcc/testsuite/c-c++-common/goacc/routine-level-of-parallelism-1.c
@@ -41,10 +41,10 @@ void s_2 (void)
 void g_3 (void)
 {
 }
-#pragma acc routine (g_3) /* { dg-error ".#pragma acc routine. already applied to .\[void \]*g_3." } */ \
+#pragma acc routine (g_3) \
   gang \
   seq /* { dg-error ".seq. specifies a conflicting level of parallelism" } */
-#pragma acc routine (g_3) /* { dg-error ".#pragma acc routine. already applied to .\[void \]*g_3." } */ \
+#pragma acc routine (g_3) \
   gang \
   vector /* { dg-error ".vector. specifies a conflicting level of parallelism" } */
 
@@ -52,10 +52,10 @@ extern void w_3 (void);
 #pragma acc routine (w_3) \
   worker \
   vector /* { dg-error ".vector. specifies a conflicting level of parallelism" } */
-#pragma acc routine (w_3) /* { dg-error ".#pragma acc routine. already applied to .\[void \]*w_3." } */ \
+#pragma acc routine (w_3) \
   worker \
   gang /* { dg-error ".gang. specifies a conflicting level of parallelism" } */
-#pragma acc routine (w_3) /* { dg-error ".#pragma acc routine. already applied to .\[void \]*w_3." } */ \
+#pragma acc routine (w_3) \
   worker \
   seq /* { dg-error ".seq. specifies a conflicting level of parallelism" } */
 
@@ -65,10 +65,10 @@ extern void w_3 (void);
 void v_3 (void)
 {
 }
-#pragma acc routine (v_3) /* { dg-error ".#pragma acc routine. already applied to .\[void \]*v_3." } */ \
+#pragma acc routine (v_3) \
   vector \
   worker /* { dg-error ".worker. specifies a conflicting level of parallelism" } */
-#pragma acc routine (v_3) /* { dg-error ".#pragma acc routine. already applied to .\[void \]*v_3." } */ \
+#pragma acc routine (v_3) \
   vector \
   gang /* { dg-error ".gang. specifies a conflicting level of parallelism" } */
 
@@ -76,10 +76,10 @@ extern void s_3 (void);
 #pragma acc routine (s_3) \
   seq \
   gang /* { dg-error ".gang. specifies a conflicting level of parallelism" } */
-#pragma acc routine (s_3) /* { dg-error ".#pragma acc routine. already applied to .\[void \]*s_3." } */ \
+#pragma acc routine (s_3) \
   seq \
   vector /* { dg-error ".vector. specifies a conflicting level of parallelism" } */
-#pragma acc routine (s_3) /* { dg-error ".#pragma acc routine. already applied to .\[void \]*s_3." } */ \
+#pragma acc routine (s_3) \
   seq \
   worker /* { dg-error ".worker. specifies a conflicting level of parallelism" } */
 
@@ -90,12 +90,12 @@ extern void s_3 (void);
   vector /* { dg-error ".vector. specifies a conflicting level of parallelism" } */ \
   seq /* { dg-error ".seq. specifies a conflicting level of parallelism" } */
 extern void g_4 (void);
-#pragma acc routine (g_4) /* { dg-error ".#pragma acc routine. already applied to .\[void \]*g_4." } */ \
+#pragma acc routine (g_4) \
   gang gang gang /* { dg-error "too many 'gang' clauses" } */ \
   vector /* { dg-error ".vector. specifies a conflicting level of parallelism" } */ \
   worker /* { dg-error ".worker. specifies a conflicting level of parallelism" } */ \
   seq /* { dg-error ".seq. specifies a conflicting level of parallelism" } */
-#pragma acc routine (g_4) /* { dg-error ".#pragma acc routine. already applied to .\[void \]*g_4." } */ \
+#pragma acc routine (g_4) \
   gang gang gang /* { dg-error "too many 'gang' clauses" } */ \
   seq /* { dg-error ".seq. specifies a conflicting level of parallelism" } */ \
   vector /* { dg-error ".vector. specifies a conflicting level of parallelism" } */ \
@@ -107,12 +107,12 @@ extern void w_4 (void);
   seq /* { dg-error ".seq. specifies a conflicting level of parallelism" } */ \
   vector /* { dg-error ".vector. specifies a conflicting level of parallelism" } */ \
   gang /* { dg-error ".gang. specifies a conflicting level of parallelism" } */
-#pragma acc routine (w_4) /* { dg-error ".#pragma acc routine. already applied to .\[void \]*w_4." } */ \
+#pragma acc routine (w_4) \
   worker worker worker /* { dg-error "too many 'worker' clauses" } */ \
   vector /* { dg-error ".vector. specifies a conflicting level of parallelism" } */ \
   seq /* { dg-error ".seq. specifies a conflicting level of parallelism" } */ \
   gang /* { dg-error ".gang. specifies a conflicting level of parallelism" } */
-#pragma acc routine (w_4) /* { dg-error ".#pragma acc routine. already applied to .\[void \]*w_4." } */ \
+#pragma acc routine (w_4) \
   worker worker worker /* { dg-error "too many 'worker' clauses" } */ \
   seq /* { dg-error ".seq. specifies a conflicting level of parallelism" } */ \
   gang /* { dg-error ".gang. specifies a conflicting level of parallelism" } */ \
@@ -126,12 +126,12 @@ extern void w_4 (void);
 void v_4 (void)
 {
 }
-#pragma acc routine (v_4) /* { dg-error ".#pragma acc routine. already applied to .\[void \]*v_4." } */ \
+#pragma acc routine (v_4) \
   vector vector vector /* { dg-error "too many 'vector' clauses" } */ \
   seq /* { dg-error ".seq. specifies a conflicting level of parallelism" } */ \
   worker /* { dg-error ".worker. specifies a conflicting level of parallelism" } */ \
   gang /* { dg-error ".gang. specifies a conflicting level of parallelism" } */
-#pragma acc routine (v_4) /* { dg-error ".#pragma acc routine. already applied to .\[void \]*v_4." } */ \
+#pragma acc routine (v_4) \
   vector vector vector /* { dg-error "too many 'vector' clauses" } */ \
   gang /* { dg-error ".gang. specifies a conflicting level of parallelism" } */ \
   seq /* { dg-error ".seq. specifies a conflicting level of parallelism" } */ \
@@ -145,12 +145,12 @@ void v_4 (void)
 void s_4 (void)
 {
 }
-#pragma acc routine (s_4) /* { dg-error ".#pragma acc routine. already applied to .\[void \]*s_4." } */ \
+#pragma acc routine (s_4) \
   seq seq seq /* { dg-error "too many 'seq' clauses" } */ \
   vector /* { dg-error ".vector. specifies a conflicting level of parallelism" } */ \
   gang /* { dg-error ".gang. specifies a conflicting level of parallelism" } */ \
   worker /* { dg-error ".worker. specifies a conflicting level of parallelism" } */
-#pragma acc routine (s_4) /* { dg-error ".#pragma acc routine. already applied to .\[void \]*s_4." } */ \
+#pragma acc routine (s_4) \
   seq seq seq /* { dg-error "too many 'seq' clauses" } */ \
   worker /* { dg-error ".worker. specifies a conflicting level of parallelism" } */ \
   vector /* { dg-error ".vector. specifies a conflicting level of parallelism" } */ \
@@ -168,7 +168,7 @@ void s_4 (void)
 void g_5 (void)
 {
 }
-#pragma acc routine (g_5) /* { dg-error ".#pragma acc routine. already applied to .\[void \]*g_5." } */ \
+#pragma acc routine (g_5) \
   gang gang gang /* { dg-error "too many 'gang' clauses" } */ \
   vector vector vector vector /* { dg-error "too many 'vector' clauses" } */ \
   /* { dg-error ".vector. specifies a conflicting level of parallelism" "" { target *-*-* } .-1 } */ \
@@ -176,7 +176,7 @@ void g_5 (void)
   /* { dg-error ".worker. specifies a conflicting level of parallelism" "" { target *-*-* } .-1 } */ \
   seq seq seq seq seq seq /* { dg-error "too many 'seq' clauses" } */ \
   /* { dg-error ".seq. specifies a conflicting level of parallelism" "" { target *-*-* } .-1 } */
-#pragma acc routine (g_5) /* { dg-error ".#pragma acc routine. already applied to .\[void \]*g_5." } */ \
+#pragma acc routine (g_5) \
   gang gang gang /* { dg-error "too many 'gang' clauses" } */ \
   seq seq seq seq seq /* { dg-error "too many 'seq' clauses" } */ \
   /* { dg-error ".seq. specifies a conflicting level of parallelism" "" { target *-*-* } .-1 } */ \
@@ -194,7 +194,7 @@ void g_5 (void)
   gang gang /* { dg-error "too many 'gang' clauses" } */ \
   /* { dg-error ".gang. specifies a conflicting level of parallelism" "" { target *-*-* } .-1 } */
 extern void w_5 (void);
-#pragma acc routine (w_5) /* { dg-error ".#pragma acc routine. already applied to .\[void \]*w_5." } */ \
+#pragma acc routine (w_5) \
   worker worker worker /* { dg-error "too many 'worker' clauses" } */ \
   vector vector vector /* { dg-error "too many 'vector' clauses" } */ \
   /* { dg-error ".vector. specifies a conflicting level of parallelism" "" { target *-*-* } .-1 } */ \
@@ -202,7 +202,7 @@ extern void w_5 (void);
   /* { dg-error ".seq. specifies a conflicting level of parallelism" "" { target *-*-* } .-1 } */ \
   gang gang /* { dg-error "too many 'gang' clauses" } */ \
   /* { dg-error ".gang. specifies a conflicting level of parallelism" "" { target *-*-* } .-1 } */
-#pragma acc routine (w_5) /* { dg-error ".#pragma acc routine. already applied to .\[void \]*w_5." } */ \
+#pragma acc routine (w_5) \
   worker worker worker /* { dg-error "too many 'worker' clauses" } */ \
   seq seq /* { dg-error "too many 'seq' clauses" } */ \
   /* { dg-error ".seq. specifies a conflicting level of parallelism" "" { target *-*-* } .-1 } */ \
@@ -220,7 +220,7 @@ extern void w_5 (void);
   gang gang /* { dg-error "too many 'gang' clauses" } */ \
   /* { dg-error ".gang. specifies a conflicting level of parallelism" "" { target *-*-* } .-1 } */
 extern void v_5 (void);
-#pragma acc routine (v_5) /* { dg-error ".#pragma acc routine. already applied to .\[void \]*v_5." } */ \
+#pragma acc routine (v_5) \
   vector vector vector /* { dg-error "too many 'vector' clauses" } */ \
   seq seq seq seq seq seq /* { dg-error "too many 'seq' clauses" } */ \
   /* { dg-error ".seq. specifies a conflicting level of parallelism" "" { target *-*-* } .-1 } */ \
@@ -228,7 +228,7 @@ extern void v_5 (void);
   /* { dg-error ".worker. specifies a conflicting level of parallelism" "" { target *-*-* } .-1 } */ \
   gang gang /* { dg-error "too many 'gang' clauses" } */ \
   /* { dg-error ".gang. specifies a conflicting level of parallelism" "" { target *-*-* } .-1 } */
-#pragma acc routine (v_5) /* { dg-error ".#pragma acc routine. already applied to .\[void \]*v_5." } */ \
+#pragma acc routine (v_5) \
   vector vector vector /* { dg-error "too many 'vector' clauses" } */ \
   gang gang /* { dg-error "too many 'gang' clauses" } */ \
   /* { dg-error ".gang. specifies a conflicting level of parallelism" "" { target *-*-* } .-1 } */ \
@@ -246,7 +246,7 @@ extern void s_5 (void);
   /* { dg-error ".worker. specifies a conflicting level of parallelism" "" { target *-*-* } .-1 } */ \
   gang gang /* { dg-error "too many 'gang' clauses" } */ \
   /* { dg-error ".gang. specifies a conflicting level of parallelism" "" { target *-*-* } .-1 } */
-#pragma acc routine (s_5) /* { dg-error ".#pragma acc routine. already applied to .\[void \]*s_5." } */ \
+#pragma acc routine (s_5) \
   seq seq seq /* { dg-error "too many 'seq' clauses" } */ \
   vector vector vector vector /* { dg-error "too many 'vector' clauses" } */ \
   /* { dg-error ".vector. specifies a conflicting level of parallelism" "" { target *-*-* } .-1 } */ \
@@ -254,7 +254,7 @@ extern void s_5 (void);
   /* { dg-error ".gang. specifies a conflicting level of parallelism" "" { target *-*-* } .-1 } */ \
   worker worker worker /* { dg-error "too many 'worker' clauses" } */ \
   /* { dg-error ".worker. specifies a conflicting level of parallelism" "" { target *-*-* } .-1 } */
-#pragma acc routine (s_5) /* { dg-error ".#pragma acc routine. already applied to .\[void \]*s_5." } */ \
+#pragma acc routine (s_5) \
   seq seq seq /* { dg-error "too many 'seq' clauses" } */ \
   worker worker /* { dg-error "too many 'worker' clauses" } */ \
   /* { dg-error ".worker. specifies a conflicting level of parallelism" "" { target *-*-* } .-1 } */ \
@@ -262,3 +262,188 @@ extern void s_5 (void);
   /* { dg-error ".vector. specifies a conflicting level of parallelism" "" { target *-*-* } .-1 } */ \
   gang gang /* { dg-error "too many 'gang' clauses" } */ \
   /* { dg-error ".gang. specifies a conflicting level of parallelism" "" { target *-*-* } .-1 } */
+
+
+/* Like the *_5 tests, but with the order of clauses changed in the second and
+   following routine directives for the specific *_5 function.  */
+
+#pragma acc routine \
+  gang gang gang /* { dg-error "too many 'gang' clauses" } */ \
+  worker worker /* { dg-error "too many 'worker' clauses" } */ \
+  /* { dg-error ".worker. specifies a conflicting level of parallelism" "" { target *-*-* } .-1 } */ \
+  vector vector vector /* { dg-error "too many 'vector' clauses" } */ \
+  /* { dg-error ".vector. specifies a conflicting level of parallelism" "" { target *-*-* } .-1 } */ \
+  seq seq seq seq seq /* { dg-error "too many 'seq' clauses" } */ \
+  /* { dg-error ".seq. specifies a conflicting level of parallelism" "" { target *-*-* } .-1 } */
+void g_6 (void)
+{
+}
+#pragma acc routine (g_6) \
+  vector vector vector vector /* { dg-error "too many 'vector' clauses" } */ \
+  /* { dg-error "incompatible .vector. clause when applying .#pragma acc routine. to .\[void \]*g_6\[\\(\\)\]*., which has already been marked with an OpenACC 'routine' directive" "" { target *-*-* } .-1 } */ \
+  gang gang gang /* { dg-error "too many 'gang' clauses" } */ \
+  /* { dg-error ".gang. specifies a conflicting level of parallelism" "" { target *-*-* } .-1 } */ \
+  worker worker worker /* { dg-error "too many 'worker' clauses" } */ \
+  /* { dg-error ".worker. specifies a conflicting level of parallelism" "" { target *-*-* } .-1 } */ \
+  seq seq seq seq seq seq /* { dg-error "too many 'seq' clauses" } */ \
+  /* { dg-error ".seq. specifies a conflicting level of parallelism" "" { target *-*-* } .-1 } */
+#pragma acc routine (g_6) \
+  seq seq seq seq seq /* { dg-error "too many 'seq' clauses" } */ \
+  /* { dg-error "incompatible .seq. clause when applying .#pragma acc routine. to .\[void \]*g_6\[\\(\\)\]*., which has already been marked with an OpenACC 'routine' directive" "" { target *-*-* } .-1 } */ \
+  gang gang gang /* { dg-error "too many 'gang' clauses" } */ \
+  /* { dg-error ".gang. specifies a conflicting level of parallelism" "" { target *-*-* } .-1 } */ \
+  vector vector vector vector /* { dg-error "too many 'vector' clauses" } */ \
+  /* { dg-error ".vector. specifies a conflicting level of parallelism" "" { target *-*-* } .-1 } */ \
+  worker worker worker worker /* { dg-error "too many 'worker' clauses" } */ \
+  /* { dg-error ".worker. specifies a conflicting level of parallelism" "" { target *-*-* } .-1 } */
+
+#pragma acc routine \
+  worker worker worker /* { dg-error "too many 'worker' clauses" } */ \
+  seq seq seq seq /* { dg-error "too many 'seq' clauses" } */ \
+  /* { dg-error ".seq. specifies a conflicting level of parallelism" "" { target *-*-* } .-1 } */ \
+  vector vector vector vector vector /* { dg-error "too many 'vector' clauses" } */ \
+  /* { dg-error ".vector. specifies a conflicting level of parallelism" "" { target *-*-* } .-1 } */ \
+  gang gang /* { dg-error "too many 'gang' clauses" } */ \
+  /* { dg-error ".gang. specifies a conflicting level of parallelism" "" { target *-*-* } .-1 } */
+extern void w_6 (void);
+#pragma acc routine (w_6) \
+  vector vector vector /* { dg-error "too many 'vector' clauses" } */ \
+  /* { dg-error "incompatible .vector. clause when applying .#pragma acc routine. to .\[void \]*w_6\[\\(\\)\]*., which has already been marked with an OpenACC 'routine' directive" "" { target *-*-* } .-1 } */ \
+  worker worker worker /* { dg-error "too many 'worker' clauses" } */ \
+  /* { dg-error ".worker. specifies a conflicting level of parallelism" "" { target *-*-* } .-1 } */ \
+  seq seq seq /* { dg-error "too many 'seq' clauses" } */ \
+  /* { dg-error ".seq. specifies a conflicting level of parallelism" "" { target *-*-* } .-1 } */ \
+  gang gang /* { dg-error "too many 'gang' clauses" } */ \
+  /* { dg-error ".gang. specifies a conflicting level of parallelism" "" { target *-*-* } .-1 } */
+#pragma acc routine (w_6) \
+  seq seq /* { dg-error "too many 'seq' clauses" } */ \
+  /* { dg-error "incompatible .seq. clause when applying .#pragma acc routine. to .\[void \]*w_6\[\\(\\)\]*., which has already been marked with an OpenACC 'routine' directive" "" { target *-*-* } .-1 } */ \
+  worker worker worker /* { dg-error "too many 'worker' clauses" } */ \
+  /* { dg-error ".worker. specifies a conflicting level of parallelism" "" { target *-*-* } .-1 } */ \
+  gang gang /* { dg-error "too many 'gang' clauses" } */ \
+  /* { dg-error ".gang. specifies a conflicting level of parallelism" "" { target *-*-* } .-1 } */ \
+  vector vector vector vector /* { dg-error "too many 'vector' clauses" } */ \
+  /* { dg-error ".vector. specifies a conflicting level of parallelism" "" { target *-*-* } .-1 } */
+
+#pragma acc routine \
+  vector vector vector /* { dg-error "too many 'vector' clauses" } */ \
+  worker worker worker /* { dg-error "too many 'worker' clauses" } */ \
+  /* { dg-error ".worker. specifies a conflicting level of parallelism" "" { target *-*-* } .-1 } */ \
+  seq seq seq seq seq seq /* { dg-error "too many 'seq' clauses" } */ \
+  /* { dg-error ".seq. specifies a conflicting level of parallelism" "" { target *-*-* } .-1 } */ \
+  gang gang /* { dg-error "too many 'gang' clauses" } */ \
+  /* { dg-error ".gang. specifies a conflicting level of parallelism" "" { target *-*-* } .-1 } */
+extern void v_6 (void);
+#pragma acc routine (v_6) \
+  seq seq seq seq seq seq /* { dg-error "too many 'seq' clauses" } */ \
+  /* { dg-error "incompatible .seq. clause when applying .#pragma acc routine. to .\[void \]*v_6\[\\(\\)\]*., which has already been marked with an OpenACC 'routine' directive" "" { target *-*-* } .-1 } */ \
+  vector vector vector /* { dg-error "too many 'vector' clauses" } */ \
+  /* { dg-error ".vector. specifies a conflicting level of parallelism" "" { target *-*-* } .-1 } */ \
+  worker worker /* { dg-error "too many 'worker' clauses" } */ \
+  /* { dg-error ".worker. specifies a conflicting level of parallelism" "" { target *-*-* } .-1 } */ \
+  gang gang /* { dg-error "too many 'gang' clauses" } */ \
+  /* { dg-error ".gang. specifies a conflicting level of parallelism" "" { target *-*-* } .-1 } */
+#pragma acc routine (v_6) \
+  gang gang /* { dg-error "too many 'gang' clauses" } */ \
+  /* { dg-error "incompatible .gang. clause when applying .#pragma acc routine. to .\[void \]*v_6\[\\(\\)\]*., which has already been marked with an OpenACC 'routine' directive" "" { target *-*-* } .-1 } */ \
+  vector vector vector /* { dg-error "too many 'vector' clauses" } */ \
+  /* { dg-error ".vector. specifies a conflicting level of parallelism" "" { target *-*-* } .-1 } */ \
+  seq seq seq seq seq /* { dg-error "too many 'seq' clauses" } */ \
+  /* { dg-error ".seq. specifies a conflicting level of parallelism" "" { target *-*-* } .-1 } */ \
+  worker worker /* { dg-error "too many 'worker' clauses" } */ \
+  /* { dg-error ".worker. specifies a conflicting level of parallelism" "" { target *-*-* } .-1 } */
+
+extern void s_6 (void);
+#pragma acc routine (s_6) \
+  seq seq seq /* { dg-error "too many 'seq' clauses" } */ \
+  vector vector vector vector /* { dg-error "too many 'vector' clauses" } */ \
+  /* { dg-error ".vector. specifies a conflicting level of parallelism" "" { target *-*-* } .-1 } */ \
+  worker worker worker worker worker /* { dg-error "too many 'worker' clauses" } */ \
+  /* { dg-error ".worker. specifies a conflicting level of parallelism" "" { target *-*-* } .-1 } */ \
+  gang gang /* { dg-error "too many 'gang' clauses" } */ \
+  /* { dg-error ".gang. specifies a conflicting level of parallelism" "" { target *-*-* } .-1 } */
+#pragma acc routine (s_6) \
+  vector vector vector vector /* { dg-error "too many 'vector' clauses" } */ \
+  /* { dg-error "incompatible .vector. clause when applying .#pragma acc routine. to .\[void \]*s_6\[\\(\\)\]*., which has already been marked with an OpenACC 'routine' directive" "" { target *-*-* } .-1 } */ \
+  seq seq seq /* { dg-error "too many 'seq' clauses" } */ \
+  /* { dg-error ".seq. specifies a conflicting level of parallelism" "" { target *-*-* } .-1 } */ \
+  gang gang /* { dg-error "too many 'gang' clauses" } */ \
+  /* { dg-error ".gang. specifies a conflicting level of parallelism" "" { target *-*-* } .-1 } */ \
+  worker worker worker /* { dg-error "too many 'worker' clauses" } */ \
+  /* { dg-error ".worker. specifies a conflicting level of parallelism" "" { target *-*-* } .-1 } */
+#pragma acc routine (s_6) \
+  worker worker /* { dg-error "too many 'worker' clauses" } */ \
+  /* { dg-error "incompatible .worker. clause when applying .#pragma acc routine. to .\[void \]*s_6\[\\(\\)\]*., which has already been marked with an OpenACC 'routine' directive" "" { target *-*-* } .-1 } */ \
+  seq seq seq /* { dg-error "too many 'seq' clauses" } */ \
+  /* { dg-error ".seq. specifies a conflicting level of parallelism" "" { target *-*-* } .-1 } */ \
+  vector vector vector vector /* { dg-error "too many 'vector' clauses" } */ \
+  /* { dg-error ".vector. specifies a conflicting level of parallelism" "" { target *-*-* } .-1 } */ \
+  gang gang /* { dg-error "too many 'gang' clauses" } */ \
+  /* { dg-error ".gang. specifies a conflicting level of parallelism" "" { target *-*-* } .-1 } */
+
+
+/* Like the *_6 tests, but without all the duplicate clauses, so that the
+   routine directives are valid in isolation.  */
+
+#pragma acc routine \
+  gang
+void g_7 (void)
+{
+}
+#pragma acc routine (g_7) \
+  vector /* { dg-error "incompatible .vector. clause when applying .#pragma acc routine. to .\[void \]*g_7\[\\(\\)\]*., which has already been marked with an OpenACC 'routine' directive" } */
+#pragma acc routine (g_7) \
+  seq /* { dg-error "incompatible .seq. clause when applying .#pragma acc routine. to .\[void \]*g_7\[\\(\\)\]*., which has already been marked with an OpenACC 'routine' directive" } */
+
+#pragma acc routine \
+  worker
+extern void w_7 (void);
+#pragma acc routine (w_7) \
+  vector /* { dg-error "incompatible .vector. clause when applying .#pragma acc routine. to .\[void \]*w_7\[\\(\\)\]*., which has already been marked with an OpenACC 'routine' directive" } */
+#pragma acc routine (w_7) \
+  seq /* { dg-error "incompatible .seq. clause when applying .#pragma acc routine. to .\[void \]*w_7\[\\(\\)\]*., which has already been marked with an OpenACC 'routine' directive" } */
+
+#pragma acc routine \
+  vector
+extern void v_7 (void);
+#pragma acc routine (v_7) \
+  seq /* { dg-error "incompatible .seq. clause when applying .#pragma acc routine. to .\[void \]*v_7\[\\(\\)\]*., which has already been marked with an OpenACC 'routine' directive" } */
+#pragma acc routine (v_7) \
+  gang /* { dg-error "incompatible .gang. clause when applying .#pragma acc routine. to .\[void \]*v_7\[\\(\\)\]*., which has already been marked with an OpenACC 'routine' directive" } */
+
+extern void s_7 (void);
+#pragma acc routine (s_7) \
+  seq
+#pragma acc routine (s_7) \
+  vector /* { dg-error "incompatible .vector. clause when applying .#pragma acc routine. to .\[void \]*s_7\[\\(\\)\]*., which has already been marked with an OpenACC 'routine' directive" } */
+#pragma acc routine (s_7) \
+  worker /* { dg-error "incompatible .worker. clause when applying .#pragma acc routine. to .\[void \]*s_7\[\\(\\)\]*., which has already been marked with an OpenACC 'routine' directive" } */
+
+
+/* Test cases for implicit seq clause.  */
+
+#pragma acc routine \
+  gang
+void g_8 (void)
+{
+}
+#pragma acc routine (g_8) /* { dg-error "incompatible .seq. clause when applying .#pragma acc routine. to .\[void \]*g_8\[\\(\\)\]*., which has already been marked with an OpenACC 'routine' directive" } */
+
+#pragma acc routine \
+  worker
+extern void w_8 (void);
+#pragma acc routine (w_8) /* { dg-error "incompatible .seq. clause when applying .#pragma acc routine. to .\[void \]*w_8\[\\(\\)\]*., which has already been marked with an OpenACC 'routine' directive" } */
+
+#pragma acc routine \
+  vector
+extern void v_8 (void);
+#pragma acc routine (v_8) /* { dg-error "incompatible .seq. clause when applying .#pragma acc routine. to .\[void \]*v_8\[\\(\\)\]*., which has already been marked with an OpenACC 'routine' directive" } */
+
+extern void s_8 (void);
+#pragma acc routine (s_8)
+#pragma acc routine (s_8) \
+  vector /* { dg-error "incompatible .vector. clause when applying .#pragma acc routine. to .\[void \]*s_8\[\\(\\)\]*., which has already been marked with an OpenACC 'routine' directive" } */
+#pragma acc routine (s_8) \
+  gang /* { dg-error "incompatible .gang. clause when applying .#pragma acc routine. to .\[void \]*s_8\[\\(\\)\]*., which has already been marked with an OpenACC 'routine' directive" } */
+#pragma acc routine (s_8) \
+  worker /* { dg-error "incompatible .worker. clause when applying .#pragma acc routine. to .\[void \]*s_8\[\\(\\)\]*., which has already been marked with an OpenACC 'routine' directive" } */
diff --git a/gcc/testsuite/c-c++-common/goacc/routine-level-of-parallelism-2.c b/gcc/testsuite/c-c++-common/goacc/routine-level-of-parallelism-2.c
new file mode 100644
index 000000000000..a066f2b9c2ba
--- /dev/null
+++ b/gcc/testsuite/c-c++-common/goacc/routine-level-of-parallelism-2.c
@@ -0,0 +1,71 @@
+/* Test various aspects of clauses specifying compatible levels of parallelism
+   with the OpenACC 'routine' directive.  The Fortran counterpart is
+   '../../gfortran.dg/goacc/routine-level-of-parallelism-1.f90'.  */
+
+#pragma acc routine gang
+void g_1 (void)
+{
+}
+#pragma acc routine (g_1) gang
+#pragma acc routine (g_1) gang
+
+
+extern void w_1 (void);
+#pragma acc routine (w_1) worker
+#pragma acc routine (w_1) worker
+#pragma acc routine (w_1) worker
+
+
+#pragma acc routine vector
+extern void v_1 (void);
+#pragma acc routine (v_1) vector
+#pragma acc routine (v_1) vector
+
+
+/* Also test the implicit seq clause.  */
+
+#pragma acc routine seq
+extern void s_1_1 (void);
+#pragma acc routine (s_1_1)
+#pragma acc routine (s_1_1) seq
+#pragma acc routine (s_1_1)
+#pragma acc routine (s_1_1) seq
+
+#pragma acc routine
+extern void s_1_2 (void);
+#pragma acc routine (s_1_2)
+#pragma acc routine (s_1_2) seq
+#pragma acc routine (s_1_2)
+#pragma acc routine (s_1_2) seq
+
+extern void s_2_1 (void);
+#pragma acc routine (s_2_1) seq
+#pragma acc routine (s_2_1)
+#pragma acc routine (s_2_1) seq
+#pragma acc routine (s_2_1)
+#pragma acc routine (s_2_1) seq
+
+extern void s_2_2 (void);
+#pragma acc routine (s_2_2)
+#pragma acc routine (s_2_2)
+#pragma acc routine (s_2_2) seq
+#pragma acc routine (s_2_2)
+#pragma acc routine (s_2_2) seq
+
+#pragma acc routine seq
+void s_3_1 (void)
+{
+}
+#pragma acc routine (s_3_1)
+#pragma acc routine (s_3_1) seq
+#pragma acc routine (s_3_1)
+#pragma acc routine (s_3_1) seq
+
+#pragma acc routine
+void s_3_2 (void)
+{
+}
+#pragma acc routine (s_3_2)
+#pragma acc routine (s_3_2) seq
+#pragma acc routine (s_3_2)
+#pragma acc routine (s_3_2) seq
diff --git a/gcc/testsuite/gfortran.dg/goacc/routine-level-of-parallelism-1.f90 b/gcc/testsuite/gfortran.dg/goacc/routine-level-of-parallelism-1.f90
index 75dd1b01f6f8..83b8c24b41d8 100644
--- a/gcc/testsuite/gfortran.dg/goacc/routine-level-of-parallelism-1.f90
+++ b/gcc/testsuite/gfortran.dg/goacc/routine-level-of-parallelism-1.f90
@@ -1,6 +1,6 @@
-! Test various aspects of clauses specifying compatible levels of
-! parallelism with the OpenACC routine directive.  The Fortran counterpart is
-! c-c++-common/goacc/routine-level-of-parallelism-2.c
+! Test various aspects of clauses specifying compatible levels of parallelism
+! with the OpenACC routine directive.  The C/C++ counterpart is
+! '../../c-c++-common/goacc/routine-level-of-parallelism-2.c'.
 
 subroutine g_1
   !$acc routine gang
-- 
2.17.1


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

* [PATCH] Handle 'omp declare target' attribute set for both OpenACC and OpenMP 'target' [PR89433, PR93465] (was: [committed] PR89433 "Repeated use of the OpenACC 'routine' directive")
  2019-05-17 19:17         ` [committed] " Thomas Schwinge
@ 2020-03-04 19:27           ` Thomas Schwinge
  2020-03-04 20:09             ` Jakub Jelinek
  0 siblings, 1 reply; 16+ messages in thread
From: Thomas Schwinge @ 2020-03-04 19:27 UTC (permalink / raw)
  To: gcc-patches, Jakub Jelinek; +Cc: gscfq, webrown.cpp

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

-----------------
Mentor Graphics (Deutschland) GmbH, Arnulfstraße 201, 80634 München / Germany
Registergericht München HRB 106955, Geschäftsführer: Thomas Heurung, Alexander Walter

[-- Attachment #2: Type: message/rfc822, Size: 10858 bytes --]

[-- Attachment #2.1.1.1: Type: text/plain, Size: 2132 bytes --]

Hi!

On 2019-05-17T21:16:57+0200, I wrote:
> Now committed to trunk in [...]
> r271345 "[PR89433] Repeated use of the C/C++ OpenACC 'routine'
> directive"

> --- a/gcc/omp-general.c
> +++ b/gcc/omp-general.c

> @@ -610,11 +610,14 @@ oacc_set_fn_attrib (tree fn, tree clauses, vec<tree> *args)
>  
>  /* Verify OpenACC routine clauses.
>  
> +   Returns 0 if FNDECL should be marked with an OpenACC 'routine' directive, 1
> +   if it has already been marked in compatible way, and -1 if incompatible.
>     Upon returning, the chain of clauses will contain exactly one clause
>     specifying the level of parallelism.  */

> +  tree attr
> +    = lookup_attribute ("omp declare target", DECL_ATTRIBUTES (fndecl));
> +  if (attr != NULL_TREE)
> +    {
> +      /* If a "#pragma acc routine" has already been applied, just verify
> +	 this one for compatibility.  */
> +      /* Collect previous directive's clauses.  */
> +      tree c_level_p = NULL_TREE;
> +      for (tree c = TREE_VALUE (attr); c; c = OMP_CLAUSE_CHAIN (c))
> +	switch (OMP_CLAUSE_CODE (c))
> +	  {
> +	  case OMP_CLAUSE_GANG:
> +	  case OMP_CLAUSE_WORKER:
> +	  case OMP_CLAUSE_VECTOR:
> +	  case OMP_CLAUSE_SEQ:
> +	    gcc_checking_assert (c_level_p == NULL_TREE);
> +	    c_level_p = c;
> +	    break;
> +	  default:
> +	    gcc_unreachable ();
> +	  }
> +      gcc_checking_assert (c_level_p != NULL_TREE);

As documented in <https://gcc.gnu.org/PR93465>, this triggers an ICE if
the 'omp declare target' attribute had already been set for '#pragma omp
declare target'.  OK to deal with this situation as in the patch
attached, "Handle 'omp declare target' attribute set for both OpenACC and
OpenMP 'target' [PR89433, PR93465]"?  If approving this patch, please
respond with "Reviewed-by: NAME <EMAIL>" so that your effort will be
recorded in the commit log, see <https://gcc.gnu.org/wiki/Reviewed-by>.

That's probably not worth backporting (a variant of this) to release
branches -- where also other such mixed OpenACC/OpenMP code is silently
accepted, with unclear semantics.


Grüße
 Thomas



[-- Warning: decoded text below may be mangled, UTF-8 assumed --]
[-- Attachment #2.1.1.2: 0001-Handle-omp-declare-target-attribute-set-for-b.master.patch --]
[-- Type: text/x-diff, Size: 4734 bytes --]

From 3f8e048f5f8d1eabde642c1c146114027bb44e79 Mon Sep 17 00:00:00 2001
From: Thomas Schwinge <thomas@codesourcery.com>
Date: Wed, 4 Mar 2020 17:58:33 +0100
Subject: [PATCH] Handle 'omp declare target' attribute set for both OpenACC
 and OpenMP 'target' [PR89433, PR93465]

... which as of PR89433 commit b48f44bf77a39fefc238a16cf1225c6464c82406 causes
an ICE.  Not sure if this is actually supposed to be valid or invalid code.
Until the interactions between OpenACC and OpenMP 'target' get defined
properly, make this a compile-time error.

	gcc/
	PR middle-end/89433
	PR middle-end/93465
	* omp-general.c (oacc_verify_routine_clauses): Diagnose if
	"#pragma omp declare target" has also been applied.
	gcc/testsuite/
	PR middle-end/89433
	PR middle-end/93465
	* c-c++-common/goacc-gomp/pr93465-1.c: New file.
---
 gcc/omp-general.c                             | 13 +++++
 .../c-c++-common/goacc-gomp/pr93465-1.c       | 56 +++++++++++++++++++
 2 files changed, 69 insertions(+)
 create mode 100644 gcc/testsuite/c-c++-common/goacc-gomp/pr93465-1.c

diff --git a/gcc/omp-general.c b/gcc/omp-general.c
index f107f4c050f1..49023f42c473 100644
--- a/gcc/omp-general.c
+++ b/gcc/omp-general.c
@@ -1776,6 +1776,19 @@ oacc_verify_routine_clauses (tree fndecl, tree *clauses, location_t loc,
     = lookup_attribute ("omp declare target", DECL_ATTRIBUTES (fndecl));
   if (attr != NULL_TREE)
     {
+      /* Diagnose if "#pragma omp declare target" has also been applied.  */
+      if (TREE_VALUE (attr) == NULL_TREE)
+	{
+	  /* See <https://gcc.gnu.org/PR93465>; the semantics of combining
+	     OpenACC and OpenMP 'target' are not clear.  */
+	  error_at (loc,
+		    "cannot apply %<%s%> to %qD, which has also been"
+		    " marked with an OpenMP 'declare target' directive",
+		    routine_str, fndecl);
+	  /* Incompatible.  */
+	  return -1;
+	}
+
       /* If a "#pragma acc routine" has already been applied, just verify
 	 this one for compatibility.  */
       /* Collect previous directive's clauses.  */
diff --git a/gcc/testsuite/c-c++-common/goacc-gomp/pr93465-1.c b/gcc/testsuite/c-c++-common/goacc-gomp/pr93465-1.c
new file mode 100644
index 000000000000..c8b9135d9973
--- /dev/null
+++ b/gcc/testsuite/c-c++-common/goacc-gomp/pr93465-1.c
@@ -0,0 +1,56 @@
+#pragma omp declare target
+#pragma acc routine seq /* { dg-error "cannot apply '#pragma acc routine' to '\(void \)?f1\(\\(\\)\)?', which has also been marked with an OpenMP 'declare target' directive" } */
+void f1 (void) {}
+#pragma omp end declare target
+
+#pragma omp declare target
+void f1 (void);
+
+#pragma acc routine seq /* { dg-error "cannot apply '#pragma acc routine' to '\(void \)?f1\(\\(\\)\)?', which has also been marked with an OpenMP 'declare target' directive" } */
+void f1 (void);
+
+
+
+#pragma omp declare target
+#pragma acc routine /* { dg-error "cannot apply '#pragma acc routine' to '\(void \)?f2\(\\(\\)\)?', which has also been marked with an OpenMP 'declare target' directive" } */
+extern void f2 (void);
+#pragma omp end declare target
+
+#pragma omp declare target
+extern void f2 (void);
+#pragma omp end declare target
+
+#pragma acc routine gang /* { dg-error "cannot apply '#pragma acc routine' to '\(void \)?f2\(\\(\\)\)?', which has also been marked with an OpenMP 'declare target' directive" } */
+extern void f2 (void);
+
+
+#pragma omp declare target
+#pragma acc routine gang /* { dg-error "cannot apply '#pragma acc routine' to '\(void \)?f3\(\\(\\)\)?', which has also been marked with an OpenMP 'declare target' directive" } */
+void f3 (void);
+#pragma omp end declare target
+
+#pragma omp declare target
+void f3 (void) {}
+#pragma omp end declare target
+
+#pragma acc routine (f3) gang /* { dg-error "cannot apply '#pragma acc routine' to '\(void \)?f3\(\\(\\)\)?', which has also been marked with an OpenMP 'declare target' directive" } */
+
+
+/* Surprisingly, this diagnosis also works for '#pragma acc routine' first,
+   followed by '#pragma omp declare target'; the latter gets applied first.  */
+
+
+#pragma acc routine /* { dg-error "cannot apply '#pragma acc routine' to '\(void \)?f4\(\\(\\)\)?', which has also been marked with an OpenMP 'declare target' directive" } */
+extern void f4 (void);
+
+#pragma omp declare target
+extern void f4 (void);
+#pragma omp end declare target
+
+
+#pragma acc routine gang /* { dg-error "cannot apply '#pragma acc routine' to '\(void \)?f5\(\\(\\)\)?', which has also been marked with an OpenMP 'declare target' directive" } */
+void f5 (void) {}
+
+#pragma omp declare target
+extern void f5 (void);
+#pragma omp end declare target
-- 
2.17.1


[-- Attachment #2.1.2: signature.asc --]
[-- Type: application/pgp-signature, Size: 658 bytes --]

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

* Re: [PATCH] Handle 'omp declare target' attribute set for both OpenACC and OpenMP 'target' [PR89433, PR93465] (was: [committed] PR89433 "Repeated use of the OpenACC 'routine' directive")
  2020-03-04 19:27           ` [PATCH] Handle 'omp declare target' attribute set for both OpenACC and OpenMP 'target' [PR89433, PR93465] (was: [committed] PR89433 "Repeated use of the OpenACC 'routine' directive") Thomas Schwinge
@ 2020-03-04 20:09             ` Jakub Jelinek
  2020-04-10 14:14               ` [PATCH] Handle 'omp declare target' attribute set for both OpenACC and OpenMP 'target' [PR89433, PR93465] Thomas Schwinge
  0 siblings, 1 reply; 16+ messages in thread
From: Jakub Jelinek @ 2020-03-04 20:09 UTC (permalink / raw)
  To: Thomas Schwinge; +Cc: gcc-patches, gscfq, webrown.cpp

On Wed, Mar 04, 2020 at 08:27:10PM +0100, Thomas Schwinge wrote:
> ... which as of PR89433 commit b48f44bf77a39fefc238a16cf1225c6464c82406 causes
> an ICE.  Not sure if this is actually supposed to be valid or invalid code.
> Until the interactions between OpenACC and OpenMP 'target' get defined
> properly, make this a compile-time error.
> 
> 	gcc/
> 	PR middle-end/89433
> 	PR middle-end/93465
> 	* omp-general.c (oacc_verify_routine_clauses): Diagnose if
> 	"#pragma omp declare target" has also been applied.
> 	gcc/testsuite/
> 	PR middle-end/89433
> 	PR middle-end/93465
> 	* c-c++-common/goacc-gomp/pr93465-1.c: New file.

Ok for trunk.

	Jakub

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

* Re: [PATCH] Handle 'omp declare target' attribute set for both OpenACC and OpenMP 'target' [PR89433, PR93465]
  2020-03-04 20:09             ` Jakub Jelinek
@ 2020-04-10 14:14               ` Thomas Schwinge
  0 siblings, 0 replies; 16+ messages in thread
From: Thomas Schwinge @ 2020-04-10 14:14 UTC (permalink / raw)
  To: Jakub Jelinek, gcc-patches; +Cc: gscfq, webrown.cpp

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

Hi!

On 2020-03-04T20:07:46+0100, Jakub Jelinek <jakub@redhat.com> wrote:
> On Wed, Mar 04, 2020 at 08:27:10PM +0100, Thomas Schwinge wrote:
>> ... which as of PR89433 commit b48f44bf77a39fefc238a16cf1225c6464c82406 causes
>> an ICE.  Not sure if this is actually supposed to be valid or invalid code.
>> Until the interactions between OpenACC and OpenMP 'target' get defined
>> properly, make this a compile-time error.
>>
>>      gcc/
>>      PR middle-end/89433
>>      PR middle-end/93465
>>      * omp-general.c (oacc_verify_routine_clauses): Diagnose if
>>      "#pragma omp declare target" has also been applied.
>>      gcc/testsuite/
>>      PR middle-end/89433
>>      PR middle-end/93465
>>      * c-c++-common/goacc-gomp/pr93465-1.c: New file.
>
> Ok for trunk.

Thanks for the review, and sorry for the delay; now pushed to master
branch in commit ff3f862b451496dd4afbe2dbfae82afab59a42c6 "Handle 'omp
declare target' attribute set for both OpenACC and OpenMP 'target'
[PR89433, PR93465]", see attached.


Grüße
 Thomas


-----------------
Mentor Graphics (Deutschland) GmbH, Arnulfstraße 201, 80634 München / Germany
Registergericht München HRB 106955, Geschäftsführer: Thomas Heurung, Alexander Walter

[-- Warning: decoded text below may be mangled, UTF-8 assumed --]
[-- Attachment #2: 0001-Handle-omp-declare-target-attribute-set-for-both-Ope.patch --]
[-- Type: text/x-diff, Size: 5669 bytes --]

From ff3f862b451496dd4afbe2dbfae82afab59a42c6 Mon Sep 17 00:00:00 2001
From: Thomas Schwinge <thomas@codesourcery.com>
Date: Wed, 4 Mar 2020 17:58:33 +0100
Subject: [PATCH] Handle 'omp declare target' attribute set for both OpenACC
 and OpenMP 'target' [PR89433, PR93465]

... which as of PR89433 commit b48f44bf77a39fefc238a16cf1225c6464c82406 causes
an ICE.  Not sure if this is actually supposed to be valid or invalid code.
Until the interactions between OpenACC and OpenMP 'target' get defined
properly, make this a compile-time error.

	gcc/
	PR middle-end/89433
	PR middle-end/93465
	* omp-general.c (oacc_verify_routine_clauses): Diagnose if
	"#pragma omp declare target" has also been applied.
	gcc/testsuite/
	PR middle-end/89433
	PR middle-end/93465
	* c-c++-common/goacc-gomp/pr93465-1.c: New file.
---
 gcc/ChangeLog                                 |  7 +++
 gcc/omp-general.c                             | 13 +++++
 gcc/testsuite/ChangeLog                       |  6 ++
 .../c-c++-common/goacc-gomp/pr93465-1.c       | 56 +++++++++++++++++++
 4 files changed, 82 insertions(+)
 create mode 100644 gcc/testsuite/c-c++-common/goacc-gomp/pr93465-1.c

diff --git a/gcc/ChangeLog b/gcc/ChangeLog
index bce700e472e..33e980bd977 100644
--- a/gcc/ChangeLog
+++ b/gcc/ChangeLog
@@ -1,3 +1,10 @@
+2020-04-10  Thomas Schwinge  <thomas@codesourcery.com>
+
+	PR middle-end/89433
+	PR middle-end/93465
+	* omp-general.c (oacc_verify_routine_clauses): Diagnose if
+	"#pragma omp declare target" has also been applied.
+
 2020-04-09  Jozef Lawrynowicz  <jozef.l@mittosystems.com>
 
 	* config/msp430/msp430.c (msp430_expand_epilogue): Use emit_jump_insn
diff --git a/gcc/omp-general.c b/gcc/omp-general.c
index f107f4c050f..49023f42c47 100644
--- a/gcc/omp-general.c
+++ b/gcc/omp-general.c
@@ -1776,6 +1776,19 @@ oacc_verify_routine_clauses (tree fndecl, tree *clauses, location_t loc,
     = lookup_attribute ("omp declare target", DECL_ATTRIBUTES (fndecl));
   if (attr != NULL_TREE)
     {
+      /* Diagnose if "#pragma omp declare target" has also been applied.  */
+      if (TREE_VALUE (attr) == NULL_TREE)
+	{
+	  /* See <https://gcc.gnu.org/PR93465>; the semantics of combining
+	     OpenACC and OpenMP 'target' are not clear.  */
+	  error_at (loc,
+		    "cannot apply %<%s%> to %qD, which has also been"
+		    " marked with an OpenMP 'declare target' directive",
+		    routine_str, fndecl);
+	  /* Incompatible.  */
+	  return -1;
+	}
+
       /* If a "#pragma acc routine" has already been applied, just verify
 	 this one for compatibility.  */
       /* Collect previous directive's clauses.  */
diff --git a/gcc/testsuite/ChangeLog b/gcc/testsuite/ChangeLog
index bced3e27d86..235d481e6cf 100644
--- a/gcc/testsuite/ChangeLog
+++ b/gcc/testsuite/ChangeLog
@@ -1,3 +1,9 @@
+2020-04-10  Thomas Schwinge  <thomas@codesourcery.com>
+
+	PR middle-end/89433
+	PR middle-end/93465
+	* c-c++-common/goacc-gomp/pr93465-1.c: New file.
+
 2020-04-10  Iain Buclaw  <ibuclaw@gdcproject.org>
 
 	* lib/gdc.exp (gdc_link_flags): Remove libdruntime library paths.
diff --git a/gcc/testsuite/c-c++-common/goacc-gomp/pr93465-1.c b/gcc/testsuite/c-c++-common/goacc-gomp/pr93465-1.c
new file mode 100644
index 00000000000..c8b9135d997
--- /dev/null
+++ b/gcc/testsuite/c-c++-common/goacc-gomp/pr93465-1.c
@@ -0,0 +1,56 @@
+#pragma omp declare target
+#pragma acc routine seq /* { dg-error "cannot apply '#pragma acc routine' to '\(void \)?f1\(\\(\\)\)?', which has also been marked with an OpenMP 'declare target' directive" } */
+void f1 (void) {}
+#pragma omp end declare target
+
+#pragma omp declare target
+void f1 (void);
+
+#pragma acc routine seq /* { dg-error "cannot apply '#pragma acc routine' to '\(void \)?f1\(\\(\\)\)?', which has also been marked with an OpenMP 'declare target' directive" } */
+void f1 (void);
+
+
+
+#pragma omp declare target
+#pragma acc routine /* { dg-error "cannot apply '#pragma acc routine' to '\(void \)?f2\(\\(\\)\)?', which has also been marked with an OpenMP 'declare target' directive" } */
+extern void f2 (void);
+#pragma omp end declare target
+
+#pragma omp declare target
+extern void f2 (void);
+#pragma omp end declare target
+
+#pragma acc routine gang /* { dg-error "cannot apply '#pragma acc routine' to '\(void \)?f2\(\\(\\)\)?', which has also been marked with an OpenMP 'declare target' directive" } */
+extern void f2 (void);
+
+
+#pragma omp declare target
+#pragma acc routine gang /* { dg-error "cannot apply '#pragma acc routine' to '\(void \)?f3\(\\(\\)\)?', which has also been marked with an OpenMP 'declare target' directive" } */
+void f3 (void);
+#pragma omp end declare target
+
+#pragma omp declare target
+void f3 (void) {}
+#pragma omp end declare target
+
+#pragma acc routine (f3) gang /* { dg-error "cannot apply '#pragma acc routine' to '\(void \)?f3\(\\(\\)\)?', which has also been marked with an OpenMP 'declare target' directive" } */
+
+
+/* Surprisingly, this diagnosis also works for '#pragma acc routine' first,
+   followed by '#pragma omp declare target'; the latter gets applied first.  */
+
+
+#pragma acc routine /* { dg-error "cannot apply '#pragma acc routine' to '\(void \)?f4\(\\(\\)\)?', which has also been marked with an OpenMP 'declare target' directive" } */
+extern void f4 (void);
+
+#pragma omp declare target
+extern void f4 (void);
+#pragma omp end declare target
+
+
+#pragma acc routine gang /* { dg-error "cannot apply '#pragma acc routine' to '\(void \)?f5\(\\(\\)\)?', which has also been marked with an OpenMP 'declare target' directive" } */
+void f5 (void) {}
+
+#pragma omp declare target
+extern void f5 (void);
+#pragma omp end declare target
-- 
2.17.1


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

end of thread, other threads:[~2020-04-10 14:14 UTC | newest]

Thread overview: 16+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2016-08-01 15:21 Use verify_oacc_routine_clauses for C/C++ Thomas Schwinge
2016-08-01 15:30 ` Use verify_oacc_routine_clauses for Fortran (was: Use verify_oacc_routine_clauses for C/C++) Thomas Schwinge
2016-08-09 15:55   ` Cesar Philippidis
2016-08-10 11:46     ` Thomas Schwinge
2016-08-17  1:02 ` Use verify_oacc_routine_clauses for C/C++ Thomas Schwinge
2018-10-02 14:12 ` [patch,openacc] Use oacc_verify_routine_clauses " Cesar Philippidis
2016-08-01 15:51   ` Repeated use of the OpenACC routine directive Thomas Schwinge
2016-08-17  1:05     ` Thomas Schwinge
2016-08-18 22:08       ` Cesar Philippidis
2016-08-19 14:39       ` Cesar Philippidis
2018-10-02 14:57     ` [patch,openacc] " Cesar Philippidis
2019-02-22 12:25       ` PR89433 "Repeated use of the OpenACC 'routine' directive" Thomas Schwinge
2019-05-17 19:17         ` [committed] " Thomas Schwinge
2020-03-04 19:27           ` [PATCH] Handle 'omp declare target' attribute set for both OpenACC and OpenMP 'target' [PR89433, PR93465] (was: [committed] PR89433 "Repeated use of the OpenACC 'routine' directive") Thomas Schwinge
2020-03-04 20:09             ` Jakub Jelinek
2020-04-10 14:14               ` [PATCH] Handle 'omp declare target' attribute set for both OpenACC and OpenMP 'target' [PR89433, PR93465] Thomas Schwinge

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