diff mbox series

[WIP,4/4] OpenMP: C and C++ front-end support for loop transforms.

Message ID 20231001201021.785572-5-sandra@codesourcery.com
State New
Headers show
Series OpenMP: support for loop transformations | expand

Commit Message

Sandra Loosemore Oct. 1, 2023, 8:10 p.m. UTC
From: Frederik Harwath <frederik@codesourcery.com>

gcc/c-family/ChangeLog:
	* c-gimplify.cc (c_genericize_control_stmt): Handle OMP_LOOP_TRANS.
	* c-omp.cc (c_omp_directives): Uncomment entries for "tile" and
	"unroll".
	* c-pragma.cc (omp_pragmas_simd): Add tile and unroll.
	* c-pragma.h (enum pragma_kind): Add PRAGMA_OMP_TILE and
	PRAGMA_OMP_UNROLL.  Adjust PRAGMA_OMP__LAST__.
	(enum pragma_clause): Add PRAGMA_OMP_CLAUSE_FULL,
	PRAGMA_OMP_CLAUSE_PARTIAL, and PRAGMA_OMP_CLAUSE_TILE.

gcc/c/ChangeLog:
	* c-parser.cc (struct omp_for_parse_data): Add clauses field.
	(c_parser_skip_std_attribute_spec_seq): New.
	(check_omp_intervening_code): Reject imperfectly-nested loops
	with TILE directive.
	(c_parser_compound_statement_nostart): Handle loop transforms.
	(c_parser_omp_clause_name): Handle "full" and "partial".
	(check_no_duplicate_clause): Change to return a boolean error value.
	(c_parser_omp_clause_unroll_full): New.
	(c_parser_omp_clause_unroll_partial): New.
	(c_parser_omp_all_clauses): Handle PRAGMA_OMP_CLAUSE_FULL and
	PRAGMA_OMP_CLAUSE_PARTIAL.
	(c_parser_see_omp_loop_nest): New.
	(c_parser_omp_loop_nest): Error on standard attributes for
	consistency with C++.  Handle loop transformations.
	(c_parser_omp_for_loop): Handle loop transformations.
	(OMP_UNROLL_CLAUSE_MASK): Define.
	(c_parser_omp_tile_sizes): New.
	(c_parser_omp_loop_transform_clause): New.
	(c_parser_omp_nested_loop_transform_clauses): New.
	(c_parser_omp_tile): New.
	(c_parser_omp_unroll): New.
	(c_parser_omp_construct): Handle PRAGMA_OMP_TILE and
	PRAGMA_OMP_UNROLL.
	* c-typeck.cc (c_finish_omp_clauses): Handle OMP_CLAUSE_UNROLL_FULL
	and OMP_CLAUSE_UNROLL_PARTIAL.

gcc/cp/ChangeLog:
	* cp/cp-gimplify.cc (cp_gimplify_expr): Handle OMP_LOOP_TRANS.
	(cp_fold_r): Handle OMP_LOOP_TRANS.
	(cp_genericize_r): Handle OMP_LOOP_TRANS.
	* cp/parser.cc (check_omp_intervening_code): Reject
	imperfectly-nested loops with TILE directive.
	(cp_parser_statement_seq_opt): Handle loop transforms.
	(cp_parser_omp_clause_name): Handle "full" and "partial".
	(check_no_duplicate_clause): Change to return a boolean error value.
	(cp_parser_omp_clause_unroll_full): New.
	(cp_parser_omp_clause_unroll_partial): New.
	(cp_parser_omp_all_clauses): Consume comma even if first.  Handle
	PRAGMA_OMP_CLAUSE_PARTIAL and PRAGMA_OMP_CLAUSE_FULL.
	(cp_parser_see_omp_loop_nest): New.
	(cp_parser_omp_loop_nest): Handle standard attribute syntax and
	loop transforms.
	(cp_parser_omp_for_loop): Handle loop transforms.
	(cp_parser_omp_tile_sizes): New.
	(cp_parser_omp_tile): New.
	(OMP_UNROLL_CLAUSE_MASK): New.
	(cp_parser_omp_loop_transform_clause): New.
	(cp_parser_nested_loop_transform_clauses): New.
	(cp_parser_omp_unroll): New.
	(cp_parser_omp_construct): Handle PRAGMA_OMP_TILE and
	PRAGMA_OMP_UNROLL.
	(cp_parser_pragma): Handle PRAGMA_OMP_TILE and PRAGMA_OMP_UNROLL.
	* cp/pt.cc (tsubst_omp_clauses): Handle new loop transform clauses.
	(tsubst_expr): Handle OMP_LOOP_TRANS.
	* cp/semantics.cc (finish_omp_clauses): Handle OMP_CLAUSE_TILE,
	OMP_CLAUSE_UNROLL_FULL, OMP_CLAUSE_UNROLL_PARTIAL, and
	OMP_CLAUSE_UNROLL_NONE.

gcc/testsuite/ChangeLog:
	* c-c++-common/gomp/imperfect-attributes.c: Adjust for new
	attribute behavior.
	* c-c++-common/gomp/loop-transforms/imperfect-loop-nest.c: New.
	* c-c++-common/gomp/loop-transforms/tile-1.c: New.
	* c-c++-common/gomp/loop-transforms/tile-2.c: New.
	* c-c++-common/gomp/loop-transforms/tile-3.c: New.
	* c-c++-common/gomp/loop-transforms/tile-4.c: New.
	* c-c++-common/gomp/loop-transforms/tile-5.c: New.
	* c-c++-common/gomp/loop-transforms/tile-6.c: New.
	* c-c++-common/gomp/loop-transforms/tile-7.c: New.
	* c-c++-common/gomp/loop-transforms/tile-8.c: New.
	* c-c++-common/gomp/loop-transforms/unroll-1.c: New.
	* c-c++-common/gomp/loop-transforms/unroll-2.c: New.
	* c-c++-common/gomp/loop-transforms/unroll-3.c: New.
	* c-c++-common/gomp/loop-transforms/unroll-4.c: New.
	* c-c++-common/gomp/loop-transforms/unroll-5.c: New.
	* c-c++-common/gomp/loop-transforms/unroll-6.c: New.
	* c-c++-common/gomp/loop-transforms/unroll-7.c: New.
	* c-c++-common/gomp/loop-transforms/unroll-8.c: New.
	* c-c++-common/gomp/loop-transforms/unroll-inner-1.c: New.
	* c-c++-common/gomp/loop-transforms/unroll-inner-2.c: New.
	* c-c++-common/gomp/loop-transforms/unroll-non-rect-1.c: New.
	* c-c++-common/gomp/loop-transforms/unroll-non-rect-2.c: New.
	* c-c++-common/gomp/loop-transforms/unroll-simd-1.c: New.
	* g++.dg/gomp/attrs-4.C: Adjust expected error message.
	* g++.dg/gomp/for-1.C: Adjust expected error message.
	* g++.dg/gomp/loop-transforms/attrs-tile-1.C: New.
	* g++.dg/gomp/loop-transforms/attrs-tile-2.C: New.
	* g++.dg/gomp/loop-transforms/attrs-tile-3.C: New.
	* g++.dg/gomp/loop-transforms/attrs-unroll-1.C: New.
	* g++.dg/gomp/loop-transforms/attrs-unroll-2.C: New.
	* g++.dg/gomp/loop-transforms/attrs-unroll-3.C: New.
	* g++.dg/gomp/loop-transforms/attrs-unroll-inner-1.C: New.
	* g++.dg/gomp/loop-transforms/attrs-unroll-inner-2.C: New.
	* g++.dg/gomp/loop-transforms/attrs-unroll-inner-3.C: New.
	* g++.dg/gomp/loop-transforms/tile-1.h: New.
	* g++.dg/gomp/loop-transforms/tile-1a.C: New.
	* g++.dg/gomp/loop-transforms/tile-1b.C: New.
	* g++.dg/gomp/loop-transforms/unroll-1.C: New.
	* g++.dg/gomp/loop-transforms/unroll-2.C: New.
	* g++.dg/gomp/loop-transforms/unroll-3.C: New.
	* g++.dg/gomp/pr94512.C: Adjust expected error message.
	* gcc.dg/gomp/for-1.c: Adjust expected error message.
	* gcc.dg/gomp/for-11.c: Adjust expected error message.

libgomp/ChangeLog:
	* testsuite/libgomp.c++/loop-transforms/matrix-no-directive-unroll-full-1.C: New.
	* testsuite/libgomp.c++/loop-transforms/tile-2.C: New.
	* testsuite/libgomp.c++/loop-transforms/tile-3.C: New.
	* testsuite/libgomp.c++/loop-transforms/unroll-1.C: New.
	* testsuite/libgomp.c++/loop-transforms/unroll-2.C: New.
	* testsuite/libgomp.c++/loop-transforms/unroll-full-tile.C: New.
	* testsuite/libgomp.c-c++-common/imperfect-transform-1.c: New.
	* testsuite/libgomp.c-c++-common/imperfect-transform-2.c: New.
	* testsuite/libgomp.c-c++-common/loop-transforms/matrix-1.h: New.
	* testsuite/libgomp.c-c++-common/loop-transforms/matrix-constant-iter.h: New.
	* testsuite/libgomp.c-c++-common/loop-transforms/matrix-helper.h: New.
	* testsuite/libgomp.c-c++-common/loop-transforms/matrix-no-directive-1.c: New.
	* testsuite/libgomp.c-c++-common/loop-transforms/matrix-no-directive-unroll-full-1.c: New.
	* testsuite/libgomp.c-c++-common/loop-transforms/matrix-omp-distribute-parallel-for-1.c: New.
	* testsuite/libgomp.c-c++-common/loop-transforms/matrix-omp-for-1.c: New.
	* testsuite/libgomp.c-c++-common/loop-transforms/matrix-omp-parallel-for-1.c: New.
	* testsuite/libgomp.c-c++-common/loop-transforms/matrix-omp-parallel-masked-taskloop-1.c: New.
	* testsuite/libgomp.c-c++-common/loop-transforms/matrix-omp-parallel-masked-taskloop-simd-1.c: New.
	* testsuite/libgomp.c-c++-common/loop-transforms/matrix-omp-target-parallel-for-1.c: New.
	* testsuite/libgomp.c-c++-common/loop-transforms/matrix-omp-target-teams-distribute-parallel-for-1.c: New.
	* testsuite/libgomp.c-c++-common/loop-transforms/matrix-omp-taskloop-1.c: New.
	* testsuite/libgomp.c-c++-common/loop-transforms/matrix-omp-teams-distribute-parallel-for-1.c: New.
	* testsuite/libgomp.c-c++-common/loop-transforms/matrix-simd-1.c: New.
	* testsuite/libgomp.c-c++-common/loop-transforms/matrix-transform-variants-1.h: New.
	* testsuite/libgomp.c-c++-common/loop-transforms/unroll-1.c: New.
	* testsuite/libgomp.c-c++-common/loop-transforms/unroll-non-rect-1.c: New.
	* testsuite/libgomp.c-c++-common/target-imperfect-transform-1.c: New.
	* testsuite/libgomp.c-c++-common/target-imperfect-transform-2.c: New.

Co-Authored-By: Sandra Loosemore <sandra@codesourcery.com>
---
 gcc/c-family/c-gimplify.cc                    |   1 +
 gcc/c-family/c-omp.cc                         |  10 +-
 gcc/c-family/c-pragma.cc                      |   2 +
 gcc/c-family/c-pragma.h                       |   7 +-
 gcc/c/c-parser.cc                             | 515 ++++++++++++++++-
 gcc/c/c-typeck.cc                             |   8 +
 gcc/cp/cp-gimplify.cc                         |   3 +
 gcc/cp/parser.cc                              | 529 +++++++++++++++++-
 gcc/cp/pt.cc                                  |  13 +
 gcc/cp/semantics.cc                           |  95 ++++
 .../c-c++-common/gomp/imperfect-attributes.c  |  18 +-
 .../loop-transforms/imperfect-loop-nest.c     |  11 +
 .../gomp/loop-transforms/tile-1.c             | 160 ++++++
 .../gomp/loop-transforms/tile-2.c             | 179 ++++++
 .../gomp/loop-transforms/tile-3.c             | 109 ++++
 .../gomp/loop-transforms/tile-4.c             | 322 +++++++++++
 .../gomp/loop-transforms/tile-5.c             | 150 +++++
 .../gomp/loop-transforms/tile-6.c             |  34 ++
 .../gomp/loop-transforms/tile-7.c             |  31 +
 .../gomp/loop-transforms/tile-8.c             |  40 ++
 .../gomp/loop-transforms/unroll-1.c           | 133 +++++
 .../gomp/loop-transforms/unroll-2.c           |  95 ++++
 .../gomp/loop-transforms/unroll-3.c           |  18 +
 .../gomp/loop-transforms/unroll-4.c           |  19 +
 .../gomp/loop-transforms/unroll-5.c           |  19 +
 .../gomp/loop-transforms/unroll-6.c           |  20 +
 .../gomp/loop-transforms/unroll-7.c           | 144 +++++
 .../gomp/loop-transforms/unroll-8.c           |  76 +++
 .../gomp/loop-transforms/unroll-inner-1.c     |  15 +
 .../gomp/loop-transforms/unroll-inner-2.c     |  29 +
 .../gomp/loop-transforms/unroll-non-rect-1.c  |  37 ++
 .../gomp/loop-transforms/unroll-non-rect-2.c  |  22 +
 .../gomp/loop-transforms/unroll-simd-1.c      |  84 +++
 gcc/testsuite/g++.dg/gomp/attrs-4.C           |   2 +-
 gcc/testsuite/g++.dg/gomp/for-1.C             |   2 +-
 .../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 +++
 .../g++.dg/gomp/loop-transforms/tile-1.h      |  27 +
 .../g++.dg/gomp/loop-transforms/tile-1a.C     |  27 +
 .../g++.dg/gomp/loop-transforms/tile-1b.C     |  27 +
 .../g++.dg/gomp/loop-transforms/unroll-1.C    |  42 ++
 .../g++.dg/gomp/loop-transforms/unroll-2.C    |  47 ++
 .../g++.dg/gomp/loop-transforms/unroll-3.C    |  37 ++
 gcc/testsuite/g++.dg/gomp/pr94512.C           |   2 +-
 gcc/testsuite/gcc.dg/gomp/for-1.c             |   2 +-
 gcc/testsuite/gcc.dg/gomp/for-11.c            |   2 +-
 .../matrix-no-directive-unroll-full-1.C       |  13 +
 .../libgomp.c++/loop-transforms/tile-2.C      |  69 +++
 .../libgomp.c++/loop-transforms/tile-3.C      |  28 +
 .../libgomp.c++/loop-transforms/unroll-1.C    |  73 +++
 .../libgomp.c++/loop-transforms/unroll-2.C    |  34 ++
 .../loop-transforms/unroll-full-tile.C        |  84 +++
 .../imperfect-transform-1.c                   |  79 +++
 .../imperfect-transform-2.c                   |  79 +++
 .../loop-transforms/matrix-1.h                |  70 +++
 .../loop-transforms/matrix-constant-iter.h    |  71 +++
 .../loop-transforms/matrix-helper.h           |  19 +
 .../loop-transforms/matrix-no-directive-1.c   |  11 +
 .../matrix-no-directive-unroll-full-1.c       |  13 +
 .../matrix-omp-distribute-parallel-for-1.c    |   8 +
 .../loop-transforms/matrix-omp-for-1.c        |  13 +
 .../matrix-omp-parallel-for-1.c               |  13 +
 .../matrix-omp-parallel-masked-taskloop-1.c   |   8 +
 ...trix-omp-parallel-masked-taskloop-simd-1.c |   8 +
 .../matrix-omp-target-parallel-for-1.c        |  15 +
 ...p-target-teams-distribute-parallel-for-1.c |  10 +
 .../loop-transforms/matrix-omp-taskloop-1.c   |   8 +
 ...trix-omp-teams-distribute-parallel-for-1.c |   8 +
 .../loop-transforms/matrix-simd-1.c           |   8 +
 .../matrix-transform-variants-1.h             | 191 +++++++
 .../loop-transforms/unroll-1.c                |  78 +++
 .../loop-transforms/unroll-non-rect-1.c       | 131 +++++
 .../target-imperfect-transform-1.c            |  82 +++
 .../target-imperfect-transform-2.c            |  82 +++
 81 files changed, 5218 insertions(+), 53 deletions(-)
 create mode 100644 gcc/testsuite/c-c++-common/gomp/loop-transforms/imperfect-loop-nest.c
 create mode 100644 gcc/testsuite/c-c++-common/gomp/loop-transforms/tile-1.c
 create mode 100644 gcc/testsuite/c-c++-common/gomp/loop-transforms/tile-2.c
 create mode 100644 gcc/testsuite/c-c++-common/gomp/loop-transforms/tile-3.c
 create mode 100644 gcc/testsuite/c-c++-common/gomp/loop-transforms/tile-4.c
 create mode 100644 gcc/testsuite/c-c++-common/gomp/loop-transforms/tile-5.c
 create mode 100644 gcc/testsuite/c-c++-common/gomp/loop-transforms/tile-6.c
 create mode 100644 gcc/testsuite/c-c++-common/gomp/loop-transforms/tile-7.c
 create mode 100644 gcc/testsuite/c-c++-common/gomp/loop-transforms/tile-8.c
 create mode 100644 gcc/testsuite/c-c++-common/gomp/loop-transforms/unroll-1.c
 create mode 100644 gcc/testsuite/c-c++-common/gomp/loop-transforms/unroll-2.c
 create mode 100644 gcc/testsuite/c-c++-common/gomp/loop-transforms/unroll-3.c
 create mode 100644 gcc/testsuite/c-c++-common/gomp/loop-transforms/unroll-4.c
 create mode 100644 gcc/testsuite/c-c++-common/gomp/loop-transforms/unroll-5.c
 create mode 100644 gcc/testsuite/c-c++-common/gomp/loop-transforms/unroll-6.c
 create mode 100644 gcc/testsuite/c-c++-common/gomp/loop-transforms/unroll-7.c
 create mode 100644 gcc/testsuite/c-c++-common/gomp/loop-transforms/unroll-8.c
 create mode 100644 gcc/testsuite/c-c++-common/gomp/loop-transforms/unroll-inner-1.c
 create mode 100644 gcc/testsuite/c-c++-common/gomp/loop-transforms/unroll-inner-2.c
 create mode 100644 gcc/testsuite/c-c++-common/gomp/loop-transforms/unroll-non-rect-1.c
 create mode 100644 gcc/testsuite/c-c++-common/gomp/loop-transforms/unroll-non-rect-2.c
 create mode 100644 gcc/testsuite/c-c++-common/gomp/loop-transforms/unroll-simd-1.c
 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
 create mode 100644 gcc/testsuite/g++.dg/gomp/loop-transforms/tile-1.h
 create mode 100644 gcc/testsuite/g++.dg/gomp/loop-transforms/tile-1a.C
 create mode 100644 gcc/testsuite/g++.dg/gomp/loop-transforms/tile-1b.C
 create mode 100644 gcc/testsuite/g++.dg/gomp/loop-transforms/unroll-1.C
 create mode 100644 gcc/testsuite/g++.dg/gomp/loop-transforms/unroll-2.C
 create mode 100644 gcc/testsuite/g++.dg/gomp/loop-transforms/unroll-3.C
 create mode 100644 libgomp/testsuite/libgomp.c++/loop-transforms/matrix-no-directive-unroll-full-1.C
 create mode 100644 libgomp/testsuite/libgomp.c++/loop-transforms/tile-2.C
 create mode 100644 libgomp/testsuite/libgomp.c++/loop-transforms/tile-3.C
 create mode 100644 libgomp/testsuite/libgomp.c++/loop-transforms/unroll-1.C
 create mode 100644 libgomp/testsuite/libgomp.c++/loop-transforms/unroll-2.C
 create mode 100644 libgomp/testsuite/libgomp.c++/loop-transforms/unroll-full-tile.C
 create mode 100644 libgomp/testsuite/libgomp.c-c++-common/imperfect-transform-1.c
 create mode 100644 libgomp/testsuite/libgomp.c-c++-common/imperfect-transform-2.c
 create mode 100644 libgomp/testsuite/libgomp.c-c++-common/loop-transforms/matrix-1.h
 create mode 100644 libgomp/testsuite/libgomp.c-c++-common/loop-transforms/matrix-constant-iter.h
 create mode 100644 libgomp/testsuite/libgomp.c-c++-common/loop-transforms/matrix-helper.h
 create mode 100644 libgomp/testsuite/libgomp.c-c++-common/loop-transforms/matrix-no-directive-1.c
 create mode 100644 libgomp/testsuite/libgomp.c-c++-common/loop-transforms/matrix-no-directive-unroll-full-1.c
 create mode 100644 libgomp/testsuite/libgomp.c-c++-common/loop-transforms/matrix-omp-distribute-parallel-for-1.c
 create mode 100644 libgomp/testsuite/libgomp.c-c++-common/loop-transforms/matrix-omp-for-1.c
 create mode 100644 libgomp/testsuite/libgomp.c-c++-common/loop-transforms/matrix-omp-parallel-for-1.c
 create mode 100644 libgomp/testsuite/libgomp.c-c++-common/loop-transforms/matrix-omp-parallel-masked-taskloop-1.c
 create mode 100644 libgomp/testsuite/libgomp.c-c++-common/loop-transforms/matrix-omp-parallel-masked-taskloop-simd-1.c
 create mode 100644 libgomp/testsuite/libgomp.c-c++-common/loop-transforms/matrix-omp-target-parallel-for-1.c
 create mode 100644 libgomp/testsuite/libgomp.c-c++-common/loop-transforms/matrix-omp-target-teams-distribute-parallel-for-1.c
 create mode 100644 libgomp/testsuite/libgomp.c-c++-common/loop-transforms/matrix-omp-taskloop-1.c
 create mode 100644 libgomp/testsuite/libgomp.c-c++-common/loop-transforms/matrix-omp-teams-distribute-parallel-for-1.c
 create mode 100644 libgomp/testsuite/libgomp.c-c++-common/loop-transforms/matrix-simd-1.c
 create mode 100644 libgomp/testsuite/libgomp.c-c++-common/loop-transforms/matrix-transform-variants-1.h
 create mode 100644 libgomp/testsuite/libgomp.c-c++-common/loop-transforms/unroll-1.c
 create mode 100644 libgomp/testsuite/libgomp.c-c++-common/loop-transforms/unroll-non-rect-1.c
 create mode 100644 libgomp/testsuite/libgomp.c-c++-common/target-imperfect-transform-1.c
 create mode 100644 libgomp/testsuite/libgomp.c-c++-common/target-imperfect-transform-2.c
diff mbox series

Patch

diff --git a/gcc/c-family/c-gimplify.cc b/gcc/c-family/c-gimplify.cc
index 17b0610a89f..35d3a6e10d6 100644
--- a/gcc/c-family/c-gimplify.cc
+++ b/gcc/c-family/c-gimplify.cc
@@ -508,6 +508,7 @@  c_genericize_control_stmt (tree *stmt_p, int *walk_subtrees, void *data,
     case OMP_DISTRIBUTE:
     case OMP_LOOP:
     case OMP_TASKLOOP:
+    case OMP_LOOP_TRANS:
     case OACC_LOOP:
       genericize_omp_for_stmt (stmt_p, walk_subtrees, data, func, lh);
       break;
diff --git a/gcc/c-family/c-omp.cc b/gcc/c-family/c-omp.cc
index 5de3b77c450..980c6fc0867 100644
--- a/gcc/c-family/c-omp.cc
+++ b/gcc/c-family/c-omp.cc
@@ -3359,14 +3359,14 @@  const struct c_omp_directive c_omp_directives[] = {
     C_OMP_DIR_STANDALONE, false },
   { "taskyield", nullptr, nullptr, PRAGMA_OMP_TASKYIELD,
     C_OMP_DIR_STANDALONE, false },
-  /* { "tile", nullptr, nullptr, PRAGMA_OMP_TILE,
-    C_OMP_DIR_CONSTRUCT, false },  */
+  { "tile", nullptr, nullptr, PRAGMA_OMP_TILE,
+    C_OMP_DIR_CONSTRUCT, false },
   { "teams", nullptr, nullptr, PRAGMA_OMP_TEAMS,
     C_OMP_DIR_CONSTRUCT, true },
   { "threadprivate", nullptr, nullptr, PRAGMA_OMP_THREADPRIVATE,
-    C_OMP_DIR_DECLARATIVE, false }
-  /* { "unroll", nullptr, nullptr, PRAGMA_OMP_UNROLL,
-    C_OMP_DIR_CONSTRUCT, false },  */
+    C_OMP_DIR_DECLARATIVE, false },
+ { "unroll", nullptr, nullptr, PRAGMA_OMP_UNROLL,
+    C_OMP_DIR_CONSTRUCT, false },
 };
 
 /* Find (non-combined/composite) OpenMP directive (if any) which starts
diff --git a/gcc/c-family/c-pragma.cc b/gcc/c-family/c-pragma.cc
index 293311dd4ce..22e5448331e 100644
--- a/gcc/c-family/c-pragma.cc
+++ b/gcc/c-family/c-pragma.cc
@@ -1550,6 +1550,8 @@  static const struct omp_pragma_def omp_pragmas_simd[] = {
   { "target", PRAGMA_OMP_TARGET },
   { "taskloop", PRAGMA_OMP_TASKLOOP },
   { "teams", PRAGMA_OMP_TEAMS },
+  { "tile", PRAGMA_OMP_TILE },
+  { "unroll", PRAGMA_OMP_UNROLL },
 };
 
 void
diff --git a/gcc/c-family/c-pragma.h b/gcc/c-family/c-pragma.h
index 603c5151978..703154d224e 100644
--- a/gcc/c-family/c-pragma.h
+++ b/gcc/c-family/c-pragma.h
@@ -81,8 +81,10 @@  enum pragma_kind {
   PRAGMA_OMP_TASKYIELD,
   PRAGMA_OMP_THREADPRIVATE,
   PRAGMA_OMP_TEAMS,
+  PRAGMA_OMP_TILE,
+  PRAGMA_OMP_UNROLL,
   /* PRAGMA_OMP__LAST_ should be equal to the last PRAGMA_OMP_* code.  */
-  PRAGMA_OMP__LAST_ = PRAGMA_OMP_TEAMS,
+  PRAGMA_OMP__LAST_ = PRAGMA_OMP_UNROLL,
 
   PRAGMA_GCC_PCH_PREPROCESS,
   PRAGMA_IVDEP,
@@ -119,6 +121,7 @@  enum pragma_omp_clause {
   PRAGMA_OMP_CLAUSE_FIRSTPRIVATE,
   PRAGMA_OMP_CLAUSE_FOR,
   PRAGMA_OMP_CLAUSE_FROM,
+  PRAGMA_OMP_CLAUSE_FULL,
   PRAGMA_OMP_CLAUSE_GRAINSIZE,
   PRAGMA_OMP_CLAUSE_HAS_DEVICE_ADDR,
   PRAGMA_OMP_CLAUSE_HINT,
@@ -141,6 +144,7 @@  enum pragma_omp_clause {
   PRAGMA_OMP_CLAUSE_ORDER,
   PRAGMA_OMP_CLAUSE_ORDERED,
   PRAGMA_OMP_CLAUSE_PARALLEL,
+  PRAGMA_OMP_CLAUSE_PARTIAL,
   PRAGMA_OMP_CLAUSE_PRIORITY,
   PRAGMA_OMP_CLAUSE_PRIVATE,
   PRAGMA_OMP_CLAUSE_PROC_BIND,
@@ -155,6 +159,7 @@  enum pragma_omp_clause {
   PRAGMA_OMP_CLAUSE_TASKGROUP,
   PRAGMA_OMP_CLAUSE_THREAD_LIMIT,
   PRAGMA_OMP_CLAUSE_THREADS,
+  PRAGMA_OMP_CLAUSE_TILE,
   PRAGMA_OMP_CLAUSE_TO,
   PRAGMA_OMP_CLAUSE_UNIFORM,
   PRAGMA_OMP_CLAUSE_UNTIED,
diff --git a/gcc/c/c-parser.cc b/gcc/c/c-parser.cc
index e6342d2188d..45f59ded1a7 100644
--- a/gcc/c/c-parser.cc
+++ b/gcc/c/c-parser.cc
@@ -1554,6 +1554,7 @@  static tree objc_foreach_break_label, objc_foreach_continue_label;
   */
 struct omp_for_parse_data {
   enum tree_code code;
+  tree clauses;
   tree declv, condv, incrv, initv;
   tree pre_body;
   tree bindings;
@@ -1662,7 +1663,10 @@  static void c_parser_omp_threadprivate (c_parser *);
 static void c_parser_omp_barrier (c_parser *);
 static void c_parser_omp_depobj (c_parser *);
 static void c_parser_omp_flush (c_parser *);
+static bool c_parser_see_omp_loop_nest (c_parser *, enum tree_code, bool);
 static tree c_parser_omp_loop_nest (c_parser *, bool *);
+static int c_parser_omp_nested_loop_transform_clauses (c_parser *, tree &, int,
+						       int, const char *);
 static tree c_parser_omp_for_loop (location_t, c_parser *, enum tree_code,
 				   tree, tree *, bool *);
 static void c_parser_omp_taskwait (c_parser *);
@@ -5720,6 +5724,37 @@  c_parser_nth_token_starts_std_attributes (c_parser *parser, unsigned int n)
   return token->type == CPP_CLOSE_SQUARE;
 }
 
+/* Skip standard attribute tokens starting at Nth token (with 1 as the
+   next token), return index of the first token after the standard
+   attribute tokens, or N on failure.  */
+
+static size_t
+c_parser_skip_std_attribute_spec_seq (c_parser *parser, size_t n)
+{
+  size_t orig_n = n;
+  while (true)
+    {
+      if (c_parser_peek_nth_token_raw (parser, n)->type == CPP_OPEN_SQUARE
+	  && (c_parser_peek_nth_token_raw (parser, n + 1)->type
+	      == CPP_OPEN_SQUARE))
+	{
+	  unsigned int m = n + 2;
+	  if (!c_parser_check_balanced_raw_token_sequence (parser, &m))
+	    return orig_n;
+	  c_token *token = c_parser_peek_nth_token_raw (parser, m);
+	  if (token->type != CPP_CLOSE_SQUARE)
+	    return orig_n;
+	  token = c_parser_peek_nth_token_raw (parser, m + 1);
+	  if (token->type != CPP_CLOSE_SQUARE)
+	    return orig_n;
+	  n = m + 2;
+	}
+      else
+	break;
+    }
+  return n;
+}
+
 static tree
 c_parser_std_attribute_specifier_sequence (c_parser *parser)
 {
@@ -6305,7 +6340,20 @@  check_omp_intervening_code (c_parser *parser)
 		    "%<reduction%> %<inscan%> clause");
 	  omp_for_parse_state->perfect_nesting_fail = true;
 	}
-      /* TODO: Also reject loops with TILE directive.  */
+      else
+	{
+	  tree c = omp_find_clause (omp_for_parse_state->clauses,
+				    OMP_CLAUSE_TILE);
+	  if (c &&
+	      ((int) tree_to_uhwi (OMP_CLAUSE_TRANSFORM_LEVEL (c))
+	       <= omp_for_parse_state->depth))
+	    {
+	      error_at (omp_for_parse_state->for_loc,
+			"inner loops must be perfectly nested "
+			"with %<tile%> directive");
+	      omp_for_parse_state->perfect_nesting_fail = true;
+	    }
+	}
       if (omp_for_parse_state->perfect_nesting_fail)
 	omp_for_parse_state->fail = true;
     }
@@ -6412,7 +6460,9 @@  c_parser_compound_statement_nostart (c_parser *parser)
       if (in_omp_loop_block && !last_label)
 	{
 	  if (want_nested_loop
-	      && c_parser_next_token_is_keyword (parser, RID_FOR))
+	      && c_parser_see_omp_loop_nest (parser,
+					     omp_for_parse_state->code,
+					     false))
 	    {
 	      /* Found the next nested loop.  If there were intervening
 		 code statements collected before now, wrap them in an
@@ -13953,6 +14003,8 @@  c_parser_omp_clause_name (c_parser *parser)
 	    result = PRAGMA_OMP_CLAUSE_FIRSTPRIVATE;
 	  else if (!strcmp ("from", p))
 	    result = PRAGMA_OMP_CLAUSE_FROM;
+	  else if (!strcmp ("full", p))
+	    result = PRAGMA_OMP_CLAUSE_FULL;
 	  break;
 	case 'g':
 	  if (!strcmp ("gang", p))
@@ -14027,6 +14079,8 @@  c_parser_omp_clause_name (c_parser *parser)
 	case 'p':
 	  if (!strcmp ("parallel", p))
 	    result = PRAGMA_OMP_CLAUSE_PARALLEL;
+	  else if (!strcmp ("partial", p))
+	    result = PRAGMA_OMP_CLAUSE_PARTIAL;
 	  else if (!strcmp ("present", p))
 	    result = PRAGMA_OACC_CLAUSE_PRESENT;
 	  /* As of OpenACC 2.5, these are now aliases of the non-present_or
@@ -14121,12 +14175,15 @@  c_parser_omp_clause_name (c_parser *parser)
 
 /* Validate that a clause of the given type does not already exist.  */
 
-static void
+static bool
 check_no_duplicate_clause (tree clauses, enum omp_clause_code code,
 			   const char *name)
 {
-  if (tree c = omp_find_clause (clauses, code))
+  tree c = omp_find_clause (clauses, code);
+  if (c)
     error_at (OMP_CLAUSE_LOCATION (c), "too many %qs clauses", name);
+
+  return c == NULL_TREE;
 }
 
 /* OpenACC 2.0
@@ -17993,6 +18050,67 @@  c_parser_omp_clause_uniform (c_parser *parser, tree list)
   return list;
 }
 
+/* OpenMP 5.1
+   full */
+
+static tree
+c_parser_omp_clause_unroll_full (c_parser *parser, tree list)
+{
+  if (!check_no_duplicate_clause (list, OMP_CLAUSE_UNROLL_FULL, "full"))
+    return list;
+
+  location_t loc = c_parser_peek_token (parser)->location;
+  tree c = build_omp_clause (loc, OMP_CLAUSE_UNROLL_FULL);
+  OMP_CLAUSE_TRANSFORM_LEVEL (c) = build_int_cst (unsigned_type_node, 0);
+  OMP_CLAUSE_CHAIN (c) = list;
+  return c;
+}
+
+/* OpenMP 5.1
+   partial ( constant-expression ) */
+
+static tree
+c_parser_omp_clause_unroll_partial (c_parser *parser, tree list)
+{
+  if (!check_no_duplicate_clause (list, OMP_CLAUSE_UNROLL_PARTIAL, "partial"))
+    return list;
+
+  tree c, num = error_mark_node;
+  HOST_WIDE_INT n;
+  location_t loc;
+
+  loc = c_parser_peek_token (parser)->location;
+  c = build_omp_clause (loc, OMP_CLAUSE_UNROLL_PARTIAL);
+  OMP_CLAUSE_UNROLL_PARTIAL_EXPR (c) = NULL_TREE;
+  OMP_CLAUSE_TRANSFORM_LEVEL (c) = build_int_cst (unsigned_type_node, 0);
+  OMP_CLAUSE_CHAIN (c) = list;
+
+  if (!c_parser_next_token_is (parser, CPP_OPEN_PAREN))
+    return c;
+
+  matching_parens parens;
+  parens.consume_open (parser);
+  num = c_parser_expr_no_commas (parser, NULL).value;
+  parens.skip_until_found_close (parser);
+
+  if (num == error_mark_node)
+    return list;
+
+  mark_exp_read (num);
+  num = c_fully_fold (num, false, NULL);
+  if (!INTEGRAL_TYPE_P (TREE_TYPE (num)) || !tree_fits_shwi_p (num)
+      || (n = tree_to_shwi (num)) <= 0 || (int)n != n)
+    {
+      error_at (loc,
+		"partial argument needs positive constant integer expression");
+      return list;
+   }
+
+  OMP_CLAUSE_UNROLL_PARTIAL_EXPR (c) = num;
+
+  return c;
+}
+
 /* OpenMP 5.0:
    detach ( event-handle ) */
 
@@ -18589,6 +18707,14 @@  c_parser_omp_all_clauses (c_parser *parser, omp_clause_mask mask,
 					    clauses);
 	  c_name = "enter";
 	  break;
+	case PRAGMA_OMP_CLAUSE_FULL:
+	  c_name = "full";
+	  clauses = c_parser_omp_clause_unroll_full (parser, clauses);
+	  break;
+	case PRAGMA_OMP_CLAUSE_PARTIAL:
+	  c_name = "partial";
+	  clauses = c_parser_omp_clause_unroll_partial (parser, clauses);
+	  break;
 	default:
 	  c_parser_error (parser, "expected an OpenMP clause");
 	  goto saw_error;
@@ -20848,6 +20974,44 @@  c_parser_omp_scan_loop_body (c_parser *parser, bool open_brace_parsed)
 }
 
 
+/* Check that the next token starts a loop nest.  Return true if yes,
+   otherwise diagnose an error if ERROR_P is true, and return false.  */
+static bool
+c_parser_see_omp_loop_nest (c_parser *parser, enum tree_code code,
+			    bool error_p)
+{
+  if (code == OACC_LOOP)
+    {
+      if (c_parser_next_token_is_keyword (parser, RID_FOR))
+	return true;
+      if (error_p)
+	c_parser_error (parser, "for statement expected");
+    }
+  else
+    {
+      if (c_parser_next_token_is_keyword (parser, RID_FOR)
+	  || c_parser_peek_token (parser)->pragma_kind == PRAGMA_OMP_UNROLL
+	  || c_parser_peek_token (parser)->pragma_kind == PRAGMA_OMP_TILE)
+	return true;
+
+      /* For consistency with C++, treat standard attributes followed
+	 by RID_FOR as a loop nest, but diagnose unknown attributes as
+	 an error in c_parser_omp_loop_nest.  */
+      size_t n = c_parser_skip_std_attribute_spec_seq (parser, 1);
+      c_token *token = c_parser_peek_nth_token_raw (parser, n);
+      /* TOKEN is a raw token that hasn't been converted to a keyword yet,
+	 we have to do the lookup explicitly.  */
+      if (token->type == CPP_NAME
+	  && C_IS_RESERVED_WORD (token->value)
+	  && C_RID_CODE (token->value) == RID_FOR)
+	return true;
+      if (error_p)
+	c_parser_error (parser,	"loop nest expected");
+    }
+
+  return false;
+}
+
 /* This function parses a single level of a loop nest, invoking itself
    recursively if necessary.
 
@@ -20883,6 +21047,51 @@  c_parser_omp_loop_nest (c_parser *parser, bool *if_p)
   gcc_assert (omp_for_parse_state);
   int depth = omp_for_parse_state->depth;
 
+  /* Reject non-empty standard attributes with an error.  C++ allows OpenMP
+     directives to be specified with attribute syntax, but C does not.  */
+  if (c_parser_nth_token_starts_std_attributes (parser, 1))
+    {
+      tree std_attrs = c_parser_std_attribute_specifier_sequence (parser);
+      if (std_attrs)
+	error_at (c_parser_peek_token (parser)->location,
+		  "attributes are not allowed on %<for%> in loop nest");
+    }
+
+  /* Handle loop transformations first.  Note that when we get here
+     omp_for_parse_state->depth has already been incremented to indicate
+     the depth of the *next* loop, not the level of the loop body the
+     transformation directive appears in.  */
+  if (c_parser_peek_token (parser)->pragma_kind == PRAGMA_OMP_UNROLL
+      || c_parser_peek_token (parser)->pragma_kind == PRAGMA_OMP_TILE)
+    {
+      int count = omp_for_parse_state->count;
+      int more = c_parser_omp_nested_loop_transform_clauses (
+		   parser, omp_for_parse_state->clauses,
+		   depth, count - depth, "loop collapse");
+      if (depth + more > count)
+	{
+	  count = depth + more;
+	  omp_for_parse_state->count = count;
+	  omp_for_parse_state->declv
+	    = grow_tree_vec (omp_for_parse_state->declv, count);
+	  omp_for_parse_state->initv
+	    = grow_tree_vec (omp_for_parse_state->initv, count);
+	  omp_for_parse_state->condv
+	    = grow_tree_vec (omp_for_parse_state->condv, count);
+	  omp_for_parse_state->incrv
+	    = grow_tree_vec (omp_for_parse_state->incrv, count);
+	}
+      if (c_parser_see_omp_loop_nest (parser, omp_for_parse_state->code,
+				      true))
+	return c_parser_omp_loop_nest (parser, if_p);
+      else
+	{
+	  /* FIXME: Better error recovery here?  */
+	  omp_for_parse_state->fail = true;
+	  return NULL_TREE;
+	}
+    }
+
   /* We have already matched the FOR token but not consumed it yet.  */
   loc = c_parser_peek_token (parser)->location;
   gcc_assert (c_parser_next_token_is_keyword (parser, RID_FOR));
@@ -21017,7 +21226,9 @@  c_parser_omp_loop_nest (c_parser *parser, bool *if_p)
 parse_next:
   moreloops = depth < omp_for_parse_state->count - 1;
   omp_for_parse_state->want_nested_loop = moreloops;
-  if (moreloops && c_parser_next_token_is_keyword (parser, RID_FOR))
+  if (moreloops
+      && c_parser_see_omp_loop_nest (parser, omp_for_parse_state->code,
+				     false))
     {
       omp_for_parse_state->depth++;
       body = c_parser_omp_loop_nest (parser, if_p);
@@ -21129,7 +21340,7 @@  c_parser_omp_for_loop (location_t loc, c_parser *parser, enum tree_code code,
   tree ret = NULL_TREE;
   tree ordered_cl = NULL_TREE;
   int i, collapse = 1, ordered = 0, count;
-  bool tiling = false;
+  bool oacc_tiling = false;
   bool inscan = false;
   struct omp_for_parse_data data;
   struct omp_for_parse_data *save_data = parser->omp_for_parse_state;
@@ -21139,7 +21350,7 @@  c_parser_omp_for_loop (location_t loc, c_parser *parser, enum tree_code code,
       collapse = tree_to_shwi (OMP_CLAUSE_COLLAPSE_EXPR (cl));
     else if (OMP_CLAUSE_CODE (cl) == OMP_CLAUSE_OACC_TILE)
       {
-	tiling = true;
+	oacc_tiling = true;
 	collapse = list_length (OMP_CLAUSE_OACC_TILE_LIST (cl));
       }
     else if (OMP_CLAUSE_CODE (cl) == OMP_CLAUSE_ORDERED
@@ -21162,15 +21373,29 @@  c_parser_omp_for_loop (location_t loc, c_parser *parser, enum tree_code code,
       ordered = collapse;
     }
 
-  gcc_assert (tiling || (collapse >= 1 && ordered >= 0));
-  count = ordered ? ordered : collapse;
+  c_parser_omp_nested_loop_transform_clauses (parser, clauses, 0, collapse,
+					      "loop collapse");
 
-  if (!c_parser_next_token_is_keyword (parser, RID_FOR))
+  /* Find the depth of the loop nest affected by "omp tile"
+     directives. There can be several such directives, but the tiling
+     depth of the outer ones may not be larger than the depth of the
+     innermost directive. */
+  int omp_tile_depth = 0;
+  for (tree c = clauses; c; c = TREE_CHAIN (c))
     {
-      c_parser_error (parser, "for statement expected");
-      return NULL;
+      if (OMP_CLAUSE_CODE (c) != OMP_CLAUSE_TILE)
+	continue;
+
+      omp_tile_depth = list_length (OMP_CLAUSE_TILE_SIZES (c));
     }
 
+  gcc_assert (oacc_tiling || (collapse >= 1 && ordered >= 0));
+  count = ordered ? ordered : collapse;
+  count = MAX (count, omp_tile_depth);
+
+  if (!c_parser_see_omp_loop_nest (parser, code, true))
+    return NULL;
+
   /* Initialize parse state for recursive descent.  */
   data.declv = make_tree_vec (count);
   data.initv = make_tree_vec (count);
@@ -21189,9 +21414,11 @@  c_parser_omp_for_loop (location_t loc, c_parser *parser, enum tree_code code,
   data.inscan = inscan;
   data.saw_intervening_code = false;
   data.code = code;
+  data.clauses = clauses;
   parser->omp_for_parse_state = &data;
 
   body = c_parser_omp_loop_nest (parser, if_p);
+  count = data.count;
 
   /* Add saved bindings for iteration variables that were declared in
      the nested for loop to the scope surrounding the entire loop.  */
@@ -24641,6 +24868,263 @@  c_parser_omp_taskloop (location_t loc, c_parser *parser,
   return ret;
 }
 
+#define OMP_UNROLL_CLAUSE_MASK					\
+	( (OMP_CLAUSE_MASK_1 << PRAGMA_OMP_CLAUSE_PARTIAL)	\
+	  | (OMP_CLAUSE_MASK_1 << PRAGMA_OMP_CLAUSE_FULL) )
+
+/* OpenMP 5.1: Parse sizes list for "omp tile sizes"
+   sizes ( size-expr-list ) */
+static tree
+c_parser_omp_tile_sizes (c_parser *parser, location_t loc)
+{
+  tree sizes = NULL_TREE;
+
+  c_token *tok = c_parser_peek_token (parser);
+  if (tok->type != CPP_NAME
+      || strcmp ("sizes", IDENTIFIER_POINTER (tok->value)))
+    {
+      c_parser_error (parser, "expected %<sizes%>");
+      return error_mark_node;
+    }
+  c_parser_consume_token (parser);
+
+  if (!c_parser_require (parser, CPP_OPEN_PAREN, "expected %<(%>"))
+    return error_mark_node;
+
+  do
+    {
+      if (sizes && !c_parser_require (parser, CPP_COMMA, "expected %<,%>"))
+	return error_mark_node;
+
+      location_t expr_loc = c_parser_peek_token (parser)->location;
+      c_expr cexpr = c_parser_expr_no_commas (parser, NULL);
+      cexpr = convert_lvalue_to_rvalue (expr_loc, cexpr, false, true);
+      tree expr = cexpr.value;
+
+      if (expr == error_mark_node)
+	{
+	  c_parser_skip_until_found (parser, CPP_CLOSE_PAREN,
+				     "expected %<)%>");
+	  return error_mark_node;
+	}
+
+      expr = c_fully_fold (expr, false, NULL);
+
+      if (!INTEGRAL_TYPE_P (TREE_TYPE (expr)) || !tree_fits_shwi_p (expr)
+	  || tree_to_shwi (expr) <= 0)
+	{
+	  c_parser_error (parser, "%<tile sizes%> argument needs positive"
+				  " integral constant");
+	  expr = integer_zero_node;
+	}
+
+      sizes = tree_cons (NULL_TREE, expr, sizes);
+    }
+  while (c_parser_next_token_is_not (parser, CPP_CLOSE_PAREN));
+  c_parser_consume_token (parser);
+
+  gcc_assert (sizes);
+  tree c  = build_omp_clause (loc, OMP_CLAUSE_TILE);
+  OMP_CLAUSE_TRANSFORM_LEVEL (c) = build_int_cst (unsigned_type_node, 0);
+  OMP_CLAUSE_TILE_SIZES (c) = sizes;
+
+  return c;
+}
+
+/* Parse a single OpenMP loop transformation directive and return the
+   clause that is used internally to represent the directive. */
+
+static tree
+c_parser_omp_loop_transform_clause (c_parser *parser)
+{
+  c_token *tok = c_parser_peek_token (parser);
+  if (tok->type != CPP_PRAGMA)
+    return NULL_TREE;
+
+  tree c;
+  switch (tok->pragma_kind)
+    {
+    case PRAGMA_OMP_UNROLL:
+      c_parser_consume_pragma (parser);
+      c = c_parser_omp_all_clauses (parser, OMP_UNROLL_CLAUSE_MASK,
+				    "#pragma omp unroll", false, true);
+      if (!c && c_parser_next_token_is (parser, CPP_PRAGMA_EOL))
+	{
+	  c = build_omp_clause (tok->location, OMP_CLAUSE_UNROLL_NONE);
+	  OMP_CLAUSE_TRANSFORM_LEVEL (c)
+	    = build_int_cst (unsigned_type_node, 0);
+	}
+      else if (!c)
+	c = error_mark_node;
+      c_parser_skip_to_pragma_eol (parser);
+      break;
+
+    case PRAGMA_OMP_TILE:
+      c_parser_consume_pragma (parser);
+      c = c_parser_omp_tile_sizes (parser, tok->location);
+      c_parser_skip_to_pragma_eol (parser);
+      break;
+
+    default:
+      c = NULL_TREE;
+      break;
+    }
+
+  gcc_assert (!c || !TREE_CHAIN (c));
+  return c;
+}
+
+/* Parse zero or more OpenMP loop transformation directives that
+   follow another directive that requires a canonical loop nest,
+   append all to CLAUSES and record the LEVEL at which the clauses
+   appear in the loop nest in each clause.
+
+   REQUIRED_DEPTH is the nesting depth of the loop nest required by
+   the preceding directive.  OUTER_DESCR is a description of the
+   language construct that requires the loop nest depth (e.g. "loop
+   collpase", "outer transformation") that is used for error
+   messages. */
+
+static int
+c_parser_omp_nested_loop_transform_clauses (c_parser *parser, tree &clauses,
+					    int level, int required_depth,
+					    const char *outer_descr)
+{
+  tree c = NULL_TREE;
+  tree last_c = tree_last (clauses);
+
+  /* The depth of the loop nest, counting from LEVEL, after the
+     transformations. That is, the nesting depth left by the outermost
+     transformation which is the first to be parsed, but the last to be
+     executed. */
+  int transformed_depth = 0;
+
+  /* The minimum nesting depth required by the last parsed transformation. */
+  int last_depth = required_depth;
+  while ((c = c_parser_omp_loop_transform_clause (parser)))
+    {
+      /* The nesting depth left after the current transformation. */
+      int depth = 1;
+      if (TREE_CODE (c) == ERROR_MARK)
+	goto error;
+
+      gcc_assert (!TREE_CHAIN (c));
+      switch (OMP_CLAUSE_CODE (c))
+	{
+	case OMP_CLAUSE_UNROLL_FULL:
+	  error_at (OMP_CLAUSE_LOCATION (c),
+		    "%<full%> clause is invalid here; "
+		    "turns loop into non-loop");
+	  goto error;
+	case OMP_CLAUSE_UNROLL_NONE:
+	  error_at (OMP_CLAUSE_LOCATION (c),
+		    "%<#pragma omp unroll%> without "
+		    "%<partial%> clause is invalid here; "
+		    "turns loop into non-loop");
+	  goto error;
+	case OMP_CLAUSE_UNROLL_PARTIAL:
+	  depth = 1;
+	  break;
+	case OMP_CLAUSE_TILE:
+	  depth = list_length (OMP_CLAUSE_TILE_SIZES (c));
+	  break;
+	default:
+	  gcc_unreachable ();
+	}
+
+      if (depth < last_depth)
+	{
+	  bool is_outermost_clause = !transformed_depth;
+	  error_at (OMP_CLAUSE_LOCATION (c),
+		    "nesting depth left after this transformation too low "
+		    "for %s",
+		    is_outermost_clause ? outer_descr
+					: "outer transformation");
+	  goto error;
+	}
+
+      last_depth = depth;
+
+      if (!transformed_depth)
+	transformed_depth = last_depth;
+
+      OMP_CLAUSE_TRANSFORM_LEVEL (c) = build_int_cst (unsigned_type_node, level);
+      if (!clauses)
+	clauses = c;
+      else if (last_c)
+	TREE_CHAIN (last_c) = c;
+
+      last_c = c;
+    }
+
+  return transformed_depth;
+
+error:
+  while (c_parser_omp_loop_transform_clause (parser))
+    ;
+  clauses = NULL_TREE;
+  return -1;
+}
+
+/* OpenMP 5.1:
+   tile sizes ( size-expr-list ) */
+
+static tree
+c_parser_omp_tile (location_t loc, c_parser *parser, bool *if_p)
+{
+  tree block;
+  tree ret = error_mark_node;
+
+  tree clauses = c_parser_omp_tile_sizes (parser, loc);
+  c_parser_skip_to_pragma_eol (parser);
+
+  if (!clauses || clauses == error_mark_node)
+    return error_mark_node;
+
+  int required_depth = list_length (OMP_CLAUSE_TILE_SIZES (clauses));
+  c_parser_omp_nested_loop_transform_clauses (parser, clauses, 0,
+					      required_depth,
+					      "outer transformation");
+
+  block = c_begin_compound_stmt (true);
+  ret = c_parser_omp_for_loop (loc, parser, OMP_LOOP_TRANS, clauses,
+			       NULL, if_p);
+  block = c_end_compound_stmt (loc, block, true);
+  add_stmt (block);
+
+  return ret;
+ }
+
+static tree
+c_parser_omp_unroll (location_t loc, c_parser *parser, bool *if_p)
+{
+  tree block, ret;
+  static const char *p_name = "#pragma omp unroll";
+  omp_clause_mask mask = OMP_UNROLL_CLAUSE_MASK;
+
+  tree clauses = c_parser_omp_all_clauses (parser, mask, p_name, false);
+  int required_depth = 1;
+  c_parser_omp_nested_loop_transform_clauses (parser, clauses, 0,
+					      required_depth,
+					      "outer transformation");
+
+  if (!clauses)
+    {
+      tree c = build_omp_clause (loc, OMP_CLAUSE_UNROLL_NONE);
+      OMP_CLAUSE_TRANSFORM_LEVEL (c) = build_int_cst (unsigned_type_node, 0);
+      OMP_CLAUSE_CHAIN (c) = clauses;
+      clauses = c;
+    }
+
+  block = c_begin_compound_stmt (true);
+  ret = c_parser_omp_for_loop (loc, parser, OMP_LOOP_TRANS, clauses,
+			       NULL, if_p);
+  block = c_end_compound_stmt (loc, block, true);
+  add_stmt (block);
+
+  return ret;
+}
+
 /* OpenMP 5.1
    #pragma omp nothing new-line  */
 
@@ -25032,6 +25516,7 @@  c_parser_omp_construct (c_parser *parser, bool *if_p)
   p_kind = c_parser_peek_token (parser)->pragma_kind;
   c_parser_consume_pragma (parser);
 
+  gcc_assert (parser->in_pragma);
   switch (p_kind)
     {
     case PRAGMA_OACC_ATOMIC:
@@ -25122,6 +25607,12 @@  c_parser_omp_construct (c_parser *parser, bool *if_p)
     case PRAGMA_OMP_ASSUME:
       c_parser_omp_assume (parser, if_p);
       return;
+    case PRAGMA_OMP_TILE:
+      stmt = c_parser_omp_tile (loc, parser, if_p);
+      break;
+    case PRAGMA_OMP_UNROLL:
+      stmt = c_parser_omp_unroll (loc, parser, if_p);
+      break;
     default:
       gcc_unreachable ();
     }
diff --git a/gcc/c/c-typeck.cc b/gcc/c/c-typeck.cc
index 54a5f208cb5..7de61ae9a2e 100644
--- a/gcc/c/c-typeck.cc
+++ b/gcc/c/c-typeck.cc
@@ -15920,6 +15920,14 @@  c_finish_omp_clauses (tree clauses, enum c_omp_region_type ort)
 	  pc = &OMP_CLAUSE_CHAIN (c);
 	  continue;
 
+	case OMP_CLAUSE_UNROLL_FULL:
+	  pc = &OMP_CLAUSE_CHAIN (c);
+	  continue;
+
+	case OMP_CLAUSE_UNROLL_PARTIAL:
+	  pc = &OMP_CLAUSE_CHAIN (c);
+	  continue;
+
 	case OMP_CLAUSE_INBRANCH:
 	case OMP_CLAUSE_NOTINBRANCH:
 	  if (branch_seen)
diff --git a/gcc/cp/cp-gimplify.cc b/gcc/cp/cp-gimplify.cc
index bdf6e5f98ff..7d6986be86f 100644
--- a/gcc/cp/cp-gimplify.cc
+++ b/gcc/cp/cp-gimplify.cc
@@ -647,6 +647,7 @@  cp_gimplify_expr (tree *expr_p, gimple_seq *pre_p, gimple_seq *post_p)
     case OMP_DISTRIBUTE:
     case OMP_LOOP:
     case OMP_TASKLOOP:
+    case OMP_LOOP_TRANS:
       ret = cp_gimplify_omp_for (expr_p, pre_p);
       break;
 
@@ -1188,6 +1189,7 @@  cp_fold_r (tree *stmt_p, int *walk_subtrees, void *data_)
     case OMP_DISTRIBUTE:
     case OMP_LOOP:
     case OMP_TASKLOOP:
+    case OMP_LOOP_TRANS:
     case OACC_LOOP:
       cp_walk_tree (&OMP_FOR_BODY (stmt), cp_fold_r, data, NULL);
       cp_walk_tree (&OMP_FOR_CLAUSES (stmt), cp_fold_r, data, NULL);
@@ -1964,6 +1966,7 @@  cp_genericize_r (tree *stmt_p, int *walk_subtrees, void *data)
     case OMP_FOR:
     case OMP_SIMD:
     case OMP_LOOP:
+    case OMP_LOOP_TRANS:
     case OACC_LOOP:
     case STATEMENT_LIST:
       /* These cases are handled by shared code.  */
diff --git a/gcc/cp/parser.cc b/gcc/cp/parser.cc
index defb81ca8c1..91b1a5a1464 100644
--- a/gcc/cp/parser.cc
+++ b/gcc/cp/parser.cc
@@ -2972,7 +2972,11 @@  static bool cp_parser_skip_up_to_closing_square_bracket
 static bool cp_parser_skip_to_closing_square_bracket
   (cp_parser *);
 static size_t cp_parser_skip_balanced_tokens (cp_parser *, size_t);
+static bool cp_parser_see_omp_loop_nest (cp_parser *, enum tree_code, bool);
 static tree cp_parser_omp_loop_nest (cp_parser *, bool *);
+static int cp_parser_omp_nested_loop_transform_clauses (cp_parser *, tree &,
+							int, int,
+							const char *);
 
 // -------------------------------------------------------------------------- //
 // Unevaluated Operand Guard
@@ -13119,7 +13123,20 @@  check_omp_intervening_code (cp_parser *parser)
 		    "%<reduction%> %<inscan%> clause");
 	  omp_for_parse_state->perfect_nesting_fail = true;
 	}
-      /* TODO: Also reject loops with TILE directive.  */
+      else
+	{
+	  tree c = omp_find_clause (omp_for_parse_state->clauses,
+				    OMP_CLAUSE_TILE);
+	  if (c &&
+	      ((int) tree_to_uhwi (OMP_CLAUSE_TRANSFORM_LEVEL (c))
+	       <= omp_for_parse_state->depth))
+	    {
+	      error_at (omp_for_parse_state->for_loc,
+			"inner loops must be perfectly nested "
+			"with %<tile%> directive");
+	      omp_for_parse_state->perfect_nesting_fail = true;
+	    }
+	}
       if (omp_for_parse_state->perfect_nesting_fail)
 	omp_for_parse_state->fail = true;
     }
@@ -13171,7 +13188,9 @@  cp_parser_statement_seq_opt (cp_parser* parser, tree in_statement_expr)
 	{
 	  bool want_nested_loop = omp_for_parse_state->want_nested_loop;
 	  if (want_nested_loop
-	      && token->type == CPP_KEYWORD && token->keyword == RID_FOR)
+	      && cp_parser_see_omp_loop_nest (parser,
+					      omp_for_parse_state->code,
+					      false))
 	    {
 	      /* Found the nested loop.  */
 	      omp_for_parse_state->depth++;
@@ -37494,6 +37513,8 @@  cp_parser_omp_clause_name (cp_parser *parser)
 	    result = PRAGMA_OMP_CLAUSE_FIRSTPRIVATE;
 	  else if (!strcmp ("from", p))
 	    result = PRAGMA_OMP_CLAUSE_FROM;
+	  else if (!strcmp ("full", p))
+	    result = PRAGMA_OMP_CLAUSE_FULL;
 	  break;
 	case 'g':
 	  if (!strcmp ("gang", p))
@@ -37568,6 +37589,8 @@  cp_parser_omp_clause_name (cp_parser *parser)
 	case 'p':
 	  if (!strcmp ("parallel", p))
 	    result = PRAGMA_OMP_CLAUSE_PARALLEL;
+	  if (!strcmp ("partial", p))
+	    result = PRAGMA_OMP_CLAUSE_PARTIAL;
 	  else if (!strcmp ("present", p))
 	    result = PRAGMA_OACC_CLAUSE_PRESENT;
 	  else if (!strcmp ("present_or_copy", p)
@@ -37658,12 +37681,15 @@  cp_parser_omp_clause_name (cp_parser *parser)
 
 /* Validate that a clause of the given type does not already exist.  */
 
-static void
+static bool
 check_no_duplicate_clause (tree clauses, enum omp_clause_code code,
 			   const char *name, location_t location)
 {
-  if (omp_find_clause (clauses, code))
+  bool found = omp_find_clause (clauses, code);
+  if (found)
     error_at (location, "too many %qs clauses", name);
+
+  return !found;
 }
 
 /* OpenMP 2.5:
@@ -39782,6 +39808,58 @@  cp_parser_omp_clause_thread_limit (cp_parser *parser, tree list,
   return c;
 }
 
+/* OpenMP 5.1
+   full */
+
+static tree
+cp_parser_omp_clause_unroll_full (tree list, location_t loc)
+{
+  if (!check_no_duplicate_clause (list, OMP_CLAUSE_UNROLL_FULL, "full", loc))
+    return list;
+
+  tree c = build_omp_clause (loc, OMP_CLAUSE_UNROLL_FULL);
+  OMP_CLAUSE_TRANSFORM_LEVEL (c) = build_int_cst (unsigned_type_node, 0);
+  OMP_CLAUSE_CHAIN (c) = list;
+  return c;
+}
+
+/* OpenMP 5.1
+   partial ( constant-expression ) */
+
+static tree
+cp_parser_omp_clause_unroll_partial (cp_parser *parser, tree list,
+				     location_t loc)
+{
+  if (!check_no_duplicate_clause (list, OMP_CLAUSE_UNROLL_PARTIAL, "partial",
+				  loc))
+    return list;
+
+  tree c, num = error_mark_node;
+  c = build_omp_clause (loc, OMP_CLAUSE_UNROLL_PARTIAL);
+  OMP_CLAUSE_UNROLL_PARTIAL_EXPR (c) = NULL_TREE;
+  OMP_CLAUSE_TRANSFORM_LEVEL (c) = build_int_cst (unsigned_type_node, 0);
+  OMP_CLAUSE_CHAIN (c) = list;
+
+  if (!cp_lexer_next_token_is (parser->lexer, CPP_OPEN_PAREN))
+    return c;
+
+  matching_parens parens;
+  parens.consume_open (parser);
+  num = cp_parser_constant_expression (parser);
+  cp_parser_skip_to_closing_parenthesis (parser, /*recovering=*/true,
+					 /*or_comma=*/false,
+					 /*consume_paren=*/true);
+
+  if (num == error_mark_node)
+    return list;
+
+  mark_exp_read (num);
+  num = fold_non_dependent_expr (num);
+
+  OMP_CLAUSE_UNROLL_PARTIAL_EXPR (c) = num;
+  return c;
+}
+
 /* OpenMP 4.0:
    aligned ( variable-list )
    aligned ( variable-list : constant-expression )  */
@@ -41473,15 +41551,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);
@@ -41822,6 +41897,16 @@  cp_parser_omp_all_clauses (cp_parser *parser, omp_clause_mask mask,
 					    clauses);
 	  c_name = "enter";
 	  break;
+	case PRAGMA_OMP_CLAUSE_PARTIAL:
+	  clauses = cp_parser_omp_clause_unroll_partial (parser, clauses,
+							 token->location);
+	  c_name = "partial";
+	  break;
+	case PRAGMA_OMP_CLAUSE_FULL:
+	  clauses = cp_parser_omp_clause_unroll_full (clauses,
+						      token->location);
+	  c_name = "full";
+	  break;
 	default:
 	  cp_parser_error (parser, "expected an OpenMP clause");
 	  goto saw_error;
@@ -43999,6 +44084,44 @@  cp_parser_omp_scan_loop_body (cp_parser *parser)
 }
 
 
+/* Check that the next token starts a loop nest.  Return true if yes,
+   otherwise diagnose an error if ERROR_P is true and return false.  */
+static bool
+cp_parser_see_omp_loop_nest (cp_parser *parser, enum tree_code code,
+			     bool error_p)
+{
+  if (code == OACC_LOOP)
+    {
+      if (cp_lexer_next_token_is_keyword (parser->lexer, RID_FOR))
+	return true;
+      if (error_p)
+	cp_parser_error (parser, "for statement expected");
+    }
+  else
+    {
+      if (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))
+	return true;
+      /* The OpenMP spec isn't very clear on this.  Here we consider that
+	 any attribute specifier sequence followed by a FOR loop is a loop
+	 nest, but cp_parser_omp_loop_nest rejects invalid attributes
+	 with an error.  If we rejected such things here, too, then the
+	 associated FOR statement would be considered intervening code
+	 instead, and we would get a different error about a loop in
+	 intervening code.  */
+      size_t n = cp_parser_skip_std_attribute_spec_seq (parser, 1);
+      if (cp_lexer_nth_token_is_keyword (parser->lexer, n, RID_FOR))
+	return true;
+      if (error_p)
+	cp_parser_error (parser, "loop nest expected");
+    }
+  return false;
+}
+
+
 /* This function parses a single level of a loop nest, invoking itself
    recursively if necessary.
 
@@ -44051,8 +44174,69 @@  cp_parser_omp_loop_nest (cp_parser *parser, bool *if_p)
   gcc_assert (omp_for_parse_state);
   int depth = omp_for_parse_state->depth;
 
-  /* We have already matched the FOR token but not consumed it yet.  */
-  gcc_assert (cp_lexer_next_token_is_keyword (parser->lexer, RID_FOR));
+  /* Handle loop transformations first.  Note that when we get here
+     omp_for_parse_state->depth has already been incremented to indicate
+     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.  Give an error if there are other random attributes present.  */
+  cp_token *token = cp_lexer_peek_token (parser->lexer);
+  tree std_attrs = cp_parser_std_attribute_spec_seq (parser);
+  std_attrs = cp_parser_handle_statement_omp_attributes (parser, std_attrs);
+  if (std_attrs)
+    error_at (token->location,
+	      "attributes other than OpenMP directives "
+	      "are not allowed on %<for%> in loop nest");
+
+  if ((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))
+    {
+      int count = omp_for_parse_state->count;
+      int more = cp_parser_omp_nested_loop_transform_clauses (
+		   parser, omp_for_parse_state->clauses,
+		   depth, count - depth, "loop collapse");
+      if (depth + more > count)
+	{
+	  count = depth + more;
+	  omp_for_parse_state->count = count;
+	  omp_for_parse_state->declv
+	    = grow_tree_vec (omp_for_parse_state->declv, count);
+	  omp_for_parse_state->initv
+	    = grow_tree_vec (omp_for_parse_state->initv, count);
+	  omp_for_parse_state->condv
+	    = grow_tree_vec (omp_for_parse_state->condv, count);
+	  omp_for_parse_state->incrv
+	    = grow_tree_vec (omp_for_parse_state->incrv, count);
+	  if (omp_for_parse_state->orig_declv)
+	    omp_for_parse_state->orig_declv
+	      = grow_tree_vec (omp_for_parse_state->orig_declv, count);
+	}
+    }
+
+  /* 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)
+	{
+	  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;
+    }
   loc = cp_lexer_consume_token (parser->lexer)->location;
 
   /* Forbid break/continue in the loop initializer, condition, and
@@ -44307,7 +44491,9 @@  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))
+  if (moreloops
+      && 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));
@@ -44582,6 +44768,10 @@  fixup_blocks_walker (tree *tp, int *walk_subtrees, void *dp)
   return NULL;
 }
 
+static int cp_parser_omp_nested_loop_transform_clauses (cp_parser *, tree &,
+							int, int,
+							const char *);
+
 /* Parse the restricted form of the for statement allowed by OpenMP.  */
 
 static tree
@@ -44592,7 +44782,7 @@  cp_parser_omp_for_loop (cp_parser *parser, enum tree_code code, tree clauses,
   tree cl, ordered_cl = NULL_TREE;
   int collapse = 1, ordered = 0;
   unsigned int count;
-  bool tiling = false;
+  bool oacc_tiling = false;
   bool inscan = false;
   struct omp_for_parse_data data;
   struct omp_for_parse_data *save_data = parser->omp_for_parse_state;
@@ -44604,7 +44794,7 @@  cp_parser_omp_for_loop (cp_parser *parser, enum tree_code code, tree clauses,
       collapse = tree_to_shwi (OMP_CLAUSE_COLLAPSE_EXPR (cl));
     else if (OMP_CLAUSE_CODE (cl) == OMP_CLAUSE_OACC_TILE)
       {
-	tiling = true;
+	oacc_tiling = true;
 	collapse = list_length (OMP_CLAUSE_OACC_TILE_LIST (cl));
       }
     else if (OMP_CLAUSE_CODE (cl) == OMP_CLAUSE_ORDERED
@@ -44627,14 +44817,28 @@  cp_parser_omp_for_loop (cp_parser *parser, enum tree_code code, tree clauses,
       ordered = collapse;
     }
 
-  gcc_assert (tiling || (collapse >= 1 && ordered >= 0));
+  gcc_assert (oacc_tiling || (collapse >= 1 && ordered >= 0));
   count = ordered ? ordered : collapse;
 
-  if (!cp_lexer_next_token_is_keyword (parser->lexer, RID_FOR))
+  cp_parser_omp_nested_loop_transform_clauses (parser, clauses, 0, count,
+					       "loop collapse");
+
+  /* Find the depth of the loop nest affected by "omp tile"
+     directives. There can be several such directives, but the tiling
+     depth of the outer ones may not be larger than the depth of the
+     innermost directive. */
+  unsigned int omp_tile_depth = 0;
+  for (tree c = clauses; c; c = TREE_CHAIN (c))
     {
-      cp_parser_error (parser, "for statement expected");
-      return NULL;
+      if (OMP_CLAUSE_CODE (c) != OMP_CLAUSE_TILE)
+	continue;
+
+      omp_tile_depth = list_length (OMP_CLAUSE_TILE_SIZES (c));
     }
+  count = MAX (count, omp_tile_depth);
+
+  if (!cp_parser_see_omp_loop_nest (parser, code, true))
+    return NULL;
 
   /* Initialize parse state for recursive descent.  */
   data.declv = make_tree_vec (count);
@@ -44687,6 +44891,7 @@  cp_parser_omp_for_loop (cp_parser *parser, enum tree_code code, tree clauses,
      We also need to flatten the init blocks, as some code for later
      processing of combined directives gets confused otherwise.  */
 
+  count = data.count;
   gcc_assert (vec_safe_length (data.init_blockv) == count);
   gcc_assert (vec_safe_length (data.body_blockv) == count);
   gcc_assert (vec_safe_length (data.init_placeholderv) == count);
@@ -46479,6 +46684,272 @@  cp_parser_omp_target (cp_parser *parser, cp_token *pragma_tok,
   return true;
 }
 
+
+/* OpenMP 5.1: Parse sizes list for "omp tile sizes"
+   sizes ( size-expr-list ) */
+static tree
+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)))
+    {
+      cp_parser_error (parser, "expected %<sizes%>");
+      return error_mark_node;
+    }
+  cp_lexer_consume_token (lexer);
+
+  if (!cp_parser_require (parser, CPP_OPEN_PAREN, RT_OPEN_PAREN))
+    return error_mark_node;
+
+  do
+    {
+      if (sizes && !cp_parser_require (parser, CPP_COMMA, RT_COMMA))
+	return error_mark_node;
+
+      tree expr = cp_parser_constant_expression (parser);
+      if (expr == error_mark_node)
+	{
+	  cp_parser_skip_to_closing_parenthesis (parser,
+						 /*recovering=*/true,
+						 /*or_comma=*/false,
+						 /*consume_paren=*/
+						 true);
+	  return error_mark_node;
+	}
+
+      sizes = tree_cons (NULL_TREE, expr, sizes);
+    }
+  while (cp_lexer_next_token_is_not (lexer, CPP_CLOSE_PAREN));
+  cp_lexer_consume_token (lexer);
+
+  gcc_assert (sizes);
+  tree c  = build_omp_clause (loc, OMP_CLAUSE_TILE);
+  OMP_CLAUSE_TRANSFORM_LEVEL (c) = build_int_cst (unsigned_type_node, 0);
+  OMP_CLAUSE_TILE_SIZES (c) = sizes;
+  OMP_CLAUSE_TRANSFORM_LEVEL (c)
+    = build_int_cst (unsigned_type_node, 0);
+
+  return c;
+}
+
+/* OpenMP 5.1:
+   tile sizes ( size-expr-list ) */
+
+static tree
+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);
+
+  if (!clauses || clauses == error_mark_node)
+    return error_mark_node;
+
+  int required_depth = list_length (OMP_CLAUSE_TILE_SIZES (clauses));
+  cp_parser_omp_nested_loop_transform_clauses (parser, clauses, 0,
+					       required_depth,
+					       "outer transformation");
+
+  block = begin_omp_structured_block ();
+  clauses = finish_omp_clauses (clauses, C_ORT_OMP);
+
+  ret = cp_parser_omp_for_loop (parser, OMP_LOOP_TRANS, clauses, NULL, if_p);
+  block = finish_omp_structured_block (block);
+  add_stmt (block);
+
+  return ret;
+}
+
+#define OMP_UNROLL_CLAUSE_MASK					\
+	( (OMP_CLAUSE_MASK_1 << PRAGMA_OMP_CLAUSE_PARTIAL)	\
+	  | (OMP_CLAUSE_MASK_1 << PRAGMA_OMP_CLAUSE_FULL) )
+
+/* Parse a single OpenMP loop transformation directive and return the
+   clause that is used internally to represent the directive. */
+
+static tree
+cp_parser_omp_loop_transform_clause (cp_parser *parser)
+{
+  cp_lexer *lexer = parser->lexer;
+  cp_token *tok = cp_lexer_peek_token (lexer);
+  if (tok->type != CPP_PRAGMA)
+    return NULL_TREE;
+
+  tree c;
+  switch (cp_parser_pragma_kind (tok))
+    {
+    case PRAGMA_OMP_UNROLL:
+      cp_lexer_consume_token (lexer);
+      lexer->in_pragma = true;
+      c = cp_parser_omp_all_clauses (parser, OMP_UNROLL_CLAUSE_MASK,
+				     "#pragma omp unroll", tok,
+				     false, true);
+      if (!c && cp_lexer_next_token_is (lexer, CPP_PRAGMA_EOL))
+	{
+	  c = build_omp_clause (tok->location, OMP_CLAUSE_UNROLL_NONE);
+	  OMP_CLAUSE_TRANSFORM_LEVEL (c)
+	    = build_int_cst (unsigned_type_node, 0);
+	}
+      else if (!c)
+	c = error_mark_node;
+      cp_parser_skip_to_pragma_eol (parser, tok);
+      break;
+
+    case PRAGMA_OMP_TILE:
+      cp_lexer_consume_token (lexer);
+      lexer->in_pragma = true;
+      c = cp_parser_omp_tile_sizes (parser, tok->location);
+      cp_parser_require_pragma_eol (parser, tok);
+      break;
+
+    default:
+      c = NULL_TREE;
+      break;
+    }
+
+  gcc_assert (!c || !TREE_CHAIN (c));
+  return c;
+}
+
+/* Parse zero or more OpenMP loop transformation directives that
+   follow another directive that requires a canonical loop nest,
+   append all to CLAUSES, and require the level at which the clause
+   appears in the loop nest in each clause.  Return the nesting depth
+   of the transformed loop nest.
+
+   REQUIRED_DEPTH is the nesting depth of the loop nest required by
+   the preceding directive.  OUTER_DESCR is a description of the
+   language construct that requires the loop nest depth (e.g. "loop
+   collpase", "outer transformation") that is used for error
+   messages. */
+
+static int
+cp_parser_omp_nested_loop_transform_clauses (cp_parser *parser, tree &clauses,
+					     int level, int required_depth,
+					     const char *outer_descr)
+{
+  tree c = NULL_TREE;
+  tree last_c = tree_last (clauses);
+
+  /* The depth of the loop nest after the transformations. That is,
+     the nesting depth left by the outermost transformation which is
+     the first to be parsed, but the last to be executed. */
+  int transformed_depth = 0;
+
+  /* The minimum nesting depth required by the last parsed transformation. */
+  int last_depth = required_depth;
+
+  while ((c = cp_parser_omp_loop_transform_clause (parser)))
+    {
+      /* The nesting depth left after the current transformation. */
+      int depth = 1;
+      if (TREE_CODE (c) == ERROR_MARK)
+	goto error;
+
+      gcc_assert (!TREE_CHAIN (c));
+      switch (OMP_CLAUSE_CODE (c))
+	{
+	case OMP_CLAUSE_UNROLL_FULL:
+	  error_at (OMP_CLAUSE_LOCATION (c),
+		    "%<full%> clause is invalid here; "
+		    "turns loop into non-loop");
+	  goto error;
+	case OMP_CLAUSE_UNROLL_NONE:
+	  error_at (OMP_CLAUSE_LOCATION (c),
+		    "%<#pragma omp unroll%> without "
+		    "%<partial%> clause is invalid here; "
+		    "turns loop into non-loop");
+	  goto error;
+	case OMP_CLAUSE_UNROLL_PARTIAL:
+	  depth = 1;
+	  break;
+	case OMP_CLAUSE_TILE:
+	  depth = list_length (OMP_CLAUSE_TILE_SIZES (c));
+	  break;
+	default:
+	  gcc_unreachable ();
+	}
+      OMP_CLAUSE_TRANSFORM_LEVEL (c)
+	= build_int_cst (unsigned_type_node, level);
+
+      if (depth < last_depth)
+	{
+	  bool is_outermost_clause = !transformed_depth;
+	  error_at (OMP_CLAUSE_LOCATION (c),
+		    "nesting depth left after this transformation too low "
+		    "for %s",
+		    is_outermost_clause ? outer_descr
+					: "outer transformation");
+	  goto error;
+	}
+
+      last_depth = depth;
+
+      if (!transformed_depth)
+	transformed_depth = last_depth;
+
+      c = finish_omp_clauses (c, C_ORT_OMP);
+
+      if (!clauses)
+	clauses = c;
+      else if (last_c)
+	TREE_CHAIN (last_c) = c;
+
+      last_c = c;
+    }
+
+  return transformed_depth;
+
+error:
+  while (cp_parser_omp_loop_transform_clause (parser))
+    ;
+  clauses = NULL_TREE;
+  return -1;
+}
+
+static tree
+cp_parser_omp_unroll (cp_parser *parser, cp_token *tok, bool *if_p)
+{
+  tree block, ret;
+  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)
+    {
+      tree c = build_omp_clause (tok->location, OMP_CLAUSE_UNROLL_NONE);
+      OMP_CLAUSE_TRANSFORM_LEVEL (c) = build_int_cst (unsigned_type_node, 0);
+      OMP_CLAUSE_CHAIN (c) = clauses;
+      clauses = c;
+    }
+
+  int required_depth = 1;
+  cp_parser_omp_nested_loop_transform_clauses (parser, clauses, 0,
+					       required_depth,
+					       "outer transformation");
+
+  block = begin_omp_structured_block ();
+  ret = cp_parser_omp_for_loop (parser, OMP_LOOP_TRANS, clauses, NULL, if_p);
+  block = finish_omp_structured_block (block);
+  add_stmt (block);
+
+  return ret;
+}
+
 /* OpenACC 2.0:
    # pragma acc cache (variable-list) new-line
 */
@@ -49675,6 +50146,12 @@  cp_parser_omp_construct (cp_parser *parser, cp_token *pragma_tok, bool *if_p)
     case PRAGMA_OMP_ASSUME:
       cp_parser_omp_assume (parser, pragma_tok, if_p);
       return;
+    case PRAGMA_OMP_TILE:
+      stmt = cp_parser_omp_tile (parser, pragma_tok, if_p);
+      break;
+    case PRAGMA_OMP_UNROLL:
+      stmt = cp_parser_omp_unroll (parser, pragma_tok, if_p);
+      break;
     default:
       gcc_unreachable ();
     }
@@ -50321,6 +50798,14 @@  cp_parser_pragma (cp_parser *parser, enum pragma_context context, bool *if_p)
       cp_parser_omp_construct (parser, pragma_tok, if_p);
       pop_omp_privatization_clauses (stmt);
       return true;
+    case PRAGMA_OMP_TILE:
+    case PRAGMA_OMP_UNROLL:
+      if (context != pragma_stmt && context != pragma_compound)
+	goto bad_stmt;
+      stmt = push_omp_privatization_clauses (false);
+      cp_parser_omp_construct (parser, pragma_tok, if_p);
+      pop_omp_privatization_clauses (stmt);
+      return true;
 
     case PRAGMA_OMP_REQUIRES:
       if (context != pragma_external)
diff --git a/gcc/cp/pt.cc b/gcc/cp/pt.cc
index d44d20767ca..6942a5c69a8 100644
--- a/gcc/cp/pt.cc
+++ b/gcc/cp/pt.cc
@@ -18142,6 +18142,16 @@  tsubst_omp_clauses (tree clauses, enum c_omp_region_type ort,
 	  OMP_CLAUSE_OPERAND (nc, 0)
 	    = tsubst_expr (OMP_CLAUSE_OPERAND (oc, 0), args, complain, in_decl);
 	  break;
+	case OMP_CLAUSE_UNROLL_PARTIAL:
+	  OMP_CLAUSE_UNROLL_PARTIAL_EXPR (nc)
+	    = tsubst_expr (OMP_CLAUSE_UNROLL_PARTIAL_EXPR (oc), args, complain,
+			   in_decl);
+	  break;
+	case OMP_CLAUSE_TILE:
+	  OMP_CLAUSE_TILE_SIZES (nc)
+	    = tsubst_expr (OMP_CLAUSE_TILE_SIZES (oc), args, complain,
+			   in_decl);
+	  break;
 	case OMP_CLAUSE_REDUCTION:
 	case OMP_CLAUSE_IN_REDUCTION:
 	case OMP_CLAUSE_TASK_REDUCTION:
@@ -18222,6 +18232,8 @@  tsubst_omp_clauses (tree clauses, enum c_omp_region_type ort,
 	case OMP_CLAUSE_IF_PRESENT:
 	case OMP_CLAUSE_FINALIZE:
 	case OMP_CLAUSE_NOHOST:
+	case OMP_CLAUSE_UNROLL_FULL:
+	case OMP_CLAUSE_UNROLL_NONE:
 	  break;
 	default:
 	  gcc_unreachable ();
@@ -19494,6 +19506,7 @@  tsubst_expr (tree t, tree args, tsubst_flags_t complain, tree in_decl)
     case OMP_SIMD:
     case OMP_DISTRIBUTE:
     case OMP_TASKLOOP:
+    case OMP_LOOP_TRANS:
     case OACC_LOOP:
       {
 	tree clauses, body, pre_body;
diff --git a/gcc/cp/semantics.cc b/gcc/cp/semantics.cc
index c934659c9f3..45d0ab8f8d8 100644
--- a/gcc/cp/semantics.cc
+++ b/gcc/cp/semantics.cc
@@ -6887,6 +6887,7 @@  finish_omp_clauses (tree clauses, enum c_omp_region_type ort)
   bool mergeable_seen = false;
   bool implicit_moved = false;
   bool target_in_reduction_seen = false;
+  bool unroll_full_seen = false;
 
   bitmap_obstack_initialize (NULL);
   bitmap_initialize (&generic_head, &bitmap_default_obstack);
@@ -8876,6 +8877,46 @@  finish_omp_clauses (tree clauses, enum c_omp_region_type ort)
 	    }
 	  break;
 
+	case OMP_CLAUSE_TILE:
+	  for (tree list = OMP_CLAUSE_TILE_SIZES (c); !remove && list;
+	       list = TREE_CHAIN (list))
+	    {
+	      t = TREE_VALUE (list);
+
+	      if (t == error_mark_node)
+		remove = true;
+	      else if (!type_dependent_expression_p (t)
+		       && !INTEGRAL_TYPE_P (TREE_TYPE (t)))
+		{
+		  error_at (OMP_CLAUSE_LOCATION (c),
+			    "%<tile sizes%> argument needs integral type");
+		  remove = true;
+		}
+	      else
+		{
+		  t = mark_rvalue_use (t);
+		  if (!processing_template_decl)
+		    {
+		      t = maybe_constant_value (t);
+		      int n;
+		      if (!tree_fits_shwi_p (t)
+			  || !INTEGRAL_TYPE_P (TREE_TYPE (t))
+			  || (n = tree_to_shwi (t)) <= 0 || (int)n != n)
+			{
+			  error_at (OMP_CLAUSE_LOCATION (c),
+				    "%<tile sizes%> argument needs positive "
+				    "integral constant");
+			  remove = true;
+			}
+		      t = fold_build_cleanup_point_expr (TREE_TYPE (t), t);
+		    }
+		}
+
+	      /* Update list item.  */
+	      TREE_VALUE (list) = t;
+	    }
+	  break;
+
 	case OMP_CLAUSE_ORDERED:
 	  ordered_seen = true;
 	  break;
@@ -8930,6 +8971,60 @@  finish_omp_clauses (tree clauses, enum c_omp_region_type ort)
 	    }
 	  break;
 
+	case OMP_CLAUSE_UNROLL_FULL:
+	  if (unroll_full_seen)
+	    {
+	      error_at (OMP_CLAUSE_LOCATION (c),
+			"%<full%> appears more than once");
+	      remove = true;
+	    }
+	  unroll_full_seen = true;
+	  break;
+
+	case OMP_CLAUSE_UNROLL_PARTIAL:
+	  {
+	    tree t = OMP_CLAUSE_UNROLL_PARTIAL_EXPR (c);
+
+	    if (!t)
+	      break;
+
+	    if (t == error_mark_node)
+	      remove = true;
+	    else if (!type_dependent_expression_p (t)
+		     && !INTEGRAL_TYPE_P (TREE_TYPE (t)))
+	      {
+		error_at (OMP_CLAUSE_LOCATION (c),
+			  "partial argument needs integral type");
+		remove = true;
+	      }
+	    else
+	      {
+		t = mark_rvalue_use (t);
+		if (!processing_template_decl)
+		  {
+		    t = maybe_constant_value (t);
+
+		    int n;
+		    if (!INTEGRAL_TYPE_P (TREE_TYPE (t))
+			|| !tree_fits_shwi_p (t)
+			|| (n = tree_to_shwi (t)) <= 0 || (int)n != n)
+		      {
+			error_at (OMP_CLAUSE_LOCATION (c),
+				  "partial argument needs positive constant "
+				  "integer expression");
+			remove = true;
+		      }
+		    t = fold_build_cleanup_point_expr (TREE_TYPE (t), t);
+		  }
+	      }
+
+	    OMP_CLAUSE_UNROLL_PARTIAL_EXPR (c) = t;
+	  }
+	  break;
+
+	case OMP_CLAUSE_UNROLL_NONE:
+	  break;
+
 	default:
 	  gcc_unreachable ();
 	}
diff --git a/gcc/testsuite/c-c++-common/gomp/imperfect-attributes.c b/gcc/testsuite/c-c++-common/gomp/imperfect-attributes.c
index 776295ce22a..3c35e7c54b6 100644
--- a/gcc/testsuite/c-c++-common/gomp/imperfect-attributes.c
+++ b/gcc/testsuite/c-c++-common/gomp/imperfect-attributes.c
@@ -1,17 +1,20 @@ 
 /* { 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.  */
+   (not the C++ attribute syntax for OpenMP directives)
+   gives an error.  */
 
 extern void do_something (void);
 
+
+/* This one should be OK, an empty attribute list is ignored in both C
+   and C++.  */
 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 i = 0; i < x; i++)
     {
-      [[]] for (int j = 0; j < y; j++)  /* { dg-error "loop not permitted in intervening code" } */
+      [[]] for (int j = 0; j < y; j++)
 	do_something ();
     }
 }
@@ -19,16 +22,15 @@  void imperfect1 (int x, int y)
 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 i = 0; i < x; i++)
     {
-      [[]] for (int j = 0; j < y; j++)  /* { dg-error "loop not permitted in intervening code" } */
+      [[]] for (int j = 0; j < y; j++)
 	do_something ();
     }
 }
 
 /* Similar, but put the attributes on a block wrapping the nested loop
-   instead.  */
+   instead.  This is not allowed by the grammar.  */
 
 void imperfect2 (int x, int y)
 {
diff --git a/gcc/testsuite/c-c++-common/gomp/loop-transforms/imperfect-loop-nest.c b/gcc/testsuite/c-c++-common/gomp/loop-transforms/imperfect-loop-nest.c
new file mode 100644
index 00000000000..22a1250ed6c
--- /dev/null
+++ b/gcc/testsuite/c-c++-common/gomp/loop-transforms/imperfect-loop-nest.c
@@ -0,0 +1,11 @@ 
+void test ()
+{
+#pragma omp tile sizes (2,4,6)
+  for (unsigned i = 0; i < 10; i++) /* { dg-error "inner loops must be perfectly nested" } */
+    for (unsigned j = 0; j < 10; j++)
+      {
+	float intervening_decl = 0;
+#pragma omp unroll partial(2)
+	for (unsigned k = 0; k < 10; k++);
+      }
+}
diff --git a/gcc/testsuite/c-c++-common/gomp/loop-transforms/tile-1.c b/gcc/testsuite/c-c++-common/gomp/loop-transforms/tile-1.c
new file mode 100644
index 00000000000..f10aea4c27f
--- /dev/null
+++ b/gcc/testsuite/c-c++-common/gomp/loop-transforms/tile-1.c
@@ -0,0 +1,160 @@ 
+extern void dummy (int);
+
+void
+test ()
+{
+    #pragma omp tile sizes(1)
+    for (int i = 0; i < 100; ++i)
+	dummy (i);
+
+    #pragma omp tile sizes(0) /* { dg-error {'tile sizes' argument needs positive integral constant} } */
+    for (int i = 0; i < 100; ++i)
+	dummy (i);
+
+    #pragma omp tile sizes(-1) /* { dg-error {'tile sizes' argument needs positive integral constant} } */
+    for (int i = 0; i < 100; ++i)
+	dummy (i);
+
+    #pragma omp tile sizes() /* { dg-error {expected expression before} "" { target c} } */
+    /* { dg-error {expected primary-expression before} "" { target c++ } .-1 } */
+    for (int i = 0; i < 100; ++i)
+	dummy (i);
+
+    #pragma omp tile sizes(,) /* { dg-error {expected expression before} "" { target c } } */
+    /* { dg-error {expected primary-expression before} "" { target c++ } .-1 } */
+    for (int i = 0; i < 100; ++i)
+	dummy (i);
+
+    #pragma omp tile sizes(1,2 /* { dg-error {expected '\,' before end of line} } */
+    for (int i = 0; i < 100; ++i)
+	dummy (i);
+
+    #pragma omp tile sizes /* { dg-error {expected '\(' before end of line} } */
+    for (int i = 0; i < 100; ++i)
+	dummy (i);
+
+    #pragma omp tile sizes(1) sizes(1) /* { dg-error {expected end of line before 'sizes'} } */
+    for (int i = 0; i < 100; ++i)
+	dummy (i);
+
+    #pragma omp tile sizes(1)
+    #pragma omp tile sizes(1)
+    for (int i = 0; i < 100; ++i)
+	dummy (i);
+
+    #pragma omp tile sizes(1, 2)
+    #pragma omp 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);
+
+    #pragma omp tile sizes(1, 2)
+    #pragma omp tile sizes(1, 2)
+    for (int i = 0; i < 100; ++i)
+    for (int j = 0; j < 100; ++j)
+    	dummy (i);
+
+    #pragma omp tile sizes(5, 6)
+    #pragma omp 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);
+
+    #pragma omp tile sizes(1)
+    #pragma omp unroll partia /* { dg-error {expected an OpenMP clause before 'partia'} } */
+    #pragma omp tile sizes(1)
+    for (int i = 0; i < 100; ++i)
+	dummy (i);
+
+    #pragma omp tile sizes(1)
+    #pragma omp 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);
+
+    #pragma omp tile sizes(1)
+    #pragma omp unroll full /* { dg-error {'full' clause is invalid here; turns loop into non-loop} } */
+    for (int i = 0; i < 100; ++i)
+	dummy (i);
+
+    #pragma omp tile sizes(1)
+    #pragma omp unroll partial
+    #pragma omp tile sizes(1)
+    for (int i = 0; i < 100; ++i)
+	dummy (i);
+
+    #pragma omp tile sizes(8,8)
+    #pragma omp unroll partial /* { dg-error {nesting depth left after this transformation too low for outer transformation} } */
+    #pragma omp tile sizes(1)
+    for (int i = 0; i < 100; ++i)
+	dummy (i);
+
+    #pragma omp tile sizes(8,8)
+    #pragma omp unroll partial /* { dg-error {nesting depth left after this transformation too low for outer transformation} } */
+    for (int i = 0; i < 100; ++i)
+	dummy (i);
+
+    #pragma omp tile sizes(1, 2)
+    for (int i = 0; i < 100; ++i)
+    for (int j = 0; j < 100; ++j)
+	dummy (i);
+
+    #pragma omp 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);
+
+    #pragma omp 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);
+
+    #pragma omp 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);
+
+    #pragma omp tile sizes(1)
+    for (int i = 0; i < 100; ++i)
+      {
+	dummy (i);
+        for (int j = 0; j < 100; ++j)
+          dummy (i);
+      }
+
+    #pragma omp tile sizes(1)
+    for (int i = 0; i < 100; ++i)
+      {
+        for (int j = 0; j < 100; ++j)
+	    dummy (j);
+	dummy (i);
+      }
+
+    #pragma omp 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);
+      }
+
+    #pragma omp 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;
+    #pragma omp 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 } */
+    /* { dg-error {'s' cannot appear in a constant-expression} "" { target c++98_only } .-2 } */
+    for (int i = 0; i < 100; ++i)
+	dummy (i);
+
+    #pragma omp tile sizes(42.0) /* { dg-error {'tile sizes' argument needs positive integral constant} "" { target c } } */
+    /* { dg-error {'tile sizes' argument needs integral type} "" { target c++ } .-1 } */
+    for (int i = 0; i < 100; ++i)
+	dummy (i);
+}
diff --git a/gcc/testsuite/c-c++-common/gomp/loop-transforms/tile-2.c b/gcc/testsuite/c-c++-common/gomp/loop-transforms/tile-2.c
new file mode 100644
index 00000000000..45b9bb1a3ed
--- /dev/null
+++ b/gcc/testsuite/c-c++-common/gomp/loop-transforms/tile-2.c
@@ -0,0 +1,179 @@ 
+extern void dummy (int);
+
+void
+test ()
+{
+    #pragma omp parallel for
+    #pragma omp tile sizes(1)
+    for (int i = 0; i < 100; ++i)
+	dummy (i);
+
+    #pragma omp parallel for
+    #pragma omp tile sizes(0) /* { dg-error {'tile sizes' argument needs positive integral constant} } */
+    for (int i = 0; i < 100; ++i)
+	dummy (i);
+
+    #pragma omp parallel for
+    #pragma omp tile sizes(-1) /* { dg-error {'tile sizes' argument needs positive integral constant} } */
+    for (int i = 0; i < 100; ++i)
+	dummy (i);
+
+    #pragma omp parallel for
+    #pragma omp tile sizes() /* { dg-error {expected expression before} "" { target c} } */
+    /* { dg-error {expected primary-expression before} "" { target c++ } .-1 } */
+    for (int i = 0; i < 100; ++i)
+	dummy (i);
+
+    #pragma omp parallel for
+    #pragma omp tile sizes(,) /* { dg-error {expected expression before} "" { target c } } */
+    /* { dg-error {expected primary-expression before} "" { target c++ } .-1 } */
+    for (int i = 0; i < 100; ++i)
+	dummy (i);
+
+    #pragma omp parallel for
+    #pragma omp tile sizes(1,2 /* { dg-error {expected '\,' before end of line} } */
+    for (int i = 0; i < 100; ++i)
+	dummy (i);
+
+    #pragma omp parallel for
+    #pragma omp tile sizes /* { dg-error {expected '\(' before end of line} } */
+    for (int i = 0; i < 100; ++i)
+	dummy (i);
+
+    #pragma omp parallel for
+    #pragma omp tile sizes(1) sizes(1) /* { dg-error {expected end of line before 'sizes'} } */
+    for (int i = 0; i < 100; ++i)
+	dummy (i);
+
+    #pragma omp parallel for
+    #pragma omp tile sizes(1)
+    #pragma omp tile sizes(1)
+    for (int i = 0; i < 100; ++i)
+	dummy (i);
+
+    #pragma omp parallel for
+    #pragma omp tile sizes(1, 2)
+    #pragma omp 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);
+
+    #pragma omp parallel for
+    #pragma omp tile sizes(1, 2)
+    #pragma omp tile sizes(1, 2)
+    for (int i = 0; i < 100; ++i)
+    for (int j = 0; j < 100; ++j)
+    	dummy (i);
+
+    #pragma omp parallel for
+    #pragma omp tile sizes(5, 6)
+    #pragma omp 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);
+
+    #pragma omp parallel for
+    #pragma omp tile sizes(1)
+    #pragma omp unroll partia /* { dg-error {expected an OpenMP clause before 'partia'} } */
+    #pragma omp tile sizes(1)
+    for (int i = 0; i < 100; ++i)
+	dummy (i);
+
+    #pragma omp parallel for
+    #pragma omp tile sizes(1)
+    #pragma omp 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);
+
+    #pragma omp parallel for
+    #pragma omp tile sizes(1)
+    #pragma omp unroll full /* { dg-error {'full' clause is invalid here; turns loop into non-loop} } */
+    for (int i = 0; i < 100; ++i)
+	dummy (i);
+
+    #pragma omp parallel for
+    #pragma omp tile sizes(1)
+    #pragma omp unroll partial
+    #pragma omp tile sizes(1)
+    for (int i = 0; i < 100; ++i)
+	dummy (i);
+
+    #pragma omp parallel for
+    #pragma omp tile sizes(8,8)
+    #pragma omp unroll partial /* { dg-error {nesting depth left after this transformation too low for outer transformation} } */
+    #pragma omp tile sizes(1)
+    for (int i = 0; i < 100; ++i)
+	dummy (i);
+
+    #pragma omp parallel for
+    #pragma omp tile sizes(8,8)
+    #pragma omp unroll partial /* { dg-error {nesting depth left after this transformation too low for outer transformation} } */
+    for (int i = 0; i < 100; ++i)
+	dummy (i);
+
+    #pragma omp parallel for
+    #pragma omp tile sizes(1, 2)
+    for (int i = 0; i < 100; ++i)
+    for (int j = 0; j < 100; ++j)
+	dummy (i);
+
+    #pragma omp parallel for
+    #pragma omp 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);
+
+    #pragma omp parallel for
+    #pragma omp 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);
+
+    #pragma omp parallel for
+    #pragma omp 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);
+
+    #pragma omp parallel for
+    #pragma omp tile sizes(1)
+    for (int i = 0; i < 100; ++i)
+      {
+	dummy (i);
+        for (int j = 0; j < 100; ++j)
+          dummy (i);
+      }
+
+    #pragma omp parallel for
+    #pragma omp tile sizes(1)
+    for (int i = 0; i < 100; ++i)
+      {
+        for (int j = 0; j < 100; ++j)
+	    dummy (j);
+	dummy (i);
+      }
+
+    #pragma omp parallel for
+    #pragma omp 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);
+      }
+
+    #pragma omp parallel for
+    #pragma omp 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);
+      }
+
+    #pragma omp parallel for
+    #pragma omp tile sizes(1)
+    for (int i = 0; i < 100; ++i)
+	dummy (i);
+}
diff --git a/gcc/testsuite/c-c++-common/gomp/loop-transforms/tile-3.c b/gcc/testsuite/c-c++-common/gomp/loop-transforms/tile-3.c
new file mode 100644
index 00000000000..e0ba1d6c444
--- /dev/null
+++ b/gcc/testsuite/c-c++-common/gomp/loop-transforms/tile-3.c
@@ -0,0 +1,109 @@ 
+extern void dummy (int);
+
+void
+test ()
+{
+    #pragma omp for
+    #pragma omp 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);
+
+    #pragma omp for
+    #pragma omp 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);
+
+
+#pragma omp for collapse(1)
+    #pragma omp tile sizes(1)
+    for (int i = 0; i < 100; ++i)
+    for (int j = 0; j < 100; ++j)
+	dummy (i);
+
+#pragma omp for collapse(2)
+    #pragma omp 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);
+
+#pragma omp for collapse(2)
+    #pragma omp tile sizes(1, 2)
+    for (int i = 0; i < 100; ++i)
+    for (int j = 0; j < 100; ++j)
+	dummy (i);
+
+#pragma omp for collapse(3)
+    #pragma omp 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);
+
+#pragma omp for collapse(1)
+#pragma omp tile sizes(1)
+#pragma omp tile sizes(1)
+    for (int i = 0; i < 100; ++i)
+	dummy (i);
+
+#pragma omp for collapse(2)
+#pragma omp tile sizes(1, 2)
+#pragma omp 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);
+
+#pragma omp for collapse(2)
+#pragma omp tile sizes(1, 2)
+#pragma omp tile sizes(1, 2)
+    for (int i = 0; i < 100; ++i) /* { dg-error {not enough nested loops} } */
+    	dummy (i);
+
+#pragma omp for collapse(2)
+#pragma omp tile sizes(5, 6)
+#pragma omp tile sizes(1, 2, 3)
+    for (int i = 0; i < 100; ++i) /* { dg-error {not enough nested loops} } */
+    	dummy (i);
+
+#pragma omp for collapse(1)
+#pragma omp tile sizes(1)
+#pragma omp tile sizes(1)
+    for (int i = 0; i < 100; ++i)
+	dummy (i);
+
+#pragma omp for collapse(2)
+#pragma omp tile sizes(1, 2)
+#pragma omp 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);
+
+#pragma omp for collapse(2)
+#pragma omp tile sizes(1, 2)
+#pragma omp tile sizes(1, 2)
+    for (int i = 0; i < 100; ++i)
+    for (int j = 0; j < 100; ++j)
+    	dummy (i);
+
+#pragma omp for collapse(2)
+#pragma omp tile sizes(5, 6)
+#pragma omp 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);
+
+#pragma omp for collapse(3)
+#pragma omp tile sizes(1, 2) /* { dg-error {nesting depth left after this transformation too low for loop collapse} } */
+#pragma omp 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);
+
+#pragma omp for collapse(3)
+#pragma omp tile sizes(5, 6) /* { dg-error {nesting depth left after this transformation too low for loop collapse} } */
+#pragma omp 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/c-c++-common/gomp/loop-transforms/tile-4.c b/gcc/testsuite/c-c++-common/gomp/loop-transforms/tile-4.c
new file mode 100644
index 00000000000..d46bb0cb642
--- /dev/null
+++ b/gcc/testsuite/c-c++-common/gomp/loop-transforms/tile-4.c
@@ -0,0 +1,322 @@ 
+/* { dg-do run } */
+/* { dg-options "-O0 -fopenmp-simd" } */
+
+#include <stdio.h>
+
+#define ASSERT_EQ(var, val) if (var != val) { fprintf (stderr, "%s:%d: Unexpected value %d, expected %d\n", __FILE__, __LINE__, var, val); \
+    __builtin_abort (); }
+
+int
+test1 ()
+{
+  int iter = 0;
+  int i;
+#pragma omp tile sizes(3)
+  for (i = 0; i < 10; i=i+2)
+	{
+	  ASSERT_EQ (i, iter)
+	  iter = iter + 2;
+	}
+
+  ASSERT_EQ (i, 10)
+  return iter;
+}
+
+int
+test2 ()
+{
+  int iter = 0;
+  int i;
+#pragma omp tile sizes(3)
+  for (i = 0; i < 10; i=i+2)
+	{
+	  ASSERT_EQ (i, iter)
+	  iter = iter + 2;
+	}
+
+  ASSERT_EQ (i, 10)
+  return iter;
+}
+
+int
+test3 ()
+{
+  int iter = 0;
+  int i;
+#pragma omp tile sizes(8)
+  for (i = 0; i < 10; i=i+2)
+	{
+	  ASSERT_EQ (i, iter)
+	  iter = iter + 2;
+	}
+
+  ASSERT_EQ (i, 10)
+  return iter;
+}
+
+int
+test4 ()
+{
+  int iter = 10;
+  int i;
+#pragma omp tile sizes(8)
+  for (i = 10; i > 0; i=i-2)
+	{
+	  ASSERT_EQ (i, iter)
+	  iter = iter - 2;
+	}
+  ASSERT_EQ (i, 0)
+  return iter;
+}
+
+int
+test5 ()
+{
+  int iter = 10;
+  int i;
+#pragma omp tile sizes(71)
+  for (i = 10; i > 0; i=i-2)
+	{
+	  ASSERT_EQ (i, iter)
+	  iter = iter - 2;
+	}
+
+  ASSERT_EQ (i, 0)
+  return iter;
+}
+
+int
+test6 ()
+{
+  int iter = 10;
+  int i;
+#pragma omp tile sizes(1)
+  for (i = 10; i > 0; i=i-2)
+	{
+	  ASSERT_EQ (i, iter)
+	  iter = iter - 2;
+	}
+  ASSERT_EQ (i, 0)
+  return iter;
+}
+
+int
+test7 ()
+{
+  int iter = 5;
+  int i;
+#pragma omp tile sizes(2)
+  for (i = 5; i < -5; i=i-3)
+	{
+	  fprintf (stderr, "%d\n", i);
+	  __builtin_abort ();
+	  iter = iter - 3;
+	}
+
+  ASSERT_EQ (i, 5)
+
+  /* No iteration expected */
+  return iter;
+}
+
+int
+test8 ()
+{
+  int iter = 5;
+  int i;
+#pragma omp tile sizes(2)
+  for (i = 5; i > -5; i=i-3)
+	{
+	  ASSERT_EQ (i, iter)
+	  /* Expect only first iteration of the last tile to execute */
+	  if (iter != -4)
+	    iter = iter - 3;
+	}
+
+  ASSERT_EQ (i, -7)
+  return iter;
+}
+
+
+int
+test9 ()
+{
+  int iter = 5;
+  int i;
+#pragma omp tile sizes(5)
+  for (i = 5; i >= -5; i=i-4)
+	{
+	  ASSERT_EQ (i, iter)
+	  /* Expect only first iteration of the last tile to execute */
+	  if (iter != - 3)
+	    iter = iter - 4;
+	}
+
+  ASSERT_EQ (i, -7)
+  return iter;
+}
+
+int
+test10 ()
+{
+  int iter = 5;
+  int i;
+#pragma omp tile sizes(5)
+  for (i = 5; i >= -5; i--)
+	{
+	  ASSERT_EQ (i, iter)
+	  iter--;
+	}
+
+  ASSERT_EQ (i, -6)
+  return iter;
+}
+
+int
+test11 ()
+{
+  int iter = 5;
+  int i;
+#pragma omp tile sizes(15)
+  for (i = 5; i != -5; i--)
+	{
+	  ASSERT_EQ (i, iter)
+	  iter--;
+	}
+  ASSERT_EQ (i, -5)
+  return iter;
+}
+
+int
+test12 ()
+{
+  int iter = 0;
+  unsigned i;
+#pragma omp tile sizes(3)
+  for (i = 0; i != 5; i++)
+	{
+	  ASSERT_EQ (i, iter)
+	  iter++;
+	}
+
+  ASSERT_EQ (i, 5)
+  return iter;
+}
+
+int
+test13 ()
+{
+  int iter = -5;
+  long long unsigned int i;
+#pragma omp tile sizes(15)
+  for (int i = -5; i < 5; i=i+3)
+	{
+	  ASSERT_EQ (i, iter)
+	  iter++;
+	}
+
+  ASSERT_EQ (i, 5)
+  return iter;
+}
+
+int
+test14 (unsigned init, int step)
+{
+  int iter = init;
+  long long unsigned int i;
+#pragma omp tile sizes(8)
+  for (i = init; i < 2*init; i=i+step)
+    iter++;
+
+  ASSERT_EQ (i, 2*init)
+  return iter;
+}
+
+int
+test15 (unsigned init, int step)
+{
+  int iter = init;
+  int i;
+#pragma omp tile sizes(8)
+  for (unsigned i = init; i > 2* init; i=i+step)
+    iter++;
+
+  return iter;
+}
+
+int
+main ()
+{
+  int last_iter;
+
+  last_iter = test1 ();
+  ASSERT_EQ (last_iter, 10);
+
+  last_iter = test2 ();
+  ASSERT_EQ (last_iter, 10);
+
+  last_iter = test3 ();
+  ASSERT_EQ (last_iter, 10);
+
+  last_iter = test4 ();
+  ASSERT_EQ (last_iter, 0);
+
+  last_iter = test5 ();
+  ASSERT_EQ (last_iter, 0);
+
+  last_iter = test6 ();
+  ASSERT_EQ (last_iter, 0);
+
+  last_iter = test7 ();
+  ASSERT_EQ (last_iter, 5);
+
+  last_iter = test8 ();
+  ASSERT_EQ (last_iter, -4);
+
+  last_iter = test9 ();
+  ASSERT_EQ (last_iter, -3);
+
+  last_iter = test10 ();
+  ASSERT_EQ (last_iter, -6);
+  return 0;
+
+  last_iter = test11 ();
+  ASSERT_EQ (last_iter, -4);
+  return 0;
+
+  last_iter = test12 ();
+  ASSERT_EQ (last_iter, 5);
+  return 0;
+
+  last_iter = test13 ();
+  ASSERT_EQ (last_iter, 4);
+  return 0;
+
+  last_iter = test14 (0, 1);
+  ASSERT_EQ (last_iter, 0);
+  return 0;
+
+  last_iter = test14 (0, -1);
+  ASSERT_EQ (last_iter, 0);
+  return 0;
+
+  last_iter = test14 (8, 2);
+  ASSERT_EQ (last_iter, 16);
+  return 0;
+
+  last_iter = test14 (5, 3);
+  ASSERT_EQ (last_iter, 9);
+  return 0;
+
+  last_iter = test15 (8, -1);
+  ASSERT_EQ (last_iter, 9);
+  return 0;
+
+  last_iter = test15 (8, -2);
+  ASSERT_EQ (last_iter, 10);
+  return 0;
+
+  last_iter = test15 (5, -3);
+  ASSERT_EQ (last_iter, 6);
+  return 0;
+}
diff --git a/gcc/testsuite/c-c++-common/gomp/loop-transforms/tile-5.c b/gcc/testsuite/c-c++-common/gomp/loop-transforms/tile-5.c
new file mode 100644
index 00000000000..815318ab27a
--- /dev/null
+++ b/gcc/testsuite/c-c++-common/gomp/loop-transforms/tile-5.c
@@ -0,0 +1,150 @@ 
+/* { dg-do run } */
+/* { dg-options "-O0 -fopenmp-simd" } */
+
+#include <stdio.h>
+
+#define ASSERT_EQ(var, val) if (var != val) { fprintf (stderr, "%s:%d: Unexpected value %d\n", __FILE__, __LINE__, var); \
+    __builtin_abort (); }
+
+#define ASSERT_EQ_PTR(var, ptr) if (var != ptr) { fprintf (stderr, "%s:%d: Unexpected value %p\n", __FILE__, __LINE__, var); \
+    __builtin_abort (); }
+
+int
+test1 (int data[10])
+{
+  int iter = 0;
+  int *i;
+  #pragma omp tile sizes(5)
+  for (i = data; i < data + 10 ; i++)
+	{
+	  ASSERT_EQ (*i, data[iter]);
+	  ASSERT_EQ_PTR (i, data + iter);
+	  iter++;
+	}
+
+  ASSERT_EQ_PTR (i, data + 10)
+  return iter;
+}
+
+int
+test2 (int data[10])
+{
+  int iter = 0;
+  int *i;
+  #pragma omp tile sizes(5)
+  for (i = data; i < data + 10 ; i=i+2)
+	{
+	  ASSERT_EQ_PTR (i, data + 2 * iter);
+	  ASSERT_EQ (*i, data[2 * iter]);
+	  iter++;
+	}
+
+  ASSERT_EQ_PTR (i, data + 10)
+  return iter;
+}
+
+int
+test3 (int data[10])
+{
+  int iter = 0;
+  int *i;
+  #pragma omp tile sizes(5)
+  for (i = data; i <= data + 9 ; i=i+2)
+	{
+	  ASSERT_EQ (*i, data[2 * iter]);
+	  iter++;
+	}
+
+  ASSERT_EQ_PTR (i, data + 10)
+  return iter;
+}
+
+int
+test4 (int data[10])
+{
+  int iter = 0;
+  int *i;
+  #pragma omp tile sizes(5)
+  for (i = data; i != data + 10 ; i=i+1)
+	{
+	  ASSERT_EQ (*i, data[iter]);
+	  iter++;
+	}
+
+  ASSERT_EQ_PTR (i, data + 10)
+  return iter;
+}
+
+int
+test5 (int data[10])
+{
+  int iter = 0;
+  int *i;
+  #pragma omp tile sizes(3)
+  for (i = data + 9; i >= data ; i--)
+	{
+	  ASSERT_EQ (*i, data[9 - iter]);
+	  iter++;
+	}
+
+  ASSERT_EQ_PTR (i, data - 1)
+  return iter;
+}
+
+int
+test6 (int data[10])
+{
+  int iter = 0;
+  int *i;
+  #pragma omp tile sizes(3)
+  for (i = data + 9; i > data - 1 ; i--)
+	{
+	  ASSERT_EQ (*i, data[9 - iter]);
+	  iter++;
+	}
+
+  ASSERT_EQ_PTR (i, data - 1)
+  return iter;
+}
+
+int
+test7 (int data[10])
+{
+  int iter = 0;
+  #pragma omp tile sizes(1)
+  for (int *i = data + 9; i != data - 1 ; i--)
+	{
+	  ASSERT_EQ (*i, data[9 - iter]);
+	  iter++;
+	}
+
+  return iter;
+}
+
+int
+main ()
+{
+  int iter_count;
+  int data[10] = { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9 };
+
+  iter_count = test1 (data);
+  ASSERT_EQ (iter_count, 10);
+
+  iter_count = test2 (data);
+  ASSERT_EQ (iter_count, 5);
+
+  iter_count = test3 (data);
+  ASSERT_EQ (iter_count, 5);
+
+  iter_count = test4 (data);
+  ASSERT_EQ (iter_count, 10);
+
+  iter_count = test5 (data);
+  ASSERT_EQ (iter_count, 10);
+
+  iter_count = test6 (data);
+  ASSERT_EQ (iter_count, 10);
+
+  iter_count = test7 (data);
+  ASSERT_EQ (iter_count, 10);
+}
diff --git a/gcc/testsuite/c-c++-common/gomp/loop-transforms/tile-6.c b/gcc/testsuite/c-c++-common/gomp/loop-transforms/tile-6.c
new file mode 100644
index 00000000000..8132128a5a8
--- /dev/null
+++ b/gcc/testsuite/c-c++-common/gomp/loop-transforms/tile-6.c
@@ -0,0 +1,34 @@ 
+/* { dg-do run } */
+/* { dg-options "-O0 -fopenmp-simd" } */
+
+#include <stdio.h>
+
+int
+test1 ()
+{
+   int sum = 0;
+for (int k = 0; k < 10; k++)
+  {
+#pragma omp tile sizes(5,7)
+  for (int i = 0; i < 10; i++)
+  for (int j = 0; j < 10; j=j+2)
+	{
+	  sum = sum + 1;
+	}
+  }
+
+  return sum;
+}
+
+int
+main ()
+{
+  int result = test1 ();
+
+  if (result != 500)
+    {
+      fprintf (stderr, "Wrong result: %d\n", result);
+    __builtin_abort ();
+    }
+  return 0;
+}
diff --git a/gcc/testsuite/c-c++-common/gomp/loop-transforms/tile-7.c b/gcc/testsuite/c-c++-common/gomp/loop-transforms/tile-7.c
new file mode 100644
index 00000000000..cd25a62c5c0
--- /dev/null
+++ b/gcc/testsuite/c-c++-common/gomp/loop-transforms/tile-7.c
@@ -0,0 +1,31 @@ 
+/* { dg-do run } */
+/* { dg-options "-O0 -fopenmp-simd" } */
+
+#include <stdio.h>
+#define ASSERT_EQ(var, val) if (var != val) { fprintf (stderr, "%s:%d: Unexpected value %d\n", __FILE__, __LINE__, var); \
+    __builtin_abort (); }
+
+#define ASSERT_EQ_PTR(var, ptr) if (var != ptr) { fprintf (stderr, "%s:%d: Unexpected value %p\n", __FILE__, __LINE__, var); \
+    __builtin_abort (); }
+
+int
+main ()
+{
+  int iter_count;
+  int data[10] = { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9 };
+
+  int iter = 0;
+  int *i;
+  #pragma omp tile sizes(1)
+  for (i = data; i < data + 10; i=i+2)
+	{
+	  ASSERT_EQ_PTR (i, data + 2 * iter);
+	  ASSERT_EQ (*i, data[2 * iter]);
+	  iter++;
+	}
+
+  unsigned long real_iter_count = ((unsigned long)i - (unsigned long)data) / (sizeof (int) * 2);
+  ASSERT_EQ (real_iter_count, 5);
+
+  return 0;
+}
diff --git a/gcc/testsuite/c-c++-common/gomp/loop-transforms/tile-8.c b/gcc/testsuite/c-c++-common/gomp/loop-transforms/tile-8.c
new file mode 100644
index 00000000000..c26e03d7e74
--- /dev/null
+++ b/gcc/testsuite/c-c++-common/gomp/loop-transforms/tile-8.c
@@ -0,0 +1,40 @@ 
+/* { dg-do run } */
+/* { dg-options "-O0 -fopenmp-simd" } */
+
+#include <stdio.h>
+
+#define ASSERT_EQ(var, val) if (var != val) { fprintf (stderr, "%s:%d: Unexpected value %d, expected %d\n", __FILE__, __LINE__, var, val); \
+    __builtin_abort (); }
+
+int
+main ()
+{
+  int iter_j = 0, iter_k = 0;
+  unsigned i, j, k;
+#pragma omp tile sizes(3,5,8)
+  for (i = 0; i < 2; i=i+2)
+  for (j = 0; j < 3; j=j+1)
+  for (k = 0; k < 5; k=k+3)
+	{
+	  /* fprintf (stderr, "i=%d j=%d k=%d\n", i, j, k);
+	   * fprintf (stderr, "iter_j=%d iter_k=%d\n", iter_j, iter_k); */
+	  ASSERT_EQ (i, 0);
+	  if (k == 0)
+	    {
+	      ASSERT_EQ (j, iter_j);
+	      iter_k = 0;
+	    }
+
+	  ASSERT_EQ (k, iter_k);
+
+	  iter_k = iter_k + 3;
+	  if (k == 3)
+	    iter_j++;
+	}
+
+  ASSERT_EQ (i, 2);
+  ASSERT_EQ (j, 3);
+  ASSERT_EQ (k, 6);
+
+  return 0;
+}
diff --git a/gcc/testsuite/c-c++-common/gomp/loop-transforms/unroll-1.c b/gcc/testsuite/c-c++-common/gomp/loop-transforms/unroll-1.c
new file mode 100644
index 00000000000..d496dc29053
--- /dev/null
+++ b/gcc/testsuite/c-c++-common/gomp/loop-transforms/unroll-1.c
@@ -0,0 +1,133 @@ 
+extern void dummy (int);
+
+void
+test1 ()
+{
+#pragma omp unroll partial
+  for (int i = 0; i < 100; ++i)
+    dummy (i);
+}
+
+void
+test2 ()
+{
+#pragma omp unroll partial(10)
+  for (int i = 0; i < 100; ++i)
+    dummy (i);
+}
+
+void
+test3 ()
+{
+#pragma omp unroll full
+  for (int i = 0; i < 100; ++i)
+    dummy (i);
+}
+
+void
+test4 ()
+{
+#pragma omp unroll full
+  for (int i = 0; i > 100; ++i)
+    dummy (i);
+}
+
+void
+test5 ()
+{
+#pragma omp unroll full
+  for (int i = 1; i <= 100; ++i)
+    dummy (i);
+}
+
+void
+test6 ()
+{
+#pragma omp unroll full
+  for (int i = 200; i >= 100; i--)
+    dummy (i);
+}
+
+void
+test7 ()
+{
+#pragma omp unroll full
+  for (int i = -100; i > 100; ++i)
+    dummy (i);
+}
+
+void
+test8 ()
+{
+#pragma omp unroll full
+  for (int i = 100; i > -200; --i)
+    dummy (i);
+}
+
+void
+test9 ()
+{
+#pragma omp unroll full
+  for (int i = -300; i != 100; ++i)
+    dummy (i);
+}
+
+void
+test10 ()
+{
+#pragma omp unroll full
+  for (int i = -300; i != 100; ++i)
+    dummy (i);
+}
+
+void
+test12 ()
+{
+#pragma omp unroll full
+#pragma omp unroll partial
+#pragma omp unroll partial
+  for (int i = -300; i != 100; ++i)
+    dummy (i);
+}
+
+void
+test13 ()
+{
+  for (int i = 0; i < 100; ++i)
+#pragma omp unroll full
+#pragma omp unroll partial
+#pragma omp unroll partial
+  for (int j = -300; j != 100; ++j)
+    dummy (i);
+}
+
+void
+test14 ()
+{
+  #pragma omp for
+  for (int i = 0; i < 100; ++i)
+#pragma omp unroll full
+#pragma omp unroll partial
+#pragma omp unroll partial
+  for (int j = -300; j != 100; ++j)
+    dummy (i);
+}
+
+void
+test15 ()
+{
+  #pragma omp for
+  for (int i = 0; i < 100; ++i)
+    {
+
+    dummy (i);
+
+#pragma omp unroll full
+#pragma omp unroll partial
+#pragma omp unroll partial
+  for (int j = -300; j != 100; ++j)
+    dummy (j);
+
+  dummy (i);
+    }
+ }
diff --git a/gcc/testsuite/c-c++-common/gomp/loop-transforms/unroll-2.c b/gcc/testsuite/c-c++-common/gomp/loop-transforms/unroll-2.c
new file mode 100644
index 00000000000..cb37e7410c8
--- /dev/null
+++ b/gcc/testsuite/c-c++-common/gomp/loop-transforms/unroll-2.c
@@ -0,0 +1,95 @@ 
+/* { dg-prune-output "error: invalid controlling predicate" } */
+/* { dg-additional-options "-std=c++11" { target c++} } */
+
+extern void dummy (int);
+
+void
+test ()
+{
+#pragma omp unroll partial
+#pragma omp unroll full /* { dg-error {'full' clause is invalid here; turns loop into non-loop} } */
+  for (int i = -300; i != 100; ++i)
+    dummy (i);
+
+#pragma omp for
+#pragma omp unroll full /* { dg-error {'full' clause is invalid here; turns loop into non-loop} } */
+#pragma omp unroll partial
+  for (int i = -300; i != 100; ++i)
+    dummy (i);
+
+#pragma omp for
+#pragma omp unroll full /* { dg-error {'full' clause is invalid here; turns loop into non-loop} } */
+#pragma omp unroll full
+  for (int i = -300; i != 100; ++i)
+    dummy (i);
+
+#pragma omp for
+#pragma omp unroll partial partial /* { dg-error {too many 'partial' clauses} } */
+  for (int i = -300; i != 100; ++i)
+    dummy (i);
+
+#pragma omp unroll full full /* { dg-error {too many 'full' clauses} } */
+  for (int i = -300; i != 100; ++i)
+    dummy (i);
+
+#pragma omp unroll partial
+#pragma omp 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);
+
+#pragma omp for
+#pragma omp 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;
+#pragma omp for
+#pragma omp unroll( /* { dg-error {expected an OpenMP clause before '\(' token} } */
+  for (int i = -300; i != 100; ++i)
+    dummy (i);
+
+#pragma omp for
+#pragma omp unroll foo /* { dg-error {expected an OpenMP clause before 'foo'} } */
+  for (int i = -300; i != 100; ++i)
+    dummy (i);
+
+#pragma omp unroll partial( /* { dg-error {expected expression before end of line} "" { target c } } */
+  /* { dg-error {expected primary-expression before end of line} "" { target c++ } .-1 } */
+  for (int i = -300; i != 100; ++i)
+    dummy (i);
+
+#pragma omp unroll partial() /* { dg-error {expected expression before '\)' token} "" { target c } } */
+  /* { dg-error {expected primary-expression before '\)' token} "" { target c++ } .-1 } */
+  for (int i = -300; i != 100; ++i)
+    dummy (i);
+
+#pragma omp 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);
+
+#pragma omp unroll parti /* { dg-error {expected an OpenMP clause before 'parti'} } */
+  for (int i = -300; i != 100; ++i)
+    dummy (i);
+
+#pragma omp for
+#pragma omp unroll partial(1)
+#pragma omp unroll parti /* { dg-error {expected an OpenMP clause before 'parti'} } */
+  for (int i = -300; i != 100; ++i)
+    dummy (i);
+
+#pragma omp for
+#pragma omp unroll partial(1)
+#pragma omp unroll parti /* { dg-error {expected an OpenMP clause before 'parti'} } */
+  for (int i = -300; i != 100; ++i)
+    dummy (i);
+
+int sum = 0;
+#pragma omp parallel for reduction(+ : sum) collapse(2)
+#pragma omp 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/c-c++-common/gomp/loop-transforms/unroll-3.c b/gcc/testsuite/c-c++-common/gomp/loop-transforms/unroll-3.c
new file mode 100644
index 00000000000..7ace5657b26
--- /dev/null
+++ b/gcc/testsuite/c-c++-common/gomp/loop-transforms/unroll-3.c
@@ -0,0 +1,18 @@ 
+/* { dg-additional-options "-fdump-tree-omp_transform_loops" }
+ * { dg-additional-options "-fdump-tree-original" } */
+
+extern void dummy (int);
+
+void
+test1 ()
+{
+  int i;
+#pragma omp 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/c-c++-common/gomp/loop-transforms/unroll-4.c b/gcc/testsuite/c-c++-common/gomp/loop-transforms/unroll-4.c
new file mode 100644
index 00000000000..5e473a099d3
--- /dev/null
+++ b/gcc/testsuite/c-c++-common/gomp/loop-transforms/unroll-4.c
@@ -0,0 +1,19 @@ 
+/* { dg-additional-options "-fdump-tree-omp_transform_loops" }
+ * { dg-additional-options "-fdump-tree-original" } */
+
+extern void dummy (int);
+
+void
+test1 ()
+{
+  int i;
+#pragma omp unroll
+  for (int i = 0; i < 100; i++)
+    dummy (i);
+}
+
+/* Loop should not be unrolled, but the internal representation should be lowered
+ * { dg-final { scan-tree-dump "#pragma omp loop_transform" "original" } }
+ * { dg-final { scan-tree-dump-not "#pragma omp" "omp_transform_loops" } }
+ * { dg-final { scan-tree-dump-times "dummy" 1 "omp_transform_loops" } }
+ * { dg-final { scan-tree-dump-times {if \(i < .+?.+goto.+else goto.*?$} 1 "omp_transform_loops" } } */
diff --git a/gcc/testsuite/c-c++-common/gomp/loop-transforms/unroll-5.c b/gcc/testsuite/c-c++-common/gomp/loop-transforms/unroll-5.c
new file mode 100644
index 00000000000..9d5101bdc60
--- /dev/null
+++ b/gcc/testsuite/c-c++-common/gomp/loop-transforms/unroll-5.c
@@ -0,0 +1,19 @@ 
+/* { dg-additional-options "-fdump-tree-omp_transform_loops -fopt-info-omp-optimized-missed" }
+ * { dg-additional-options "-fdump-tree-original" } */
+
+extern void dummy (int);
+
+void
+test1 ()
+{
+  int i;
+#pragma omp unroll partial  /* { dg-optimized {'partial' clause without unrolling factor turned into 'partial\(5\)' clause} } */
+  for (int i = 0; i < 100; i++)
+    dummy (i);
+}
+
+/* Loop should be unrolled 5 times and the internal representation should be lowered
+ * { dg-final { scan-tree-dump "#pragma omp loop_transform" "original" } }
+ * { dg-final { scan-tree-dump-not "#pragma omp" "omp_transform_loops" } }
+ * { dg-final { scan-tree-dump-times "dummy" 5 "omp_transform_loops" } }
+ * { dg-final { scan-tree-dump-times {if \(i < .+?.+goto.+else goto.*?$} 1 "omp_transform_loops" } } */
diff --git a/gcc/testsuite/c-c++-common/gomp/loop-transforms/unroll-6.c b/gcc/testsuite/c-c++-common/gomp/loop-transforms/unroll-6.c
new file mode 100644
index 00000000000..ee2d000239d
--- /dev/null
+++ b/gcc/testsuite/c-c++-common/gomp/loop-transforms/unroll-6.c
@@ -0,0 +1,20 @@ 
+/* { dg-additional-options "--param=omp-unroll-default-factor=100" }
+ * { dg-additional-options "-fdump-tree-omp_transform_loops -fopt-info-omp-optimized-missed" }
+ * { dg-additional-options "-fdump-tree-original" } */
+
+extern void dummy (int);
+
+void
+test1 ()
+{
+  int i;
+#pragma omp unroll /* { dg-optimized {added 'partial\(100\)' clause to 'omp unroll' directive} } */
+  for (int i = 0; i < 100; i++)
+    dummy (i);
+}
+
+/* Loop should be unrolled 5 times and the internal representation should be lowered
+ * { dg-final { scan-tree-dump "#pragma omp loop_transform unroll_none" "original" } }
+ * { dg-final { scan-tree-dump-not "#pragma omp" "omp_transform_loops" } }
+ * { dg-final { scan-tree-dump-times "dummy" 100 "omp_transform_loops" } }
+ * { dg-final { scan-tree-dump-times {if \(i < .+?.+goto.+else goto.*?$} 1 "omp_transform_loops" } } */
diff --git a/gcc/testsuite/c-c++-common/gomp/loop-transforms/unroll-7.c b/gcc/testsuite/c-c++-common/gomp/loop-transforms/unroll-7.c
new file mode 100644
index 00000000000..0458cb030a9
--- /dev/null
+++ b/gcc/testsuite/c-c++-common/gomp/loop-transforms/unroll-7.c
@@ -0,0 +1,144 @@ 
+/* { dg-do run } */
+/* { dg-options "-O0 -fopenmp-simd" } */
+
+#include <stdio.h>
+
+#define ASSERT_EQ(var, val) if (var != val) { fprintf (stderr, "%s:%d: Unexpected value %d\n", __FILE__, __LINE__, var); \
+    __builtin_abort (); }
+
+#define ASSERT_EQ_PTR(var, ptr) if (var != ptr) { fprintf (stderr, "%s:%d: Unexpected value %p\n", __FILE__, __LINE__, var); \
+    __builtin_abort (); }
+
+int
+test1 (int data[10])
+{
+  int iter = 0;
+  int *i;
+  #pragma omp unroll partial(8)
+  for (i = data; i < data + 10 ; i++)
+	{
+	  ASSERT_EQ (*i, data[iter]);
+	  ASSERT_EQ_PTR (i, data + iter);
+	  iter++;
+	}
+
+  return iter;
+}
+
+int
+test2 (int data[10])
+{
+  int iter = 0;
+  int *i;
+  #pragma omp unroll partial(8)
+  for (i = data; i < data + 10 ; i=i+2)
+	{
+	  ASSERT_EQ_PTR (i, data + 2 * iter);
+	  ASSERT_EQ (*i, data[2 * iter]);
+	  iter++;
+	}
+
+  return iter;
+}
+
+int
+test3 (int data[10])
+{
+  int iter = 0;
+  int *i;
+  #pragma omp unroll partial(8)
+  for (i = data; i <= data + 9 ; i=i+2)
+	{
+	  ASSERT_EQ (*i, data[2 * iter]);
+	  iter++;
+	}
+
+  return iter;
+}
+
+int
+test4 (int data[10])
+{
+  int iter = 0;
+  int *i;
+  #pragma omp unroll partial(8)
+  for (i = data; i != data + 10 ; i=i+1)
+	{
+	  ASSERT_EQ (*i, data[iter]);
+	  iter++;
+	}
+
+  return iter;
+}
+
+int
+test5 (int data[10])
+{
+  int iter = 0;
+  int *i;
+  #pragma omp unroll partial(7)
+  for (i = data + 9; i >= data ; i--)
+	{
+	  ASSERT_EQ (*i, data[9 - iter]);
+	  iter++;
+	}
+
+  return iter;
+}
+
+int
+test6 (int data[10])
+{
+  int iter = 0;
+  int *i;
+  #pragma omp unroll partial(7)
+  for (i = data + 9; i > data - 1 ; i--)
+	{
+	  ASSERT_EQ (*i, data[9 - iter]);
+	  iter++;
+	}
+
+  return iter;
+}
+
+int
+test7 (int data[10])
+{
+  int iter = 0;
+  #pragma omp unroll partial(7)
+  for (int *i = data + 9; i != data - 1 ; i--)
+	{
+	  ASSERT_EQ (*i, data[9 - iter]);
+	  iter++;
+	}
+
+  return iter;
+}
+
+int
+main ()
+{
+  int iter_count;
+  int data[10] = { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9 };
+
+  iter_count = test1 (data);
+  ASSERT_EQ (iter_count, 10);
+
+  iter_count = test2 (data);
+  ASSERT_EQ (iter_count, 5);
+
+  iter_count = test3 (data);
+  ASSERT_EQ (iter_count, 5);
+
+  iter_count = test4 (data);
+  ASSERT_EQ (iter_count, 10);
+
+  iter_count = test5 (data);
+  ASSERT_EQ (iter_count, 10);
+
+  iter_count = test6 (data);
+  ASSERT_EQ (iter_count, 10);
+
+  iter_count = test7 (data);
+  ASSERT_EQ (iter_count, 10);
+}
diff --git a/gcc/testsuite/c-c++-common/gomp/loop-transforms/unroll-8.c b/gcc/testsuite/c-c++-common/gomp/loop-transforms/unroll-8.c
new file mode 100644
index 00000000000..d49d7c42c87
--- /dev/null
+++ b/gcc/testsuite/c-c++-common/gomp/loop-transforms/unroll-8.c
@@ -0,0 +1,76 @@ 
+extern void dummy(int);
+
+void
+test1 ()
+{
+#pragma omp unroll full /* { dg-warning "Cannot apply full unrolling to infinite loop" } */
+  for (int i = 101; i > 100; i++)
+    dummy (i);
+}
+
+
+void
+test2 ()
+{
+#pragma omp unroll full
+  for (int i = 101; i != 100; i++)
+    dummy (i);
+}
+
+void
+test3 ()
+{
+#pragma omp unroll full /* { dg-warning "Cannot apply full unrolling to infinite loop" } */
+  for (int i = 0; i <= 0; i--)
+    dummy (i);
+}
+
+void
+test4 ()
+{
+#pragma omp unroll full /* { dg-warning "Cannot apply full unrolling to infinite loop" } */
+  for (int i = 101; i > 100; i=i+2)
+    dummy (i);
+}
+
+void
+test5 ()
+{
+#pragma omp unroll full /* { dg-warning "Cannot apply full unrolling to infinite loop" } */
+  for (int i = -101; i < 100; i=i-10)
+    dummy (i);
+}
+
+void
+test6 ()
+{
+#pragma omp unroll full /* { dg-warning "Cannot apply full unrolling to infinite loop" } */
+  for (int i = -101; i < 100; i=i-300)
+    dummy (i);
+}
+
+void
+test7 ()
+{
+#pragma omp unroll full /* { dg-warning "Cannot apply full unrolling to infinite loop" } */
+  for (int i = 101; i > -100; i=i+300)
+    dummy (i);
+
+  /* Loop does not iterate, hence no warning. */
+#pragma omp unroll full
+  for (int i = 101; i > 101; i=i+300)
+    dummy (i);
+}
+
+void
+test8 ()
+{
+#pragma omp unroll full /* { dg-warning "Cannot apply full unrolling to infinite loop" } */
+  for (int i = -21; i < -20; i=i-40)
+    dummy (i);
+
+  /* Loop does not iterate, hence no warning. */
+#pragma omp unroll full
+  for (int i = -21; i > 20; i=i-40)
+    dummy (i);
+}
diff --git a/gcc/testsuite/c-c++-common/gomp/loop-transforms/unroll-inner-1.c b/gcc/testsuite/c-c++-common/gomp/loop-transforms/unroll-inner-1.c
new file mode 100644
index 00000000000..c365d942591
--- /dev/null
+++ b/gcc/testsuite/c-c++-common/gomp/loop-transforms/unroll-inner-1.c
@@ -0,0 +1,15 @@ 
+/* { dg-additional-options "-std=c++11" { target c++} } */
+
+extern void dummy (int);
+
+void
+test ()
+{
+
+#pragma omp target parallel for collapse(2)
+  for (int i = -300; i != 100; ++i)
+    #pragma omp unroll partial
+    for (int j = 0; j != 100; ++j)
+      dummy (i);
+}
+
diff --git a/gcc/testsuite/c-c++-common/gomp/loop-transforms/unroll-inner-2.c b/gcc/testsuite/c-c++-common/gomp/loop-transforms/unroll-inner-2.c
new file mode 100644
index 00000000000..2082009a385
--- /dev/null
+++ b/gcc/testsuite/c-c++-common/gomp/loop-transforms/unroll-inner-2.c
@@ -0,0 +1,29 @@ 
+/* { dg-additional-options "-std=c++11" { target c++} } */
+
+extern void dummy (int);
+
+void
+test ()
+{
+
+#pragma omp target parallel for collapse(2)
+  for (int i = -300; i != 100; ++i)
+#pragma omp tile sizes(2)
+    for (int j = 0; j != 100; ++j)
+      dummy (i);
+
+#pragma omp target parallel for collapse(2)
+  for (int i = -300; i != 100; ++i) /* { dg-error {not enough nested loops} } */
+#pragma omp tile sizes(2, 3)
+    for (int j = 0; j != 100; ++j)
+      dummy (i);
+
+#pragma omp target parallel for collapse(2)
+  for (int i = -300; i != 100; ++i)
+#pragma omp 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/c-c++-common/gomp/loop-transforms/unroll-non-rect-1.c b/gcc/testsuite/c-c++-common/gomp/loop-transforms/unroll-non-rect-1.c
new file mode 100644
index 00000000000..40e7f8e4bfb
--- /dev/null
+++ b/gcc/testsuite/c-c++-common/gomp/loop-transforms/unroll-non-rect-1.c
@@ -0,0 +1,37 @@ 
+extern void dummy (int);
+
+void
+test1 ()
+{
+#pragma omp target parallel for collapse(2)
+  for (int i = -300; i != 100; ++i)
+#pragma omp unroll partial(2)
+    for (int j = i * 2; j <= i * 4 + 1; ++j)
+      dummy (i);
+
+#pragma omp target parallel for collapse(3)
+  for (int i = -300; i != 100; ++i)
+    for (int j = i; j != i * 2; ++j)
+    #pragma omp unroll partial
+    for (int k = 2; k != 100; ++k)
+      dummy (i);
+
+#pragma omp unroll full
+  for (int i = -300; i != 100; ++i)
+    for (int j = i; j != i * 2; ++j)
+    for (int k = 2; k != 100; ++k)
+      dummy (i);
+
+  for (int i = -300; i != 100; ++i)
+#pragma omp unroll full
+    for (int j = i; j != i + 10; ++j)
+    for (int k = 2; k != 100; ++k)
+      dummy (i);
+
+  for (int i = -300; i != 100; ++i)
+#pragma omp unroll full
+    for (int j = i; j != i + 10; ++j)
+    for (int k = j; k != 100; ++k)
+      dummy (i);
+}
+
diff --git a/gcc/testsuite/c-c++-common/gomp/loop-transforms/unroll-non-rect-2.c b/gcc/testsuite/c-c++-common/gomp/loop-transforms/unroll-non-rect-2.c
new file mode 100644
index 00000000000..7696e5d5fab
--- /dev/null
+++ b/gcc/testsuite/c-c++-common/gomp/loop-transforms/unroll-non-rect-2.c
@@ -0,0 +1,22 @@ 
+extern void dummy (int);
+
+void
+test1 ()
+{
+#pragma omp target parallel for collapse(2) /* { dg-error {invalid OpenMP non-rectangular loop step; \'\(1 - 0\) \* 1\' is not a multiple of loop 2 step \'5\'} "" { target c } } */
+  for (int i = -300; i != 100; ++i) /* { dg-error {invalid OpenMP non-rectangular loop step; \'\(1 - 0\) \* 1\' is not a multiple of loop 2 step \'5\'} "" { target c++ } } */
+#pragma omp unroll partial
+    for (int j = 2; j != i; ++j)
+      dummy (i);
+}
+
+void
+test2 ()
+{
+  int i,j;
+#pragma omp target parallel for collapse(2)
+  for (i = -300; i != 100; ++i)
+    #pragma omp unroll partial
+    for (j = 2; j != i; ++j)
+      dummy (i);
+}
diff --git a/gcc/testsuite/c-c++-common/gomp/loop-transforms/unroll-simd-1.c b/gcc/testsuite/c-c++-common/gomp/loop-transforms/unroll-simd-1.c
new file mode 100644
index 00000000000..1cd4d6e7322
--- /dev/null
+++ b/gcc/testsuite/c-c++-common/gomp/loop-transforms/unroll-simd-1.c
@@ -0,0 +1,84 @@ 
+/* { dg-options "-fno-openmp -fopenmp-simd" } */
+/* { dg-do run } */
+/* { dg-additional-options "-fdump-tree-original" } */
+/* { dg-additional-options "-fdump-tree-omp_transform_loops" } */
+
+#include <stdio.h>
+
+int compute_sum1 ()
+{
+  int sum = 0;
+  int i,j;
+
+#pragma omp simd reduction(+:sum)
+  for (i = 3; i < 10; ++i)
+  #pragma omp unroll full
+  for (j = -2; j < 7; ++j)
+    sum++;
+
+  if (j != 7)
+    __builtin_abort;
+
+  return sum;
+}
+
+int compute_sum2()
+{
+  int sum = 0;
+  int i,j;
+#pragma omp simd reduction(+:sum)
+#pragma omp unroll partial(5)
+  for (i = 3; i < 10; ++i)
+  for (j = -2; j < 7; ++j)
+    sum++;
+
+  if (j != 7)
+    __builtin_abort;
+
+  return sum;
+}
+
+int compute_sum3()
+{
+  int sum = 0;
+  int i,j;
+#pragma omp simd reduction(+:sum)
+#pragma omp unroll partial(1)
+  for (i = 3; i < 10; ++i)
+  for (j = -2; j < 7; ++j)
+    sum++;
+
+  if (j != 7)
+    __builtin_abort;
+
+  return sum;
+}
+
+int main ()
+{
+  int result = compute_sum1 ();
+  if (result != 7 * 9)
+    {
+      fprintf (stderr, "%d: Wrong result %d\n", __LINE__, result);
+      __builtin_abort ();
+    }
+
+  result = compute_sum1 ();
+  if (result != 7 * 9)
+    {
+      fprintf (stderr, "%d: Wrong result %d\n", __LINE__, result);
+      __builtin_abort ();
+    }
+
+  result = compute_sum3 ();
+  if (result != 7 * 9)
+    {
+      fprintf (stderr, "%d: Wrong result %d\n", __LINE__, result);
+      __builtin_abort ();
+    }
+
+  return 0;
+}
+
+/* { dg-final { scan-tree-dump {omp loop_transform} "original" } } */
+/* { dg-final { scan-tree-dump-not {omp loop_transform} "omp_transform_loops" } } */
diff --git a/gcc/testsuite/g++.dg/gomp/attrs-4.C b/gcc/testsuite/g++.dg/gomp/attrs-4.C
index 005add826ba..a730ad7db50 100644
--- a/gcc/testsuite/g++.dg/gomp/attrs-4.C
+++ b/gcc/testsuite/g++.dg/gomp/attrs-4.C
@@ -49,7 +49,7 @@  foo (int x)
   for (int i = 0; i < 16; i++)
     ;
   #pragma omp for
-  [[omp::directive (master)]]					// { dg-error "for statement expected before '\\\[' token" }
+  [[omp::directive (master)]]					// { dg-error "loop nest expected before '\\\[' token" }
   ;
   #pragma omp target teams
   [[omp::directive (parallel)]]					// { dg-error "mixing OpenMP directives with attribute and pragma syntax on the same statement" }
diff --git a/gcc/testsuite/g++.dg/gomp/for-1.C b/gcc/testsuite/g++.dg/gomp/for-1.C
index f8bb9d54727..0e042fd1381 100644
--- a/gcc/testsuite/g++.dg/gomp/for-1.C
+++ b/gcc/testsuite/g++.dg/gomp/for-1.C
@@ -24,7 +24,7 @@  void foo (int j, int k)
 
   // Malformed parallel loops.
   #pragma omp for
-  i = 0;		// { dg-error "for statement expected" }
+  i = 0;		// { dg-error "loop nest expected" }
   for ( ; i < 10; )
     {
       baz (i);
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);
+}
+
diff --git a/gcc/testsuite/g++.dg/gomp/loop-transforms/tile-1.h b/gcc/testsuite/g++.dg/gomp/loop-transforms/tile-1.h
new file mode 100644
index 00000000000..166d1d48677
--- /dev/null
+++ b/gcc/testsuite/g++.dg/gomp/loop-transforms/tile-1.h
@@ -0,0 +1,27 @@ 
+// { dg-do compile }
+// { dg-additional-options "-std=c++11" }
+
+#include <vector>
+
+extern void dummy (int);
+
+template<class T, int U, unsigned V> void
+test1_template ()
+{
+  std::vector<int> v;
+
+  for (unsigned i = 0; i < 10; i++)
+    v.push_back (i);
+
+#pragma omp for
+  for (int i : v)
+    dummy (i);
+
+#pragma omp tile sizes (U, 10, V)
+  for (T i : v)
+  for (T j : v)
+  for (T k : v)
+    dummy (i);
+}
+
+void test () { test1_template <long, 5, 3> (); };
diff --git a/gcc/testsuite/g++.dg/gomp/loop-transforms/tile-1a.C b/gcc/testsuite/g++.dg/gomp/loop-transforms/tile-1a.C
new file mode 100644
index 00000000000..1ee76da3d4a
--- /dev/null
+++ b/gcc/testsuite/g++.dg/gomp/loop-transforms/tile-1a.C
@@ -0,0 +1,27 @@ 
+// { dg-do compile }
+// { dg-additional-options "-std=c++11" }
+
+#include <vector>
+
+extern void dummy (int);
+
+template<class T, int U, unsigned V> void
+test1_template ()
+{
+  std::vector<int> v;
+
+  for (unsigned i = 0; i < 10; i++)
+    v.push_back (i);
+
+#pragma omp teams distribute parallel for num_teams(V)
+  for (int i : v)
+    dummy (i);
+
+#pragma omp tile sizes (V, U)
+  for (T i : v)
+  for (T j : v)
+  for (T k : v)
+    dummy (i);
+}
+
+void test () { test1_template <long, 5, 3> (); };
diff --git a/gcc/testsuite/g++.dg/gomp/loop-transforms/tile-1b.C b/gcc/testsuite/g++.dg/gomp/loop-transforms/tile-1b.C
new file mode 100644
index 00000000000..263c9b301c6
--- /dev/null
+++ b/gcc/testsuite/g++.dg/gomp/loop-transforms/tile-1b.C
@@ -0,0 +1,27 @@ 
+// { dg-do compile }
+// { dg-additional-options "-std=c++11" }
+
+#include <vector>
+
+extern void dummy (int);
+
+template<class T, int U, unsigned V> void
+test1_template ()
+{
+  std::vector<int> v;
+
+  for (unsigned i = 0; i < 10; i++)
+    v.push_back (i);
+
+#pragma omp for
+  for (int i : v)
+    dummy (i);
+
+#pragma omp tile sizes (U, 10, V) // { dg-error {'tile sizes' argument needs positive integral constant} }
+  for (T i : v)
+  for (T j : v)
+  for (T k : v)
+    dummy (i);
+}
+
+void test () { test1_template <long, 5, 0> (); };
diff --git a/gcc/testsuite/g++.dg/gomp/loop-transforms/unroll-1.C b/gcc/testsuite/g++.dg/gomp/loop-transforms/unroll-1.C
new file mode 100644
index 00000000000..cba37c88ebe
--- /dev/null
+++ b/gcc/testsuite/g++.dg/gomp/loop-transforms/unroll-1.C
@@ -0,0 +1,42 @@ 
+// { dg-do compile }
+// { dg-additional-options "-std=c++11" }
+#include <vector>
+
+extern void dummy (int);
+
+void
+test1 ()
+{
+  std::vector<int> v;
+
+  for (unsigned i = 0; i < 1000; i++)
+    v.push_back (i);
+
+#pragma omp for
+  for (int i : v)
+    dummy (i);
+
+#pragma omp unroll partial(5)
+  for (int i : v)
+    dummy (i);
+}
+
+void
+test2 ()
+{
+  std::vector<std::vector<int>> v;
+
+  for (unsigned i = 0; i < 10; i++)
+    {
+      std::vector<int> u;
+      for (unsigned j = 0; j < 10; j++)
+	u.push_back (j);
+      v.push_back (u);
+    }
+
+#pragma omp for
+#pragma omp unroll partial(5)
+  for (auto u : v)
+    for (int i : u)
+      dummy (i);
+}
diff --git a/gcc/testsuite/g++.dg/gomp/loop-transforms/unroll-2.C b/gcc/testsuite/g++.dg/gomp/loop-transforms/unroll-2.C
new file mode 100644
index 00000000000..f606f3de757
--- /dev/null
+++ b/gcc/testsuite/g++.dg/gomp/loop-transforms/unroll-2.C
@@ -0,0 +1,47 @@ 
+// { dg-do link }
+// { dg-additional-options "-std=c++11" }
+#include <vector>
+
+extern void dummy (int);
+
+template<class T, int U1, int U2, int U3> void
+test_template ()
+{
+  std::vector<int> v;
+
+  for (unsigned i = 0; i < 1000; i++)
+    v.push_back (i);
+
+#pragma omp for
+  for (int i : v)
+    dummy (i);
+
+#pragma omp unroll partial(U1)
+  for (T i : v)
+    dummy (i);
+
+#pragma omp unroll partial(U2) // { dg-error {partial argument needs positive constant integer expression} }
+  for (T i : v)
+    dummy (i);
+
+#pragma omp unroll partial(U3) // { dg-error {partial argument needs positive constant integer expression} }
+  for (T i : v)
+    dummy (i);
+
+#pragma omp for
+#pragma omp unroll partial(U1)
+  for (T i : v)
+    dummy (i);
+
+#pragma omp for
+#pragma omp unroll partial(U2) // { dg-error {partial argument needs positive constant integer expression} }
+  for (T i : v)
+    dummy (i);
+
+#pragma omp for
+#pragma omp unroll partial(U3) // { dg-error {partial argument needs positive constant integer expression} }
+  for (T i : v)
+    dummy (i);
+}
+
+void test () { test_template <long, 5,-2, 0> (); };
diff --git a/gcc/testsuite/g++.dg/gomp/loop-transforms/unroll-3.C b/gcc/testsuite/g++.dg/gomp/loop-transforms/unroll-3.C
new file mode 100644
index 00000000000..ae9f5500360
--- /dev/null
+++ b/gcc/testsuite/g++.dg/gomp/loop-transforms/unroll-3.C
@@ -0,0 +1,37 @@ 
+// { dg-do compile }
+// { dg-additional-options "-std=c++11" }
+// { dg-additional-options "-fdump-tree-omp_transform_loops -fopt-info-omp-optimized-missed" }
+// { dg-additional-options "-fdump-tree-original" }
+#include <vector>
+
+extern void dummy (int);
+
+constexpr unsigned fib (unsigned n)
+{
+  return n <= 2 ? 1 : fib (n-1) + fib (n-2);
+}
+
+void
+test1 ()
+{
+  std::vector<int> v;
+
+  for (unsigned i = 0; i < 1000; i++)
+    v.push_back (i);
+
+#pragma omp unroll partial(fib(10))
+  for (int i : v)
+    dummy (i);
+}
+
+
+// Loop should be unrolled fib(10) = 55 times
+// ! { dg-final { scan-tree-dump {#pragma omp loop_transform unroll_partial\(55\)} "original" } }
+// ! { dg-final { scan-tree-dump-not "#pragma omp" "omp_transform_loops" } }
+// ! { dg-final { scan-tree-dump-times "dummy" 55 "omp_transform_loops" } }
+
+// There should be one loop that fills the vector ...
+// ! { dg-final { scan-tree-dump-times {if \(i.*? <= .+?.+goto.+else goto.*?$} 1 "omp_transform_loops" } }
+
+// ... and one resulting from the lowering of the unrolled loop
+// ! { dg-final { scan-tree-dump-times {if \(D\.[0-9]+ < retval.+?.+goto.+else goto.*?$} 1 "omp_transform_loops" } }
diff --git a/gcc/testsuite/g++.dg/gomp/pr94512.C b/gcc/testsuite/g++.dg/gomp/pr94512.C
index 8ba0e65795f..1d5cf150987 100644
--- a/gcc/testsuite/g++.dg/gomp/pr94512.C
+++ b/gcc/testsuite/g++.dg/gomp/pr94512.C
@@ -8,7 +8,7 @@  void
 bar ()
 {
 #pragma omp parallel master taskloop
-  foo ();	// { dg-error "for statement expected before" }
+  foo ();	// { dg-error "loop nest expected before" }
 }
 
 void
diff --git a/gcc/testsuite/gcc.dg/gomp/for-1.c b/gcc/testsuite/gcc.dg/gomp/for-1.c
index 80e0d0be844..ecaf0c55796 100644
--- a/gcc/testsuite/gcc.dg/gomp/for-1.c
+++ b/gcc/testsuite/gcc.dg/gomp/for-1.c
@@ -26,7 +26,7 @@  void foo (int j, int k)
 
   /* Malformed parallel loops.  */
   #pragma omp for
-  i = 0;		/* { dg-error "3:for statement expected" } */
+  i = 0;		/* { dg-error "3:loop nest expected" } */
   for ( ; i < 10; )
     {
       baz (i);
diff --git a/gcc/testsuite/gcc.dg/gomp/for-11.c b/gcc/testsuite/gcc.dg/gomp/for-11.c
index 8c747cdb981..abafa487283 100644
--- a/gcc/testsuite/gcc.dg/gomp/for-11.c
+++ b/gcc/testsuite/gcc.dg/gomp/for-11.c
@@ -30,7 +30,7 @@  void foo (int j, int k)
 
   /* Malformed parallel loops.  */
   #pragma omp for
-  i = 0;		/* { dg-error "for statement expected" } */
+  i = 0;		/* { dg-error "loop nest expected" } */
   for ( ; i < 10; )
     {
       baz (i);
diff --git a/libgomp/testsuite/libgomp.c++/loop-transforms/matrix-no-directive-unroll-full-1.C b/libgomp/testsuite/libgomp.c++/loop-transforms/matrix-no-directive-unroll-full-1.C
new file mode 100644
index 00000000000..3a684219627
--- /dev/null
+++ b/libgomp/testsuite/libgomp.c++/loop-transforms/matrix-no-directive-unroll-full-1.C
@@ -0,0 +1,13 @@ 
+/* { dg-additional-options { -O0 -fdump-tree-original -Wall -Wno-unknown-pragmas } } */
+
+#define COMMON_DIRECTIVE
+#define COMMON_TOP_TRANSFORM omp unroll full
+#define COLLAPSE_1
+#define COLLAPSE_2
+#define COLLAPSE_3
+#define IMPLEMENTATION_FILE "../../libgomp.c-c++-common/loop-transforms/matrix-constant-iter.h"
+
+#include "../../libgomp.c-c++-common/loop-transforms/matrix-transform-variants-1.h"
+
+/* A consistency check to prevent broken macro usage. */
+/* { dg-final { scan-tree-dump-times "unroll_full" 13 "original" } } */
diff --git a/libgomp/testsuite/libgomp.c++/loop-transforms/tile-2.C b/libgomp/testsuite/libgomp.c++/loop-transforms/tile-2.C
new file mode 100644
index 00000000000..780421fa4c7
--- /dev/null
+++ b/libgomp/testsuite/libgomp.c++/loop-transforms/tile-2.C
@@ -0,0 +1,69 @@ 
+// { dg-additional-options "-std=c++11" }
+// { dg-additional-options "-O0" }
+
+#include <vector>
+#include <stdio.h>
+
+constexpr unsigned fib (unsigned n)
+{
+  return n <= 2 ? 1 : fib (n-1) + fib (n-2);
+}
+
+int
+test1 ()
+{
+  std::vector<int> v;
+
+  for (unsigned i = 0; i <= 9; i++)
+    v.push_back (1);
+
+  int sum = 0;
+  for (int k = 0; k < 10; k++)
+    #pragma omp tile sizes(fib(4))
+    for (int i : v) {
+      for (int j = 8; j != -2; --j)
+	sum = sum + i;
+    }
+
+  return sum;
+}
+
+int
+test2 ()
+{
+  std::vector<int> v;
+
+  for (unsigned i = 0; i <= 10; i++)
+    v.push_back (i);
+
+  int sum = 0;
+  for (int k = 0; k < 10; k++)
+#pragma omp parallel for collapse(2) reduction(+:sum)
+#pragma omp tile sizes(fib(4), 1)
+  for (int i : v)
+    for (int j = 8; j > -2; --j)
+	sum = sum + i;
+
+  return sum;
+}
+
+int
+main ()
+{
+  int result = test1 ();
+
+  if (result != 1000)
+    {
+      fprintf (stderr, "%d: Wrong result: %d\n", __LINE__, result);
+      __builtin_abort ();
+    }
+
+  result = test2 ();
+  if (result != 5500)
+    {
+      fprintf (stderr, "%d: Wrong result: %d\n", __LINE__, result);
+    __builtin_abort ();
+    }
+
+  return 0;
+}
diff --git a/libgomp/testsuite/libgomp.c++/loop-transforms/tile-3.C b/libgomp/testsuite/libgomp.c++/loop-transforms/tile-3.C
new file mode 100644
index 00000000000..91ec8f5c137
--- /dev/null
+++ b/libgomp/testsuite/libgomp.c++/loop-transforms/tile-3.C
@@ -0,0 +1,28 @@ 
+// { dg-additional-options "-std=c++11" }
+// { dg-additional-options "-O0" }
+
+#include <vector>
+
+int
+main ()
+{
+  std::vector<int> v;
+  std::vector<int> w;
+
+  for (unsigned i = 0; i <= 9; i++)
+    v.push_back (i);
+
+  int iter = 0;
+#pragma omp for
+#pragma omp tile sizes(5)
+  for (int i : v)
+    {
+      w.push_back (iter);
+      iter++;
+    }
+
+  for (int i = 0; i < w.size (); i++)
+    if (w[i] != i)
+      __builtin_abort ();
+  return 0;
+}
diff --git a/libgomp/testsuite/libgomp.c++/loop-transforms/unroll-1.C b/libgomp/testsuite/libgomp.c++/loop-transforms/unroll-1.C
new file mode 100644
index 00000000000..004eef91649
--- /dev/null
+++ b/libgomp/testsuite/libgomp.c++/loop-transforms/unroll-1.C
@@ -0,0 +1,73 @@ 
+// { dg-additional-options "-std=c++11" }
+// { dg-additional-options "-O0" }
+
+#include <vector>
+#include <stdio.h>
+
+constexpr unsigned fib (unsigned n)
+{
+  return n <= 2 ? 1 : fib (n-1) + fib (n-2);
+}
+
+int
+test1 ()
+{
+  std::vector<int> v;
+
+  for (unsigned i = 0; i <= 9; i++)
+    v.push_back (1);
+
+  int sum = 0;
+  for (int k = 0; k < 10; k++)
+#pragma omp unroll partial(fib(3))
+    for (int i : v) {
+      for (int j = 8; j != -2; --j)
+	sum = sum + i;
+    }
+
+  return sum;
+}
+
+int
+test2 ()
+{
+  std::vector<int> v;
+
+  for (unsigned i = 0; i <= 10; i++)
+    v.push_back (i);
+
+  int sum = 0;
+#pragma omp parallel for reduction(+:sum)
+  for (int k = 0; k < 10; k++)
+#pragma omp unroll
+#pragma omp unroll partial(fib(4))
+  for (int i : v)
+    {
+      #pragma omp unroll full
+      for (int j = 8; j != -2; --j)
+	sum = sum + i;
+    }
+
+  return sum;
+}
+
+int
+main ()
+{
+  int result = test1 ();
+
+  if (result != 1000)
+    {
+      fprintf (stderr, "Wrong result: %d\n", result);
+    __builtin_abort ();
+    }
+
+  result = test2 ();
+  if (result != 5500)
+    {
+      fprintf (stderr, "Wrong result: %d\n", result);
+    __builtin_abort ();
+    }
+
+  return 0;
+}
diff --git a/libgomp/testsuite/libgomp.c++/loop-transforms/unroll-2.C b/libgomp/testsuite/libgomp.c++/loop-transforms/unroll-2.C
new file mode 100644
index 00000000000..90d2775c95b
--- /dev/null
+++ b/libgomp/testsuite/libgomp.c++/loop-transforms/unroll-2.C
@@ -0,0 +1,34 @@ 
+// { dg-do run }
+// { dg-additional-options "-std=c++11" }
+#include <vector>
+#include <iostream>
+
+int
+main ()
+{
+  std::vector<std::vector<int>> v;
+  std::vector<int> w;
+
+  for (unsigned i = 0; i < 10; i++)
+    {
+      std::vector<int> u;
+      for (unsigned j = 0; j < 10; j++)
+	u.push_back (j);
+      v.push_back (u);
+    }
+
+#pragma omp for
+#pragma omp unroll partial(7)
+  for (auto u : v)
+    for (int x : u)
+      w.push_back (x);
+
+  std::size_t l = w.size ();
+  for (std::size_t i = 0; i < l; i++)
+    {
+      if (w[i] != i % 10)
+	__builtin_abort ();
+    }
+
+  return 0;
+}
diff --git a/libgomp/testsuite/libgomp.c++/loop-transforms/unroll-full-tile.C b/libgomp/testsuite/libgomp.c++/loop-transforms/unroll-full-tile.C
new file mode 100644
index 00000000000..8970bfa7fd8
--- /dev/null
+++ b/libgomp/testsuite/libgomp.c++/loop-transforms/unroll-full-tile.C
@@ -0,0 +1,84 @@ 
+#include <stdlib.h>
+#include <stdio.h>
+
+template <int dim0, int dim1>
+int sum ()
+{
+  int sum = 0;
+#pragma omp unroll full
+#pragma omp tile sizes(dim0, dim1)
+  for (unsigned i = 0; i < 4; i++)
+    for (unsigned j = 0; j < 5; j++)
+      sum++;
+
+  return sum;
+}
+
+int main ()
+{
+  if (sum <1,1> () != 20)
+    __builtin_abort ();
+  if (sum <1,2> () != 20)
+    __builtin_abort ();
+  if (sum <1,3> () != 20)
+    __builtin_abort ();
+  if (sum <1,4> () != 20)
+    __builtin_abort ();
+  if (sum <1,5> () != 20)
+    __builtin_abort ();
+
+  if (sum <2,1> () != 20)
+    __builtin_abort ();
+  if (sum <2,2> () != 20)
+    __builtin_abort ();
+  if (sum <2,3> () != 20)
+    __builtin_abort ();
+  if (sum <2,4> () != 20)
+    __builtin_abort ();
+  if (sum <2,5> () != 20)
+    __builtin_abort ();
+
+  if (sum <3,1> () != 20)
+    __builtin_abort ();
+  if (sum <3,2> () != 20)
+    __builtin_abort ();
+  if (sum <3,3> () != 20)
+    __builtin_abort ();
+  if (sum <3,4> () != 20)
+    __builtin_abort ();
+  if (sum <3,5> () != 20)
+    __builtin_abort ();
+
+  if (sum <4,1> () != 20)
+    __builtin_abort ();
+  if (sum <4,2> () != 20)
+    __builtin_abort ();
+  if (sum <4,3> () != 20)
+    __builtin_abort ();
+  if (sum <4,4> () != 20)
+    __builtin_abort ();
+  if (sum <4,5> () != 20)
+    __builtin_abort ();
+
+  if (sum <5,1> () != 20)
+    __builtin_abort ();
+  if (sum <5,2> () != 20)
+    __builtin_abort ();
+  if (sum <5,3> () != 20)
+    __builtin_abort ();
+  if (sum <5,4> () != 20)
+    __builtin_abort ();
+  if (sum <5,5> () != 20)
+    __builtin_abort ();
+
+  if (sum <6,1> () != 20)
+    __builtin_abort ();
+  if (sum <6,2> () != 20)
+    __builtin_abort ();
+  if (sum <6,3> () != 20)
+    __builtin_abort ();
+  if (sum <6,4> () != 20)
+    __builtin_abort ();
+  if (sum <6,5> () != 20)
+    __builtin_abort ();
+}
diff --git a/libgomp/testsuite/libgomp.c-c++-common/imperfect-transform-1.c b/libgomp/testsuite/libgomp.c-c++-common/imperfect-transform-1.c
new file mode 100644
index 00000000000..6743594b2eb
--- /dev/null
+++ b/libgomp/testsuite/libgomp.c-c++-common/imperfect-transform-1.c
@@ -0,0 +1,79 @@ 
+/* { dg-do run } */
+
+/* Like imperfect1.c, but also includes loop transforms.  */
+
+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(2)
+  for (i = 0; i < a1; i++)
+    {
+      f1 (0, i);
+      for (j = 0; j < a2; j++)
+	{
+	  f1 (1, j);
+#pragma omp unroll partial
+	  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/imperfect-transform-2.c b/libgomp/testsuite/libgomp.c-c++-common/imperfect-transform-2.c
new file mode 100644
index 00000000000..e7d6a9941b4
--- /dev/null
+++ b/libgomp/testsuite/libgomp.c-c++-common/imperfect-transform-2.c
@@ -0,0 +1,79 @@ 
+/* { dg-do run } */
+
+/* Like imperfect1.c, but also includes loop transforms.  */
+
+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(2)
+  for (i = 0; i < a1; i++)
+    {
+      f1 (0, i);
+      for (j = 0; j < a2; j++)
+	{
+	  f1 (1, j);
+#pragma omp tile sizes(5)
+	  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/loop-transforms/matrix-1.h b/libgomp/testsuite/libgomp.c-c++-common/loop-transforms/matrix-1.h
new file mode 100644
index 00000000000..b9b865cf554
--- /dev/null
+++ b/libgomp/testsuite/libgomp.c-c++-common/loop-transforms/matrix-1.h
@@ -0,0 +1,70 @@ 
+#include <stdlib.h>
+#include <string.h>
+#include <stdio.h>
+#include <math.h>
+
+#ifndef FUN_NAME_SUFFIX
+#define FUN_NAME_SUFFIX
+#endif
+
+#ifdef MULT
+#undef MULT
+#endif
+#define MULT CAT(mult, FUN_NAME_SUFFIX)
+
+#ifdef MAIN
+#undef MAIN
+#endif
+#define MAIN CAT(main, FUN_NAME_SUFFIX)
+
+void MULT (float *matrix1, float *matrix2, float *result,
+	   unsigned dim0, unsigned dim1)
+{
+  unsigned i;
+
+  memset (result, 0, sizeof (float) * dim0 * dim1);
+  DIRECTIVE
+  TRANSFORMATION1
+  for (i = 0; i < dim0; i++)
+    TRANSFORMATION2
+    for (unsigned j = 0; j < dim1; j++)
+      TRANSFORMATION3
+      for (unsigned k = 0; k < dim1; k++)
+	result[i * dim1 + j] += matrix1[i * dim1 + k] * matrix2[k * dim0 + j];
+}
+
+int MAIN ()
+{
+  unsigned dim0 = 20;
+  unsigned dim1 = 20;
+
+  float *result = (float *)malloc (sizeof (float) * dim0 * dim1);
+  float *matrix1 = (float *)malloc (sizeof (float) * dim0 * dim1);
+  float *matrix2 = (float *)malloc (sizeof (float) * dim0 * dim1);
+
+  for (unsigned i = 0; i < dim0; i++)
+    for (unsigned j = 0; j < dim1; j++)
+      matrix1[i * dim1 + j] = j;
+
+  for (unsigned i = 0; i < dim0; i++)
+    for (unsigned j = 0; j < dim1; j++)
+      if (i == j)
+	matrix2[i * dim1 + j] = 1;
+      else
+	matrix2[i * dim1 + j] = 0;
+
+   MULT (matrix1, matrix2, result, dim0, dim1);
+
+   for (unsigned i = 0; i < dim0; i++)
+     for (unsigned j = 0; j < dim1; j++) {
+       if (matrix1[i * dim1 + j] != result[i * dim1 + j]) {
+	 print_matrix (matrix1, dim0, dim1);
+	 print_matrix (matrix2, dim0, dim1);
+	 print_matrix (result, dim0, dim1);
+	 fprintf(stderr, "%s: ERROR at %d, %d\n", __FUNCTION__, i, j);
+	 abort();
+       }
+     }
+
+  return 0;
+}
diff --git a/libgomp/testsuite/libgomp.c-c++-common/loop-transforms/matrix-constant-iter.h b/libgomp/testsuite/libgomp.c-c++-common/loop-transforms/matrix-constant-iter.h
new file mode 100644
index 00000000000..769c04044c3
--- /dev/null
+++ b/libgomp/testsuite/libgomp.c-c++-common/loop-transforms/matrix-constant-iter.h
@@ -0,0 +1,71 @@ 
+#include <stdlib.h>
+#include <string.h>
+#include <stdio.h>
+#include <math.h>
+
+#ifndef FUN_NAME_SUFFIX
+#define FUN_NAME_SUFFIX
+#endif
+
+#ifdef MULT
+#undef MULT
+#endif
+#define MULT CAT(mult, FUN_NAME_SUFFIX)
+
+#ifdef MAIN
+#undef MAIN
+#endif
+#define MAIN CAT(main, FUN_NAME_SUFFIX)
+
+void MULT (float *matrix1, float *matrix2, float *result)
+{
+  const unsigned dim0 = 20;
+  const unsigned dim1 = 20;
+
+  memset (result, 0, sizeof (float) * dim0 * dim1);
+  DIRECTIVE
+  TRANSFORMATION1
+  for (unsigned i = 0; i < dim0; i++)
+    TRANSFORMATION2
+    for (unsigned j = 0; j < dim1; j++)
+      TRANSFORMATION3
+      for (unsigned k = 0; k < dim1; k++)
+	result[i * dim1 + j] += matrix1[i * dim1 + k] * matrix2[k * dim0 + j];
+}
+
+int MAIN ()
+{
+  const unsigned dim0 = 20;
+  const unsigned dim1 = 20;
+
+  float *result = (float *)malloc (sizeof (float) * dim0 * dim1);
+  float *matrix1 = (float *)malloc (sizeof (float) * dim0 * dim1);
+  float *matrix2 = (float *)malloc (sizeof (float) * dim0 * dim1);
+
+  for (unsigned i = 0; i < dim0; i++)
+    for (unsigned j = 0; j < dim1; j++)
+      matrix1[i * dim1 + j] = j;
+
+  for (unsigned i = 0; i < dim0; i++)
+    for (unsigned j = 0; j < dim1; j++)
+      if (i == j)
+	matrix2[i * dim1 + j] = 1;
+      else
+	matrix2[i * dim1 + j] = 0;
+
+   MULT (matrix1, matrix2, result);
+
+   for (unsigned i = 0; i < dim0; i++)
+     for (unsigned j = 0; j < dim1; j++) {
+       if (matrix1[i * dim1 + j] != result[i * dim1 + j]) {
+	 __builtin_printf("%s: error at %d, %d\n", __FUNCTION__, i, j);
+	 print_matrix (matrix1, dim0, dim1);
+	 print_matrix (matrix2, dim0, dim1);
+	 print_matrix (result, dim0, dim1);
+	 __builtin_printf("\n");
+	 __builtin_abort();
+       }
+     }
+
+  return 0;
+}
diff --git a/libgomp/testsuite/libgomp.c-c++-common/loop-transforms/matrix-helper.h b/libgomp/testsuite/libgomp.c-c++-common/loop-transforms/matrix-helper.h
new file mode 100644
index 00000000000..4f69463d9dd
--- /dev/null
+++ b/libgomp/testsuite/libgomp.c-c++-common/loop-transforms/matrix-helper.h
@@ -0,0 +1,19 @@ 
+#include <stdio.h>
+#include <stdlib.h>
+
+#define CAT(x,y) XCAT(x,y)
+#define XCAT(x,y) x ## y
+#define DO_PRAGMA(x) XDO_PRAGMA(x)
+#define XDO_PRAGMA(x) _Pragma (#x)
+
+
+void print_matrix (float *matrix, unsigned dim0, unsigned dim1)
+{
+  for (unsigned i = 0; i < dim0; i++)
+    {
+      for (unsigned j = 0; j < dim1; j++)
+	fprintf (stderr, "%f ", matrix[i * dim1 + j]);
+      fprintf (stderr, "\n");
+    }
+  fprintf (stderr, "\n");
+}
diff --git a/libgomp/testsuite/libgomp.c-c++-common/loop-transforms/matrix-no-directive-1.c b/libgomp/testsuite/libgomp.c-c++-common/loop-transforms/matrix-no-directive-1.c
new file mode 100644
index 00000000000..7904a5617f3
--- /dev/null
+++ b/libgomp/testsuite/libgomp.c-c++-common/loop-transforms/matrix-no-directive-1.c
@@ -0,0 +1,11 @@ 
+/* { dg-additional-options { -fdump-tree-original -Wall -Wno-unknown-pragmas } } */
+
+#define COMMON_DIRECTIVE
+#define COLLAPSE_1 collapse(1)
+#define COLLAPSE_2 collapse(2)
+#define COLLAPSE_3 collapse(3)
+
+#include "matrix-transform-variants-1.h"
+
+/* A consistency check to prevent broken macro usage. */
+/* { dg-final { scan-tree-dump-times "unroll_partial" 12 "original" } } */
diff --git a/libgomp/testsuite/libgomp.c-c++-common/loop-transforms/matrix-no-directive-unroll-full-1.c b/libgomp/testsuite/libgomp.c-c++-common/loop-transforms/matrix-no-directive-unroll-full-1.c
new file mode 100644
index 00000000000..bd431a25102
--- /dev/null
+++ b/libgomp/testsuite/libgomp.c-c++-common/loop-transforms/matrix-no-directive-unroll-full-1.c
@@ -0,0 +1,13 @@ 
+/* { dg-additional-options { -O2 -fdump-tree-original -Wall -Wno-unknown-pragmas } } */
+
+#define COMMON_DIRECTIVE
+#define COMMON_TOP_TRANSFORM omp unroll full
+#define COLLAPSE_1
+#define COLLAPSE_2
+#define COLLAPSE_3
+#define IMPLEMENTATION_FILE "matrix-constant-iter.h"
+
+#include "matrix-transform-variants-1.h"
+
+/* A consistency check to prevent broken macro usage. */
+/* { dg-final { scan-tree-dump-times "unroll_full" 13 "original" } } */
diff --git a/libgomp/testsuite/libgomp.c-c++-common/loop-transforms/matrix-omp-distribute-parallel-for-1.c b/libgomp/testsuite/libgomp.c-c++-common/loop-transforms/matrix-omp-distribute-parallel-for-1.c
new file mode 100644
index 00000000000..3875014dc96
--- /dev/null
+++ b/libgomp/testsuite/libgomp.c-c++-common/loop-transforms/matrix-omp-distribute-parallel-for-1.c
@@ -0,0 +1,8 @@ 
+/* { dg-additional-options { -Wall -Wno-unknown-pragmas } } */
+
+#define COMMON_DIRECTIVE "omp teams distribute parallel for"
+#define COLLAPSE_1 "collapse(1)"
+#define COLLAPSE_2 "collapse(2)"
+#define COLLAPSE_3 "collapse(3)"
+
+#include "matrix-transform-variants-1.h"
diff --git a/libgomp/testsuite/libgomp.c-c++-common/loop-transforms/matrix-omp-for-1.c b/libgomp/testsuite/libgomp.c-c++-common/loop-transforms/matrix-omp-for-1.c
new file mode 100644
index 00000000000..671396cd533
--- /dev/null
+++ b/libgomp/testsuite/libgomp.c-c++-common/loop-transforms/matrix-omp-for-1.c
@@ -0,0 +1,13 @@ 
+/* { dg-additional-options { -fdump-tree-original -Wall -Wno-unknown-pragmas } } */
+
+#define COMMON_DIRECTIVE omp for
+#define COLLAPSE_1 collapse(1)
+#define COLLAPSE_2 collapse(2)
+#define COLLAPSE_3 collapse(3)
+
+#include "matrix-transform-variants-1.h"
+
+
+/* A consistency check to prevent broken macro usage. */
+/* { dg-final { scan-tree-dump-times "omp for" 13 "original" } } */
+/* { dg-final { scan-tree-dump-times "collapse" 12 "original" } } */
diff --git a/libgomp/testsuite/libgomp.c-c++-common/loop-transforms/matrix-omp-parallel-for-1.c b/libgomp/testsuite/libgomp.c-c++-common/loop-transforms/matrix-omp-parallel-for-1.c
new file mode 100644
index 00000000000..cc66df42679
--- /dev/null
+++ b/libgomp/testsuite/libgomp.c-c++-common/loop-transforms/matrix-omp-parallel-for-1.c
@@ -0,0 +1,13 @@ 
+/* { dg-additional-options { -fdump-tree-original -Wall -Wno-unknown-pragmas } } */
+
+#define COMMON_DIRECTIVE omp parallel for
+#define COLLAPSE_1 collapse(1)
+#define COLLAPSE_2 collapse(2)
+#define COLLAPSE_3
+
+#include "matrix-transform-variants-1.h"
+
+
+/* A consistency check to prevent broken macro usage. */
+/* { dg-final { scan-tree-dump-times "omp parallel" 13 "original" } } */
+/* { dg-final { scan-tree-dump-times "collapse" 9 "original" } } */
diff --git a/libgomp/testsuite/libgomp.c-c++-common/loop-transforms/matrix-omp-parallel-masked-taskloop-1.c b/libgomp/testsuite/libgomp.c-c++-common/loop-transforms/matrix-omp-parallel-masked-taskloop-1.c
new file mode 100644
index 00000000000..890b460f374
--- /dev/null
+++ b/libgomp/testsuite/libgomp.c-c++-common/loop-transforms/matrix-omp-parallel-masked-taskloop-1.c
@@ -0,0 +1,8 @@ 
+/* { dg-additional-options { -Wall -Wno-unknown-pragmas } } */
+
+#define COMMON_DIRECTIVE omp parallel masked taskloop
+#define COLLAPSE_1 collapse(1)
+#define COLLAPSE_2 collapse(2)
+#define COLLAPSE_3
+
+#include "matrix-transform-variants-1.h"
diff --git a/libgomp/testsuite/libgomp.c-c++-common/loop-transforms/matrix-omp-parallel-masked-taskloop-simd-1.c b/libgomp/testsuite/libgomp.c-c++-common/loop-transforms/matrix-omp-parallel-masked-taskloop-simd-1.c
new file mode 100644
index 00000000000..74f6271504a
--- /dev/null
+++ b/libgomp/testsuite/libgomp.c-c++-common/loop-transforms/matrix-omp-parallel-masked-taskloop-simd-1.c
@@ -0,0 +1,8 @@ 
+/* { dg-additional-options { -Wall -Wno-unknown-pragmas } } */
+
+#define COMMON_DIRECTIVE omp parallel masked taskloop simd
+#define COLLAPSE_1 collapse(1)
+#define COLLAPSE_2 collapse(2)
+#define COLLAPSE_3
+
+#include "matrix-transform-variants-1.h"
diff --git a/libgomp/testsuite/libgomp.c-c++-common/loop-transforms/matrix-omp-target-parallel-for-1.c b/libgomp/testsuite/libgomp.c-c++-common/loop-transforms/matrix-omp-target-parallel-for-1.c
new file mode 100644
index 00000000000..4abeda73b48
--- /dev/null
+++ b/libgomp/testsuite/libgomp.c-c++-common/loop-transforms/matrix-omp-target-parallel-for-1.c
@@ -0,0 +1,15 @@ 
+/* This test appears to have too much parallelism to run without a GPU.  */
+/* { dg-do run { target { offload_device } } } */
+/* { dg-additional-options { -fdump-tree-original -Wall -Wno-unknown-pragmas } } */
+
+#define COMMON_DIRECTIVE omp target parallel for map(tofrom:result[0:dim0*dim1]) map(to:matrix1[0:dim0*dim1], matrix2[0:dim0*dim1])
+#define COLLAPSE_1 collapse(1)
+#define COLLAPSE_2 collapse(2)
+#define COLLAPSE_3
+
+#include "matrix-transform-variants-1.h"
+
+/* A consistency check to prevent broken macro usage. */
+/* { dg-final { scan-tree-dump-times "omp target" 13 "original" } } */
+/* { dg-final { scan-tree-dump-times "collapse" 9 "original" } } */
+/* { dg-final { scan-tree-dump-times "unroll_partial" 12 "original" } } */
diff --git a/libgomp/testsuite/libgomp.c-c++-common/loop-transforms/matrix-omp-target-teams-distribute-parallel-for-1.c b/libgomp/testsuite/libgomp.c-c++-common/loop-transforms/matrix-omp-target-teams-distribute-parallel-for-1.c
new file mode 100644
index 00000000000..f836707c43b
--- /dev/null
+++ b/libgomp/testsuite/libgomp.c-c++-common/loop-transforms/matrix-omp-target-teams-distribute-parallel-for-1.c
@@ -0,0 +1,10 @@ 
+/* This test appears to have too much parallelism to run without a GPU.  */
+/* { dg-do run { target { offload_device } } } */
+/* { dg-additional-options { -Wall -Wno-unknown-pragmas } } */
+
+#define COMMON_DIRECTIVE omp target teams distribute parallel for map(tofrom:result[:dim0*dim1]) map(to:matrix1[0:dim0*dim1], matrix2[0:dim0*dim1])
+#define COLLAPSE_1 collapse(1)
+#define COLLAPSE_2 collapse(2)
+#define COLLAPSE_3
+
+#include "matrix-transform-variants-1.h"
diff --git a/libgomp/testsuite/libgomp.c-c++-common/loop-transforms/matrix-omp-taskloop-1.c b/libgomp/testsuite/libgomp.c-c++-common/loop-transforms/matrix-omp-taskloop-1.c
new file mode 100644
index 00000000000..28edb6ce83e
--- /dev/null
+++ b/libgomp/testsuite/libgomp.c-c++-common/loop-transforms/matrix-omp-taskloop-1.c
@@ -0,0 +1,8 @@ 
+/* { dg-additional-options { -Wall -Wno-unknown-pragmas } } */
+
+#define COMMON_DIRECTIVE omp taskloop
+#define COLLAPSE_1 collapse(1)
+#define COLLAPSE_2 collapse(2)
+#define COLLAPSE_3 collapse(3)
+
+#include "matrix-transform-variants-1.h"
diff --git a/libgomp/testsuite/libgomp.c-c++-common/loop-transforms/matrix-omp-teams-distribute-parallel-for-1.c b/libgomp/testsuite/libgomp.c-c++-common/loop-transforms/matrix-omp-teams-distribute-parallel-for-1.c
new file mode 100644
index 00000000000..481a20a18d0
--- /dev/null
+++ b/libgomp/testsuite/libgomp.c-c++-common/loop-transforms/matrix-omp-teams-distribute-parallel-for-1.c
@@ -0,0 +1,8 @@ 
+/* { dg-additional-options { -Wall -Wno-unknown-pragmas } } */
+
+#define COMMON_DIRECTIVE omp teams distribute parallel for
+#define COLLAPSE_1 collapse(1)
+#define COLLAPSE_2 collapse(2)
+#define COLLAPSE_3
+
+#include "matrix-transform-variants-1.h"
diff --git a/libgomp/testsuite/libgomp.c-c++-common/loop-transforms/matrix-simd-1.c b/libgomp/testsuite/libgomp.c-c++-common/loop-transforms/matrix-simd-1.c
new file mode 100644
index 00000000000..200ddd859f5
--- /dev/null
+++ b/libgomp/testsuite/libgomp.c-c++-common/loop-transforms/matrix-simd-1.c
@@ -0,0 +1,8 @@ 
+/* { dg-additional-options { -Wall -Wno-unknown-pragmas } } */
+
+#define COMMON_DIRECTIVE omp simd
+#define COLLAPSE_1 collapse(1)
+#define COLLAPSE_2 collapse(2)
+#define COLLAPSE_3 collapse(3)
+
+#include "matrix-transform-variants-1.h"
diff --git a/libgomp/testsuite/libgomp.c-c++-common/loop-transforms/matrix-transform-variants-1.h b/libgomp/testsuite/libgomp.c-c++-common/loop-transforms/matrix-transform-variants-1.h
new file mode 100644
index 00000000000..24c3d073024
--- /dev/null
+++ b/libgomp/testsuite/libgomp.c-c++-common/loop-transforms/matrix-transform-variants-1.h
@@ -0,0 +1,191 @@ 
+#include "matrix-helper.h"
+
+#ifndef COMMON_TOP_TRANSFORM
+#define COMMON_TOP_TRANSFORM
+#endif
+
+#ifndef IMPLEMENTATION_FILE
+#define IMPLEMENTATION_FILE "matrix-1.h"
+#endif
+
+#define FUN_NAME_SUFFIX 1
+#define DIRECTIVE DO_PRAGMA(COMMON_DIRECTIVE)
+#define TRANSFORMATION1 DO_PRAGMA(COMMON_TOP_TRANSFORM) _Pragma("omp unroll partial(2)") _Pragma("omp tile sizes(10)")
+#define TRANSFORMATION2
+#define TRANSFORMATION3
+#include IMPLEMENTATION_FILE
+
+#undef DIRECTIVE
+#undef TRANSFORMATION1
+#undef TRANSFORMATION2
+#undef TRANSFORMATION3
+#undef FUN_NAME_SUFFIX
+
+#define FUN_NAME_SUFFIX 2
+#define DIRECTIVE DO_PRAGMA(COMMON_DIRECTIVE COLLAPSE_3)
+#define TRANSFORMATION1 DO_PRAGMA(COMMON_TOP_TRANSFORM) _Pragma("omp tile sizes(8,16,4)")
+#define TRANSFORMATION2
+#define TRANSFORMATION3
+#include IMPLEMENTATION_FILE
+
+#undef DIRECTIVE
+#undef TRANSFORMATION1
+#undef TRANSFORMATION2
+#undef TRANSFORMATION3
+#undef FUN_NAME_SUFFIX
+
+#define FUN_NAME_SUFFIX 3
+#define DIRECTIVE DO_PRAGMA(COMMON_DIRECTIVE COLLAPSE_2)
+#define TRANSFORMATION1 DO_PRAGMA(COMMON_TOP_TRANSFORM) _Pragma("omp tile sizes(8, 8)")
+#define TRANSFORMATION2
+#define TRANSFORMATION3
+#include IMPLEMENTATION_FILE
+
+#undef DIRECTIVE
+#undef TRANSFORMATION1
+#undef TRANSFORMATION2
+#undef TRANSFORMATION3
+#undef FUN_NAME_SUFFIX
+
+#define FUN_NAME_SUFFIX 4
+#define DIRECTIVE DO_PRAGMA(COMMON_DIRECTIVE COLLAPSE_1)
+#define TRANSFORMATION1 DO_PRAGMA(COMMON_TOP_TRANSFORM) _Pragma("omp tile sizes(8, 8)")
+#define TRANSFORMATION2
+#define TRANSFORMATION3
+#include IMPLEMENTATION_FILE
+
+#undef DIRECTIVE
+#undef TRANSFORMATION1
+#undef TRANSFORMATION2
+#undef TRANSFORMATION3
+#undef FUN_NAME_SUFFIX
+
+#define FUN_NAME_SUFFIX 5
+#define DIRECTIVE DO_PRAGMA(COMMON_DIRECTIVE COLLAPSE_1)
+#define TRANSFORMATION1 DO_PRAGMA(COMMON_TOP_TRANSFORM) _Pragma("omp tile sizes(8, 8, 8)")
+#define TRANSFORMATION2
+#define TRANSFORMATION3
+#include IMPLEMENTATION_FILE
+
+#undef DIRECTIVE
+#undef TRANSFORMATION1
+#undef TRANSFORMATION2
+#undef TRANSFORMATION3
+#undef FUN_NAME_SUFFIX
+
+#define FUN_NAME_SUFFIX 6
+#define DIRECTIVE DO_PRAGMA(COMMON_DIRECTIVE COLLAPSE_1)
+#define TRANSFORMATION1 DO_PRAGMA(COMMON_TOP_TRANSFORM) _Pragma("omp tile sizes(10)") _Pragma("omp unroll partial(2)")
+#define TRANSFORMATION2
+#define TRANSFORMATION3
+#include IMPLEMENTATION_FILE
+
+#undef DIRECTIVE
+#undef TRANSFORMATION1
+#undef TRANSFORMATION2
+#undef TRANSFORMATION3
+#undef FUN_NAME_SUFFIX
+
+#define FUN_NAME_SUFFIX 7
+#define DIRECTIVE DO_PRAGMA(COMMON_DIRECTIVE COLLAPSE_2)
+#define TRANSFORMATION1 DO_PRAGMA(COMMON_TOP_TRANSFORM) _Pragma("omp tile sizes(7, 11)")
+#define TRANSFORMATION2 _Pragma("omp unroll partial(7)")
+#define TRANSFORMATION3
+#include IMPLEMENTATION_FILE
+
+#undef DIRECTIVE
+#undef TRANSFORMATION1
+#undef TRANSFORMATION2
+#undef TRANSFORMATION3
+#undef FUN_NAME_SUFFIX
+
+#define FUN_NAME_SUFFIX 8
+#define DIRECTIVE DO_PRAGMA(COMMON_DIRECTIVE COLLAPSE_2)
+#define TRANSFORMATION1 DO_PRAGMA(COMMON_TOP_TRANSFORM) _Pragma("omp tile sizes(7, 11)")
+#define TRANSFORMATION2 _Pragma("omp tile sizes(7)") _Pragma("omp unroll partial(7)")
+#define TRANSFORMATION3
+#include IMPLEMENTATION_FILE
+
+#undef DIRECTIVE
+#undef TRANSFORMATION1
+#undef TRANSFORMATION2
+#undef TRANSFORMATION3
+#undef FUN_NAME_SUFFIX
+
+#define FUN_NAME_SUFFIX 9
+#define DIRECTIVE DO_PRAGMA(COMMON_DIRECTIVE COLLAPSE_2)
+#define TRANSFORMATION1 DO_PRAGMA(COMMON_TOP_TRANSFORM) _Pragma("omp tile sizes(7, 11)")
+#define TRANSFORMATION2 _Pragma("omp tile sizes(7)") _Pragma("omp unroll partial(3)") _Pragma("omp tile sizes(7)")
+#define TRANSFORMATION3
+#include IMPLEMENTATION_FILE
+
+#undef DIRECTIVE
+#undef TRANSFORMATION1
+#undef TRANSFORMATION2
+#undef TRANSFORMATION3
+#undef FUN_NAME_SUFFIX
+
+#define FUN_NAME_SUFFIX 10
+#define DIRECTIVE DO_PRAGMA(COMMON_DIRECTIVE COLLAPSE_1)
+#define TRANSFORMATION1 DO_PRAGMA(COMMON_TOP_TRANSFORM) _Pragma("omp unroll partial(5)") _Pragma("omp tile sizes(7)") _Pragma("omp unroll partial(3)") _Pragma("omp tile sizes(7)")
+#define TRANSFORMATION2
+#define TRANSFORMATION3
+#include IMPLEMENTATION_FILE
+
+#undef DIRECTIVE
+#undef TRANSFORMATION1
+#undef TRANSFORMATION2
+#undef TRANSFORMATION3
+#undef FUN_NAME_SUFFIX
+
+#define FUN_NAME_SUFFIX 11
+#define DIRECTIVE DO_PRAGMA(COMMON_DIRECTIVE COLLAPSE_2)
+#define TRANSFORMATION1 DO_PRAGMA(COMMON_TOP_TRANSFORM)
+#define TRANSFORMATION2 _Pragma("omp unroll partial(5)") _Pragma("omp tile sizes(7)") _Pragma("omp unroll partial(3)") _Pragma("omp tile sizes(7)")
+#define TRANSFORMATION3
+#include IMPLEMENTATION_FILE
+
+#undef DIRECTIVE
+#undef TRANSFORMATION1
+#undef TRANSFORMATION2
+#undef TRANSFORMATION3
+#undef FUN_NAME_SUFFIX
+
+#define FUN_NAME_SUFFIX 12
+#define DIRECTIVE DO_PRAGMA(COMMON_DIRECTIVE COLLAPSE_3)
+#define TRANSFORMATION1 DO_PRAGMA(COMMON_TOP_TRANSFORM)
+#define TRANSFORMATION2
+#define TRANSFORMATION3 _Pragma("omp unroll partial(5)") _Pragma("omp tile sizes(7)") _Pragma("omp unroll partial(3)") _Pragma("omp tile sizes(7)")
+#include IMPLEMENTATION_FILE
+
+#undef DIRECTIVE
+#undef TRANSFORMATION1
+#undef TRANSFORMATION2
+#undef TRANSFORMATION3
+#undef FUN_NAME_SUFFIX
+
+#define FUN_NAME_SUFFIX 13
+#define DIRECTIVE DO_PRAGMA(COMMON_DIRECTIVE COLLAPSE_3)
+#define TRANSFORMATION1 DO_PRAGMA(COMMON_TOP_TRANSFORM)
+#define TRANSFORMATION2 _Pragma("omp tile sizes(7,8)")
+#define TRANSFORMATION3 _Pragma("omp unroll partial(3)") _Pragma("omp tile sizes(7)")
+#include IMPLEMENTATION_FILE
+
+int main ()
+{
+  main1 ();
+  main2 ();
+  main3 ();
+  main4 ();
+  main5 ();
+  main6 ();
+  main7 ();
+  main8 ();
+  main9 ();
+  main10 ();
+  main11 ();
+  main12 ();
+  main13 ();
+
+  return 0;
+}
diff --git a/libgomp/testsuite/libgomp.c-c++-common/loop-transforms/unroll-1.c b/libgomp/testsuite/libgomp.c-c++-common/loop-transforms/unroll-1.c
new file mode 100644
index 00000000000..eb5d3d77eb8
--- /dev/null
+++ b/libgomp/testsuite/libgomp.c-c++-common/loop-transforms/unroll-1.c
@@ -0,0 +1,78 @@ 
+/* { dg-additional-options { -Wall -Wno-unknown-pragmas } } */
+
+#include <stdio.h>
+
+int compute_sum1 ()
+{
+  int sum = 0;
+  int i,j;
+#pragma omp parallel for reduction(+:sum) lastprivate(j)
+#pragma omp unroll partial
+  for (i = 3; i < 10; ++i)
+  for (j = -2; j < 7; ++j)
+    sum++;
+
+  if (j != 7)
+    __builtin_abort ();
+
+  return sum;
+}
+
+int compute_sum2()
+{
+  int sum = 0;
+  int i,j;
+#pragma omp parallel for reduction(+:sum) lastprivate(j)
+#pragma omp unroll partial(5)
+  for (i = 3; i < 10; ++i)
+  for (j = -2; j < 7; ++j)
+    sum++;
+
+  if (j != 7)
+    __builtin_abort ();
+
+  return sum;
+}
+
+int compute_sum3()
+{
+  int sum = 0;
+  int i,j;
+#pragma omp parallel for reduction(+:sum)
+#pragma omp unroll partial(1)
+  for (i = 3; i < 10; ++i)
+  for (j = -2; j < 7; ++j)
+    sum++;
+
+  if (j != 7)
+    __builtin_abort ();
+
+  return sum;
+}
+
+int main ()
+{
+  int result;
+  result = compute_sum1 ();
+  if (result != 7 * 9)
+    {
+      fprintf (stderr, "%d: Wrong result %d\n", __LINE__, result);
+      __builtin_abort ();
+    }
+
+  result = compute_sum2 ();
+  if (result != 7 * 9)
+    {
+      fprintf (stderr, "%d: Wrong result %d\n", __LINE__, result);
+      __builtin_abort ();
+    }
+
+  result = compute_sum3 ();
+  if (result != 7 * 9)
+    {
+      fprintf (stderr, "%d: Wrong result %d\n", __LINE__, result);
+      __builtin_abort ();
+    }
+
+  return 0;
+}
diff --git a/libgomp/testsuite/libgomp.c-c++-common/loop-transforms/unroll-non-rect-1.c b/libgomp/testsuite/libgomp.c-c++-common/loop-transforms/unroll-non-rect-1.c
new file mode 100644
index 00000000000..7bd9b906235
--- /dev/null
+++ b/libgomp/testsuite/libgomp.c-c++-common/loop-transforms/unroll-non-rect-1.c
@@ -0,0 +1,131 @@ 
+/* { dg-additional-options { -Wall -Wno-unknown-pragmas } } */
+
+#include <stdio.h>
+#include <stdlib.h>
+
+void test1 ()
+{
+  int sum = 0;
+  for (int i = -3; i != 1; ++i)
+    for (int j = -2; j < i * -1; ++j)
+      sum++;
+
+  if (sum != 14)
+    {
+      fprintf (stderr, "%s: Wrong sum: %d\n", __FUNCTION__, sum);
+      abort ();
+    }
+}
+
+void test2 ()
+{
+  int sum = 0;
+  #pragma omp unroll partial
+  for (int i = -3; i != 1; ++i)
+    for (int j = -2; j < i * -1; ++j)
+      sum++;
+
+  if (sum != 14)
+    {
+      fprintf (stderr, "%s: Wrong sum: %d\n", __FUNCTION__, sum);
+      abort ();
+    }
+}
+
+void test3 ()
+{
+  int sum = 0;
+  #pragma omp unroll partial
+  for (int i = -3; i != 1; ++i)
+  #pragma omp unroll partial
+    for (int j = -2; j < i * -1; ++j)
+      sum++;
+
+  if (sum != 14)
+    {
+      fprintf (stderr, "%s: Wrong sum: %d\n", __FUNCTION__, sum);
+      abort ();
+    }
+}
+
+void test4 ()
+{
+  int sum = 0;
+#pragma omp for
+#pragma omp unroll partial(5)
+  for (int i = -3; i != 1; ++i)
+#pragma omp unroll partial(2)
+    for (int j = -2; j < i * -1; ++j)
+      sum++;
+
+  if (sum != 14)
+    {
+      fprintf (stderr, "%s: Wrong sum: %d\n", __FUNCTION__, sum);
+      abort ();
+    }
+}
+
+void test5 ()
+{
+  int sum = 0;
+#pragma omp parallel for reduction(+:sum)
+#pragma omp unroll partial(2)
+  for (int i = -3; i != 1; ++i)
+#pragma omp unroll partial(2)
+    for (int j = -2; j < i * -1; ++j)
+      sum++;
+
+  if (sum != 14)
+    {
+      fprintf (stderr, "%s: Wrong sum: %d\n", __FUNCTION__, sum);
+      abort ();
+    }
+}
+
+void test6 ()
+{
+  int sum = 0;
+#pragma omp target parallel for reduction(+:sum)
+#pragma omp unroll partial(7)
+  for (int i = -3; i != 1; ++i)
+#pragma omp unroll partial(2)
+    for (int j = -2; j < i * -1; ++j)
+      sum++;
+
+  if (sum != 14)
+    {
+      fprintf (stderr, "%s: Wrong sum: %d\n", __FUNCTION__, sum);
+      abort ();
+    }
+}
+
+void test7 ()
+{
+  int sum = 0;
+#pragma omp target teams distribute parallel for reduction(+:sum)
+#pragma omp unroll partial(7)
+  for (int i = -3; i != 1; ++i)
+#pragma omp unroll partial(2)
+    for (int j = -2; j < i * -1; ++j)
+      sum++;
+
+  if (sum != 14)
+    {
+      fprintf (stderr, "%s: Wrong sum: %d\n", __FUNCTION__, sum);
+      abort ();
+    }
+}
+
+int
+main ()
+{
+  test1 ();
+  test2 ();
+  test3 ();
+  test4 ();
+  test5 ();
+  test6 ();
+  test7 ();
+
+  return 0;
+}
diff --git a/libgomp/testsuite/libgomp.c-c++-common/target-imperfect-transform-1.c b/libgomp/testsuite/libgomp.c-c++-common/target-imperfect-transform-1.c
new file mode 100644
index 00000000000..0e33e028ac2
--- /dev/null
+++ b/libgomp/testsuite/libgomp.c-c++-common/target-imperfect-transform-1.c
@@ -0,0 +1,82 @@ 
+/* { dg-do run } */
+
+/* Like imperfect-transform.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(2) map(always, tofrom:f1count, f2count)
+  for (i = 0; i < a1; i++)
+    {
+      f1 (0, i);
+      for (j = 0; j < a2; j++)
+	{
+	  f1 (1, j);
+#pragma omp unroll partial
+	  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-imperfect-transform-2.c b/libgomp/testsuite/libgomp.c-c++-common/target-imperfect-transform-2.c
new file mode 100644
index 00000000000..78986e8d3ae
--- /dev/null
+++ b/libgomp/testsuite/libgomp.c-c++-common/target-imperfect-transform-2.c
@@ -0,0 +1,82 @@ 
+/* { dg-do run } */
+
+/* Like imperfect-transform.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(2) map(always, tofrom:f1count, f2count)
+  for (i = 0; i < a1; i++)
+    {
+      f1 (0, i);
+      for (j = 0; j < a2; j++)
+	{
+	  f1 (1, j);
+#pragma omp tile sizes(5)
+	  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 ();
+}