[committed] OpenMP declare variant context strict subset testing
diff mbox series

Message ID 20191101233224.GW4650@tucnak
State New
Headers show
Series
  • [committed] OpenMP declare variant context strict subset testing
Related show

Commit Message

Jakub Jelinek Nov. 1, 2019, 11:32 p.m. UTC
Hi!

The following patch implements one sentence from the OpenMP 5
standard:
"A context selector that is a strict subset of another context selector
has a score of zero."
I have scoring mostly written too, but haven't added testcases yet, so that
will be in a follow-up patch.

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

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

	* omp-general.h (omp_context_selector_set_compare): Declare.
	* omp-general.c (omp_construct_simd_compare,
	omp_context_selector_props_compare, omp_context_selector_set_compare,
	omp_context_selector_compare): New functions.
	(omp_resolve_declare_variant): Prune variants that are strict subset
	of another variant.
c-family/
	* c-omp.c (c_omp_mark_declare_variant): Use
	omp_context_selector_set_compare.
testsuite/
	* c-c++-common/gomp/declare-variant-6.c: Expect construct rather than
	constructor in diagnostic messages.
	* c-c++-common/gomp/declare-variant-7.c: Likewise.
	* c-c++-common/gomp/declare-variant-11.c: New test.


	Jakub

Patch
diff mbox series

--- gcc/omp-general.h.jj	2019-10-25 00:27:43.618318678 +0200
+++ gcc/omp-general.h	2019-11-01 12:37:33.806852041 +0100
@@ -86,6 +86,7 @@  extern poly_uint64 omp_max_vf (void);
 extern int omp_max_simt_vf (void);
 extern int omp_constructor_traits_to_codes (tree, enum tree_code *);
 extern int omp_context_selector_matches (tree);
+extern int omp_context_selector_set_compare (const char *, tree, tree);
 extern tree omp_resolve_declare_variant (tree);
 extern tree oacc_launch_pack (unsigned code, tree device, unsigned op);
 extern tree oacc_replace_fn_attrib_attr (tree attribs, tree dims);
--- gcc/omp-general.c.jj	2019-10-31 11:05:50.519136130 +0100
+++ gcc/omp-general.c	2019-11-01 14:53:18.593998097 +0100
@@ -947,6 +947,320 @@  omp_context_selector_matches (tree ctx)
   return ret;
 }
 
+/* Compare construct={simd} CLAUSES1 with CLAUSES2, return 0/-1/1/2 as
+   in omp_context_selector_set_compare.  */
+
+static int
+omp_construct_simd_compare (tree clauses1, tree clauses2)
+{
+  if (clauses1 == NULL_TREE)
+    return clauses2 == NULL_TREE ? 0 : -1;
+  if (clauses2 == NULL_TREE)
+    return 1;
+
+  int r = 0;
+  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 = i ? clauses2 : clauses1; 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;
+      }
+  /* Here, r is used as a bitmask, 2 is set if CLAUSES1 has something
+     CLAUSES2 doesn't, 1 is set if CLAUSES2 has something CLAUSES1
+     doesn't.  Thus, r == 3 implies return value 2, r == 1 implies
+     -1, r == 2 implies 1 and r == 0 implies 0.  */
+  if (data[0].inbranch != data[1].inbranch)
+    r |= data[0].inbranch ? 2 : 1;
+  if (data[0].notinbranch != data[1].notinbranch)
+    r |= data[0].notinbranch ? 2 : 1;
+  if (!simple_cst_equal (data[0].simdlen, data[1].simdlen))
+    {
+      if (data[0].simdlen && data[1].simdlen)
+	return 2;
+      r |= data[0].simdlen ? 2 : 1;
+    }
+  if (data[0].data_sharing.length () < data[1].data_sharing.length ()
+      || data[0].aligned.length () < data[1].aligned.length ())
+    r |= 1;
+  tree c1, c2;
+  FOR_EACH_VEC_ELT (data[0].data_sharing, i, c1)
+    {
+      c2 = (i < data[1].data_sharing.length ()
+	    ? data[1].data_sharing[i] : NULL_TREE);
+      if ((c1 == NULL_TREE) != (c2 == NULL_TREE))
+	{
+	  r |= c1 != NULL_TREE ? 2 : 1;
+	  continue;
+	}
+      if (c1 == NULL_TREE)
+	continue;
+      if (OMP_CLAUSE_CODE (c1) != OMP_CLAUSE_CODE (c2))
+	return 2;
+      if (OMP_CLAUSE_CODE (c1) != OMP_CLAUSE_LINEAR)
+	continue;
+      if (OMP_CLAUSE_LINEAR_VARIABLE_STRIDE (c1)
+	  != OMP_CLAUSE_LINEAR_VARIABLE_STRIDE (c2))
+	return 2;
+      if (OMP_CLAUSE_LINEAR_KIND (c1) != OMP_CLAUSE_LINEAR_KIND (c2))
+	return 2;
+      if (!simple_cst_equal (OMP_CLAUSE_LINEAR_STEP (c1),
+			     OMP_CLAUSE_LINEAR_STEP (c2)))
+	return 2;
+    }
+  FOR_EACH_VEC_ELT (data[0].aligned, i, c1)
+    {
+      c2 = i < data[1].aligned.length () ? data[1].aligned[i] : NULL_TREE;
+      if ((c1 == NULL_TREE) != (c2 == NULL_TREE))
+	{
+	  r |= c1 != NULL_TREE ? 2 : 1;
+	  continue;
+	}
+      if (c1 == NULL_TREE)
+	continue;
+      if (!simple_cst_equal (OMP_CLAUSE_ALIGNED_ALIGNMENT (c1),
+			     OMP_CLAUSE_ALIGNED_ALIGNMENT (c2)))
+	return 2;
+    }
+  switch (r)
+    {
+    case 0: return 0;
+    case 1: return -1;
+    case 2: return 1;
+    case 3: return 2;
+    default: gcc_unreachable ();
+    }
+}
+
+/* Compare properties of selectors SEL from SET other than construct.
+   Return 0/-1/1/2 as in omp_context_selector_set_compare.
+   Unlike set names or selector names, properties can have duplicates.  */
+
+static int
+omp_context_selector_props_compare (const char *set, const char *sel,
+				    tree ctx1, tree ctx2)
+{
+  int ret = 0;
+  for (int pass = 0; pass < 2; pass++)
+    for (tree t1 = pass ? ctx2 : ctx1; t1; t1 = TREE_CHAIN (t1))
+      {
+	tree t2;
+	for (t2 = pass ? ctx1 : ctx2; t2; t2 = TREE_CHAIN (t2))
+	  if (TREE_PURPOSE (t1) == TREE_PURPOSE (t2))
+	    {
+	      if (TREE_PURPOSE (t1) == NULL_TREE)
+		{
+		  if (set[0] == 'u' && strcmp (sel, "condition") == 0)
+		    {
+		      if (integer_zerop (TREE_VALUE (t1))
+			  != integer_zerop (TREE_VALUE (t2)))
+			return 2;
+		      break;
+		    }
+		  if (simple_cst_equal (TREE_VALUE (t1), TREE_VALUE (t2)))
+		    break;
+		}
+	      else if (strcmp (IDENTIFIER_POINTER (TREE_PURPOSE (t1)),
+			       " score") == 0)
+		{
+		  if (!simple_cst_equal (TREE_VALUE (t1), TREE_VALUE (t2)))
+		    return 2;
+		  break;
+		}
+	      else
+		break;
+	    }
+	if (t2 == NULL_TREE)
+	  {
+	    int r = pass ? -1 : 1;
+	    if (ret && ret != r)
+	      return 2;
+	    else if (pass)
+	      return r;
+	    else
+	      {
+		ret = r;
+		break;
+	      }
+	  }
+      }
+  return ret;
+}
+
+/* Compare single context selector sets CTX1 and CTX2 with SET name.
+   Return 0 if CTX1 is equal to CTX2,
+   -1 if CTX1 is a strict subset of CTX2,
+   1 if CTX2 is a strict subset of CTX1, or
+   2 if neither context is a subset of another one.  */
+
+int
+omp_context_selector_set_compare (const char *set, tree ctx1, tree ctx2)
+{
+  bool swapped = false;
+  int ret = 0;
+  int len1 = list_length (ctx1);
+  int len2 = list_length (ctx2);
+  int cnt = 0;
+  if (len1 < len2)
+    {
+      swapped = true;
+      std::swap (ctx1, ctx2);
+      std::swap (len1, len2);
+    }
+  if (set[0] == 'c')
+    {
+      tree t1;
+      tree t2 = ctx2;
+      tree simd = get_identifier ("simd");
+      /* Handle construct set specially.  In this case the order
+	 of the selector matters too.  */
+      for (t1 = ctx1; t1; t1 = TREE_CHAIN (t1))
+	if (TREE_PURPOSE (t1) == TREE_PURPOSE (t2))
+	  {
+	    int r = 0;
+	    if (TREE_PURPOSE (t1) == simd)
+	      r = omp_construct_simd_compare (TREE_VALUE (t1),
+					      TREE_VALUE (t2));
+	    if (r == 2 || (ret && r && (ret < 0) != (r < 0)))
+	      return 2;
+	    if (ret == 0)
+	      ret = r;
+	    t2 = TREE_CHAIN (t2);
+	    if (t2 == NULL_TREE)
+	      {
+		t1 = TREE_CHAIN (t1);
+		break;
+	      }
+	  }
+	else if (ret < 0)
+	  return 2;
+	else
+	  ret = 1;
+      if (t2 != NULL_TREE)
+	return 2;
+      if (t1 != NULL_TREE)
+	{
+	  if (ret < 0)
+	    return 2;
+	  ret = 1;
+	}
+      if (ret == 0)
+	return 0;
+      return swapped ? -ret : ret;
+    }
+  for (tree t1 = ctx1; t1; t1 = TREE_CHAIN (t1))
+    {
+      tree t2;
+      for (t2 = ctx2; t2; t2 = TREE_CHAIN (t2))
+	if (TREE_PURPOSE (t1) == TREE_PURPOSE (t2))
+	  {
+	    const char *sel = IDENTIFIER_POINTER (TREE_PURPOSE (t1));
+	    int r = omp_context_selector_props_compare (set, sel,
+							TREE_VALUE (t1),
+							TREE_VALUE (t2));
+	    if (r == 2 || (ret && r && (ret < 0) != (r < 0)))
+	      return 2;
+	    if (ret == 0)
+	      ret = r;
+	    cnt++;
+	    break;
+	  }
+      if (t2 == NULL_TREE)
+	{
+	  if (ret == -1)
+	    return 2;
+	  ret = 1;
+	}
+    }
+  if (cnt < len2)
+    return 2;
+  if (ret == 0)
+    return 0;
+  return swapped ? -ret : ret;
+}
+
+/* Compare whole context selector specification CTX1 and CTX2.
+   Return 0 if CTX1 is equal to CTX2,
+   -1 if CTX1 is a strict subset of CTX2,
+   1 if CTX2 is a strict subset of CTX1, or
+   2 if neither context is a subset of another one.  */
+
+static int
+omp_context_selector_compare (tree ctx1, tree ctx2)
+{
+  bool swapped = false;
+  int ret = 0;
+  int len1 = list_length (ctx1);
+  int len2 = list_length (ctx2);
+  int cnt = 0;
+  if (len1 < len2)
+    {
+      swapped = true;
+      std::swap (ctx1, ctx2);
+      std::swap (len1, len2);
+    }
+  for (tree t1 = ctx1; t1; t1 = TREE_CHAIN (t1))
+    {
+      tree t2;
+      for (t2 = ctx2; t2; t2 = TREE_CHAIN (t2))
+	if (TREE_PURPOSE (t1) == TREE_PURPOSE (t2))
+	  {
+	    const char *set = IDENTIFIER_POINTER (TREE_PURPOSE (t1));
+	    int r = omp_context_selector_set_compare (set, TREE_VALUE (t1),
+						      TREE_VALUE (t2));
+	    if (r == 2 || (ret && r && (ret < 0) != (r < 0)))
+	      return 2;
+	    if (ret == 0)
+	      ret = r;
+	    cnt++;
+	    break;
+	  }
+      if (t2 == NULL_TREE)
+	{
+	  if (ret == -1)
+	    return 2;
+	  ret = 1;
+	}
+    }
+  if (cnt < len2)
+    return 2;
+  if (ret == 0)
+    return 0;
+  return swapped ? -ret : ret;
+}
+
 /* Try to resolve declare variant, return the variant decl if it should
    be used instead of base, or base otherwise.  */
 
@@ -954,11 +1268,14 @@  tree
 omp_resolve_declare_variant (tree base)
 {
   tree variant = NULL_TREE;
+  auto_vec <tree, 16> variants;
   for (tree attr = DECL_ATTRIBUTES (base); attr; attr = TREE_CHAIN (attr))
     {
       attr = lookup_attribute ("omp declare variant base", attr);
       if (attr == NULL_TREE)
 	break;
+      if (TREE_CODE (TREE_PURPOSE (TREE_VALUE (attr))) != FUNCTION_DECL)
+	continue;
       switch (omp_context_selector_matches (TREE_VALUE (TREE_VALUE (attr))))
 	{
 	case 0:
@@ -968,16 +1285,49 @@  omp_resolve_declare_variant (tree base)
 	  /* Needs to be deferred.  */
 	  return base;
 	default:
-	  /* FIXME: Scoring not implemented yet, so just resolve it
-	     if there is a single variant only.  */
-	  if (variant)
-	    return base;
-	  if (TREE_CODE (TREE_PURPOSE (TREE_VALUE (attr))) == FUNCTION_DECL)
-	    variant = TREE_PURPOSE (TREE_VALUE (attr));
-	  else
-	    return base;
+	  variants.safe_push (attr);
 	}
     }
+  if (variants.length () == 0)
+    return base;
+  if (variants.length () == 1)
+    return TREE_PURPOSE (TREE_VALUE (variants[0]));
+
+  /* A context selector that is a strict subset of another context selector has a score
+     of zero.  */
+  tree attr1, attr2;
+  unsigned int i, j;
+  FOR_EACH_VEC_ELT (variants, i, attr1)
+    if (attr1)
+      {
+	tree ctx1 = TREE_VALUE (TREE_VALUE (attr1));
+	FOR_EACH_VEC_ELT_FROM (variants, j, attr2, i + 1)
+	  if (attr2)
+	    {
+	      tree ctx2 = TREE_VALUE (TREE_VALUE (attr2));
+	      int r = omp_context_selector_compare (ctx1, ctx2);
+	      if (r == -1)
+		{
+		  /* ctx1 is a strict subset of ctx2, remove
+		     attr1 from the vector.  */
+		  variants[i] = NULL_TREE;
+		  break;
+		}
+	      else if (r == 1)
+		/* ctx2 is a strict subset of ctx1, remove attr2
+		   from the vector.  */
+		variants[j] = NULL_TREE;
+	    }
+      }
+  /* FIXME: Scoring not implemented yet, so just resolve it
+     if there is a single variant left.  */
+  FOR_EACH_VEC_ELT (variants, i, attr1)
+    if (attr1)
+      {
+	if (variant)
+	  return base;
+	variant = TREE_PURPOSE (TREE_VALUE (attr1));
+      }
   return variant ? variant : base;
 }
 
--- gcc/c-family/c-omp.c.jj	2019-10-25 00:27:43.000000000 +0200
+++ gcc/c-family/c-omp.c	2019-11-01 13:06:51.462874386 +0100
@@ -2275,113 +2275,10 @@  c_omp_mark_declare_variant (location_t l
       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%> "
+  if ((TREE_VALUE (attr) != NULL_TREE) != (construct != NULL_TREE)
+      || (construct != NULL_TREE
+	  && omp_context_selector_set_compare ("construct", TREE_VALUE (attr),
+					       construct)))
+    error_at (loc, "%qD used as a variant with incompatible %<construct%> "
 		   "selector sets", variant);
 }
--- gcc/testsuite/c-c++-common/gomp/declare-variant-6.c.jj	2019-10-30 12:38:38.187142834 +0100
+++ gcc/testsuite/c-c++-common/gomp/declare-variant-6.c	2019-11-01 13:08:44.300144825 +0100
@@ -7,29 +7,29 @@  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 "'\[^'\n\r]*f5\[^'\n\r]*' used as a variant with incompatible 'constructor' selector sets" } */
+#pragma omp declare variant (f5) match (construct={parallel},user={condition(score(1):1)})	/* { dg-error "'\[^'\n\r]*f5\[^'\n\r]*' used as a variant with incompatible 'construct' selector sets" } */
 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 "'\[^'\n\r]*f8\[^'\n\r]*' used as a variant with incompatible 'constructor' selector sets" } */
+#pragma omp declare variant (f8) match (user={condition(1)})					/* { dg-error "'\[^'\n\r]*f8\[^'\n\r]*' used as a variant with incompatible 'construct' selector sets" } */
 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 "'\[^'\n\r]*f11\[^'\n\r]*' used as a variant with incompatible 'constructor' selector sets" } */
+#pragma omp declare variant (f11) match (implementation={vendor(gnu)},construct={target,teams,parallel})	/* { dg-error "'\[^'\n\r]*f11\[^'\n\r]*' used as a variant with incompatible 'construct' selector sets" } */
 double f14 (int, long, float);
-#pragma omp declare variant (f11) match (device={kind(any)},construct={teams,parallel})		/* { dg-error "'\[^'\n\r]*f11\[^'\n\r]*' used as a variant with incompatible 'constructor' selector sets" } */
+#pragma omp declare variant (f11) match (device={kind(any)},construct={teams,parallel})		/* { dg-error "'\[^'\n\r]*f11\[^'\n\r]*' used as a variant with incompatible 'construct' selector sets" } */
 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 "'\[^'\n\r]*f16\[^'\n\r]*' used as a variant with incompatible 'constructor' selector sets" } */
+#pragma omp declare variant (f16) match(construct={teams,parallel,for})				/* { dg-error "'\[^'\n\r]*f16\[^'\n\r]*' used as a variant with incompatible 'construct' selector sets" } */
 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 "'\[^'\n\r]*f19\[^'\n\r]*' used as a variant with incompatible 'constructor' selector sets" } */
+#pragma omp declare variant (f19) match (construct={for},implementation={vendor(gnu,llvm)})	/* { dg-error "'\[^'\n\r]*f19\[^'\n\r]*' used as a variant with incompatible 'construct' selector sets" } */
 double f21 (int, long, float);
--- gcc/testsuite/c-c++-common/gomp/declare-variant-7.c.jj	2019-10-14 08:52:03.747578485 +0200
+++ gcc/testsuite/c-c++-common/gomp/declare-variant-7.c	2019-11-01 13:09:41.771263923 +0100
@@ -13,23 +13,23 @@  __v4si f3 (__v4si, int, __v4si);
 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 } } */
+#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 'construct' 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 } } */
+#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 'construct' 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 } } */
+#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 'construct' 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 } } */
+#pragma omp declare variant (f2) match (construct={for,simd(linear(z:2),simdlen(8),notinbranch)})	/* { dg-error "'f2' used as a variant with incompatible 'construct' 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 } } */
+#pragma omp declare variant (f3) match (construct={simd(inbranch,simdlen(4),linear(q:2))})		/* { dg-error "'f3' used as a variant with incompatible 'construct' selector sets" "" { target c } } */
 int f14 (int x, int q);
 #pragma omp declare variant (f3) match (construct={simd(inbranch simdlen (4) linear (q:1))})		/* { dg-error "clauses in 'simd' trait should be separated by ','" } */
 int f15 (int x, int q);
--- gcc/testsuite/c-c++-common/gomp/declare-variant-11.c.jj	2019-11-01 13:13:46.817507882 +0100
+++ gcc/testsuite/c-c++-common/gomp/declare-variant-11.c	2019-11-01 14:31:22.285145658 +0100
@@ -0,0 +1,83 @@ 
+/* { dg-do compile } */
+/* { dg-additional-options "-foffload=disable -fdump-tree-gimple" } */
+/* { dg-additional-options "-mavx512bw -mavx512vl" { target { i?86-*-* x86_64-*-* } } } */
+
+void f01 (void);
+void f02 (void);
+#pragma omp declare variant (f01) match (device={isa(avx512f,avx512vl)})
+#pragma omp declare variant (f02) match (device={isa(avx512bw,avx512vl,avx512f)})
+void f03 (void);
+void f04 (void);
+void f05 (void);
+#pragma omp declare variant (f04) match (device={isa(avx512f,avx512vl)})
+#pragma omp declare variant (f05) match (device={isa(avx512bw,avx512vl,avx512f)})
+void f06 (void);
+void f07 (void);
+void f08 (void);
+#pragma omp declare variant (f07) match (device={isa(sse4,sse3,avx)})
+#pragma omp declare variant (f08) match (device={isa(avx,sse3)})
+void f09 (void);
+void f10 (void);
+void f11 (void);
+void f12 (void);
+#pragma omp declare variant (f10) match (device={isa(avx512f)})
+#pragma omp declare variant (f11) match (user={condition(1)},device={isa(avx512f)},implementation={vendor(gnu)})
+#pragma omp declare variant (f12) match (user={condition(2 + 1)},device={isa(avx512f)})
+void f13 (void);
+void f14 (void);
+void f15 (void);
+void f16 (void);
+void f17 (void);
+#pragma omp declare variant (f14) match (construct={teams,for})
+#pragma omp declare variant (f15) match (construct={teams,parallel,for})
+#pragma omp declare variant (f16) match (construct={for})
+#pragma omp declare variant (f17) match (construct={parallel,for})
+void f18 (void);
+void f19 (void);
+void f20 (void);
+void f21 (void);
+void f22 (void);
+#pragma omp declare variant (f19) match (construct={teams,for})
+#pragma omp declare variant (f20) match (construct={teams,parallel,for})
+#pragma omp declare variant (f21) match (construct={for})
+#pragma omp declare variant (f22) match (construct={parallel,for})
+void f23 (void);
+void f24 (void);
+void f25 (void);
+void f26 (void);
+#pragma omp declare variant (f24) match (device={kind(cpu)})
+#pragma omp declare variant (f25) match (device={kind(cpu),isa(avx512f),arch(x86_64)})
+#pragma omp declare variant (f26) match (device={arch(x86_64),kind(cpu)})
+void f27 (void);
+
+void
+test1 (void)
+{
+  int i;
+  f03 ();	/* { dg-final { scan-tree-dump-times "f02 \\\(\\\);" 1 "gimple" { target i?86-*-* x86_64-*-* } } } */
+		/* { dg-final { scan-tree-dump-times "f03 \\\(\\\);" 1 "gimple" { target { ! { i?86-*-* x86_64-*-* } } } } } */
+  f09 ();	/* { dg-final { scan-tree-dump-times "f07 \\\(\\\);" 1 "gimple" { target i?86-*-* x86_64-*-* } } } */
+		/* { dg-final { scan-tree-dump-times "f09 \\\(\\\);" 1 "gimple" { target { ! { i?86-*-* x86_64-*-* } } } } } */
+  f13 ();	/* { dg-final { scan-tree-dump-times "f11 \\\(\\\);" 1 "gimple" { target i?86-*-* x86_64-*-* } } } */
+		/* { dg-final { scan-tree-dump-times "f13 \\\(\\\);" 1 "gimple" { target { ! { i?86-*-* x86_64-*-* } } } } } */
+  #pragma omp teams distribute parallel for
+  for (i = 0; i < 1; i++)
+    f18 ();	/* { dg-final { scan-tree-dump-times "f15 \\\(\\\);" 1 "gimple" } } */
+  #pragma omp parallel for
+  for (i = 0; i < 1; i++)
+    f23 ();	/* { dg-final { scan-tree-dump-times "f22 \\\(\\\);" 1 "gimple" } } */
+  f27 ();	/* { dg-final { scan-tree-dump-times "f25 \\\(\\\);" 1 "gimple" { target { { i?86-*-* x86_64-*-* } && lp64 } } } } */
+		/* { dg-final { scan-tree-dump-times "f24 \\\(\\\);" 1 "gimple" { target { { i?86-*-* x86_64-*-* } && { ! lp64 } } } } } */
+		/* { dg-final { scan-tree-dump-times "f24 \\\(\\\);" 1 "gimple" { target { ! { nvptx*-*-* amdgcn*-*-* i?86-*-* x86_64-*-* } } } } } */
+		/* { dg-final { scan-tree-dump-times "f27 \\\(\\\);" 1 "gimple" { target { nvptx*-*-* amdgcn*-*-* } } } } */
+}
+
+#if defined(__i386__) || defined(__x86_64__)
+__attribute__((target ("no-avx512bw,avx512f,avx512vl")))
+#endif
+void
+test2 (void)
+{
+  f06 ();	/* { dg-final { scan-tree-dump-times "f04 \\\(\\\);" 1 "gimple" { target i?86-*-* x86_64-*-* } } } */
+		/* { dg-final { scan-tree-dump-times "f06 \\\(\\\);" 1 "gimple" { target { ! { i?86-*-* x86_64-*-* } } } } } */
+}