diff mbox series

[committed] Further work on declare variant

Message ID 20191012083147.GZ15914@tucnak
State New
Headers show
Series [committed] Further work on declare variant | expand

Commit Message

Jakub Jelinek Oct. 12, 2019, 8:31 a.m. UTC
Hi!

The following patch adds diagnostics if the same function serves as variant
for multiple base functions and there is a mismatch in the construct
selector set.  Additionally, it adds a start of a function that says if a
context selector matches the OpenMP context, doesn't or we don't know yet.
In the last case we need to defer the decision until later.
The C++ part for this isn't done, because I need to figure out the right way
to perform the lookup (using dummy arguments (and dummy instance for
methods) based on the function argument types and method context)).

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

2019-10-12  Jakub Jelinek  <jakub@redhat.com>

c-family/
	* c-common.h (c_omp_mark_declare_variant,
	c_omp_context_selector_matches): Declare.
	* c-omp.c: Include attribs.h, gimplify.h, cgraph.h, symbol-summary.h
	and hsa-common.h.
	(c_omp_get_context_selector): Support second argument NULL.
	(c_omp_mark_declare_variant, c_omp_context_selector_matches): New
	functions.
	* c-attribs.c (c_common_attribute_table): Remove "omp declare variant"
	attribute, add "omp declare variant base" and
	"omp declare variant variant" attributes.
c/
	* c-parser.c (c_parser_omp_context_selector): Improve error recovery.
	For simd properties, put them directly into TREE_VALUE.
	(c_finish_omp_declare_variant): Call c_omp_mark_declare_variant.
	If c_omp_context_selector_matches is 0, don't add attribute, otherwise
	add "omp declare variant base" attribute rather than
	"omp declare variant".
cp/
	* parser.c (cp_parser_omp_context_selector): Improve error recovery.
	For simd properties, put them directly into TREE_VALUE.
	(cp_finish_omp_declare_variant): Add "omp declare variant base"
	attribute rather than "omp declare variant".
testsuite/
	* c-c++-common/gomp/declare-variant-2.c: Adjust for error recovery
	improvements.  Add new tests.
	* c-c++-common/gomp/declare-variant-4.c: New test.
	* c-c++-common/gomp/declare-variant-5.c: New test.
	* c-c++-common/gomp/declare-variant-6.c: New test.
	* c-c++-common/gomp/declare-variant-7.c: New test.


	Jakub
diff mbox series

Patch

--- gcc/c-family/c-common.h.jj	2019-10-11 20:48:23.823186584 +0200
+++ gcc/c-family/c-common.h	2019-10-11 20:48:36.795991404 +0200
@@ -1191,6 +1191,8 @@  extern bool c_omp_predefined_variable (t
 extern enum omp_clause_default_kind c_omp_predetermined_sharing (tree);
 extern tree c_omp_check_context_selector (location_t, tree);
 extern tree c_omp_get_context_selector (tree, const char *, const char *);
+extern void c_omp_mark_declare_variant (location_t, tree, tree);
+extern int c_omp_context_selector_matches (tree);
 
 /* Return next tree in the chain for chain_next walking of tree nodes.  */
 static inline tree
--- gcc/c-family/c-omp.c.jj	2019-10-11 20:48:23.812186749 +0200
+++ gcc/c-family/c-omp.c	2019-10-11 21:07:33.083904116 +0200
@@ -32,6 +32,11 @@  along with GCC; see the file COPYING3.
 #include "omp-general.h"
 #include "gomp-constants.h"
 #include "memmodel.h"
+#include "attribs.h"
+#include "gimplify.h"
+#include "cgraph.h"
+#include "symbol-summary.h"
+#include "hsa-common.h"
 
 
 /* Complete a #pragma oacc wait construct.  LOC is the location of
@@ -2236,17 +2241,335 @@  c_omp_check_context_selector (location_t
 }
 
 /* From context selector CTX, return trait-selector with name SEL in
-   trait-selector-set with name SET if any, or NULL_TREE if not found.  */
+   trait-selector-set with name SET if any, or NULL_TREE if not found.
+   If SEL is NULL, return the list of trait-selectors in SET.  */
 
 tree
 c_omp_get_context_selector (tree ctx, const char *set, const char *sel)
 {
   tree setid = get_identifier (set);
-  tree selid = get_identifier (sel);
+  tree selid = sel ? get_identifier (sel) : NULL_TREE;
   for (tree t1 = ctx; t1; t1 = TREE_CHAIN (t1))
     if (TREE_PURPOSE (t1) == setid)
-      for (tree t2 = TREE_VALUE (t1); t2; t2 = TREE_CHAIN (t2))
-	if (TREE_PURPOSE (t2) == selid)
-	  return t2;
+      {
+	if (sel == NULL)
+	  return TREE_VALUE (t1);
+	for (tree t2 = TREE_VALUE (t1); t2; t2 = TREE_CHAIN (t2))
+	  if (TREE_PURPOSE (t2) == selid)
+	    return t2;
+      }
   return NULL_TREE;
 }
+
+/* Register VARIANT as variant of some base function marked with
+   #pragma omp declare variant.  CONSTRUCT is corresponding construct
+   selector set.  */
+
+void
+c_omp_mark_declare_variant (location_t loc, tree variant, tree construct)
+{
+  tree attr = lookup_attribute ("omp declare variant variant",
+				DECL_ATTRIBUTES (variant));
+  if (attr == NULL_TREE)
+    {
+      attr = tree_cons (get_identifier ("omp declare variant variant"),
+			unshare_expr (construct),
+			DECL_ATTRIBUTES (variant));
+      DECL_ATTRIBUTES (variant) = attr;
+      return;
+    }
+  tree t1 = TREE_VALUE (attr);
+  tree t2 = construct;
+  tree simd = get_identifier ("simd");
+  while (t1 && t2)
+    {
+      if (TREE_PURPOSE (t1) != TREE_PURPOSE (t2))
+	break;
+      if (TREE_PURPOSE (t1) == simd)
+	{
+	  if ((TREE_VALUE (t1) == NULL_TREE)
+	      != (TREE_VALUE (t2) == NULL_TREE))
+	    break;
+	  if (TREE_VALUE (t1))
+	    {
+	      struct declare_variant_simd_data {
+		bool inbranch, notinbranch;
+		tree simdlen;
+		auto_vec<tree,16> data_sharing;
+		auto_vec<tree,16> aligned;
+		declare_variant_simd_data ()
+		  : inbranch(false), notinbranch(false), simdlen(NULL_TREE) {}
+	      } data[2];
+	      unsigned int i;
+	      for (i = 0; i < 2; i++)
+		for (tree c = TREE_VALUE (i ? t2 : t1);
+		     c; c = OMP_CLAUSE_CHAIN (c))
+		  {
+		    vec<tree> *v;
+		    switch (OMP_CLAUSE_CODE (c))
+		      {
+		      case OMP_CLAUSE_INBRANCH:
+			data[i].inbranch = true;
+			continue;
+		      case OMP_CLAUSE_NOTINBRANCH:
+			data[i].notinbranch = true;
+			continue;
+		      case OMP_CLAUSE_SIMDLEN:
+			data[i].simdlen = OMP_CLAUSE_SIMDLEN_EXPR (c);
+			continue;
+		      case OMP_CLAUSE_UNIFORM:
+		      case OMP_CLAUSE_LINEAR:
+			v = &data[i].data_sharing;
+			break;
+		      case OMP_CLAUSE_ALIGNED:
+			v = &data[i].aligned;
+			break;
+		      default:
+			gcc_unreachable ();
+		      }
+		    unsigned HOST_WIDE_INT argno
+		      = tree_to_uhwi (OMP_CLAUSE_DECL (c));
+		    if (argno >= v->length ())
+		      v->safe_grow_cleared (argno + 1);
+		    (*v)[argno] = c;
+		  }
+	      if (data[0].inbranch != data[1].inbranch
+		  || data[0].notinbranch != data[1].notinbranch
+		  || !simple_cst_equal (data[0].simdlen,
+					data[1].simdlen)
+		  || (data[0].data_sharing.length ()
+		      != data[1].data_sharing.length ())
+		  || (data[0].aligned.length ()
+		      != data[1].aligned.length ()))
+		break;
+	      tree c1, c2;
+	      FOR_EACH_VEC_ELT (data[0].data_sharing, i, c1)
+		{
+		  c2 = data[1].data_sharing[i];
+		  if ((c1 == NULL_TREE) != (c2 == NULL_TREE))
+		    break;
+		  if (c1 == NULL_TREE)
+		    continue;
+		  if (OMP_CLAUSE_CODE (c1) != OMP_CLAUSE_CODE (c2))
+		    break;
+		  if (OMP_CLAUSE_CODE (c1) != OMP_CLAUSE_LINEAR)
+		    continue;
+		  if (OMP_CLAUSE_LINEAR_VARIABLE_STRIDE (c1)
+		      != OMP_CLAUSE_LINEAR_VARIABLE_STRIDE (c2))
+		    break;
+		  if (OMP_CLAUSE_LINEAR_KIND (c1)
+		      != OMP_CLAUSE_LINEAR_KIND (c2))
+		    break;
+		  if (!simple_cst_equal (OMP_CLAUSE_LINEAR_STEP (c1),
+					 OMP_CLAUSE_LINEAR_STEP (c2)))
+		    break;
+		}
+	      if (i < data[0].data_sharing.length ())
+		break;
+	      FOR_EACH_VEC_ELT (data[0].aligned, i, c1)
+		{
+		  c2 = data[1].aligned[i];
+		  if ((c1 == NULL_TREE) != (c2 == NULL_TREE))
+		    break;
+		  if (c1 == NULL_TREE)
+		    continue;
+		  if (!simple_cst_equal (OMP_CLAUSE_ALIGNED_ALIGNMENT (c1),
+					 OMP_CLAUSE_ALIGNED_ALIGNMENT (c2)))
+		    break;
+		}
+	      if (i < data[0].aligned.length ())
+		break;
+	    }
+	}
+      t1 = TREE_CHAIN (t1);
+      t2 = TREE_CHAIN (t2);
+    }
+  if (t1 || t2)
+    error_at (loc, "%qD used as a variant with incompatible %<constructor%> "
+		   "selector sets", variant);
+}
+
+/* Return 1 if context selector matches the current OpenMP context, 0
+   if it does not and -1 if it is unknown and need to be determined later.
+   Some properties can be checked right away during parsing (this routine),
+   others need to wait until the whole TU is parsed, others need to wait until
+   IPA, others until vectorization.  */
+
+int
+c_omp_context_selector_matches (tree ctx)
+{
+  int ret = 1;
+  for (tree t1 = ctx; t1; t1 = TREE_CHAIN (t1))
+    {
+      char set = IDENTIFIER_POINTER (TREE_PURPOSE (t1))[0];
+      if (set == 'c')
+	{
+	  /* For now, ignore the construct set.  While something can be
+	     determined already during parsing, we don't know until end of TU
+	     whether additional constructs aren't added through declare variant
+	     unless "omp declare variant variant" attribute exists already
+	     (so in most of the cases), and we'd need to maintain set of
+	     surrounding OpenMP constructs, which is better handled during
+	     gimplification.  */
+	  ret = -1;
+	  continue;
+	}
+      for (tree t2 = TREE_VALUE (t1); t2; t2 = TREE_CHAIN (t2))
+	{
+	  const char *sel = IDENTIFIER_POINTER (TREE_PURPOSE (t2));
+	  switch (*sel)
+	    {
+	    case 'v':
+	      if (set == 'i' && !strcmp (sel, "vendor"))
+		for (tree t3 = TREE_VALUE (t2); t3; t3 = TREE_CHAIN (t3))
+		  {
+		    const char *prop = IDENTIFIER_POINTER (TREE_PURPOSE (t3));
+		    if (!strcmp (prop, " score") || !strcmp (prop, "gnu"))
+		      continue;
+		    return 0;
+		  }
+	      break;
+	    case 'e':
+	      if (set == 'i' && !strcmp (sel, "extension"))
+		/* We don't support any extensions right now.  */
+		return 0;
+	      break;
+	    case 'a':
+	      if (set == 'i' && !strcmp (sel, "atomic_default_mem_order"))
+		{
+		  enum omp_memory_order omo
+		    = ((enum omp_memory_order)
+		       (omp_requires_mask
+			& OMP_REQUIRES_ATOMIC_DEFAULT_MEM_ORDER));
+		  if (omo == OMP_MEMORY_ORDER_UNSPECIFIED)
+		    {
+		      /* We don't know yet, until end of TU.  */
+		      ret = -1;
+		      break;
+		    }
+		  tree t3 = TREE_VALUE (t2);
+		  const char *prop = IDENTIFIER_POINTER (TREE_PURPOSE (t3));
+		  if (!strcmp (prop, " score"))
+		    {
+		      t3 = TREE_CHAIN (t3);
+		      prop = IDENTIFIER_POINTER (TREE_PURPOSE (t3));
+		    }
+		  if (!strcmp (prop, "relaxed")
+		      && omo != OMP_MEMORY_ORDER_RELAXED)
+		    return 0;
+		  else if (!strcmp (prop, "seq_cst")
+			   && omo != OMP_MEMORY_ORDER_SEQ_CST)
+		    return 0;
+		  else if (!strcmp (prop, "acq_rel")
+			   && omo != OMP_MEMORY_ORDER_ACQ_REL)
+		    return 0;
+		}
+	      if (set == 'd' && !strcmp (sel, "arch"))
+		/* For now, need a target hook.  */
+		ret = -1;
+	      break;
+	    case 'u':
+	      if (set == 'i' && !strcmp (sel, "unified_address"))
+		{
+		  if ((omp_requires_mask & OMP_REQUIRES_UNIFIED_ADDRESS) == 0)
+		    ret = -1;
+		  break;
+		}
+	      if (set == 'i' && !strcmp (sel, "unified_shared_memory"))
+		{
+		  if ((omp_requires_mask
+		       & OMP_REQUIRES_UNIFIED_SHARED_MEMORY) == 0)
+		    ret = -1;
+		  break;
+		}
+	      break;
+	    case 'd':
+	      if (set == 'i' && !strcmp (sel, "dynamic_allocators"))
+		{
+		  if ((omp_requires_mask
+		       & OMP_REQUIRES_DYNAMIC_ALLOCATORS) == 0)
+		    ret = -1;
+		  break;
+		}
+	      break;
+	    case 'r':
+	      if (set == 'i' && !strcmp (sel, "reverse_offload"))
+		{
+		  if ((omp_requires_mask & OMP_REQUIRES_REVERSE_OFFLOAD) == 0)
+		    ret = -1;
+		  break;
+		}
+	      break;
+	    case 'k':
+	      if (set == 'd' && !strcmp (sel, "kind"))
+		for (tree t3 = TREE_VALUE (t2); t3; t3 = TREE_CHAIN (t3))
+		  {
+		    const char *prop = IDENTIFIER_POINTER (TREE_PURPOSE (t3));
+		    if (!strcmp (prop, "any"))
+		      continue;
+		    if (!strcmp (prop, "fpga"))
+		      return 0;	/* Right now GCC doesn't support any fpgas.  */
+		    if (!strcmp (prop, "host"))
+		      {
+			if (ENABLE_OFFLOADING || hsa_gen_requested_p ())
+			  ret = -1;
+			continue;
+		      }
+		    if (!strcmp (prop, "nohost"))
+		      {
+			if (ENABLE_OFFLOADING || hsa_gen_requested_p ())
+			  ret = -1;
+			else
+			  return 0;
+			continue;
+		      }
+		    if (!strcmp (prop, "cpu") || !strcmp (prop, "gpu"))
+		      {
+			bool maybe_gpu = false;
+			if (hsa_gen_requested_p ())
+			  maybe_gpu = true;
+			else if (ENABLE_OFFLOADING)
+			  for (const char *c = getenv ("OFFLOAD_TARGET_NAMES");
+			       c; )
+			    {
+			      if (!strncmp (c, "nvptx", strlen ("nvptx"))
+				  || !strncmp (c, "amdgcn", strlen ("amdgcn")))
+				{
+				  maybe_gpu = true;
+				  break;
+				}
+			      else if ((c = strchr (c, ',')))
+				c++;
+			    }
+			if (!maybe_gpu)
+			  {
+			    if (prop[0] == 'g')
+			      return 0;
+			  }
+			else
+			  ret = -1;
+			continue;
+		      }
+		    /* Any other kind doesn't match.  */
+		    return 0;
+		  }
+	      break;
+	    case 'i':
+	      if (set == 'd' && !strcmp (sel, "isa"))
+		/* For now, need a target hook.  */
+		ret = -1;
+	      break;
+	    case 'c':
+	      if (set == 'u' && !strcmp (sel, "condition"))
+		for (tree t3 = TREE_VALUE (t2); t3; t3 = TREE_CHAIN (t3))
+		  if (TREE_PURPOSE (t3) == NULL_TREE
+		      && integer_zerop (TREE_VALUE (t3)))
+		    return 0;
+	      break;
+	    default:
+	      break;
+	    }
+	}
+    }
+  return ret;
+}
--- gcc/c-family/c-attribs.c.jj	2019-10-11 20:48:23.842186298 +0200
+++ gcc/c-family/c-attribs.c	2019-10-11 20:48:36.797991374 +0200
@@ -444,7 +444,9 @@  const struct attribute_spec c_common_att
 			      handle_returns_nonnull_attribute, NULL },
   { "omp declare simd",       0, -1, true,  false, false, false,
 			      handle_omp_declare_simd_attribute, NULL },
-  { "omp declare variant",    0, -1, true,  false, false, false,
+  { "omp declare variant base", 0, -1, true,  false, false, false,
+			      handle_omp_declare_variant_attribute, NULL },
+  { "omp declare variant variant", 0, -1, true,  false, false, false,
 			      handle_omp_declare_variant_attribute, NULL },
   { "simd",		      0, 1, true,  false, false, false,
 			      handle_simd_attribute, NULL },
@@ -3068,7 +3070,7 @@  handle_omp_declare_simd_attribute (tree
   return NULL_TREE;
 }
 
-/* Handle an "omp declare variant" attribute; arguments as in
+/* Handle an "omp declare variant {base,variant}" attribute; arguments as in
    struct attribute_spec.handler.  */
 
 static tree
--- gcc/c/c-parser.c.jj	2019-10-11 20:48:23.637189382 +0200
+++ gcc/c/c-parser.c	2019-10-11 20:48:36.801991314 +0200
@@ -19219,6 +19219,8 @@  c_parser_omp_context_selector (c_parser
 		      else
 			properties = tree_cons (NULL_TREE, t, properties);
 		    }
+		  else
+		    return error_mark_node;
 
 		  if (c_parser_next_token_is (parser, CPP_COMMA))
 		    c_parser_consume_token (parser);
@@ -19263,6 +19265,8 @@  c_parser_omp_context_selector (c_parser
 		  else
 		    properties = tree_cons (NULL_TREE, t, properties);
 		}
+	      else
+		return error_mark_node;
 	      break;
 	    case CTX_PROPERTY_SIMD:
 	      if (parms == NULL_TREE)
@@ -19280,7 +19284,7 @@  c_parser_omp_context_selector (c_parser
 							 == error_mark_node
 							 ? NULL_TREE : parms,
 							 c);
-	      properties = tree_cons (NULL_TREE, c, properties);
+	      properties = c;
 	      break;
 	    default:
 	      gcc_unreachable ();
@@ -19389,7 +19393,7 @@  c_parser_omp_context_selector_specificat
 }
 
 /* Finalize #pragma omp declare variant after FNDECL has been parsed, and put
-   that into "omp declare variant" attribute.  */
+   that into "omp declare variant base" attribute.  */
 
 static void
 c_finish_omp_declare_variant (c_parser *parser, tree fndecl, tree parms)
@@ -19473,10 +19477,16 @@  c_finish_omp_declare_variant (c_parser *
       if (variant != error_mark_node)
 	{
 	  C_DECL_USED (variant) = 1;
-	  tree attr = tree_cons (get_identifier ("omp declare variant"),
-				 build_tree_list (variant, ctx),
-				 DECL_ATTRIBUTES (fndecl));
-	  DECL_ATTRIBUTES (fndecl) = attr;
+	  tree construct = c_omp_get_context_selector (ctx, "construct", NULL);
+	  c_omp_mark_declare_variant (match_loc, variant, construct);
+	  if (c_omp_context_selector_matches (ctx))
+	    {
+	      tree attr
+		= tree_cons (get_identifier ("omp declare variant base"),
+			     build_tree_list (variant, ctx),
+			     DECL_ATTRIBUTES (fndecl));
+	      DECL_ATTRIBUTES (fndecl) = attr;
+	    }
 	}
     }
 
@@ -19486,7 +19496,7 @@  c_finish_omp_declare_variant (c_parser *
 
 /* Finalize #pragma omp declare simd or #pragma omp declare variant
    clauses after FNDECL has been parsed, and put that into "omp declare simd"
-   or "omp declare variant" attribute.  */
+   or "omp declare variant base" attribute.  */
 
 static void
 c_finish_omp_declare_simd (c_parser *parser, tree fndecl, tree parms,
--- gcc/cp/parser.c.jj	2019-10-11 20:48:23.607189833 +0200
+++ gcc/cp/parser.c	2019-10-11 20:48:36.806991239 +0200
@@ -40488,6 +40488,8 @@  cp_parser_omp_context_selector (cp_parse
 		      else
 			properties = tree_cons (NULL_TREE, t, properties);
 		    }
+		  else
+		    return error_mark_node;
 
 		  if (cp_lexer_next_token_is (parser->lexer, CPP_COMMA))
 		    cp_lexer_consume_token (parser->lexer);
@@ -40532,6 +40534,8 @@  cp_parser_omp_context_selector (cp_parse
 		  else
 		    properties = tree_cons (NULL_TREE, t, properties);
 		}
+	      else
+		return error_mark_node;
 	      break;
 	    case CTX_PROPERTY_SIMD:
 	      if (!has_parms_p)
@@ -40541,11 +40545,10 @@  cp_parser_omp_context_selector (cp_parse
 			    "%<metadirective%>");
 		  return error_mark_node;
 		}
-	      tree c;
-	      c = cp_parser_omp_all_clauses (parser,
+	      properties
+		= cp_parser_omp_all_clauses (parser,
 					     OMP_DECLARE_SIMD_CLAUSE_MASK,
 					     "simd", NULL, true, true);
-	      properties = tree_cons (NULL_TREE, c, properties);
 	      break;
 	    default:
 	      gcc_unreachable ();
@@ -40662,7 +40665,7 @@  cp_parser_omp_context_selector_specifica
 }
 
 /* Finalize #pragma omp declare variant after a fndecl has been parsed, and put
-   that into "omp declare variant" attribute.  */
+   that into "omp declare variant base" attribute.  */
 
 static tree
 cp_finish_omp_declare_variant (cp_parser *parser, cp_token *pragma_tok,
@@ -40717,7 +40720,7 @@  cp_finish_omp_declare_variant (cp_parser
   ctx = c_omp_check_context_selector (match_loc, ctx);
   if (ctx != error_mark_node && variant != error_mark_node)
     {
-      attrs = tree_cons (get_identifier ("omp declare variant"),
+      attrs = tree_cons (get_identifier ("omp declare variant base"),
 			 build_tree_list (variant, ctx), attrs);
       if (processing_template_decl)
 	ATTR_IS_DEPENDENT (attrs) = 1;
--- gcc/testsuite/c-c++-common/gomp/declare-variant-2.c.jj	2019-10-11 20:48:23.730187983 +0200
+++ gcc/testsuite/c-c++-common/gomp/declare-variant-2.c	2019-10-11 20:48:36.806991239 +0200
@@ -1,3 +1,4 @@ 
+void f0 (void);
 void f1 (void);
 #pragma omp declare variant	/* { dg-error "expected '\\(' before end of line" } */
 void f2 (void);
@@ -34,7 +35,7 @@  void f17 (void);
 #pragma omp declare variant (f1) match(user={condition})	/* { dg-error "expected '\\(' before '\\\}' token" } */
 void f18 (void);
 #pragma omp declare variant (f1) match(user={condition(})	/* { dg-error "expected \[^\n\r]*expression before '\\\}' token" } */
-void f19 (void);						/* { dg-error "expected '\\)' before '\\\}' token" "" { target c++ } .-1 } */
+void f19 (void);
 #pragma omp declare variant (f1) match(user={condition()})	/* { dg-error "expected \[^\n\r]*expression before '\\)' token" } */
 void f20 (void);
 #pragma omp declare variant (f1) match(user={condition(f1)})	/* { dg-error "property must be constant integer expression" "" { target { c || c++11 } } } */
@@ -50,7 +51,7 @@  void f25 (void);						/* { dg-error "exp
 								/* { dg-error "expected '\\\}' before '\\(' token" "" { target c } .-2 } */
 #pragma omp declare variant (f1) match(construct={parallel(1)})	/* { dg-error "selector 'parallel' does not accept any properties" } */
 void f26 (void);							/* { dg-error "expected '\\\}' before '\\(' token" "" { target c } .-1 } */
-#pragma omp declare variant (f1) match(construct={simd(12)})	/* { dg-error "expected \[^\n\r]* clause before" } */
+#pragma omp declare variant (f0) match(construct={simd(12)})	/* { dg-error "expected \[^\n\r]* clause before" } */
 void f27 (void);						/* { dg-error "'\\)' before numeric constant" "" { target c++ } .-1 } */
 #pragma omp declare variant (f1) match(construct={parallel},construct={for})	/* { dg-error "selector set 'construct' specified more than once" } */
 void f28 (void);
@@ -120,3 +121,31 @@  void f58 (void);						/* { dg-error "exp
 void f59 (void);						/* { dg-error "expected '\\\}' before '\\(' token" "" { target c } .-1 } */
 #pragma omp declare variant (f1) match(construct={parallel},foobar={bar})	/* { dg-error "expected 'construct', 'device', 'implementation' or 'user' before 'foobar'" } */
 void f60 (void);
+#pragma omp declare variant (f1) match(construct={parallel,parallel})	/* { dg-error "selector 'parallel' specified more than once in set 'construct'" } */
+void f61 (void);
+#pragma omp declare variant (f1) match(construct={target,parallel,for,simd,parallel})	/* { dg-error "selector 'parallel' specified more than once in set 'construct'" } */
+void f62 (void);
+#pragma omp declare variant (f1) match(construct={target,teams,teams})	/* { dg-error "selector 'teams' specified more than once in set 'construct'" } */
+void f63 (void);
+#pragma omp declare variant (f1) match(construct={single})	/* { dg-error "selector 'single' not allowed for context selector set 'construct'" } */
+void f64 (void);
+#pragma omp declare variant (f1) match(construct={taskgroup})	/* { dg-error "selector 'taskgroup' not allowed for context selector set 'construct'" } */
+void f65 (void);
+#pragma omp declare variant (f1) match(construct={do})	/* { dg-error "selector 'do' not allowed for context selector set 'construct'" } */
+void f66 (void);
+#pragma omp declare variant (f1) match(construct={threadprivate})	/* { dg-error "selector 'threadprivate' not allowed for context selector set 'construct'" } */
+void f67 (void);
+#pragma omp declare variant (f1) match(construct={critical})	/* { dg-error "selector 'critical' not allowed for context selector set 'construct'" } */
+void f68 (void);
+#pragma omp declare variant (f1) match(construct={task})	/* { dg-error "selector 'task' not allowed for context selector set 'construct'" } */
+void f69 (void);
+#pragma omp declare variant (f1) match(construct={taskloop})	/* { dg-error "selector 'taskloop' not allowed for context selector set 'construct'" } */
+void f70 (void);
+#pragma omp declare variant (f1) match(construct={sections})	/* { dg-error "selector 'sections' not allowed for context selector set 'construct'" } */
+void f71 (void);
+#pragma omp declare variant (f1) match(construct={section})	/* { dg-error "selector 'section' not allowed for context selector set 'construct'" } */
+void f72 (void);
+#pragma omp declare variant (f1) match(construct={workshare})	/* { dg-error "selector 'workshare' not allowed for context selector set 'construct'" } */
+void f73 (void);
+#pragma omp declare variant (f1) match(construct={requires})	/* { dg-error "selector 'requires' not allowed for context selector set 'construct'" } */
+void f74 (void);
--- gcc/testsuite/c-c++-common/gomp/declare-variant-4.c.jj	2019-10-11 20:48:36.806991239 +0200
+++ gcc/testsuite/c-c++-common/gomp/declare-variant-4.c	2019-10-11 20:48:36.806991239 +0200
@@ -0,0 +1,22 @@ 
+double f1 (int, long, float);
+double f2 (int, long, float);
+double f3 (int, long, float);
+double f4 (int, long, float);
+double f5 (int, long, float);
+
+#pragma omp declare variant (f1) match (user={condition(1)})
+#pragma omp declare variant (f2) match (user={condition(score(1):1)})
+#pragma omp declare variant (f3) match (user={condition(score(3):1)})
+#pragma omp declare variant (f4) match (user={condition(score(2):1)})
+#pragma omp declare variant (f5) match (implementation={vendor(gnu)})
+double
+f6 (int x, long y, float z)
+{
+  return z + x + y;
+}
+
+double
+test (int x)
+{
+  return f6 (x, x, 3.5f);
+}
--- gcc/testsuite/c-c++-common/gomp/declare-variant-5.c.jj	2019-10-11 20:48:36.806991239 +0200
+++ gcc/testsuite/c-c++-common/gomp/declare-variant-5.c	2019-10-11 20:48:36.806991239 +0200
@@ -0,0 +1,36 @@ 
+/* { dg-do compile { target i?86-*-* x86_64-*-* } } */
+/* { dg-additional-options "-mavx2" } */
+
+typedef float __v4sf __attribute__((vector_size (16)));
+typedef int __v4si __attribute__((vector_size (16)));
+typedef float __v8sf __attribute__((vector_size (32)));
+typedef int __v8si __attribute__((vector_size (32)));
+__v4si f1 (__v4sf, __v4sf, float *);
+__v8si f2 (__v8sf, __v8sf, float *);
+__v4si f3 (__v4si, int, __v4si);
+
+#pragma omp declare variant (f1) match (construct={parallel,for,simd(simdlen(4),notinbranch,uniform(z),aligned(z:4 * sizeof (*z)))})
+#pragma omp declare variant (f2) match (construct={for,simd(uniform(z),simdlen(8),notinbranch)})
+int f4 (float x, float y, float *z);
+
+#pragma omp declare variant (f3) match (construct={simd(simdlen(4),inbranch,linear(y:1))})
+int f5 (int x, int y);
+
+void
+test (int *x, float *y, float *z, float *w)
+{
+  #pragma omp parallel
+  #pragma omp for simd aligned (w:4 * sizeof (float))
+  for (int i = 0; i < 1024; i++)
+    x[i] = f4 (y[i], z[i], w);
+  #pragma omp parallel for simd aligned (w:4 * sizeof (float)) simdlen(4)
+  for (int i = 1024; i < 2048; i++)
+    x[i] = f4 (y[i], z[i], w);
+  #pragma omp simd aligned (w:4 * sizeof (float))
+  for (int i = 2048; i < 4096; i++)
+    x[i] = f4 (y[i], z[i], w);
+  #pragma omp simd
+  for (int i = 4096; i < 8192; i++)
+    if (x[i] > 10)
+      x[i] = f5 (x[i], i);
+}
--- gcc/testsuite/c-c++-common/gomp/declare-variant-6.c.jj	2019-10-11 20:48:36.806991239 +0200
+++ gcc/testsuite/c-c++-common/gomp/declare-variant-6.c	2019-10-11 20:48:36.806991239 +0200
@@ -0,0 +1,35 @@ 
+double f1 (int, long, float);
+double f2 (int, long, float);
+#pragma omp declare variant (f1) match (user={condition(0)},construct={parallel})
+double f3 (int, long, float);
+#pragma omp declare variant (f1) match (construct={parallel},user={condition(score(1):1)})
+double f4 (int, long, float);
+double f5 (int, long, float);
+#pragma omp declare variant (f5) match (user={condition(0)})
+double f6 (int, long, float);
+#pragma omp declare variant (f5) match (construct={parallel},user={condition(score(1):1)})	/* { dg-error "'f5' used as a variant with incompatible 'constructor' selector sets" "" { target c } } */
+double f7 (int, long, float);
+double f8 (int, long, float);
+#pragma omp declare variant (f8) match (user={condition(0)},construct={for})
+double f9 (int, long, float);
+#pragma omp declare variant (f8) match (user={condition(1)})					/* { dg-error "'f8' used as a variant with incompatible 'constructor' selector sets" "" { target c } } */
+double f10 (int, long, float);
+double f11 (int, long, float);
+#pragma omp declare variant (f11) match (construct={target,teams,parallel,for})
+double f12 (int, long, float);
+#pragma omp declare variant (f11) match (user={condition(score(1):1)},construct={target,teams,parallel,for})
+double f13 (int, long, float);
+#pragma omp declare variant (f11) match (implementation={vendor(gnu)},construct={target,teams,parallel})	/* { dg-error "'f11' used as a variant with incompatible 'constructor' selector sets" "" { target c } } */
+double f14 (int, long, float);
+#pragma omp declare variant (f11) match (device={kind(any)},construct={teams,parallel})		/* { dg-error "'f11' used as a variant with incompatible 'constructor' selector sets" "" { target c } } */
+double f15 (int, long, float);
+double f16 (int, long, float);
+#pragma omp declare variant (f16) match (construct={teams,parallel})
+double f17 (int, long, float);
+#pragma omp declare variant (f16) match(construct={teams,parallel,for})				/* { dg-error "'f16' used as a variant with incompatible 'constructor' selector sets" "" { target c } } */
+double f18 (int, long, float);
+double f19 (int, long, float);
+#pragma omp declare variant (f19) match (construct={parallel})
+double f20 (int, long, float);
+#pragma omp declare variant (f19) match (construct={for},implementation={vendor(gnu,llvm)})	/* { dg-error "'f19' used as a variant with incompatible 'constructor' selector sets" "" { target c } } */
+double f21 (int, long, float);
--- gcc/testsuite/c-c++-common/gomp/declare-variant-7.c.jj	2019-10-11 20:48:36.806991239 +0200
+++ gcc/testsuite/c-c++-common/gomp/declare-variant-7.c	2019-10-11 20:48:36.806991239 +0200
@@ -0,0 +1,33 @@ 
+/* { dg-do compile { target i?86-*-* x86_64-*-* } } */
+/* { dg-additional-options "-mavx2" } */
+
+typedef float __v4sf __attribute__((vector_size (16)));
+typedef int __v4si __attribute__((vector_size (16)));
+typedef float __v8sf __attribute__((vector_size (32)));
+typedef int __v8si __attribute__((vector_size (32)));
+__v4si f1 (__v4sf, __v4sf, float *);
+__v8si f2 (__v8sf, __v8sf, float *);
+__v4si f3 (__v4si, int, __v4si);
+
+#pragma omp declare variant (f1) match (construct={parallel,for,simd(simdlen(4),notinbranch,uniform(z),aligned(z:4 * sizeof (*z)))})
+int f4 (float x, float y, float *z);
+#pragma omp declare variant (f1) match (construct={parallel,for,simd(uniform(w),simdlen(8*2-12),aligned(w:4*sizeof (float)),notinbranch)})
+int f5 (float u, float v, float *w);
+#pragma omp declare variant (f1) match (construct={parallel,for,simd(linear(w),notinbranch,simdlen(4),aligned(w:4*sizeof (float)))})	/* { dg-error "'f1' used as a variant with incompatible 'constructor' selector sets" "" { target c } } */
+int f6 (float u, float v, float *w);
+#pragma omp declare variant (f1) match (construct={parallel,for,simd(uniform(w),notinbranch,simdlen(4),aligned(w:2*sizeof (float)))})	/* { dg-error "'f1' used as a variant with incompatible 'constructor' selector sets" "" { target c } } */
+int f7 (float u, float v, float *w);
+#pragma omp declare variant (f1) match (construct={parallel,for,simd(uniform(w),notinbranch,simdlen(4),aligned(w))})			/* { dg-error "'f1' used as a variant with incompatible 'constructor' selector sets" "" { target c } } */
+int f8 (float u, float v, float *w);
+#pragma omp declare variant (f2) match (construct={for,simd(uniform(z),simdlen(8),notinbranch)})
+int f9 (float x, float y, float *z);
+#pragma omp declare variant (f2) match (construct={for,simd(notinbranch,simdlen(2+2+4),uniform (q))})
+int f10 (float x, float y, float *q);
+#pragma omp declare variant (f2) match (construct={for,simd(linear(z:2),simdlen(8),notinbranch)})	/* { dg-error "'f2' used as a variant with incompatible 'constructor' selector sets" "" { target c } } */
+int f11 (float x, float y, float *z);
+#pragma omp declare variant (f3) match (construct={simd(simdlen(4),inbranch,linear(y:1))})
+int f12 (int x, int y);
+#pragma omp declare variant (f3) match (construct={simd(inbranch,simdlen(5-1),linear(q:4-3))})
+int f13 (int x, int q);
+#pragma omp declare variant (f3) match (construct={simd(inbranch,simdlen(4),linear(q:2))})		/* { dg-error "'f3' used as a variant with incompatible 'constructor' selector sets" "" { target c } } */
+int f14 (int x, int q);