[OG13,committed,3/3] OpenMP: C++ attribute syntax fixes/testcases for loop transformations

Message ID 20230818173938.430758-4-sandra@codesourcery.com
State Unresolved
Headers
Series C++ attribute syntax fixes/testcases |

Checks

Context Check Description
snail/gcc-patch-check warning Git am fail log

Commit Message

Sandra Loosemore Aug. 18, 2023, 5:39 p.m. UTC
  gcc/cp/ChangeLog
	* parser.cc (cp_parser_omp_all_clauses): Allow comma before first
	clause.
	(cp_parser_see_omp_loop_nest): Accept C++ standard attributes
	before RID_FOR.
	(cp_parser_omp_loop_nest): Process C++ standard attributes like
	pragmas.  Improve error handling for bad pragmas/attributes.
	Use cp_parser_see_omp_loop_nest instead of duplicating what it
	does.
	(cp_parser_omp_tile_sizes): Permit comma before the clause.
	(cp_parser_omp_tile): Assert that this isn't called for inner
	directive.
	(cp_parser_omp_unroll): Likewise.

gcc/testsuite/ChangeLog
	* g++.dg/gomp/loop-transforms/attrs-tile-1.C: New file.
	* g++.dg/gomp/loop-transforms/attrs-tile-2.C: New file.
	* g++.dg/gomp/loop-transforms/attrs-tile-3.C: New file.
	* g++.dg/gomp/loop-transforms/attrs-unroll-1.C: New file.
	* g++.dg/gomp/loop-transforms/attrs-unroll-2.C: New file.
	* g++.dg/gomp/loop-transforms/attrs-unroll-3.C: New file.
	* g++.dg/gomp/loop-transforms/attrs-unroll-inner-1.C: New file.
	* g++.dg/gomp/loop-transforms/attrs-unroll-inner-2.C: New file.
	* g++.dg/gomp/loop-transforms/attrs-unroll-inner-3.C: New file.
---
 gcc/cp/ChangeLog.omp                          |  15 ++
 gcc/cp/parser.cc                              |  69 ++++---
 gcc/testsuite/ChangeLog.omp                   |  12 ++
 .../gomp/loop-transforms/attrs-tile-1.C       | 164 +++++++++++++++++
 .../gomp/loop-transforms/attrs-tile-2.C       | 174 ++++++++++++++++++
 .../gomp/loop-transforms/attrs-tile-3.C       | 111 +++++++++++
 .../gomp/loop-transforms/attrs-unroll-1.C     | 135 ++++++++++++++
 .../gomp/loop-transforms/attrs-unroll-2.C     |  81 ++++++++
 .../gomp/loop-transforms/attrs-unroll-3.C     |  20 ++
 .../loop-transforms/attrs-unroll-inner-1.C    |  15 ++
 .../loop-transforms/attrs-unroll-inner-2.C    |  29 +++
 .../loop-transforms/attrs-unroll-inner-3.C    |  71 +++++++
 12 files changed, 872 insertions(+), 24 deletions(-)
 create mode 100644 gcc/testsuite/g++.dg/gomp/loop-transforms/attrs-tile-1.C
 create mode 100644 gcc/testsuite/g++.dg/gomp/loop-transforms/attrs-tile-2.C
 create mode 100644 gcc/testsuite/g++.dg/gomp/loop-transforms/attrs-tile-3.C
 create mode 100644 gcc/testsuite/g++.dg/gomp/loop-transforms/attrs-unroll-1.C
 create mode 100644 gcc/testsuite/g++.dg/gomp/loop-transforms/attrs-unroll-2.C
 create mode 100644 gcc/testsuite/g++.dg/gomp/loop-transforms/attrs-unroll-3.C
 create mode 100644 gcc/testsuite/g++.dg/gomp/loop-transforms/attrs-unroll-inner-1.C
 create mode 100644 gcc/testsuite/g++.dg/gomp/loop-transforms/attrs-unroll-inner-2.C
 create mode 100644 gcc/testsuite/g++.dg/gomp/loop-transforms/attrs-unroll-inner-3.C
  

Patch

diff --git a/gcc/cp/ChangeLog.omp b/gcc/cp/ChangeLog.omp
index 1b2d71422d8..fe5ef67a7ad 100644
--- a/gcc/cp/ChangeLog.omp
+++ b/gcc/cp/ChangeLog.omp
@@ -1,3 +1,18 @@ 
+2023-08-18  Sandra Loosemore  <sandra@codesourcery.com>
+
+	* parser.cc (cp_parser_omp_all_clauses): Allow comma before first
+	clause.
+	(cp_parser_see_omp_loop_nest): Accept C++ standard attributes
+	before RID_FOR.
+	(cp_parser_omp_loop_nest): Process C++ standard attributes like
+	pragmas.  Improve error handling for bad pragmas/attributes.
+	Use cp_parser_see_omp_loop_nest instead of duplicating what it
+	does.
+	(cp_parser_omp_tile_sizes): Permit comma before the clause.
+	(cp_parser_omp_tile): Assert that this isn't called for inner
+	directive.
+	(cp_parser_omp_unroll): Likewise.
+
 2023-08-18  Sandra Loosemore  <sandra@codesourcery.com>
 
 	* parser.cc (cp_parser_omp_declare_mapper): Allow commas between
diff --git a/gcc/cp/parser.cc b/gcc/cp/parser.cc
index 0ce2a7be608..4871f4511a9 100644
--- a/gcc/cp/parser.cc
+++ b/gcc/cp/parser.cc
@@ -42240,15 +42240,12 @@  cp_parser_omp_all_clauses (cp_parser *parser, omp_clause_mask mask,
       if (nested && cp_lexer_next_token_is (parser->lexer, CPP_CLOSE_PAREN))
 	break;
 
-      if (!first || nested != 2)
-	{
-	  if (cp_lexer_next_token_is (parser->lexer, CPP_COMMA))
-	    cp_lexer_consume_token (parser->lexer);
-	  else if (nested == 2)
-	    error_at (cp_lexer_peek_token (parser->lexer)->location,
-		      "clauses in %<simd%> trait should be separated "
-                      "by %<,%>");
-	}
+      if (cp_lexer_next_token_is (parser->lexer, CPP_COMMA))
+	cp_lexer_consume_token (parser->lexer);
+      else if (!first && nested == 2)
+	error_at (cp_lexer_peek_token (parser->lexer)->location,
+		  "clauses in %<simd%> trait should be separated "
+		  "by %<,%>");
 
       token = cp_lexer_peek_token (parser->lexer);
       c_kind = cp_parser_omp_clause_name (parser);
@@ -44803,6 +44800,11 @@  cp_parser_see_omp_loop_nest (cp_parser *parser, enum tree_code code,
 	  || (cp_parser_pragma_kind (cp_lexer_peek_token (parser->lexer))
 	      == PRAGMA_OMP_TILE))
 	return true;
+      if (cp_lexer_nth_token_is_keyword
+	  (parser->lexer,
+	   cp_parser_skip_std_attribute_spec_seq (parser, 1),
+	   RID_FOR))
+	return true;
       if (error_p)
 	cp_parser_error (parser, "loop nest expected");
     }
@@ -44868,6 +44870,11 @@  cp_parser_omp_loop_nest (cp_parser *parser, bool *if_p)
      the depth of the *next* loop, not the level of the loop body the
      transformation directive appears in.  */
 
+  /* Arrange for C++ standard attribute syntax to be parsed as regular
+     pragmas.  */
+  tree std_attrs = cp_parser_std_attribute_spec_seq (parser);
+  std_attrs = cp_parser_handle_statement_omp_attributes (parser, std_attrs);
+
   if ((cp_parser_pragma_kind (cp_lexer_peek_token (parser->lexer))
        == PRAGMA_OMP_UNROLL)
       || (cp_parser_pragma_kind (cp_lexer_peek_token (parser->lexer))
@@ -44893,19 +44900,29 @@  cp_parser_omp_loop_nest (cp_parser *parser, bool *if_p)
 	    omp_for_parse_state->orig_declv
 	      = grow_tree_vec (omp_for_parse_state->orig_declv, count);
 	}
-      if (cp_parser_see_omp_loop_nest (parser, omp_for_parse_state->code,
-				       true))
-	return cp_parser_omp_loop_nest (parser, if_p);
-      else
+    }
+
+  /* Diagnose errors if we don't have a "for" loop following the
+     optional loop transforms.  Otherwise, consume the token.  */
+  if (!cp_lexer_next_token_is_keyword (parser->lexer, RID_FOR))
+    {
+      omp_for_parse_state->fail = true;
+      cp_token *token = cp_lexer_peek_token (parser->lexer);
+      /* Don't call cp_parser_error here since it overrides the
+	 provided message with a more confusing one if there was
+	 a bad pragma or attribute directive.  */
+      error_at (token->location, "loop nest expected");
+      /* See if we can recover by skipping over bad pragma(s).  */
+      while (token->type == CPP_PRAGMA)
 	{
-	  /* FIXME: Better error recovery here?  */
-	  omp_for_parse_state->fail = true;
-	  return NULL_TREE;
+	  cp_parser_skip_to_pragma_eol (parser, token);
+	  if (cp_parser_see_omp_loop_nest (parser, omp_for_parse_state->code,
+					   false))
+	    return cp_parser_omp_loop_nest (parser, if_p);
+	  token = cp_lexer_peek_token (parser->lexer);
 	}
+      return NULL_TREE;
     }
-
-  /* We have already matched the FOR token but not consumed it yet.  */
-  gcc_assert (cp_lexer_next_token_is_keyword (parser->lexer, RID_FOR));
   loc = cp_lexer_consume_token (parser->lexer)->location;
 
   /* Forbid break/continue in the loop initializer, condition, and
@@ -45161,11 +45178,8 @@  cp_parser_omp_loop_nest (cp_parser *parser, bool *if_p)
   moreloops = depth < omp_for_parse_state->count - 1;
   omp_for_parse_state->want_nested_loop = moreloops;
   if (moreloops
-      && (cp_lexer_next_token_is_keyword (parser->lexer, RID_FOR)
-	  || (cp_parser_pragma_kind (cp_lexer_peek_token (parser->lexer))
-	      == PRAGMA_OMP_UNROLL)
-	  || (cp_parser_pragma_kind (cp_lexer_peek_token (parser->lexer))
-	      == PRAGMA_OMP_TILE)))
+      && cp_parser_see_omp_loop_nest (parser, omp_for_parse_state->code,
+				      false))
     {
       omp_for_parse_state->depth++;
       add_stmt (cp_parser_omp_loop_nest (parser, if_p));
@@ -47323,6 +47337,9 @@  cp_parser_omp_tile_sizes (cp_parser *parser, location_t loc)
   tree sizes = NULL_TREE;
   cp_lexer *lexer = parser->lexer;
 
+  if (cp_lexer_next_token_is (parser->lexer, CPP_COMMA))
+    cp_lexer_consume_token (parser->lexer);
+
   cp_token *tok = cp_lexer_peek_token (lexer);
   if (tok->type != CPP_NAME
       || strcmp ("sizes", IDENTIFIER_POINTER (tok->u.value)))
@@ -47375,6 +47392,8 @@  cp_parser_omp_tile (cp_parser *parser, cp_token *tok, bool *if_p)
   tree block;
   tree ret = error_mark_node;
 
+  gcc_assert (!parser->omp_for_parse_state);
+
   tree clauses = cp_parser_omp_tile_sizes (parser, tok->location);
   cp_parser_require_pragma_eol (parser, tok);
 
@@ -47553,6 +47572,8 @@  cp_parser_omp_unroll (cp_parser *parser, cp_token *tok, bool *if_p)
   static const char *p_name = "#pragma omp unroll";
   omp_clause_mask mask = OMP_UNROLL_CLAUSE_MASK;
 
+  gcc_assert (!parser->omp_for_parse_state);
+
   tree clauses = cp_parser_omp_all_clauses (parser, mask, p_name, tok, true);
 
   if (!clauses)
diff --git a/gcc/testsuite/ChangeLog.omp b/gcc/testsuite/ChangeLog.omp
index a7d11777988..3008a8e52eb 100644
--- a/gcc/testsuite/ChangeLog.omp
+++ b/gcc/testsuite/ChangeLog.omp
@@ -1,3 +1,15 @@ 
+2023-08-18  Sandra Loosemore  <sandra@codesourcery.com>
+
+	* g++.dg/gomp/loop-transforms/attrs-tile-1.C: New file.
+	* g++.dg/gomp/loop-transforms/attrs-tile-2.C: New file.
+	* g++.dg/gomp/loop-transforms/attrs-tile-3.C: New file.
+	* g++.dg/gomp/loop-transforms/attrs-unroll-1.C: New file.
+	* g++.dg/gomp/loop-transforms/attrs-unroll-2.C: New file.
+	* g++.dg/gomp/loop-transforms/attrs-unroll-3.C: New file.
+	* g++.dg/gomp/loop-transforms/attrs-unroll-inner-1.C: New file.
+	* g++.dg/gomp/loop-transforms/attrs-unroll-inner-2.C: New file.
+	* g++.dg/gomp/loop-transforms/attrs-unroll-inner-3.C: New file.
+
 2023-08-18  Sandra Loosemore  <sandra@codesourcery.com>
 
 	* g++.dg/gomp/attrs-declare-mapper-3.C: New file.
diff --git a/gcc/testsuite/g++.dg/gomp/loop-transforms/attrs-tile-1.C b/gcc/testsuite/g++.dg/gomp/loop-transforms/attrs-tile-1.C
new file mode 100644
index 00000000000..0906ff3bbe8
--- /dev/null
+++ b/gcc/testsuite/g++.dg/gomp/loop-transforms/attrs-tile-1.C
@@ -0,0 +1,164 @@ 
+// { dg-do compile { target c++11 } } 
+
+extern void dummy (int);
+
+void
+test ()
+{
+    [[omp::directive (tile sizes(1))]]
+    for (int i = 0; i < 100; ++i)
+	dummy (i);
+
+    [[omp::directive (tile sizes(0))]] /* { dg-error {'tile sizes' argument needs positive integral constant} } */
+    for (int i = 0; i < 100; ++i)
+	dummy (i);
+
+    [[omp::directive (tile sizes(-1))]] /* { dg-error {'tile sizes' argument needs positive integral constant} } */
+    for (int i = 0; i < 100; ++i)
+	dummy (i);
+
+    [[omp::directive (tile sizes())]] /* { dg-error {expected primary-expression before} } */
+    for (int i = 0; i < 100; ++i)
+	dummy (i);
+
+    [[omp::directive (tile sizes)]] /* { dg-error {expected '\(' before end of line} } */
+    for (int i = 0; i < 100; ++i)
+	dummy (i);
+
+    [[omp::directive (tile sizes(1) sizes(1))]] /* { dg-error {expected end of line before 'sizes'} } */
+    for (int i = 0; i < 100; ++i)
+	dummy (i);
+
+    [[omp::directive (tile, sizes(1), sizes(1))]] /* { dg-error {expected end of line before ','} } */
+    for (int i = 0; i < 100; ++i)
+	dummy (i);
+
+    [[omp::sequence
+      (directive (tile sizes(1)),
+       directive (tile sizes(1)))]]
+    for (int i = 0; i < 100; ++i)
+      dummy (i);
+
+    [[omp::sequence 
+      (directive (tile sizes(1, 2)),
+       directive (tile sizes(1)))]] /* { dg-error {nesting depth left after this transformation too low for outer transformation} } */
+    for (int i = 0; i < 100; ++i)
+      for (int j = 0; j < 100; ++j)
+    	dummy (i);
+
+    [[omp::sequence
+      (directive (tile sizes(1, 2)),
+       directive (tile sizes(1, 2)))]]
+    for (int i = 0; i < 100; ++i)
+      for (int j = 0; j < 100; ++j)
+    	dummy (i);
+
+    [[omp::sequence
+      (directive (tile sizes(5, 6)),
+       directive (tile sizes(1, 2, 3)))]]
+    for (int i = 0; i < 100; ++i)
+      for (int j = 0; j < 100; ++j)
+	for (int k = 0; k < 100; ++k)
+	  dummy (i);
+
+    [[omp::sequence
+      (directive (tile sizes(1)),
+       directive (unroll partia), /* { dg-error {expected an OpenMP clause before 'partia'} } */
+       directive (tile sizes(1)))]]
+    for (int i = 0; i < 100; ++i)
+      dummy (i);
+
+    [[omp::sequence
+      (directive (tile sizes(1)),
+       directive (unroll))]] /* { dg-error {'#pragma omp unroll' without 'partial' clause is invalid here; turns loop into non-loop} } */
+    for (int i = 0; i < 100; ++i)
+	dummy (i);
+
+    [[omp::sequence
+      (directive (tile sizes(1)),
+       directive (unroll full))]] /* { dg-error {'full' clause is invalid here; turns loop into non-loop} } */
+    for (int i = 0; i < 100; ++i)
+	dummy (i);
+
+    [[omp::sequence
+      (directive (tile sizes(1)),
+       directive (unroll partial),
+       directive (tile sizes(1)))]]
+    for (int i = 0; i < 100; ++i)
+	dummy (i);
+
+    [[omp::sequence
+      (directive (tile sizes(8,8)),
+       directive (unroll partial), /* { dg-error {nesting depth left after this transformation too low for outer transformation} } */
+       directive (tile sizes(1)))]]
+    for (int i = 0; i < 100; ++i)
+	dummy (i);
+
+    [[omp::sequence
+      (directive (tile sizes(8,8)),
+       directive (unroll partial))]] /* { dg-error {nesting depth left after this transformation too low for outer transformation} } */
+    for (int i = 0; i < 100; ++i)
+	dummy (i);
+
+    [[omp::directive (tile sizes(1, 2))]]
+    for (int i = 0; i < 100; ++i)
+    for (int j = 0; j < 100; ++j)
+	dummy (i);
+
+    [[omp::directive (tile sizes(1, 2))]] /* { dg-error {'tile' loop transformation may not appear on non-rectangular for} } */
+    for (int i = 0; i < 100; ++i)
+    for (int j = i; j < 100; ++j)
+	dummy (i);
+
+    [[omp::directive (tile sizes(1, 2))]] /* { dg-error {'tile' loop transformation may not appear on non-rectangular for} } */
+    for (int i = 0; i < 100; ++i)
+    for (int j = 2; j < i; ++j)
+	dummy (i);
+
+    [[omp::directive (tile sizes(1, 2, 3))]]
+    for (int i = 0; i < 100; ++i) /* { dg-error {not enough nested loops} } */
+      for (int j = 0; j < 100; ++j)
+        dummy (i);
+
+    [[omp::directive (tile sizes(1))]]
+    for (int i = 0; i < 100; ++i)
+      {
+	dummy (i);
+        for (int j = 0; j < 100; ++j)
+          dummy (i);
+      }
+
+    [[omp::directive (tile sizes(1))]]
+    for (int i = 0; i < 100; ++i)
+      {
+        for (int j = 0; j < 100; ++j)
+	    dummy (j);
+	dummy (i);
+      }
+
+    [[omp::directive (tile sizes(1, 2))]]
+    for (int i = 0; i < 100; ++i) /* { dg-error {inner loops must be perfectly nested} } */
+      {
+        dummy (i);
+        for (int j = 0; j < 100; ++j)
+          dummy (j);
+      }
+
+    [[omp::directive (tile sizes(1, 2))]]
+    for (int i = 0; i < 100; ++i) /* { dg-error {inner loops must be perfectly nested} } */
+      {
+        for (int j = 0; j < 100; ++j)
+	  dummy (j);
+	dummy (i);
+      }
+
+    int s;
+    [[omp::directive (tile sizes(s))]] /* { dg-error {'tile sizes' argument needs positive integral constant} "" { target { ! c++98_only } } } */
+    /* { dg-error {the value of 's' is not usable in a constant expression} "" { target { c++ && { ! c++98_only } } } .-1 } */
+    for (int i = 0; i < 100; ++i)
+	dummy (i);
+
+    [[omp::directive (tile sizes(42.0))]] /* { dg-error {'tile sizes' argument needs integral type} } */
+    for (int i = 0; i < 100; ++i)
+	dummy (i);
+}
diff --git a/gcc/testsuite/g++.dg/gomp/loop-transforms/attrs-tile-2.C b/gcc/testsuite/g++.dg/gomp/loop-transforms/attrs-tile-2.C
new file mode 100644
index 00000000000..ab02924defa
--- /dev/null
+++ b/gcc/testsuite/g++.dg/gomp/loop-transforms/attrs-tile-2.C
@@ -0,0 +1,174 @@ 
+// { dg-do compile { target c++11 } }
+
+extern void dummy (int);
+
+void
+test ()
+{
+    [[omp::sequence (directive (parallel for),
+      directive (tile sizes(1)))]]
+    for (int i = 0; i < 100; ++i)
+	dummy (i);
+
+    [[omp::sequence (directive (parallel for),
+      directive (tile sizes(0)))]] /* { dg-error {'tile sizes' argument needs positive integral constant} } */
+    for (int i = 0; i < 100; ++i)
+	dummy (i);
+
+    [[omp::sequence (directive (parallel for),
+      directive (tile sizes(-1)))]] /* { dg-error {'tile sizes' argument needs positive integral constant} } */
+    for (int i = 0; i < 100; ++i)
+	dummy (i);
+
+    [[omp::sequence (directive (parallel for),
+      directive (tile sizes()))]] /* { dg-error {expected primary-expression before} } */
+    for (int i = 0; i < 100; ++i)
+	dummy (i);
+
+    [[omp::sequence (directive (parallel for),
+      directive (tile sizes(,)))]] /* { dg-error {expected primary-expression before} } */
+    for (int i = 0; i < 100; ++i)
+	dummy (i);
+
+    [[omp::sequence (directive (parallel for),
+      directive (tile sizes))]] /* { dg-error {expected '\(' before end of line} } */
+    for (int i = 0; i < 100; ++i)
+	dummy (i);
+
+    [[omp::sequence (directive (parallel for),
+      directive (tile sizes(1) sizes(1)))]] /* { dg-error {expected end of line before 'sizes'} } */
+    for (int i = 0; i < 100; ++i)
+	dummy (i);
+
+    [[omp::sequence (directive (parallel for),
+      directive (tile sizes(1)),
+      directive (tile sizes(1)))]]
+    for (int i = 0; i < 100; ++i)
+	dummy (i);
+
+    [[omp::sequence (directive (parallel for),
+      directive (tile sizes(1, 2)),
+      directive (tile sizes(1)))]] /* { dg-error {nesting depth left after this transformation too low for outer transformation} } */
+    for (int i = 0; i < 100; ++i)
+    for (int j = 0; j < 100; ++j)
+    	dummy (i);
+
+    [[omp::sequence (directive (parallel for),
+      directive (tile sizes(1, 2)),
+      directive (tile sizes(1, 2)))]]
+    for (int i = 0; i < 100; ++i)
+      for (int j = 0; j < 100; ++j)
+    	dummy (i);
+
+    [[omp::sequence (directive (parallel for),
+      directive (tile sizes(5, 6)),
+      directive (tile sizes(1, 2, 3)))]]
+    for (int i = 0; i < 100; ++i)
+      for (int j = 0; j < 100; ++j)
+        for (int k = 0; k < 100; ++k)
+      	dummy (i);
+
+    [[omp::sequence (directive (parallel for),
+      directive (tile sizes(1)),
+      directive (unroll partia), /* { dg-error {expected an OpenMP clause before 'partia'} } */
+      directive (tile sizes(1)))]]
+    for (int i = 0; i < 100; ++i)
+	dummy (i);
+
+    [[omp::sequence (directive (parallel for),
+      directive (tile sizes(1)),
+      directive (unroll))]] /* { dg-error {'#pragma omp unroll' without 'partial' clause is invalid here; turns loop into non-loop} } */
+    for (int i = 0; i < 100; ++i)
+	dummy (i);
+
+    [[omp::sequence (directive (parallel for),
+      directive (tile sizes(1)),
+      directive (unroll full))]] /* { dg-error {'full' clause is invalid here; turns loop into non-loop} } */
+    for (int i = 0; i < 100; ++i)
+	dummy (i);
+
+    [[omp::sequence (directive (parallel for),
+      directive (tile sizes(1)),
+      directive (unroll partial),
+      directive (tile sizes(1)))]]
+    for (int i = 0; i < 100; ++i)
+	dummy (i);
+
+    [[omp::sequence (directive (parallel for),
+      directive (tile sizes(8,8)),
+      directive (unroll partial), /* { dg-error {nesting depth left after this transformation too low for outer transformation} } */
+      directive (tile sizes(1)))]]
+    for (int i = 0; i < 100; ++i)
+	dummy (i);
+
+    [[omp::sequence (directive (parallel for),
+      directive (tile sizes(8,8)),
+      directive (unroll partial))]] /* { dg-error {nesting depth left after this transformation too low for outer transformation} } */
+    for (int i = 0; i < 100; ++i)
+	dummy (i);
+
+    [[omp::sequence (directive (parallel for),
+      directive (tile sizes(1, 2)))]]
+    for (int i = 0; i < 100; ++i)
+    for (int j = 0; j < 100; ++j)
+	dummy (i);
+
+    [[omp::sequence (directive (parallel for),
+      directive (tile sizes(1, 2)))]] /* { dg-error {'tile' loop transformation may not appear on non-rectangular for} } */
+    for (int i = 0; i < 100; ++i)
+    for (int j = i; j < 100; ++j)
+	dummy (i);
+
+    [[omp::sequence (directive (parallel for),
+      directive (tile sizes(1, 2)))]] /* { dg-error {'tile' loop transformation may not appear on non-rectangular for} } */
+    for (int i = 0; i < 100; ++i)
+    for (int j = 2; j < i; ++j)
+	dummy (i);
+
+    [[omp::sequence (directive (parallel for),
+      directive (tile sizes(1, 2, 3)))]]
+    for (int i = 0; i < 100; ++i) /* { dg-error {not enough nested loops} } */
+      for (int j = 0; j < 100; ++j)
+        dummy (i);
+
+    [[omp::sequence (directive (parallel for),
+      directive (tile sizes(1)))]]
+    for (int i = 0; i < 100; ++i)
+      {
+	dummy (i);
+        for (int j = 0; j < 100; ++j)
+          dummy (i);
+      }
+
+    [[omp::sequence (directive (parallel for),
+      directive (tile sizes(1)))]]
+    for (int i = 0; i < 100; ++i)
+      {
+        for (int j = 0; j < 100; ++j)
+	    dummy (j);
+	dummy (i);
+      }
+
+    [[omp::sequence (directive (parallel for),
+      directive (tile sizes(1, 2)))]]
+    for (int i = 0; i < 100; ++i) /* { dg-error {inner loops must be perfectly nested} } */
+      {
+        dummy (i);
+        for (int j = 0; j < 100; ++j)
+          dummy (j);
+      }
+
+    [[omp::sequence (directive (parallel for),
+      directive (tile sizes(1, 2)))]]
+    for (int i = 0; i < 100; ++i) /* { dg-error {inner loops must be perfectly nested} } */
+      {
+        for (int j = 0; j < 100; ++j)
+	  dummy (j);
+	dummy (i);
+      }
+
+    [[omp::sequence (directive (parallel for),
+      directive (tile sizes(1)))]]
+    for (int i = 0; i < 100; ++i)
+	dummy (i);
+}
diff --git a/gcc/testsuite/g++.dg/gomp/loop-transforms/attrs-tile-3.C b/gcc/testsuite/g++.dg/gomp/loop-transforms/attrs-tile-3.C
new file mode 100644
index 00000000000..95a0115b014
--- /dev/null
+++ b/gcc/testsuite/g++.dg/gomp/loop-transforms/attrs-tile-3.C
@@ -0,0 +1,111 @@ 
+// { dg-do compile { target c++11 } }
+
+extern void dummy (int);
+
+void
+test ()
+{
+    [[omp::sequence (directive (for),
+      directive (tile sizes(1, 2)))]] /* { dg-error {'tile' loop transformation may not appear on non-rectangular for} } */
+    for (int i = 0; i < 100; ++i)
+    for (int j = i; j < 100; ++j)
+	dummy (i);
+
+    [[omp::sequence (directive (for),
+      directive (tile sizes(1, 2)))]] /* { dg-error {'tile' loop transformation may not appear on non-rectangular for} } */
+    for (int i = 0; i < 100; ++i)
+    for (int j = 0; j < i; ++j)
+	dummy (i);
+
+
+    [[omp::sequence (directive (for collapse(1)),
+      directive (tile sizes(1)))]]
+    for (int i = 0; i < 100; ++i)
+    for (int j = 0; j < 100; ++j)
+	dummy (i);
+
+    [[omp::sequence (directive (for collapse(2)),
+      directive (tile sizes(1)))]] /* { dg-error {nesting depth left after this transformation too low for loop collapse} } */
+    for (int i = 0; i < 100; ++i)
+    for (int j = 0; j < 100; ++j)
+	dummy (i);
+
+    [[omp::sequence (directive (for collapse(2)),
+      directive (tile sizes(1, 2)))]]
+    for (int i = 0; i < 100; ++i)
+    for (int j = 0; j < 100; ++j)
+	dummy (i);
+
+    [[omp::sequence (directive (for collapse(3)),
+      directive (tile sizes(1, 2)))]] /* { dg-error {nesting depth left after this transformation too low for loop collapse} } */
+    for (int i = 0; i < 100; ++i) /* { dg-error {not enough nested loops} } */
+    for (int j = 0; j < 100; ++j)
+	dummy (i);
+
+    [[omp::sequence (directive (for collapse(1)),
+      directive (tile sizes(1)),
+      directive (tile sizes(1)))]]
+    for (int i = 0; i < 100; ++i)
+	dummy (i);
+
+    [[omp::sequence (directive (for collapse(2)),
+      directive (tile sizes(1, 2)),
+      directive (tile sizes(1)))]] /* { dg-error {nesting depth left after this transformation too low for outer transformation} } */
+    for (int i = 0; i < 100; ++i) /* { dg-error {not enough nested loops} } */
+    	dummy (i);
+
+    [[omp::sequence (directive (for collapse(2)),
+      directive (tile sizes(1, 2)),
+      directive (tile sizes(1, 2)))]]
+    for (int i = 0; i < 100; ++i) /* { dg-error {not enough nested loops} } */
+    	dummy (i);
+
+    [[omp::sequence (directive (for collapse(2)),
+      directive (tile sizes(5, 6)),
+      directive (tile sizes(1, 2, 3)))]]
+    for (int i = 0; i < 100; ++i) /* { dg-error {not enough nested loops} } */
+    	dummy (i); 
+
+    [[omp::sequence (directive (for collapse(1)),
+      directive (tile sizes(1)),
+      directive (tile sizes(1)))]]
+    for (int i = 0; i < 100; ++i)
+	dummy (i);
+
+    [[omp::sequence (directive (for collapse(2)),
+      directive (tile sizes(1, 2)),
+      directive (tile sizes(1)))]] /* { dg-error {nesting depth left after this transformation too low for outer transformation} } */
+    for (int i = 0; i < 100; ++i)
+    for (int j = 0; j < 100; ++j)
+    	dummy (i);
+
+    [[omp::sequence (directive (for collapse(2)),
+      directive (tile sizes(1, 2)),
+      directive (tile sizes(1, 2)))]]
+    for (int i = 0; i < 100; ++i)
+    for (int j = 0; j < 100; ++j)
+    	dummy (i);
+
+    [[omp::sequence (directive (for collapse(2)),
+      directive (tile sizes(5, 6)),
+      directive (tile sizes(1, 2, 3)))]]
+    for (int i = 0; i < 100; ++i)
+    for (int j = 0; j < 100; ++j)
+    for (int k = 0; k < 100; ++k)
+    	dummy (i);
+
+    [[omp::sequence (directive (for collapse(3)),
+      directive (tile sizes(1, 2)), /* { dg-error {nesting depth left after this transformation too low for loop collapse} } */
+      directive (tile sizes(1, 2)))]]
+    for (int i = 0; i < 100; ++i) /* { dg-error {not enough nested loops} } */
+    for (int j = 0; j < 100; ++j)
+    	dummy (i);
+
+    [[omp::sequence (directive (for collapse(3)),
+      directive (tile sizes(5, 6)), /* { dg-error {nesting depth left after this transformation too low for loop collapse} } */
+      directive (tile sizes(1, 2, 3)))]]
+    for (int i = 0; i < 100; ++i)
+    for (int j = 0; j < 100; ++j)
+    for (int k = 0; k < 100; ++k)
+    	dummy (i);
+}
diff --git a/gcc/testsuite/g++.dg/gomp/loop-transforms/attrs-unroll-1.C b/gcc/testsuite/g++.dg/gomp/loop-transforms/attrs-unroll-1.C
new file mode 100644
index 00000000000..5b93b9fa59e
--- /dev/null
+++ b/gcc/testsuite/g++.dg/gomp/loop-transforms/attrs-unroll-1.C
@@ -0,0 +1,135 @@ 
+// { dg-do compile { target c++11 } }
+
+extern void dummy (int);
+
+void
+test1 ()
+{
+[[omp::directive (unroll partial)]]
+  for (int i = 0; i < 100; ++i)
+    dummy (i);
+}
+
+void
+test2 ()
+{
+[[omp::directive (unroll partial(10))]]
+  for (int i = 0; i < 100; ++i)
+    dummy (i);
+}
+
+void
+test3 ()
+{
+[[omp::directive (unroll full)]]
+  for (int i = 0; i < 100; ++i)
+    dummy (i);
+}
+
+void
+test4 ()
+{
+[[omp::directive (unroll full)]]
+  for (int i = 0; i > 100; ++i)
+    dummy (i);
+}
+
+void
+test5 ()
+{
+[[omp::directive (unroll full)]]
+  for (int i = 1; i <= 100; ++i)
+    dummy (i);
+}
+
+void
+test6 ()
+{
+[[omp::directive (unroll full)]]
+  for (int i = 200; i >= 100; i--)
+    dummy (i);
+}
+
+void
+test7 ()
+{
+[[omp::directive (unroll full)]]
+  for (int i = -100; i > 100; ++i)
+    dummy (i);
+}
+
+void
+test8 ()
+{
+[[omp::directive (unroll full)]]
+  for (int i = 100; i > -200; --i)
+    dummy (i);
+}
+
+void
+test9 ()
+{
+[[omp::directive (unroll full)]]
+  for (int i = -300; i != 100; ++i)
+    dummy (i);
+}
+
+void
+test10 ()
+{
+[[omp::directive (unroll full)]]
+  for (int i = -300; i != 100; ++i)
+    dummy (i);
+}
+
+void
+test12 ()
+{
+[[omp::sequence (directive (unroll full),
+  directive (unroll partial),
+  directive (unroll partial))]]
+  for (int i = -300; i != 100; ++i)
+    dummy (i);
+}
+
+void
+test13 ()
+{
+  for (int i = 0; i < 100; ++i)
+[[omp::sequence (directive (unroll full),
+  directive (unroll partial),
+  directive (unroll partial))]]
+  for (int j = -300; j != 100; ++j)
+    dummy (i);
+}
+
+void
+test14 ()
+{
+  [[omp::directive (for)]]
+  for (int i = 0; i < 100; ++i)
+    [[omp::sequence (directive (unroll full),
+      directive (unroll partial),
+      directive (unroll partial))]]
+  for (int j = -300; j != 100; ++j)
+    dummy (i);
+}
+
+void
+test15 ()
+{
+  [[omp::directive (for)]]
+  for (int i = 0; i < 100; ++i)
+    {
+
+    dummy (i);
+
+  [[omp::sequence (directive (unroll full),
+    directive (unroll partial),
+    directive (unroll partial))]]
+  for (int j = -300; j != 100; ++j)
+    dummy (j);
+
+  dummy (i);
+    }
+ }
diff --git a/gcc/testsuite/g++.dg/gomp/loop-transforms/attrs-unroll-2.C b/gcc/testsuite/g++.dg/gomp/loop-transforms/attrs-unroll-2.C
new file mode 100644
index 00000000000..1a45eadec64
--- /dev/null
+++ b/gcc/testsuite/g++.dg/gomp/loop-transforms/attrs-unroll-2.C
@@ -0,0 +1,81 @@ 
+/* { dg-prune-output "error: invalid controlling predicate" } */
+// { dg-do compile { target c++11 } } 
+
+extern void dummy (int);
+
+void
+test ()
+{
+[[omp::sequence (directive (unroll partial),
+  directive (unroll full))]] /* { dg-error {'full' clause is invalid here; turns loop into non-loop} } */
+  for (int i = -300; i != 100; ++i)
+    dummy (i);
+
+[[omp::sequence (directive (for),
+  directive (unroll full), /* { dg-error {'full' clause is invalid here; turns loop into non-loop} } */
+  directive (unroll partial))]]
+  for (int i = -300; i != 100; ++i)
+    dummy (i);
+
+[[omp::sequence (directive (for),
+  directive (unroll full), /* { dg-error {'full' clause is invalid here; turns loop into non-loop} } */
+  directive (unroll full))]]
+  for (int i = -300; i != 100; ++i)
+    dummy (i);
+
+[[omp::sequence (directive (for),
+  directive (unroll partial partial))]] /* { dg-error {too many 'partial' clauses} } */
+  for (int i = -300; i != 100; ++i)
+    dummy (i);
+
+[[omp::directive (unroll full full)]] /* { dg-error {too many 'full' clauses} } */
+  for (int i = -300; i != 100; ++i)
+    dummy (i);
+
+[[omp::sequence (directive (unroll partial),
+  directive (unroll))]] /* { dg-error {'#pragma omp unroll' without 'partial' clause is invalid here; turns loop into non-loop} } */
+  for (int i = -300; i != 100; ++i)
+    dummy (i);
+
+[[omp::sequence (directive (for),
+  directive (unroll))]] /* { dg-error {'#pragma omp unroll' without 'partial' clause is invalid here; turns loop into non-loop} } */
+  for (int i = -300; i != 100; ++i)
+    dummy (i);
+
+  int i;
+
+[[omp::sequence (directive (for),
+  directive (unroll foo))]] /* { dg-error {expected an OpenMP clause before 'foo'} } */
+  for (int i = -300; i != 100; ++i)
+    dummy (i);
+
+[[omp::directive (unroll partial(i))]]
+ /* { dg-error {the value of 'i' is not usable in a constant expression} "" { target c++ } .-1 } */
+ /* { dg-error {partial argument needs positive constant integer expression} "" { target *-*-* } .-2 } */
+  for (int i = -300; i != 100; ++i)
+    dummy (i);
+
+[[omp::directive (unroll parti)]] /* { dg-error {expected an OpenMP clause before 'parti'} } */
+  for (int i = -300; i != 100; ++i)
+    dummy (i);
+
+[[omp::sequence (directive (for),
+  directive (unroll partial(1)),
+  directive (unroll parti))]] /* { dg-error {expected an OpenMP clause before 'parti'} } */
+  for (int i = -300; i != 100; ++i)
+    dummy (i);
+
+[[omp::sequence (directive (for),
+  directive (unroll partial(1)),
+  directive (unroll parti))]] /* { dg-error {expected an OpenMP clause before 'parti'} } */
+  for (int i = -300; i != 100; ++i)
+    dummy (i);
+
+int sum = 0;
+[[omp::sequence (directive (parallel for reduction(+ : sum) collapse(2)),
+  directive (unroll partial(1)))]] /* { dg-error {nesting depth left after this transformation too low for loop collapse} } */
+  for (int i = 3; i < 10; ++i)
+    for (int j = -2; j < 7; ++j)
+      sum++;
+}
+
diff --git a/gcc/testsuite/g++.dg/gomp/loop-transforms/attrs-unroll-3.C b/gcc/testsuite/g++.dg/gomp/loop-transforms/attrs-unroll-3.C
new file mode 100644
index 00000000000..20c11c0f314
--- /dev/null
+++ b/gcc/testsuite/g++.dg/gomp/loop-transforms/attrs-unroll-3.C
@@ -0,0 +1,20 @@ 
+// { dg-do compile { target c++11 } } 
+
+/* { dg-additional-options "-fdump-tree-omp_transform_loops" }
+ * { dg-additional-options "-fdump-tree-original" } */
+
+extern void dummy (int);
+
+void
+test1 ()
+{
+  int i;
+  [[omp::directive (unroll full)]]
+  for (int i = 0; i < 10; i++)
+    dummy (i);
+}
+
+ /* Loop should be removed with 10 copies of the body remaining
+  * { dg-final { scan-tree-dump-times "dummy" 10 "omp_transform_loops" } }
+  * { dg-final { scan-tree-dump "#pragma omp loop_transform" "original" } }
+  * { dg-final { scan-tree-dump-not "#pragma omp" "omp_transform_loops" } } */
diff --git a/gcc/testsuite/g++.dg/gomp/loop-transforms/attrs-unroll-inner-1.C b/gcc/testsuite/g++.dg/gomp/loop-transforms/attrs-unroll-inner-1.C
new file mode 100644
index 00000000000..234753ad017
--- /dev/null
+++ b/gcc/testsuite/g++.dg/gomp/loop-transforms/attrs-unroll-inner-1.C
@@ -0,0 +1,15 @@ 
+// { dg-do compile { target c++11 } }
+
+extern void dummy (int);
+
+void
+test ()
+{
+
+  [[omp::directive (target parallel for collapse(2))]]
+  for (int i = -300; i != 100; ++i)
+    [[omp::directive (unroll, partial)]]
+    for (int j = 0; j != 100; ++j)
+      dummy (i);
+}
+
diff --git a/gcc/testsuite/g++.dg/gomp/loop-transforms/attrs-unroll-inner-2.C b/gcc/testsuite/g++.dg/gomp/loop-transforms/attrs-unroll-inner-2.C
new file mode 100644
index 00000000000..26cc665007d
--- /dev/null
+++ b/gcc/testsuite/g++.dg/gomp/loop-transforms/attrs-unroll-inner-2.C
@@ -0,0 +1,29 @@ 
+// { dg-do compile { target c++11 } }
+
+extern void dummy (int);
+
+void
+test ()
+{
+
+#pragma omp target parallel for collapse(2)
+  for (int i = -300; i != 100; ++i)
+    [[omp::directive (tile sizes (2))]]
+    for (int j = 0; j != 100; ++j)
+      dummy (i);
+
+  [[omp::directive (target parallel for collapse(2))]]
+  for (int i = -300; i != 100; ++i) /* { dg-error {not enough nested loops} } */
+    [[omp::directive (tile sizes(2, 3))]]
+    for (int j = 0; j != 100; ++j)
+      dummy (i);
+
+  [[omp::directive (target parallel for, collapse(2))]]
+  for (int i = -300; i != 100; ++i)
+    [[omp::directive (tile, sizes(2, 3))]]
+    for (int j = 0; j != 100; ++j)
+      for (int k = 0; k != 100; ++k)
+	dummy (i);
+}
+
+
diff --git a/gcc/testsuite/g++.dg/gomp/loop-transforms/attrs-unroll-inner-3.C b/gcc/testsuite/g++.dg/gomp/loop-transforms/attrs-unroll-inner-3.C
new file mode 100644
index 00000000000..46970b84a24
--- /dev/null
+++ b/gcc/testsuite/g++.dg/gomp/loop-transforms/attrs-unroll-inner-3.C
@@ -0,0 +1,71 @@ 
+// { dg-do compile { target c++11 } }
+
+// Test that omp::sequence is handled properly in a loop nest, but that
+// invalid attribute specifiers are rejected.
+
+extern void dummy (int);
+
+void
+test1 ()
+{
+  [[omp::directive (target parallel for collapse(2))]]
+  for (int i = -300; i != 100; ++i)
+    [[omp::sequence (directive (unroll, partial))]]  // OK
+    for (int j = 0; j != 100; ++j)
+      dummy (i);
+}
+
+void
+test2 ()
+{
+  [[omp::directive (target parallel for collapse(2))]]
+  for (int i = -300; i != 100; ++i)
+    [[omp::directive (masked)]]  // { dg-error "loop nest expected" }
+    for (int j = 0; j != 100; ++j)
+      dummy (i);
+}
+
+void
+test3 ()
+{
+  [[omp::directive (target parallel for collapse(2))]]
+  for (int i = -300; i != 100; ++i)
+    [[omp::directive (unroll, partial)]]  // { dg-error "attributes on the same statement" }
+    [[omp::directive (masked)]]
+    for (int j = 0; j != 100; ++j)
+      dummy (i);
+}
+
+void
+test4 ()
+{
+  [[omp::directive (target parallel for collapse(2))]]
+  for (int i = -300; i != 100; ++i)
+    [[omp::sequence (directive (unroll, partial),
+		     directive (masked))]]  // { dg-error "loop nest expected" }
+    for (int j = 0; j != 100; ++j)
+      dummy (i);
+}
+
+void
+test5 ()
+{
+  [[omp::directive (target parallel for collapse(2))]]
+  for (int i = -300; i != 100; ++i)
+    [[omp::sequence (directive (masked),  // { dg-error "loop nest expected" }
+		     directive (unroll, partial))]]
+    for (int j = 0; j != 100; ++j)
+      dummy (i);
+}
+
+void
+test6 ()
+{
+  [[omp::directive (target parallel for collapse(2))]]
+  for (int i = -300; i != 100; ++i)
+    [[omp::directive (unroll, partial),  // { dg-error "attributes on the same statement" }
+      omp::directive (masked)]]
+    for (int j = 0; j != 100; ++j)
+      dummy (i);
+}
+