[V2,4/5] OpenMP: New C/C++ testcases for imperfectly nested loops.

Message ID 20230723221521.3739463-5-sandra@codesourcery.com
State Accepted
Headers
Series OpenMP: support for imperfectly-nested loops |

Checks

Context Check Description
snail/gcc-patch-check success Github commit url

Commit Message

Sandra Loosemore July 23, 2023, 10:15 p.m. UTC
  gcc/testsuite/ChangeLog
	* c-c++-common/gomp/imperfect-attributes.c: New.
	* c-c++-common/gomp/imperfect-badloops.c: New.
	* c-c++-common/gomp/imperfect-blocks.c: New.
	* c-c++-common/gomp/imperfect-extension.c: New.
	* c-c++-common/gomp/imperfect-gotos.c: New.
	* c-c++-common/gomp/imperfect-invalid-scope.c: New.
	* c-c++-common/gomp/imperfect-labels.c: New.
	* c-c++-common/gomp/imperfect-legacy-syntax.c: New.
	* c-c++-common/gomp/imperfect-pragmas.c: New.
	* c-c++-common/gomp/imperfect1.c: New.
	* c-c++-common/gomp/imperfect2.c: New.
	* c-c++-common/gomp/imperfect3.c: New.
	* c-c++-common/gomp/imperfect4.c: New.
	* c-c++-common/gomp/imperfect5.c: New.

libgomp/ChangeLog
	* testsuite/libgomp.c-c++-common/imperfect1.c: New.
	* testsuite/libgomp.c-c++-common/imperfect2.c: New.
	* testsuite/libgomp.c-c++-common/imperfect3.c: New.
	* testsuite/libgomp.c-c++-common/imperfect4.c: New.
	* testsuite/libgomp.c-c++-common/imperfect5.c: New.
	* testsuite/libgomp.c-c++-common/imperfect6.c: New.
	* testsuite/libgomp.c-c++-common/target-imperfect1.c: New.
	* testsuite/libgomp.c-c++-common/target-imperfect2.c: New.
	* testsuite/libgomp.c-c++-common/target-imperfect3.c: New.
	* testsuite/libgomp.c-c++-common/target-imperfect4.c: New.
---
 .../c-c++-common/gomp/imperfect-attributes.c  |  81 ++++++++
 .../c-c++-common/gomp/imperfect-badloops.c    |  50 +++++
 .../c-c++-common/gomp/imperfect-blocks.c      |  75 ++++++++
 .../c-c++-common/gomp/imperfect-extension.c   |  55 ++++++
 .../c-c++-common/gomp/imperfect-gotos.c       | 174 ++++++++++++++++++
 .../gomp/imperfect-invalid-scope.c            |  77 ++++++++
 .../c-c++-common/gomp/imperfect-labels.c      |  85 +++++++++
 .../gomp/imperfect-legacy-syntax.c            |  44 +++++
 .../c-c++-common/gomp/imperfect-pragmas.c     |  85 +++++++++
 gcc/testsuite/c-c++-common/gomp/imperfect1.c  |  38 ++++
 gcc/testsuite/c-c++-common/gomp/imperfect2.c  |  34 ++++
 gcc/testsuite/c-c++-common/gomp/imperfect3.c  |  33 ++++
 gcc/testsuite/c-c++-common/gomp/imperfect4.c  |  33 ++++
 gcc/testsuite/c-c++-common/gomp/imperfect5.c  |  57 ++++++
 .../libgomp.c-c++-common/imperfect1.c         |  76 ++++++++
 .../libgomp.c-c++-common/imperfect2.c         | 114 ++++++++++++
 .../libgomp.c-c++-common/imperfect3.c         | 119 ++++++++++++
 .../libgomp.c-c++-common/imperfect4.c         | 117 ++++++++++++
 .../libgomp.c-c++-common/imperfect5.c         |  49 +++++
 .../libgomp.c-c++-common/imperfect6.c         | 115 ++++++++++++
 .../libgomp.c-c++-common/target-imperfect1.c  |  81 ++++++++
 .../libgomp.c-c++-common/target-imperfect2.c  | 122 ++++++++++++
 .../libgomp.c-c++-common/target-imperfect3.c  | 125 +++++++++++++
 .../libgomp.c-c++-common/target-imperfect4.c  | 122 ++++++++++++
 24 files changed, 1961 insertions(+)
 create mode 100644 gcc/testsuite/c-c++-common/gomp/imperfect-attributes.c
 create mode 100644 gcc/testsuite/c-c++-common/gomp/imperfect-badloops.c
 create mode 100644 gcc/testsuite/c-c++-common/gomp/imperfect-blocks.c
 create mode 100644 gcc/testsuite/c-c++-common/gomp/imperfect-extension.c
 create mode 100644 gcc/testsuite/c-c++-common/gomp/imperfect-gotos.c
 create mode 100644 gcc/testsuite/c-c++-common/gomp/imperfect-invalid-scope.c
 create mode 100644 gcc/testsuite/c-c++-common/gomp/imperfect-labels.c
 create mode 100644 gcc/testsuite/c-c++-common/gomp/imperfect-legacy-syntax.c
 create mode 100644 gcc/testsuite/c-c++-common/gomp/imperfect-pragmas.c
 create mode 100644 gcc/testsuite/c-c++-common/gomp/imperfect1.c
 create mode 100644 gcc/testsuite/c-c++-common/gomp/imperfect2.c
 create mode 100644 gcc/testsuite/c-c++-common/gomp/imperfect3.c
 create mode 100644 gcc/testsuite/c-c++-common/gomp/imperfect4.c
 create mode 100644 gcc/testsuite/c-c++-common/gomp/imperfect5.c
 create mode 100644 libgomp/testsuite/libgomp.c-c++-common/imperfect1.c
 create mode 100644 libgomp/testsuite/libgomp.c-c++-common/imperfect2.c
 create mode 100644 libgomp/testsuite/libgomp.c-c++-common/imperfect3.c
 create mode 100644 libgomp/testsuite/libgomp.c-c++-common/imperfect4.c
 create mode 100644 libgomp/testsuite/libgomp.c-c++-common/imperfect5.c
 create mode 100644 libgomp/testsuite/libgomp.c-c++-common/imperfect6.c
 create mode 100644 libgomp/testsuite/libgomp.c-c++-common/target-imperfect1.c
 create mode 100644 libgomp/testsuite/libgomp.c-c++-common/target-imperfect2.c
 create mode 100644 libgomp/testsuite/libgomp.c-c++-common/target-imperfect3.c
 create mode 100644 libgomp/testsuite/libgomp.c-c++-common/target-imperfect4.c
  

Comments

Jakub Jelinek Aug. 22, 2023, 1:34 p.m. UTC | #1
On Sun, Jul 23, 2023 at 04:15:20PM -0600, Sandra Loosemore wrote:
> gcc/testsuite/ChangeLog
> 	* c-c++-common/gomp/imperfect-attributes.c: New.
> 	* c-c++-common/gomp/imperfect-badloops.c: New.
> 	* c-c++-common/gomp/imperfect-blocks.c: New.
> 	* c-c++-common/gomp/imperfect-extension.c: New.
> 	* c-c++-common/gomp/imperfect-gotos.c: New.
> 	* c-c++-common/gomp/imperfect-invalid-scope.c: New.
> 	* c-c++-common/gomp/imperfect-labels.c: New.
> 	* c-c++-common/gomp/imperfect-legacy-syntax.c: New.
> 	* c-c++-common/gomp/imperfect-pragmas.c: New.
> 	* c-c++-common/gomp/imperfect1.c: New.
> 	* c-c++-common/gomp/imperfect2.c: New.
> 	* c-c++-common/gomp/imperfect3.c: New.
> 	* c-c++-common/gomp/imperfect4.c: New.
> 	* c-c++-common/gomp/imperfect5.c: New.
> 
> libgomp/ChangeLog
> 	* testsuite/libgomp.c-c++-common/imperfect1.c: New.
> 	* testsuite/libgomp.c-c++-common/imperfect2.c: New.
> 	* testsuite/libgomp.c-c++-common/imperfect3.c: New.
> 	* testsuite/libgomp.c-c++-common/imperfect4.c: New.
> 	* testsuite/libgomp.c-c++-common/imperfect5.c: New.
> 	* testsuite/libgomp.c-c++-common/imperfect6.c: New.
> 	* testsuite/libgomp.c-c++-common/target-imperfect1.c: New.
> 	* testsuite/libgomp.c-c++-common/target-imperfect2.c: New.
> 	* testsuite/libgomp.c-c++-common/target-imperfect3.c: New.
> 	* testsuite/libgomp.c-c++-common/target-imperfect4.c: New.

As I wrote in reply to the cover letter, I'd prefer the
ordered(2)/ordered(3) nests to have #pragma omp ordered doacross(source:)
and #pragma omp ordered doacross(sink: ...) directives and
use the libgomp scan-1.c as basis for the scan tests.
Otherwise LGTM.

	Jakub
  

Patch

diff --git a/gcc/testsuite/c-c++-common/gomp/imperfect-attributes.c b/gcc/testsuite/c-c++-common/gomp/imperfect-attributes.c
new file mode 100644
index 00000000000..776295ce22a
--- /dev/null
+++ b/gcc/testsuite/c-c++-common/gomp/imperfect-attributes.c
@@ -0,0 +1,81 @@ 
+/* { dg-do compile { target { c || c++11 } } } */
+
+/* Check that a nested FOR loop with standard c/c++ attributes on it 
+   is treated as intervening code, since it doesn't match the grammar
+   for canonical loop nest form.  */
+
+extern void do_something (void);
+
+void imperfect1 (int x, int y)
+{
+#pragma omp for collapse (2)
+  for (int i = 0; i < x; i++)  /* { dg-error "not enough nested loops" } */
+    {
+      [[]] for (int j = 0; j < y; j++)  /* { dg-error "loop not permitted in intervening code" } */
+	do_something ();
+    }
+}
+
+void perfect1 (int x, int y)
+{
+#pragma omp for ordered (2)
+  for (int i = 0; i < x; i++)  /* { dg-error "not enough nested loops" } */
+    /* { dg-error "inner loops must be perfectly nested" "" { target *-*-*} .-1 } */
+    {
+      [[]] for (int j = 0; j < y; j++)  /* { dg-error "loop not permitted in intervening code" } */
+	do_something ();
+    }
+}
+
+/* Similar, but put the attributes on a block wrapping the nested loop
+   instead.  */
+
+void imperfect2 (int x, int y)
+{
+#pragma omp for collapse (2)
+  for (int i = 0; i < x; i++)  /* { dg-error "not enough nested loops" } */
+    {
+    [[]]
+      {
+	for (int j = 0; j < y; j++)  /* { dg-error "loop not permitted in intervening code" } */
+	  do_something ();
+      }
+    }
+}
+
+void perfect2 (int x, int y)
+{
+#pragma omp for ordered (2)
+  for (int i = 0; i < x; i++)  /* { dg-error "not enough nested loops" } */
+    /* { dg-error "inner loops must be perfectly nested" "" { target *-*-*} .-1 } */
+    {
+    [[]]
+      {
+	for (int j = 0; j < y; j++)  /* { dg-error "loop not permitted in intervening code" } */
+	  do_something ();
+      }
+    }
+}
+
+/* Make sure attributes are accepted in the innermost loop body, which has
+   no intervening code restrictions.  */
+
+void imperfect3 (int x, int y)
+{
+#pragma omp for collapse (2)
+  for (int i = 0; i < x; i++)
+    for (int j = 0; j < y; j++)
+      {
+	[[]] do_something ();
+      }
+}
+
+void perfect3 (int x, int y)
+{
+#pragma omp for ordered (2)
+  for (int i = 0; i < x; i++)
+      for (int j = 0; j < y; j++)
+	{
+	  [[]] do_something ();
+	}
+}
diff --git a/gcc/testsuite/c-c++-common/gomp/imperfect-badloops.c b/gcc/testsuite/c-c++-common/gomp/imperfect-badloops.c
new file mode 100644
index 00000000000..dfd40b6cb2d
--- /dev/null
+++ b/gcc/testsuite/c-c++-common/gomp/imperfect-badloops.c
@@ -0,0 +1,50 @@ 
+/* { dg-do compile } */
+
+/* This test case is expected to fail due to errors.  */
+
+int f1 (int depth, int iter);
+int f2 (int depth, int iter);
+void do_something (void);
+
+void s1 (int a1, int a2, int a3)
+{
+  int i, j, k;
+
+#pragma omp for collapse(3)
+  for (i = 0; i < a1; i++)
+    {
+      f1 (0, i);
+      if (a1 < a2)
+	{
+	  int z = 0;
+	  while (z < i)	/* { dg-error "loop not permitted in intervening code " } */
+	    {
+	      do_something ();
+	      z++;
+	    }
+	  do		/* { dg-error "loop not permitted in intervening code " } */
+	    {
+	      do_something ();
+	      z--;
+	    } while (z >= 0);
+	}
+      for (j = 0; j < a2; j++)
+	{
+	  for (k = 0; k < a3; k++)
+	    {
+	      f1 (2, k);
+	      f2 (2, k);
+	    }
+	  f2 (1, j);
+	}
+      if (a1 < a3)
+	{
+	  int z;
+	  for (z = 0; z < i; z++)	/* { dg-error "loop not permitted in intervening code " } */
+	    {
+	      do_something ();
+	    }
+	}
+      f2 (0, i);
+    }
+}
diff --git a/gcc/testsuite/c-c++-common/gomp/imperfect-blocks.c b/gcc/testsuite/c-c++-common/gomp/imperfect-blocks.c
new file mode 100644
index 00000000000..0fea58faf78
--- /dev/null
+++ b/gcc/testsuite/c-c++-common/gomp/imperfect-blocks.c
@@ -0,0 +1,75 @@ 
+/* { dg-do compile } */
+
+/* Check that compound statements in intervening code are correctly
+   handled.  */
+
+extern void do_something (void);
+
+void imperfect1 (int x, int y)
+{
+#pragma omp for collapse (2)
+  for (int i = 0; i < x; i++)
+    {
+      {}
+      for (int j = 0; j < y; j++)
+	do_something ();
+    }
+}
+
+void perfect1 (int x, int y)
+{
+#pragma omp for ordered (2)
+  for (int i = 0; i < x; i++)  /* { dg-error "inner loops must be perfectly nested" } */
+    {
+      {}
+      for (int j = 0; j < y; j++)
+	do_something ();
+    }
+}
+
+void imperfect2 (int x, int y)
+{
+#pragma omp for collapse (2)
+  for (int i = 0; i < x; i++)
+    {
+      for (int j = 0; j < y; j++)
+	do_something ();
+      {}
+    }
+}
+
+void perfect2 (int x, int y)
+{
+#pragma omp for ordered (2)
+  for (int i = 0; i < x; i++)  /* { dg-error "inner loops must be perfectly nested" } */
+    {
+      for (int j = 0; j < y; j++)
+	do_something ();
+      {}
+    }
+}
+
+void imperfect3 (int x, int y)
+{
+#pragma omp for collapse (2)
+  for (int i = 0; i < x; i++)
+    {
+      { do_something (); }
+      for (int j = 0; j < y; j++)
+	do_something ();
+      { do_something (); }
+    }
+}
+
+void perfect3 (int x, int y)
+{
+#pragma omp for ordered (2)
+  for (int i = 0; i < x; i++)  /* { dg-error "inner loops must be perfectly nested" } */
+    {
+      { do_something (); }
+      for (int j = 0; j < y; j++)
+	do_something ();
+      { do_something (); }
+    }
+}
+
diff --git a/gcc/testsuite/c-c++-common/gomp/imperfect-extension.c b/gcc/testsuite/c-c++-common/gomp/imperfect-extension.c
new file mode 100644
index 00000000000..a8a8f9ebe1d
--- /dev/null
+++ b/gcc/testsuite/c-c++-common/gomp/imperfect-extension.c
@@ -0,0 +1,55 @@ 
+/* { dg-do compile } */
+
+/* Check that __extension__ introduces intervening code.  */
+
+extern void do_something (void);
+
+void imperfect1 (int x, int y)
+{
+#pragma omp for collapse (2)
+  for (int i = 0; i < x; i++)  /* { dg-error "not enough nested loops" } */
+    {
+      __extension__ ({
+	  for (int j = 0; j < y; j++)  /* { dg-error "loop not permitted in intervening code" } */
+	    do_something ();
+	});
+    }
+}
+
+void perfect1 (int x, int y)
+{
+#pragma omp for ordered (2)
+  for (int i = 0; i < x; i++)  /* { dg-error "not enough nested loops" } */
+    /* { dg-error "inner loops must be perfectly nested" "" { target *-*-*} .-1 } */
+    {
+      __extension__ ({
+	  for (int j = 0; j < y; j++)  /* { dg-error "loop not permitted in intervening code" } */
+	    do_something ();
+	});
+    }
+}
+
+/* Check that we don't barf on __extension__ in the inner loop body.  */
+void imperfect2 (int x, int y)
+{
+#pragma omp for collapse (2)
+  for (int i = 0; i < x; i++)
+    for (int j = 0; j < y; j++)
+      {
+	__extension__ ({
+	    do_something ();
+	  });
+      }
+}
+
+void perfect2 (int x, int y)
+{
+#pragma omp for ordered (2)
+  for (int i = 0; i < x; i++)
+    for (int j = 0; j < y; j++)
+      {
+	__extension__ ({
+	    do_something ();
+	  });
+      }
+}
diff --git a/gcc/testsuite/c-c++-common/gomp/imperfect-gotos.c b/gcc/testsuite/c-c++-common/gomp/imperfect-gotos.c
new file mode 100644
index 00000000000..897eed275ec
--- /dev/null
+++ b/gcc/testsuite/c-c++-common/gomp/imperfect-gotos.c
@@ -0,0 +1,174 @@ 
+/* { dg-do compile } */
+
+/* This file contains tests that are expected to fail.  */
+
+
+/* These jumps are all OK since they are to/from the same structured block.  */
+
+void f1a (void)
+{
+#pragma omp for collapse(2)
+  for (int i = 0; i < 64; ++i)
+    {
+      goto a; a:;
+      for (int j = 0; j < 64; ++j)
+	{
+	  goto c; c:;
+	}
+      goto b; b:;
+    }
+}
+
+/* Jump around loop body to/from different structured blocks of intervening
+   code.  */
+void f2a (void)
+{
+#pragma omp for collapse(2)
+  for (int i = 0; i < 64; ++i)
+    {
+      goto a; a:;
+      if (i > 16) goto b; /* { dg-error "invalid branch to/from OpenMP structured block" } */
+      for (int j = 0; j < 64; ++j)
+	{
+	  goto c; c:;
+	}
+      goto b; b:;
+    }
+}
+
+/* Jump into loop body from intervening code.  */
+void f3a (void)
+{
+#pragma omp for collapse(2)
+  for (int i = 0; i < 64; ++i)
+    {
+      goto a; a:;
+      if (i > 16) goto c; /* { dg-error "invalid branch to/from OpenMP structured block" } */
+      for (int j = 0; j < 64; ++j)
+	{
+	c:
+	  ;
+	}
+      goto b; b:;
+    }
+}
+
+/* Jump out of loop body to intervening code.  */
+void f4a (void)
+{
+#pragma omp for collapse(2)
+  for (int i = 0; i < 64; ++i)
+    {
+      goto a; a:;
+      for (int j = 0; j < 64; ++j)
+	if (i > 16) goto c; /* { dg-error "invalid branch to/from OpenMP structured block" } */
+      c:
+	;
+      goto b; b:;
+    }
+}
+
+/* The next group of tests use the GNU extension for local labels.  Expected
+   behavior is the same as the above group.  */
+
+/* These jumps are all OK since they are to/from the same structured block.  */
+
+void f1b (void)
+{
+#pragma omp for collapse(2)
+  for (int i = 0; i < 64; ++i)
+    {
+      __label__ a, b, c;
+      goto a; a:;
+      for (int j = 0; j < 64; ++j)
+	{
+	  goto c; c:;
+	}
+      goto b; b:;
+    }
+}
+
+/* Jump around loop body to/from different structured blocks of intervening
+   code.  */
+void f2b (void)
+{
+#pragma omp for collapse(2)
+  for (int i = 0; i < 64; ++i)
+    {
+      __label__ a, b, c;
+      goto a; a:;
+      if (i > 16) goto b; /* { dg-error "invalid branch to/from OpenMP structured block" } */
+      for (int j = 0; j < 64; ++j)
+	{
+	  goto c; c:;
+	}
+      goto b; b:;
+    }
+}
+
+/* Jump into loop body from intervening code.  */
+void f3b (void)
+{
+#pragma omp for collapse(2)
+  for (int i = 0; i < 64; ++i)
+    {
+      __label__ a, b, c;
+      goto a; a:;
+      if (i > 16) goto c; /* { dg-error "invalid branch to/from OpenMP structured block" } */
+      for (int j = 0; j < 64; ++j)
+	{
+	c:
+	  ;
+	}
+      goto b; b:;
+    }
+}
+
+/* Jump out of loop body to intervening code.  */
+void f4b (void)
+{
+#pragma omp for collapse(2)
+  for (int i = 0; i < 64; ++i)
+    {
+      __label__ a, b, c;
+      goto a; a:;
+      for (int j = 0; j < 64; ++j)
+	if (i > 16) goto c; /* { dg-error "invalid branch to/from OpenMP structured block" } */
+      c:
+	;
+      goto b; b:;
+    }
+}
+
+/* Test that the even the valid jumps are rejected when intervening code
+   is not allowed at all.  */
+
+void f1c (void)
+{
+#pragma omp for ordered(2)
+  for (int i = 0; i < 64; ++i)  /* { dg-error "inner loops must be perfectly nested" } */
+    {
+      goto a; a:;
+      for (int j = 0; j < 64; ++j)
+	{
+	  goto c; c:;
+	}
+      goto b; b:;
+    }
+}
+
+void f1d (void)
+{
+#pragma omp for ordered(2)
+  for (int i = 0; i < 64; ++i)  /* { dg-error "inner loops must be perfectly nested" } */
+    {
+      __label__ a, b, c;
+      goto a; a:;
+      for (int j = 0; j < 64; ++j)
+	{
+	  goto c; c:;
+	}
+      goto b; b:;
+    }
+}
+
diff --git a/gcc/testsuite/c-c++-common/gomp/imperfect-invalid-scope.c b/gcc/testsuite/c-c++-common/gomp/imperfect-invalid-scope.c
new file mode 100644
index 00000000000..5c24aae07cc
--- /dev/null
+++ b/gcc/testsuite/c-c++-common/gomp/imperfect-invalid-scope.c
@@ -0,0 +1,77 @@ 
+/* { dg-do compile } */
+
+/* Check that various cases of invalid references to variables bound
+   in an intervening code scope are diagnosed and do not ICE.  This test
+   is expected to produce errors.  */
+
+extern void foo (int, int);
+
+void f1 (void)
+{
+#pragma omp for collapse (2)
+  for (int i = 0; i < 64; i++)
+    {
+      int v = (i + 4) * 2;
+      for (int j = v; j < 64; j++)  /* { dg-error "initializer is bound in intervening code" }  */
+	foo (i, j);
+    }
+}
+
+void f2 (void)
+{
+#pragma omp for collapse (2)
+  for (int i = 0; i < 64; i++)
+    {
+      int v = (i + 4) * 2;
+      for (int j = 0; j < v; j++)  /* { dg-error "end test is bound in intervening code" }  */
+	foo (i, j);
+    }
+}
+
+void f3 (void)
+{
+#pragma omp for collapse (2)
+  for (int i = 0; i < 64; i++)
+    {
+      int v = (i + 4) * 2;
+      for (int j = 0; j < 64; j = j + v)  /* { dg-error "increment expression is bound in intervening code" }  */
+	foo (i, j);
+    }
+}
+
+void f4 (void)
+{
+#pragma omp for collapse (2)
+  for (int i = 0; i < 64; i++)
+    {
+      int v = 8;
+      for (int j = v; j < 64; j++)  /* { dg-error "initializer is bound in intervening code" }  */
+	foo (i, j);
+    }
+}
+
+void f5 (void)
+{
+#pragma omp for collapse (2)
+  for (int i = 0; i < 64; i++)
+    {
+      int j;
+      for (j = 0; j < 64; j++)  /* { dg-error "loop variable is bound in intervening code" }  */
+	foo (i, j);
+    }
+}
+
+void f6 (void)
+{
+#pragma omp for collapse (2)
+  for (int i = 0; i < 64; i++)
+    {
+      int j;
+      {
+	int v = 8;
+	for (j = v; j < 64; j++)    /* { dg-error "loop variable is bound in intervening code" }  */
+	  /* { dg-error "initializer is bound in intervening code" "" { target *-*-* } .-1 } */
+	  foo (i, j);
+      }
+    }
+}
diff --git a/gcc/testsuite/c-c++-common/gomp/imperfect-labels.c b/gcc/testsuite/c-c++-common/gomp/imperfect-labels.c
new file mode 100644
index 00000000000..b7a7a4c8358
--- /dev/null
+++ b/gcc/testsuite/c-c++-common/gomp/imperfect-labels.c
@@ -0,0 +1,85 @@ 
+/* { dg-do compile } */
+
+/* Check that a nested FOR loop with a label on it is treated as
+   intervening code, since it doesn't match the grammar for canonical
+   loop nest form.  */
+
+extern void do_something (void);
+
+void imperfect1 (int x, int y)
+{
+#pragma omp for collapse (2)
+  for (int i = 0; i < x; i++)  /* { dg-error "not enough nested loops" } */
+    {
+    foo:
+      for (int j = 0; j < y; j++)  /* { dg-error "loop not permitted in intervening code" } */
+	do_something ();
+    }
+}
+
+void perfect1 (int x, int y)
+{
+#pragma omp for ordered (2)
+  for (int i = 0; i < x; i++)  /* { dg-error "not enough nested loops" } */
+    /* { dg-error "inner loops must be perfectly nested" "" { target *-*-*} .-1 } */
+    {
+    foo:
+      for (int j = 0; j < y; j++)  /* { dg-error "loop not permitted in intervening code" } */
+	do_something ();
+    }
+}
+
+
+/* Similar, but put the label on a block wrapping the nested loop instead.  */
+
+void imperfect2 (int x, int y)
+{
+#pragma omp for collapse (2)
+  for (int i = 0; i < x; i++)  /* { dg-error "not enough nested loops" } */
+    {
+    foo:
+      {
+	for (int j = 0; j < y; j++)  /* { dg-error "loop not permitted in intervening code" } */
+	  do_something ();
+      }
+    }
+}
+
+void perfect2 (int x, int y)
+{
+#pragma omp for ordered (2)
+  for (int i = 0; i < x; i++)  /* { dg-error "not enough nested loops" } */
+    /* { dg-error "inner loops must be perfectly nested" "" { target *-*-*} .-1 } */
+    {
+    foo:
+      {
+	for (int j = 0; j < y; j++)  /* { dg-error "loop not permitted in intervening code" } */
+	  do_something ();
+      }
+    }
+}
+
+/* Sanity check that labels are allowed in the innermost loop body.  */
+
+void imperfect3 (int x, int y)
+{
+#pragma omp for collapse (2)
+  for (int i = 0; i < x; i++)
+    for (int j = 0; j < y; j++)
+      {
+      foo:
+	do_something ();
+      }
+}
+
+void perfect3 (int x, int y)
+{
+#pragma omp for ordered (2)
+  for (int i = 0; i < x; i++)
+    for (int j = 0; j < y; j++)
+      {
+      foo:
+	do_something ();
+      }
+}
+
diff --git a/gcc/testsuite/c-c++-common/gomp/imperfect-legacy-syntax.c b/gcc/testsuite/c-c++-common/gomp/imperfect-legacy-syntax.c
new file mode 100644
index 00000000000..571e067091b
--- /dev/null
+++ b/gcc/testsuite/c-c++-common/gomp/imperfect-legacy-syntax.c
@@ -0,0 +1,44 @@ 
+/* { dg-do compile } */
+
+/* Braces may enclose a nested FOR even when intervening code is not
+   permitted.  Before GCC implemented OpenMP 5.1 canonical loop syntax
+   and support for intervening code, it used to ignore empty statements
+   instead of treating them as intervening code; as an extension, those
+   are still accepted without complaint even in constructs where intervening
+   code is not supposed to be valid.  */
+
+void s1 (int a1, int a2, int a3)
+{
+  int i, j, k;
+
+#pragma omp for ordered(3)
+  for (i = 0; i < a1; i++)
+    {
+      for (j = 0; j < a2; j++)
+	{
+	  for (k = 0; k < a3; k++)
+	    {
+	    }
+	}
+    }
+}
+
+void s2 (int a1, int a2, int a3)
+{
+  int i, j, k;
+
+#pragma omp for ordered(3)
+  for (i = 0; i < a1; i++)
+    {
+      ;
+      for (j = 0; j < a2; j++)
+	{
+	  ;
+	  for (k = 0; k < a3; k++)
+	    {
+	    }
+	  ;
+	}
+      ;
+    }
+}
diff --git a/gcc/testsuite/c-c++-common/gomp/imperfect-pragmas.c b/gcc/testsuite/c-c++-common/gomp/imperfect-pragmas.c
new file mode 100644
index 00000000000..a9f55224158
--- /dev/null
+++ b/gcc/testsuite/c-c++-common/gomp/imperfect-pragmas.c
@@ -0,0 +1,85 @@ 
+/* { dg-do compile } */
+
+/* Check that non-statement pragmas are accepted in a canonical loop nest
+   even when perfect nesting is required.  */
+
+extern void do_something (void);
+
+void imperfect1 (int x, int y)
+{
+#pragma omp for collapse (2)
+  for (int i = 0; i < x; i++)
+    {
+#pragma GCC diagnostic push
+      for (int j = 0; j < y; j++)
+	do_something ();
+#pragma GCC diagnostic pop
+    }
+}
+
+void perfect1 (int x, int y)
+{
+#pragma omp for ordered (2)
+  for (int i = 0; i < x; i++)
+    {
+#pragma GCC diagnostic push
+      for (int j = 0; j < y; j++)
+	do_something ();
+#pragma GCC diagnostic pop
+    }
+}
+
+
+/* "GCC unroll" is a statement pragma that consumes the following loop as
+   a substatement.  Thus, the inner loop should be treated as intervening
+   code rather than part of the loop nest.  */
+
+void imperfect2 (int x, int y)
+{
+#pragma omp for collapse (2)
+  for (int i = 0; i < x; i++)  /* { dg-error "not enough nested loops" } */
+    {
+#pragma GCC unroll 4
+      for (int j = 0; j < y; j++)  /* { dg-error "loop not permitted in intervening code" } */
+	do_something ();
+    }
+}
+
+void perfect2 (int x, int y)
+{
+#pragma omp for ordered (2)
+  for (int i = 0; i < x; i++)  /* { dg-error "not enough nested loops" } */
+    /* { dg-error "inner loops must be perfectly nested" "" { target *-*-*} .-1 } */
+    {
+#pragma GCC unroll 4
+      for (int j = 0; j < y; j++)  /* { dg-error "loop not permitted in intervening code" } */
+	do_something ();
+    }
+}
+
+
+/* Check that statement pragmas are accepted in the innermost loop body.  */
+
+void imperfect3 (int x, int y)
+{
+#pragma omp for collapse (2)
+  for (int i = 0; i < x; i++)
+    for (int j = 0; j < y; j++)
+      {
+#pragma GCC unroll 4
+	for (int k = 0; k < 4; k++)
+	  do_something ();
+      }
+}
+
+void perfect3 (int x, int y)
+{
+#pragma omp for ordered (2)
+  for (int i = 0; i < x; i++)
+    for (int j = 0; j < y; j++)
+      {
+#pragma GCC unroll 4
+	for (int k = 0; k < 4; k++)
+	  do_something ();
+      }
+}
diff --git a/gcc/testsuite/c-c++-common/gomp/imperfect1.c b/gcc/testsuite/c-c++-common/gomp/imperfect1.c
new file mode 100644
index 00000000000..705626ad169
--- /dev/null
+++ b/gcc/testsuite/c-c++-common/gomp/imperfect1.c
@@ -0,0 +1,38 @@ 
+/* { dg-do compile } */
+
+/* This test case is expected to fail due to errors.  */
+
+int f1 (int depth, int iter);
+int f2 (int depth, int iter);
+
+void s1 (int a1, int a2, int a3)
+{
+  int i, j, k;
+
+#pragma omp for collapse(3)
+  for (i = 0; i < a1; i++)
+    {
+      f1 (0, i);
+      for (j = 0; j < a2; j++)
+	{
+#pragma omp barrier	/* { dg-error "intervening code must not contain OpenMP directives" } */
+	  f1 (1, j);
+	  if (i == 2)
+	    continue;	/* { dg-error "invalid exit" } */
+	  else
+	    break;	/* { dg-error "invalid exit" } */
+	  for (k = 0; k < a3; k++)
+	    {
+	      f1 (2, k);
+	      f2 (2, k);
+	    }
+	  f2 (1, j);
+	}
+      for (k = 0; k < a3; k++)	/* { dg-error "loop not permitted in intervening code " } */
+	{
+	  f1 (2, k);
+	  f2 (2, k);
+	}
+      f2 (0, i);
+    }
+}
diff --git a/gcc/testsuite/c-c++-common/gomp/imperfect2.c b/gcc/testsuite/c-c++-common/gomp/imperfect2.c
new file mode 100644
index 00000000000..dff17dd3ca5
--- /dev/null
+++ b/gcc/testsuite/c-c++-common/gomp/imperfect2.c
@@ -0,0 +1,34 @@ 
+/* { dg-do compile } */
+
+/* This test case is expected to fail due to errors.  */
+
+/* These functions that are part of the OpenMP runtime API would ordinarily
+   be declared in omp.h, but we don't have that here.  */
+extern int omp_get_num_threads(void);
+extern int omp_get_max_threads(void);
+
+int f1 (int depth, int iter);
+int f2 (int depth, int iter);
+
+void s1 (int a1, int a2, int a3)
+{
+  int i, j, k;
+#pragma omp for collapse(3)
+  for (i = 0; i < a1; i++)
+    {
+      f1 (0, i);
+      for (j = 0; j < omp_get_num_threads (); j++)  /* This is OK */
+	{
+	  f1 (1, omp_get_num_threads ());  /* { dg-error "not permitted in intervening code" } */
+	  for (k = omp_get_num_threads (); k < a3; k++)  /* This is OK */
+	    {
+	      f1 (2, omp_get_num_threads ());
+	      f2 (2, omp_get_max_threads ());
+	    }
+	  f2 (1, omp_get_max_threads ());  /* { dg-error "not permitted in intervening code" } */
+	}
+      f2 (0, i);
+    }
+}
+
+
diff --git a/gcc/testsuite/c-c++-common/gomp/imperfect3.c b/gcc/testsuite/c-c++-common/gomp/imperfect3.c
new file mode 100644
index 00000000000..ad727ed3170
--- /dev/null
+++ b/gcc/testsuite/c-c++-common/gomp/imperfect3.c
@@ -0,0 +1,33 @@ 
+/* { dg-do compile } */
+
+/* This test case is expected to fail due to errors.  */
+
+/* Test that the imperfectly-nested loops with the ordered clause gives
+   an error, and that there is only one error (and not one on every
+   intervening statement).  */
+
+int f1 (int depth, int iter);
+int f2 (int depth, int iter);
+
+void s1 (int a1, int a2, int a3)
+{
+  int i, j, k;
+
+#pragma omp for ordered(3)
+  for (i = 0; i < a1; i++)  /* { dg-error "inner loops must be perfectly nested" } */
+    {
+      f1 (0, i);
+      for (j = 0; j < a2; j++)
+	{
+	  f1 (1, j);
+	  for (k = 0; k < a3; k++)
+	    {
+	      f1 (2, k);
+	      f2 (2, k);
+	    }
+	  f2 (1, j);
+	}
+      f2 (0, i);
+    }
+}
+
diff --git a/gcc/testsuite/c-c++-common/gomp/imperfect4.c b/gcc/testsuite/c-c++-common/gomp/imperfect4.c
new file mode 100644
index 00000000000..1a0c07cd48e
--- /dev/null
+++ b/gcc/testsuite/c-c++-common/gomp/imperfect4.c
@@ -0,0 +1,33 @@ 
+/* { dg-do compile } */
+
+/* This test case is expected to fail due to errors.  */
+
+int f1 (int depth, int iter);
+int f2 (int depth, int iter);
+
+void s1 (int a1, int a2, int a3)
+{
+  int i, j, k;
+
+#pragma omp for collapse(4)
+  for (i = 0; i < a1; i++)	/* { dg-error "not enough nested loops" } */
+    {
+      f1 (0, i);
+      for (j = 0; j < a2; j++)
+	{
+	  f1 (1, j);
+	  for (k = 0; k < a3; k++)
+	    {
+	      /* According to the grammar, this is intervening code; we
+		 don't know that we are also missing a nested for loop
+		 until we have parsed this whole compound expression.  */
+#pragma omp barrier	/* { dg-error "intervening code must not contain OpenMP directives" } */
+	      f1 (2, k);
+	      f2 (2, k);
+	    }
+	  f2 (1, j);
+	}
+      f2 (0, i);
+    }
+}
+
diff --git a/gcc/testsuite/c-c++-common/gomp/imperfect5.c b/gcc/testsuite/c-c++-common/gomp/imperfect5.c
new file mode 100644
index 00000000000..585d89ff789
--- /dev/null
+++ b/gcc/testsuite/c-c++-common/gomp/imperfect5.c
@@ -0,0 +1,57 @@ 
+/* { dg-do compile } */
+
+/* This test case is expected to fail due to errors.  */
+
+int f1 (int depth, int iter);
+int f2 (int depth, int iter);
+int ijk (int x, int y, int z);
+void f3 (int sum);
+
+/* This function isn't particularly meaningful, but it should compile without
+   error.  */
+int s1 (int a1, int a2, int a3)
+{
+  int i, j, k;
+  int r = 0;
+
+#pragma omp simd collapse(3) reduction (inscan, +:r)
+  for (i = 0; i < a1; i++)
+    {
+      for (j = 0; j < a2; j++)
+	{
+	  for (k = 0; k < a3; k++)
+	    {
+	      r = r + ijk (i, j, k);
+#pragma omp scan exclusive (r)
+	      f3 (r);
+	    }
+	}
+    }
+  return r;
+}
+
+/* Adding intervening code should trigger an error.  */
+int s2 (int a1, int a2, int a3)
+{
+  int i, j, k;
+  int r = 0;
+
+#pragma omp simd collapse(3) reduction (inscan, +:r)
+  for (i = 0; i < a1; i++)  /* { dg-error "inner loops must be perfectly nested" } */
+    {
+      f1 (0, i);
+      for (j = 0; j < a2; j++)
+	{
+	  f1 (1, j);
+	  for (k = 0; k < a3; k++)
+	    {
+	      r = r + ijk (i, j, k);
+#pragma omp scan exclusive (r)
+	      f3 (r);
+	    }
+	  f2 (1, j);
+	}
+      f2 (0, i);
+    }
+  return r;
+}
diff --git a/libgomp/testsuite/libgomp.c-c++-common/imperfect1.c b/libgomp/testsuite/libgomp.c-c++-common/imperfect1.c
new file mode 100644
index 00000000000..cafdcaf25b0
--- /dev/null
+++ b/libgomp/testsuite/libgomp.c-c++-common/imperfect1.c
@@ -0,0 +1,76 @@ 
+/* { dg-do run } */
+
+static int f1count[3], f2count[3];
+
+#ifndef __cplusplus
+extern void abort (void);
+#else
+extern "C" void abort (void);
+#endif
+
+int f1 (int depth, int iter)
+{
+  f1count[depth]++;
+  return iter;
+}
+
+int f2 (int depth, int iter)
+{
+  f2count[depth]++;
+  return iter;
+}
+
+void s1 (int a1, int a2, int a3)
+{
+  int i, j, k;
+
+#pragma omp for collapse(3)
+  for (i = 0; i < a1; i++)
+    {
+      f1 (0, i);
+      for (j = 0; j < a2; j++)
+	{
+	  f1 (1, j);
+	  for (k = 0; k < a3; k++)
+	    {
+	      f1 (2, k);
+	      f2 (2, k);
+	    }
+	  f2 (1, j);
+	}
+      f2 (0, i);
+    }
+}
+
+int
+main (void)
+{
+  f1count[0] = 0;
+  f1count[1] = 0;
+  f1count[2] = 0;
+  f2count[0] = 0;
+  f2count[1] = 0;
+  f2count[2] = 0;
+
+  s1 (3, 4, 5);
+
+  /* All intervening code at the same depth must be executed the same
+     number of times. */
+  if (f1count[0] != f2count[0]) abort ();
+  if (f1count[1] != f2count[1]) abort ();
+  if (f1count[2] != f2count[2]) abort ();
+
+  /* Intervening code must be executed at least as many times as the loop
+     that encloses it. */
+  if (f1count[0] < 3) abort ();
+  if (f1count[1] < 3 * 4) abort ();
+
+  /* Intervening code must not be executed more times than the number
+     of logical iterations. */
+  if (f1count[0] > 3 * 4 * 5) abort ();
+  if (f1count[1] > 3 * 4 * 5) abort ();
+
+  /* Check that the innermost loop body is executed exactly the number
+     of logical iterations expected. */
+  if (f1count[2] != 3 * 4 * 5) abort ();
+}
diff --git a/libgomp/testsuite/libgomp.c-c++-common/imperfect2.c b/libgomp/testsuite/libgomp.c-c++-common/imperfect2.c
new file mode 100644
index 00000000000..e2098006eab
--- /dev/null
+++ b/libgomp/testsuite/libgomp.c-c++-common/imperfect2.c
@@ -0,0 +1,114 @@ 
+/* { dg-do run } */
+
+static int f1count[3], f2count[3];
+static int g1count[3], g2count[3];
+
+#ifndef __cplusplus
+extern void abort (void);
+#else
+extern "C" void abort (void);
+#endif
+
+int f1 (int depth, int iter)
+{
+  f1count[depth]++;
+  return iter;
+}
+
+int f2 (int depth, int iter)
+{
+  f2count[depth]++;
+  return iter;
+}
+
+int g1 (int depth, int iter)
+{
+  g1count[depth]++;
+  return iter;
+}
+
+int g2 (int depth, int iter)
+{
+  g2count[depth]++;
+  return iter;
+}
+
+void s1 (int a1, int a2, int a3)
+{
+  int i, j, k;
+
+#pragma omp for collapse(3)
+  for (i = 0; i < a1; i++)
+    {
+      f1 (0, i);
+      {
+	g1 (0, i);
+	for (j = 0; j < a2; j++)
+	  {
+	    f1 (1, j);
+	    {
+	      g1 (1, j);
+	      for (k = 0; k < a3; k++)
+		{
+		  f1 (2, k);
+		  {
+		    g1 (2, k);
+		    g2 (2, k);
+		  }
+		  f2 (2, k);
+		}
+	      g2 (1, j);
+	    }
+	  f2 (1, j);
+	  }
+	g2 (0, i);
+      }
+      f2 (0, i);
+    }
+}
+
+int
+main (void)
+{
+  f1count[0] = 0;
+  f1count[1] = 0;
+  f1count[2] = 0;
+  f2count[0] = 0;
+  f2count[1] = 0;
+  f2count[2] = 0;
+
+  g1count[0] = 0;
+  g1count[1] = 0;
+  g1count[2] = 0;
+  g2count[0] = 0;
+  g2count[1] = 0;
+  g2count[2] = 0;
+
+  s1 (3, 4, 5);
+
+  /* All intervening code at the same depth must be executed the same
+     number of times. */
+  if (f1count[0] != f2count[0]) abort ();
+  if (f1count[1] != f2count[1]) abort ();
+  if (f1count[2] != f2count[2]) abort ();
+  if (g1count[0] != f1count[0]) abort ();
+  if (g2count[0] != f1count[0]) abort ();
+  if (g1count[1] != f1count[1]) abort ();
+  if (g2count[1] != f1count[1]) abort ();
+  if (g1count[2] != f1count[2]) abort ();
+  if (g2count[2] != f1count[2]) abort ();
+
+  /* Intervening code must be executed at least as many times as the loop
+     that encloses it. */
+  if (f1count[0] < 3) abort ();
+  if (f1count[1] < 3 * 4) abort ();
+
+  /* Intervening code must not be executed more times than the number
+     of logical iterations. */
+  if (f1count[0] > 3 * 4 * 5) abort ();
+  if (f1count[1] > 3 * 4 * 5) abort ();
+
+  /* Check that the innermost loop body is executed exactly the number
+     of logical iterations expected. */
+  if (f1count[2] != 3 * 4 * 5) abort ();
+}
diff --git a/libgomp/testsuite/libgomp.c-c++-common/imperfect3.c b/libgomp/testsuite/libgomp.c-c++-common/imperfect3.c
new file mode 100644
index 00000000000..feb5e32d1d6
--- /dev/null
+++ b/libgomp/testsuite/libgomp.c-c++-common/imperfect3.c
@@ -0,0 +1,119 @@ 
+/* { dg-do run } */
+
+/* Like imperfect2.c, but includes bindings in the blocks.  */
+
+static int f1count[3], f2count[3];
+static int g1count[3], g2count[3];
+
+#ifndef __cplusplus
+extern void abort (void);
+#else
+extern "C" void abort (void);
+#endif
+
+int f1 (int depth, int iter)
+{
+  f1count[depth]++;
+  return iter;
+}
+
+int f2 (int depth, int iter)
+{
+  f2count[depth]++;
+  return iter;
+}
+
+int g1 (int depth, int iter)
+{
+  g1count[depth]++;
+  return iter;
+}
+
+int g2 (int depth, int iter)
+{
+  g2count[depth]++;
+  return iter;
+}
+
+void s1 (int a1, int a2, int a3)
+{
+  int i, j, k;
+
+#pragma omp for collapse(3)
+  for (i = 0; i < a1; i++)
+    {
+      int local0 = 0;
+      f1 (local0, i);
+      {
+	g1 (local0, i);
+	for (j = 0; j < a2; j++)
+	  {
+	    int local1 = 1;
+	    f1 (local1, j);
+	    {
+	      g1 (local1, j);
+	      for (k = 0; k < a3; k++)
+		{
+		  int local2 = 2;
+		  f1 (local2, k);
+		  {
+		    g1 (local2, k);
+		    g2 (local2, k);
+		  }
+		  f2 (local2, k);
+		}
+	      g2 (local1, j);
+	    }
+	  f2 (local1, j);
+	  }
+	g2 (local0, i);
+      }
+      f2 (local0, i);
+    }
+}
+
+int
+main (void)
+{
+  f1count[0] = 0;
+  f1count[1] = 0;
+  f1count[2] = 0;
+  f2count[0] = 0;
+  f2count[1] = 0;
+  f2count[2] = 0;
+
+  g1count[0] = 0;
+  g1count[1] = 0;
+  g1count[2] = 0;
+  g2count[0] = 0;
+  g2count[1] = 0;
+  g2count[2] = 0;
+
+  s1 (3, 4, 5);
+
+  /* All intervening code at the same depth must be executed the same
+     number of times. */
+  if (f1count[0] != f2count[0]) abort ();
+  if (f1count[1] != f2count[1]) abort ();
+  if (f1count[2] != f2count[2]) abort ();
+  if (g1count[0] != f1count[0]) abort ();
+  if (g2count[0] != f1count[0]) abort ();
+  if (g1count[1] != f1count[1]) abort ();
+  if (g2count[1] != f1count[1]) abort ();
+  if (g1count[2] != f1count[2]) abort ();
+  if (g2count[2] != f1count[2]) abort ();
+
+  /* Intervening code must be executed at least as many times as the loop
+     that encloses it. */
+  if (f1count[0] < 3) abort ();
+  if (f1count[1] < 3 * 4) abort ();
+
+  /* Intervening code must not be executed more times than the number
+     of logical iterations. */
+  if (f1count[0] > 3 * 4 * 5) abort ();
+  if (f1count[1] > 3 * 4 * 5) abort ();
+
+  /* Check that the innermost loop body is executed exactly the number
+     of logical iterations expected. */
+  if (f1count[2] != 3 * 4 * 5) abort ();
+}
diff --git a/libgomp/testsuite/libgomp.c-c++-common/imperfect4.c b/libgomp/testsuite/libgomp.c-c++-common/imperfect4.c
new file mode 100644
index 00000000000..e29301bfbad
--- /dev/null
+++ b/libgomp/testsuite/libgomp.c-c++-common/imperfect4.c
@@ -0,0 +1,117 @@ 
+/* { dg-do run } */
+
+/* Like imperfect2.c, but includes blocks that are themselves intervening
+   code.  */
+
+static int f1count[3], f2count[3];
+static int g1count[3], g2count[3];
+
+#ifndef __cplusplus
+extern void abort (void);
+#else
+extern "C" void abort (void);
+#endif
+
+int f1 (int depth, int iter)
+{
+  f1count[depth]++;
+  return iter;
+}
+
+int f2 (int depth, int iter)
+{
+  f2count[depth]++;
+  return iter;
+}
+
+int g1 (int depth, int iter)
+{
+  g1count[depth]++;
+  return iter;
+}
+
+int g2 (int depth, int iter)
+{
+  g2count[depth]++;
+  return iter;
+}
+
+void s1 (int a1, int a2, int a3)
+{
+  int i, j, k;
+
+#pragma omp for collapse(3)
+  for (i = 0; i < a1; i++)
+    {
+      { f1 (0, i); }
+      {
+	g1 (0, i);
+	for (j = 0; j < a2; j++)
+	  {
+	    { f1 (1, j); }
+	    {
+	      { g1 (1, j); }
+	      for (k = 0; k < a3; k++)
+		{
+		  f1 (2, k);
+		  {
+		    g1 (2, k);
+		    g2 (2, k);
+		  }
+		  f2 (2, k);
+		}
+	      { g2 (1, j); }
+	    }
+	    { f2 (1, j); }
+	  }
+	{ g2 (0, i); }
+      }
+      { f2 (0, i); }
+    }
+}
+
+int
+main (void)
+{
+  f1count[0] = 0;
+  f1count[1] = 0;
+  f1count[2] = 0;
+  f2count[0] = 0;
+  f2count[1] = 0;
+  f2count[2] = 0;
+
+  g1count[0] = 0;
+  g1count[1] = 0;
+  g1count[2] = 0;
+  g2count[0] = 0;
+  g2count[1] = 0;
+  g2count[2] = 0;
+
+  s1 (3, 4, 5);
+
+  /* All intervening code at the same depth must be executed the same
+     number of times. */
+  if (f1count[0] != f2count[0]) abort ();
+  if (f1count[1] != f2count[1]) abort ();
+  if (f1count[2] != f2count[2]) abort ();
+  if (g1count[0] != f1count[0]) abort ();
+  if (g2count[0] != f1count[0]) abort ();
+  if (g1count[1] != f1count[1]) abort ();
+  if (g2count[1] != f1count[1]) abort ();
+  if (g1count[2] != f1count[2]) abort ();
+  if (g2count[2] != f1count[2]) abort ();
+
+  /* Intervening code must be executed at least as many times as the loop
+     that encloses it. */
+  if (f1count[0] < 3) abort ();
+  if (f1count[1] < 3 * 4) abort ();
+
+  /* Intervening code must not be executed more times than the number
+     of logical iterations. */
+  if (f1count[0] > 3 * 4 * 5) abort ();
+  if (f1count[1] > 3 * 4 * 5) abort ();
+
+  /* Check that the innermost loop body is executed exactly the number
+     of logical iterations expected. */
+  if (f1count[2] != 3 * 4 * 5) abort ();
+}
diff --git a/libgomp/testsuite/libgomp.c-c++-common/imperfect5.c b/libgomp/testsuite/libgomp.c-c++-common/imperfect5.c
new file mode 100644
index 00000000000..7bd4f12d472
--- /dev/null
+++ b/libgomp/testsuite/libgomp.c-c++-common/imperfect5.c
@@ -0,0 +1,49 @@ 
+/* { dg-do run } */
+
+#ifndef __cplusplus
+extern void abort (void);
+#else
+extern "C" void abort (void);
+#endif
+
+static int inner_loop_count = 0;
+static int intervening_code_count = 0;
+
+void
+g (int x, int y)
+{
+  inner_loop_count++;
+}
+
+int
+foo (int imax, int jmax)
+{
+  int j = 0;
+
+#pragma omp for collapse(2)
+  for (int i = 0; i < imax; ++i)
+    {
+      /* All the intervening code at the same level must be executed
+	 the same number of times.  */
+      ++intervening_code_count;
+      for (int j = 0; j < jmax; ++j)
+	{
+	  g (i, j);
+	}
+      /* This is the outer j, not the one from the inner collapsed loop.  */
+      ++j;
+    }
+  return j;
+}
+
+int
+main (void)
+{
+  int j = foo (5, 3);
+  if (j != intervening_code_count)
+    abort ();
+  if (inner_loop_count != 5 * 3)
+    abort ();
+  if (intervening_code_count < 5 || intervening_code_count > 5 * 3)
+    abort ();
+}
diff --git a/libgomp/testsuite/libgomp.c-c++-common/imperfect6.c b/libgomp/testsuite/libgomp.c-c++-common/imperfect6.c
new file mode 100644
index 00000000000..808c6540890
--- /dev/null
+++ b/libgomp/testsuite/libgomp.c-c++-common/imperfect6.c
@@ -0,0 +1,115 @@ 
+/* { dg-do run } */
+
+/* Like imperfect4.c, but bind the iteration variables in the loops.  */
+
+static int f1count[3], f2count[3];
+static int g1count[3], g2count[3];
+
+#ifndef __cplusplus
+extern void abort (void);
+#else
+extern "C" void abort (void);
+#endif
+
+int f1 (int depth, int iter)
+{
+  f1count[depth]++;
+  return iter;
+}
+
+int f2 (int depth, int iter)
+{
+  f2count[depth]++;
+  return iter;
+}
+
+int g1 (int depth, int iter)
+{
+  g1count[depth]++;
+  return iter;
+}
+
+int g2 (int depth, int iter)
+{
+  g2count[depth]++;
+  return iter;
+}
+
+void s1 (int a1, int a2, int a3)
+{
+
+#pragma omp for collapse(3)
+  for (int i = 0; i < a1; i++)
+    {
+      { f1 (0, i); }
+      {
+	g1 (0, i);
+	for (int j = 0; j < a2; j++)
+	  {
+	    { f1 (1, j); }
+	    {
+	      { g1 (1, j); }
+	      for (int k = 0; k < a3; k++)
+		{
+		  f1 (2, k);
+		  {
+		    g1 (2, k);
+		    g2 (2, k);
+		  }
+		  f2 (2, k);
+		}
+	      { g2 (1, j); }
+	    }
+	    { f2 (1, j); }
+	  }
+	{ g2 (0, i); }
+      }
+      { f2 (0, i); }
+    }
+}
+
+int
+main (void)
+{
+  f1count[0] = 0;
+  f1count[1] = 0;
+  f1count[2] = 0;
+  f2count[0] = 0;
+  f2count[1] = 0;
+  f2count[2] = 0;
+
+  g1count[0] = 0;
+  g1count[1] = 0;
+  g1count[2] = 0;
+  g2count[0] = 0;
+  g2count[1] = 0;
+  g2count[2] = 0;
+
+  s1 (3, 4, 5);
+
+  /* All intervening code at the same depth must be executed the same
+     number of times. */
+  if (f1count[0] != f2count[0]) abort ();
+  if (f1count[1] != f2count[1]) abort ();
+  if (f1count[2] != f2count[2]) abort ();
+  if (g1count[0] != f1count[0]) abort ();
+  if (g2count[0] != f1count[0]) abort ();
+  if (g1count[1] != f1count[1]) abort ();
+  if (g2count[1] != f1count[1]) abort ();
+  if (g1count[2] != f1count[2]) abort ();
+  if (g2count[2] != f1count[2]) abort ();
+
+  /* Intervening code must be executed at least as many times as the loop
+     that encloses it. */
+  if (f1count[0] < 3) abort ();
+  if (f1count[1] < 3 * 4) abort ();
+
+  /* Intervening code must not be executed more times than the number
+     of logical iterations. */
+  if (f1count[0] > 3 * 4 * 5) abort ();
+  if (f1count[1] > 3 * 4 * 5) abort ();
+
+  /* Check that the innermost loop body is executed exactly the number
+     of logical iterations expected. */
+  if (f1count[2] != 3 * 4 * 5) abort ();
+}
diff --git a/libgomp/testsuite/libgomp.c-c++-common/target-imperfect1.c b/libgomp/testsuite/libgomp.c-c++-common/target-imperfect1.c
new file mode 100644
index 00000000000..53bc611ace3
--- /dev/null
+++ b/libgomp/testsuite/libgomp.c-c++-common/target-imperfect1.c
@@ -0,0 +1,81 @@ 
+/* { dg-do run } */
+
+/* Like imperfect1.c, but enables offloading.  */
+
+static int f1count[3], f2count[3];
+#pragma omp declare target enter (f1count, f2count)
+
+#ifndef __cplusplus
+extern void abort (void);
+#else
+extern "C" void abort (void);
+#endif
+
+int f1 (int depth, int iter)
+{
+  #pragma omp atomic
+  f1count[depth]++;
+  return iter;
+}
+
+int f2 (int depth, int iter)
+{
+  #pragma omp atomic
+  f2count[depth]++;
+  return iter;
+}
+
+void s1 (int a1, int a2, int a3)
+{
+  int i, j, k;
+
+#pragma omp target parallel for collapse(3) map(always, tofrom:f1count, f2count)
+  for (i = 0; i < a1; i++)
+    {
+      f1 (0, i);
+      for (j = 0; j < a2; j++)
+	{
+	  f1 (1, j);
+	  for (k = 0; k < a3; k++)
+	    {
+	      f1 (2, k);
+	      f2 (2, k);
+	    }
+	  f2 (1, j);
+	}
+      f2 (0, i);
+    }
+}
+
+int
+main (void)
+{
+  f1count[0] = 0;
+  f1count[1] = 0;
+  f1count[2] = 0;
+  f2count[0] = 0;
+  f2count[1] = 0;
+  f2count[2] = 0;
+
+  s1 (3, 4, 5);
+
+  /* All intervening code at the same depth must be executed the same
+     number of times. */
+  if (f1count[0] != f2count[0]) abort ();
+  if (f1count[1] != f2count[1]) abort ();
+  if (f1count[2] != f2count[2]) abort ();
+
+  /* Intervening code must be executed at least as many times as the loop
+     that encloses it. */
+  if (f1count[0] < 3) abort ();
+  if (f1count[1] < 3 * 4) abort ();
+
+  /* Intervening code must not be executed more times than the number
+     of logical iterations. */
+  if (f1count[0] > 3 * 4 * 5) abort ();
+  if (f1count[1] > 3 * 4 * 5) abort ();
+
+  /* Check that the innermost loop body is executed exactly the number
+     of logical iterations expected. */
+  if (f1count[2] != 3 * 4 * 5) abort ();
+}
diff --git a/libgomp/testsuite/libgomp.c-c++-common/target-imperfect2.c b/libgomp/testsuite/libgomp.c-c++-common/target-imperfect2.c
new file mode 100644
index 00000000000..bc2901a517e
--- /dev/null
+++ b/libgomp/testsuite/libgomp.c-c++-common/target-imperfect2.c
@@ -0,0 +1,122 @@ 
+/* { dg-do run } */
+
+/* Like imperfect2.c, but enables offloading.  */
+
+static int f1count[3], f2count[3];
+static int g1count[3], g2count[3];
+#pragma omp declare target enter (f1count, f2count)
+#pragma omp declare target enter (g1count, g2count)
+
+#ifndef __cplusplus
+extern void abort (void);
+#else
+extern "C" void abort (void);
+#endif
+
+int f1 (int depth, int iter)
+{
+  #pragma omp atomic
+  f1count[depth]++;
+  return iter;
+}
+
+int f2 (int depth, int iter)
+{
+  #pragma omp atomic
+  f2count[depth]++;
+  return iter;
+}
+
+int g1 (int depth, int iter)
+{
+  #pragma omp atomic
+  g1count[depth]++;
+  return iter;
+}
+
+int g2 (int depth, int iter)
+{
+  #pragma omp atomic
+  g2count[depth]++;
+  return iter;
+}
+
+void s1 (int a1, int a2, int a3)
+{
+  int i, j, k;
+
+#pragma omp target parallel for collapse(3) map(always, tofrom:f1count, f2count, g1count, g2count)
+  for (i = 0; i < a1; i++)
+    {
+      f1 (0, i);
+      {
+	g1 (0, i);
+	for (j = 0; j < a2; j++)
+	  {
+	    f1 (1, j);
+	    {
+	      g1 (1, j);
+	      for (k = 0; k < a3; k++)
+		{
+		  f1 (2, k);
+		  {
+		    g1 (2, k);
+		    g2 (2, k);
+		  }
+		  f2 (2, k);
+		}
+	      g2 (1, j);
+	    }
+	  f2 (1, j);
+	  }
+	g2 (0, i);
+      }
+      f2 (0, i);
+    }
+}
+
+int
+main (void)
+{
+  f1count[0] = 0;
+  f1count[1] = 0;
+  f1count[2] = 0;
+  f2count[0] = 0;
+  f2count[1] = 0;
+  f2count[2] = 0;
+
+  g1count[0] = 0;
+  g1count[1] = 0;
+  g1count[2] = 0;
+  g2count[0] = 0;
+  g2count[1] = 0;
+  g2count[2] = 0;
+
+  s1 (3, 4, 5);
+
+  /* All intervening code at the same depth must be executed the same
+     number of times. */
+  if (f1count[0] != f2count[0]) abort ();
+  if (f1count[1] != f2count[1]) abort ();
+  if (f1count[2] != f2count[2]) abort ();
+  if (g1count[0] != f1count[0]) abort ();
+  if (g2count[0] != f1count[0]) abort ();
+  if (g1count[1] != f1count[1]) abort ();
+  if (g2count[1] != f1count[1]) abort ();
+  if (g1count[2] != f1count[2]) abort ();
+  if (g2count[2] != f1count[2]) abort ();
+
+  /* Intervening code must be executed at least as many times as the loop
+     that encloses it. */
+  if (f1count[0] < 3) abort ();
+  if (f1count[1] < 3 * 4) abort ();
+
+  /* Intervening code must not be executed more times than the number
+     of logical iterations. */
+  if (f1count[0] > 3 * 4 * 5) abort ();
+  if (f1count[1] > 3 * 4 * 5) abort ();
+
+  /* Check that the innermost loop body is executed exactly the number
+     of logical iterations expected. */
+  if (f1count[2] != 3 * 4 * 5) abort ();
+}
diff --git a/libgomp/testsuite/libgomp.c-c++-common/target-imperfect3.c b/libgomp/testsuite/libgomp.c-c++-common/target-imperfect3.c
new file mode 100644
index 00000000000..ddcfcf4b7eb
--- /dev/null
+++ b/libgomp/testsuite/libgomp.c-c++-common/target-imperfect3.c
@@ -0,0 +1,125 @@ 
+/* { dg-do run } */
+
+/* Like imperfect3.c, but enables offloading.  */
+
+static int f1count[3], f2count[3];
+static int g1count[3], g2count[3];
+#pragma omp declare target enter (f1count, f2count)
+#pragma omp declare target enter (g1count, g2count)
+
+#ifndef __cplusplus
+extern void abort (void);
+#else
+extern "C" void abort (void);
+#endif
+
+int f1 (int depth, int iter)
+{
+  #pragma omp atomic
+  f1count[depth]++;
+  return iter;
+}
+
+int f2 (int depth, int iter)
+{
+  #pragma omp atomic
+  f2count[depth]++;
+  return iter;
+}
+
+int g1 (int depth, int iter)
+{
+  #pragma omp atomic
+  g1count[depth]++;
+  return iter;
+}
+
+int g2 (int depth, int iter)
+{
+  #pragma omp atomic
+  g2count[depth]++;
+  return iter;
+}
+
+void s1 (int a1, int a2, int a3)
+{
+  int i, j, k;
+
+#pragma omp target parallel for collapse(3) map(always, tofrom:f1count, f2count, g1count, g2count)
+  for (i = 0; i < a1; i++)
+    {
+      int local0 = 0;
+      f1 (local0, i);
+      {
+	g1 (local0, i);
+	for (j = 0; j < a2; j++)
+	  {
+	    int local1 = 1;
+	    f1 (local1, j);
+	    {
+	      g1 (local1, j);
+	      for (k = 0; k < a3; k++)
+		{
+		  int local2 = 2;
+		  f1 (local2, k);
+		  {
+		    g1 (local2, k);
+		    g2 (local2, k);
+		  }
+		  f2 (local2, k);
+		}
+	      g2 (local1, j);
+	    }
+	  f2 (local1, j);
+	  }
+	g2 (local0, i);
+      }
+      f2 (local0, i);
+    }
+}
+
+int
+main (void)
+{
+  f1count[0] = 0;
+  f1count[1] = 0;
+  f1count[2] = 0;
+  f2count[0] = 0;
+  f2count[1] = 0;
+  f2count[2] = 0;
+
+  g1count[0] = 0;
+  g1count[1] = 0;
+  g1count[2] = 0;
+  g2count[0] = 0;
+  g2count[1] = 0;
+  g2count[2] = 0;
+
+  s1 (3, 4, 5);
+
+  /* All intervening code at the same depth must be executed the same
+     number of times. */
+  if (f1count[0] != f2count[0]) abort ();
+  if (f1count[1] != f2count[1]) abort ();
+  if (f1count[2] != f2count[2]) abort ();
+  if (g1count[0] != f1count[0]) abort ();
+  if (g2count[0] != f1count[0]) abort ();
+  if (g1count[1] != f1count[1]) abort ();
+  if (g2count[1] != f1count[1]) abort ();
+  if (g1count[2] != f1count[2]) abort ();
+  if (g2count[2] != f1count[2]) abort ();
+
+  /* Intervening code must be executed at least as many times as the loop
+     that encloses it. */
+  if (f1count[0] < 3) abort ();
+  if (f1count[1] < 3 * 4) abort ();
+
+  /* Intervening code must not be executed more times than the number
+     of logical iterations. */
+  if (f1count[0] > 3 * 4 * 5) abort ();
+  if (f1count[1] > 3 * 4 * 5) abort ();
+
+  /* Check that the innermost loop body is executed exactly the number
+     of logical iterations expected. */
+  if (f1count[2] != 3 * 4 * 5) abort ();
+}
diff --git a/libgomp/testsuite/libgomp.c-c++-common/target-imperfect4.c b/libgomp/testsuite/libgomp.c-c++-common/target-imperfect4.c
new file mode 100644
index 00000000000..ede488977b8
--- /dev/null
+++ b/libgomp/testsuite/libgomp.c-c++-common/target-imperfect4.c
@@ -0,0 +1,122 @@ 
+/* { dg-do run } */
+
+/* Like imperfect4.c, but enables offloading.  */
+
+static int f1count[3], f2count[3];
+static int g1count[3], g2count[3];
+#pragma omp declare target enter (f1count, f2count)
+#pragma omp declare target enter (g1count, g2count)
+
+#ifndef __cplusplus
+extern void abort (void);
+#else
+extern "C" void abort (void);
+#endif
+
+int f1 (int depth, int iter)
+{
+  #pragma omp atomic
+  f1count[depth]++;
+  return iter;
+}
+
+int f2 (int depth, int iter)
+{
+  #pragma omp atomic
+  f2count[depth]++;
+  return iter;
+}
+
+int g1 (int depth, int iter)
+{
+  #pragma omp atomic
+  g1count[depth]++;
+  return iter;
+}
+
+int g2 (int depth, int iter)
+{
+  #pragma omp atomic
+  g2count[depth]++;
+  return iter;
+}
+
+void s1 (int a1, int a2, int a3)
+{
+  int i, j, k;
+
+#pragma omp target parallel for collapse(3) map(always, tofrom:f1count, f2count, g1count, g2count)
+  for (i = 0; i < a1; i++)
+    {
+      { f1 (0, i); }
+      {
+	g1 (0, i);
+	for (j = 0; j < a2; j++)
+	  {
+	    { f1 (1, j); }
+	    {
+	      { g1 (1, j); }
+	      for (k = 0; k < a3; k++)
+		{
+		  f1 (2, k);
+		  {
+		    g1 (2, k);
+		    g2 (2, k);
+		  }
+		  f2 (2, k);
+		}
+	      { g2 (1, j); }
+	    }
+	    { f2 (1, j); }
+	  }
+	{ g2 (0, i); }
+      }
+      { f2 (0, i); }
+    }
+}
+
+int
+main (void)
+{
+  f1count[0] = 0;
+  f1count[1] = 0;
+  f1count[2] = 0;
+  f2count[0] = 0;
+  f2count[1] = 0;
+  f2count[2] = 0;
+
+  g1count[0] = 0;
+  g1count[1] = 0;
+  g1count[2] = 0;
+  g2count[0] = 0;
+  g2count[1] = 0;
+  g2count[2] = 0;
+
+  s1 (3, 4, 5);
+
+  /* All intervening code at the same depth must be executed the same
+     number of times. */
+  if (f1count[0] != f2count[0]) abort ();
+  if (f1count[1] != f2count[1]) abort ();
+  if (f1count[2] != f2count[2]) abort ();
+  if (g1count[0] != f1count[0]) abort ();
+  if (g2count[0] != f1count[0]) abort ();
+  if (g1count[1] != f1count[1]) abort ();
+  if (g2count[1] != f1count[1]) abort ();
+  if (g1count[2] != f1count[2]) abort ();
+  if (g2count[2] != f1count[2]) abort ();
+
+  /* Intervening code must be executed at least as many times as the loop
+     that encloses it. */
+  if (f1count[0] < 3) abort ();
+  if (f1count[1] < 3 * 4) abort ();
+
+  /* Intervening code must not be executed more times than the number
+     of logical iterations. */
+  if (f1count[0] > 3 * 4 * 5) abort ();
+  if (f1count[1] > 3 * 4 * 5) abort ();
+
+  /* Check that the innermost loop body is executed exactly the number
+     of logical iterations expected. */
+  if (f1count[2] != 3 * 4 * 5) abort ();
+}