diff --git a/gcc/testsuite/c-c++-common/gomp/metadirective-1.c b/gcc/testsuite/c-c++-common/gomp/metadirective-1.c
new file mode 100644
index 00000000000..37b56237531
--- /dev/null
+++ b/gcc/testsuite/c-c++-common/gomp/metadirective-1.c
@@ -0,0 +1,52 @@
+/* { dg-do compile } */
+
+#define N 100
+
+void f (int a[], int b[], int c[])
+{
+  int i;
+
+  #pragma omp metadirective \
+      default (teams loop) \
+      default (parallel loop) /* { dg-error "too many 'otherwise' or 'default' clauses in 'metadirective'" } */
+    for (i = 0; i < N; i++) c[i] = a[i] * b[i];
+
+  #pragma omp metadirective \
+      otherwise (teams loop) \
+      default (parallel loop) /* { dg-error "too many 'otherwise' or 'default' clauses in 'metadirective'" } */
+    for (i = 0; i < N; i++) c[i] = a[i] * b[i];
+
+  #pragma omp metadirective \
+      otherwise (teams loop) \
+      otherwise (parallel loop) /* { dg-error "too many 'otherwise' or 'default' clauses in 'metadirective'" } */
+    for (i = 0; i < N; i++) c[i] = a[i] * b[i];
+
+  #pragma omp metadirective \
+      default (bad_directive) /* { dg-error "unknown directive name before '\\)' token" } */
+    for (i = 0; i < N; i++) c[i] = a[i] * b[i];
+
+  #pragma omp metadirective \
+      default (teams loop) \
+      where (device={arch("nvptx")}: parallel loop) /* { dg-error "'where' is not valid for 'metadirective'" } */
+    for (i = 0; i < N; i++) c[i] = a[i] * b[i];
+
+  #pragma omp metadirective \
+      default (teams loop) \
+      when (device={arch("nvptx")} parallel loop) /* { dg-error "expected ':' before 'parallel'" } */
+    for (i = 0; i < N; i++) c[i] = a[i] * b[i];
+
+  #pragma omp metadirective \
+	default (metadirective default (flush))	/* { dg-error "metadirectives cannot be used as variants of a 'metadirective' before 'default'" } */
+    for (i = 0; i < N; i++) c[i] = a[i] * b[i];
+
+  /* Test improperly nested metadirectives - even though the second
+     metadirective resolves to 'omp nothing', that is not the same as there
+     being literally nothing there.  */
+  #pragma omp metadirective \
+      when (implementation={vendor("gnu")}: parallel for)
+    #pragma omp metadirective \
+	when (implementation={vendor("cray")}: parallel for)
+	/* { dg-error "for statement expected before '#pragma'" "" { target c } .-2 } */
+	/* { dg-error "'#pragma' is not allowed here" "" { target c++ } .-3 } */
+      for (i = 0; i < N; i++) c[i] = a[i] * b[i];
+}
diff --git a/gcc/testsuite/c-c++-common/gomp/metadirective-2.c b/gcc/testsuite/c-c++-common/gomp/metadirective-2.c
new file mode 100644
index 00000000000..ea6904c9c12
--- /dev/null
+++ b/gcc/testsuite/c-c++-common/gomp/metadirective-2.c
@@ -0,0 +1,74 @@
+/* { dg-do compile } */
+
+#define N 100
+
+int main (void)
+{
+  int x = 0;
+  int y = 0;
+
+  /* Test implicit default (nothing).  */
+  #pragma omp metadirective \
+      when (device={arch("nvptx")}: barrier)
+    x = 1;
+
+  /* Test with multiple standalone directives.  */
+  #pragma omp metadirective \
+      when (device={arch("nvptx")}: barrier) \
+      default (flush)
+    x = 1;
+
+  /* Test combining a standalone directive with one that takes a statement
+     body.  */
+  #pragma omp metadirective \
+      when (device={arch("nvptx")}: parallel) \
+      default (barrier)
+    x = 1;
+
+  /* Test combining a standalone directive with one that takes a for loop.  */
+  #pragma omp metadirective \
+      when (device={arch("nvptx")}: parallel for) \
+      default (barrier)
+    for (int i = 0; i < N; i++)
+      x += i;
+
+  /* Test combining a directive that takes a for loop with one that takes
+     a regular statement body.  */
+  #pragma omp metadirective \
+      when (device={arch("nvptx")}: parallel for) \
+      default (parallel)
+    for (int i = 0; i < N; i++)
+      x += i;
+
+  /* Test labels inside statement body.  */
+  #pragma omp metadirective \
+    when (device={arch("nvptx")}: teams num_teams(512)) \
+    when (device={arch("gcn")}: teams num_teams(256)) \
+    default (teams num_teams(4))
+  {
+    if (x)
+      goto l1;
+    else
+      goto l2;
+  l1: ;
+  l2: ;
+  }
+
+  /* Test local labels inside statement body.  */
+  #pragma omp metadirective \
+    when (device={arch("nvptx")}: teams num_teams(512)) \
+    when (device={arch("gcn")}: teams num_teams(256)) \
+    default (teams num_teams(4))
+  {
+    //__label__ l1, l2;
+
+    if (x)
+      goto l1;
+    else
+      goto l2;
+  l1: ;
+  l2: ;
+  }
+
+  return 0;
+}
diff --git a/gcc/testsuite/c-c++-common/gomp/metadirective-3.c b/gcc/testsuite/c-c++-common/gomp/metadirective-3.c
new file mode 100644
index 00000000000..7a2818dd710
--- /dev/null
+++ b/gcc/testsuite/c-c++-common/gomp/metadirective-3.c
@@ -0,0 +1,31 @@
+/* { dg-do compile } */
+/* { dg-additional-options "-fdump-tree-original" } */
+/* { dg-additional-options "-fdump-tree-gimple" } */
+/* { dg-additional-options "-fdump-tree-optimized" } */
+
+#define N 100
+
+void f (int x[], int y[], int z[])
+{
+  int i;
+
+  #pragma omp target map(to: x, y) map(from: z)
+    #pragma omp metadirective \
+	when (device={arch("nvptx")}: teams loop) \
+	default (parallel loop)
+      for (i = 0; i < N; i++)
+	z[i] = x[i] * y[i];
+}
+
+/* The metadirective should be resolved after Gimplification.  */
+
+/* { dg-final { scan-tree-dump-times "#pragma omp metadirective" 1 "original" } } */
+/* { dg-final { scan-tree-dump-times "when \\(device = .*arch.*nvptx.*\\):" 1 "original" } } */
+/* { dg-final { scan-tree-dump-times "#pragma omp teams" 1 "original" } } */
+/* { dg-final { scan-tree-dump-times "default:" 1 "original" } } */
+/* { dg-final { scan-tree-dump-times "#pragma omp parallel" 1 "original" } } */
+/* { dg-final { scan-tree-dump-times "#pragma omp loop" 2 "original" } } */
+
+/* { dg-final { scan-tree-dump-times "#pragma omp metadirective" 1 "gimple" } } */
+
+/* { dg-final { scan-tree-dump-not "#pragma omp metadirective" "optimized" } } */
diff --git a/gcc/testsuite/c-c++-common/gomp/metadirective-4.c b/gcc/testsuite/c-c++-common/gomp/metadirective-4.c
new file mode 100644
index 00000000000..5fa86561a23
--- /dev/null
+++ b/gcc/testsuite/c-c++-common/gomp/metadirective-4.c
@@ -0,0 +1,40 @@
+/* { dg-do compile } */
+/* { dg-additional-options "-fdump-tree-original" } */
+/* { dg-additional-options "-fdump-tree-gimple" } */
+
+#define N 100
+
+#pragma omp declare target
+void f(double a[], double x) {
+  int i;
+
+  #pragma omp metadirective \
+	when (construct={target}: distribute parallel for) \
+	default (parallel for simd)
+   for (i = 0; i < N; i++)
+     a[i] = x * i;
+}
+#pragma omp end declare target
+
+ int main()
+{
+  double a[N];
+
+  #pragma omp target teams map(from: a[0:N])
+    f (a, 3.14159);
+
+  /* TODO: This does not execute a version of f with the default clause
+     active as might be expected.  */
+  f (a, 2.71828); /* { dg-warning "direct calls to an offloadable function containing metadirectives with a 'construct={target}' selector may produce unexpected results" } */
+
+  return 0;
+ }
+
+ /* The metadirective should be resolved during Gimplification.  */
+
+/* { dg-final { scan-tree-dump-times "#pragma omp metadirective" 1 "original" } } */
+/* { dg-final { scan-tree-dump-times "when \\(construct = .*target.*\\):" 1 "original" } } */
+/* { dg-final { scan-tree-dump-times "default:" 1 "original" } } */
+/* { dg-final { scan-tree-dump-times "#pragma omp parallel" 2 "original" } } */
+
+/* { dg-final { scan-tree-dump-not "#pragma omp metadirective" "gimple" } } */
diff --git a/gcc/testsuite/c-c++-common/gomp/metadirective-5.c b/gcc/testsuite/c-c++-common/gomp/metadirective-5.c
new file mode 100644
index 00000000000..4a9f1aa85a6
--- /dev/null
+++ b/gcc/testsuite/c-c++-common/gomp/metadirective-5.c
@@ -0,0 +1,24 @@
+/* { dg-do compile } */
+/* { dg-additional-options "-fdump-tree-original" } */
+
+#define N 100
+
+void f (int a[], int flag)
+{
+  int i;
+  #pragma omp metadirective \
+	when (user={condition(flag)}: \
+		target teams distribute parallel for map(from: a[0:N])) \
+	default (parallel for)
+  for (i = 0; i < N; i++)
+    a[i] = i;
+}
+
+/* The metadirective should be resolved at parse time.  */
+
+/* { dg-final { scan-tree-dump-not "#pragma omp metadirective" "original" } } */
+/* { dg-final { scan-tree-dump-times "#pragma omp target" 1 "original" } } */
+/* { dg-final { scan-tree-dump-times "#pragma omp teams" 1 "original" } } */
+/* { dg-final { scan-tree-dump-times  "#pragma omp distribute" 1 "original" } } */
+/* { dg-final { scan-tree-dump-times "#pragma omp parallel" 2 "original" } } */
+/* { dg-final { scan-tree-dump-times "#pragma omp for" 2 "original" } } */
diff --git a/gcc/testsuite/c-c++-common/gomp/metadirective-6.c b/gcc/testsuite/c-c++-common/gomp/metadirective-6.c
new file mode 100644
index 00000000000..26c7a008e95
--- /dev/null
+++ b/gcc/testsuite/c-c++-common/gomp/metadirective-6.c
@@ -0,0 +1,31 @@
+/* { dg-do compile } */
+/* { dg-additional-options "-fdump-tree-original" } */
+/* { dg-additional-options "-fdump-tree-gimple" } */
+
+#define N 100
+
+void bar (int a[], int run_parallel, int run_guided)
+{
+  #pragma omp metadirective \
+	when (user={condition(run_parallel)}: parallel)
+  {
+    int i;
+    #pragma omp metadirective \
+	when (construct={parallel}, user={condition(run_guided)}: \
+	      for schedule(guided)) \
+	when (construct={parallel}: for schedule(static))
+      for (i = 0; i < N; i++)
+	a[i] = i;
+   }
+ }
+
+/* The outer metadirective should be resolved at parse time.  */
+/* The inner metadirective should be resolved during Gimplificiation.  */
+
+/* { dg-final { scan-tree-dump-times "#pragma omp metadirective" 2 "original" } } */
+/* { dg-final { scan-tree-dump-times "#pragma omp parallel" 1 "original" } } */
+/* { dg-final { scan-tree-dump-times "#pragma omp for" 4 "original" } } */
+/* { dg-final { scan-tree-dump-times "when \\(construct = .parallel" 4 "original" } } */
+/* { dg-final { scan-tree-dump-times "default:" 2 "original" } } */
+
+/* { dg-final { scan-tree-dump-not "#pragma omp metadirective" "gimple" } } */
diff --git a/gcc/testsuite/c-c++-common/gomp/metadirective-7.c b/gcc/testsuite/c-c++-common/gomp/metadirective-7.c
new file mode 100644
index 00000000000..4e763cd03d4
--- /dev/null
+++ b/gcc/testsuite/c-c++-common/gomp/metadirective-7.c
@@ -0,0 +1,31 @@
+/* { dg-do compile } */
+/* { dg-additional-options "-fdump-tree-gimple" } */
+
+#define N 256
+
+void f (int a[], int num)
+{
+  int i;
+
+  #pragma omp metadirective \
+      when (target_device={device_num(num), kind("gpu"), arch("nvptx")}: \
+	    target parallel for map(tofrom: a[0:N])) \
+      when (target_device={device_num(num), kind("gpu"), \
+			   arch("amdgcn"), isa("gfx906")}: \
+	    target parallel for) \
+      when (target_device={device_num(num), kind("cpu"), arch("x86_64")}: \
+	    parallel for)
+    for (i = 0; i < N; i++)
+      a[i] += i;
+
+  #pragma omp metadirective \
+      when (target_device={kind("gpu"), arch("nvptx")}: \
+	    target parallel for map(tofrom: a[0:N]))
+    for (i = 0; i < N; i++)
+      a[i] += i;
+}
+
+/* { dg-final { scan-tree-dump "__builtin_GOMP_evaluate_target_device \\(num, &\"gpu.x00\"\\\[0\\\], &\"amdgcn.x00\"\\\[0\\\], &\"gfx906.x00\"\\\[0\\\]\\)" "gimple" } } */
+/* { dg-final { scan-tree-dump "__builtin_GOMP_evaluate_target_device \\(num, &\"gpu.x00\"\\\[0\\\], &\"nvptx.x00\"\\\[0\\\], 0B\\)" "gimple" } } */
+/* { dg-final { scan-tree-dump "__builtin_GOMP_evaluate_target_device \\(num, &\"cpu.x00\"\\\[0\\\], &\"x86_64.x00\"\\\[0\\\], 0B\\)" "gimple" } } */
+/* { dg-final { scan-tree-dump "__builtin_GOMP_evaluate_target_device \\(-2, &\"gpu.x00\"\\\[0\\\], &\"nvptx.x00\"\\\[0\\\], 0B\\)" "gimple" } } */
diff --git a/gcc/testsuite/c-c++-common/gomp/metadirective-8.c b/gcc/testsuite/c-c++-common/gomp/metadirective-8.c
new file mode 100644
index 00000000000..c7d9c31ed73
--- /dev/null
+++ b/gcc/testsuite/c-c++-common/gomp/metadirective-8.c
@@ -0,0 +1,16 @@
+/* { dg-do compile } */
+
+#define N 256
+
+void f ()
+{
+  int i;
+  int a[N];
+
+  #pragma omp metadirective \
+      when( device={kind(nohost)}: nothing ) \
+      when( device={arch("nvptx")}: nothing) \
+      default( parallel for)
+    for (i = 0; i < N; i++)
+      a[i] = i;
+}
diff --git a/gcc/testsuite/c-c++-common/gomp/metadirective-construct.c b/gcc/testsuite/c-c++-common/gomp/metadirective-construct.c
new file mode 100644
index 00000000000..a61966aa899
--- /dev/null
+++ b/gcc/testsuite/c-c++-common/gomp/metadirective-construct.c
@@ -0,0 +1,177 @@
+/* { dg-do compile } */
+/* { dg-additional-options "-foffload=disable -fdump-tree-optimized" } */
+
+#include <stdlib.h>
+
+static void
+init (int n, double *a)
+{
+  for (int i = 0; i < n; i++)
+    a[i] = (double) i;
+}
+
+static void
+check (int n, double *a, double s)
+{
+  for (int i = 0; i < n; i++)
+    if (a[i] != (double) i * s)
+      abort ();
+}
+
+typedef void (transform_fn) (int, double *, double);
+
+static void doit (transform_fn *f, int n, double *a, double s)
+{
+  init (n, a);
+  (*f) (n, a, s);
+  check (n, a, s);
+}
+
+/* Check various combinations for enforcing correct ordering of 
+   construct matches.  */
+static void
+f1 (int n, double* a, double s)
+{
+#pragma omp target teams
+#pragma omp parallel  
+#pragma omp metadirective					\
+  when (construct={target}					\
+	: for)							\
+  default (error at(execution) message("f1 match failed"))
+  for (int i = 0; i < n; i++)
+    a[i] = a[i] * s;
+}
+
+static void
+f2 (int n, double* a, double s)
+{
+#pragma omp target teams
+#pragma omp parallel
+#pragma omp metadirective					\
+  when (construct={teams, parallel}				\
+	: for)							\
+  default (error at(execution) message("f2 match failed"))
+  for (int i = 0; i < n; i++)
+    a[i] = a[i] * s;
+}
+
+static void
+f3 (int n, double* a, double s)
+{
+#pragma omp target teams
+#pragma omp parallel
+#pragma omp metadirective					\
+  when (construct={target, teams, parallel}			\
+	: for)							\
+  default (error at(execution) message("f3 match failed"))
+  for (int i = 0; i < n; i++)
+    a[i] = a[i] * s;
+}
+
+static void
+f4 (int n, double* a, double s)
+{
+#pragma omp target teams
+#pragma omp parallel
+#pragma omp metadirective					\
+  when (construct={target, parallel}				\
+	: for)							\
+  default (error at(execution) message("f4 match failed"))
+  for (int i = 0; i < n; i++)
+    a[i] = a[i] * s;
+}
+
+static void
+f5 (int n, double* a, double s)
+{
+#pragma omp target teams
+#pragma omp parallel
+#pragma omp metadirective					\
+  when (construct={target, teams}				\
+	: for)							\
+  default (error at(execution) message("f5 match failed"))
+  for (int i = 0; i < n; i++)
+    a[i] = a[i] * s;
+}
+
+/* Next batch is for things where the construct doesn't match the context.  */
+static void
+f6 (int n, double* a, double s)
+{
+#pragma omp target
+#pragma omp teams
+#pragma omp metadirective					\
+  when (construct={parallel}					\
+	: error at(execution) message("f6 match failed"))	\
+  default (parallel for)
+  for (int i = 0; i < n; i++)
+    a[i] = a[i] * s;
+}
+
+static void
+f7 (int n, double* a, double s)
+{
+#pragma omp target
+#pragma omp teams
+#pragma omp metadirective					\
+  when (construct={target, parallel}				\
+	: error at(execution) message("f7 match failed"))	\
+  default (parallel for)
+  for (int i = 0; i < n; i++)
+    a[i] = a[i] * s;
+}
+
+static void
+f8 (int n, double* a, double s)
+{
+#pragma omp target
+#pragma omp teams
+#pragma omp metadirective					\
+  when (construct={parallel, target}				\
+	: error at(execution) message("f8 match failed"))	\
+  default (parallel for)
+  for (int i = 0; i < n; i++)
+    a[i] = a[i] * s;
+}
+
+/* Next test choosing the best alternative when there are multiple
+   matches.  */
+static void
+f9 (int n, double* a, double s)
+{
+#pragma omp target teams
+#pragma omp parallel
+#pragma omp metadirective					\
+  when (construct={teams, parallel}				\
+	: error at(execution) message("f9 match incorrect 1"))	\
+  when (construct={target, teams, parallel}			\
+	: for)							\
+  when (construct={target, teams}				\
+	: error at(execution) message("f9 match incorrect 2"))	\
+  default (error at(execution) message("f9 match failed"))
+  for (int i = 0; i < n; i++)
+    a[i] = a[i] * s;
+}
+
+/* Note there are no tests for the matching the extended simd clause
+   syntax, which is only useful for "declare variant".  */
+
+#define N 10
+#define S 2.0
+
+int main (void)
+{
+  double a[N];
+  doit (f1, N, a, S);
+  doit (f2, N, a, S);
+  doit (f3, N, a, S);
+  doit (f4, N, a, S);
+  doit (f5, N, a, S);
+  doit (f6, N, a, S);
+  doit (f7, N, a, S);
+  doit (f8, N, a, S);
+  doit (f9, N, a, S);
+}
+
+/* All the error calls should be optimized away.  */
+/* { dg-final { scan-tree-dump-not "__builtin_GOMP_error" "optimized" } } */
diff --git a/gcc/testsuite/c-c++-common/gomp/metadirective-device.c b/gcc/testsuite/c-c++-common/gomp/metadirective-device.c
new file mode 100644
index 00000000000..9261331260c
--- /dev/null
+++ b/gcc/testsuite/c-c++-common/gomp/metadirective-device.c
@@ -0,0 +1,147 @@
+/* { dg-do compile }  */
+/* { dg-additional-options "-foffload=disable -fdump-tree-optimized" } */
+/* { dg-additional-options "-DDEVICE_ARCH=x86_64 -DDEVICE_ISA=sse -msse" { target x86_64-*-* } } */
+
+#include <stdlib.h>
+
+static void
+init (int n, double *a)
+{
+  for (int i = 0; i < n; i++)
+    a[i] = (double) i;
+}
+
+static void
+check (int n, double *a, double s)
+{
+  for (int i = 0; i < n; i++)
+    if (a[i] != (double) i * s)
+      abort ();
+}
+
+typedef void (transform_fn) (int, double *, double);
+
+static void doit (transform_fn *f, int n, double *a, double s)
+{
+  init (n, a);
+  (*f) (n, a, s);
+  check (n, a, s);
+}
+
+/* Check kind=host matches (with offloading disabled).  */
+static void
+f1 (int n, double* a, double s)
+{
+#pragma omp metadirective					\
+  when (device={kind(host)}					\
+	: parallel for)						\
+  default (error at(execution) message("f1 match failed"))
+  for (int i = 0; i < n; i++)
+    a[i] = a[i] * s;
+}
+
+/* Check kind=nohost does not match (with offloading disabled).  */
+static void
+f2 (int n, double* a, double s)
+{
+#pragma omp metadirective					\
+  when (device={kind(nohost)}					\
+	: error at(execution) message("f2 match failed"))	\
+  default (parallel for)
+  for (int i = 0; i < n; i++)
+    a[i] = a[i] * s;
+}
+
+/* Check arch.  Either DEVICE_ARCH is defined by command-line option, 
+   or we know it is not x86_64.  */
+#ifdef DEVICE_ARCH
+static void
+f3 (int n, double* a, double s)
+{
+#pragma omp metadirective					\
+  when (device={arch(DEVICE_ARCH)}				\
+	: parallel for)						\
+  default (error at(execution) message("f3 match failed"))
+  for (int i = 0; i < n; i++)
+    a[i] = a[i] * s;
+}
+#else
+static void
+f3 (int n, double* a, double s)
+{
+#pragma omp metadirective					\
+  when (device={arch("x86_64")}					\
+	: error at(execution) message("f3 match failed"))	\
+  default (parallel for)
+  for (int i = 0; i < n; i++)
+    a[i] = a[i] * s;
+}
+#endif
+
+/* Check both kind and arch together.  */
+#ifdef DEVICE_ARCH
+static void
+f4 (int n, double* a, double s)
+{
+#pragma omp metadirective					\
+  when (device={arch(DEVICE_ARCH), kind(host)}			\
+	: parallel for)						\
+  default (error at(execution) message("f4 match failed"))
+  for (int i = 0; i < n; i++)
+    a[i] = a[i] * s;
+}
+#else
+static void
+f4 (int n, double* a, double s)
+{
+#pragma omp metadirective					\
+  when (device={arch("x86_64"), kind(host)}			\
+	: error at(execution) message("f4 match failed"))	\
+  default (parallel for)
+  for (int i = 0; i < n; i++)
+    a[i] = a[i] * s;
+}
+#endif
+
+/* Check kind, arch, and ISA together.  */
+#if defined(DEVICE_ARCH) && defined(DEVICE_ISA)
+static void
+f5 (int n, double* a, double s)
+{
+#pragma omp metadirective						\
+  when (device={arch(DEVICE_ARCH), kind(host), isa(DEVICE_ISA)}		\
+	: parallel for)							\
+  default (error at(execution) message("f5 match failed"))
+  for (int i = 0; i < n; i++)
+    a[i] = a[i] * s;
+}
+#else
+static void
+f5 (int n, double* a, double s)
+{
+#pragma omp metadirective					\
+  when (device={arch("x86_64"), kind(host), isa("sse")}		\
+	: error at(execution) message("f5 match failed"))	\
+  default (parallel for)
+  for (int i = 0; i < n; i++)
+    a[i] = a[i] * s;
+}
+#endif
+
+#define N 10
+#define S 2.0
+
+int main (void)
+{
+  double a[N];
+  doit (f1, N, a, S);
+  doit (f2, N, a, S);
+  doit (f3, N, a, S);
+  doit (f4, N, a, S);
+  doit (f5, N, a, S);
+}
+
+/* All the metadirectives involving the device selector should be
+   fully resolved and the error calls optimized away.  */
+
+/* { dg-final { scan-tree-dump-not "__builtin_GOMP_error" "optimized" } } */
diff --git a/gcc/testsuite/c-c++-common/gomp/metadirective-no-score.c b/gcc/testsuite/c-c++-common/gomp/metadirective-no-score.c
new file mode 100644
index 00000000000..1f1053eaffa
--- /dev/null
+++ b/gcc/testsuite/c-c++-common/gomp/metadirective-no-score.c
@@ -0,0 +1,95 @@
+/* { dg-do compile { target x86_64-*-* } } */
+/* { dg-additional-options "-foffload=disable" } */
+
+/* This test is expected to fail with compile-time errors:
+   "A trait-score cannot be specified in traits from the construct,
+   device or target_device trait-selector-sets."  */
+
+/* Define this to avoid dependence on libgomp header files.  */
+
+#define omp_initial_device -1
+
+void
+f1 (int n, double *a, double s)
+{
+#pragma omp metadirective		\
+  when (device={kind (score(5) : host)} \
+	: parallel for)
+  /* { dg-error ".score. cannot be specified in traits in the .device. trait-selector-set" "" { target *-*-*} .-2 } */
+  for (int i = 0; i < n; i++)
+    a[i] = a[i] * s;
+}
+
+void
+f2 (int n, double *a, double s)
+{
+#pragma omp metadirective					      \
+  when (device={kind (host), arch (score(6) : x86_64), isa (avx512f)} \
+	: parallel for)
+  /* { dg-error ".score. cannot be specified in traits in the .device. trait-selector-set" "" { target *-*-*} .-2 } */
+  for (int i = 0; i < n; i++)
+    a[i] = a[i] * s;
+}
+
+void
+f3 (int n, double *a, double s)
+{
+#pragma omp metadirective					\
+  when (device={kind (host), arch (score(6) : x86_64),		\
+		  isa (score(7): avx512f)}			\
+	: parallel for)
+  /* { dg-error ".score. cannot be specified in traits in the .device. trait-selector-set" "" { target *-*-*} .-3 } */
+  /* { dg-error ".score. cannot be specified in traits in the .device. trait-selector-set" "" { target *-*-*} .-3 } */
+  for (int i = 0; i < n; i++)
+    a[i] = a[i] * s;
+}
+
+void
+f4 (int n, double *a, double s)
+{
+#pragma omp metadirective						\
+  when (target_device={device_num (score(42) : omp_initial_device),	\
+			 kind (host)}					\
+	: parallel for)
+  /* { dg-error ".score. cannot be specified in traits in the .target_device. trait-selector-set" "" { target *-*-*} .-3 } */
+  for (int i = 0; i < n; i++)
+    a[i] = a[i] * s;
+}
+
+void
+f5 (int n, double *a, double s)
+{
+#pragma omp metadirective				\
+  when (target_device={device_num(omp_initial_device),	\
+			 kind (score(5) : host)}	\
+	: parallel for)
+  /* { dg-error ".score. cannot be specified in traits in the .target_device. trait-selector-set" "" { target *-*-*} .-2 } */
+  for (int i = 0; i < n; i++)
+    a[i] = a[i] * s;
+}
+
+void
+f6 (int n, double *a, double s)
+{
+#pragma omp metadirective					      \
+  when (target_device={device_num(omp_initial_device), kind (host),   \
+			 arch (score(6) : x86_64), isa (avx512f)}     \
+	: parallel for)
+  /* { dg-error ".score. cannot be specified in traits in the .target_device. trait-selector-set" "" { target *-*-*} .-2 } */
+  for (int i = 0; i < n; i++)
+    a[i] = a[i] * s;
+}
+
+void
+f7 (int n, double *a, double s)
+{
+#pragma omp metadirective						\
+  when (target_device={device_num(omp_initial_device), kind (host),	\
+			 arch (score(6) : x86_64),			\
+			 isa (score(7): avx512f)}			\
+	: parallel for)
+  /* { dg-error ".score. cannot be specified in traits in the .target_device. trait-selector-set" "" { target *-*-*} .-3 } */
+  /* { dg-error ".score. cannot be specified in traits in the .target_device. trait-selector-set" "" { target *-*-*} .-3 } */
+  for (int i = 0; i < n; i++)
+    a[i] = a[i] * s;
+}
diff --git a/gcc/testsuite/c-c++-common/gomp/metadirective-target-device.c b/gcc/testsuite/c-c++-common/gomp/metadirective-target-device.c
new file mode 100644
index 00000000000..335db416a4b
--- /dev/null
+++ b/gcc/testsuite/c-c++-common/gomp/metadirective-target-device.c
@@ -0,0 +1,147 @@
+/* { dg-do compile }  */
+/* { dg-additional-options "-foffload=disable -fdump-tree-optimized" } */
+/* { dg-additional-options "-DDEVICE_ARCH=x86_64 -DDEVICE_ISA=mmx -mmmx" { target x86_64-*-* } }  */
+
+#include <stdlib.h>
+
+static void
+init (int n, double *a)
+{
+  for (int i = 0; i < n; i++)
+    a[i] = (double) i;
+}
+
+static void
+check (int n, double *a, double s)
+{
+  for (int i = 0; i < n; i++)
+    if (a[i] != (double) i * s)
+      abort ();
+}
+
+typedef void (transform_fn) (int, double *, double);
+
+static void doit (transform_fn *f, int n, double *a, double s)
+{
+  init (n, a);
+  (*f) (n, a, s);
+  check (n, a, s);
+}
+
+/* Check kind=host matches (with offloading disabled).  */
+static void
+f1 (int n, double* a, double s)
+{
+#pragma omp metadirective					\
+  when (target_device={kind(host)}				\
+	: parallel for)						\
+  default (error at(execution) message("f1 match failed"))
+  for (int i = 0; i < n; i++)
+    a[i] = a[i] * s;
+}
+
+/* Check kind=nohost does not match (with offloading disabled).  */
+static void
+f2 (int n, double* a, double s)
+{
+#pragma omp metadirective					\
+  when (target_device={kind(nohost)}				\
+	: error at(execution) message("f2 match failed"))	\
+  default (parallel for)
+  for (int i = 0; i < n; i++)
+    a[i] = a[i] * s;
+}
+
+/* Check arch.  Either DEVICE_ARCH is defined by command-line option, 
+   or we know it is not x86_64.  */
+#ifdef DEVICE_ARCH
+static void
+f3 (int n, double* a, double s)
+{
+#pragma omp metadirective					\
+  when (target_device={arch(DEVICE_ARCH)}			\
+	: parallel for)						\
+  default (error at(execution) message("f3 match failed"))
+  for (int i = 0; i < n; i++)
+    a[i] = a[i] * s;
+}
+#else
+static void
+f3 (int n, double* a, double s)
+{
+#pragma omp metadirective					\
+  when (target_device={arch("x86_64")}				\
+	: error at(execution) message("f3 match failed"))	\
+  default (parallel for)
+  for (int i = 0; i < n; i++)
+    a[i] = a[i] * s;
+}
+#endif
+
+/* Check both kind and arch together.  */
+#ifdef DEVICE_ARCH
+static void
+f4 (int n, double* a, double s)
+{
+#pragma omp metadirective					\
+  when (target_device={arch(DEVICE_ARCH), kind(host)}		\
+	: parallel for)						\
+  default (error at(execution) message("f4 match failed"))
+  for (int i = 0; i < n; i++)
+    a[i] = a[i] * s;
+}
+#else
+static void
+f4 (int n, double* a, double s)
+{
+#pragma omp metadirective					\
+  when (target_device={arch("x86_64"), kind(host)}		\
+	: error at(execution) message("f4 match failed"))	\
+  default (parallel for)
+  for (int i = 0; i < n; i++)
+    a[i] = a[i] * s;
+}
+#endif
+
+/* Check kind, arch, and ISA together.  */
+#if defined(DEVICE_ARCH) && defined(DEVICE_ISA)
+static void
+f5 (int n, double* a, double s)
+{
+#pragma omp metadirective						\
+  when (target_device={arch(DEVICE_ARCH), kind(host), isa(DEVICE_ISA)}	\
+	: parallel for)							\
+  default (error at(execution) message("f5 match failed"))
+  for (int i = 0; i < n; i++)
+    a[i] = a[i] * s;
+}
+#else
+static void
+f5 (int n, double* a, double s)
+{
+#pragma omp metadirective					\
+  when (target_device={arch("x86_64"), kind(host), isa("mmx")}	\
+	: error at(execution) message("f5 match failed"))	\
+  default (parallel for)
+  for (int i = 0; i < n; i++)
+    a[i] = a[i] * s;
+}
+#endif
+
+#define N 10
+#define S 2.0
+
+int main (void)
+{
+  double a[N];
+  doit (f1, N, a, S);
+  doit (f2, N, a, S);
+  doit (f3, N, a, S);
+  doit (f4, N, a, S);
+  doit (f5, N, a, S);
+}
+
+/* Since the target_device selector is dynamic, none of
+   error checks tests can be optimized away.  */
+
+/* { dg-final { scan-tree-dump-times "__builtin_GOMP_error" 5 "optimized" } } */
diff --git a/libgomp/testsuite/libgomp.c-c++-common/metadirective-1.c b/libgomp/testsuite/libgomp.c-c++-common/metadirective-1.c
new file mode 100644
index 00000000000..0de59cbe3d3
--- /dev/null
+++ b/libgomp/testsuite/libgomp.c-c++-common/metadirective-1.c
@@ -0,0 +1,35 @@
+/* { dg-do run } */
+
+#define N 100
+
+void f (int x[], int y[], int z[])
+{
+  int i;
+
+  #pragma omp target map(to: x[0:N], y[0:N]) map(from: z[0:N])
+    #pragma omp metadirective \
+	when (device={arch("nvptx")}: teams loop) \
+	default (parallel loop)
+      for (i = 0; i < N; i++)
+	z[i] = x[i] * y[i];
+}
+
+int main (void)
+{
+  int x[N], y[N], z[N];
+  int i;
+
+  for (i = 0; i < N; i++)
+    {
+      x[i] = i;
+      y[i] = -i;
+    }
+
+  f (x, y, z);
+
+  for (i = 0; i < N; i++)
+    if (z[i] != x[i] * y[i])
+      return 1;
+
+  return 0;
+}
diff --git a/libgomp/testsuite/libgomp.c-c++-common/metadirective-2.c b/libgomp/testsuite/libgomp.c-c++-common/metadirective-2.c
new file mode 100644
index 00000000000..55a6098e525
--- /dev/null
+++ b/libgomp/testsuite/libgomp.c-c++-common/metadirective-2.c
@@ -0,0 +1,41 @@
+/* { dg-do run } */
+
+#include <math.h>
+
+#define N 100
+#define EPSILON 0.001
+
+#pragma omp declare target
+void f(double a[], double x) {
+  int i;
+
+  #pragma omp metadirective \
+	when (construct={target}: distribute parallel for) \
+	default (parallel for simd)
+   for (i = 0; i < N; i++)
+     a[i] = x * i;
+}
+#pragma omp end declare target
+
+ int main()
+{
+  double a[N];
+  int i;
+
+  #pragma omp target teams map(from: a[0:N])
+    f (a, M_PI);
+
+  for (i = 0; i < N; i++)
+    if (fabs (a[i] - (M_PI * i)) > EPSILON)
+      return 1;
+
+  /* TODO: This does not execute a version of f with the default clause
+     active as might be expected.  */
+  f (a, M_E); /* { dg-warning "direct calls to an offloadable function containing metadirectives with a 'construct={target}' selector may produce unexpected results" } */
+
+  for (i = 0; i < N; i++)
+    if (fabs (a[i] - (M_E * i)) > EPSILON)
+      return 1;
+
+  return 0;
+ }
diff --git a/libgomp/testsuite/libgomp.c-c++-common/metadirective-3.c b/libgomp/testsuite/libgomp.c-c++-common/metadirective-3.c
new file mode 100644
index 00000000000..e31daf2cb64
--- /dev/null
+++ b/libgomp/testsuite/libgomp.c-c++-common/metadirective-3.c
@@ -0,0 +1,34 @@
+/* { dg-do run } */
+
+#define N 100
+
+int f (int a[], int flag)
+{
+  int i;
+  int res = 0;
+
+  #pragma omp metadirective \
+	when (user={condition(!flag)}: \
+		target teams distribute parallel for \
+		  map(from: a[0:N]) private(res)) \
+	default (parallel for)
+  for (i = 0; i < N; i++)
+    {
+      a[i] = i;
+      res = 1;
+    }
+
+  return res;
+}
+
+int main (void)
+{
+  int a[N];
+
+  if (f (a, 0))
+    return 1;
+  if (!f (a, 1))
+    return 1;
+
+  return 0;
+}
diff --git a/libgomp/testsuite/libgomp.c-c++-common/metadirective-4.c b/libgomp/testsuite/libgomp.c-c++-common/metadirective-4.c
new file mode 100644
index 00000000000..7fc601eaba6
--- /dev/null
+++ b/libgomp/testsuite/libgomp.c-c++-common/metadirective-4.c
@@ -0,0 +1,52 @@
+/* { dg-do run } */
+
+#include <omp.h>
+
+#define N 100
+
+int f (int a[], int run_parallel, int run_static)
+{
+  int is_parallel = 0;
+  int is_static = 0;
+
+  #pragma omp metadirective \
+	when (user={condition(run_parallel)}: parallel)
+  {
+    int i;
+
+    if (omp_in_parallel ())
+      is_parallel = 1;
+
+    #pragma omp metadirective \
+	when (construct={parallel}, user={condition(!run_static)}: \
+	      for schedule(guided) private(is_static)) \
+	when (construct={parallel}: for schedule(static))
+      for (i = 0; i < N; i++)
+	{
+	  a[i] = i;
+	  is_static = 1;
+	}
+   }
+
+  return (is_parallel << 1) | is_static;
+}
+
+int main (void)
+{
+  int a[N];
+
+  /* is_static is always set if run_parallel is false.  */
+  if (f (a, 0, 0) != 1)
+    return 1;
+
+  if (f (a, 0, 1) != 1)
+    return 1;
+
+  if (f (a, 1, 0) != 2)
+    return 1;
+
+  if (f (a, 1, 1) != 3)
+    return 1;
+
+  return 0;
+}
diff --git a/libgomp/testsuite/libgomp.c-c++-common/metadirective-5.c b/libgomp/testsuite/libgomp.c-c++-common/metadirective-5.c
new file mode 100644
index 00000000000..e8ab7ccb166
--- /dev/null
+++ b/libgomp/testsuite/libgomp.c-c++-common/metadirective-5.c
@@ -0,0 +1,46 @@
+/* { dg-do run } */
+
+#define N 100
+
+#include <stdio.h>
+#include <omp.h>
+
+int f(int a[], int num)
+{
+  int on_device = 0;
+  int i;
+
+  #pragma omp metadirective \
+      when (target_device={device_num(num), kind("gpu")}: \
+	target parallel for map(to: a[0:N]), map(from: on_device)) \
+      default (parallel for private (on_device))
+    for (i = 0; i < N; i++)
+      {
+	a[i] += i;
+	on_device = 1;
+      }
+
+  return on_device;
+}
+
+int main (void)
+{
+  int a[N];
+  int on_device_count = 0;
+  int i;
+
+  for (i = 0; i < N; i++)
+    a[i] = i;
+
+  for (i = 0; i <= omp_get_num_devices (); i++)
+    on_device_count += f (a, i);
+
+  if (on_device_count != omp_get_num_devices ())
+    return 1;
+
+  for (i = 0; i < N; i++)
+    if (a[i] != 2 * i)
+      return 2;
+
+  return 0;
+}
