diff mbox series

[committed] openmp: OpenMP loop transformation support

Message ID ZmCjMc0mjBbDH6Tt@tucnak
State New
Headers show
Series [committed] openmp: OpenMP loop transformation support | expand

Commit Message

Jakub Jelinek June 5, 2024, 5:41 p.m. UTC
Hi!

This patch is largely rewritten version of the
https://gcc.gnu.org/pipermail/gcc-patches/2023-October/631764.html
patch set which I've promissed to adjust the way I'd like it but didn't
get to it until now.
The previous series together in diffstat was
 176 files changed, 12107 insertions(+), 298 deletions(-)
This patch is
 197 files changed, 10843 insertions(+), 212 deletions(-)
and diff between the old series and new patch is
 268 files changed, 8053 insertions(+), 9231 deletions(-)

Only the 5.1/5.2 tile/unroll constructs are supported, in various
places some preparations for the other 6.0 loop transformations
constructs (interchange/reverse/fuse) are done, but certainly
not complete and not everywhere.  The important difference is that
because tile/unroll partial map 1:1 the original loops to generated
canonical loops and add another set of generated loops without canonical
form inside of it, the tile/unroll partial constructs are terminal
for the generated loop, one can't have some loops from the tile or
unroll partial and some further loops from inside the body of that
construct.
The GENERIC representation attempts to match what the standard specifies,
so there are separate OMP_TILE and OMP_UNROLL trees.  If for a particular
loop in a loop nest of some OpenMP loop it awaits a generated loop from a
nested loop, or if in OMP_LOOPXFORM_LOWERED OMP_TILE/UNROLL construct
a generated loop has been moved to some surrounding construct, that
particular loop is represented by all NULL_TREEs in the
OMP_FOR_{INIT,COND,INCR,ORIG_DECLS} vector.
The lowering of the loop transforming constructs is done at gimplification
time, at the start of gimplify_omp_for.
I think this way it is more maintainable over magic clauses with various
loop depths on the other looping constructs or the magic OMP_LOOP_TRANS
construct.
Though, I admit I'm still undecided how to represent the OpenMP 6.0
loop transformation case of say:
  #pragma omp for collapse (4)
  for (int i = 0; i < 32; ++i)
  #pragma omp interchange permutation (2, 1)
[jakub@tucnak libgomp]$ git log -n 1 --format=%B | head -n 60
openmp: OpenMP loop transformation support

This patch is largely rewritten version of the
https://gcc.gnu.org/pipermail/gcc-patches/2023-October/631764.html
patch set which I've promissed to adjust the way I'd like it but didn't
get to it until now.
The previous series together in diffstat was
 176 files changed, 12107 insertions(+), 298 deletions(-)
This patch is
 197 files changed, 10843 insertions(+), 212 deletions(-)
and diff between the old series and new patch is
 268 files changed, 8053 insertions(+), 9231 deletions(-)

Only the 5.1/5.2 tile/unroll constructs are supported, in various
places some preparations for the other 6.0 loop transformations
constructs (interchange/reverse/fuse) are done, but certainly
not complete and not everywhere.  The important difference is that
because tile/unroll partial map 1:1 the original loops to generated
canonical loops and add another set of generated loops without canonical
form inside of it, the tile/unroll partial constructs are terminal
for the generated loop, one can't have some loops from the tile or
unroll partial and some further loops from inside the body of that
construct.
The GENERIC representation attempts to match what the standard specifies,
so there are separate OMP_TILE and OMP_UNROLL trees.  If for a particular
loop in a loop nest of some OpenMP loop it awaits a generated loop from a
nested loop, or if in OMP_LOOPXFORM_LOWERED OMP_TILE/UNROLL construct
a generated loop has been moved to some surrounding construct, that
particular loop is represented by all NULL_TREEs in the
OMP_FOR_{INIT,COND,INCR,ORIG_DECLS} vector.
The lowering of the loop transforming constructs is done at gimplification
time, at the start of gimplify_omp_for.
I think this way it is more maintainable over magic clauses with various
loop depths on the other looping constructs or the magic OMP_LOOP_TRANS
construct.
Though, I admit I'm still undecided how to represent the OpenMP 6.0
loop transformation case of say:
  #pragma omp for collapse (4)
  for (int i = 0; i < 32; ++i)
  #pragma omp interchange permutation (2, 1)
  #pragma omp reverse
  for (int j = 0; j < 32; ++j)
  #pragma omp reverse
  for (int k = 0; k < 32; ++k)
  for (int l = 0; l < 32; ++l)
    ;
Surely the i loop would go to first vector elements of OMP_FOR_*
of the work-sharing loop, then 2 loops are expecting generated loops
from interchange which would be inside of the body.  But the innermost
l loop isn't part of the interchange, so the question is where to
put it.  One possibility is to have it in the 4th loop of the OMP_FOR,
another possibility would be to add some artificial construct inside
of the OMP_INTERCHANGE and 2 OMP_REVERSE bodies which would contain
the inner loop(s), e.g. it could be OMP_INTERCHANGE without permutation
clause or some artificial ones or whatever.

I've recently raised various unclear things in the 5.1/5.2/TRs versions
regarding loop transformations, in particular
https://github.com/OpenMP/spec/issues/3908
https://github.com/OpenMP/spec/issues/3909
[jakub@tucnak libgomp]$ git log -n 1 --format=%B | head -n 80
openmp: OpenMP loop transformation support

This patch is largely rewritten version of the
https://gcc.gnu.org/pipermail/gcc-patches/2023-October/631764.html
patch set which I've promissed to adjust the way I'd like it but didn't
get to it until now.
The previous series together in diffstat was
 176 files changed, 12107 insertions(+), 298 deletions(-)
This patch is
 197 files changed, 10843 insertions(+), 212 deletions(-)
and diff between the old series and new patch is
 268 files changed, 8053 insertions(+), 9231 deletions(-)

Only the 5.1/5.2 tile/unroll constructs are supported, in various
places some preparations for the other 6.0 loop transformations
constructs (interchange/reverse/fuse) are done, but certainly
not complete and not everywhere.  The important difference is that
because tile/unroll partial map 1:1 the original loops to generated
canonical loops and add another set of generated loops without canonical
form inside of it, the tile/unroll partial constructs are terminal
for the generated loop, one can't have some loops from the tile or
unroll partial and some further loops from inside the body of that
construct.
The GENERIC representation attempts to match what the standard specifies,
so there are separate OMP_TILE and OMP_UNROLL trees.  If for a particular
loop in a loop nest of some OpenMP loop it awaits a generated loop from a
nested loop, or if in OMP_LOOPXFORM_LOWERED OMP_TILE/UNROLL construct
a generated loop has been moved to some surrounding construct, that
particular loop is represented by all NULL_TREEs in the
OMP_FOR_{INIT,COND,INCR,ORIG_DECLS} vector.
The lowering of the loop transforming constructs is done at gimplification
time, at the start of gimplify_omp_for.
I think this way it is more maintainable over magic clauses with various
loop depths on the other looping constructs or the magic OMP_LOOP_TRANS
construct.
Though, I admit I'm still undecided how to represent the OpenMP 6.0
loop transformation case of say:
  #pragma omp for collapse (4)
  for (int i = 0; i < 32; ++i)
  #pragma omp interchange permutation (2, 1)
  #pragma omp reverse
  for (int j = 0; j < 32; ++j)
  #pragma omp reverse
  for (int k = 0; k < 32; ++k)
  for (int l = 0; l < 32; ++l)
    ;
Surely the i loop would go to first vector elements of OMP_FOR_*
of the work-sharing loop, then 2 loops are expecting generated loops
from interchange which would be inside of the body.  But the innermost
l loop isn't part of the interchange, so the question is where to
put it.  One possibility is to have it in the 4th loop of the OMP_FOR,
another possibility would be to add some artificial construct inside
of the OMP_INTERCHANGE and 2 OMP_REVERSE bodies which would contain
the inner loop(s), e.g. it could be OMP_INTERCHANGE without permutation
clause or some artificial ones or whatever.

I've recently raised various unclear things in the 5.1/5.2/TRs versions
regarding loop transformations, in particular
https://github.com/OpenMP/spec/issues/3908
https://github.com/OpenMP/spec/issues/3909
(sorry, private links unless you have OpenMP membership).  Until those
are resolved, I have a sorry on trying to mix generated loops with
non-rectangular loops (way too many questions need to be answered before
that can be done) and similarly for mixing non-perfectly nested loops
with generated loops (again, it can be implemented somehow, but is way
too unclear).  The second issue is mostly about data sharing, which is
ambiguous, the patch makes the artificial iterators of the loops effectively
private in the associated constructs (more like local), but for user
iterators doesn't do anything in particular, so for now one needs to use
explicit data sharing clauses on the non-loop transformation OpenMP looping
constructs or surrounding parallel/task/target etc.

So that I fit into the ml limits, I'll send the libgomp/testsuite part
of the changes separately.

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

2024-06-05  Jakub Jelinek  <jakub@redhat.com>
	    Frederik Harwath  <frederik@codesourcery.com>
	    Sandra Loosemore  <sandra@codesourcery.com>

gcc/
	* tree.def (OMP_TILE, OMP_UNROLL): New tree codes.
	* tree-core.h (enum omp_clause_code): Add OMP_CLAUSE_PARTIAL,
	OMP_CLAUSE_FULL and OMP_CLAUSE_SIZES.
	* tree.h (OMP_LOOPXFORM_CHECK): Define.
	(OMP_LOOPXFORM_LOWERED): Define.
	(OMP_CLAUSE_PARTIAL_EXPR): Define.
	(OMP_CLAUSE_SIZES_LIST): Define.
	* tree.cc (omp_clause_num_ops, omp_clause_code_name): Add entries
	for OMP_CLAUSE_{PARTIAL,FULL,SIZES}.
	* tree-pretty-print.cc (dump_omp_clause): Handle
	OMP_CLAUSE_{PARTIAL,FULL,SIZES}.
	(dump_generic_node): Handle OMP_TILE and OMP_UNROLL.  Skip printing
	loops with NULL OMP_FOR_INIT (node) vector element.
	* gimplify.cc (is_gimple_stmt): Handle OMP_TILE and OMP_UNROLL.
	(gimplify_omp_taskloop_expr): For SAVE_EXPR use gimplify_save_expr.
	(gimplify_omp_loop_xform): New function.
	(gimplify_omp_for): Call omp_maybe_apply_loop_xforms and if that
	reshuffles what the passed pointer points to, retry or return GS_OK.
	Handle OMP_TILE and OMP_UNROLL.
	(gimplify_omp_loop): Call omp_maybe_apply_loop_xforms and if that
	reshuffles what the passed pointer points to, return GS_OK.
	(gimplify_expr): Handle OMP_TILE and OMP_UNROLL.
	* omp-general.h (omp_loop_number_of_iterations,
	omp_maybe_apply_loop_xforms): Declare.
	* omp-general.cc (omp_adjust_for_condition): For LE_EXPR and GE_EXPR
	with pointers, don't add/subtract one, but the size of what the
	pointer points to.
	(omp_loop_number_of_iterations, omp_apply_tile,
	find_nested_loop_xform, omp_maybe_apply_loop_xforms): New functions.
gcc/c-family/
	* c-common.h (c_omp_find_generated_loop): Declare.
	* c-gimplify.cc (c_genericize_control_stmt): Handle OMP_TILE and
	OMP_UNROLL.
	* c-omp.cc (c_finish_omp_for): Handle generated loops.
	(c_omp_is_loop_iterator): Likewise.
	(c_find_nested_loop_xform_r, c_omp_find_generated_loop): New
	functions.
	(c_omp_check_loop_iv): Handle generated loops.  For now sorry
	on mixing non-rectangular loop with generated loops.
	(c_omp_check_loop_binding_exprs): For now sorry on mixing
	imperfect loops with generated loops.
	(c_omp_directives): Uncomment tile and unroll entries.
	* c-pragma.h (enum pragma_kind): Add PRAGMA_OMP_TILE and
	PRAGMA_OMP_UNROLL, change PRAGMA_OMP__LAST_ to the latter.
	(enum pragma_omp_clause): Add PRAGMA_OMP_CLAUSE_FULL and
	PRAGMA_OMP_CLAUSE_PARTIAL.
	* c-pragma.cc (omp_pragmas_simd): Add tile and unroll omp pragmas.
gcc/c/
	* c-parser.cc (c_parser_skip_std_attribute_spec_seq): New function.
	(check_omp_intervening_code): Reject imperfectly nested tile.
	(c_parser_compound_statement_nostart): If want_nested_loop, use
	c_parser_omp_next_tokens_can_be_canon_loop instead of just checking
	for RID_FOR keyword.
	(c_parser_omp_clause_name): Handle full and partial clause names.
	(c_parser_omp_clause_allocate): Remove spurious semicolon.
	(c_parser_omp_clause_full, c_parser_omp_clause_partial): New
	functions.
	(c_parser_omp_all_clauses): Handle PRAGMA_OMP_CLAUSE_FULL and
	PRAGMA_OMP_CLAUSE_PARTIAL.
	(c_parser_omp_next_tokens_can_be_canon_loop): New function.
	(c_parser_omp_loop_nest): Parse C23 attributes.  Handle tile/unroll
	constructs.  Use c_parser_omp_next_tokens_can_be_canon_loop instead
	of just checking for RID_FOR keyword.  Only add_stmt (body) if it is
	non-NULL.
	(c_parser_omp_for_loop): Rename tiling variable to oacc_tiling.  For
	OMP_CLAUSE_SIZES set collapse to list length of OMP_CLAUSE_SIZES_LIST.
	Use c_parser_omp_next_tokens_can_be_canon_loop instead of just
	checking for RID_FOR keyword.  Remove spurious semicolon.  Don't call
	c_omp_check_loop_binding_exprs if stmt is NULL.  Skip generated loops.
	(c_parser_omp_tile_sizes, c_parser_omp_tile): New functions.
	(OMP_UNROLL_CLAUSE_MASK): Define.
	(c_parser_omp_unroll): New function.
	(c_parser_omp_construct): Handle PRAGMA_OMP_TILE and
	PRAGMA_OMP_UNROLL.
	* c-typeck.cc (c_finish_omp_clauses): Adjust wording of some of the
	conflicting clause diagnostic messages to include word clause.
	Handle OMP_CLAUSE_{FULL,PARTIAL,SIZES} and diagnose full vs. partial
	conflict.
gcc/cp/
	* cp-tree.h (dependent_omp_for_p): Add another tree argument.
	* parser.cc (check_omp_intervening_code): Reject imperfectly nested
	tile.
	(cp_parser_statement_seq_opt): If want_nested_loop, use
	cp_parser_next_tokens_can_be_canon_loop instead of just checking
	for RID_FOR keyword.
	(cp_parser_omp_clause_name): Handle full and partial clause names.
	(cp_parser_omp_clause_full, cp_parser_omp_clause_partial): New
	functions.
	(cp_parser_omp_all_clauses): Formatting fix.  Handle
	PRAGMA_OMP_CLAUSE_PARTIAL and PRAGMA_OMP_CLAUSE_FULL.
	(cp_parser_next_tokens_can_be_canon_loop): New function.
	(cp_parser_omp_loop_nest): Parse C++11 attributes.  Handle tile/unroll
	constructs.  Use cp_parser_next_tokens_can_be_canon_loop instead
	of just checking for RID_FOR keyword.  Only add_stmt
	cp_parser_omp_loop_nest result if it is non-NULL.
	(cp_parser_omp_for_loop): Rename tiling variable to oacc_tiling.  For
	OMP_CLAUSE_SIZES set collapse to list length of OMP_CLAUSE_SIZES_LIST.
	Use cp_parser_next_tokens_can_be_canon_loop instead of just
	checking for RID_FOR keyword.  Remove spurious semicolon.  Don't call
	c_omp_check_loop_binding_exprs if stmt is NULL.  Skip and/or handle
	generated loops.  Remove spurious ()s around & operands.
	(cp_parser_omp_tile_sizes, cp_parser_omp_tile): New functions.
	(OMP_UNROLL_CLAUSE_MASK): Define.
	(cp_parser_omp_unroll): New function.
	(cp_parser_omp_construct): Handle PRAGMA_OMP_TILE and
	PRAGMA_OMP_UNROLL.
	(cp_parser_pragma): Likewise.
	* semantics.cc (finish_omp_clauses): Don't call
	fold_build_cleanup_point_expr for cases which obviously won't need it,
	like checked INTEGER_CSTs.  Handle OMP_CLAUSE_{FULL,PARTIAL,SIZES}
	and diagnose full vs. partial conflict.  Adjust wording of some of the
	conflicting clause diagnostic messages to include word clause.
	(finish_omp_for): Use decl equal to global_namespace as a marker for
	generated loop.  Pass also body to dependent_omp_for_p.  Skip
	generated loops.
	(finish_omp_for_block): Skip generated loops.
	* pt.cc (tsubst_omp_clauses): Handle OMP_CLAUSE_{FULL,PARTIAL,SIZES}.
	(tsubst_stmt): Handle OMP_TILE and OMP_UNROLL.  Handle or skip
	generated loops.
	(dependent_omp_for_p): Add body argument.  If declv vector element
	is NULL, find generated loop.
	* cp-gimplify.cc (cp_gimplify_expr): Handle OMP_TILE and OMP_UNROLL.
	(cp_fold_r): Likewise.
	(cp_genericize_r): Likewise.  Skip generated loops.
gcc/fortran/
	* gfortran.h (enum gfc_statement): Add ST_OMP_UNROLL,
	ST_OMP_END_UNROLL, ST_OMP_TILE and ST_OMP_END_TILE.
	(struct gfc_omp_clauses): Add sizes_list, partial, full and erroneous
	members.
	(enum gfc_exec_op): Add EXEC_OMP_UNROLL and EXEC_OMP_TILE.
	(gfc_expr_list_len): Declare.
	* match.h (gfc_match_omp_tile, gfc_match_omp_unroll): Declare.
	* openmp.cc (gfc_get_location): Declare.
	(gfc_free_omp_clauses): Free sizes_list.
	(match_oacc_expr_list): Rename to ...
	(match_omp_oacc_expr_list): ... this.  Add is_omp argument and
	change diagnostic wording if it is true.
	(enum omp_mask2): Add OMP_CLAUSE_{FULL,PARTIAL,SIZES}.
	(gfc_match_omp_clauses): Parse full, partial and sizes clauses.
	(gfc_match_oacc_wait): Use match_omp_oacc_expr_list instead of
	match_oacc_expr_list.
	(OMP_UNROLL_CLAUSES, OMP_TILE_CLAUSES): Define.
	(gfc_match_omp_tile, gfc_match_omp_unroll): New functions.
	(resolve_omp_clauses): Diagnose full vs. partial clause conflict.
	Resolve sizes clause arguments.
	(find_nested_loop_in_chain): Use switch instead of series of ifs.
	Handle EXEC_OMP_TILE and EXEC_OMP_UNROLL.
	(gfc_resolve_omp_do_blocks): Set omp_current_do_collapse to
	list length of sizes_list if present.
	(gfc_resolve_do_iterator): Return for EXEC_OMP_TILE or
	EXEC_OMP_UNROLL.
	(restructure_intervening_code): Remove spurious ()s around & operands.
	(is_outer_iteration_variable): Handle EXEC_OMP_TILE and
	EXEC_OMP_UNROLL.
	(check_nested_loop_in_chain): Likewise.
	(expr_is_invariant): Likewise.
	(resolve_omp_do): Handle EXEC_OMP_TILE and EXEC_OMP_UNROLL.  Diagnose
	tile without sizes clause.  Use sizes_list length for count if
	non-NULL.  Set code->ext.omp_clauses->erroneous on loops where we've
	reported diagnostics.  Sorry for mixing non-rectangular loops with
	generated loops.
	(omp_code_to_statement): Handle EXEC_OMP_TILE and EXEC_OMP_UNROLL.
	(gfc_resolve_omp_directive): Likewise.
	* parse.cc (decode_omp_directive): Parse end tile, end unroll, tile
	and unroll.  Move nothing entry alphabetically.
	(case_exec_markers): Add ST_OMP_TILE and ST_OMP_UNROLL.
	(gfc_ascii_statement): Handle ST_OMP_END_TILE, ST_OMP_END_UNROLL,
	ST_OMP_TILE and ST_OMP_UNROLL.
	(parse_omp_do): Add nested argument.  Handle ST_OMP_TILE and
	ST_OMP_UNROLL.
	(parse_omp_structured_block): Adjust parse_omp_do caller.
	(parse_executable): Likewise.  Handle ST_OMP_TILE and ST_OMP_UNROLL.
	* resolve.cc (gfc_resolve_blocks): Handle EXEC_OMP_TILE and
	EXEC_OMP_UNROLL.
	(gfc_resolve_code): Likewise.
	* st.cc (gfc_free_statement): Likewise.
	* trans.cc (trans_code): Likewise.
	* trans-openmp.cc (gfc_trans_omp_clauses): Handle full, partial and
	sizes clauses.  Use tree_cons + nreverse instead of
	temporary vector and build_tree_list_vec for tile_list handling.
	(gfc_expr_list_len): New function.
	(gfc_trans_omp_do): Rename tile to oacc_tile.  Handle sizes clause.
	Don't assert code->op is EXEC_DO.  Handle EXEC_OMP_TILE and
	EXEC_OMP_UNROLL.
	(gfc_trans_omp_directive): Handle EXEC_OMP_TILE and EXEC_OMP_UNROLL.
	* dump-parse-tree.cc (show_omp_clauses): Dump full, partial and
	sizes clauses.
	(show_omp_node): Handle EXEC_OMP_TILE and EXEC_OMP_UNROLL.
	(show_code_node): Likewise.
gcc/testsuite/
	* c-c++-common/gomp/attrs-tile-1.c: New test.
	* c-c++-common/gomp/attrs-tile-2.c: New test.
	* c-c++-common/gomp/attrs-tile-3.c: New test.
	* c-c++-common/gomp/attrs-tile-4.c: New test.
	* c-c++-common/gomp/attrs-tile-5.c: New test.
	* c-c++-common/gomp/attrs-tile-6.c: New test.
	* c-c++-common/gomp/attrs-unroll-1.c: New test.
	* c-c++-common/gomp/attrs-unroll-2.c: New test.
	* c-c++-common/gomp/attrs-unroll-3.c: New test.
	* c-c++-common/gomp/attrs-unroll-inner-1.c: New test.
	* c-c++-common/gomp/attrs-unroll-inner-2.c: New test.
	* c-c++-common/gomp/attrs-unroll-inner-3.c: New test.
	* c-c++-common/gomp/attrs-unroll-inner-4.c: New test.
	* c-c++-common/gomp/attrs-unroll-inner-5.c: New test.
	* c-c++-common/gomp/imperfect-attributes.c: Adjust expected
	diagnostics.
	* c-c++-common/gomp/imperfect-loop-nest.c: New test.
	* c-c++-common/gomp/ordered-5.c: New test.
	* c-c++-common/gomp/scan-7.c: New test.
	* c-c++-common/gomp/tile-1.c: New test.
	* c-c++-common/gomp/tile-2.c: New test.
	* c-c++-common/gomp/tile-3.c: New test.
	* c-c++-common/gomp/tile-4.c: New test.
	* c-c++-common/gomp/tile-5.c: New test.
	* c-c++-common/gomp/tile-6.c: New test.
	* c-c++-common/gomp/tile-7.c: New test.
	* c-c++-common/gomp/tile-8.c: New test.
	* c-c++-common/gomp/tile-9.c: New test.
	* c-c++-common/gomp/tile-10.c: New test.
	* c-c++-common/gomp/tile-11.c: New test.
	* c-c++-common/gomp/tile-12.c: New test.
	* c-c++-common/gomp/tile-13.c: New test.
	* c-c++-common/gomp/tile-14.c: New test.
	* c-c++-common/gomp/tile-15.c: New test.
	* c-c++-common/gomp/unroll-1.c: New test.
	* c-c++-common/gomp/unroll-2.c: New test.
	* c-c++-common/gomp/unroll-3.c: New test.
	* c-c++-common/gomp/unroll-4.c: New test.
	* c-c++-common/gomp/unroll-5.c: New test.
	* c-c++-common/gomp/unroll-6.c: New test.
	* c-c++-common/gomp/unroll-7.c: New test.
	* c-c++-common/gomp/unroll-8.c: New test.
	* c-c++-common/gomp/unroll-9.c: New test.
	* c-c++-common/gomp/unroll-inner-1.c: New test.
	* c-c++-common/gomp/unroll-inner-2.c: New test.
	* c-c++-common/gomp/unroll-inner-3.c: New test.
	* c-c++-common/gomp/unroll-non-rect-1.c: New test.
	* c-c++-common/gomp/unroll-non-rect-2.c: New test.
	* c-c++-common/gomp/unroll-non-rect-3.c: New test.
	* c-c++-common/gomp/unroll-simd-1.c: New test.
	* gcc.dg/gomp/attrs-4.c: Adjust expected diagnostics.
	* gcc.dg/gomp/for-1.c: Likewise.
	* gcc.dg/gomp/for-11.c: Likewise.
	* g++.dg/gomp/attrs-4.C: Likewise.
	* g++.dg/gomp/for-1.C: Likewise.
	* g++.dg/gomp/pr94512.C: Likewise.
	* g++.dg/gomp/tile-1.C: New test.
	* g++.dg/gomp/tile-2.C: New test.
	* g++.dg/gomp/unroll-1.C: New test.
	* g++.dg/gomp/unroll-2.C: New test.
	* g++.dg/gomp/unroll-3.C: New test.
	* gfortran.dg/gomp/inner-loops-1.f90: New test.
	* gfortran.dg/gomp/inner-loops-2.f90: New test.
	* gfortran.dg/gomp/pure-1.f90: Add tests for !$omp unroll
	and !$omp tile.
	* gfortran.dg/gomp/pure-2.f90: Remove those tests from here.
	* gfortran.dg/gomp/scan-9.f90: New test.
	* gfortran.dg/gomp/tile-1.f90: New test.
	* gfortran.dg/gomp/tile-2.f90: New test.
	* gfortran.dg/gomp/tile-3.f90: New test.
	* gfortran.dg/gomp/tile-4.f90: New test.
	* gfortran.dg/gomp/tile-5.f90: New test.
	* gfortran.dg/gomp/tile-6.f90: New test.
	* gfortran.dg/gomp/tile-7.f90: New test.
	* gfortran.dg/gomp/tile-8.f90: New test.
	* gfortran.dg/gomp/tile-9.f90: New test.
	* gfortran.dg/gomp/tile-10.f90: New test.
	* gfortran.dg/gomp/tile-imperfect-nest-1.f90: New test.
	* gfortran.dg/gomp/tile-imperfect-nest-2.f90: New test.
	* gfortran.dg/gomp/tile-inner-loops-1.f90: New test.
	* gfortran.dg/gomp/tile-inner-loops-2.f90: New test.
	* gfortran.dg/gomp/tile-inner-loops-3.f90: New test.
	* gfortran.dg/gomp/tile-inner-loops-4.f90: New test.
	* gfortran.dg/gomp/tile-inner-loops-5.f90: New test.
	* gfortran.dg/gomp/tile-inner-loops-6.f90: New test.
	* gfortran.dg/gomp/tile-inner-loops-7.f90: New test.
	* gfortran.dg/gomp/tile-inner-loops-8.f90: New test.
	* gfortran.dg/gomp/tile-non-rectangular-1.f90: New test.
	* gfortran.dg/gomp/tile-non-rectangular-2.f90: New test.
	* gfortran.dg/gomp/tile-non-rectangular-3.f90: New test.
	* gfortran.dg/gomp/tile-unroll-1.f90: New test.
	* gfortran.dg/gomp/tile-unroll-2.f90: New test.
	* gfortran.dg/gomp/unroll-1.f90: New test.
	* gfortran.dg/gomp/unroll-2.f90: New test.
	* gfortran.dg/gomp/unroll-3.f90: New test.
	* gfortran.dg/gomp/unroll-4.f90: New test.
	* gfortran.dg/gomp/unroll-5.f90: New test.
	* gfortran.dg/gomp/unroll-6.f90: New test.
	* gfortran.dg/gomp/unroll-7.f90: New test.
	* gfortran.dg/gomp/unroll-8.f90: New test.
	* gfortran.dg/gomp/unroll-9.f90: New test.
	* gfortran.dg/gomp/unroll-10.f90: New test.
	* gfortran.dg/gomp/unroll-11.f90: New test.
	* gfortran.dg/gomp/unroll-12.f90: New test.
	* gfortran.dg/gomp/unroll-13.f90: New test.
	* gfortran.dg/gomp/unroll-inner-loop-1.f90: New test.
	* gfortran.dg/gomp/unroll-inner-loop-2.f90: New test.
	* gfortran.dg/gomp/unroll-no-clause-1.f90: New test.
	* gfortran.dg/gomp/unroll-non-rect-1.f90: New test.
	* gfortran.dg/gomp/unroll-non-rect-2.f90: New test.
	* gfortran.dg/gomp/unroll-simd-1.f90: New test.
	* gfortran.dg/gomp/unroll-simd-2.f90: New test.
	* gfortran.dg/gomp/unroll-simd-3.f90: New test.
	* gfortran.dg/gomp/unroll-tile-1.f90: New test.
	* gfortran.dg/gomp/unroll-tile-2.f90: New test.
	* gfortran.dg/gomp/unroll-tile-inner-1.f90: New test.
libgomp/
	* testsuite/libgomp.c-c++-common/imperfect-transform-1.c: New test.
	* testsuite/libgomp.c-c++-common/imperfect-transform-2.c: New test.
	* testsuite/libgomp.c-c++-common/matrix-1.h: New test.
	* testsuite/libgomp.c-c++-common/matrix-constant-iter.h: New test.
	* testsuite/libgomp.c-c++-common/matrix-helper.h: New test.
	* testsuite/libgomp.c-c++-common/matrix-no-directive-1.c: New test.
	* testsuite/libgomp.c-c++-common/matrix-no-directive-unroll-full-1.c:
	New test.
	* testsuite/libgomp.c-c++-common/matrix-omp-distribute-parallel-for-1.c:
	New test.
	* testsuite/libgomp.c-c++-common/matrix-omp-for-1.c: New test.
	* testsuite/libgomp.c-c++-common/matrix-omp-parallel-for-1.c: New test.
	* testsuite/libgomp.c-c++-common/matrix-omp-parallel-masked-taskloop-1.c:
	New test.
	* testsuite/libgomp.c-c++-common/matrix-omp-parallel-masked-taskloop-simd-1.c:
	New test.
	* testsuite/libgomp.c-c++-common/matrix-omp-target-parallel-for-1.c:
	New test.
	* testsuite/libgomp.c-c++-common/matrix-omp-target-teams-distribute-parallel-for-1.c:
	New test.
	* testsuite/libgomp.c-c++-common/matrix-omp-taskloop-1.c: New test.
	* testsuite/libgomp.c-c++-common/matrix-omp-teams-distribute-parallel-for-1.c:
	New test.
	* testsuite/libgomp.c-c++-common/matrix-simd-1.c: New test.
	* testsuite/libgomp.c-c++-common/matrix-transform-variants-1.h:
	New test.
	* testsuite/libgomp.c-c++-common/target-imperfect-transform-1.c:
	New test.
	* testsuite/libgomp.c-c++-common/target-imperfect-transform-2.c:
	New test.
	* testsuite/libgomp.c-c++-common/unroll-1.c: New test.
	* testsuite/libgomp.c-c++-common/unroll-non-rect-1.c: New test.
	* testsuite/libgomp.c++/matrix-no-directive-unroll-full-1.C: New test.
	* testsuite/libgomp.c++/tile-2.C: New test.
	* testsuite/libgomp.c++/tile-3.C: New test.
	* testsuite/libgomp.c++/unroll-1.C: New test.
	* testsuite/libgomp.c++/unroll-2.C: New test.
	* testsuite/libgomp.c++/unroll-full-tile.C: New test.
	* testsuite/libgomp.fortran/imperfect-transform-1.f90: New test.
	* testsuite/libgomp.fortran/imperfect-transform-2.f90: New test.
	* testsuite/libgomp.fortran/inner-1.f90: New test.
	* testsuite/libgomp.fortran/nested-fn.f90: New test.
	* testsuite/libgomp.fortran/target-imperfect-transform-1.f90: New test.
	* testsuite/libgomp.fortran/target-imperfect-transform-2.f90: New test.
	* testsuite/libgomp.fortran/tile-1.f90: New test.
	* testsuite/libgomp.fortran/tile-2.f90: New test.
	* testsuite/libgomp.fortran/tile-unroll-1.f90: New test.
	* testsuite/libgomp.fortran/tile-unroll-2.f90: New test.
	* testsuite/libgomp.fortran/tile-unroll-3.f90: New test.
	* testsuite/libgomp.fortran/tile-unroll-4.f90: New test.
	* testsuite/libgomp.fortran/unroll-1.f90: New test.
	* testsuite/libgomp.fortran/unroll-2.f90: New test.
	* testsuite/libgomp.fortran/unroll-3.f90: New test.
	* testsuite/libgomp.fortran/unroll-4.f90: New test.
	* testsuite/libgomp.fortran/unroll-5.f90: New test.
	* testsuite/libgomp.fortran/unroll-6.f90: New test.
	* testsuite/libgomp.fortran/unroll-7a.f90: New test.
	* testsuite/libgomp.fortran/unroll-7b.f90: New test.
	* testsuite/libgomp.fortran/unroll-7c.f90: New test.
	* testsuite/libgomp.fortran/unroll-7.f90: New test.
	* testsuite/libgomp.fortran/unroll-8.f90: New test.
	* testsuite/libgomp.fortran/unroll-simd-1.f90: New test.
	* testsuite/libgomp.fortran/unroll-tile-1.f90: New test.
	* testsuite/libgomp.fortran/unroll-tile-2.f90: New test.


	Jakub
diff mbox series

Patch

--- gcc/tree.def.jj	2024-06-04 13:18:26.432100990 +0200
+++ gcc/tree.def	2024-06-04 13:30:00.562874319 +0200
@@ -1230,6 +1230,14 @@  DEFTREECODE (OMP_TASKLOOP, "omp_taskloop
    Operands like for OMP_FOR.  */
 DEFTREECODE (OMP_LOOP, "omp_loop", tcc_statement, 7)
 
+/* OpenMP - #pragma omp tile [clause1 ... clauseN]
+   Operands like for OMP_FOR.  */
+DEFTREECODE (OMP_TILE, "omp_tile", tcc_statement, 7)
+
+/* OpenMP - #pragma omp unroll [clause1 ... clauseN]
+   Operands like for OMP_FOR.  */
+DEFTREECODE (OMP_UNROLL, "omp_unroll", tcc_statement, 7)
+
 /* OpenMP - #pragma acc loop [clause1 ... clauseN]
    Operands like for OMP_FOR.  */
 DEFTREECODE (OACC_LOOP, "oacc_loop", tcc_statement, 7)
--- gcc/tree-core.h.jj	2024-06-04 13:18:26.314102560 +0200
+++ gcc/tree-core.h	2024-06-04 13:30:00.901869817 +0200
@@ -497,6 +497,15 @@  enum omp_clause_code {
   /* OpenMP clause: indirect [(constant-integer-expression)].  */
   OMP_CLAUSE_INDIRECT,
 
+  /* OpenMP clause: partial (constant-integer-expression).  */
+  OMP_CLAUSE_PARTIAL,
+
+  /* OpenMP clause: full.  */
+  OMP_CLAUSE_FULL,
+
+  /* OpenMP clause: sizes (constant-integer-expression-list).  */
+  OMP_CLAUSE_SIZES,
+
   /* Internally used only clause, holding SIMD uid.  */
   OMP_CLAUSE__SIMDUID_,
 
--- gcc/tree.h.jj	2024-06-04 13:19:05.068586875 +0200
+++ gcc/tree.h	2024-06-04 13:30:00.767871596 +0200
@@ -1546,6 +1546,10 @@  class auto_suppress_location_wrappers
 #define OMP_FOR_PRE_BODY(NODE)	   TREE_OPERAND (OMP_LOOPING_CHECK (NODE), 5)
 #define OMP_FOR_ORIG_DECLS(NODE)   TREE_OPERAND (OMP_LOOPING_CHECK (NODE), 6)
 
+#define OMP_LOOPXFORM_CHECK(NODE) TREE_RANGE_CHECK (NODE, OMP_TILE, OMP_UNROLL)
+#define OMP_LOOPXFORM_LOWERED(NODE) \
+  (OMP_LOOPXFORM_CHECK (NODE)->base.public_flag)
+
 #define OMP_SECTIONS_BODY(NODE)    TREE_OPERAND (OMP_SECTIONS_CHECK (NODE), 0)
 #define OMP_SECTIONS_CLAUSES(NODE) TREE_OPERAND (OMP_SECTIONS_CHECK (NODE), 1)
 
@@ -1742,6 +1746,10 @@  class auto_suppress_location_wrappers
   OMP_CLAUSE_OPERAND (OMP_CLAUSE_SUBCODE_CHECK (NODE, OMP_CLAUSE_HINT), 0)
 #define OMP_CLAUSE_FILTER_EXPR(NODE) \
   OMP_CLAUSE_OPERAND (OMP_CLAUSE_SUBCODE_CHECK (NODE, OMP_CLAUSE_FILTER), 0)
+#define OMP_CLAUSE_PARTIAL_EXPR(NODE) \
+  OMP_CLAUSE_OPERAND (OMP_CLAUSE_SUBCODE_CHECK (NODE, OMP_CLAUSE_PARTIAL), 0)
+#define OMP_CLAUSE_SIZES_LIST(NODE) \
+  OMP_CLAUSE_OPERAND (OMP_CLAUSE_SUBCODE_CHECK (NODE, OMP_CLAUSE_SIZES), 0)
 
 #define OMP_CLAUSE_GRAINSIZE_EXPR(NODE) \
   OMP_CLAUSE_OPERAND (OMP_CLAUSE_SUBCODE_CHECK (NODE, OMP_CLAUSE_GRAINSIZE),0)
--- gcc/tree.cc.jj	2024-06-04 13:19:05.067586888 +0200
+++ gcc/tree.cc	2024-06-04 13:30:00.548874505 +0200
@@ -316,6 +316,9 @@  unsigned const char omp_clause_num_ops[]
   0, /* OMP_CLAUSE_BIND  */
   1, /* OMP_CLAUSE_FILTER  */
   1, /* OMP_CLAUSE_INDIRECT  */
+  1, /* OMP_CLAUSE_PARTIAL  */
+  0, /* OMP_CLAUSE_FULL  */
+  1, /* OMP_CLAUSE_SIZES  */
   1, /* OMP_CLAUSE__SIMDUID_  */
   0, /* OMP_CLAUSE__SIMT_  */
   0, /* OMP_CLAUSE_INDEPENDENT  */
@@ -409,6 +412,9 @@  const char * const omp_clause_code_name[
   "bind",
   "filter",
   "indirect",
+  "partial",
+  "full",
+  "sizes",
   "_simduid_",
   "_simt_",
   "independent",
--- gcc/tree-pretty-print.cc.jj	2024-06-04 13:18:26.345102148 +0200
+++ gcc/tree-pretty-print.cc	2024-06-04 13:30:01.041867958 +0200
@@ -1461,6 +1461,25 @@  dump_omp_clause (pretty_printer *pp, tre
 			 spc, flags, false);
       pp_right_paren (pp);
       break;
+    case OMP_CLAUSE_PARTIAL:
+      pp_string (pp, "partial");
+      if (OMP_CLAUSE_PARTIAL_EXPR (clause))
+	{
+	  pp_left_paren (pp);
+	  dump_generic_node (pp, OMP_CLAUSE_PARTIAL_EXPR (clause),
+			     spc, flags, false);
+	  pp_right_paren (pp);
+	}
+      break;
+    case OMP_CLAUSE_FULL:
+      pp_string (pp, "full");
+      break;
+    case OMP_CLAUSE_SIZES:
+      pp_string (pp, "sizes(");
+      dump_generic_node (pp, OMP_CLAUSE_SIZES_LIST (clause),
+			 spc, flags, false);
+      pp_right_paren (pp);
+      break;
 
     case OMP_CLAUSE_IF_PRESENT:
       pp_string (pp, "if_present");
@@ -3796,6 +3815,14 @@  dump_generic_node (pretty_printer *pp, t
       pp_string (pp, "#pragma omp loop");
       goto dump_omp_loop;
 
+    case OMP_TILE:
+      pp_string (pp, "#pragma omp tile");
+      goto dump_omp_loop;
+
+    case OMP_UNROLL:
+      pp_string (pp, "#pragma omp unroll");
+      goto dump_omp_loop;
+
     case OACC_LOOP:
       pp_string (pp, "#pragma acc loop");
       goto dump_omp_loop;
@@ -3853,6 +3880,8 @@  dump_generic_node (pretty_printer *pp, t
 	      spc -= 2;
 	      for (i = 0; i < TREE_VEC_LENGTH (OMP_FOR_INIT (node)); i++)
 		{
+		  if (TREE_VEC_ELT (OMP_FOR_INIT (node), i) == NULL_TREE)
+		    continue;
 		  spc += 2;
 		  newline_and_indent (pp, spc);
 		  pp_string (pp, "for (");
--- gcc/gimplify.cc.jj	2024-06-04 13:18:25.896108122 +0200
+++ gcc/gimplify.cc	2024-06-04 13:30:00.579874093 +0200
@@ -6304,6 +6304,8 @@  is_gimple_stmt (tree t)
     case OMP_SIMD:
     case OMP_DISTRIBUTE:
     case OMP_LOOP:
+    case OMP_TILE:
+    case OMP_UNROLL:
     case OACC_LOOP:
     case OMP_SCAN:
     case OMP_SCOPE:
@@ -15027,7 +15029,10 @@  gimplify_omp_taskloop_expr (tree type, t
   if (*tp == NULL || is_gimple_constant (*tp))
     return;
 
-  *tp = get_initialized_tmp_var (*tp, pre_p, NULL, false);
+  if (TREE_CODE (*tp) == SAVE_EXPR)
+    gimplify_save_expr (tp, pre_p, NULL);
+  else
+    *tp = get_initialized_tmp_var (*tp, pre_p, NULL, false);
   /* Reference to pointer conversion is considered useless,
      but is significant for firstprivate clause.  Force it
      here.  */
@@ -15070,6 +15075,141 @@  find_standalone_omp_ordered (tree *tp, i
   return NULL_TREE;
 }
 
+/* Gimplify standalone loop transforming directive which has the
+   transformations applied already.  So, all that is needed is gimplify
+   the remaining loops as normal loops.  */
+
+static enum gimplify_status
+gimplify_omp_loop_xform (tree *expr_p, gimple_seq *pre_p)
+{
+  tree for_stmt = *expr_p;
+
+  if (OMP_FOR_PRE_BODY (for_stmt))
+    gimplify_and_add (OMP_FOR_PRE_BODY (for_stmt), pre_p);
+
+  gimple_seq pre_body = NULL, post_body = NULL;
+  for (int i = 0; i < TREE_VEC_LENGTH (OMP_FOR_INIT (for_stmt)); i++)
+    {
+      if (TREE_VEC_ELT (OMP_FOR_INIT (for_stmt), i) == NULL_TREE)
+	continue;
+      tree iters = NULL_TREE;
+      if (i == 0
+	  && TREE_CODE (for_stmt) == OMP_UNROLL
+	  && !omp_find_clause (OMP_FOR_CLAUSES (for_stmt), OMP_CLAUSE_PARTIAL))
+	{
+	  if (omp_find_clause (OMP_FOR_CLAUSES (for_stmt), OMP_CLAUSE_FULL))
+	    iters = omp_loop_number_of_iterations (for_stmt, 0, NULL);
+	  else
+	    iters = build_int_cst (integer_type_node, 8);
+	}
+      tree t = TREE_VEC_ELT (OMP_FOR_INIT (for_stmt), i);
+      gcc_assert (TREE_CODE (t) == MODIFY_EXPR);
+      tree decl = TREE_OPERAND (t, 0);
+      gcc_assert (DECL_P (decl));
+      gcc_assert (INTEGRAL_TYPE_P (TREE_TYPE (decl))
+		  || POINTER_TYPE_P (TREE_TYPE (decl)));
+      if (DECL_ARTIFICIAL (decl)
+	  && TREE_PRIVATE (t)
+	  && gimplify_omp_ctxp
+	  && gimplify_omp_ctxp->region_type != ORT_NONE)
+	{
+	  struct gimplify_omp_ctx *ctx = gimplify_omp_ctxp;
+	  do
+	    {
+	      splay_tree_node n
+		= splay_tree_lookup (ctx->variables, (splay_tree_key) decl);
+	      if (n != NULL)
+		break;
+	      else if (ctx->region_type != ORT_WORKSHARE
+		       && ctx->region_type != ORT_TASKGROUP
+		       && ctx->region_type != ORT_SIMD
+		       && ctx->region_type != ORT_ACC
+		       && !(ctx->region_type & ORT_TARGET_DATA))
+		{
+		  omp_add_variable (ctx, decl, GOVD_PRIVATE);
+		  break;
+		}
+	      ctx = ctx->outer_context;
+	    }
+	  while (ctx);
+	}
+      if (TREE_CODE (TREE_OPERAND (t, 1)) == TREE_VEC)
+	{
+	  gcc_assert (seen_error ());
+	  continue;
+	}
+      gimplify_expr (&TREE_OPERAND (t, 1), pre_p, NULL, is_gimple_val,
+		     fb_rvalue);
+      gimplify_and_add (t, &pre_body);
+      t = TREE_VEC_ELT (OMP_FOR_COND (for_stmt), i);
+      gcc_assert (TREE_OPERAND (t, 0) == decl);
+      if (TREE_CODE (TREE_OPERAND (t, 1)) == TREE_VEC)
+	{
+	  gcc_assert (seen_error ());
+	  continue;
+	}
+      gimplify_expr (&TREE_OPERAND (t, 1), pre_p, NULL, is_gimple_val,
+		     fb_rvalue);
+      tree l1 = create_artificial_label (UNKNOWN_LOCATION);
+      tree l2 = create_artificial_label (UNKNOWN_LOCATION);
+      tree l3 = create_artificial_label (UNKNOWN_LOCATION);
+      gimplify_seq_add_stmt (&pre_body, gimple_build_goto (l2));
+      gimplify_seq_add_stmt (&pre_body, gimple_build_label (l1));
+      gimple_seq this_post_body = NULL;
+      t = TREE_VEC_ELT (OMP_FOR_INCR (for_stmt), i);
+      if (TREE_CODE (t) == MODIFY_EXPR)
+	{
+	  t = TREE_OPERAND (t, 1);
+	  if (TREE_CODE (t) == PLUS_EXPR
+	      && TREE_OPERAND (t, 1) == decl)
+	    {
+	      TREE_OPERAND (t, 1) = TREE_OPERAND (t, 0);
+	      TREE_OPERAND (t, 0) = decl;
+	    }
+	  gimplify_expr (&TREE_OPERAND (t, 1), pre_p, NULL, is_gimple_val,
+			 fb_rvalue);
+	}
+      gimplify_and_add (TREE_VEC_ELT (OMP_FOR_INCR (for_stmt), i),
+			&this_post_body);
+      gimplify_seq_add_stmt (&this_post_body, gimple_build_label (l2));
+      t = TREE_VEC_ELT (OMP_FOR_COND (for_stmt), i);
+      gcond *cond = NULL;
+      tree d = decl;
+      gimplify_expr (&d, &this_post_body, NULL, is_gimple_val, fb_rvalue);
+      if (iters && tree_fits_uhwi_p (iters))
+	{
+	  unsigned HOST_WIDE_INT niters = tree_to_uhwi (iters);
+	  if ((unsigned HOST_WIDE_INT) (int) niters == niters
+	      && (int) niters > 0)
+	    {
+	      t = build2 (TREE_CODE (t), boolean_type_node, d,
+			  TREE_OPERAND (t, 1));
+	      t = build3 (ANNOTATE_EXPR, TREE_TYPE (t), t,
+			  build_int_cst (integer_type_node,
+					 annot_expr_unroll_kind),
+			  build_int_cst (integer_type_node, niters));
+	      gimplify_expr (&t, &this_post_body, NULL, is_gimple_val,
+			     fb_rvalue);
+	      cond = gimple_build_cond (NE_EXPR, t, boolean_false_node,
+					l1, l3);
+	    }
+	}
+      if (cond == NULL)
+	cond = gimple_build_cond (TREE_CODE (t), d, TREE_OPERAND (t, 1),
+				  l1, l3);
+      gimplify_seq_add_stmt (&this_post_body, cond);
+      gimplify_seq_add_stmt (&this_post_body, gimple_build_label (l3));
+      gimplify_seq_add_seq (&this_post_body, post_body);
+      post_body = this_post_body;
+    }
+  gimplify_seq_add_seq (pre_p, pre_body);
+  gimplify_and_add (OMP_FOR_BODY (for_stmt), pre_p);
+  gimplify_seq_add_seq (pre_p, post_body);
+
+  *expr_p = NULL_TREE;
+  return GS_ALL_DONE;
+}
+
 /* Gimplify the gross structure of an OMP_FOR statement.  */
 
 static enum gimplify_status
@@ -15089,7 +15229,7 @@  gimplify_omp_for (tree *expr_p, gimple_s
 
   bool loop_p = (omp_find_clause (OMP_FOR_CLAUSES (for_stmt), OMP_CLAUSE_BIND)
 		 != NULL_TREE);
-  if (OMP_FOR_INIT (for_stmt) == NULL_TREE)
+  while (OMP_FOR_INIT (for_stmt) == NULL_TREE)
     {
       tree *data[4] = { NULL, NULL, NULL, NULL };
       gcc_assert (TREE_CODE (for_stmt) != OACC_LOOP);
@@ -15101,6 +15241,15 @@  gimplify_omp_for (tree *expr_p, gimple_s
 	  *expr_p = NULL_TREE;
 	  return GS_ERROR;
 	}
+      gcc_assert (inner_for_stmt == *data[3]);
+      omp_maybe_apply_loop_xforms (data[3],
+				   data[2]
+				   ? OMP_FOR_CLAUSES (*data[2])
+				   : TREE_CODE (for_stmt) == OMP_FOR
+				   ? OMP_FOR_CLAUSES (for_stmt)
+				   : NULL_TREE);
+      if (inner_for_stmt != *data[3])
+	continue;
       if (data[2] && OMP_FOR_PRE_BODY (*data[2]))
 	{
 	  append_to_statement_list_force (OMP_FOR_PRE_BODY (*data[2]),
@@ -15254,6 +15403,13 @@  gimplify_omp_for (tree *expr_p, gimple_s
 		  OMP_PARALLEL_CLAUSES (*data[1]) = c;
 		}
 	    }
+      break;
+    }
+  if (OMP_FOR_INIT (for_stmt) != NULL_TREE)
+    {
+      omp_maybe_apply_loop_xforms (expr_p, NULL_TREE);
+      if (*expr_p != for_stmt)
+	return GS_OK;
     }
 
   switch (TREE_CODE (for_stmt))
@@ -15305,6 +15461,10 @@  gimplify_omp_for (tree *expr_p, gimple_s
     case OMP_SIMD:
       ort = ORT_SIMD;
       break;
+    case OMP_TILE:
+    case OMP_UNROLL:
+      gcc_assert (inner_for_stmt == NULL_TREE);
+      return gimplify_omp_loop_xform (expr_p, pre_p);
     default:
       gcc_unreachable ();
     }
@@ -16330,6 +16490,10 @@  gimplify_omp_loop (tree *expr_p, gimple_
   enum omp_clause_bind_kind kind = OMP_CLAUSE_BIND_THREAD;
   int i;
 
+  omp_maybe_apply_loop_xforms (expr_p, NULL_TREE);
+  if (*expr_p != for_stmt)
+    return GS_OK;
+
   /* If order is not present, the behavior is as if order(concurrent)
      appeared.  */
   tree order = omp_find_clause (clauses, OMP_CLAUSE_ORDER);
@@ -18397,6 +18561,8 @@  gimplify_expr (tree *expr_p, gimple_seq
 	case OMP_FOR:
 	case OMP_DISTRIBUTE:
 	case OMP_TASKLOOP:
+	case OMP_TILE:
+	case OMP_UNROLL:
 	case OACC_LOOP:
 	  ret = gimplify_omp_for (expr_p, pre_p);
 	  break;
--- gcc/omp-general.h.jj	2024-06-04 13:18:25.972107111 +0200
+++ gcc/omp-general.h	2024-06-04 13:30:00.775871490 +0200
@@ -283,4 +283,7 @@  typedef omp_addr_tokenizer::omp_addr_tok
 
 extern bool omp_parse_expr (vec<omp_addr_token *> &, tree);
 
+extern tree omp_loop_number_of_iterations (tree, int, tree * = NULL);
+extern void omp_maybe_apply_loop_xforms (tree *, tree);
+
 #endif /* GCC_OMP_GENERAL_H */
--- gcc/omp-general.cc.jj	2024-06-04 13:18:25.970107138 +0200
+++ gcc/omp-general.cc	2024-06-04 13:30:00.913869657 +0200
@@ -144,7 +144,11 @@  omp_adjust_for_condition (location_t loc
 
     case LE_EXPR:
       if (POINTER_TYPE_P (TREE_TYPE (*n2)))
-	*n2 = fold_build_pointer_plus_hwi_loc (loc, *n2, 1);
+	{
+	  tree unit = TYPE_SIZE_UNIT (TREE_TYPE (TREE_TYPE (v)));
+	  gcc_assert (TREE_CODE (unit) == INTEGER_CST);
+	  *n2 = fold_build_pointer_plus_loc (loc, *n2, unit);
+	}
       else
 	*n2 = fold_build2_loc (loc, PLUS_EXPR, TREE_TYPE (*n2), *n2,
 			       build_int_cst (TREE_TYPE (*n2), 1));
@@ -152,7 +156,14 @@  omp_adjust_for_condition (location_t loc
       break;
     case GE_EXPR:
       if (POINTER_TYPE_P (TREE_TYPE (*n2)))
-	*n2 = fold_build_pointer_plus_hwi_loc (loc, *n2, -1);
+	{
+	  tree unit = TYPE_SIZE_UNIT (TREE_TYPE (TREE_TYPE (v)));
+	  gcc_assert (TREE_CODE (unit) == INTEGER_CST);
+	  unit = convert_to_ptrofftype_loc (loc, unit);
+	  unit = fold_build1_loc (loc, NEGATE_EXPR, TREE_TYPE (unit),
+				  unit);
+	  *n2 = fold_build_pointer_plus_loc (loc, *n2, unit);
+	}
       else
 	*n2 = fold_build2_loc (loc, MINUS_EXPR, TREE_TYPE (*n2), *n2,
 			       build_int_cst (TREE_TYPE (*n2), 1));
@@ -3789,5 +3800,480 @@  debug_omp_tokenized_addr (vec<omp_addr_t
   fputs ("\n", stderr);
 }
 
+/* Return number of iterations of loop I in FOR_STMT.  If PSTEP is non-NULL,
+   *PSTEP will be the loop step.  */
+
+tree
+omp_loop_number_of_iterations (tree for_stmt, int i, tree *pstep)
+{
+  tree t = TREE_VEC_ELT (OMP_FOR_INIT (for_stmt), i);
+  gcc_assert (TREE_CODE (t) == MODIFY_EXPR);
+  tree decl = TREE_OPERAND (t, 0);
+  tree n1 = TREE_OPERAND (t, 1);
+  tree type = TREE_TYPE (decl);
+  tree cond = TREE_VEC_ELT (OMP_FOR_COND (for_stmt), i);
+  gcc_assert (COMPARISON_CLASS_P (cond));
+  gcc_assert (TREE_OPERAND (cond, 0) == decl);
+  tree_code cond_code = TREE_CODE (cond);
+  tree n2 = TREE_OPERAND (cond, 1);
+  t = TREE_VEC_ELT (OMP_FOR_INCR (for_stmt), i);
+  tree step = NULL_TREE;
+  switch (TREE_CODE (t))
+    {
+    case PREINCREMENT_EXPR:
+    case POSTINCREMENT_EXPR:
+      gcc_assert (!POINTER_TYPE_P (type));
+      gcc_assert (TREE_OPERAND (t, 0) == decl);
+      step = build_int_cst (type, 1);
+      break;
+    case PREDECREMENT_EXPR:
+    case POSTDECREMENT_EXPR:
+      gcc_assert (!POINTER_TYPE_P (type));
+      gcc_assert (TREE_OPERAND (t, 0) == decl);
+      step = build_int_cst (type, -1);
+      break;
+    case MODIFY_EXPR:
+      gcc_assert (TREE_OPERAND (t, 0) == decl);
+      t = TREE_OPERAND (t, 1);
+      switch (TREE_CODE (t))
+	{
+	case PLUS_EXPR:
+	  if (TREE_OPERAND (t, 1) == decl)
+	    {
+	      TREE_OPERAND (t, 1) = TREE_OPERAND (t, 0);
+	      TREE_OPERAND (t, 0) = decl;
+	    }
+	  /* FALLTHRU */
+	case POINTER_PLUS_EXPR:
+	case MINUS_EXPR:
+	  step = omp_get_for_step_from_incr (EXPR_LOCATION (t), t);
+	  break;
+	default:
+	  gcc_unreachable ();
+	}
+      break;
+    default:
+      gcc_unreachable ();
+    }
+  omp_adjust_for_condition (EXPR_LOCATION (for_stmt), &cond_code, &n2,
+			    decl, step);
+  if (pstep)
+    *pstep = step;
+  if (INTEGRAL_TYPE_P (type)
+      && TYPE_PRECISION (type) < TYPE_PRECISION (long_long_integer_type_node))
+    {
+      n1 = fold_convert (long_long_integer_type_node, n1);
+      n2 = fold_convert (long_long_integer_type_node, n2);
+      step = fold_convert (long_long_integer_type_node, step);
+    }
+  if (cond_code == LT_EXPR
+      || POINTER_TYPE_P (type)
+      || !TYPE_UNSIGNED (TREE_TYPE (n1)))
+    {
+      if (POINTER_TYPE_P (type))
+	t = fold_build2 (POINTER_DIFF_EXPR, ssizetype, n2, n1);
+      else
+	t = fold_build2 (MINUS_EXPR, TREE_TYPE (n1), n2, n1);
+      t = fold_build2 (CEIL_DIV_EXPR, TREE_TYPE (t), t, step);
+    }
+  else
+    {
+      t = fold_build2 (MINUS_EXPR, type, n1, n2);
+      t = fold_build2 (CEIL_DIV_EXPR, type, t,
+		       fold_build1 (NEGATE_EXPR, type, step));
+    }
+  return t;
+}
+
+/* Tile transformation:
+   Original loop:
+
+  #pragma omp tile sizes(16, 32)
+  for (i = 0; i < k; ++i)
+    for (j = 0; j < 128; j += 2)
+      {
+	baz (i, j);
+      }
+
+   Transformed loop:
+  #pragma omp tile sizes(16, 32)
+  for (i.0 = 0; i.0 < k; i.0 += 16)
+    for (j.0 = 0; j.0 < 128; j.0 += 64)
+      {
+	i = i.0;
+	i.1 = MIN_EXPR <i.0 + 16, k>;
+	goto <D.2783>;
+	<D.2782>:;
+	j = j.0;
+	j.1 = j.0 + 32;
+	goto <D.2786>;
+	<D.2785>:;
+	{
+	  baz (i, j);
+	}
+	j += 2;
+	<D.2786>:;
+	if (j < j.1) goto <D.2785>; else goto <D.2787>;
+	<D.2787>:;
+	++i;
+	<D.2783>:;
+	if (i < i.1) goto <D.2782>; else goto <D.2784>;
+	<D.2784>:;
+      }
+
+   where the grid loops have canonical form, but the inner
+   loops don't and so are immediately lowered.  */
+
+static void
+omp_apply_tile (tree for_stmt, tree sizes, int size)
+{
+  tree pre_body = NULL_TREE, post_body = NULL_TREE;
+  tree orig_sizes = sizes;
+  if (OMP_FOR_NON_RECTANGULAR (for_stmt))
+    {
+      error_at (EXPR_LOCATION (for_stmt), "non-rectangular %<tile%>");
+      return;
+    }
+  for (int i = 0; i < TREE_VEC_LENGTH (OMP_FOR_INIT (for_stmt)); i++)
+    {
+      if (orig_sizes)
+	{
+	  size = tree_to_uhwi (TREE_VALUE (sizes));
+	  sizes = TREE_CHAIN (sizes);
+	}
+      if (size == 1)
+	continue;
+      if (OMP_FOR_ORIG_DECLS (for_stmt) == NULL_TREE)
+	{
+	  OMP_FOR_ORIG_DECLS (for_stmt)
+	    = make_tree_vec (TREE_VEC_LENGTH (OMP_FOR_INIT (for_stmt)));
+	  for (int j = 0; j < TREE_VEC_LENGTH (OMP_FOR_INIT (for_stmt)); j++)
+	    {
+	      gcc_assert (TREE_CODE (TREE_VEC_ELT (OMP_FOR_INIT (for_stmt), j))
+			  == MODIFY_EXPR);
+	      TREE_VEC_ELT (OMP_FOR_ORIG_DECLS (for_stmt), j)
+		= TREE_OPERAND (TREE_VEC_ELT (OMP_FOR_INIT (for_stmt), j), 0);
+	    }
+	}
+      tree step;
+      tree iters = omp_loop_number_of_iterations (for_stmt, i, &step);
+      tree t = TREE_VEC_ELT (OMP_FOR_INIT (for_stmt), i);
+      tree decl = TREE_OPERAND (t, 0);
+      tree type = TREE_TYPE (decl);
+      tree griddecl = create_tmp_var_raw (type);
+      DECL_CONTEXT (griddecl) = current_function_decl;
+      t = build1 (DECL_EXPR, void_type_node, griddecl);
+      append_to_statement_list (t, &OMP_FOR_PRE_BODY (for_stmt));
+      TREE_OPERAND (TREE_VEC_ELT (OMP_FOR_INIT (for_stmt), i), 0) = griddecl;
+      TREE_PRIVATE (TREE_VEC_ELT (OMP_FOR_INIT (for_stmt), i)) = 1;
+      tree cond = TREE_VEC_ELT (OMP_FOR_COND (for_stmt), i);
+      TREE_OPERAND (cond, 0) = griddecl;
+      tree ub = save_expr (TREE_OPERAND (cond, 1));
+      TREE_OPERAND (cond, 1) = ub;
+      t = TREE_VEC_ELT (OMP_FOR_INCR (for_stmt), i);
+      if (TREE_CODE (cond) == NE_EXPR)
+	{
+	  tree_code cond_code = TREE_CODE (cond);
+	  omp_adjust_for_condition (EXPR_LOCATION (for_stmt), &cond_code,
+				    &ub, griddecl, step);
+	  TREE_SET_CODE (cond, cond_code);
+	}
+      step = save_expr (step);
+      tree gridstep = fold_build2 (MULT_EXPR, TREE_TYPE (step),
+				   step, build_int_cst (TREE_TYPE (step),
+							size));
+      if (POINTER_TYPE_P (type))
+	t = build2 (POINTER_PLUS_EXPR, type, griddecl,
+		    fold_convert (sizetype, gridstep));
+      else
+	t = build2 (PLUS_EXPR, type, griddecl, gridstep);
+      t = build2 (MODIFY_EXPR, type, griddecl, t);
+      TREE_VEC_ELT (OMP_FOR_INCR (for_stmt), i) = t;
+      t = build2 (MODIFY_EXPR, type, decl, griddecl);
+      append_to_statement_list (t, &pre_body);
+      if (POINTER_TYPE_P (type))
+	t = build2 (POINTER_PLUS_EXPR, type, griddecl,
+		    fold_convert (sizetype, gridstep));
+      else
+	t = build2 (PLUS_EXPR, type, griddecl, gridstep);
+      bool minmax_needed = true;
+      if (TREE_CODE (iters) == INTEGER_CST)
+	{
+	  wide_int witers = wi::to_wide (iters);
+	  wide_int wsize = wide_int::from (size, witers.get_precision (),
+					   TYPE_SIGN (TREE_TYPE (iters)));
+	  if (wi::multiple_of_p (witers, wsize, TYPE_SIGN (TREE_TYPE (iters))))
+	    minmax_needed = false;
+	}
+      if (minmax_needed)
+	switch (TREE_CODE (cond))
+	  {
+	  case LE_EXPR:
+	    if (POINTER_TYPE_P (type))
+	      t = build2 (MIN_EXPR, type, t,
+			  build2 (POINTER_PLUS_EXPR, type, ub, size_int (1)));
+	    else
+	      t = build2 (MIN_EXPR, type, t,
+			  build2 (PLUS_EXPR, type, ub, build_one_cst (type)));
+	    break;
+	  case LT_EXPR:
+	    t = build2 (MIN_EXPR, type, t, ub);
+	    break;
+	  case GE_EXPR:
+	    if (POINTER_TYPE_P (type))
+	      t = build2 (MAX_EXPR, type, t,
+			  build2 (POINTER_PLUS_EXPR, type, ub, size_int (-1)));
+	    else
+	      t = build2 (MAX_EXPR, type, t,
+			  build2 (PLUS_EXPR, type, ub,
+				  build_minus_one_cst (type)));
+	    break;
+	  case GT_EXPR:
+	    t = build2 (MAX_EXPR, type, t, ub);
+	    break;
+	  default:
+	    gcc_unreachable ();
+	  }
+      tree end = create_tmp_var_raw (type);
+      DECL_CONTEXT (end) = current_function_decl;
+      end = build4 (TARGET_EXPR, type, end, t, NULL_TREE, NULL_TREE);
+      TREE_SIDE_EFFECTS (end) = 1;
+      append_to_statement_list (end, &pre_body);
+      tree lab1 = create_artificial_label (UNKNOWN_LOCATION);
+      tree lab2 = create_artificial_label (UNKNOWN_LOCATION);
+      t = build1 (GOTO_EXPR, void_type_node, lab2);
+      append_to_statement_list (t, &pre_body);
+      t = build1 (LABEL_EXPR, void_type_node, lab1);
+      append_to_statement_list (t, &pre_body);
+      tree this_post_body = NULL_TREE;
+      if (POINTER_TYPE_P (type))
+	t = build2 (POINTER_PLUS_EXPR, type, decl,
+		    fold_convert (sizetype, step));
+      else
+	t = build2 (PLUS_EXPR, type, decl, step);
+      t = build2 (MODIFY_EXPR, type, decl, t);
+      append_to_statement_list (t, &this_post_body);
+      t = build1 (LABEL_EXPR, void_type_node, lab2);
+      append_to_statement_list (t, &this_post_body);
+      t = build2 ((TREE_CODE (cond) == LT_EXPR || TREE_CODE (cond) == LE_EXPR)
+		  ? LT_EXPR : GT_EXPR, boolean_type_node, decl, end);
+      if (orig_sizes == NULL_TREE)
+	{
+	  gcc_assert (i == 0);
+	  t = build3 (ANNOTATE_EXPR, TREE_TYPE (t), t,
+		      build_int_cst (integer_type_node,
+				     annot_expr_unroll_kind),
+		      build_int_cst (integer_type_node, size));
+	}
+      t = build3 (COND_EXPR, void_type_node, t,
+		  build1 (GOTO_EXPR, void_type_node, lab1), NULL_TREE);
+      append_to_statement_list (t, &this_post_body);
+      append_to_statement_list (post_body, &this_post_body);
+      post_body = this_post_body;
+    }
+  if (pre_body || post_body)
+    {
+      append_to_statement_list (OMP_FOR_BODY (for_stmt), &pre_body);
+      append_to_statement_list (post_body, &pre_body);
+      OMP_FOR_BODY (for_stmt) = pre_body;
+    }
+}
+
+/* Callback for walk_tree to find nested loop transforming construct.  */
+
+static tree
+find_nested_loop_xform (tree *tp, int *walk_subtrees, void *data)
+{
+  tree **pdata = (tree **) data;
+  *walk_subtrees = 0;
+  switch (TREE_CODE (*tp))
+    {
+    case OMP_TILE:
+    case OMP_UNROLL:
+      pdata[1] = tp;
+      return *tp;
+    case BIND_EXPR:
+      if (BIND_EXPR_VARS (*tp)
+	  || (BIND_EXPR_BLOCK (*tp)
+	      && BLOCK_VARS (BIND_EXPR_BLOCK (*tp))))
+	pdata[0] = tp;
+      *walk_subtrees = 1;
+      break;
+    case STATEMENT_LIST:
+      if (!tsi_one_before_end_p (tsi_start (*tp)))
+	pdata[0] = tp;
+      *walk_subtrees = 1;
+      break;
+    case TRY_FINALLY_EXPR:
+      pdata[0] = tp;
+      *walk_subtrees = 1;
+      break;
+    default:
+      break;
+    }
+  return NULL;
+}
+
+/* Main entry point for performing OpenMP loop transformations.  */
+
+void
+omp_maybe_apply_loop_xforms (tree *expr_p, tree for_clauses)
+{
+  tree for_stmt = *expr_p;
+
+  switch (TREE_CODE (for_stmt))
+    {
+    case OMP_TILE:
+    case OMP_UNROLL:
+      if (OMP_LOOPXFORM_LOWERED (for_stmt))
+	return;
+      break;
+    default:
+      break;
+    }
+
+  tree *inner_expr_p = expr_p;
+  tree inner_for_stmt = for_stmt;
+  for (int i = 0; i < TREE_VEC_LENGTH (OMP_FOR_INIT (for_stmt)); i++)
+    {
+      /* If some loop nest needs one or more loops in canonical form
+	 from nested loop transforming constructs, first perform the
+	 loop transformation on the nested construct and then move over
+	 the corresponding loops in canonical form from the inner construct
+	 to the outer one.  */
+      if (TREE_VEC_ELT (OMP_FOR_INIT (for_stmt), i) == NULL_TREE)
+	{
+	  if (inner_for_stmt == for_stmt
+	      && omp_find_clause (for_clauses ? for_clauses
+				  : OMP_FOR_CLAUSES (for_stmt),
+				  OMP_CLAUSE_ORDERED))
+	    {
+	      error_at (EXPR_LOCATION (for_stmt),
+			"%<ordered%> clause used with generated loops");
+	      *expr_p = void_node;
+	      return;
+	    }
+	  tree *data[2] = { NULL, NULL };
+	  walk_tree (&OMP_FOR_BODY (inner_for_stmt),
+		     find_nested_loop_xform, &data, NULL);
+	  gcc_assert (data[1]);
+	  if (data[0])
+	    {
+	      /* If there is a BIND_EXPR declaring some vars, or statement
+		 list with more than one stmt etc., move the intervening
+		 code around the outermost loop.  */
+	      tree t = *inner_expr_p;
+	      *inner_expr_p = OMP_FOR_BODY (inner_for_stmt);
+	      OMP_FOR_BODY (inner_for_stmt) = *data[1];
+	      *data[1] = t;
+	      inner_expr_p = data[1];
+	      data[1] = &OMP_FOR_BODY (inner_for_stmt);
+	    }
+	  inner_for_stmt = *data[1];
+
+	  omp_maybe_apply_loop_xforms (data[1], NULL_TREE);
+	  if (*data[1] != inner_for_stmt)
+	    {
+	      tree *data2[2] = { NULL, NULL };
+	      walk_tree (data[1], find_nested_loop_xform, &data2, NULL);
+	      gcc_assert (data2[1]
+			  && *data2[1] == inner_for_stmt
+			  && data2[0]);
+	      tree t = *inner_expr_p;
+	      *inner_expr_p = *data[1];
+	      *data[1] = *data2[1];
+	      *data2[1] = t;
+	      inner_expr_p = data2[1];
+	    }
+	  tree clauses = OMP_FOR_CLAUSES (inner_for_stmt);
+	  gcc_checking_assert (TREE_CODE (inner_for_stmt) != OMP_UNROLL
+			       || omp_find_clause (clauses,
+						   OMP_CLAUSE_PARTIAL));
+	  append_to_statement_list (OMP_FOR_PRE_BODY (inner_for_stmt),
+				    &OMP_FOR_PRE_BODY (for_stmt));
+	  OMP_FOR_PRE_BODY (inner_for_stmt) = NULL_TREE;
+	  if (OMP_FOR_ORIG_DECLS (for_stmt) == NULL_TREE
+	      && OMP_FOR_ORIG_DECLS (inner_for_stmt) != NULL_TREE)
+	    {
+	      OMP_FOR_ORIG_DECLS (for_stmt)
+		= make_tree_vec (TREE_VEC_LENGTH (OMP_FOR_INIT (for_stmt)));
+	      for (int j = 0; j < TREE_VEC_LENGTH (OMP_FOR_INIT (for_stmt));
+		   j++)
+		{
+		  if (TREE_VEC_ELT (OMP_FOR_INIT (for_stmt), j) == NULL_TREE)
+		    continue;
+		  gcc_assert (TREE_CODE (TREE_VEC_ELT (OMP_FOR_INIT (for_stmt),
+						       j)) == MODIFY_EXPR);
+		  TREE_VEC_ELT (OMP_FOR_ORIG_DECLS (for_stmt), j)
+		    = TREE_OPERAND (TREE_VEC_ELT (OMP_FOR_INIT (for_stmt), j),
+				    0);
+		}
+	    }
+	  for (int j = 0; j < TREE_VEC_LENGTH (OMP_FOR_INIT (inner_for_stmt));
+	       ++j)
+	    {
+	      if (i + j == TREE_VEC_LENGTH (OMP_FOR_INIT (for_stmt)))
+		break;
+	      if (OMP_FOR_ORIG_DECLS (for_stmt))
+		{
+		  if (OMP_FOR_ORIG_DECLS (inner_for_stmt))
+		    {
+		      TREE_VEC_ELT (OMP_FOR_ORIG_DECLS (for_stmt), i + j)
+			= TREE_VEC_ELT (OMP_FOR_ORIG_DECLS (inner_for_stmt),
+					j);
+		      TREE_VEC_ELT (OMP_FOR_ORIG_DECLS (inner_for_stmt), j)
+			= NULL_TREE;
+		    }
+		  else
+		    {
+		      tree t = TREE_VEC_ELT (OMP_FOR_INIT (inner_for_stmt), j);
+		      gcc_assert (TREE_CODE (t) == MODIFY_EXPR);
+		      TREE_VEC_ELT (OMP_FOR_ORIG_DECLS (for_stmt), i + j)
+			= TREE_OPERAND (t, 0);
+		    }
+		}
+	      TREE_VEC_ELT (OMP_FOR_INIT (for_stmt), i + j)
+		= TREE_VEC_ELT (OMP_FOR_INIT (inner_for_stmt), j);
+	      TREE_VEC_ELT (OMP_FOR_COND (for_stmt), i + j)
+		= TREE_VEC_ELT (OMP_FOR_COND (inner_for_stmt), j);
+	      TREE_VEC_ELT (OMP_FOR_INCR (for_stmt), i + j)
+		= TREE_VEC_ELT (OMP_FOR_INCR (inner_for_stmt), j);
+	      TREE_VEC_ELT (OMP_FOR_INIT (inner_for_stmt), j) = NULL_TREE;
+	      TREE_VEC_ELT (OMP_FOR_COND (inner_for_stmt), j) = NULL_TREE;
+	      TREE_VEC_ELT (OMP_FOR_INCR (inner_for_stmt), j) = NULL_TREE;
+	    }
+	}
+    }
+
+  switch (TREE_CODE (for_stmt))
+    {
+    case OMP_TILE:
+      tree sizes;
+      sizes = omp_find_clause (OMP_FOR_CLAUSES (for_stmt), OMP_CLAUSE_SIZES);
+      omp_apply_tile (for_stmt, OMP_CLAUSE_SIZES_LIST (sizes), 0);
+      OMP_LOOPXFORM_LOWERED (for_stmt) = 1;
+      break;
+    case OMP_UNROLL:
+      tree partial;
+      partial = omp_find_clause (OMP_FOR_CLAUSES (for_stmt),
+				 OMP_CLAUSE_PARTIAL);
+      if (partial)
+	omp_apply_tile (for_stmt, NULL_TREE,
+			OMP_CLAUSE_PARTIAL_EXPR (partial)
+			? tree_to_shwi (OMP_CLAUSE_PARTIAL_EXPR (partial))
+			: 8);
+      else if (omp_find_clause (OMP_FOR_CLAUSES (for_stmt), OMP_CLAUSE_FULL))
+	{
+	  tree iters = omp_loop_number_of_iterations (for_stmt, 0, NULL);
+	  if (TREE_CODE (iters) != INTEGER_CST)
+	    error_at (EXPR_LOCATION (for_stmt),
+		      "non-constant iteration count of %<unroll full%> loop");
+	}
+      OMP_LOOPXFORM_LOWERED (for_stmt) = 1;
+      break;
+    default:
+      break;
+    }
+}
 
 #include "gt-omp-general.h"
--- gcc/c-family/c-common.h.jj	2024-06-04 13:19:03.339609881 +0200
+++ gcc/c-family/c-common.h	2024-06-04 13:30:00.831870746 +0200
@@ -1304,6 +1304,7 @@  extern void c_finish_omp_taskwait (locat
 extern void c_finish_omp_taskyield (location_t);
 extern tree c_finish_omp_for (location_t, enum tree_code, tree, tree, tree,
 			      tree, tree, tree, tree, bool);
+extern int c_omp_find_generated_loop (tree &, int, walk_tree_lh);
 extern bool c_omp_check_loop_iv (tree, tree, walk_tree_lh);
 extern bool c_omp_check_loop_iv_exprs (location_t, enum tree_code, tree, int,
 				       tree, tree, tree, walk_tree_lh);
--- gcc/c-family/c-gimplify.cc.jj	2024-06-04 13:18:24.921121096 +0200
+++ gcc/c-family/c-gimplify.cc	2024-06-04 13:30:00.849870507 +0200
@@ -529,6 +529,8 @@  c_genericize_control_stmt (tree *stmt_p,
     case OMP_DISTRIBUTE:
     case OMP_LOOP:
     case OMP_TASKLOOP:
+    case OMP_TILE:
+    case OMP_UNROLL:
     case OACC_LOOP:
       genericize_omp_for_stmt (stmt_p, walk_subtrees, data, func, lh);
       break;
--- gcc/c-family/c-omp.cc.jj	2024-06-04 13:18:24.943120803 +0200
+++ gcc/c-family/c-omp.cc	2024-06-04 19:42:24.044594326 +0200
@@ -958,6 +958,19 @@  c_finish_omp_for (location_t locus, enum
       tree cond = TREE_VEC_ELT (condv, i);
       tree incr = TREE_VEC_ELT (incrv, i);
 
+      if (init == NULL_TREE)
+	{
+	  gcc_assert (decl == NULL_TREE
+		      && cond == NULL_TREE
+		      && incr == NULL_TREE);
+	  for (i++; i < TREE_VEC_LENGTH (declv); i++)
+	    gcc_assert (TREE_VEC_ELT (declv, i) == NULL_TREE
+			&& TREE_VEC_ELT (initv, i) == NULL_TREE
+			&& TREE_VEC_ELT (condv, i) == NULL_TREE
+			&& TREE_VEC_ELT (incrv, i) == NULL_TREE);
+	  break;
+	}
+
       elocus = locus;
       if (EXPR_HAS_LOCATION (init))
 	elocus = EXPR_LOCATION (init);
@@ -1304,9 +1317,11 @@  static int
 c_omp_is_loop_iterator (tree decl, struct c_omp_check_loop_iv_data *d)
 {
   for (int i = 0; i < TREE_VEC_LENGTH (d->declv); i++)
-    if (decl == TREE_VEC_ELT (d->declv, i)
-	|| (TREE_CODE (TREE_VEC_ELT (d->declv, i)) == TREE_LIST
-	    && decl == TREE_PURPOSE (TREE_VEC_ELT (d->declv, i))))
+    if (TREE_VEC_ELT (d->declv, i) == NULL_TREE)
+      continue;
+    else if (decl == TREE_VEC_ELT (d->declv, i)
+	     || (TREE_CODE (TREE_VEC_ELT (d->declv, i)) == TREE_LIST
+		 && decl == TREE_PURPOSE (TREE_VEC_ELT (d->declv, i))))
       return i;
     else if (TREE_CODE (TREE_VEC_ELT (d->declv, i)) == TREE_LIST
 	     && TREE_CHAIN (TREE_VEC_ELT (d->declv, i))
@@ -1584,6 +1599,68 @@  c_omp_check_nonrect_loop_iv (tree *tp, s
   return ret;
 }
 
+/* Callback for walk_tree to find nested loop transforming construct.  */
+
+static tree
+c_find_nested_loop_xform_r (tree *tp, int *walk_subtrees, void *)
+{
+  *walk_subtrees = 0;
+  switch (TREE_CODE (*tp))
+    {
+    case OMP_TILE:
+    case OMP_UNROLL:
+      return *tp;
+    case BIND_EXPR:
+      *walk_subtrees = 1;
+      break;
+    case STATEMENT_LIST:
+      *walk_subtrees = 1;
+      break;
+    case TRY_FINALLY_EXPR:
+      *walk_subtrees = 1;
+      break;
+    default:
+      break;
+    }
+  return NULL;
+}
+
+/* Find Jth loop among generated loops of STMT.  */
+
+int
+c_omp_find_generated_loop (tree &stmt, int j, walk_tree_lh lh)
+{
+  stmt = walk_tree_1 (&stmt, c_find_nested_loop_xform_r,
+		      NULL, NULL, lh);
+  gcc_assert (stmt);
+  switch (TREE_CODE (stmt))
+    {
+    case OMP_UNROLL:
+      gcc_assert (omp_find_clause (OMP_FOR_CLAUSES (stmt),
+				   OMP_CLAUSE_PARTIAL));
+      /* FALLTHRU */
+    case OMP_TILE:
+      int k;
+      k = 0;
+      for (int i = 0; i < TREE_VEC_LENGTH (OMP_FOR_INIT (stmt)); ++i)
+	if (i == j)
+	  {
+	    if (TREE_VEC_ELT (OMP_FOR_INIT (stmt), i) == NULL_TREE)
+	      {
+		stmt = OMP_FOR_BODY (stmt);
+		return c_omp_find_generated_loop (stmt, k, lh);
+	      }
+	    else
+	      return i;
+	  }
+	else if (TREE_VEC_ELT (OMP_FOR_INIT (stmt), i) == NULL_TREE)
+	  ++k;
+      gcc_unreachable ();
+    default:
+      gcc_unreachable ();
+    }
+}
+
 /* Diagnose invalid references to loop iterators in lb, b and incr
    expressions.  */
 
@@ -1592,7 +1669,7 @@  c_omp_check_loop_iv (tree stmt, tree dec
 {
   hash_set<tree> pset;
   struct c_omp_check_loop_iv_data data;
-  int i;
+  int i, k = 0;
 
   data.declv = declv;
   data.fail = false;
@@ -1602,13 +1679,24 @@  c_omp_check_loop_iv (tree stmt, tree dec
   data.ppset = &pset;
   for (i = 0; i < TREE_VEC_LENGTH (OMP_FOR_INIT (stmt)); i++)
     {
+      tree this_stmt = stmt;
+      int j = i;
       tree init = TREE_VEC_ELT (OMP_FOR_INIT (stmt), i);
+      if (init == NULL_TREE)
+	{
+	  if (k == 0)
+	    data.declv = copy_node (declv);
+	  this_stmt = OMP_FOR_BODY (stmt);
+	  j = c_omp_find_generated_loop (this_stmt, k++, lh);
+	  init = TREE_VEC_ELT (OMP_FOR_INIT (this_stmt), j);
+	  TREE_VEC_ELT (data.declv, i) = TREE_OPERAND (init, 0);
+	}
       gcc_assert (TREE_CODE (init) == MODIFY_EXPR);
       tree decl = TREE_OPERAND (init, 0);
-      tree cond = TREE_VEC_ELT (OMP_FOR_COND (stmt), i);
+      tree cond = TREE_VEC_ELT (OMP_FOR_COND (this_stmt), j);
       gcc_assert (COMPARISON_CLASS_P (cond));
       gcc_assert (TREE_OPERAND (cond, 0) == decl);
-      tree incr = TREE_VEC_ELT (OMP_FOR_INCR (stmt), i);
+      tree incr = TREE_VEC_ELT (OMP_FOR_INCR (this_stmt), j);
       data.expr_loc = EXPR_LOCATION (TREE_OPERAND (init, 1));
       tree vec_outer1 = NULL_TREE, vec_outer2 = NULL_TREE;
       int kind = 0;
@@ -1636,9 +1724,9 @@  c_omp_check_loop_iv (tree stmt, tree dec
 	 expression then involves the subtraction and always refers
 	 to the original value.  The C++ FE needs to warn on those
 	 earlier.  */
-      if (decl == TREE_VEC_ELT (declv, i)
-	  || (TREE_CODE (TREE_VEC_ELT (declv, i)) == TREE_LIST
-	      && decl == TREE_PURPOSE (TREE_VEC_ELT (declv, i))))
+      if (decl == TREE_VEC_ELT (data.declv, i)
+	  || (TREE_CODE (TREE_VEC_ELT (data.declv, i)) == TREE_LIST
+	      && decl == TREE_PURPOSE (TREE_VEC_ELT (data.declv, i))))
 	{
 	  data.expr_loc = EXPR_LOCATION (cond);
 	  data.kind = kind | 1;
@@ -1657,6 +1745,15 @@  c_omp_check_loop_iv (tree stmt, tree dec
 			 " used in a single loop", vec_outer1, vec_outer2);
 	  data.fail = true;
 	}
+      else if ((vec_outer1 || vec_outer2) && this_stmt != stmt)
+	{
+	  location_t loc = data.expr_loc;
+	  if (loc == UNKNOWN_LOCATION)
+	    loc = data.stmt_loc;
+	  sorry_at (loc, "non-rectangular loops from generated loops "
+			 "unsupported");
+	  data.fail = true;
+	}
       if (vec_outer1 || vec_outer2)
 	OMP_FOR_NON_RECTANGULAR (stmt) = 1;
       if (TREE_CODE (incr) == MODIFY_EXPR)
@@ -1853,6 +1950,12 @@  c_omp_check_loop_binding_exprs (tree stm
   for (int i = 1; i < TREE_VEC_LENGTH (OMP_FOR_INIT (stmt)); i++)
     {
       tree init = TREE_VEC_ELT (OMP_FOR_INIT (stmt), i);
+      if (init == NULL_TREE)
+	{
+	  sorry_at (loc, "imperfectly nested loop using generated loops");
+	  ok = false;
+	  continue;
+	}
       tree cond = TREE_VEC_ELT (OMP_FOR_COND (stmt), i);
       tree incr = TREE_VEC_ELT (OMP_FOR_INCR (stmt), i);
       gcc_assert (TREE_CODE (init) == MODIFY_EXPR);
@@ -4268,14 +4371,14 @@  const struct c_omp_directive c_omp_direc
     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
--- gcc/c-family/c-pragma.h.jj	2024-06-04 13:18:25.004119992 +0200
+++ gcc/c-family/c-pragma.h	2024-06-04 13:30:00.855870428 +0200
@@ -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,
@@ -142,6 +145,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,
--- gcc/c-family/c-pragma.cc.jj	2024-06-04 13:18:24.973120404 +0200
+++ gcc/c-family/c-pragma.cc	2024-06-04 13:30:00.886870016 +0200
@@ -1556,6 +1556,8 @@  static const struct omp_pragma_def omp_p
   { "target", PRAGMA_OMP_TARGET },
   { "taskloop", PRAGMA_OMP_TASKLOOP },
   { "teams", PRAGMA_OMP_TEAMS },
+  { "tile", PRAGMA_OMP_TILE },
+  { "unroll", PRAGMA_OMP_UNROLL },
 };
 
 void
--- gcc/c/c-parser.cc.jj	2024-06-04 13:19:03.420608803 +0200
+++ gcc/c/c-parser.cc	2024-06-05 15:30:39.227093973 +0200
@@ -1711,6 +1711,8 @@  static void c_parser_omp_threadprivate (
 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_omp_next_tokens_can_be_canon_loop (c_parser *,
+							enum tree_code, bool);
 static tree c_parser_omp_loop_nest (c_parser *, bool *);
 static tree c_parser_omp_for_loop (location_t, c_parser *, enum tree_code,
 				   tree, tree *, bool *);
@@ -5992,6 +5994,37 @@  c_parser_nth_token_starts_std_attributes
   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)
 {
@@ -6580,7 +6613,13 @@  check_omp_intervening_code (c_parser *pa
 		    "%<reduction%> %<inscan%> clause");
 	  omp_for_parse_state->perfect_nesting_fail = true;
 	}
-      /* TODO: Also reject loops with TILE directive.  */
+      else if (omp_for_parse_state->code == OMP_TILE)
+	{
+	  error_at (omp_for_parse_state->for_loc,
+		    "inner loops must be perfectly nested in "
+		     "%<pragma omp tile%>");
+	  omp_for_parse_state->perfect_nesting_fail = true;
+	}
       if (omp_for_parse_state->perfect_nesting_fail)
 	omp_for_parse_state->fail = true;
     }
@@ -7030,8 +7069,10 @@  c_parser_compound_statement_nostart (c_p
 	 __extension__ before the nested statement.  */
       if (in_omp_loop_block && !last_label)
 	{
+	  tree_code code = omp_for_parse_state->code;
 	  if (want_nested_loop
-	      && c_parser_next_token_is_keyword (parser, RID_FOR))
+	      && c_parser_omp_next_tokens_can_be_canon_loop (parser, code,
+							     false))
 	    {
 	      /* Found the next nested loop.  If there were intervening
 		 code statements collected before now, wrap them in an
@@ -14932,6 +14973,8 @@  c_parser_omp_clause_name (c_parser *pars
 	    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))
@@ -15008,6 +15051,8 @@  c_parser_omp_clause_name (c_parser *pars
 	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
@@ -17932,7 +17977,7 @@  c_parser_omp_clause_allocate (c_parser *
 	  if (has_modifiers)
 	    {
 	      c_parser_consume_token (parser);
-	      matching_parens parens2;;
+	      matching_parens parens2;
 	      parens2.require_open (parser);
 	      location_t expr_loc = c_parser_peek_token (parser)->location;
 	      c_expr expr = c_parser_expr_no_commas (parser, NULL);
@@ -19196,6 +19241,61 @@  c_parser_omp_clause_uniform (c_parser *p
   return list;
 }
 
+/* OpenMP 5.1
+   full */
+
+static tree
+c_parser_omp_clause_full (c_parser *parser, tree list)
+{
+  check_no_duplicate_clause (list, OMP_CLAUSE_FULL, "full");
+
+  location_t loc = c_parser_peek_token (parser)->location;
+  tree c = build_omp_clause (loc, OMP_CLAUSE_FULL);
+  OMP_CLAUSE_CHAIN (c) = list;
+  return c;
+}
+
+/* OpenMP 5.1
+   partial ( constant-expression ) */
+
+static tree
+c_parser_omp_clause_partial (c_parser *parser, tree list)
+{
+  tree num = NULL_TREE;
+  location_t loc = c_parser_peek_token (parser)->location;
+
+  check_no_duplicate_clause (list, OMP_CLAUSE_PARTIAL, "partial");
+
+  if (c_parser_next_token_is (parser, CPP_OPEN_PAREN))
+    {
+      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);
+      HOST_WIDE_INT n;
+      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;
+	}
+    }
+
+  tree c = build_omp_clause (loc, OMP_CLAUSE_PARTIAL);
+  OMP_CLAUSE_PARTIAL_EXPR (c) = num;
+  OMP_CLAUSE_CHAIN (c) = list;
+  return c;
+}
+
 /* OpenMP 5.0:
    detach ( event-handle ) */
 
@@ -19807,6 +19907,14 @@  c_parser_omp_all_clauses (c_parser *pars
 					    clauses);
 	  c_name = "enter";
 	  break;
+	case PRAGMA_OMP_CLAUSE_FULL:
+	  c_name = "full";
+	  clauses = c_parser_omp_clause_full (parser, clauses);
+	  break;
+	case PRAGMA_OMP_CLAUSE_PARTIAL:
+	  c_name = "partial";
+	  clauses = c_parser_omp_clause_partial (parser, clauses);
+	  break;
 	default:
 	  c_parser_error (parser, "expected an OpenMP clause");
 	  goto saw_error;
@@ -22223,6 +22331,56 @@  c_parser_omp_scan_loop_body (c_parser *p
 }
 
 
+/* Check if the next tokens can start a canonical loop.  Return true if yes,
+   otherwise diagnose an error if ERROR_P is true, and return false.  */
+static bool
+c_parser_omp_next_tokens_can_be_canon_loop (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))
+	return true;
+
+      if (c_parser_next_token_is (parser, CPP_PRAGMA))
+	switch (c_parser_peek_token (parser)->pragma_kind)
+	  {
+	  case PRAGMA_OMP_UNROLL:
+	  case PRAGMA_OMP_TILE:
+	    return true;
+	  default:
+	    break;
+	  }
+
+      /* Skip standard attributes on next for in case they are
+	 [[omp::directive (unroll partial (4))]] or
+	 [[omp::directive (tile sizes (1, 2, 3))]] etc.  */
+      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;
+}
+
+static tree c_parser_omp_tile (location_t, c_parser *, bool *);
+static tree c_parser_omp_unroll (location_t, c_parser *, bool *);
+
 /* This function parses a single level of a loop nest, invoking itself
    recursively if necessary.
 
@@ -22258,8 +22416,107 @@  c_parser_omp_loop_nest (c_parser *parser
   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.  */
+  /* Arrange for C23 standard attribute syntax to be parsed as regular
+     pragmas.  */
+  if (c_parser_nth_token_starts_std_attributes (parser, 1))
+    {
+      tree std_attrs = c_parser_std_attribute_specifier_sequence (parser);
+      c_parser_handle_statement_omp_attributes (parser, std_attrs, NULL);
+      if (std_attrs)
+	error_at (c_parser_peek_token (parser)->location,
+		  "attributes other than OpenMP directives "
+		  "are not allowed on %<for%> in loop nest");
+    }
+
   loc = c_parser_peek_token (parser)->location;
+
+  /* Handle loop transformations first.  */
+  if (c_parser_next_token_is (parser, CPP_PRAGMA))
+    {
+      tree transform = NULL_TREE, sizes, body = NULL_TREE;
+      int count = 0;
+      switch (c_parser_peek_token (parser)->pragma_kind)
+	{
+	case PRAGMA_OMP_UNROLL:
+	  c_parser_consume_pragma (parser);
+	  body = push_stmt_list ();
+	  transform = c_parser_omp_unroll (loc, parser, if_p);
+	  body = pop_stmt_list (body);
+	  if (transform == NULL_TREE || transform == error_mark_node)
+	    {
+	      transform = error_mark_node;
+	      break;
+	    }
+	  gcc_assert (TREE_CODE (transform) == OMP_UNROLL);
+	  if (omp_find_clause (OMP_FOR_CLAUSES (transform),
+			       OMP_CLAUSE_PARTIAL))
+	    {
+	      if (omp_for_parse_state->count - depth > 1)
+		{
+		  error_at (loc, "%<unroll%> construct with %<partial%> "
+				 "clause generates just one loop with "
+				 "canonical form but %d loops are needed",
+			    omp_for_parse_state->count - depth);
+		  transform = error_mark_node;
+		}
+	      else
+		count = 1;
+	    }
+	  else
+	    {
+	      error_at (loc, "generated loop of %<unroll%> construct "
+			     "without %<partial%> clause does not have "
+			     "canonical form");
+	      transform = error_mark_node;
+	    }
+	  break;
+	case PRAGMA_OMP_TILE:
+	  c_parser_consume_pragma (parser);
+	  body = push_stmt_list ();
+	  transform = c_parser_omp_tile (loc, parser, if_p);
+	  body = pop_stmt_list (body);
+	  if (transform == NULL_TREE || transform == error_mark_node)
+	    {
+	      transform = error_mark_node;
+	      break;
+	    }
+	  gcc_assert (TREE_CODE (transform) == OMP_TILE);
+	  sizes = omp_find_clause (OMP_FOR_CLAUSES (transform),
+				   OMP_CLAUSE_SIZES);
+	  gcc_assert (sizes);
+	  count = list_length (OMP_CLAUSE_SIZES_LIST (sizes));
+	  if (depth + count < omp_for_parse_state->count)
+	    {
+	      error_at (loc, "%<tile%> construct generates %d loops "
+			     "with canonical form but %d loops are needed",
+			count, omp_for_parse_state->count - depth);
+	      transform = error_mark_node;
+	    }
+	  break;
+	default:
+	  c_parser_pragma (parser, pragma_stmt, NULL);
+	  break;
+	}
+      if (transform == NULL_TREE)
+	error_at (loc, "expected %<for%> loop or OpenMP loop "
+		       "transformation construct");
+      if (transform == NULL_TREE || transform == error_mark_node)
+	{
+	  omp_for_parse_state->fail = true;
+	  return NULL_TREE;
+	}
+      for (count = omp_for_parse_state->count; depth < count; ++depth)
+	{
+	  TREE_VEC_ELT (omp_for_parse_state->declv, depth) = NULL_TREE;
+	  TREE_VEC_ELT (omp_for_parse_state->initv, depth) = NULL_TREE;
+	  TREE_VEC_ELT (omp_for_parse_state->condv, depth) = NULL_TREE;
+	  TREE_VEC_ELT (omp_for_parse_state->incrv, depth) = NULL_TREE;
+	}
+      omp_for_parse_state->want_nested_loop = false;
+      return body;
+    }
+
+  /* We have already matched the FOR token but not consumed it yet.  */
   gcc_assert (c_parser_next_token_is_keyword (parser, RID_FOR));
   c_parser_consume_token (parser);
 
@@ -22392,7 +22649,10 @@  c_parser_omp_loop_nest (c_parser *parser
 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_omp_next_tokens_can_be_canon_loop (parser,
+						     omp_for_parse_state->code,
+						     false))
     {
       omp_for_parse_state->depth++;
       body = c_parser_omp_loop_nest (parser, if_p);
@@ -22455,7 +22715,8 @@  parse_next:
      OMP_FOR.  That keeps the gimplifier happy later on, and meanwhile
      we have already resolved all references to the iteration variable
      in its true scope.  */
-  add_stmt (body);
+  if (body)
+    add_stmt (body);
   body = c_end_compound_stmt (loc, loop_scope, true);
   if (decl && TREE_CODE (body) == BIND_EXPR)
     {
@@ -22504,7 +22765,7 @@  c_parser_omp_for_loop (location_t loc, c
   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;
@@ -22514,9 +22775,11 @@  c_parser_omp_for_loop (location_t loc, c
       collapse = tree_to_shwi (OMP_CLAUSE_COLLAPSE_EXPR (cl));
     else if (OMP_CLAUSE_CODE (cl) == OMP_CLAUSE_TILE)
       {
-	tiling = true;
+	oacc_tiling = true;
 	collapse = list_length (OMP_CLAUSE_TILE_LIST (cl));
       }
+    else if (OMP_CLAUSE_CODE (cl) == OMP_CLAUSE_SIZES)
+      collapse = list_length (OMP_CLAUSE_SIZES_LIST (cl));
     else if (OMP_CLAUSE_CODE (cl) == OMP_CLAUSE_ORDERED
 	     && OMP_CLAUSE_ORDERED_EXPR (cl))
       {
@@ -22537,21 +22800,18 @@  c_parser_omp_for_loop (location_t loc, c
       ordered = collapse;
     }
 
-  gcc_assert (tiling || (collapse >= 1 && ordered >= 0));
+  gcc_assert (oacc_tiling || (collapse >= 1 && ordered >= 0));
   count = ordered ? ordered : collapse;
 
-  if (!c_parser_next_token_is_keyword (parser, RID_FOR))
-    {
-      c_parser_error (parser, "for statement expected");
-      return NULL;
-    }
+  if (!c_parser_omp_next_tokens_can_be_canon_loop (parser, code, true))
+    return NULL;
 
   /* Initialize parse state for recursive descent.  */
   data.declv = make_tree_vec (count);
   data.initv = make_tree_vec (count);
   data.condv = make_tree_vec (count);
   data.incrv = make_tree_vec (count);
-  data.pre_body = NULL_TREE;;
+  data.pre_body = NULL_TREE;
   data.bindings = NULL_TREE;
   data.for_loc = c_parser_peek_token (parser)->location;
   data.count = count;
@@ -22595,6 +22855,7 @@  c_parser_omp_for_loop (location_t loc, c
       /* Check for errors involving lb/ub/incr expressions referencing
 	 variables declared in intervening code.  */
       if (data.saw_intervening_code
+	  && stmt
 	  && !c_omp_check_loop_binding_exprs (stmt, NULL))
 	stmt = NULL_TREE;
 
@@ -22605,6 +22866,8 @@  c_parser_omp_for_loop (location_t loc, c
 	  for (i = 0; i < TREE_VEC_LENGTH (OMP_FOR_INIT (stmt)); i++)
 	    {
 	      tree init = TREE_VEC_ELT (OMP_FOR_INIT (stmt), i);
+	      if (init == NULL_TREE)
+		break;
 	      gcc_assert (TREE_CODE (init) == MODIFY_EXPR);
 	      tree decl = TREE_OPERAND (init, 0);
 	      tree cond = TREE_VEC_ELT (OMP_FOR_COND (stmt), i);
@@ -26124,6 +26387,119 @@  c_parser_omp_taskloop (location_t loc, c
   return ret;
 }
 
+/* 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;
+
+  if (c_parser_next_token_is (parser, CPP_COMMA))
+    c_parser_consume_token (parser);
+
+  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);
+
+  matching_parens parens;
+  if (!parens.require_open (parser))
+    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)
+	{
+	  parens.skip_until_found_close (parser);
+	  return error_mark_node;
+	}
+
+      expr = c_fully_fold (expr, false, NULL);
+
+      HOST_WIDE_INT n;
+      if (!INTEGRAL_TYPE_P (TREE_TYPE (expr))
+	  || !tree_fits_shwi_p (expr)
+	  || (n = tree_to_shwi (expr)) <= 0
+	  || (int) n != n)
+	{
+	  c_parser_error (parser, "%<sizes%> argument needs positive"
+				  " integral constant");
+	  expr = integer_one_node;
+	}
+
+      sizes = tree_cons (NULL_TREE, expr, sizes);
+    }
+  while (c_parser_next_token_is_not (parser, CPP_CLOSE_PAREN));
+  parens.require_close (parser);
+
+  gcc_assert (sizes);
+  tree c = build_omp_clause (loc, OMP_CLAUSE_SIZES);
+  OMP_CLAUSE_SIZES_LIST (c) = nreverse (sizes);
+
+  return c;
+}
+
+/* OpenMP 5.1:
+   #pragma omp tile sizes ( size-expr-list ) new-line
+     for-loop
+
+   LOC is the location of the #pragma token.  */
+
+static tree
+c_parser_omp_tile (location_t loc, c_parser *parser, bool *if_p)
+{
+  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;
+
+  tree block = c_begin_compound_stmt (true);
+  tree ret = c_parser_omp_for_loop (loc, parser, OMP_TILE, clauses,
+				    NULL, if_p);
+  block = c_end_compound_stmt (loc, block, true);
+  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))
+
+/* OpenMP 5.1
+   #pragma omp unroll unroll-clause[optseq] new-line
+     for-loop
+
+   LOC is the location of the #pragma token.  */
+
+static tree
+c_parser_omp_unroll (location_t loc, c_parser *parser, bool *if_p)
+{
+  tree clauses = c_parser_omp_all_clauses (parser, OMP_UNROLL_CLAUSE_MASK,
+					   "#pragma omp unroll", true);
+
+  tree block = c_begin_compound_stmt (true);
+  tree ret = c_parser_omp_for_loop (loc, parser, OMP_UNROLL, 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  */
 
@@ -26605,6 +26981,12 @@  c_parser_omp_construct (c_parser *parser
     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 ();
     }
--- gcc/c/c-typeck.cc.jj	2024-06-04 13:19:03.423608764 +0200
+++ gcc/c/c-typeck.cc	2024-06-04 13:30:00.791871278 +0200
@@ -14984,6 +14984,8 @@  c_finish_omp_clauses (tree clauses, enum
   bool allocate_seen = false;
   bool implicit_moved = false;
   bool target_in_reduction_seen = false;
+  tree *full_seen = NULL;
+  bool partial_seen = false;
   bool openacc = (ort & C_ORT_ACC) != 0;
 
   bitmap_obstack_initialize (NULL);
@@ -15298,7 +15300,7 @@  c_finish_omp_clauses (tree clauses, enum
 	    {
 	      error_at (OMP_CLAUSE_LOCATION (*nowait_clause),
 			"%<nowait%> clause must not be used together "
-			"with %<copyprivate%>");
+			"with %<copyprivate%> clause");
 	      *nowait_clause = OMP_CLAUSE_CHAIN (*nowait_clause);
 	      nowait_clause = NULL;
 	    }
@@ -16210,7 +16212,7 @@  c_finish_omp_clauses (tree clauses, enum
 	    {
 	      error_at (OMP_CLAUSE_LOCATION (c),
 			"%<nowait%> clause must not be used together "
-			"with %<copyprivate%>");
+			"with %<copyprivate%> clause");
 	      remove = true;
 	      break;
 	    }
@@ -16223,7 +16225,7 @@  c_finish_omp_clauses (tree clauses, enum
 	    {
 	      error_at (OMP_CLAUSE_LOCATION (c),
 			"%<order%> clause must not be used together "
-			"with %<ordered%>");
+			"with %<ordered%> clause");
 	      remove = true;
 	      break;
 	    }
@@ -16326,7 +16328,7 @@  c_finish_omp_clauses (tree clauses, enum
 	    {
 	      error_at (OMP_CLAUSE_LOCATION (*order_clause),
 			"%<order%> clause must not be used together "
-			"with %<ordered%>");
+			"with %<ordered%> clause");
 	      *order_clause = OMP_CLAUSE_CHAIN (*order_clause);
 	      order_clause = NULL;
 	    }
@@ -16342,6 +16344,20 @@  c_finish_omp_clauses (tree clauses, enum
 	  pc = &OMP_CLAUSE_CHAIN (c);
 	  continue;
 
+	case OMP_CLAUSE_FULL:
+	  full_seen = pc;
+	  pc = &OMP_CLAUSE_CHAIN (c);
+	  continue;
+
+	case OMP_CLAUSE_PARTIAL:
+	  partial_seen = true;
+	  pc = &OMP_CLAUSE_CHAIN (c);
+	  continue;
+
+	case OMP_CLAUSE_SIZES:
+	  pc = &OMP_CLAUSE_CHAIN (c);
+	  continue;
+
 	case OMP_CLAUSE_INBRANCH:
 	case OMP_CLAUSE_NOTINBRANCH:
 	  if (branch_seen)
@@ -16598,6 +16614,14 @@  c_finish_omp_clauses (tree clauses, enum
       *grainsize_seen = OMP_CLAUSE_CHAIN (*grainsize_seen);
     }
 
+  if (full_seen && partial_seen)
+    {
+      error_at (OMP_CLAUSE_LOCATION (*full_seen),
+		"%<full%> clause must not be used together with "
+		"%<partial%> clause");
+      *full_seen = OMP_CLAUSE_CHAIN (*full_seen);
+    }
+
   if (detach_seen)
     {
       if (mergeable_seen)
--- gcc/cp/cp-tree.h.jj	2024-06-04 13:19:03.700605078 +0200
+++ gcc/cp/cp-tree.h	2024-06-04 13:30:00.611873668 +0200
@@ -7612,7 +7612,7 @@  extern bool type_dependent_expression_p_
 extern bool value_dependent_expression_p	(tree);
 extern bool instantiation_dependent_uneval_expression_p (tree);
 extern bool any_value_dependent_elements_p      (const_tree);
-extern bool dependent_omp_for_p			(tree, tree, tree, tree);
+extern bool dependent_omp_for_p			(tree, tree, tree, tree, tree);
 extern tree resolve_typename_type		(tree, bool);
 extern tree template_for_substitution		(tree);
 extern bool reregister_specialization		(tree, tree, tree);
--- gcc/cp/parser.cc.jj	2024-06-04 13:19:03.745604479 +0200
+++ gcc/cp/parser.cc	2024-06-04 19:39:00.109289939 +0200
@@ -3039,6 +3039,8 @@  static bool cp_parser_skip_up_to_closing
 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_next_tokens_can_be_canon_loop (cp_parser *,
+						     enum tree_code, bool);
 static tree cp_parser_omp_loop_nest (cp_parser *, bool *);
 
 // -------------------------------------------------------------------------- //
@@ -13345,7 +13347,13 @@  check_omp_intervening_code (cp_parser *p
 		    "%<reduction%> %<inscan%> clause");
 	  omp_for_parse_state->perfect_nesting_fail = true;
 	}
-      /* TODO: Also reject loops with TILE directive.  */
+      else if (omp_for_parse_state->code == OMP_TILE)
+	{
+	  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;
     }
@@ -13396,8 +13404,9 @@  cp_parser_statement_seq_opt (cp_parser*
       else if (in_omp_loop_block)
 	{
 	  bool want_nested_loop = omp_for_parse_state->want_nested_loop;
+	  tree_code code = omp_for_parse_state->code;
 	  if (want_nested_loop
-	      && token->type == CPP_KEYWORD && token->keyword == RID_FOR)
+	      && cp_parser_next_tokens_can_be_canon_loop (parser, code, false))
 	    {
 	      /* Found the nested loop.  */
 	      omp_for_parse_state->depth++;
@@ -38202,6 +38211,8 @@  cp_parser_omp_clause_name (cp_parser *pa
 	    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))
@@ -38278,6 +38289,8 @@  cp_parser_omp_clause_name (cp_parser *pa
 	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)
@@ -40658,6 +40671,48 @@  cp_parser_omp_clause_thread_limit (cp_pa
   return c;
 }
 
+/* OpenMP 5.1
+   full */
+
+static tree
+cp_parser_omp_clause_full (tree list, location_t loc)
+{
+  check_no_duplicate_clause (list, OMP_CLAUSE_FULL, "full", loc);
+
+  tree c = build_omp_clause (loc, OMP_CLAUSE_FULL);
+  OMP_CLAUSE_CHAIN (c) = list;
+  return c;
+}
+
+/* OpenMP 5.1
+   partial ( constant-expression ) */
+
+static tree
+cp_parser_omp_clause_partial (cp_parser *parser, tree list, location_t loc)
+{
+  tree num = NULL_TREE;
+  check_no_duplicate_clause (list, OMP_CLAUSE_PARTIAL, "partial", loc);
+
+  if (cp_lexer_next_token_is (parser->lexer, CPP_OPEN_PAREN))
+    {
+      matching_parens parens;
+      parens.consume_open (parser);
+      num = cp_parser_constant_expression (parser);
+      if (num == error_mark_node
+	  || !parens.require_close (parser))
+	cp_parser_skip_to_closing_parenthesis (parser, /*recovering=*/true,
+					       /*or_comma=*/false,
+					       /*consume_paren=*/true);
+      if (num == error_mark_node)
+	return list;
+    }
+
+  tree c = build_omp_clause (loc, OMP_CLAUSE_PARTIAL);
+  OMP_CLAUSE_PARTIAL_EXPR (c) = num;
+  OMP_CLAUSE_CHAIN (c) = list;
+  return c;
+}
+
 /* OpenMP 4.0:
    aligned ( variable-list )
    aligned ( variable-list : constant-expression )  */
@@ -42409,7 +42464,7 @@  cp_parser_omp_all_clauses (cp_parser *pa
 	  else if (nested == 2)
 	    error_at (cp_lexer_peek_token (parser->lexer)->location,
 		      "clauses in %<simd%> trait should be separated "
-                      "by %<,%>");
+		      "by %<,%>");
 	}
 
       token = cp_lexer_peek_token (parser->lexer);
@@ -42756,6 +42811,15 @@  cp_parser_omp_all_clauses (cp_parser *pa
 					    clauses);
 	  c_name = "enter";
 	  break;
+	case PRAGMA_OMP_CLAUSE_PARTIAL:
+	  clauses = cp_parser_omp_clause_partial (parser, clauses,
+						  token->location);
+	  c_name = "partial";
+	  break;
+	case PRAGMA_OMP_CLAUSE_FULL:
+	  clauses = cp_parser_omp_clause_full (clauses, token->location);
+	  c_name = "full";
+	  break;
 	default:
 	  cp_parser_error (parser, "expected an OpenMP clause");
 	  goto saw_error;
@@ -44979,6 +45043,50 @@  cp_parser_omp_scan_loop_body (cp_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_next_tokens_can_be_canon_loop (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))
+	return true;
+      if (cp_lexer_next_token_is (parser->lexer, CPP_PRAGMA)
+	  && ((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;
+      /* Skip standard attributes on next for in case they are
+	 [[omp::directive (unroll partial (4))]] or
+	 [[omp::directive (tile sizes (1, 2, 3))]] etc.  */
+      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)
+	{
+	  if (cp_lexer_next_token_is (parser->lexer, CPP_PRAGMA))
+	    error_at (cp_lexer_peek_token (parser->lexer)->location,
+		      "loop nest expected");
+	  else
+	    cp_parser_error (parser, "loop nest expected");
+	}
+    }
+  return false;
+}
+
+static tree cp_parser_omp_unroll (cp_parser *, cp_token *, bool *);
+static tree cp_parser_omp_tile (cp_parser *, cp_token *, bool *);
+
 /* This function parses a single level of a loop nest, invoking itself
    recursively if necessary.
 
@@ -45031,8 +45139,142 @@  cp_parser_omp_loop_nest (cp_parser *pars
   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_lexer_next_token_is (parser->lexer, CPP_PRAGMA))
+    {
+      tree transform = NULL_TREE, sizes, body = NULL_TREE;
+      int count = 0;
+      cp_token *pragma_tok;
+      tree stmt;
+      loc = cp_lexer_peek_token (parser->lexer)->location;
+      switch (cp_parser_pragma_kind (cp_lexer_peek_token (parser->lexer)))
+	{
+	case PRAGMA_OMP_UNROLL:
+	  pragma_tok = cp_lexer_consume_token (parser->lexer);
+	  parser->lexer->in_pragma = true;
+	  body = push_stmt_list ();
+	  stmt = push_omp_privatization_clauses (false);
+	  transform = cp_parser_omp_unroll (parser, pragma_tok, if_p);
+	  pop_omp_privatization_clauses (stmt);
+	  body = pop_stmt_list (body);
+	  if (transform == NULL_TREE || transform == error_mark_node)
+	    {
+	      transform = error_mark_node;
+	      break;
+	    }
+	  gcc_assert (TREE_CODE (transform) == OMP_UNROLL);
+	  if (omp_find_clause (OMP_FOR_CLAUSES (transform),
+			       OMP_CLAUSE_PARTIAL))
+	    {
+	      if (omp_for_parse_state->count - depth > 1)
+		{
+		  error_at (loc, "%<unroll%> construct with %<partial%> "
+				 "clause generates just one loop with "
+				 "canonical form but %d loops are needed",
+			    omp_for_parse_state->count - depth);
+		  transform = error_mark_node;
+		}
+	      else
+		count = 1;
+	    }
+	  else
+	    {
+	      error_at (loc, "generated loop of %<unroll%> construct "
+			     "without %<partial%> clause does not have "
+			     "canonical form");
+	      transform = error_mark_node;
+	    }
+	  break;
+	case PRAGMA_OMP_TILE:
+	  pragma_tok = cp_lexer_consume_token (parser->lexer);
+	  parser->lexer->in_pragma = true;
+	  body = push_stmt_list ();
+	  stmt = push_omp_privatization_clauses (false);
+	  transform = cp_parser_omp_tile (parser, pragma_tok, if_p);
+	  pop_omp_privatization_clauses (stmt);
+	  body = pop_stmt_list (body);
+	  if (transform == NULL_TREE || transform == error_mark_node)
+	    {
+	      transform = error_mark_node;
+	      break;
+	    }
+	  gcc_assert (TREE_CODE (transform) == OMP_TILE);
+	  sizes = omp_find_clause (OMP_FOR_CLAUSES (transform),
+				   OMP_CLAUSE_SIZES);
+	  gcc_assert (sizes);
+	  count = list_length (OMP_CLAUSE_SIZES_LIST (sizes));
+	  if (depth + count < omp_for_parse_state->count)
+	    {
+	      error_at (loc, "%<tile%> construct generates %d loops "
+			     "with canonical form but %d loops are needed",
+			count, omp_for_parse_state->count - depth);
+	      transform = error_mark_node;
+	    }
+	  break;
+	default:
+	  cp_parser_pragma (parser, pragma_stmt, NULL);
+	  break;
+	}
+      if (transform == NULL_TREE)
+	error_at (loc, "expected %<for%> loop or OpenMP loop "
+		  "transformation construct");
+      if (transform == NULL_TREE || transform == error_mark_node)
+	{
+	  omp_for_parse_state->fail = true;
+	  return NULL_TREE;
+	}
+      for (count = omp_for_parse_state->count; depth < count; ++depth)
+	{
+	  TREE_VEC_ELT (omp_for_parse_state->declv, depth) = global_namespace;
+	  TREE_VEC_ELT (omp_for_parse_state->initv, depth) = NULL_TREE;
+	  TREE_VEC_ELT (omp_for_parse_state->condv, depth) = NULL_TREE;
+	  TREE_VEC_ELT (omp_for_parse_state->incrv, depth) = NULL_TREE;
+	  if (omp_for_parse_state->orig_declv)
+	    TREE_VEC_ELT (omp_for_parse_state->incrv, depth) = NULL_TREE;
+	  vec_safe_push (omp_for_parse_state->init_blockv, NULL_TREE);
+	  vec_safe_push (omp_for_parse_state->body_blockv, NULL_TREE);
+	  vec_safe_push (omp_for_parse_state->init_placeholderv, NULL_TREE);
+	  vec_safe_push (omp_for_parse_state->body_placeholderv, NULL_TREE);
+	}
+      omp_for_parse_state->want_nested_loop = false;
+      return body;
+    }
+
+  /* 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_next_tokens_can_be_canon_loop (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
@@ -45287,10 +45529,15 @@  cp_parser_omp_loop_nest (cp_parser *pars
 
   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_next_tokens_can_be_canon_loop (parser,
+						  omp_for_parse_state->code,
+						  false))
     {
       omp_for_parse_state->depth++;
-      add_stmt (cp_parser_omp_loop_nest (parser, if_p));
+      tree nest = cp_parser_omp_loop_nest (parser, if_p);
+      if (nest)
+	add_stmt (nest);
       omp_for_parse_state->depth--;
     }
   else if (moreloops
@@ -45572,7 +45819,7 @@  cp_parser_omp_for_loop (cp_parser *parse
   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;
@@ -45584,9 +45831,11 @@  cp_parser_omp_for_loop (cp_parser *parse
       collapse = tree_to_shwi (OMP_CLAUSE_COLLAPSE_EXPR (cl));
     else if (OMP_CLAUSE_CODE (cl) == OMP_CLAUSE_TILE)
       {
-	tiling = true;
+	oacc_tiling = true;
 	collapse = list_length (OMP_CLAUSE_TILE_LIST (cl));
       }
+    else if (OMP_CLAUSE_CODE (cl) == OMP_CLAUSE_SIZES)
+      collapse = list_length (OMP_CLAUSE_SIZES_LIST (cl));
     else if (OMP_CLAUSE_CODE (cl) == OMP_CLAUSE_ORDERED
 	     && OMP_CLAUSE_ORDERED_EXPR (cl))
       {
@@ -45607,14 +45856,11 @@  cp_parser_omp_for_loop (cp_parser *parse
       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_error (parser, "for statement expected");
-      return NULL;
-    }
+  if (!cp_parser_next_tokens_can_be_canon_loop (parser, code, true))
+    return NULL;
 
   /* Initialize parse state for recursive descent.  */
   data.declv = make_tree_vec (count);
@@ -45639,7 +45885,7 @@  cp_parser_omp_for_loop (cp_parser *parse
   data.ordered_cl = ordered_cl;
   parser->omp_for_parse_state = &data;
 
-  cp_parser_omp_loop_nest (parser, if_p);
+  tree body = cp_parser_omp_loop_nest (parser, if_p);
 
   /* Bomb out early if there was an error (not enough loops, etc).  */
   if (data.fail || data.declv == NULL_TREE)
@@ -45675,66 +45921,93 @@  cp_parser_omp_for_loop (cp_parser *parse
   /* First insert markers for structured blocks for intervening code in
      the loop bodies.  */
   for (unsigned int i = 0; i < count - 1; i++)
-    {
-      bool good = find_structured_blocks (&(data.body_blockv[i]),
-					  data.init_placeholderv[i+1]);
-      gcc_assert (good);
-    }
+    if (data.body_blockv[i])
+      for (unsigned int j = i + 1; j < count; j++)
+	if (data.init_placeholderv[j])
+	  {
+	    bool good = find_structured_blocks (&data.body_blockv[i],
+						data.init_placeholderv[j]);
+	    gcc_assert (good);
+	    break;
+	  }
 
   /* Do the substitution from the inside out.  */
   for (unsigned int i = count - 1; i > 0; i--)
-    {
-      substitute_in_tree (&(data.body_blockv[i-1]),
-			  data.init_placeholderv[i],
-			  data.body_blockv[i], false);
-      substitute_in_tree (&(data.init_blockv[i-1]),
-			  data.body_placeholderv[i-1],
-			  data.init_blockv[i], true);
-    }
+    if (data.init_placeholderv[i])
+      for (unsigned int j = i; j > 0; j--)
+	if (data.body_blockv[j - 1])
+	  {
+	    substitute_in_tree (&data.body_blockv[j - 1],
+				data.init_placeholderv[i],
+				data.body_blockv[i], false);
+	    substitute_in_tree (&data.init_blockv[j - 1],
+				data.body_placeholderv[j - 1],
+				data.init_blockv[i], true);
+	    break;
+	  }
+
+  for (unsigned int i = 0; i < count; ++i)
+    if (data.body_blockv[i])
+      {
+	body = data.body_blockv[i];
+	break;
+      }
 
   /* Generate the OMP_FOR.  Note finish_omp_for adds the OMP_FOR
      (and possibly other stuff) to the current statement list but
      returns a pointer to the OMP_FOR itself, or null in case of error.  */
   result = push_stmt_list ();
   ret = finish_omp_for (loc_first, code, data.declv, data.orig_declv,
-			data.initv, data.condv, data.incrv,
-			data.body_blockv[0],
+			data.initv, data.condv, data.incrv, body,
 			data.pre_body, &data.orig_inits, data.clauses);
   result = pop_stmt_list (result);
 
   /* Check for errors involving lb/ub/incr expressions referencing
      variables declared in intervening code.  */
   if (data.saw_intervening_code
+      && ret
       && !c_omp_check_loop_binding_exprs (ret, &data.orig_inits))
     ret = NULL_TREE;
 
   if (ret)
-    {
-      /* Splice the omp_for into the nest of init blocks.  */
-      substitute_in_tree (&(data.init_blockv[0]),
-			  data.body_placeholderv[count - 1],
-			  result, true);
-
-      /* Some later processing for combined directives assumes
-	 that the BIND_EXPR containing range for variables appears
-	 at top level in the OMP_FOR body.  Fix that up if it's
-	 not the case, e.g. because there is intervening code.  */
-      if (code != OACC_LOOP)
-	finish_omp_for_block (data.init_blockv[0], ret);
-
-      /* Clean up the block subblock/superblock links.  Per comment in
-	 begin_compound_stmt, "we don't build BLOCK nodes when processing
-	 templates", so skip this step in that case.  */
-      if (!processing_template_decl)
+    for (unsigned int i = 0; i < count; ++i)
+      if (data.init_blockv[i])
 	{
-	  tree superblock = NULL_TREE;
-	  cp_walk_tree (&data.init_blockv[0], fixup_blocks_walker,
-			(void *)&superblock, NULL);
-	}
+	  int j;
+	  for (j = count - 1; j >= 0; --j)
+	    if (data.body_placeholderv[j])
+	      break;
+	  gcc_assert (j >= 0);
 
-      /* Finally record the result.  */
-      add_stmt (data.init_blockv[0]);
-    }
+	  /* Splice the omp_for into the nest of init blocks.  */
+	  substitute_in_tree (&data.init_blockv[i],
+			      data.body_placeholderv[j],
+			      result, true);
+
+	  /* Some later processing for combined directives assumes
+	     that the BIND_EXPR containing range for variables appears
+	     at top level in the OMP_FOR body.  Fix that up if it's
+	     not the case, e.g. because there is intervening code.  */
+	  if (code != OACC_LOOP)
+	    finish_omp_for_block (data.init_blockv[i], ret);
+
+	  /* Clean up the block subblock/superblock links.  Per comment in
+	     begin_compound_stmt, "we don't build BLOCK nodes when processing
+	     templates", so skip this step in that case.  */
+	  if (!processing_template_decl)
+	    {
+	      tree superblock = NULL_TREE;
+	      cp_walk_tree (&data.init_blockv[i], fixup_blocks_walker,
+			    (void *)&superblock, NULL);
+	    }
+
+	  /* Finally record the result.  */
+	  add_stmt (data.init_blockv[0]);
+	  result = NULL_TREE;
+	  break;
+	}
+  if (ret && result)
+    add_stmt (result);
 
   parser->omp_for_parse_state = save_data;
   return ret;
@@ -47464,6 +47737,98 @@  cp_parser_omp_target (cp_parser *parser,
   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;
+
+  if (cp_lexer_next_token_is (parser->lexer, CPP_COMMA))
+    cp_lexer_consume_token (parser->lexer);
+
+  cp_token *tok = cp_lexer_peek_token (parser->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 (parser->lexer);
+
+  matching_parens parens;
+  if (!parens.require_open (parser))
+    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 (parser->lexer, CPP_CLOSE_PAREN));
+  parens.require_close (parser);
+
+  gcc_assert (sizes);
+  tree c = build_omp_clause (loc, OMP_CLAUSE_SIZES);
+  OMP_CLAUSE_SIZES_LIST (c) = nreverse (sizes);
+  return c;
+}
+
+/* OpenMP 5.1:
+   #pragma omp tile sizes ( size-expr-list )  */
+
+static tree
+cp_parser_omp_tile (cp_parser *parser, cp_token *tok, bool *if_p)
+{
+  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;
+
+  tree block = begin_omp_structured_block ();
+  clauses = finish_omp_clauses (clauses, C_ORT_OMP);
+  tree ret = cp_parser_omp_for_loop (parser, OMP_TILE, 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))
+
+/* OpenMP 5.1:
+   #pragma omp unroll unroll-clause[optseq]  */
+
+static tree
+cp_parser_omp_unroll (cp_parser *parser, cp_token *tok, bool *if_p)
+{
+  tree clauses = cp_parser_omp_all_clauses (parser, OMP_UNROLL_CLAUSE_MASK,
+					    "#pragma omp unroll", tok, true);
+
+  tree block = begin_omp_structured_block ();
+  tree ret = cp_parser_omp_for_loop (parser, OMP_UNROLL, 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
 
@@ -50656,6 +51021,12 @@  cp_parser_omp_construct (cp_parser *pars
     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 ();
     }
@@ -51278,6 +51649,8 @@  cp_parser_pragma (cp_parser *parser, enu
     case PRAGMA_OMP_TASKGROUP:
     case PRAGMA_OMP_TASKLOOP:
     case PRAGMA_OMP_TEAMS:
+    case PRAGMA_OMP_TILE:
+    case PRAGMA_OMP_UNROLL:
       if (context != pragma_stmt && context != pragma_compound)
 	goto bad_stmt;
       stmt = push_omp_privatization_clauses (false);
--- gcc/cp/semantics.cc.jj	2024-06-04 13:18:25.443114150 +0200
+++ gcc/cp/semantics.cc	2024-06-04 13:30:00.629873429 +0200
@@ -7099,6 +7099,7 @@  finish_omp_clauses (tree clauses, enum c
   bool implicit_moved = false;
   bool target_in_reduction_seen = false;
   bool num_tasks_seen = false;
+  bool partial_seen = false;
 
   bitmap_obstack_initialize (NULL);
   bitmap_initialize (&generic_head, &bitmap_default_obstack);
@@ -8031,8 +8032,6 @@  finish_omp_clauses (tree clauses, enum c
 				"be positive constant integer expression");
 		      remove = true;
 		    }
-		  else
-		    t = fold_build_cleanup_point_expr (TREE_TYPE (t), t);
 		}
 	      OMP_CLAUSE_ALIGNED_ALIGNMENT (c) = t;
 	    }
@@ -8886,7 +8885,6 @@  finish_omp_clauses (tree clauses, enum c
 	      if (!processing_template_decl)
 		{
 		  t = maybe_constant_value (t);
-		  t = fold_build_cleanup_point_expr (TREE_TYPE (t), t);
 		  if (TREE_CODE (t) != INTEGER_CST)
 		    {
 		      error_at (OMP_CLAUSE_LOCATION (c),
@@ -9051,7 +9049,6 @@  finish_omp_clauses (tree clauses, enum c
 				    "integral constant");
 			  remove = true;
 			}
-		      t = fold_build_cleanup_point_expr (TREE_TYPE (t), t);
 		    }
 		}
 
@@ -9060,6 +9057,47 @@  finish_omp_clauses (tree clauses, enum c
 	    }
 	  break;
 
+	case OMP_CLAUSE_SIZES:
+	  for (tree list = OMP_CLAUSE_SIZES_LIST (c);
+	       !remove && list; list = TREE_CHAIN (list))
+	    {
+	      t = TREE_VALUE (list);
+
+	      if (t == error_mark_node)
+		t = integer_one_node;
+	      else if (!type_dependent_expression_p (t)
+		       && !INTEGRAL_TYPE_P (TREE_TYPE (t)))
+		{
+		  error_at (OMP_CLAUSE_LOCATION (c),
+			    "%<sizes%> argument needs positive integral "
+			    "constant");
+		  t = integer_one_node;
+		}
+	      else
+		{
+		  t = mark_rvalue_use (t);
+		  if (!processing_template_decl)
+		    {
+		      t = maybe_constant_value (t);
+		      HOST_WIDE_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),
+				    "%<sizes%> argument needs positive "
+				    "integral constant");
+			  t = integer_one_node;
+			}
+		    }
+		}
+
+	      /* Update list item.  */
+	      TREE_VALUE (list) = t;
+	    }
+	  break;
+
 	case OMP_CLAUSE_ORDERED:
 	  ordered_seen = true;
 	  break;
@@ -9114,6 +9152,49 @@  finish_omp_clauses (tree clauses, enum c
 	    }
 	  break;
 
+	case OMP_CLAUSE_FULL:
+	  break;
+
+	case OMP_CLAUSE_PARTIAL:
+	  partial_seen = true;
+	  t = OMP_CLAUSE_PARTIAL_EXPR (c);
+	  if (!t)
+	    break;
+
+	  if (t == error_mark_node)
+	    t = NULL_TREE;
+	  else if (!type_dependent_expression_p (t)
+		   && !INTEGRAL_TYPE_P (TREE_TYPE (t)))
+	    {
+	      error_at (OMP_CLAUSE_LOCATION (c),
+			"%<partial%> argument needs positive constant "
+			"integer expression");
+	      t = NULL_TREE;
+	    }
+	  else
+	    {
+	      t = mark_rvalue_use (t);
+	      if (!processing_template_decl)
+		{
+		  t = maybe_constant_value (t);
+
+		  HOST_WIDE_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");
+		      t = NULL_TREE;
+		    }
+		}
+	    }
+
+	  OMP_CLAUSE_PARTIAL_EXPR (c) = t;
+	  break;
+
 	default:
 	  gcc_unreachable ();
 	}
@@ -9274,7 +9355,7 @@  finish_omp_clauses (tree clauses, enum c
 	    {
 	      error_at (OMP_CLAUSE_LOCATION (c),
 			"%<order%> clause must not be used together "
-			"with %<ordered%>");
+			"with %<ordered%> clause");
 	      *pc = OMP_CLAUSE_CHAIN (c);
 	      continue;
 	    }
@@ -9307,12 +9388,23 @@  finish_omp_clauses (tree clauses, enum c
 	    }
 	  pc = &OMP_CLAUSE_CHAIN (c);
 	  continue;
+	case OMP_CLAUSE_FULL:
+	  if (partial_seen)
+	    {
+	      error_at (OMP_CLAUSE_LOCATION (c),
+			"%<full%> clause must not be used together "
+			"with %<partial%> clause");
+	      *pc = OMP_CLAUSE_CHAIN (c);
+	      continue;
+	    }
+	  pc = &OMP_CLAUSE_CHAIN (c);
+	  continue;
 	case OMP_CLAUSE_NOWAIT:
 	  if (copyprivate_seen)
 	    {
 	      error_at (OMP_CLAUSE_LOCATION (c),
 			"%<nowait%> clause must not be used together "
-			"with %<copyprivate%>");
+			"with %<copyprivate%> clause");
 	      *pc = OMP_CLAUSE_CHAIN (c);
 	      continue;
 	    }
@@ -10753,16 +10845,14 @@  finish_omp_for (location_t locus, enum t
   gcc_assert (TREE_VEC_LENGTH (declv) == TREE_VEC_LENGTH (incrv));
   if (TREE_VEC_LENGTH (declv) > 1)
     {
-      tree c;
-
-      c = omp_find_clause (clauses, OMP_CLAUSE_TILE);
-      if (c)
-	collapse = list_length (OMP_CLAUSE_TILE_LIST (c));
+      if (tree ti = omp_find_clause (clauses, OMP_CLAUSE_TILE))
+	collapse = list_length (OMP_CLAUSE_TILE_LIST (ti));
       else
 	{
-	  c = omp_find_clause (clauses, OMP_CLAUSE_COLLAPSE);
-	  if (c)
-	    collapse = tree_to_shwi (OMP_CLAUSE_COLLAPSE_EXPR (c));
+	  if (tree co = omp_find_clause (clauses, OMP_CLAUSE_COLLAPSE))
+	    collapse = tree_to_shwi (OMP_CLAUSE_COLLAPSE_EXPR (co));
+	  else if (tree si = omp_find_clause (clauses, OMP_CLAUSE_SIZES))
+	    collapse = list_length (OMP_CLAUSE_SIZES_LIST (si));
 	  if (collapse != TREE_VEC_LENGTH (declv))
 	    ordered = TREE_VEC_LENGTH (declv);
 	}
@@ -10775,6 +10865,13 @@  finish_omp_for (location_t locus, enum t
       incr = TREE_VEC_ELT (incrv, i);
       elocus = locus;
 
+      if (decl == global_namespace)
+	{
+	  gcc_assert (init == NULL_TREE && cond == NULL_TREE && incr == NULL_TREE);
+	  TREE_VEC_ELT (declv, i) = NULL_TREE;
+	  init_locv.safe_push (UNKNOWN_LOCATION);
+	  continue;
+	}
       /* We are going to throw out the init's original MODIFY_EXPR or
 	 MODOP_EXPR below.  Save its location so we can use it when
 	 reconstructing the expression farther down.  Alternatively, if the
@@ -10859,7 +10956,7 @@  finish_omp_for (location_t locus, enum t
 	return NULL;
     }
 
-  if (dependent_omp_for_p (declv, initv, condv, incrv))
+  if (dependent_omp_for_p (declv, initv, condv, incrv, body))
     {
       tree stmt;
 
@@ -10867,6 +10964,8 @@  finish_omp_for (location_t locus, enum t
 
       for (i = 0; i < TREE_VEC_LENGTH (declv); i++)
 	{
+	  if (TREE_VEC_ELT (declv, i) == NULL_TREE)
+	    continue;
 	  /* This is really just a place-holder.  We'll be decomposing this
 	     again and going through the cp_build_modify_expr path below when
 	     we instantiate the thing.  */
@@ -10903,6 +11002,12 @@  finish_omp_for (location_t locus, enum t
 	TREE_VEC_ELT (orig_incr, i) = incr;
       elocus = init_locv[i];
 
+      if (decl == NULL_TREE)
+	{
+	  i++;
+	  continue;
+	}
+
       if (!DECL_P (decl))
 	{
 	  error_at (elocus, "expected iteration declaration or initialization");
@@ -10976,6 +11081,8 @@  finish_omp_for (location_t locus, enum t
   for (i = 0; i < TREE_VEC_LENGTH (OMP_FOR_INCR (omp_for)); i++)
     {
       init = TREE_VEC_ELT (OMP_FOR_INIT (omp_for), i);
+      if (init == NULL_TREE)
+	continue;
       decl = TREE_OPERAND (init, 0);
       cond = TREE_VEC_ELT (OMP_FOR_COND (omp_for), i);
       incr = TREE_VEC_ELT (OMP_FOR_INCR (omp_for), i);
@@ -11173,7 +11280,9 @@  finish_omp_for_block (tree bind, tree om
   fofb.b = NULL_TREE;
   fofb.omp_for = omp_for;
   for (int i = 0; i < TREE_VEC_LENGTH (OMP_FOR_INIT (omp_for)); i++)
-    if (TREE_CODE (TREE_VEC_ELT (OMP_FOR_ORIG_DECLS (omp_for), i)) == TREE_LIST
+    if (TREE_VEC_ELT (OMP_FOR_INIT (omp_for), i)
+	&& (TREE_CODE (TREE_VEC_ELT (OMP_FOR_ORIG_DECLS (omp_for), i))
+	    == TREE_LIST)
 	&& TREE_CHAIN (TREE_VEC_ELT (OMP_FOR_ORIG_DECLS (omp_for), i)))
       {
 	tree v = TREE_CHAIN (TREE_VEC_ELT (OMP_FOR_ORIG_DECLS (omp_for), i));
--- gcc/cp/pt.cc.jj	2024-06-05 17:59:58.302098560 +0200
+++ gcc/cp/pt.cc	2024-06-05 18:47:27.735647994 +0200
@@ -17691,6 +17691,16 @@  tsubst_omp_clauses (tree clauses, enum c
 	  OMP_CLAUSE_OPERAND (nc, 0)
 	    = tsubst_stmt (OMP_CLAUSE_OPERAND (oc, 0), args, complain, in_decl);
 	  break;
+	case OMP_CLAUSE_PARTIAL:
+	  OMP_CLAUSE_PARTIAL_EXPR (nc)
+	    = tsubst_expr (OMP_CLAUSE_PARTIAL_EXPR (oc), args, complain,
+			   in_decl);
+	  break;
+	case OMP_CLAUSE_SIZES:
+	  OMP_CLAUSE_SIZES_LIST (nc)
+	    = tsubst_expr (OMP_CLAUSE_SIZES_LIST (oc), args, complain,
+			   in_decl);
+	  break;
 	case OMP_CLAUSE_REDUCTION:
 	case OMP_CLAUSE_IN_REDUCTION:
 	case OMP_CLAUSE_TASK_REDUCTION:
@@ -17771,6 +17781,7 @@  tsubst_omp_clauses (tree clauses, enum c
 	case OMP_CLAUSE_IF_PRESENT:
 	case OMP_CLAUSE_FINALIZE:
 	case OMP_CLAUSE_NOHOST:
+	case OMP_CLAUSE_FULL:
 	  break;
 	default:
 	  gcc_unreachable ();
@@ -19034,6 +19045,8 @@  tsubst_stmt (tree t, tree args, tsubst_f
     case OMP_SIMD:
     case OMP_DISTRIBUTE:
     case OMP_TASKLOOP:
+    case OMP_TILE:
+    case OMP_UNROLL:
     case OACC_LOOP:
       {
 	tree clauses, body, pre_body;
@@ -19069,10 +19082,22 @@  tsubst_stmt (tree t, tree args, tsubst_f
 
 	if (OMP_FOR_INIT (t) != NULL_TREE)
 	  for (i = 0; i < TREE_VEC_LENGTH (OMP_FOR_INIT (t)); i++)
-	    any_range_for
-	      |= tsubst_omp_for_iterator (t, i, declv, orig_declv, initv,
-					  condv, incrv, &clauses, args,
-					  complain, in_decl);
+	    {
+	      if (TREE_VEC_ELT (OMP_FOR_INIT (t), i))
+		any_range_for
+		  |= tsubst_omp_for_iterator (t, i, declv, orig_declv, initv,
+					      condv, incrv, &clauses, args,
+					      complain, in_decl);
+	      else
+		{
+		  TREE_VEC_ELT (declv, i) = global_namespace;
+		  TREE_VEC_ELT (initv, i) = NULL_TREE;
+		  TREE_VEC_ELT (condv, i) = NULL_TREE;
+		  TREE_VEC_ELT (incrv, i) = NULL_TREE;
+		  if (orig_declv)
+		    TREE_VEC_ELT (orig_declv, i) = NULL_TREE;
+		}
+	    }
 	omp_parallel_combined_clauses = NULL;
 
 	if (any_range_for)
@@ -19080,7 +19105,8 @@  tsubst_stmt (tree t, tree args, tsubst_f
 	    gcc_assert (orig_declv);
 	    body = begin_omp_structured_block ();
 	    for (i = 0; i < TREE_VEC_LENGTH (OMP_FOR_INIT (t)); i++)
-	      if (TREE_VEC_ELT (orig_declv, i) != TREE_VEC_ELT (declv, i)
+	      if (TREE_VEC_ELT (declv, i) != global_namespace
+		  && TREE_VEC_ELT (orig_declv, i) != TREE_VEC_ELT (declv, i)
 		  && TREE_CODE (TREE_VEC_ELT (orig_declv, i)) == TREE_LIST
 		  && TREE_CHAIN (TREE_VEC_ELT (orig_declv, i)))
 		cp_finish_omp_range_for (TREE_VEC_ELT (orig_declv, i),
@@ -29108,23 +29134,34 @@  dependent_template_id_p (tree tmpl, tree
 }
 
 /* Returns TRUE if OMP_FOR with DECLV, INITV, CONDV and INCRV vectors
-   are dependent.  */
+   are dependent.  BODY is the body to use for loop transforming
+   constructs.  */
 
 bool
-dependent_omp_for_p (tree declv, tree initv, tree condv, tree incrv)
+dependent_omp_for_p (tree declv, tree initv, tree condv, tree incrv, tree body)
 {
-  int i;
+  int i, k;
 
   if (!processing_template_decl)
     return false;
 
-  for (i = 0; i < TREE_VEC_LENGTH (declv); i++)
+  for (i = 0, k = 0; i < TREE_VEC_LENGTH (declv); i++)
     {
       tree decl = TREE_VEC_ELT (declv, i);
       tree init = TREE_VEC_ELT (initv, i);
       tree cond = TREE_VEC_ELT (condv, i);
       tree incr = TREE_VEC_ELT (incrv, i);
 
+      if (decl == NULL_TREE)
+	{
+	  tree stmt = body;
+	  int j = c_omp_find_generated_loop (stmt, k++, cp_walk_subtrees);
+	  init = TREE_VEC_ELT (OMP_FOR_INIT (stmt), j);
+	  decl = TREE_OPERAND (init, 0);
+	  cond = TREE_VEC_ELT (OMP_FOR_INIT (stmt), j);
+	  incr = TREE_VEC_ELT (OMP_FOR_INIT (stmt), j);
+	}
+
       if (type_dependent_expression_p (decl)
 	  || TREE_CODE (decl) == SCOPE_REF)
 	return true;
--- gcc/cp/cp-gimplify.cc.jj	2024-06-04 13:18:25.304116000 +0200
+++ gcc/cp/cp-gimplify.cc	2024-06-04 13:30:00.636873336 +0200
@@ -759,6 +759,8 @@  cp_gimplify_expr (tree *expr_p, gimple_s
     case OMP_DISTRIBUTE:
     case OMP_LOOP:
     case OMP_TASKLOOP:
+    case OMP_TILE:
+    case OMP_UNROLL:
       ret = cp_gimplify_omp_for (expr_p, pre_p);
       break;
 
@@ -1396,6 +1398,8 @@  cp_fold_r (tree *stmt_p, int *walk_subtr
     case OMP_DISTRIBUTE:
     case OMP_LOOP:
     case OMP_TASKLOOP:
+    case OMP_TILE:
+    case OMP_UNROLL:
     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);
@@ -2169,7 +2173,8 @@  cp_genericize_r (tree *stmt_p, int *walk
 	      && TREE_CODE (inner) == OMP_FOR)
 	    {
 	      for (int i = 0; i < TREE_VEC_LENGTH (OMP_FOR_INIT (inner)); i++)
-		if (OMP_FOR_ORIG_DECLS (inner)
+		if (TREE_VEC_ELT (OMP_FOR_INIT (inner), i)
+		    && OMP_FOR_ORIG_DECLS (inner)
 		    && TREE_CODE (TREE_VEC_ELT (OMP_FOR_ORIG_DECLS (inner),
 				  i)) == TREE_LIST
 		    && TREE_PURPOSE (TREE_VEC_ELT (OMP_FOR_ORIG_DECLS (inner),
@@ -2229,6 +2234,8 @@  cp_genericize_r (tree *stmt_p, int *walk
     case OMP_FOR:
     case OMP_SIMD:
     case OMP_LOOP:
+    case OMP_TILE:
+    case OMP_UNROLL:
     case OACC_LOOP:
     case STATEMENT_LIST:
       /* These cases are handled by shared code.  */
--- gcc/fortran/gfortran.h.jj	2024-06-04 13:18:25.559112607 +0200
+++ gcc/fortran/gfortran.h	2024-06-05 12:37:28.704240094 +0200
@@ -321,7 +321,9 @@  enum gfc_statement
   ST_OMP_ALLOCATE, ST_OMP_ALLOCATE_EXEC,
   ST_OMP_ALLOCATORS, ST_OMP_END_ALLOCATORS,
   /* Note: gfc_match_omp_nothing returns ST_NONE. */
-  ST_OMP_NOTHING, ST_NONE
+  ST_OMP_NOTHING, ST_NONE,
+  ST_OMP_UNROLL, ST_OMP_END_UNROLL,
+  ST_OMP_TILE, ST_OMP_END_TILE
 };
 
 /* Types of interfaces that we can have.  Assignment interfaces are
@@ -1577,11 +1579,13 @@  typedef struct gfc_omp_clauses
   struct gfc_expr *dist_chunk_size;
   struct gfc_expr *message;
   struct gfc_omp_assumptions *assume;
+  struct gfc_expr_list *sizes_list;
   const char *critical_name;
   enum gfc_omp_default_sharing default_sharing;
   enum gfc_omp_atomic_op atomic_op;
   enum gfc_omp_defaultmap defaultmap[OMP_DEFAULTMAP_CAT_NUM];
   int collapse, orderedc;
+  int partial;
   unsigned nowait:1, ordered:1, untied:1, mergeable:1, ancestor:1;
   unsigned inbranch:1, notinbranch:1, nogroup:1;
   unsigned sched_simd:1, sched_monotonic:1, sched_nonmonotonic:1;
@@ -1591,6 +1595,7 @@  typedef struct gfc_omp_clauses
   unsigned non_rectangular:1, order_concurrent:1;
   unsigned contains_teams_construct:1, target_first_st_is_teams:1;
   unsigned contained_in_target_construct:1, indirect:1;
+  unsigned full:1, erroneous:1;
   ENUM_BITFIELD (gfc_omp_sched_kind) sched_kind:3;
   ENUM_BITFIELD (gfc_omp_device_type) device_type:2;
   ENUM_BITFIELD (gfc_omp_memorder) memorder:3;
@@ -3033,6 +3038,7 @@  enum gfc_exec_op
   EXEC_OMP_TARGET_TEAMS_LOOP, EXEC_OMP_MASKED, EXEC_OMP_PARALLEL_MASKED,
   EXEC_OMP_PARALLEL_MASKED_TASKLOOP, EXEC_OMP_PARALLEL_MASKED_TASKLOOP_SIMD,
   EXEC_OMP_MASKED_TASKLOOP, EXEC_OMP_MASKED_TASKLOOP_SIMD, EXEC_OMP_SCOPE,
+  EXEC_OMP_UNROLL, EXEC_OMP_TILE,
   EXEC_OMP_ERROR, EXEC_OMP_ALLOCATE, EXEC_OMP_ALLOCATORS
 };
 
@@ -3958,6 +3964,9 @@  void gfc_generate_module_code (gfc_names
 /* trans-intrinsic.cc */
 bool gfc_inline_intrinsic_function_p (gfc_expr *);
 
+/* trans-openmp.cc */
+int gfc_expr_list_len (gfc_expr_list *);
+
 /* bbt.cc */
 typedef int (*compare_fn) (void *, void *);
 void gfc_insert_bbt (void *, void *, compare_fn);
--- gcc/fortran/match.h.jj	2024-06-04 13:18:25.602112034 +0200
+++ gcc/fortran/match.h	2024-06-04 13:30:00.995868568 +0200
@@ -228,6 +228,8 @@  match gfc_match_omp_teams_distribute_par
 match gfc_match_omp_teams_distribute_simd (void);
 match gfc_match_omp_teams_loop (void);
 match gfc_match_omp_threadprivate (void);
+match gfc_match_omp_tile (void);
+match gfc_match_omp_unroll (void);
 match gfc_match_omp_workshare (void);
 match gfc_match_omp_end_critical (void);
 match gfc_match_omp_end_nowait (void);
--- gcc/fortran/openmp.cc.jj	2024-06-04 13:18:25.653111356 +0200
+++ gcc/fortran/openmp.cc	2024-06-05 13:21:26.159769330 +0200
@@ -33,6 +33,7 @@  along with GCC; see the file COPYING3.
 #include "bitmap.h"
 #include "omp-api.h"  /* For omp_runtime_api_procname.  */
 
+location_t gfc_get_location (locus *);
 
 static gfc_statement omp_code_to_statement (gfc_code *);
 
@@ -195,6 +196,7 @@  gfc_free_omp_clauses (gfc_omp_clauses *c
 			   i == OMP_LIST_USES_ALLOCATORS);
   gfc_free_expr_list (c->wait_list);
   gfc_free_expr_list (c->tile_list);
+  gfc_free_expr_list (c->sizes_list);
   free (CONST_CAST (char *, c->critical_name));
   if (c->assume)
     {
@@ -762,8 +764,8 @@  cleanup:
 }
 
 static match
-match_oacc_expr_list (const char *str, gfc_expr_list **list,
-		      bool allow_asterisk)
+match_omp_oacc_expr_list (const char *str, gfc_expr_list **list,
+			  bool allow_asterisk, bool is_omp)
 {
   gfc_expr_list *head, *tail, *p;
   locus old_loc;
@@ -815,7 +817,10 @@  match_oacc_expr_list (const char *str, g
   return MATCH_YES;
 
 syntax:
-  gfc_error ("Syntax error in OpenACC expression list at %C");
+  if (is_omp)
+    gfc_error ("Syntax error in OpenMP expression list at %C");
+  else
+    gfc_error ("Syntax error in OpenACC expression list at %C");
 
 cleanup:
   gfc_free_expr_list (head);
@@ -1098,6 +1103,9 @@  enum omp_mask2
   OMP_CLAUSE_ASSUMPTIONS, /* OpenMP 5.1. */
   OMP_CLAUSE_USES_ALLOCATORS, /* OpenMP 5.0  */
   OMP_CLAUSE_INDIRECT, /* OpenMP 5.1  */
+  OMP_CLAUSE_FULL,  /* OpenMP 5.1.  */
+  OMP_CLAUSE_PARTIAL,  /* OpenMP 5.1.  */
+  OMP_CLAUSE_SIZES,  /* OpenMP 5.1.  */
   /* This must come last.  */
   OMP_MASK2_LAST
 };
@@ -2682,6 +2690,14 @@  gfc_match_omp_clauses (gfc_omp_clauses *
 	      && gfc_match_motion_var_list ("from (", &c->lists[OMP_LIST_FROM],
 					     &head) == MATCH_YES)
 	    continue;
+	  if ((mask & OMP_CLAUSE_FULL)
+	      && (m = gfc_match_dupl_check (!c->full, "full")) != MATCH_NO)
+	    {
+	      if (m == MATCH_ERROR)
+		goto error;
+	      c->full = needs_space = true;
+	      continue;
+	    }
 	  break;
 	case 'g':
 	  if ((mask & OMP_CLAUSE_GANG)
@@ -3367,6 +3383,32 @@  gfc_match_omp_clauses (gfc_omp_clauses *
 	    }
 	  break;
 	case 'p':
+	  if (mask & OMP_CLAUSE_PARTIAL)
+	    {
+	      if ((m = gfc_match_dupl_check (!c->partial, "partial"))
+		  != MATCH_NO)
+		{
+		  int expr;
+		  if (m == MATCH_ERROR)
+		    goto error;
+
+		  c->partial = -1;
+
+		  gfc_expr *cexpr = NULL;
+		  m = gfc_match (" ( %e )", &cexpr);
+		  if (m == MATCH_NO)
+		    ;
+		  else if (m == MATCH_YES
+			   && !gfc_extract_int (cexpr, &expr, -1)
+			   && expr > 0)
+		    c->partial = expr;
+		  else
+		    gfc_error_now ("PARTIAL clause argument not constant "
+				   "positive integer at %C");
+		  gfc_free_expr (cexpr);
+		  continue;
+		}
+	    }
 	  if ((mask & OMP_CLAUSE_COPY)
 	      && gfc_match ("pcopy ( ") == MATCH_YES
 	      && gfc_match_omp_map_clause (&c->lists[OMP_LIST_MAP],
@@ -3649,6 +3691,20 @@  gfc_match_omp_clauses (gfc_omp_clauses *
 		}
 	      continue;
 	    }
+	  if ((mask & OMP_CLAUSE_SIZES)
+	      && ((m = gfc_match_dupl_check (!c->sizes_list, "sizes"))
+		  != MATCH_NO))
+	    {
+	      if (m == MATCH_ERROR)
+		goto error;
+	      m = match_omp_oacc_expr_list (" (", &c->sizes_list, false, true);
+	      if (m == MATCH_ERROR)
+		goto error;
+	      if (m == MATCH_YES)
+		continue;
+	      gfc_error ("Expected %<(%> after %qs at %C", "sizes");
+	      goto error;
+	    }
 	  break;
 	case 't':
 	  if ((mask & OMP_CLAUSE_TASK_REDUCTION)
@@ -3675,8 +3731,8 @@  gfc_match_omp_clauses (gfc_omp_clauses *
 	    }
 	  if ((mask & OMP_CLAUSE_TILE)
 	      && !c->tile_list
-	      && match_oacc_expr_list ("tile (", &c->tile_list,
-				       true) == MATCH_YES)
+	      && match_omp_oacc_expr_list ("tile (", &c->tile_list,
+					   true, false) == MATCH_YES)
 	    continue;
 	  if ((mask & OMP_CLAUSE_TO) && (mask & OMP_CLAUSE_LINK))
 	    {
@@ -3772,7 +3828,7 @@  gfc_match_omp_clauses (gfc_omp_clauses *
 	  if ((mask & OMP_CLAUSE_WAIT)
 	      && gfc_match ("wait") == MATCH_YES)
 	    {
-	      m = match_oacc_expr_list (" (", &c->wait_list, false);
+	      m = match_omp_oacc_expr_list (" (", &c->wait_list, false, false);
 	      if (m == MATCH_ERROR)
 		goto error;
 	      else if (m == MATCH_NO)
@@ -4128,7 +4184,7 @@  gfc_match_oacc_wait (void)
   bool space = true;
   match m;
 
-  m = match_oacc_expr_list (" (", &wait_list, true);
+  m = match_omp_oacc_expr_list (" (", &wait_list, true, false);
   if (m == MATCH_ERROR)
     return m;
   else if (m == MATCH_YES)
@@ -4528,6 +4584,10 @@  cleanup:
   (omp_mask (OMP_CLAUSE_AT) | OMP_CLAUSE_MESSAGE | OMP_CLAUSE_SEVERITY)
 #define OMP_WORKSHARE_CLAUSES \
   omp_mask (OMP_CLAUSE_NOWAIT)
+#define OMP_UNROLL_CLAUSES \
+  (omp_mask (OMP_CLAUSE_FULL) | OMP_CLAUSE_PARTIAL)
+#define OMP_TILE_CLAUSES \
+  (omp_mask (OMP_CLAUSE_SIZES))
 #define OMP_ALLOCATORS_CLAUSES \
   omp_mask (OMP_CLAUSE_ALLOCATE)
 
@@ -6793,6 +6853,17 @@  gfc_match_omp_teams_distribute_simd (voi
 		    | OMP_SIMD_CLAUSES);
 }
 
+match
+gfc_match_omp_tile (void)
+{
+  return match_omp (EXEC_OMP_TILE, OMP_TILE_CLAUSES);
+}
+
+match
+gfc_match_omp_unroll (void)
+{
+  return match_omp (EXEC_OMP_UNROLL, OMP_UNROLL_CLAUSES);
+}
 
 match
 gfc_match_omp_workshare (void)
@@ -9182,6 +9253,9 @@  resolve_omp_clauses (gfc_code *code, gfc
     gfc_error ("%<REDUCTION%> clause at %L must not be used together with "
 	       "%<NOGROUP%> clause",
 	       &omp_clauses->lists[OMP_LIST_REDUCTION]->where);
+  if (omp_clauses->full && omp_clauses->partial)
+    gfc_error ("%<FULL%> clause at %C must not be used together with "
+	       "%<PARTIAL%> clause");
   if (omp_clauses->async)
     if (omp_clauses->async_expr)
       resolve_scalar_int_expr (omp_clauses->async_expr, "ASYNC");
@@ -9233,6 +9307,22 @@  resolve_omp_clauses (gfc_code *code, gfc
 	gfc_error ("%s must contain at least one MAP clause at %L",
 		   p, &code->loc);
     }
+  if (omp_clauses->sizes_list)
+    {
+      gfc_expr_list *el;
+      for (el = omp_clauses->sizes_list; el; el = el->next)
+	{
+	  resolve_scalar_int_expr (el->expr, "SIZES");
+	  if (el->expr->expr_type != EXPR_CONSTANT)
+	    gfc_error ("SIZES requires constant expression at %L",
+		       &el->expr->where);
+	  else if (el->expr->expr_type == EXPR_CONSTANT
+		   && el->expr->ts.type == BT_INTEGER
+		   && mpz_sgn (el->expr->value.integer) <= 0)
+	    gfc_error ("INTEGER expression of %s clause at %L must be "
+		       "positive", "SIZES", &el->expr->where);
+	}
+    }
 
   if (!openacc && omp_clauses->detach)
     {
@@ -9913,16 +10003,19 @@  find_nested_loop_in_chain (gfc_code *cha
     return NULL;
 
   for (code = chain; code; code = code->next)
-    {
-      if (code->op == EXEC_DO)
+    switch (code->op)
+      {
+      case EXEC_DO:
+      case EXEC_OMP_TILE:
+      case EXEC_OMP_UNROLL:
 	return code;
-      else if (code->op == EXEC_BLOCK)
-	{
-	  gfc_code *c = find_nested_loop_in_block (code);
-	  if (c)
-	    return c;
-	}
-    }
+      case EXEC_BLOCK:
+	if (gfc_code *c = find_nested_loop_in_block (code))
+	  return c;
+	break;
+      default:
+	break;
+      }
   return NULL;
 }
 
@@ -9950,6 +10043,9 @@  gfc_resolve_omp_do_blocks (gfc_code *cod
 	omp_current_do_collapse = code->ext.omp_clauses->orderedc;
       else if (code->ext.omp_clauses->collapse)
 	omp_current_do_collapse = code->ext.omp_clauses->collapse;
+      else if (code->ext.omp_clauses->sizes_list)
+	omp_current_do_collapse
+	  = gfc_expr_list_len (code->ext.omp_clauses->sizes_list);
       else
 	omp_current_do_collapse = 1;
       if (code->ext.omp_clauses->lists[OMP_LIST_REDUCTION_INSCAN])
@@ -10141,6 +10237,8 @@  gfc_resolve_do_iterator (gfc_code *code,
       if (code == c)
 	return;
       c = find_nested_loop_in_chain (c->block->next);
+      if (c && (c->op == EXEC_OMP_TILE || c->op == EXEC_OMP_UNROLL))
+	return;
     }
 
   /* An openacc context may represent a data clause.  Abort if so.  */
@@ -10439,7 +10537,7 @@  restructure_intervening_code (gfc_code *
   gfc_code *tail = NULL;
   gfc_code *innermost_loop = NULL;
 
-  for (code = *chainp; code; code = code->next, chainp = &((*chainp)->next))
+  for (code = *chainp; code; code = code->next, chainp = &(*chainp)->next)
     {
       if (code->op == EXEC_DO)
 	{
@@ -10452,7 +10550,7 @@  restructure_intervening_code (gfc_code *
 	    innermost_loop = code;
 	  else
 	    innermost_loop
-	      = restructure_intervening_code (&(code->block->next),
+	      = restructure_intervening_code (&code->block->next,
 					      code, count - 1);
 	  break;
 	}
@@ -10467,7 +10565,7 @@  restructure_intervening_code (gfc_code *
 	  code->next = NULL;
 
 	  innermost_loop
-	    = restructure_intervening_code (&(ns->code), outer_loop,
+	    = restructure_intervening_code (&ns->code, outer_loop,
 					    count);
 
 	  /* At this point we have already pulled out the nested loop and
@@ -10524,6 +10622,11 @@  is_outer_iteration_variable (gfc_code *c
     {
       do_code = find_nested_loop_in_chain (do_code->block->next);
       gcc_assert (do_code);
+      if (do_code->op == EXEC_OMP_TILE || do_code->op == EXEC_OMP_UNROLL)
+	{
+	  --i;
+	  continue;
+	}
       gfc_symbol *ivar = do_code->ext.iterator->var->symtree->n.sym;
       if (var == ivar)
 	return true;
@@ -10548,6 +10651,8 @@  check_nested_loop_in_chain (gfc_code *ch
     {
       if (code->op == EXEC_DO)
 	return code;
+      else if (code->op == EXEC_OMP_TILE || code->op == EXEC_OMP_UNROLL)
+	return check_nested_loop_in_chain (code->block->next, expr, sym, bad);
       else if (code->op == EXEC_BLOCK)
 	{
 	  gfc_code *c = check_nested_loop_in_block (code, expr, sym, bad);
@@ -10654,6 +10759,11 @@  expr_is_invariant (gfc_code *code, int d
     {
       do_code = find_nested_loop_in_chain (do_code->block->next);
       gcc_assert (do_code);
+      if (do_code->op == EXEC_OMP_TILE || do_code->op == EXEC_OMP_UNROLL)
+	{
+	  --i;
+	  continue;
+	}
       gfc_symbol *ivar = do_code->ext.iterator->var->symtree->n.sym;
       if (gfc_find_sym_in_expr (ivar, expr))
 	return false;
@@ -10728,13 +10838,14 @@  static void
 resolve_omp_do (gfc_code *code)
 {
   gfc_code *do_code, *next;
-  int list, i, count;
+  int list, i, count, non_generated_count;
   gfc_omp_namelist *n;
   gfc_symbol *dovar;
   const char *name;
   bool is_simd = false;
   bool errorp = false;
   bool perfect_nesting_errorp = false;
+  bool imperfect = false;
 
   switch (code->op)
     {
@@ -10829,15 +10940,23 @@  resolve_omp_do (gfc_code *code)
       is_simd = true;
       break;
     case EXEC_OMP_TEAMS_LOOP: name = "!$OMP TEAMS LOOP"; break;
+    case EXEC_OMP_TILE: name = "!$OMP TILE"; break;
+    case EXEC_OMP_UNROLL: name = "!$OMP UNROLL"; break;
     default: gcc_unreachable ();
     }
 
   if (code->ext.omp_clauses)
     resolve_omp_clauses (code, code->ext.omp_clauses, NULL);
 
+  if (code->op == EXEC_OMP_TILE && code->ext.omp_clauses->sizes_list == NULL)
+    gfc_error ("SIZES clause is required on !$OMP TILE construct at %L",
+	       &code->loc);
+
   do_code = code->block->next;
   if (code->ext.omp_clauses->orderedc)
     count = code->ext.omp_clauses->orderedc;
+  else if (code->ext.omp_clauses->sizes_list)
+    count = gfc_expr_list_len (code->ext.omp_clauses->sizes_list);
   else
     {
       count = code->ext.omp_clauses->collapse;
@@ -10845,6 +10964,7 @@  resolve_omp_do (gfc_code *code)
 	count = 1;
     }
 
+  non_generated_count = count;
   /* While the spec defines the loop nest depth independently of the COLLAPSE
      clause, in practice the middle end only pays attention to the COLLAPSE
      depth and treats any further inner loops as the final-loop-body.  So
@@ -10858,13 +10978,61 @@  resolve_omp_do (gfc_code *code)
 	{
 	  gfc_error ("%s cannot be a DO WHILE or DO without loop control "
 		     "at %L", name, &do_code->loc);
-	  return;
+	  goto fail;
 	}
       if (do_code->op == EXEC_DO_CONCURRENT)
 	{
 	  gfc_error ("%s cannot be a DO CONCURRENT loop at %L", name,
 		     &do_code->loc);
-	  return;
+	  goto fail;
+	}
+      if (do_code->op == EXEC_OMP_TILE || do_code->op == EXEC_OMP_UNROLL)
+	{
+	  if (do_code->op == EXEC_OMP_UNROLL)
+	    {
+	      if (!do_code->ext.omp_clauses->partial)
+		{
+		  gfc_error ("Generated loop of UNROLL construct at %L "
+			     "without PARTIAL clause does not have "
+			     "canonical form", &do_code->loc);
+		  goto fail;
+		}
+	      else if (i != count)
+		{
+		  gfc_error ("UNROLL construct at %L with PARTIAL clause "
+			     "generates just one loop with canonical form "
+			     "but %d loops are needed",
+			     &do_code->loc, count - i + 1);
+		  goto fail;
+		}
+	    }
+	  else if (do_code->op == EXEC_OMP_TILE)
+	    {
+	      if (do_code->ext.omp_clauses->sizes_list == NULL)
+		/* This should have been diagnosed earlier already.  */
+		return;
+	      int l = gfc_expr_list_len (do_code->ext.omp_clauses->sizes_list);
+	      if (count - i + 1 > l)
+		{
+		  gfc_error ("TILE construct at %L generates %d loops "
+			     "with canonical form but %d loops are needed",
+			     &do_code->loc, l, count - i + 1);
+		  goto fail;
+		}
+	    }
+	  if (do_code->ext.omp_clauses && do_code->ext.omp_clauses->erroneous)
+	    goto fail;
+	  if (imperfect && !perfect_nesting_errorp)
+	    {
+	      sorry_at (gfc_get_location (&do_code->loc),
+			"Imperfectly nested loop using generated loops");
+	      errorp = true;
+	    }
+	  if (non_generated_count == count)
+	    non_generated_count = i - 1;
+	  --i;
+	  do_code = do_code->block->next;
+	  continue;
 	}
       gcc_assert (do_code->op == EXEC_DO);
       if (do_code->ext.iterator->var->ts.type != BT_INTEGER)
@@ -10966,7 +11134,16 @@  resolve_omp_do (gfc_code *code)
 	  errorp = true;
 	}
       if (start_var || end_var)
-	code->ext.omp_clauses->non_rectangular = 1;
+	{
+	  code->ext.omp_clauses->non_rectangular = 1;
+	  if (i > non_generated_count)
+	    {
+	      sorry_at (gfc_get_location (&do_code->loc),
+			"Non-rectangular loops from generated loops "
+			"unsupported");
+	      errorp = true;
+	    }
+	}
 
       /* Only parse loop body into nested loop and intervening code if
 	 there are supposed to be more loops in the nest to collapse.  */
@@ -10980,7 +11157,7 @@  resolve_omp_do (gfc_code *code)
 	  /* Parse error, can't recover from this.  */
 	  gfc_error ("not enough DO loops for collapsed %s (level %d) at %L",
 		     name, i, &code->loc);
-	  return;
+	  goto fail;
 	}
       else if (next != do_code->block->next || next->next)
 	/* Imperfectly nested loop found.  */
@@ -11002,22 +11179,35 @@  resolve_omp_do (gfc_code *code)
 			     name, &code->loc);
 		  perfect_nesting_errorp = true;
 		}
-	      /* FIXME: Also diagnose for TILE directives.  */
+	      else if (code->op == EXEC_OMP_TILE)
+		{
+		  gfc_error ("%s inner loops must be perfectly nested at %L",
+			     name, &code->loc);
+		  perfect_nesting_errorp = true;
+		}
 	      if (perfect_nesting_errorp)
 		errorp = true;
 	    }
 	  if (diagnose_intervening_code_errors (do_code->block->next,
 						name, next))
 	    errorp = true;
+	  imperfect = true;
 	}
       do_code = next;
     }
 
   /* Give up now if we found any constraint violations.  */
   if (errorp)
-    return;
+    {
+    fail:
+      if (code->ext.omp_clauses)
+	code->ext.omp_clauses->erroneous = 1;
+      return;
+    }
 
-  restructure_intervening_code (&(code->block->next), code, count);
+  if (non_generated_count)
+    restructure_intervening_code (&code->block->next, code,
+				  non_generated_count);
 }
 
 
@@ -11168,6 +11358,10 @@  omp_code_to_statement (gfc_code *code)
       return ST_OMP_PARALLEL_LOOP;
     case EXEC_OMP_DEPOBJ:
       return ST_OMP_DEPOBJ;
+    case EXEC_OMP_TILE:
+      return ST_OMP_TILE;
+    case EXEC_OMP_UNROLL:
+      return ST_OMP_UNROLL;
     default:
       gcc_unreachable ();
     }
@@ -11632,6 +11826,8 @@  gfc_resolve_omp_directive (gfc_code *cod
     case EXEC_OMP_TEAMS_DISTRIBUTE_PARALLEL_DO_SIMD:
     case EXEC_OMP_TEAMS_DISTRIBUTE_SIMD:
     case EXEC_OMP_TEAMS_LOOP:
+    case EXEC_OMP_TILE:
+    case EXEC_OMP_UNROLL:
       resolve_omp_do (code);
       break;
     case EXEC_OMP_TARGET:
--- gcc/fortran/parse.cc.jj	2024-06-04 13:18:25.675111063 +0200
+++ gcc/fortran/parse.cc	2024-06-04 13:30:01.016868290 +0200
@@ -1006,14 +1006,22 @@  decode_omp_directive (void)
     case 'e':
       matchs ("end assume", gfc_match_omp_eos_error, ST_OMP_END_ASSUME);
       matchs ("end simd", gfc_match_omp_eos_error, ST_OMP_END_SIMD);
+      matchs ("end tile", gfc_match_omp_eos_error, ST_OMP_END_TILE);
+      matchs ("end unroll", gfc_match_omp_eos_error, ST_OMP_END_UNROLL);
       matcho ("error", gfc_match_omp_error, ST_OMP_ERROR);
       break;
+    case 'n':
+      matcho ("nothing", gfc_match_omp_nothing, ST_NONE);
+      break;
     case 's':
       matchs ("scan", gfc_match_omp_scan, ST_OMP_SCAN);
       matchs ("simd", gfc_match_omp_simd, ST_OMP_SIMD);
       break;
-    case 'n':
-      matcho ("nothing", gfc_match_omp_nothing, ST_NONE);
+    case 't':
+      matchs ("tile", gfc_match_omp_tile, ST_OMP_TILE);
+      break;
+    case 'u':
+      matchs ("unroll", gfc_match_omp_unroll, ST_OMP_UNROLL);
       break;
     }
 
@@ -1916,6 +1924,7 @@  next_statement (void)
   case ST_OMP_LOOP: case ST_OMP_PARALLEL_LOOP: case ST_OMP_TEAMS_LOOP: \
   case ST_OMP_TARGET_PARALLEL_LOOP: case ST_OMP_TARGET_TEAMS_LOOP: \
   case ST_OMP_ALLOCATE_EXEC: case ST_OMP_ALLOCATORS: case ST_OMP_ASSUME: \
+  case ST_OMP_TILE: case ST_OMP_UNROLL: \
   case ST_CRITICAL: \
   case ST_OACC_PARALLEL_LOOP: case ST_OACC_PARALLEL: case ST_OACC_KERNELS: \
   case ST_OACC_DATA: case ST_OACC_HOST_DATA: case ST_OACC_LOOP: \
@@ -2786,6 +2795,12 @@  gfc_ascii_statement (gfc_statement st, b
     case ST_OMP_END_TEAMS_LOOP:
       p = "!$OMP END TEAMS LOOP";
       break;
+    case ST_OMP_END_TILE:
+      p = "!$OMP END TILE";
+      break;
+    case ST_OMP_END_UNROLL:
+      p = "!$OMP END UNROLL";
+      break;
     case ST_OMP_END_WORKSHARE:
       p = "!$OMP END WORKSHARE";
       break;
@@ -2968,6 +2983,12 @@  gfc_ascii_statement (gfc_statement st, b
     case ST_OMP_THREADPRIVATE:
       p = "!$OMP THREADPRIVATE";
       break;
+    case ST_OMP_TILE:
+      p = "!$OMP TILE";
+      break;
+    case ST_OMP_UNROLL:
+      p = "!$OMP UNROLL";
+      break;
     case ST_OMP_WORKSHARE:
       p = "!$OMP WORKSHARE";
       break;
@@ -5441,7 +5462,7 @@  loop:
 /* Parse the statements of OpenMP do/parallel do.  */
 
 static gfc_statement
-parse_omp_do (gfc_statement omp_st)
+parse_omp_do (gfc_statement omp_st, int nested)
 {
   gfc_statement st;
   gfc_code *cp, *np;
@@ -5462,11 +5483,20 @@  parse_omp_do (gfc_statement omp_st)
 	unexpected_eof ();
       else if (st == ST_DO)
 	break;
+      else if (st == ST_OMP_UNROLL || st == ST_OMP_TILE)
+	{
+	  st = parse_omp_do (st, nested + 1);
+	  if (st == ST_IMPLIED_ENDDO)
+	    return st;
+	  goto do_end;
+	}
       else
 	unexpected_statement (st);
     }
 
   parse_do_block ();
+  for (; nested; --nested)
+    pop_state ();
   if (gfc_statement_label != NULL
       && gfc_state_stack->previous != NULL
       && gfc_state_stack->previous->state == COMP_DO
@@ -5487,6 +5517,7 @@  parse_omp_do (gfc_statement omp_st)
   pop_state ();
 
   st = next_statement ();
+do_end:
   gfc_statement omp_end_st = ST_OMP_END_DO;
   switch (omp_st)
     {
@@ -5570,9 +5601,9 @@  parse_omp_do (gfc_statement omp_st)
     case ST_OMP_TEAMS_DISTRIBUTE_SIMD:
       omp_end_st = ST_OMP_END_TEAMS_DISTRIBUTE_SIMD;
       break;
-    case ST_OMP_TEAMS_LOOP:
-      omp_end_st = ST_OMP_END_TEAMS_LOOP;
-      break;
+    case ST_OMP_TEAMS_LOOP: omp_end_st = ST_OMP_END_TEAMS_LOOP; break;
+    case ST_OMP_TILE: omp_end_st = ST_OMP_END_TILE; break;
+    case ST_OMP_UNROLL: omp_end_st = ST_OMP_END_UNROLL; break;
     default: gcc_unreachable ();
     }
   if (st == omp_end_st)
@@ -6073,7 +6104,7 @@  parse_omp_structured_block (gfc_statemen
 
 		case ST_OMP_PARALLEL_DO:
 		case ST_OMP_PARALLEL_DO_SIMD:
-		  st = parse_omp_do (st);
+		  st = parse_omp_do (st, 0);
 		  continue;
 
 		case ST_OMP_ATOMIC:
@@ -6370,7 +6401,9 @@  parse_executable (gfc_statement st)
 	case ST_OMP_TEAMS_DISTRIBUTE_PARALLEL_DO_SIMD:
 	case ST_OMP_TEAMS_DISTRIBUTE_SIMD:
 	case ST_OMP_TEAMS_LOOP:
-	  st = parse_omp_do (st);
+	case ST_OMP_TILE:
+	case ST_OMP_UNROLL:
+	  st = parse_omp_do (st, 0);
 	  if (st == ST_IMPLIED_ENDDO)
 	    return st;
 	  continue;
--- gcc/fortran/resolve.cc.jj	2024-06-04 13:18:25.707110637 +0200
+++ gcc/fortran/resolve.cc	2024-06-04 13:30:00.955869100 +0200
@@ -11437,6 +11437,8 @@  gfc_resolve_blocks (gfc_code *b, gfc_nam
 	case EXEC_OMP_TEAMS_DISTRIBUTE_PARALLEL_DO_SIMD:
 	case EXEC_OMP_TEAMS_LOOP:
 	case EXEC_OMP_TEAMS_DISTRIBUTE_SIMD:
+	case EXEC_OMP_TILE:
+	case EXEC_OMP_UNROLL:
 	case EXEC_OMP_WORKSHARE:
 	  break;
 
@@ -12604,6 +12606,8 @@  gfc_resolve_code (gfc_code *code, gfc_na
 	    case EXEC_OMP_LOOP:
 	    case EXEC_OMP_SIMD:
 	    case EXEC_OMP_TARGET_SIMD:
+	    case EXEC_OMP_TILE:
+	    case EXEC_OMP_UNROLL:
 	      gfc_resolve_omp_do_blocks (code, ns);
 	      break;
 	    case EXEC_SELECT_TYPE:
@@ -13102,6 +13106,8 @@  start:
 	case EXEC_OMP_TEAMS_DISTRIBUTE_PARALLEL_DO_SIMD:
 	case EXEC_OMP_TEAMS_DISTRIBUTE_SIMD:
 	case EXEC_OMP_TEAMS_LOOP:
+	case EXEC_OMP_TILE:
+	case EXEC_OMP_UNROLL:
 	case EXEC_OMP_WORKSHARE:
 	  gfc_resolve_omp_directive (code, ns);
 	  break;
--- gcc/fortran/st.cc.jj	2024-06-04 13:18:25.744110145 +0200
+++ gcc/fortran/st.cc	2024-06-04 13:30:00.977868808 +0200
@@ -279,6 +279,8 @@  gfc_free_statement (gfc_code *p)
     case EXEC_OMP_TEAMS_DISTRIBUTE_PARALLEL_DO_SIMD:
     case EXEC_OMP_TEAMS_DISTRIBUTE_SIMD:
     case EXEC_OMP_TEAMS_LOOP:
+    case EXEC_OMP_TILE:
+    case EXEC_OMP_UNROLL:
     case EXEC_OMP_WORKSHARE:
       gfc_free_omp_clauses (p->ext.omp_clauses);
       break;
--- gcc/fortran/trans.cc.jj	2024-06-04 13:18:25.756109985 +0200
+++ gcc/fortran/trans.cc	2024-06-04 13:30:00.931869418 +0200
@@ -2656,6 +2656,8 @@  trans_code (gfc_code * code, tree cond)
 	case EXEC_OMP_TEAMS_DISTRIBUTE_PARALLEL_DO_SIMD:
 	case EXEC_OMP_TEAMS_DISTRIBUTE_SIMD:
 	case EXEC_OMP_TEAMS_LOOP:
+	case EXEC_OMP_TILE:
+	case EXEC_OMP_UNROLL:
 	case EXEC_OMP_WORKSHARE:
 	  res = gfc_trans_omp_directive (code);
 	  break;
--- gcc/fortran/trans-openmp.cc.jj	2024-06-04 13:18:25.754110012 +0200
+++ gcc/fortran/trans-openmp.cc	2024-06-04 13:30:00.921869551 +0200
@@ -4327,6 +4327,34 @@  gfc_trans_omp_clauses (stmtblock_t *bloc
       omp_clauses = gfc_trans_add_clause (c, omp_clauses);
     }
 
+  if (clauses->full)
+    {
+      c = build_omp_clause (gfc_get_location (&where), OMP_CLAUSE_FULL);
+      omp_clauses = gfc_trans_add_clause (c, omp_clauses);
+    }
+
+  if (clauses->partial)
+    {
+      c = build_omp_clause (gfc_get_location (&where), OMP_CLAUSE_PARTIAL);
+      OMP_CLAUSE_PARTIAL_EXPR (c)
+	= (clauses->partial > 0
+	   ? build_int_cst (integer_type_node, clauses->partial)
+	   : NULL_TREE);
+      omp_clauses = gfc_trans_add_clause (c, omp_clauses);
+    }
+
+  if (clauses->sizes_list)
+    {
+      tree list = NULL_TREE;
+      for (gfc_expr_list *el = clauses->sizes_list; el; el = el->next)
+	list = tree_cons (NULL_TREE, gfc_convert_expr_to_tree (block, el->expr),
+			  list);
+
+      c = build_omp_clause (gfc_get_location (&where), OMP_CLAUSE_SIZES);
+      OMP_CLAUSE_SIZES_LIST (c) = nreverse (list);
+      omp_clauses = gfc_trans_add_clause (c, omp_clauses);
+    }
+
   if (clauses->ordered)
     {
       c = build_omp_clause (gfc_get_location (&where), OMP_CLAUSE_ORDERED);
@@ -4783,18 +4811,14 @@  gfc_trans_omp_clauses (stmtblock_t *bloc
     }
   if (clauses->tile_list)
     {
-      vec<tree, va_gc> *tvec;
-      gfc_expr_list *el;
-
-      vec_alloc (tvec, 4);
-
-      for (el = clauses->tile_list; el; el = el->next)
-	vec_safe_push (tvec, gfc_convert_expr_to_tree (block, el->expr));
+      tree list = NULL_TREE;
+      for (gfc_expr_list *el = clauses->tile_list; el; el = el->next)
+	list = tree_cons (NULL_TREE, gfc_convert_expr_to_tree (block, el->expr),
+			  list);
 
       c = build_omp_clause (gfc_get_location (&where), OMP_CLAUSE_TILE);
-      OMP_CLAUSE_TILE_LIST (c) = build_tree_list_vec (tvec);
+      OMP_CLAUSE_TILE_LIST (c) = nreverse (list);
       omp_clauses = gfc_trans_add_clause (c, omp_clauses);
-      tvec->truncate (0);
     }
   if (clauses->vector)
     {
@@ -5718,6 +5742,16 @@  gfc_nonrect_loop_expr (stmtblock_t *pblo
   return true;
 }
 
+int
+gfc_expr_list_len (gfc_expr_list *list)
+{
+  unsigned len = 0;
+  for (; list; list = list->next)
+    len++;
+
+  return len;
+}
+
 static tree
 gfc_trans_omp_do (gfc_code *code, gfc_exec_op op, stmtblock_t *pblock,
 		  gfc_omp_clauses *do_clauses, tree par_clauses)
@@ -5733,18 +5767,19 @@  gfc_trans_omp_do (gfc_code *code, gfc_ex
   dovar_init *di;
   unsigned ix;
   vec<tree, va_heap, vl_embed> *saved_doacross_steps = doacross_steps;
-  gfc_expr_list *tile = do_clauses ? do_clauses->tile_list : clauses->tile_list;
+  gfc_expr_list *oacc_tile
+    = do_clauses ? do_clauses->tile_list : clauses->tile_list;
+  gfc_expr_list *sizes
+    = do_clauses ? do_clauses->sizes_list : clauses->sizes_list;
   gfc_code *orig_code = code;
 
   /* Both collapsed and tiled loops are lowered the same way.  In
      OpenACC, those clauses are not compatible, so prioritize the tile
      clause, if present.  */
-  if (tile)
-    {
-      collapse = 0;
-      for (gfc_expr_list *el = tile; el; el = el->next)
-	collapse++;
-    }
+  if (oacc_tile)
+    collapse = gfc_expr_list_len (oacc_tile);
+  else if (sizes)
+    collapse = gfc_expr_list_len (sizes);
 
   doacross_steps = NULL;
   if (clauses->orderedc)
@@ -5753,7 +5788,6 @@  gfc_trans_omp_do (gfc_code *code, gfc_ex
     collapse = 1;
 
   code = code->block->next;
-  gcc_assert (code->op == EXEC_DO);
 
   init = make_tree_vec (collapse);
   cond = make_tree_vec (collapse);
@@ -5779,6 +5813,17 @@  gfc_trans_omp_do (gfc_code *code, gfc_ex
       int dovar_found = 0;
       tree dovar_decl;
 
+      if (code->op == EXEC_OMP_TILE || code->op == EXEC_OMP_UNROLL)
+	{
+	  TREE_VEC_ELT (init, i) = NULL_TREE;
+	  TREE_VEC_ELT (cond, i) = NULL_TREE;
+	  TREE_VEC_ELT (incr, i) = NULL_TREE;
+	  TREE_VEC_ELT (incr, i) = NULL_TREE;
+	  if (orig_decls)
+	    TREE_VEC_ELT (orig_decls, i) = NULL_TREE;
+	  continue;
+	}
+      gcc_assert (code->op == EXEC_DO);
       if (clauses)
 	{
 	  gfc_omp_namelist *n = NULL;
@@ -6092,6 +6137,8 @@  gfc_trans_omp_do (gfc_code *code, gfc_ex
       if (code1 != scan)
 	tmpcode->next = scan;
     }
+  else if (code->op == EXEC_OMP_TILE || code->op == EXEC_OMP_UNROLL)
+    tmp = gfc_trans_omp_code (code, true);
   else
     tmp = gfc_trans_omp_code (code->block->next, true);
   gfc_add_expr_to_block (&body, tmp);
@@ -6112,6 +6159,8 @@  gfc_trans_omp_do (gfc_code *code, gfc_ex
     case EXEC_OMP_LOOP: stmt = make_node (OMP_LOOP); break;
     case EXEC_OMP_TASKLOOP: stmt = make_node (OMP_TASKLOOP); break;
     case EXEC_OACC_LOOP: stmt = make_node (OACC_LOOP); break;
+    case EXEC_OMP_TILE: stmt = make_node (OMP_TILE); break;
+    case EXEC_OMP_UNROLL: stmt = make_node (OMP_UNROLL); break;
     default: gcc_unreachable ();
     }
 
@@ -8219,6 +8268,8 @@  gfc_trans_omp_directive (gfc_code *code)
     case EXEC_OMP_LOOP:
     case EXEC_OMP_SIMD:
     case EXEC_OMP_TASKLOOP:
+    case EXEC_OMP_TILE:
+    case EXEC_OMP_UNROLL:
       return gfc_trans_omp_do (code, code->op, NULL, code->ext.omp_clauses,
 			       NULL);
     case EXEC_OMP_DISTRIBUTE_PARALLEL_DO:
--- gcc/fortran/dump-parse-tree.cc.jj	2024-06-04 13:18:25.528113019 +0200
+++ gcc/fortran/dump-parse-tree.cc	2024-06-04 13:30:00.936869352 +0200
@@ -2119,6 +2119,26 @@  show_omp_clauses (gfc_omp_clauses *omp_c
     }
   if (omp_clauses->assume)
     show_omp_assumes (omp_clauses->assume);
+  if (omp_clauses->full)
+    fputs (" FULL", dumpfile);
+  if (omp_clauses->partial)
+    {
+      fputs (" PARTIAL", dumpfile);
+      if (omp_clauses->partial > 0)
+	fprintf (dumpfile, "(%d)", omp_clauses->partial);
+    }
+  if (omp_clauses->sizes_list)
+    {
+      gfc_expr_list *sizes;
+      fputs (" SIZES(", dumpfile);
+      for (sizes = omp_clauses->sizes_list; sizes; sizes = sizes->next)
+	{
+	  show_expr (sizes->expr);
+	  if (sizes->next)
+	    fputs (", ", dumpfile);
+	}
+      fputc (')', dumpfile);
+    }
 }
 
 /* Show a single OpenMP or OpenACC directive node and everything underneath it
@@ -2231,6 +2251,8 @@  show_omp_node (int level, gfc_code *c)
       name = "TEAMS DISTRIBUTE PARALLEL DO SIMD"; break;
     case EXEC_OMP_TEAMS_DISTRIBUTE_SIMD: name = "TEAMS DISTRIBUTE SIMD"; break;
     case EXEC_OMP_TEAMS_LOOP: name = "TEAMS LOOP"; break;
+    case EXEC_OMP_TILE: name = "TILE"; break;
+    case EXEC_OMP_UNROLL: name = "UNROLL"; break;
     case EXEC_OMP_WORKSHARE: name = "WORKSHARE"; break;
     default:
       gcc_unreachable ();
@@ -2309,6 +2331,8 @@  show_omp_node (int level, gfc_code *c)
     case EXEC_OMP_TEAMS_DISTRIBUTE_PARALLEL_DO_SIMD:
     case EXEC_OMP_TEAMS_DISTRIBUTE_SIMD:
     case EXEC_OMP_TEAMS_LOOP:
+    case EXEC_OMP_TILE:
+    case EXEC_OMP_UNROLL:
     case EXEC_OMP_WORKSHARE:
       omp_clauses = c->ext.omp_clauses;
       break;
@@ -3559,6 +3583,8 @@  show_code_node (int level, gfc_code *c)
     case EXEC_OMP_TEAMS_DISTRIBUTE_PARALLEL_DO_SIMD:
     case EXEC_OMP_TEAMS_DISTRIBUTE_SIMD:
     case EXEC_OMP_TEAMS_LOOP:
+    case EXEC_OMP_TILE:
+    case EXEC_OMP_UNROLL:
     case EXEC_OMP_WORKSHARE:
       show_omp_node (level, c);
       break;
--- gcc/testsuite/c-c++-common/gomp/attrs-tile-1.c.jj	2024-06-04 13:30:00.679872765 +0200
+++ gcc/testsuite/c-c++-common/gomp/attrs-tile-1.c	2024-06-04 13:30:00.679872765 +0200
@@ -0,0 +1,57 @@ 
+/* { dg-do compile { target { c || c++11 } } } */
+/* { dg-additional-options "-std=c23" { target c } } */
+
+extern void dummy (int);
+
+void
+test (void)
+{
+  [[omp::directive (tile sizes(1))]]
+  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, 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 partial),
+		   directive (tile sizes(1)))]]
+  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))]]
+  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);
+    }
+}
--- gcc/testsuite/c-c++-common/gomp/attrs-tile-2.c.jj	2024-06-04 13:30:00.680872752 +0200
+++ gcc/testsuite/c-c++-common/gomp/attrs-tile-2.c	2024-06-04 13:30:00.680872752 +0200
@@ -0,0 +1,70 @@ 
+/* { dg-do compile { target { c || c++11 } } } */
+/* { dg-additional-options "-std=c23" { target c } } */
+
+extern void dummy (int);
+
+void
+test (void)
+{
+  [[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(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, 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 partial),
+		   directive (tile sizes(1)))]]
+  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)))]]
+  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)))]]
+  for (int i = 0; i < 100; ++i)
+    dummy (i);
+}
--- gcc/testsuite/c-c++-common/gomp/attrs-tile-3.c.jj	2024-06-04 13:30:00.694872566 +0200
+++ gcc/testsuite/c-c++-common/gomp/attrs-tile-3.c	2024-06-04 13:30:00.694872566 +0200
@@ -0,0 +1,47 @@ 
+/* { dg-do compile { target { c || c++11 } } } */
+/* { dg-additional-options "-std=c23" { target c } } */
+
+extern void dummy (int);
+
+void
+test (void)
+{
+  [[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, 2)))]]
+  for (int i = 0; i < 100; ++i)
+    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(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, 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);
+}
--- gcc/testsuite/c-c++-common/gomp/attrs-tile-4.c.jj	2024-06-04 13:30:00.692872592 +0200
+++ gcc/testsuite/c-c++-common/gomp/attrs-tile-4.c	2024-06-04 13:30:00.692872592 +0200
@@ -0,0 +1,106 @@ 
+/* { dg-do compile { target { c || c++11 } } } */
+/* { dg-additional-options "-std=c23" { target c } } */
+
+extern void dummy (int);
+
+void
+test (void)
+{
+  [[omp::directive (tile sizes(0))]] /* { dg-error "'sizes' argument needs positive integral constant" } */
+  for (int i = 0; i < 100; ++i)
+    dummy (i);
+
+  [[omp::directive (tile sizes(-1))]] /* { dg-error "'sizes' argument needs positive integral constant" } */
+  for (int i = 0; i < 100; ++i)
+    dummy (i);
+
+  [[omp::directive (tile sizes())]] /* { dg-error "expected expression before" "" { target c } } */
+  for (int i = 0; i < 100; ++i) /* { dg-error "expected primary-expression before" "" { target c++ } .-1 } */
+    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, 2)),
+		   directive (tile sizes(1)))]] /* { dg-error "'tile' construct generates 1 loops with canonical form but 2 loops are needed" } */
+  for (int i = 0; i < 100; ++i)
+    for (int j = 0; j < 100; ++j)
+      dummy (i);
+
+  [[omp::sequence (directive (tile sizes(1)),
+		   directive (unroll partia), /* { dg-error "expected an OpenMP clause before 'partia'" } */
+		   directive (tile sizes(1)))]] /* { dg-error "generated loop of 'unroll' construct without 'partial' clause does not have canonical form" "" { target *-*-* } .-1 } */
+  for (int i = 0; i < 100; ++i)
+    dummy (i);
+
+  [[omp::sequence (directive (tile sizes(1)),
+		   directive (unroll))]] /* { dg-error "generated loop of 'unroll' construct without 'partial' clause does not have canonical form" } */
+  for (int i = 0; i < 100; ++i)
+    dummy (i);
+
+  [[omp::sequence (directive (tile sizes(1)),
+		   directive (unroll full))]] /* { dg-error "generated loop of 'unroll' construct without 'partial' clause does not have canonical form" } */
+  for (int i = 0; i < 100; ++i)
+    dummy (i);
+
+  [[omp::sequence (directive (tile sizes(8,8)),
+		   directive (unroll partial), /* { dg-error "'unroll' construct with 'partial' clause generates just one loop with canonical form but 2 loops are needed" } */
+		   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 "'unroll' construct with 'partial' clause generates just one loop with canonical form but 2 loops are needed" } */
+  for (int i = 0; i < 100; ++i)
+    dummy (i);
+
+  [[omp::directive (tile sizes(1, 2))]] /* { dg-error "non-rectangular 'tile'" } */
+  for (int i = 0; i < 100; ++i)
+    for (int j = i; j < 100; ++j)
+      dummy (i);
+
+  [[omp::directive (tile sizes(1, 2))]] /* { dg-error "non-rectangular 'tile'" } */
+  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, 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 "'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 "'sizes' argument needs positive integral constant" } */
+  for (int i = 0; i < 100; ++i)
+    dummy (i);
+}
--- gcc/testsuite/c-c++-common/gomp/attrs-tile-5.c.jj	2024-06-04 13:30:00.680872752 +0200
+++ gcc/testsuite/c-c++-common/gomp/attrs-tile-5.c	2024-06-04 13:30:00.680872752 +0200
@@ -0,0 +1,113 @@ 
+/* { dg-do compile { target { c || c++11 } } } */
+/* { dg-additional-options "-std=c23" { target c } } */
+
+extern void dummy (int);
+
+void
+test (void)
+{
+  [[omp::sequence (directive (parallel for),
+		   directive (tile sizes(0)))]] /* { dg-error "'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 "'sizes' argument needs positive integral constant" } */
+  for (int i = 0; i < 100; ++i)
+    dummy (i);
+
+  [[omp::sequence (directive (parallel for), /* { dg-error "expected primary-expression before" "" { target c++ } .+1 } */
+		   directive (tile sizes()))]] /* { dg-error "expected expression before" "" { target c } } */
+  for (int i = 0; i < 100; ++i)
+    dummy (i);
+
+  [[omp::sequence (directive (parallel for), /* { dg-error "expected primary-expression before" "" { target c++ } .+1 } */
+		   directive (tile sizes(,)))]] /* { dg-error "expected expression before" "" { target c } } */
+  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, 2)),
+		   directive (tile sizes(1)))]] /* { dg-error "'tile' construct generates 1 loops with canonical form but 2 loops are needed" } */
+  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)),
+		   directive (unroll partia), /* { dg-error "expected an OpenMP clause before 'partia'" } */
+		   directive (tile sizes(1)))]] /* { dg-error "generated loop of 'unroll' construct without 'partial' clause does not have canonical form" "" { target *-*-* } .-1 } */
+  for (int i = 0; i < 100; ++i)
+    dummy (i);
+
+  [[omp::sequence (directive (parallel for),
+		   directive (tile sizes(1)),
+		   directive (unroll))]] /* { dg-error "generated loop of 'unroll' construct without 'partial' clause does not have canonical form" } */
+  for (int i = 0; i < 100; ++i)
+    dummy (i);
+
+  [[omp::sequence (directive (parallel for),
+		   directive (tile sizes(1)),
+		   directive (unroll full))]] /* { dg-error "generated loop of 'unroll' construct without 'partial' clause does not have canonical form" } */
+  for (int i = 0; i < 100; ++i)
+    dummy (i);
+
+  [[omp::sequence (directive (parallel for),
+		   directive (tile sizes(8,8)),
+		   directive (unroll partial), /* { dg-error "'unroll' construct with 'partial' clause generates just one loop with canonical form but 2 loops are needed" } */
+		   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 "'unroll' construct with 'partial' clause generates just one loop with canonical form but 2 loops are needed" } */
+  for (int i = 0; i < 100; ++i)
+    dummy (i);
+
+  [[omp::sequence (directive (parallel for),
+		   directive (tile sizes(1, 2)))]] /* { dg-error "non-rectangular 'tile'" "" { target c } } */
+  for (int i = 0; i < 100; ++i) /* { dg-error "non-rectangular 'tile'" "" { target c++ } } */
+    for (int j = i; j < 100; ++j)
+      dummy (i);
+
+  [[omp::sequence (directive (parallel for),
+		   directive (tile sizes(1, 2)))]] /* { dg-error "non-rectangular 'tile'" "" { target c } } */
+  for (int i = 0; i < 100; ++i) /* { dg-error "non-rectangular 'tile'" "" { target c++ } } */
+    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, 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);
+    }
+}
--- gcc/testsuite/c-c++-common/gomp/attrs-tile-6.c.jj	2024-06-04 13:30:00.694872566 +0200
+++ gcc/testsuite/c-c++-common/gomp/attrs-tile-6.c	2024-06-04 13:30:00.694872566 +0200
@@ -0,0 +1,72 @@ 
+/* { dg-do compile { target { c || c++11 } } } */
+/* { dg-additional-options "-std=c23" { target c } } */
+
+extern void dummy (int);
+
+void
+test (void)
+{
+  [[omp::sequence (directive (for),
+		   directive (tile sizes(1, 2)))]] /* { dg-error "non-rectangular 'tile'" "" { target c } } */
+  for (int i = 0; i < 100; ++i) /* { dg-error "non-rectangular 'tile'" "" { target c++ } } */
+    for (int j = i; j < 100; ++j)
+      dummy (i);
+
+  [[omp::sequence (directive (for),
+		   directive (tile sizes(1, 2)))]] /* { dg-error "non-rectangular 'tile'" "" { target c } } */
+  for (int i = 0; i < 100; ++i) /* { dg-error "non-rectangular 'tile'" "" { target c++ } } */
+    for (int j = 0; j < i; ++j)
+      dummy (i);
+
+  [[omp::sequence (directive (for collapse(2)),
+		   directive (tile sizes(1)))]] /* { dg-error "'tile' construct generates 1 loops with canonical form but 2 loops are needed" } */
+  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 "'tile' construct generates 2 loops with canonical form but 3 loops are needed" } */
+  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)))]] /* { dg-error "'tile' construct generates 1 loops with canonical form but 2 loops are needed" } */
+  for (int i = 0; i < 100; ++i)
+    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(2)),
+		   directive (tile sizes(1, 2)),
+		   directive (tile sizes(1)))]] /* { dg-error "'tile' construct generates 1 loops with canonical form but 2 loops are needed" } */
+  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 "'tile' construct generates 2 loops with canonical form but 3 loops are needed" } */
+		   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(5, 6)), /* { dg-error "'tile' construct generates 2 loops with canonical form but 3 loops are needed" } */
+		   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);
+}
--- gcc/testsuite/c-c++-common/gomp/attrs-unroll-1.c.jj	2024-06-04 13:30:00.680872752 +0200
+++ gcc/testsuite/c-c++-common/gomp/attrs-unroll-1.c	2024-06-04 13:30:00.680872752 +0200
@@ -0,0 +1,135 @@ 
+/* { dg-do compile { target { c || c++11 } } } */
+/* { dg-additional-options "-std=c23" { target c } } */
+
+extern void dummy (int);
+
+void
+test1 (void)
+{
+  [[omp::directive (unroll partial)]]
+  for (int i = 0; i < 100; ++i)
+    dummy (i);
+}
+
+void
+test2 (void)
+{
+  [[omp::directive (unroll partial(10))]]
+  for (int i = 0; i < 100; ++i)
+    dummy (i);
+}
+
+void
+test3 (void)
+{
+  [[omp::directive (unroll full)]]
+  for (int i = 0; i < 100; ++i)
+    dummy (i);
+}
+
+void
+test4 (void)
+{
+  [[omp::directive (unroll full)]]
+  for (int i = 0; i > 100; ++i)
+    dummy (i);
+}
+
+void
+test5 (void)
+{
+  [[omp::directive (unroll full)]]
+  for (int i = 1; i <= 100; ++i)
+    dummy (i);
+}
+
+void
+test6 (void)
+{
+  [[omp::directive (unroll full)]]
+  for (int i = 200; i >= 100; i--)
+    dummy (i);
+}
+
+void
+test7 (void)
+{
+  [[omp::directive (unroll full)]]
+  for (int i = -100; i > 100; ++i)
+    dummy (i);
+}
+
+void
+test8 (void)
+{
+  [[omp::directive (unroll full)]]
+  for (int i = 100; i > -200; --i)
+    dummy (i);
+}
+
+void
+test9 (void)
+{
+  [[omp::directive (unroll full)]]
+  for (int i = -300; i != 100; ++i)
+    dummy (i);
+}
+
+void
+test10 (void)
+{
+  [[omp::directive (unroll full)]]
+  for (int i = -300; i != 100; ++i)
+    dummy (i);
+}
+
+void
+test12 (void)
+{
+  [[omp::sequence (directive (unroll full),
+		   directive (unroll partial),
+		   directive (unroll partial))]]
+  for (int i = -300; i != 100; ++i)
+    dummy (i);
+}
+
+void
+test13 (void)
+{
+  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 (void)
+{
+  [[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 (void)
+{
+  [[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);
+    }
+ }
--- gcc/testsuite/c-c++-common/gomp/attrs-unroll-2.c.jj	2024-06-04 13:30:00.693872579 +0200
+++ gcc/testsuite/c-c++-common/gomp/attrs-unroll-2.c	2024-06-04 13:30:00.693872579 +0200
@@ -0,0 +1,81 @@ 
+/* { dg-do compile { target { c || c++11 } } } */
+/* { dg-additional-options "-std=c23" { target c } } */
+/* { dg-prune-output "error: invalid controlling predicate" } */
+
+extern void dummy (int);
+
+void
+test (void)
+{
+  [[omp::sequence (directive (unroll partial),
+		   directive (unroll full))]] /* { dg-error "generated loop of 'unroll' construct without 'partial' clause does not have canonical form" } */
+  for (int i = -300; i != 100; ++i)
+    dummy (i);
+
+  [[omp::sequence (directive (for),
+		   directive (unroll full), /* { dg-error "generated loop of 'unroll' construct without 'partial' clause does not have canonical form" } */
+		   directive (unroll partial))]]
+  for (int i = -300; i != 100; ++i)
+    dummy (i);
+
+  [[omp::sequence (directive (for),
+		   directive (unroll full),
+		   directive (unroll full))]] /* { dg-error "generated loop of 'unroll' construct without 'partial' clause does not have canonical form" } */
+  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 "generated loop of 'unroll' construct without 'partial' clause does not have canonical form" } */
+  for (int i = -300; i != 100; ++i)
+    dummy (i);
+
+  [[omp::sequence (directive (for),
+		   directive (unroll))]] /* { dg-error "generated loop of 'unroll' construct without 'partial' clause does not have canonical form" } */
+  for (int i = -300; i != 100; ++i)
+    dummy (i);
+
+  int i;
+
+  [[omp::sequence (directive (for), /* { dg-error "generated loop of 'unroll' construct without 'partial' clause does not have canonical form" "" { target *-*-* } .+1 } */
+		   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)), /* { dg-error "generated loop of 'unroll' construct without 'partial' clause does not have canonical form" "" { target *-*-* } .+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)), /* { dg-error "generated loop of 'unroll' construct without 'partial' clause does not have canonical form" "" { target *-*-* } .+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 "'unroll' construct with 'partial' clause generates just one loop with canonical form but 2 loops are needed" } */
+  for (int i = 3; i < 10; ++i)
+    for (int j = -2; j < 7; ++j)
+      sum++;
+}
--- gcc/testsuite/c-c++-common/gomp/attrs-unroll-3.c.jj	2024-06-04 13:30:00.691872606 +0200
+++ gcc/testsuite/c-c++-common/gomp/attrs-unroll-3.c	2024-06-04 13:30:00.691872606 +0200
@@ -0,0 +1,18 @@ 
+/* { dg-do compile { target { c || c++11 } } } */
+/* { dg-additional-options "-std=c23" { target c } } */
+/* { dg-additional-options "-O2 -fdump-tree-original -fdump-tree-gimple -fdump-tree-cunroll" } */
+
+extern void dummy (int);
+
+void
+test1 (void)
+{
+  [[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 "cunroll" } } */
+/* { dg-final { scan-tree-dump "#pragma omp unroll" "original" } } */
+/* { dg-final { scan-tree-dump-not "#pragma omp" "gimple" } } */
--- gcc/testsuite/c-c++-common/gomp/attrs-unroll-inner-1.c.jj	2024-06-04 13:30:00.679872765 +0200
+++ gcc/testsuite/c-c++-common/gomp/attrs-unroll-inner-1.c	2024-06-04 13:30:00.679872765 +0200
@@ -0,0 +1,14 @@ 
+/* { dg-do compile { target { c || c++11 } } } */
+/* { dg-additional-options "-std=c23" { target c } } */
+
+extern void dummy (int);
+
+void
+test (void)
+{
+  [[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);
+}
--- gcc/testsuite/c-c++-common/gomp/attrs-unroll-inner-2.c.jj	2024-06-04 13:30:00.679872765 +0200
+++ gcc/testsuite/c-c++-common/gomp/attrs-unroll-inner-2.c	2024-06-04 13:30:00.679872765 +0200
@@ -0,0 +1,21 @@ 
+/* { dg-do compile { target { c || c++11 } } } */
+/* { dg-additional-options "-std=c23" { target c } } */
+
+extern void dummy (int);
+
+void
+test (void)
+{
+  #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)
+    [[omp::directive (tile, sizes(2, 3))]]
+    for (int j = 0; j != 100; ++j)
+      for (int k = 0; k != 100; ++k)
+	dummy (i);
+}
--- gcc/testsuite/c-c++-common/gomp/attrs-unroll-inner-3.c.jj	2024-06-04 13:30:00.680872752 +0200
+++ gcc/testsuite/c-c++-common/gomp/attrs-unroll-inner-3.c	2024-06-04 13:30:00.680872752 +0200
@@ -0,0 +1,17 @@ 
+/* { dg-do compile { target { c || c++11 } } } */
+/* { dg-additional-options "-std=c23" { target c } } */
+
+/* Test that omp::sequence is handled properly in a loop nest, but that
+   invalid attribute specifiers are rejected.  */
+
+extern void dummy (int);
+
+void
+test1 (void)
+{
+  [[omp::directive (target parallel for collapse(2))]]
+  for (int i = -300; i != 100; ++i)
+    [[omp::sequence (directive (unroll, partial))]]
+    for (int j = 0; j != 100; ++j)
+      dummy (i);
+}
--- gcc/testsuite/c-c++-common/gomp/attrs-unroll-inner-4.c.jj	2024-06-04 13:30:00.679872765 +0200
+++ gcc/testsuite/c-c++-common/gomp/attrs-unroll-inner-4.c	2024-06-04 13:30:00.679872765 +0200
@@ -0,0 +1,14 @@ 
+/* { dg-do compile { target { c || c++11 } } } */
+/* { dg-additional-options "-std=c23" { target c } } */
+
+extern void dummy (int);
+
+void
+test (void)
+{
+  [[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) /* { dg-error "not enough nested loops" } */
+      dummy (i);
+}
--- gcc/testsuite/c-c++-common/gomp/attrs-unroll-inner-5.c.jj	2024-06-04 13:30:00.692872592 +0200
+++ gcc/testsuite/c-c++-common/gomp/attrs-unroll-inner-5.c	2024-06-04 13:30:00.692872592 +0200
@@ -0,0 +1,61 @@ 
+/* { dg-do compile { target { c || c++11 } } } */
+/* { dg-additional-options "-std=c23" { target c } } */
+
+/* Test that omp::sequence is handled properly in a loop nest, but that
+   invalid attribute specifiers are rejected.  */
+
+extern void dummy (int);
+
+void
+test2 (void)
+{
+  [[omp::directive (target parallel for collapse(2))]]
+  for (int i = -300; i != 100; ++i)
+    [[omp::directive (masked)]]  /* { dg-error "expected 'for' loop or OpenMP loop transformation construct" } */
+    for (int j = 0; j != 100; ++j) /* { dg-error "loop not permitted" } */
+      dummy (i);
+}
+
+void
+test3 (void)
+{
+  [[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 (void)
+{
+  [[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); /* { dg-error "declared" } */
+}
+
+void
+test5 (void)
+{
+  [[omp::directive (target parallel for collapse(2))]]
+  for (int i = -300; i != 100; ++i)
+    [[omp::sequence (directive (masked),  /* { dg-error "expected 'for' loop or OpenMP loop transformation construct" } */
+		     directive (unroll, partial))]]
+    for (int j = 0; j != 100; ++j)
+      dummy (i);
+}
+
+void
+test6 (void)
+{
+  [[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);
+}
--- gcc/testsuite/c-c++-common/gomp/imperfect-attributes.c.jj	2024-06-04 13:18:26.095105475 +0200
+++ gcc/testsuite/c-c++-common/gomp/imperfect-attributes.c	2024-06-04 13:30:00.690872619 +0200
@@ -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)
 {
--- gcc/testsuite/c-c++-common/gomp/imperfect-loop-nest.c.jj	2024-06-04 13:30:00.679872765 +0200
+++ gcc/testsuite/c-c++-common/gomp/imperfect-loop-nest.c	2024-06-04 20:10:31.832210914 +0200
@@ -0,0 +1,12 @@ 
+void
+test (void)
+{
+#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++);
+      }
+}
--- gcc/testsuite/c-c++-common/gomp/ordered-5.c.jj	2024-06-04 13:30:00.693872579 +0200
+++ gcc/testsuite/c-c++-common/gomp/ordered-5.c	2024-06-04 13:30:00.693872579 +0200
@@ -0,0 +1,140 @@ 
+/* { dg-do compile } */
+/* { dg-options "-fopenmp" } */
+
+void
+foo (void)
+{
+  int i;
+  #pragma omp for ordered			/* { dg-error "'ordered' clause used with generated loops" } */
+  #pragma omp tile sizes (2)
+  for (i = 0; i < 64; i++)
+    {
+      #pragma omp ordered
+      ;
+    }
+  #pragma omp for ordered			/* { dg-error "'ordered' clause used with generated loops" } */
+  #pragma omp tile sizes (2)
+  for (i = 0; i < 64; i++)
+    {
+      #pragma omp ordered threads
+      ;
+    }
+  #pragma omp for simd ordered			/* { dg-error "'ordered' clause used with generated loops" "" { target c } } */
+  #pragma omp tile sizes (2)			/* { dg-error "'ordered' clause used with generated loops" "" { target c++ } } */
+  for (i = 0; i < 64; i++)
+    {
+      #pragma omp ordered threads, simd
+      ;
+    }
+  #pragma omp for simd ordered(1)		/* { dg-error "'ordered' clause used with generated loops" "" { target c } } */
+  #pragma omp tile sizes (2)			/* { dg-error "'ordered' clause used with generated loops" "" { target c++ } } */
+  for (i = 0; i < 64; i++)
+    {
+      #pragma omp ordered depend(sink: i - 1)
+      #pragma omp ordered depend(source)
+    }
+  #pragma omp for simd ordered(1)		/* { dg-error "'ordered' clause used with generated loops" "" { target c } } */
+  #pragma omp tile sizes (2)			/* { dg-error "'ordered' clause used with generated loops" "" { target c++ } } */
+  for (i = 0; i < 64; i++)
+    {
+      #pragma omp ordered doacross(sink: i - 1)
+      #pragma omp ordered doacross(source:omp_cur_iteration)
+    }
+  #pragma omp parallel for ordered		/* { dg-error "'ordered' clause used with generated loops" "" { target c } } */
+  #pragma omp tile sizes (2)			/* { dg-error "'ordered' clause used with generated loops" "" { target c++ } } */
+  for (i = 0; i < 64; i++)
+    {
+      #pragma omp ordered depend(sink: i - 1)
+      #pragma omp ordered depend(source)
+    }
+  #pragma omp parallel for ordered		/* { dg-error "'ordered' clause used with generated loops" "" { target c } } */
+  #pragma omp tile sizes (2)			/* { dg-error "'ordered' clause used with generated loops" "" { target c++ } } */
+  for (i = 0; i < 64; i++)
+    {
+      #pragma omp ordered doacross(sink: i - 1)
+      #pragma omp ordered doacross(source:)
+    }
+  #pragma omp for ordered(1)			/* { dg-error "'ordered' clause used with generated loops" } */
+  #pragma omp tile sizes (2)
+  for (i = 0; i < 64; i++)
+    {
+      #pragma omp ordered depend(sink: i - 1)
+      #pragma omp ordered depend(source)
+    }
+  #pragma omp for ordered(1)			/* { dg-error "'ordered' clause used with generated loops" } */
+  #pragma omp tile sizes (2)
+  for (i = 0; i < 64; i++)
+    {
+      #pragma omp ordered doacross(sink: i - 1)
+      #pragma omp ordered doacross(source:omp_cur_iteration)
+    }
+}
+
+void
+bar (void)
+{
+  int i;
+  #pragma omp for ordered			/* { dg-error "'ordered' clause used with generated loops" } */
+  #pragma omp unroll partial (2)
+  for (i = 0; i < 64; i++)
+    {
+      #pragma omp ordered
+      ;
+    }
+  #pragma omp for ordered			/* { dg-error "'ordered' clause used with generated loops" } */
+  #pragma omp unroll partial (2)
+  for (i = 0; i < 64; i++)
+    {
+      #pragma omp ordered threads
+      ;
+    }
+  #pragma omp for simd ordered			/* { dg-error "'ordered' clause used with generated loops" "" { target c } } */
+  #pragma omp unroll partial (2)		/* { dg-error "'ordered' clause used with generated loops" "" { target c++ } } */
+  for (i = 0; i < 64; i++)
+    {
+      #pragma omp ordered threads, simd
+      ;
+    }
+  #pragma omp for simd ordered(1)		/* { dg-error "'ordered' clause used with generated loops" "" { target c } } */
+  #pragma omp unroll partial (2)		/* { dg-error "'ordered' clause used with generated loops" "" { target c++ } } */
+  for (i = 0; i < 64; i++)
+    {
+      #pragma omp ordered depend(sink: i - 1)
+      #pragma omp ordered depend(source)
+    }
+  #pragma omp for simd ordered(1)		/* { dg-error "'ordered' clause used with generated loops" "" { target c } } */
+  #pragma omp unroll partial (2)		/* { dg-error "'ordered' clause used with generated loops" "" { target c++ } } */
+  for (i = 0; i < 64; i++)
+    {
+      #pragma omp ordered doacross(sink: i - 1)
+      #pragma omp ordered doacross(source:omp_cur_iteration)
+    }
+  #pragma omp parallel for ordered		/* { dg-error "'ordered' clause used with generated loops" "" { target c } } */
+  #pragma omp unroll partial (2)		/* { dg-error "'ordered' clause used with generated loops" "" { target c++ } } */
+  for (i = 0; i < 64; i++)
+    {
+      #pragma omp ordered depend(sink: i - 1)
+      #pragma omp ordered depend(source)
+    }
+  #pragma omp parallel for ordered		/* { dg-error "'ordered' clause used with generated loops" "" { target c } } */
+  #pragma omp unroll partial (2)		/* { dg-error "'ordered' clause used with generated loops" "" { target c++ } } */
+  for (i = 0; i < 64; i++)
+    {
+      #pragma omp ordered doacross(sink: i - 1)
+      #pragma omp ordered doacross(source:)
+    }
+  #pragma omp for ordered(1)			/* { dg-error "'ordered' clause used with generated loops" } */
+  #pragma omp unroll partial (2)
+  for (i = 0; i < 64; i++)
+    {
+      #pragma omp ordered depend(sink: i - 1)
+      #pragma omp ordered depend(source)
+    }
+  #pragma omp for ordered(1)			/* { dg-error "'ordered' clause used with generated loops" } */
+  #pragma omp unroll partial (2)
+  for (i = 0; i < 64; i++)
+    {
+      #pragma omp ordered doacross(sink: i - 1)
+      #pragma omp ordered doacross(source:omp_cur_iteration)
+    }
+}
--- gcc/testsuite/c-c++-common/gomp/scan-7.c.jj	2024-06-05 15:08:40.588600390 +0200
+++ gcc/testsuite/c-c++-common/gomp/scan-7.c	2024-06-05 15:22:09.375860103 +0200
@@ -0,0 +1,55 @@ 
+int a;
+
+void
+foo (int *c, int *d)
+{
+  #pragma omp for reduction (inscan, +: a) /* { dg-error "'a' specified in 'inscan' 'reduction' clause but not in 'scan' directive clause" } */
+  #pragma omp tile sizes (2)
+  for (int i = 0; i < 64; ++i)
+    {
+      a = a + c[i];
+      #pragma omp scan inclusive (a) /* { dg-error "'#pragma omp scan' may only be used in a loop construct with 'inscan' 'reduction' clause" } */
+      d[i] = a;
+    }
+}
+
+void
+bar (int **c, int **d)
+{
+  #pragma omp for collapse (2) reduction (inscan, +: a) /* { dg-error "'a' specified in 'inscan' 'reduction' clause but not in 'scan' directive clause" } */
+  for (int i = 0; i < 64; ++i)
+    #pragma omp tile sizes (3)
+    for (int j = 0; j < 64; ++j)
+      {
+	d[i][j] = a;
+	#pragma omp scan exclusive (a) /* { dg-error "'#pragma omp scan' may only be used in a loop construct with 'inscan' 'reduction' clause" } */
+	a = a + c[i][j];
+      }
+}
+
+void
+baz (int *c, int *d)
+{
+  #pragma omp for reduction (inscan, +: a) /* { dg-error "'a' specified in 'inscan' 'reduction' clause but not in 'scan' directive clause" } */
+  #pragma omp unroll partial (2)
+  for (int i = 0; i < 64; ++i)
+    {
+      d[i] = a;
+      #pragma omp scan exclusive (a) /* { dg-error "'#pragma omp scan' may only be used in a loop construct with 'inscan' 'reduction' clause" } */
+      a = a + c[i];
+    }
+}
+
+void
+qux (int **c, int **d)
+{
+  #pragma omp for collapse (2) reduction (inscan, +: a) /* { dg-error "'a' specified in 'inscan' 'reduction' clause but not in 'scan' directive clause" } */
+  for (int i = 0; i < 64; ++i)
+    #pragma omp unroll partial (3)
+    for (int j = 0; j < 64; ++j)
+      {
+	a = a + c[i][j];
+	#pragma omp scan inclusive (a) /* { dg-error "'#pragma omp scan' may only be used in a loop construct with 'inscan' 'reduction' clause" } */
+	d[i][j] = a;
+      }
+}
--- gcc/testsuite/c-c++-common/gomp/tile-1.c.jj	2024-06-04 13:30:00.692872592 +0200
+++ gcc/testsuite/c-c++-common/gomp/tile-1.c	2024-06-04 13:30:00.692872592 +0200
@@ -0,0 +1,54 @@ 
+extern void dummy (int);
+
+void
+test (void)
+{
+  #pragma omp tile sizes(1)
+  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, 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 partial
+  #pragma omp tile sizes(1)
+  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)
+  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);
+    }
+}
--- gcc/testsuite/c-c++-common/gomp/tile-2.c.jj	2024-06-04 13:30:00.694872566 +0200
+++ gcc/testsuite/c-c++-common/gomp/tile-2.c	2024-06-04 13:30:00.694872566 +0200
@@ -0,0 +1,67 @@ 
+extern void dummy (int);
+
+void
+test (void)
+{
+  #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(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, 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 partial
+  #pragma omp tile sizes(1)
+  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)
+  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)
+  for (int i = 0; i < 100; ++i)
+    dummy (i);
+}
--- gcc/testsuite/c-c++-common/gomp/tile-3.c.jj	2024-06-04 13:30:00.691872606 +0200
+++ gcc/testsuite/c-c++-common/gomp/tile-3.c	2024-06-04 13:30:00.691872606 +0200
@@ -0,0 +1,44 @@ 
+extern void dummy (int);
+
+void
+test (void)
+{
+  #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, 2)
+  for (int i = 0; i < 100; ++i)
+    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(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, 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);
+}
--- gcc/testsuite/c-c++-common/gomp/tile-4.c.jj	2024-06-04 13:30:00.691872606 +0200
+++ gcc/testsuite/c-c++-common/gomp/tile-4.c	2024-06-04 13:30:00.691872606 +0200
@@ -0,0 +1,320 @@ 
+/* { dg-do run } */
+/* { dg-options "-O0 -fopenmp-simd" } */
+
+#include <stdio.h>
+
+#define ASSERT_EQ(var, val) \
+  do									\
+    {									\
+      if ((var) != (val))						\
+	{								\
+	  fprintf (stderr, "%s:%d: Unexpected value %d, expected %d\n",	\
+		   __FILE__, __LINE__, (var), (val));			\
+	  __builtin_abort ();						\
+	}								\
+    }									\
+  while (0)
+
+int
+test1 (void)
+{
+  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 (void)
+{
+  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 (void)
+{
+  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 (void)
+{
+  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 (void)
+{
+  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 (void)
+{
+  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 (void)
+{
+  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;
+    }
+
+  /* No iteration expected */
+  return iter;
+}
+
+int
+test8 (void)
+{
+  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 (void)
+{
+  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 (void)
+{
+  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 (void)
+{
+  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 (void)
+{
+  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 (void)
+{
+  int iter = -5;
+  long long unsigned int i = 42;
+#pragma omp tile sizes(15)
+  for (int i = -5; i < 5; i = i + 3)
+    {
+      ASSERT_EQ (i, iter);
+      iter += 3;
+    }
+
+  ASSERT_EQ (i, 42);
+  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++;
+
+  if (init)
+    ASSERT_EQ (i, 2 * init + (init == 5));
+  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);
+
+  last_iter = test11 ();
+  ASSERT_EQ (last_iter, -5);
+
+  last_iter = test12 ();
+  ASSERT_EQ (last_iter, 5);
+
+  last_iter = test13 ();
+  ASSERT_EQ (last_iter, 7);
+
+  last_iter = test14 (0, 1);
+  ASSERT_EQ (last_iter, 0);
+
+  last_iter = test14 (0, -1);
+  ASSERT_EQ (last_iter, 0);
+
+  last_iter = test14 (8, 2);
+  ASSERT_EQ (last_iter, 12);
+
+  last_iter = test14 (5, 3);
+  ASSERT_EQ (last_iter, 7);
+
+  last_iter = test15 (8, -1);
+  ASSERT_EQ (last_iter, 8);
+
+  last_iter = test15 (8, -2);
+  ASSERT_EQ (last_iter, 8);
+
+  last_iter = test15 (5, -3);
+  ASSERT_EQ (last_iter, 5);
+  return 0;
+}
--- gcc/testsuite/c-c++-common/gomp/tile-5.c.jj	2024-06-04 13:30:00.691872606 +0200
+++ gcc/testsuite/c-c++-common/gomp/tile-5.c	2024-06-04 13:30:00.691872606 +0200
@@ -0,0 +1,169 @@ 
+/* { dg-do run } */
+/* { dg-options "-O0 -fopenmp-simd" } */
+
+#include <stdio.h>
+
+#define ASSERT_EQ(var, val) \
+  do									\
+    {									\
+      if ((var) != (val))						\
+	{								\
+	  fprintf (stderr, "%s:%d: Unexpected value %d, expected %d\n",	\
+		   __FILE__, __LINE__, (var), (val));			\
+	  __builtin_abort ();						\
+	}								\
+    }									\
+  while (0)
+
+#define ASSERT_EQ_PTR(var, ptr) \
+  do									\
+    {									\
+      if ((var) != (ptr))						\
+	{								\
+	  fprintf (stderr, "%s:%d: Unexpected value %p, expected %p\n",	\
+		   __FILE__, __LINE__, (var), (ptr));			\
+	  __builtin_abort ();						\
+	}								\
+    }									\
+  while (0)
+
+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[15])
+{
+  int iter = 0;
+  int *i;
+  #pragma omp tile sizes(3)
+  for (i = data + 14; i >= data + 5; i--)
+    {
+      ASSERT_EQ (*i, data[14 - iter]);
+      iter++;
+    }
+
+  ASSERT_EQ_PTR (i, data + 4);
+  return iter;
+}
+
+int
+test6 (int data[15])
+{
+  int iter = 0;
+  int *i;
+  #pragma omp tile sizes(3)
+  for (i = data + 14; i > data + 4; i--)
+    {
+      ASSERT_EQ (*i, data[14 - iter]);
+      iter++;
+    }
+
+  ASSERT_EQ_PTR (i, data + 4);
+  return iter;
+}
+
+int
+test7 (int data[15])
+{
+  int iter = 0;
+  #pragma omp tile sizes(1)
+  for (int *i = data + 14; i != data + 4; i--)
+    {
+      ASSERT_EQ (*i, data[14 - iter]);
+      iter++;
+    }
+
+  return iter;
+}
+
+int
+main ()
+{
+  int iter_count;
+  int data[10] = { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9 };
+  int data2[15] = { -1, -1, -1, -1, -1, 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 (data2);
+  ASSERT_EQ (iter_count, 10);
+
+  iter_count = test6 (data2);
+  ASSERT_EQ (iter_count, 10);
+
+  iter_count = test7 (data2);
+  ASSERT_EQ (iter_count, 10);
+}
--- gcc/testsuite/c-c++-common/gomp/tile-6.c.jj	2024-06-04 13:30:00.693872579 +0200
+++ gcc/testsuite/c-c++-common/gomp/tile-6.c	2024-06-04 13:30:00.693872579 +0200
@@ -0,0 +1,27 @@ 
+/* { dg-do run } */
+/* { dg-options "-O0 -fopenmp-simd" } */
+
+int
+test1 (void)
+{
+  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)
+    __builtin_abort ();
+  return 0;
+}
--- gcc/testsuite/c-c++-common/gomp/tile-7.c.jj	2024-06-04 13:30:00.691872606 +0200
+++ gcc/testsuite/c-c++-common/gomp/tile-7.c	2024-06-04 13:30:00.691872606 +0200
@@ -0,0 +1,51 @@ 
+/* { dg-do run } */
+/* { dg-options "-O0 -fopenmp-simd" } */
+
+#include <stdio.h>
+
+#define ASSERT_EQ(var, val) \
+  do									\
+    {									\
+      if ((var) != (val))						\
+	{								\
+	  fprintf (stderr, "%s:%d: Unexpected value %d, expected %d\n",	\
+		   __FILE__, __LINE__, (var), (val));			\
+	  __builtin_abort ();						\
+	}								\
+    }									\
+  while (0)
+
+#define ASSERT_EQ_PTR(var, ptr) \
+  do									\
+    {									\
+      if ((var) != (ptr))						\
+	{								\
+	  fprintf (stderr, "%s:%d: Unexpected value %p, expected %p\n",	\
+		   __FILE__, __LINE__, (var), (ptr));			\
+	  __builtin_abort ();						\
+	}								\
+    }									\
+  while (0)
+
+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;
+}
--- gcc/testsuite/c-c++-common/gomp/tile-8.c.jj	2024-06-04 13:30:00.680872752 +0200
+++ gcc/testsuite/c-c++-common/gomp/tile-8.c	2024-06-04 13:30:00.680872752 +0200
@@ -0,0 +1,50 @@ 
+/* { dg-do run } */
+/* { dg-options "-O0 -fopenmp-simd" } */
+
+#include <stdio.h>
+
+#define ASSERT_EQ(var, val) \
+  do									\
+    {									\
+      if ((var) != (val))						\
+	{								\
+	  fprintf (stderr, "%s:%d: Unexpected value %d, expected %d\n",	\
+		   __FILE__, __LINE__, (var), (val));			\
+	  __builtin_abort ();						\
+	}								\
+    }									\
+  while (0)
+
+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;
+}
--- gcc/testsuite/c-c++-common/gomp/tile-9.c.jj	2024-06-04 13:30:00.692872592 +0200
+++ gcc/testsuite/c-c++-common/gomp/tile-9.c	2024-06-04 13:30:00.692872592 +0200
@@ -0,0 +1,19 @@ 
+/* { dg-additional-options "-O2" } */
+
+int foo (int);
+
+void
+mult (float *matrix1, float *matrix2, float *result,
+      unsigned dim0, unsigned dim1, unsigned dim2, unsigned dim3)
+{
+  #pragma omp taskloop collapse(3)
+  for (unsigned i = 0; i < dim0; i++)
+    #pragma omp tile sizes(2, 2)
+    #pragma omp tile sizes(2, 2)
+    #pragma omp tile sizes(2, 2)
+    for (unsigned j = 0; j < dim1; j += dim2 * foo (0))
+      #pragma omp unroll partial(2)
+      #pragma omp unroll partial(2)
+      for (unsigned k = 0; k < dim1; k += dim3 * foo (1))
+	result[i * dim1 + j] += matrix1[i * dim1 + k] * matrix2[k * dim0 + j];
+}
--- gcc/testsuite/c-c++-common/gomp/tile-10.c.jj	2024-06-04 13:30:00.692872592 +0200
+++ gcc/testsuite/c-c++-common/gomp/tile-10.c	2024-06-04 13:30:00.692872592 +0200
@@ -0,0 +1,90 @@ 
+void
+test1 (void)
+{
+  int i, j, k, l;
+  #pragma omp for collapse(4) private (i, j, k, l) /* { dg-error "the same loop iteration variables 'i' used in multiple associated loops" "" { target c } } */
+  for (i = 0; i < 1024; ++i) /* { dg-error "the same loop iteration variables 'i' used in multiple associated loops" "" { target c++ } } */
+    #pragma omp tile sizes (2, 2, 2)
+    for (j = 0; j < 1024; ++j)
+      #pragma omp tile sizes (3, 3)
+      for (k = 0; k < 1024; ++k)
+	#pragma omp tile sizes (4)
+	for (i = 0; i < 1024; ++i)
+	  ;
+}
+
+void
+test2 (void)
+{
+  long long i;
+  int j, k, l;
+  #pragma omp for collapse(4) private (i, j, k, l) /* { dg-error "outer iteration variable 'i' used in initializer expression has type other than 'int'" "" { target c } } */
+  for (i = 0; i < 1024; ++i) /* { dg-error "outer iteration variable 'i' used in initializer expression has type other than 'int'" "" { target c++ } } */
+    #pragma omp tile sizes (2, 2, 2)
+    for (j = 0; j < 1024; ++j)
+      #pragma omp tile sizes (3, 3)
+      for (k = 0; k < 1024; ++k)
+	#pragma omp tile sizes (4)
+	for (l = i; l < 1024; ++l)
+	  ;
+}
+
+void
+test3 (void)
+{
+  int i, j, k, l;
+  #pragma omp for collapse(4) private (i, j, k, l)
+  for (i = 0; i < 1024; ++i)
+    #pragma omp tile sizes (2, 2, 2)
+    for (j = 0; j < 1024; ++j)
+      #pragma omp tile sizes (3, 3)
+      for (k = 0; k < 1024; ++k)
+	#pragma omp tile sizes (4)
+	for (l = 0; l < 7 * i * i; ++l) /* { dg-error "condition expression refers to iteration variable 'i'" } */
+	  ;
+}
+
+void
+test4 (void)
+{
+  int i, j, k, l;
+  #pragma omp for collapse(4) private (i, j, k, l)
+  for (i = 0; i < 1024; ++i)
+    #pragma omp tile sizes (2, 2, 2)
+    for (j = 0; j < 1024; ++j)
+      #pragma omp tile sizes (3, 3)
+      for (k = 0; k < 1024; ++k)
+	#pragma omp tile sizes (4)
+	for (l = i * i; l < 1024; ++l) /* { dg-error "initializer expression refers to iteration variable 'i'" } */
+	  ;
+}
+
+void
+test5 (void)
+{
+  int i, j, k, l;
+  #pragma omp for collapse(4) private (i, j, k, l)
+  for (i = 0; i < 1024; ++i)
+    #pragma omp tile sizes (2, 2, 2) /* { dg-error "increment expression refers to iteration variable 'j'" "" { target c } } */
+    for (j = 0; j < 1024; ++j) /* { dg-error "increment expression refers to iteration variable 'j'" "" { target c++ } } */
+      #pragma omp tile sizes (3, 3)
+      for (k = 0; k < 1024; ++k)
+	#pragma omp tile sizes (4)
+	for (l = 0; l < 1024; l += j)
+	  ;
+}
+
+void
+test6 (void)
+{
+  int i, j, k, l;
+  #pragma omp for collapse(4) private (i, j, k, l)
+  for (i = 0; i < 1024; ++i)
+    #pragma omp tile sizes (2, 2, 2)
+    for (j = 0; j < 1024; ++j)
+      #pragma omp tile sizes (3, 3)
+      for (k = 0; k < 1024; ++k)
+	#pragma omp tile sizes (4)
+	for (l = 0; l < i - 2; ++l) /* { dg-message "non-rectangular loops from generated loops unsupported" } */
+	  ;
+}
--- gcc/testsuite/c-c++-common/gomp/tile-11.c.jj	2024-06-04 13:30:00.680872752 +0200
+++ gcc/testsuite/c-c++-common/gomp/tile-11.c	2024-06-04 13:30:00.680872752 +0200
@@ -0,0 +1,110 @@ 
+extern void dummy (int);
+
+void
+test (void)
+{
+  #pragma omp tile sizes(0) /* { dg-error "'sizes' argument needs positive integral constant" } */
+  for (int i = 0; i < 100; ++i)
+    dummy (i);
+
+  #pragma omp tile sizes(-1) /* { dg-error "'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, 2)
+  #pragma omp tile sizes(1) /* { dg-error "'tile' construct generates 1 loops with canonical form but 2 loops are needed" } */
+  for (int i = 0; i < 100; ++i)
+  for (int j = 0; j < 100; ++j)
+      dummy (i);
+
+  #pragma omp tile sizes(1)
+  #pragma omp unroll partia /* { dg-error "expected an OpenMP clause before 'partia'" } */
+  #pragma omp tile sizes(1) /* { dg-error "generated loop of 'unroll' construct without 'partial' clause does not have canonical form" "" { target *-*-* } .-1 } */
+  for (int i = 0; i < 100; ++i)
+    dummy (i);
+
+  #pragma omp tile sizes(1)
+  #pragma omp unroll /* { dg-error "generated loop of 'unroll' construct without 'partial' clause does not have canonical form" } */
+  for (int i = 0; i < 100; ++i)
+    dummy (i);
+
+  #pragma omp tile sizes(1)
+  #pragma omp unroll full /* { dg-error "generated loop of 'unroll' construct without 'partial' clause does not have canonical form" } */
+  for (int i = 0; i < 100; ++i)
+    dummy (i);
+
+  #pragma omp tile sizes(8,8)
+  #pragma omp unroll partial /* { dg-error "'unroll' construct with 'partial' clause generates just one loop with canonical form but 2 loops are needed" } */
+  #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 "'unroll' construct with 'partial' clause generates just one loop with canonical form but 2 loops are needed" } */
+  for (int i = 0; i < 100; ++i)
+    dummy (i);
+
+  #pragma omp tile sizes(1, 2) /* { dg-error "non-rectangular 'tile'" } */
+  for (int i = 0; i < 100; ++i)
+  for (int j = i; j < 100; ++j)
+    dummy (i);
+
+  #pragma omp tile sizes(1, 2) /* { dg-error "non-rectangular 'tile'" } */
+  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, 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 "'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 "'sizes' argument needs positive integral constant" } */
+  for (int i = 0; i < 100; ++i)
+    dummy (i);
+}
--- gcc/testsuite/c-c++-common/gomp/tile-12.c.jj	2024-06-04 13:30:00.693872579 +0200
+++ gcc/testsuite/c-c++-common/gomp/tile-12.c	2024-06-04 13:30:00.693872579 +0200
@@ -0,0 +1,134 @@ 
+extern void dummy (int);
+
+void
+test (void)
+{
+  #pragma omp parallel for
+  #pragma omp tile sizes(0) /* { dg-error "'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 "'sizes' argument needs positive integral constant" } */
+  for (int i = 0; i < 100; ++i)
+    dummy (i);
+
+  #pragma omp tile sizes(5, -42) /* { dg-error "'sizes' argument needs positive integral constant" } */
+  for (int i = 0; i < 100; ++i)
+    for (int j = 0; j < 100; ++j)
+      dummy (i);
+
+  #pragma omp tile sizes(0.5f) /* { dg-error "'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, 2)
+  #pragma omp tile sizes(1) /* { dg-error "'tile' construct generates 1 loops with canonical form but 2 loops are needed" } */
+  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)
+  #pragma omp unroll partia /* { dg-error "expected an OpenMP clause before 'partia'" } */
+  #pragma omp tile sizes(1) /* { dg-error "generated loop of 'unroll' construct without 'partial' clause does not have canonical form" "" { target *-*-* } .-1 } */
+  for (int i = 0; i < 100; ++i)
+    dummy (i);
+
+  #pragma omp parallel for
+  #pragma omp tile sizes(1)
+  #pragma omp unroll /* { dg-error "generated loop of 'unroll' construct without 'partial' clause does not have canonical form" } */
+  for (int i = 0; i < 100; ++i)
+    dummy (i);
+
+  #pragma omp parallel for
+  #pragma omp tile sizes(1)
+  #pragma omp unroll full /* { dg-error "generated loop of 'unroll' construct without 'partial' clause does not have canonical form" } */
+  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 "'unroll' construct with 'partial' clause generates just one loop with canonical form but 2 loops are needed" } */
+  #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 "'unroll' construct with 'partial' clause generates just one loop with canonical form but 2 loops are needed" } */
+  for (int i = 0; i < 100; ++i)
+    dummy (i);
+
+  #pragma omp parallel for
+  #pragma omp tile sizes(1, 2) /* { dg-error "non-rectangular 'tile'" "" { target c } } */
+  for (int i = 0; i < 100; ++i) /* { dg-error "non-rectangular 'tile'" "" { target c++ } } */
+    for (int j = i; j < 100; ++j)
+      dummy (i);
+
+  #pragma omp parallel for
+  #pragma omp tile sizes(1, 2) /* { dg-error "non-rectangular 'tile'" "" { target c } } */
+  for (int i = 0; i < 100; ++i) /* { dg-error "non-rectangular 'tile'" "" { target c++ } } */
+    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, 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 tile /* { dg-error "expected 'sizes'" } */
+  for (int i = 0; i < 100; ++i)
+    dummy (i);
+
+  #pragma omp tile sizes (1) sizes (1) /* { dg-error "expected end of line" } */
+  for (int i = 0; i < 100; ++i)
+    dummy (i);
+}
--- gcc/testsuite/c-c++-common/gomp/tile-13.c.jj	2024-06-04 13:30:00.692872592 +0200
+++ gcc/testsuite/c-c++-common/gomp/tile-13.c	2024-06-04 13:30:00.692872592 +0200
@@ -0,0 +1,70 @@ 
+extern void dummy (int);
+
+void
+test (void)
+{
+  #pragma omp for
+  #pragma omp tile sizes(1, 2) /* { dg-error "non-rectangular 'tile'" "" { target c } } */
+  for (int i = 0; i < 100; ++i) /* { dg-error "non-rectangular 'tile'" "" { target c++ } } */
+    for (int j = i; j < 100; ++j)
+      dummy (i);
+
+  #pragma omp for
+  #pragma omp tile sizes(1, 2) /* { dg-error "non-rectangular 'tile'" "" { target c } } */
+  for (int i = 0; i < 100; ++i) /* { dg-error "non-rectangular 'tile'" "" { target c++ } } */
+    for (int j = 0; j < i; ++j)
+      dummy (i);
+
+
+  #pragma omp for collapse(2)
+  #pragma omp tile sizes(1) /* { dg-error "'tile' construct generates 1 loops with canonical form but 2 loops are needed" } */
+  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 "'tile' construct generates 2 loops with canonical form but 3 loops are needed" } */
+  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) /* { dg-error "'tile' construct generates 1 loops with canonical form but 2 loops are needed" } */
+  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, 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(2)
+  #pragma omp tile sizes(1, 2)
+  #pragma omp tile sizes(1) /* { dg-error "'tile' construct generates 1 loops with canonical form but 2 loops are needed" } */
+  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 "'tile' construct generates 2 loops with canonical form but 3 loops are needed" } */
+  #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(5, 6) /* { dg-error "'tile' construct generates 2 loops with canonical form but 3 loops are needed" } */
+  #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);
+}
--- gcc/testsuite/c-c++-common/gomp/tile-14.c.jj	2024-06-04 13:30:00.693872579 +0200
+++ gcc/testsuite/c-c++-common/gomp/tile-14.c	2024-06-04 13:30:00.693872579 +0200
@@ -0,0 +1,53 @@ 
+extern void dummy (int);
+
+void
+test (void)
+{
+  #pragma omp for
+  #pragma omp tile sizes(2, 3)
+  #pragma omp tile sizes(3, 4, 5)
+  #pragma omp tile sizes(6, 7, 8, 9)
+  for (int i = 0; i < 100; ++i)
+    for (int j = 0; j < 100; ++j)
+      for (int k = 0; k < 100; ++k)
+	for (int l = 0; l < 100; ++l)
+	  dummy (i);
+
+  #pragma omp for
+  #pragma omp tile sizes(2, 3)
+  for (int i = 0; i < 100; ++i)
+    #pragma omp tile sizes(3, 4, 5)
+    for (int j = 0; j < 100; ++j)
+      #pragma omp tile sizes(6, 7, 8, 9)
+      for (int k = 0; k < 100; ++k)
+	for (int l = 0; l < 100; ++l)
+	  for (int m = 0; m < 100; ++m)
+	    #pragma omp unroll partial(2)
+	    for (int n = 0; n < 100; ++n)
+	      dummy (i);
+
+  #pragma omp for collapse(2)
+  for (int i = 0; i < 100; ++i)
+    #pragma omp tile sizes(2, 3)
+    #pragma omp tile sizes(3, 4, 5)
+    #pragma omp tile sizes(6, 7, 8, 9)
+    for (int j = 0; j < 100; ++j)
+      for (int k = 0; k < 100; ++k)
+	for (int l = 0; l < 100; ++l)
+	  for (int m = 0; m < 100; ++m)
+	    dummy (i);
+
+  #pragma omp for collapse(2)
+  for (int i = 0; i < 100; ++i)
+    #pragma omp tile sizes(2, 3)
+    for (int j = 0; j < 100; ++j)
+      #pragma omp tile sizes(3, 4, 5)
+      for (int k = 0; k < 100; ++k)
+	#pragma omp tile sizes(6, 7, 8, 9)
+	for (int l = 0; l < 100; ++l)
+	  for (int m = 0; m < 100; ++m)
+	    for (int n = 0; n < 100; ++n)
+	      #pragma omp unroll partial(2)
+	      for (int o = 0; o < 100; ++o)
+		dummy (i);
+}
--- gcc/testsuite/c-c++-common/gomp/tile-15.c.jj	2024-06-04 20:04:31.068990476 +0200
+++ gcc/testsuite/c-c++-common/gomp/tile-15.c	2024-06-05 15:30:39.224094012 +0200
@@ -0,0 +1,68 @@ 
+/* It isn't really clear what is supposed to be valid and what isn't when mixing
+   imperfectly nested loops with generated loops.  Sorry for now until that is
+   clarified.  */
+void foo (int, int);
+
+void
+bar (void)
+{
+  #pragma omp for collapse(2)		/* { dg-message "imperfectly nested loop using generated loops" "" { target c } } */
+  for (int i = 0; i < 32; ++i)		/* { dg-message "imperfectly nested loop using generated loops" "" { target c++ } } */
+    {
+      foo (i, -1);
+      #pragma omp tile sizes (2)
+      for (int j = 0; j < 32; ++j)
+	foo (i, j);
+      foo (i, -2);
+    }
+}
+
+void
+baz (void)
+{
+  #pragma omp for collapse(2)		/* { dg-message "imperfectly nested loop using generated loops" "" { target c } } */
+  for (int i = 0; i < 32; ++i)		/* { dg-message "imperfectly nested loop using generated loops" "" { target c++ } } */
+    {
+      foo (i, -1);
+      #pragma omp tile sizes (2, 2)
+      for (int j = 0; j < 32; ++j)
+	#pragma omp tile sizes (2, 2)
+	for (int k = 0; k < 32; ++k)
+	  for (int l = 0; l < 32; ++l)
+	    foo (i + k, j + l);
+      foo (i, -2);
+    }
+}
+
+void
+qux (void)
+{
+  #pragma omp for collapse(2)		/* { dg-message "imperfectly nested loop using generated loops" "" { target c } } */
+  for (int i = 0; i < 32; ++i)		/* { dg-message "imperfectly nested loop using generated loops" "" { target c++ } } */
+    {
+      int m = i + 6;
+      foo (i, -1);
+      #pragma omp tile sizes (2)
+      for (int j = m; j < 32; ++j)
+	foo (i, j);
+      foo (i, -2);
+    }
+}
+
+void
+freddy (void)
+{
+  #pragma omp for collapse(2)		/* { dg-message "imperfectly nested loop using generated loops" "" { target c } } */
+  for (int i = 0; i < 32; ++i)		/* { dg-message "imperfectly nested loop using generated loops" "" { target c++ } } */
+    {
+      int m = i + 6;
+      foo (i, -1);
+      #pragma omp tile sizes (2, 2)
+      for (int j = 0; j < 32; ++j)
+	#pragma omp tile sizes (2, 2)
+	for (int k = 0; k < 32; ++k)
+	  for (int l = m; l < 32; ++l)
+	    foo (i + k, j + l);
+      foo (i, -2);
+    }
+}
--- gcc/testsuite/c-c++-common/gomp/unroll-1.c.jj	2024-06-04 13:30:00.692872592 +0200
+++ gcc/testsuite/c-c++-common/gomp/unroll-1.c	2024-06-04 13:30:00.691872606 +0200
@@ -0,0 +1,132 @@ 
+extern void dummy (int);
+
+void
+test1 (void)
+{
+  #pragma omp unroll partial
+  for (int i = 0; i < 100; ++i)
+    dummy (i);
+}
+
+void
+test2 (void)
+{
+  #pragma omp unroll partial(10)
+  for (int i = 0; i < 100; ++i)
+    dummy (i);
+}
+
+void
+test3 (void)
+{
+  #pragma omp unroll full
+  for (int i = 0; i < 100; ++i)
+    dummy (i);
+}
+
+void
+test4 (void)
+{
+  #pragma omp unroll full
+  for (int i = 0; i > 100; ++i)
+    dummy (i);
+}
+
+void
+test5 (void)
+{
+  #pragma omp unroll full
+  for (int i = 1; i <= 100; ++i)
+    dummy (i);
+}
+
+void
+test6 (void)
+{
+  #pragma omp unroll full
+  for (int i = 200; i >= 100; i--)
+    dummy (i);
+}
+
+void
+test7 (void)
+{
+  #pragma omp unroll full
+  for (int i = -100; i > 100; ++i)
+    dummy (i);
+}
+
+void
+test8 (void)
+{
+  #pragma omp unroll full
+  for (int i = 100; i > -200; --i)
+    dummy (i);
+}
+
+void
+test9 (void)
+{
+  #pragma omp unroll full
+  for (int i = -300; i != 100; ++i)
+    dummy (i);
+}
+
+void
+test10 (void)
+{
+  #pragma omp unroll full
+  for (int i = -300; i != 100; ++i)
+    dummy (i);
+}
+
+void
+test12 (void)
+{
+  #pragma omp unroll full
+  #pragma omp unroll partial
+  #pragma omp unroll partial
+  for (int i = -300; i != 100; ++i)
+    dummy (i);
+}
+
+void
+test13 (void)
+{
+  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 (void)
+{
+  #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 (void)
+{
+  #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);
+    }
+ }
--- gcc/testsuite/c-c++-common/gomp/unroll-2.c.jj	2024-06-04 13:30:00.694872566 +0200
+++ gcc/testsuite/c-c++-common/gomp/unroll-2.c	2024-06-04 13:30:00.694872566 +0200
@@ -0,0 +1,122 @@ 
+/* { dg-do compile { target { c || c++11 } } } */
+/* { dg-prune-output "error: invalid controlling predicate" } */
+
+extern void dummy (int);
+
+void
+test (void)
+{
+  #pragma omp unroll partial
+  #pragma omp unroll full /* { dg-error "generated loop of 'unroll' construct without 'partial' clause does not have canonical form" } */
+  for (int i = -300; i != 100; ++i)
+    dummy (i);
+
+  #pragma omp for
+  #pragma omp unroll full /* { dg-error "generated loop of 'unroll' construct without 'partial' clause does not have canonical form" } */
+  #pragma omp unroll partial
+  for (int i = -300; i != 100; ++i)
+    dummy (i);
+
+  #pragma omp for
+  #pragma omp unroll full
+  #pragma omp unroll full /* { dg-error "generated loop of 'unroll' construct without 'partial' clause does not have canonical form" } */
+  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 "generated loop of 'unroll' construct without 'partial' clause does not have canonical form" } */
+  for (int i = -300; i != 100; ++i)
+    dummy (i);
+
+  #pragma omp for
+  #pragma omp unroll /* { dg-error "generated loop of 'unroll' construct without 'partial' clause does not have canonical form" } */
+  for (int i = -300; i != 100; ++i)
+    dummy (i);
+
+  int i;
+  #pragma omp for /* { dg-error "generated loop of 'unroll' construct without 'partial' clause does not have canonical form" "" { target *-*-* } .+1 } */
+  #pragma omp unroll( /* { dg-error "expected an OpenMP clause before '\\\(' token" } */
+  for (int i = -300; i != 100; ++i)
+    dummy (i);
+
+  #pragma omp for /* { dg-error "generated loop of 'unroll' construct without 'partial' clause does not have canonical form" "" { target *-*-* } .+1 } */
+  #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) /* { dg-error "generated loop of 'unroll' construct without 'partial' clause does not have canonical form" "" { target *-*-* } .+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) /* { dg-error "generated loop of 'unroll' construct without 'partial' clause does not have canonical form" "" { target *-*-* } .+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 "'unroll' construct with 'partial' clause generates just one loop with canonical form but 2 loops are needed" } */
+  for (int i = 3; i < 10; ++i)
+    for (int j = -2; j < 7; ++j)
+      sum++;
+
+  #pragma omp unroll partial full /* { dg-error "'full' clause must not be used together with 'partial' clause" } */
+  for (int i = 0; i < 42; ++i)
+    dummy (i);
+
+  #pragma omp unroll full partial /* { dg-error "'full' clause must not be used together with 'partial' clause" } */
+  for (int i = 0; i < 42; ++i)
+    dummy (i);
+
+  #pragma omp unroll partial(7) full /* { dg-error "'full' clause must not be used together with 'partial' clause" } */
+  for (int i = 0; i < 42; ++i)
+    dummy (i);
+
+  #pragma omp unroll full partial(28) /* { dg-error "'full' clause must not be used together with 'partial' clause" } */
+  for (int i = 0; i < 42; ++i)
+    dummy (i);
+
+  #pragma omp unroll partial(0.5) /* { dg-error "'partial' argument needs positive constant integer expression" } */
+  for (int i = 0; i < 42; ++i)
+    dummy (i);
+
+  #pragma omp unroll partial(0) /* { dg-error "'partial' argument needs positive constant integer expression" } */
+  for (int i = 0; i < 42; ++i)
+    dummy (i);
+
+  #pragma omp unroll partial(-42) /* { dg-error "'partial' argument needs positive constant integer expression" } */
+  for (int i = 0; i < 42; ++i)
+    dummy (i);
+}
--- gcc/testsuite/c-c++-common/gomp/unroll-3.c.jj	2024-06-04 13:30:00.691872606 +0200
+++ gcc/testsuite/c-c++-common/gomp/unroll-3.c	2024-06-04 13:30:00.691872606 +0200
@@ -0,0 +1,16 @@ 
+/* { dg-additional-options "-O2 -fdump-tree-cunroll -fdump-tree-original -fdump-tree-gimple" } */
+
+extern void dummy (int);
+
+void
+test1 (void)
+{
+#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 "cunroll" } } */
+/* { dg-final { scan-tree-dump "#pragma omp unroll" "original" } } */
+/* { dg-final { scan-tree-dump-not "#pragma omp" "gimple" } } */
--- gcc/testsuite/c-c++-common/gomp/unroll-4.c.jj	2024-06-04 13:30:00.690872619 +0200
+++ gcc/testsuite/c-c++-common/gomp/unroll-4.c	2024-06-04 13:30:00.690872619 +0200
@@ -0,0 +1,18 @@ 
+/* { dg-additional-options "-O2 -fdump-tree-original -fdump-tree-gimple" } */
+
+extern void dummy (int);
+
+void
+test1 (void)
+{
+  int i;
+#pragma omp unroll
+  for (int i = 0; i < 96; i++)
+    dummy (i);
+}
+
+/* GCC unrolls the loop 8 times, but only during RTL optimizations.  */
+/* { dg-final { scan-tree-dump "#pragma omp unroll" "original" } } */
+/* { dg-final { scan-tree-dump-not "#pragma omp" "gimple" } } */
+/* { dg-final { scan-tree-dump-times "dummy" 1 "gimple" } } */
+/* { dg-final { scan-assembler-times "dummy" 8 } } */
--- gcc/testsuite/c-c++-common/gomp/unroll-5.c.jj	2024-06-04 13:30:00.680872752 +0200
+++ gcc/testsuite/c-c++-common/gomp/unroll-5.c	2024-06-04 13:30:00.680872752 +0200
@@ -0,0 +1,18 @@ 
+/* { dg-additional-options "-O2 -fdump-tree-original -fdump-tree-gimple" } */
+
+extern void dummy (int);
+
+void
+test1 (void)
+{
+  int i;
+#pragma omp unroll partial
+  for (int i = 0; i < 96; i++)
+    dummy (i);
+}
+
+/* GCC uses partial(8) for this case.  */
+/* { dg-final { scan-tree-dump "#pragma omp unroll" "original" } } */
+/* { dg-final { scan-tree-dump-not "#pragma omp" "gimple" } } */
+/* { dg-final { scan-tree-dump-times "dummy" 1 "gimple" } } */
+/* { dg-final { scan-assembler-times "dummy" 8 } } */
--- gcc/testsuite/c-c++-common/gomp/unroll-6.c.jj	2024-06-04 13:30:00.693872579 +0200
+++ gcc/testsuite/c-c++-common/gomp/unroll-6.c	2024-06-04 13:30:00.693872579 +0200
@@ -0,0 +1,168 @@ 
+/* { dg-do run } */
+/* { dg-options "-O0 -fopenmp-simd" } */
+
+#include <stdio.h>
+
+#define ASSERT_EQ(var, val) \
+  do									\
+    {									\
+      if ((var) != (val))						\
+	{								\
+	  fprintf (stderr, "%s:%d: Unexpected value %d, expected %d\n",	\
+		   __FILE__, __LINE__, (var), (val));			\
+	  __builtin_abort ();						\
+	}								\
+    }									\
+  while (0)
+
+#define ASSERT_EQ_PTR(var, ptr) \
+  do									\
+    {									\
+      if ((var) != (ptr))						\
+	{								\
+	  fprintf (stderr, "%s:%d: Unexpected value %p, expected %p\n",	\
+		   __FILE__, __LINE__, (var), (ptr));			\
+	  __builtin_abort ();						\
+	}								\
+    }									\
+  while (0)
+
+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[15])
+{
+  int iter = 0;
+  int *i;
+  #pragma omp unroll partial(7)
+  for (i = data + 14; i >= data + 5; i--)
+    {
+      ASSERT_EQ (*i, data[14 - iter]);
+      iter++;
+    }
+
+  return iter;
+}
+
+int
+test6 (int data[15])
+{
+  int iter = 0;
+  int *i;
+
+  #pragma omp unroll partial(7)
+  for (i = data + 14; i > data + 4; i--)
+    {
+      ASSERT_EQ (*i, data[14 - iter]);
+      iter++;
+    }
+
+  return iter;
+}
+
+int
+test7 (int data[10])
+{
+  int iter = 0;
+
+  #pragma omp unroll partial(7)
+  for (int *i = data + 14; i != data + 4; i--)
+    {
+      ASSERT_EQ (*i, data[14 - iter]);
+      iter++;
+    }
+
+  return iter;
+}
+
+int
+main ()
+{
+  int iter_count;
+  int data[10] = { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9 };
+  int data2[15] = { -1, -1, -1, -1, -1, 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 (data2);
+  ASSERT_EQ (iter_count, 10);
+
+  iter_count = test6 (data2);
+  ASSERT_EQ (iter_count, 10);
+
+  iter_count = test7 (data2);
+  ASSERT_EQ (iter_count, 10);
+}
--- gcc/testsuite/c-c++-common/gomp/unroll-7.c.jj	2024-06-04 13:30:00.691872606 +0200
+++ gcc/testsuite/c-c++-common/gomp/unroll-7.c	2024-06-04 13:30:00.691872606 +0200
@@ -0,0 +1,18 @@ 
+extern void dummy (int);
+int a[100];
+
+void
+test1 (void)
+{
+#pragma omp unroll full
+  for (int i = -20; i < 20; i += 6)
+    dummy (i);
+}
+
+void
+test2 (void)
+{
+#pragma omp unroll full
+  for (int *i = &a[6]; i < &a[78]; i += 4)
+    dummy (*i);
+}
--- gcc/testsuite/c-c++-common/gomp/unroll-8.c.jj	2024-06-04 13:30:00.691872606 +0200
+++ gcc/testsuite/c-c++-common/gomp/unroll-8.c	2024-06-04 13:30:00.691872606 +0200
@@ -0,0 +1,25 @@ 
+extern void dummy (int);
+
+void
+test3 (int x)
+{
+#pragma omp unroll full	/* { dg-error "non-constant iteration count of 'unroll full' loop" } */
+  for (int i = x; i < 20; i += 3)
+    dummy (i);
+}
+
+void
+test4 (int x)
+{
+#pragma omp unroll full	/* { dg-error "non-constant iteration count of 'unroll full' loop" } */
+  for (int i = 5; i < x + 6; ++i)
+    dummy (i);
+}
+
+void
+test5 (int x)
+{
+#pragma omp unroll full	/* { dg-error "non-constant iteration count of 'unroll full' loop" } */
+  for (int i = 5; i < 142; i += x)
+    dummy (i);
+}
--- gcc/testsuite/c-c++-common/gomp/unroll-9.c.jj	2024-06-04 13:30:00.694872566 +0200
+++ gcc/testsuite/c-c++-common/gomp/unroll-9.c	2024-06-04 13:30:00.694872566 +0200
@@ -0,0 +1,13 @@ 
+/* { dg-additional-options "-O2 -fdump-tree-gimple" } */
+
+void bar (int);
+
+void
+foo (void)
+{
+  #pragma omp unroll full
+  for (int i = 1; i <= 100; i += 6)
+    bar (i);
+}
+
+/* { dg-final { scan-tree-dump "\.ANNOTATE \\\(\[^\n\r]*, 1, 17\\\);" "gimple" } } */
--- gcc/testsuite/c-c++-common/gomp/unroll-inner-1.c.jj	2024-06-04 13:30:00.680872752 +0200
+++ gcc/testsuite/c-c++-common/gomp/unroll-inner-1.c	2024-06-04 13:30:00.680872752 +0200
@@ -0,0 +1,11 @@ 
+extern void dummy (int);
+
+void
+test (void)
+{
+  #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);
+}
--- gcc/testsuite/c-c++-common/gomp/unroll-inner-2.c.jj	2024-06-04 13:30:00.692872592 +0200
+++ gcc/testsuite/c-c++-common/gomp/unroll-inner-2.c	2024-06-04 13:30:00.692872592 +0200
@@ -0,0 +1,18 @@ 
+extern void dummy (int);
+
+void
+test (void)
+{
+  #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)
+    #pragma omp tile sizes(2, 3)
+    for (int j = 0; j != 100; ++j)
+      for (int k = 0; k != 100; ++k)
+	dummy (i);
+}
--- gcc/testsuite/c-c++-common/gomp/unroll-inner-3.c.jj	2024-06-04 13:30:00.679872765 +0200
+++ gcc/testsuite/c-c++-common/gomp/unroll-inner-3.c	2024-06-04 13:30:00.679872765 +0200
@@ -0,0 +1,11 @@ 
+extern void dummy (int);
+
+void
+test (void)
+{
+  #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) /* { dg-error "not enough nested loops" } */
+      dummy (i);
+}
--- gcc/testsuite/c-c++-common/gomp/unroll-non-rect-1.c.jj	2024-06-04 13:30:00.680872752 +0200
+++ gcc/testsuite/c-c++-common/gomp/unroll-non-rect-1.c	2024-06-04 13:30:00.680872752 +0200
@@ -0,0 +1,30 @@ 
+extern void dummy (int);
+
+void
+test1 (void)
+{
+  #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);
+}
--- gcc/testsuite/c-c++-common/gomp/unroll-non-rect-2.c.jj	2024-06-04 13:30:00.690872619 +0200
+++ gcc/testsuite/c-c++-common/gomp/unroll-non-rect-2.c	2024-06-04 13:30:00.690872619 +0200
@@ -0,0 +1,22 @@ 
+extern void dummy (int);
+
+void
+test1 (void)
+{
+  #pragma omp target parallel for collapse(2)
+  for (int i = -300; i != 100; ++i)
+    #pragma omp unroll partial
+    for (int j = 2; j != i; ++j) /* { dg-message "non-rectangular loops from generated loops unsupported" } */
+      dummy (i);
+}
+
+void
+test2 (void)
+{
+  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) /* { dg-message "non-rectangular loops from generated loops unsupported" } */
+      dummy (i);
+}
--- gcc/testsuite/c-c++-common/gomp/unroll-non-rect-3.c.jj	2024-06-04 13:30:00.693872579 +0200
+++ gcc/testsuite/c-c++-common/gomp/unroll-non-rect-3.c	2024-06-04 13:30:00.693872579 +0200
@@ -0,0 +1,11 @@ 
+extern void dummy (int);
+
+void
+test1 (void)
+{
+  #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) /* { dg-message "non-rectangular loops from generated loops unsupported" } */
+      dummy (i);
+}
--- gcc/testsuite/c-c++-common/gomp/unroll-simd-1.c.jj	2024-06-04 13:30:00.694872566 +0200
+++ gcc/testsuite/c-c++-common/gomp/unroll-simd-1.c	2024-06-04 13:30:00.694872566 +0200
@@ -0,0 +1,70 @@ 
+/* { dg-do run } */
+/* { dg-options "-fno-openmp -fopenmp-simd -fdump-tree-original -fdump-tree-gimple" } */
+
+int
+compute_sum1 (void)
+{
+  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 (i != 10 || j != 7)
+    __builtin_abort ();
+
+  return sum;
+}
+
+int
+compute_sum2 (void)
+{
+  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 (i != 10 || j != 7)
+    __builtin_abort ();
+
+  return sum;
+}
+
+int
+compute_sum3 (void)
+{
+  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 (i != 10 || j != 7)
+    __builtin_abort ();
+
+  return sum;
+}
+
+int
+main ()
+{
+  if (compute_sum1 () != 7 * 9
+      || compute_sum2 () != 7 * 9
+      || compute_sum3 () != 7 * 9)
+    __builtin_abort ();
+
+  return 0;
+}
+
+/* { dg-final { scan-tree-dump {omp unroll} "original" } } */
+/* { dg-final { scan-tree-dump-not {omp unroll} "gimple" } } */
--- gcc/testsuite/gcc.dg/gomp/attrs-4.c.jj	2024-06-04 13:18:26.172104450 +0200
+++ gcc/testsuite/gcc.dg/gomp/attrs-4.c	2024-06-04 13:30:00.739871968 +0200
@@ -50,7 +50,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" } */
--- gcc/testsuite/gcc.dg/gomp/for-1.c.jj	2024-06-04 13:18:26.194104157 +0200
+++ gcc/testsuite/gcc.dg/gomp/for-1.c	2024-06-04 13:30:00.745871888 +0200
@@ -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);
--- gcc/testsuite/gcc.dg/gomp/for-11.c.jj	2024-06-04 13:18:26.194104157 +0200
+++ gcc/testsuite/gcc.dg/gomp/for-11.c	2024-06-04 13:30:00.729872101 +0200
@@ -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);
--- gcc/testsuite/g++.dg/gomp/attrs-4.C.jj	2024-06-04 13:18:26.109105288 +0200
+++ gcc/testsuite/g++.dg/gomp/attrs-4.C	2024-06-04 13:30:00.709872367 +0200
@@ -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" }
--- gcc/testsuite/g++.dg/gomp/for-1.C.jj	2024-06-04 13:18:26.109105288 +0200
+++ gcc/testsuite/g++.dg/gomp/for-1.C	2024-06-04 13:30:00.701872473 +0200
@@ -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);
--- gcc/testsuite/g++.dg/gomp/pr94512.C.jj	2024-06-04 13:18:26.131104995 +0200
+++ gcc/testsuite/g++.dg/gomp/pr94512.C	2024-06-04 13:30:00.700872486 +0200
@@ -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
--- gcc/testsuite/g++.dg/gomp/tile-1.C.jj	2024-06-04 13:30:00.694872566 +0200
+++ gcc/testsuite/g++.dg/gomp/tile-1.C	2024-06-04 13:30:00.694872566 +0200
@@ -0,0 +1,30 @@ 
+// { dg-do compile { target 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> ();
+};
--- gcc/testsuite/g++.dg/gomp/tile-2.C.jj	2024-06-04 13:30:00.700872486 +0200
+++ gcc/testsuite/g++.dg/gomp/tile-2.C	2024-06-04 13:30:00.700872486 +0200
@@ -0,0 +1,30 @@ 
+// { dg-do compile { target 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 "'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> ();
+}
--- gcc/testsuite/g++.dg/gomp/unroll-1.C.jj	2024-06-04 13:30:00.701872473 +0200
+++ gcc/testsuite/g++.dg/gomp/unroll-1.C	2024-06-04 13:30:00.701872473 +0200
@@ -0,0 +1,42 @@ 
+// { dg-do compile { target 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);
+}
--- gcc/testsuite/g++.dg/gomp/unroll-2.C.jj	2024-06-04 13:30:00.695872553 +0200
+++ gcc/testsuite/g++.dg/gomp/unroll-2.C	2024-06-04 13:30:00.695872553 +0200
@@ -0,0 +1,51 @@ 
+// { dg-do compile { target 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> ();
+}
--- gcc/testsuite/g++.dg/gomp/unroll-3.C.jj	2024-06-04 13:30:00.694872566 +0200
+++ gcc/testsuite/g++.dg/gomp/unroll-3.C	2024-06-05 09:44:28.145797161 +0200
@@ -0,0 +1,30 @@ 
+// { dg-do compile { target c++11 } }
+// { dg-additional-options "-fdump-tree-original -fdump-tree-gimple" }
+
+#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 unroll partial\(55\)} "original" } }
+// { dg-final { scan-tree-dump-not "#pragma omp" "gimple" } }
--- gcc/testsuite/gfortran.dg/gomp/inner-loops-1.f90.jj	2024-06-04 13:30:00.711872340 +0200
+++ gcc/testsuite/gfortran.dg/gomp/inner-loops-1.f90	2024-06-04 13:30:00.711872340 +0200
@@ -0,0 +1,60 @@ 
+subroutine test1
+  !$omp parallel do collapse(2)
+  do i=0,100
+    !$omp unroll partial(2)
+    do j=-300,100
+      call dummy (j)
+    end do
+  end do
+end subroutine test1
+
+subroutine test3
+  !$omp parallel do collapse(3)
+  do i=0,100
+    do j=-300,100
+      !$omp unroll partial(2)
+       do k=-300,100
+         call dummy (k)
+       end do
+    end do
+  end do
+end subroutine test3
+
+subroutine test6
+  !$omp parallel do collapse(3)
+  do i=0,100
+    !$omp tile sizes(3,2)
+    do j=-300,100
+      !$omp unroll partial(2)
+      do k=-300,100
+        call dummy (k)
+      end do
+    end do
+  end do
+end subroutine test6
+
+subroutine test7
+  !$omp parallel do collapse(3)
+  do i=0,100
+    !$omp tile sizes(3,3)
+    do j=-300,100
+      !$omp tile sizes(5)
+      do k=-300,100
+        call dummy (k)
+      end do
+    end do
+  end do
+end subroutine test7
+
+subroutine test8
+  !$omp parallel do collapse(1)
+  do i=0,100
+    !$omp tile sizes(3,3)
+    do j=-300,100
+      !$omp tile sizes(5)
+      do k=-300,100
+        call dummy (k)
+      end do
+    end do
+  end do
+end subroutine test8
--- gcc/testsuite/gfortran.dg/gomp/inner-loops-2.f90.jj	2024-06-04 13:30:00.711872340 +0200
+++ gcc/testsuite/gfortran.dg/gomp/inner-loops-2.f90	2024-06-04 13:30:00.711872340 +0200
@@ -0,0 +1,62 @@ 
+subroutine test2
+  !$omp parallel do collapse(3)
+  do i=0,100
+    !$omp unroll partial(2) ! { dg-error "UNROLL construct at \\\(1\\\) with PARTIAL clause generates just one loop with canonical form but 2 loops are needed" }
+    do j=-300,100
+      do k=-300,100
+        call dummy (k)
+      end do
+    end do
+  end do
+end subroutine test2
+
+subroutine test4
+  !$omp parallel do collapse(3)
+  do i=0,100
+    !$omp tile sizes(3) ! { dg-error "TILE construct at \\\(1\\\) generates 1 loops with canonical form but 2 loops are needed" }
+    do j=-300,100
+      !$omp unroll partial(2)
+      do k=-300,100
+        call dummy (k)
+      end do
+    end do
+  end do
+end subroutine test4
+
+subroutine test5
+  !$omp parallel do collapse(3)
+  !$omp tile sizes(3,2) ! { dg-error "TILE construct at \\\(1\\\) generates 2 loops with canonical form but 3 loops are needed" }
+  do i=0,100
+    do j=-300,100
+      do k=-300,100
+        call dummy (k)
+      end do
+    end do
+  end do
+end subroutine test5
+
+subroutine test9
+  !$omp parallel do collapse(3)
+  do i=0,100
+    !$omp tile sizes(3,3,3)
+    do j=-300,100
+      !$omp tile sizes(5) ! { dg-error "TILE construct at \\\(1\\\) generates 1 loops with canonical form but 2 loops are needed" }
+      do k=-300,100
+        call dummy (k)
+      end do
+    end do
+  end do
+end subroutine test9
+
+subroutine test10
+  !$omp parallel do
+  do i=0,100
+    !$omp tile sizes(3,3,3)
+    do j=-300,100
+      !$omp tile sizes(5) ! { dg-error "TILE construct at \\\(1\\\) generates 1 loops with canonical form but 2 loops are needed" }
+      do k=-300,100
+        call dummy (k)
+      end do
+    end do
+  end do
+end subroutine test10
--- gcc/testsuite/gfortran.dg/gomp/pure-1.f90.jj	2024-06-04 13:18:26.229103691 +0200
+++ gcc/testsuite/gfortran.dg/gomp/pure-1.f90	2024-06-04 13:30:00.722872194 +0200
@@ -86,3 +86,27 @@  pure integer function func_simd(n)
   end do
   func_simd = r
 end
+
+pure integer function func_unroll(n)
+  implicit none
+  integer, value :: n
+  integer :: j, r
+  r = 0
+  !$omp unroll partial(2)
+  do j = 1, n
+    r = r + j
+  end do
+  func_unroll = r
+end
+
+pure integer function func_tile(n)
+  implicit none
+  integer, value :: n
+  integer :: j, r
+  r = 0
+  !$omp tile sizes(2)
+  do j = 1, n
+    r = r + j
+  end do
+  func_tile = r
+end
--- gcc/testsuite/gfortran.dg/gomp/pure-2.f90.jj	2024-06-04 13:18:26.277103053 +0200
+++ gcc/testsuite/gfortran.dg/gomp/pure-2.f90	2024-06-04 13:30:00.726872141 +0200
@@ -46,28 +46,3 @@  logical function func_reverse(n)
   end do
 end
 
-!pure integer function func_unroll(n)
-integer function func_unroll(n)
-  implicit none
-  integer, value :: n
-  integer :: j, r
-  r = 0
-  !$omp unroll partial(2) ! { dg-error "Unclassifiable OpenMP directive" }
-  do j = 1, n
-    r = r + j
-  end do
-  func_unroll = r
-end
-
-!pure integer function func_tile(n)
-integer function func_tile(n)
-  implicit none
-  integer, value :: n
-  integer :: j, r
-  r = 0
-  !$omp tile sizes(2) ! { dg-error "Unclassifiable OpenMP directive" }
-  do j = 1, n
-    r = r + j
-  end do
-  func_tile = r
-end
--- gcc/testsuite/gfortran.dg/gomp/scan-9.f90.jj	2024-06-05 14:08:41.713387838 +0200
+++ gcc/testsuite/gfortran.dg/gomp/scan-9.f90	2024-06-05 15:08:11.047992889 +0200
@@ -0,0 +1,47 @@ 
+subroutine foo (c, d, a)
+  integer :: i, a, c(64), d(64)
+  !$omp do reduction (inscan, +: a)
+  !$omp tile sizes (2)
+  do i = 1, 64
+    a = a + c(i)
+    !$omp scan inclusive (a) ! { dg-error "Unexpected !\\\$OMP SCAN at \\\(1\\\) outside loop construct with 'inscan' REDUCTION clause" }
+    d(i) = a
+  end do
+end subroutine foo
+
+subroutine bar (c, d, a)
+  integer :: i, j, a, c(64, 64), d(64, 64)
+  !$omp do collapse (2) reduction (inscan, +: a)
+  do i = 1, 64
+    !$omp tile sizes (2)
+    do j = 1, 64
+      d(i, j) = a
+      !$omp scan exclusive (a) ! { dg-error "Unexpected !\\\$OMP SCAN at \\\(1\\\) outside loop construct with 'inscan' REDUCTION clause" }
+      a = a + c(i, j)
+    end do
+  end do
+end subroutine bar
+
+subroutine baz (c, d, a)
+  integer :: i, a, c(64), d(64)
+  !$omp do reduction (inscan, +: a)
+  !$omp unroll partial (2)
+  do i = 1, 64
+    d(i) = a
+    !$omp scan exclusive (a) ! { dg-error "Unexpected !\\\$OMP SCAN at \\\(1\\\) outside loop construct with 'inscan' REDUCTION clause" }
+    a = a + c(i)
+  end do
+end subroutine baz
+
+subroutine qux (c, d, a)
+  integer :: i, j, a, c(64, 64), d(64, 64)
+  !$omp do collapse (2) reduction (inscan, +: a)
+  do i = 1, 64
+    !$omp tile sizes (2)
+    do j = 1, 64
+      a = a + c(i, j)
+      !$omp scan inclusive (a) ! { dg-error "Unexpected !\\\$OMP SCAN at \\\(1\\\) outside loop construct with 'inscan' REDUCTION clause" }
+      d(i, j) = a
+    end do
+  end do
+end subroutine qux
--- gcc/testsuite/gfortran.dg/gomp/tile-1.f90.jj	2024-06-04 13:30:00.710872353 +0200
+++ gcc/testsuite/gfortran.dg/gomp/tile-1.f90	2024-06-04 13:30:00.710872353 +0200
@@ -0,0 +1,39 @@ 
+subroutine test
+  implicit none
+  integer :: i, j, k
+
+  !$omp tile sizes ( 1 )
+  do i = 1,100
+    call dummy(i)
+  end do
+
+  !$omp tile sizes(1)
+  do i = 1,100
+    call dummy(i)
+  end do
+  !$omp end tile
+
+  !$omp tile sizes(2+3)
+  do i = 1,100
+    call dummy(i)
+  end do
+  !$omp end tile
+
+  !$omp tile sizes(1,2)
+  do i = 1,100
+    do j = 1,100
+      call dummy(j)
+    end do
+  end do
+  !$omp end tile
+
+  !$omp tile sizes(1,2,1)
+  do i = 1,100
+    do j = 1,100
+      do k = 1,100
+        call dummy(i)
+      end do
+    end do
+  end do
+  !$omp end tile
+end subroutine test
--- gcc/testsuite/gfortran.dg/gomp/tile-2.f90.jj	2024-06-04 13:30:00.710872353 +0200
+++ gcc/testsuite/gfortran.dg/gomp/tile-2.f90	2024-06-04 13:30:00.710872353 +0200
@@ -0,0 +1,61 @@ 
+subroutine test1
+  implicit none
+  integer :: i, j, k
+
+  !$omp tile sizes (1,2)
+  !$omp tile sizes (1,2)
+  do i = 1,100
+    do j = 1,100
+      call dummy(j)
+      do k = 1,100
+        call dummy(i)
+      end do
+    end do
+  end do
+  !$omp end tile
+
+  !$omp tile sizes (8)
+  !$omp tile sizes (1,2)
+  !$omp tile sizes (1,2,3)
+  do i = 1,100
+    do j = 1,100
+      do k = 1,100
+        call dummy(i)
+      end do
+    end do
+  end do
+  !$omp end tile
+end subroutine test1
+
+subroutine test2
+  implicit none
+  integer :: i, j, k
+
+  !$omp taskloop collapse(2)
+  !$omp tile sizes (3,4)
+  !$omp tile sizes (1,2)
+  do i = 1,100
+    do j = 1,100
+      call dummy(j)
+      do k = 1,100
+        call dummy(i)
+      end do
+    end do
+  end do
+  !$omp end tile
+  !$omp end taskloop
+
+  !$omp taskloop simd
+  !$omp tile sizes (8)
+  !$omp tile sizes (1,2)
+  !$omp tile sizes (1,2,3)
+  do i = 1,100
+    do j = 1,100
+      do k = 1,100
+        call dummy(i)
+      end do
+    end do
+  end do
+  !$omp end tile
+  !$omp end taskloop simd
+end subroutine test2
--- gcc/testsuite/gfortran.dg/gomp/tile-3.f90.jj	2024-06-04 13:30:00.727872128 +0200
+++ gcc/testsuite/gfortran.dg/gomp/tile-3.f90	2024-06-04 13:30:00.727872128 +0200
@@ -0,0 +1,17 @@ 
+subroutine test
+  implicit none
+  integer :: i, j, k
+
+  !$omp parallel do collapse(2) ordered(2) ! { dg-error "'ordered' clause used with generated loops" }
+  !$omp tile sizes (1,2)
+  do i = 1,100
+    do j = 1,100
+      call dummy(j)
+      do k = 1,100
+        call dummy(i)
+      end do
+    end do
+  end do
+  !$omp end tile
+  !$omp end parallel do
+end subroutine test
--- gcc/testsuite/gfortran.dg/gomp/tile-4.f90.jj	2024-06-04 13:30:00.728872114 +0200
+++ gcc/testsuite/gfortran.dg/gomp/tile-4.f90	2024-06-04 13:30:00.728872114 +0200
@@ -0,0 +1,89 @@ 
+subroutine test1
+  implicit none
+  integer :: i, j, k
+
+  !$omp tile sizes (1,2)
+  !$omp tile sizes (1)  ! { dg-error "TILE construct at \\\(1\\\) generates 1 loops with canonical form but 2 loops are needed" }
+  do i = 1,100
+    do j = 1,100
+      call dummy(j)
+      do k = 1,100
+        call dummy(i)
+      end do
+    end do
+  end do
+  !$omp end tile
+end subroutine test1
+
+subroutine test2
+  implicit none
+  integer :: i, j, k
+
+  !$omp tile sizes (1,2)
+  !$omp tile sizes (1)  ! { dg-error "TILE construct at \\\(1\\\) generates 1 loops with canonical form but 2 loops are needed" }
+  do i = 1,100
+    do j = 1,100
+      call dummy(j)
+      do k = 1,100
+        call dummy(i)
+      end do
+    end do
+  end do
+  !$omp end tile
+end subroutine test2
+
+subroutine test3
+  implicit none
+  integer :: i, j, k
+
+  !$omp target teams distribute
+  !$omp tile sizes (1,2)
+  !$omp tile sizes (1)  ! { dg-error "TILE construct at \\\(1\\\) generates 1 loops with canonical form but 2 loops are needed" }
+  do i = 1,100
+    do j = 1,100
+      call dummy(j)
+      do k = 1,100
+        call dummy(i)
+      end do
+    end do
+  end do
+  !$omp end tile
+end subroutine test3
+
+subroutine test4
+  implicit none
+  integer :: i, j, k
+
+  !$omp target teams distribute collapse(2)
+  !$omp tile sizes (8)  ! { dg-error "TILE construct at \\\(1\\\) generates 1 loops with canonical form but 2 loops are needed" }
+  !$omp tile sizes (1,2)
+  do i = 1,100
+    do j = 1,100
+      call dummy(j)
+      do k = 1,100
+        call dummy(i)
+      end do
+    end do
+  end do
+  !$omp end tile
+end subroutine test4
+
+subroutine test5
+  implicit none
+  integer :: i, j, k
+
+  !$omp parallel do collapse(2) ordered(2)
+  !$omp tile sizes (8)  ! { dg-error "TILE construct at \\\(1\\\) generates 1 loops with canonical form but 2 loops are needed" }
+  !$omp tile sizes (1,2)
+  do i = 1,100
+    do j = 1,100
+      call dummy(j)
+      do k = 1,100
+        call dummy(i)
+      end do
+    end do
+  end do
+  !$omp end tile
+  !$omp end tile
+  !$omp end parallel do
+end subroutine test5
--- gcc/testsuite/gfortran.dg/gomp/tile-5.f90.jj	2024-06-04 13:30:00.728872114 +0200
+++ gcc/testsuite/gfortran.dg/gomp/tile-5.f90	2024-06-04 13:30:00.728872114 +0200
@@ -0,0 +1,73 @@ 
+subroutine test
+  implicit none
+  integer :: i, j, k, l, m, n, o
+  !$omp do private (i, j, k, l)
+  !$omp tile sizes(2, 3)
+  !$omp tile sizes(3, 4, 5)
+  !$omp tile sizes(6, 7, 8, 9)
+  do i = 1, 100
+    do j = 1, 100
+      do k = 1, 100
+        do l = 1, 100
+          call dummy(i)
+        end do
+      end do
+    end do
+  end do
+
+  !$omp do private (i, j, k, l, m, n)
+  !$omp tile sizes(2, 3)
+  do i = 1, 100
+    !$omp tile sizes(3, 4, 5)
+    do j = 1, 100
+      !$omp tile sizes(6, 7, 8, 9)
+      do k = 1, 100
+        do l = 1, 100
+          do m = 1, 100
+            !$omp unroll partial(2)
+            do n = 1, 100
+              call dummy(i)
+            end do
+          end do
+        end do
+      end do
+    end do
+  end do
+
+  !$omp do collapse(2) private (i, j, k, l, m)
+  do i = 1, 100
+    !$omp tile sizes(2, 3)
+    !$omp tile sizes(3, 4, 5)
+    !$omp tile sizes(6, 7, 8, 9)
+    do j = 1, 100
+      do k = 1, 100
+        do l = 1, 100
+          do m = 1, 100
+            call dummy(i)
+          end do
+        end do
+      end do
+    end do
+  end do
+
+  !$omp do private (i, j, k, l, m, n, o) collapse(2)
+  do i = 1, 100
+    !$omp tile sizes(2, 3)
+    do j = 1, 100
+      !$omp tile sizes(3, 4, 5)
+      do k = 1, 100
+        !$omp tile sizes(6, 7, 8, 9)
+        do l = 1, 100
+          do m = 1, 100
+            do n = 1, 100
+              !$omp unroll partial(2)
+              do o = 1, 100
+                call dummy(i)
+              end do
+            end do
+          end do
+        end do
+      end do
+    end do
+  end do
+end subroutine test
--- gcc/testsuite/gfortran.dg/gomp/tile-6.f90.jj	2024-06-04 13:30:00.727872128 +0200
+++ gcc/testsuite/gfortran.dg/gomp/tile-6.f90	2024-06-04 13:30:00.727872128 +0200
@@ -0,0 +1,9 @@ 
+subroutine test
+  !$omp tile sizes(1,2,1) ! { dg-error "not enough DO loops for collapsed !\\\$OMP TILE" }
+  do i = 1,100
+    do j = 1,100
+      call dummy(i)
+    end do
+  end do
+  !$omp end tile
+end subroutine test
--- gcc/testsuite/gfortran.dg/gomp/tile-7.f90.jj	2024-06-04 13:30:00.711872340 +0200
+++ gcc/testsuite/gfortran.dg/gomp/tile-7.f90	2024-06-04 13:30:00.710872353 +0200
@@ -0,0 +1,128 @@ 
+subroutine test
+  implicit none
+  integer :: i, j, k
+
+  !$omp tile sizes(-21) ! { dg-error "INTEGER expression of SIZES clause at \\\(1\\\) must be positive" }
+  do i = 1,100
+    call dummy(i)
+  end do
+  !$omp end tile
+
+  !$omp tile sizes(0) ! { dg-error "INTEGER expression of SIZES clause at \\\(1\\\) must be positive" }
+  do i = 1,100
+    call dummy(i)
+  end do
+  !$omp end tile
+
+  !$omp tile sizes(i) ! { dg-error "SIZES requires constant expression" }
+  do i = 1,100
+    call dummy(i)
+  end do
+  !$omp end tile
+
+  !$omp tile sizes ! { dg-error "Expected '\\\(' after 'sizes' at \\\(1\\\)" }
+  do i = 1,100
+    call dummy(i)
+  end do
+  !$omp end tile ! { dg-error "Unexpected !\\\$OMP END TILE statement at \\\(1\\\)" }
+
+  !$omp tile sizes( ! { dg-error "Syntax error in OpenMP expression list at \\\(1\\\)" }
+  do i = 1,100
+    call dummy(i)
+  end do
+  !$omp end tile ! { dg-error "Unexpected !\\\$OMP END TILE statement at \\\(1\\\)" }
+
+  !$omp tile sizes(2 ! { dg-error "Syntax error in OpenMP expression list at \\\(1\\\)" }
+  do i = 1,100
+    call dummy(i)
+  end do
+  !$omp end tile ! { dg-error "Unexpected !\\\$OMP END TILE statement at \\\(1\\\)" }
+
+  !$omp tile sizes() ! { dg-error "Syntax error in OpenMP expression list at \\\(1\\\)" }
+  do i = 1,100
+    call dummy(i)
+  end do
+  !$omp end tile ! { dg-error "Unexpected !\\\$OMP END TILE statement at \\\(1\\\)" }
+
+  !$omp tile sizes(2,) ! { dg-error "Syntax error in OpenMP expression list at \\\(1\\\)" }
+  do i = 1,100
+    call dummy(i)
+  end do
+  !$omp end tile ! { dg-error "Unexpected !\\\$OMP END TILE statement at \\\(1\\\)" }
+
+  !$omp tile sizes(,2) ! { dg-error "Syntax error in OpenMP expression list at \\\(1\\\)" }
+  do i = 1,100
+    call dummy(i)
+  end do
+  !$omp end tile ! { dg-error "Unexpected !\\\$OMP END TILE statement at \\\(1\\\)" }
+
+  !$omp tile sizes(,i) ! { dg-error "Syntax error in OpenMP expression list at \\\(1\\\)" }
+  do i = 1,100
+    call dummy(i)
+  end do
+  !$omp end tile ! { dg-error "Unexpected !\\\$OMP END TILE statement at \\\(1\\\)" }
+
+  !$omp tile sizes(i,) ! { dg-error "Syntax error in OpenMP expression list at \\\(1\\\)" }
+  do i = 1,100
+    call dummy(i)
+  end do
+  !$omp end tile ! { dg-error "Unexpected !\\\$OMP END TILE statement at \\\(1\\\)" }
+
+  !$omp tile sizes(1,2) ! { dg-error "not enough DO loops for collapsed !\\\$OMP TILE" }
+  do i = 1,100
+    call dummy(i)
+  end do
+  !$omp end tile
+
+  !$omp tile sizes(1,2,1) ! { dg-error "not enough DO loops for collapsed !\\\$OMP TILE" }
+  do i = 1,100
+    do j = 1,100
+      call dummy(i)
+    end do
+  end do
+  !$omp end tile
+
+  !$omp tile sizes(1,2,1) ! { dg-error "!\\\$OMP TILE inner loops must be perfectly nested at \\\(1\\\)" }
+  do i = 1,100
+    do j = 1,100
+      do k = 1,100
+        call dummy(i)
+      end do
+    end do
+    call dummy(i)
+  end do
+  !$omp end tile
+
+  !$omp tile sizes(1,2,1) ! { dg-error "!\\\$OMP TILE inner loops must be perfectly nested at \\\(1\\\)" }
+  do i = 1,100
+    do j = 1,100
+      do k = 1,100
+        call dummy(i)
+      end do
+      call dummy(j)
+    end do
+  end do
+  !$omp end tile
+
+  !$omp tile sizes(1,2,1) ! { dg-error "!\\\$OMP TILE inner loops must be perfectly nested at \\\(1\\\)" }
+  do i = 1,100
+    call dummy(i)
+    do j = 1,100
+      do k = 1,100
+        call dummy(i)
+      end do
+    end do
+  end do
+  !$omp end tile
+
+  !$omp tile sizes(1,2,1) ! { dg-error "!\\\$OMP TILE inner loops must be perfectly nested at \\\(1\\\)" }
+  do i = 1,100
+    do j = 1,100
+      call dummy(j)
+      do k = 1,100
+        call dummy(i)
+      end do
+    end do
+  end do
+  !$omp end tile
+end subroutine test
--- gcc/testsuite/gfortran.dg/gomp/tile-8.f90.jj	2024-06-04 13:30:00.712872327 +0200
+++ gcc/testsuite/gfortran.dg/gomp/tile-8.f90	2024-06-04 13:30:00.712872327 +0200
@@ -0,0 +1,18 @@ 
+subroutine test3
+  implicit none
+  integer :: i, j, k
+
+  !$omp taskloop collapse(3)
+  !$omp tile sizes (1,2) ! { dg-error "TILE construct at \\\(1\\\) generates 2 loops with canonical form but 3 loops are needed" }
+  !$omp tile sizes (1,2)
+  do i = 1,100
+    do j = 1,100
+      call dummy(j)
+      do k = 1,100
+        call dummy(i)
+      end do
+    end do
+  end do
+  !$omp end tile
+  !$omp end taskloop
+end subroutine test3
--- gcc/testsuite/gfortran.dg/gomp/tile-9.f90.jj	2024-06-05 09:56:32.366054960 +0200
+++ gcc/testsuite/gfortran.dg/gomp/tile-9.f90	2024-06-05 15:04:21.333045044 +0200
@@ -0,0 +1,96 @@ 
+subroutine test1
+  integer :: i, j, k, l
+  !$omp do collapse(4) private (i, j, k, l)
+  do i = 0, 1023 ! { dg-error "cannot be redefined inside loop" }
+    !$omp tile sizes (2, 2, 2)
+    do j = 0, 1023
+      !$omp tile sizes (3, 3)
+      do k = 0, 1023
+        !$omp tile sizes (4)
+        do i = 0, 1023 ! { dg-error "!\\\$OMP DO iteration variable used in more than one loop" }
+        end do ! { dg-error "cannot be redefined inside loop" "" { target *-*-* } .-1 }
+      end do
+    end do
+  end do
+end subroutine test1
+
+subroutine test2
+  integer(kind=8) :: i
+  integer :: j, k, l
+  !$omp do collapse(4) private (i, j, k, l)
+  do i = 0, 1023
+    !$omp tile sizes (2, 2, 2)
+    do j = 0, 1023
+      !$omp tile sizes (3, 3)
+      do k = 0, 1023
+        !$omp tile sizes (4)
+        do l = i, 1023 ! { dg-error "!\\\$OMP DO loop start expression not in canonical form" }
+        end do
+      end do
+    end do
+  end do
+end subroutine test2
+
+subroutine test3
+  integer :: i, j, k, l
+  !$omp do collapse(4) private (i, j, k, l)
+  do i = 0, 1023
+    !$omp tile sizes (2, 2, 2)
+    do j = 0, 1023
+      !$omp tile sizes (3, 3)
+      do k = 0, 1023
+        !$omp tile sizes (4)
+        do l = 0, 7 * i * i ! { dg-error "!\\\$OMP DO loop end expression not in canonical form" }
+        end do
+      end do
+    end do
+  end do
+end subroutine test3
+
+subroutine test4
+  integer :: i, j, k, l
+  !$omp do collapse(4) private (i, j, k, l)
+  do i = 0, 1023
+    !$omp tile sizes (2, 2, 2)
+    do j = 0, 1023
+      !$omp tile sizes (3, 3)
+      do k = 0, 1023
+        !$omp tile sizes (4)
+        do l = i * i, 1023 ! { dg-error "!\\\$OMP DO loop start expression not in canonical form" }
+        end do
+      end do
+    end do
+  end do
+end subroutine test4
+
+subroutine test5
+  integer :: i, j, k, l
+  !$omp do collapse(4) private (i, j, k, l)
+  do i = 0, 1023
+    !$omp tile sizes (2, 2, 2)
+    do j = 0, 1023
+      !$omp tile sizes (3, 3)
+      do k = 0, 1023
+        !$omp tile sizes (4)
+        do l = 0, 1023, j ! { dg-error "!\\\$OMP TILE loop increment not in canonical form" }
+        end do
+      end do
+    end do
+  end do
+end subroutine test5
+
+subroutine test6
+  integer :: i, j, k, l
+  !$omp do collapse(4) private (i, j, k, l)
+  do i = 0, 1023
+    !$omp tile sizes (2, 2, 2)
+    do j = 0, 1023
+      !$omp tile sizes (3, 3)
+      do k = 0, 1023
+        !$omp tile sizes (4)
+        do l = 0, i - 2 ! { dg-message "Non-rectangular loops from generated loops unsupported" }
+        end do
+      end do
+    end do
+  end do
+end subroutine test6
--- gcc/testsuite/gfortran.dg/gomp/tile-10.f90.jj	2024-06-05 09:56:39.737955858 +0200
+++ gcc/testsuite/gfortran.dg/gomp/tile-10.f90	2024-06-05 14:13:06.303879384 +0200
@@ -0,0 +1,70 @@ 
+! It isn't really clear what is supposed to be valid and what isn't when mixing
+! imperfectly nested loops with generated loops.  Sorry for now until that is
+! clarified.
+
+subroutine bar
+  integer :: i, j
+  !$omp do collapse(2)
+  do i = 0, 31
+    call foo (i, -1)
+    !$omp tile sizes (2)	! { dg-message "Imperfectly nested loop using generated loops" }
+    do j = 0, 31
+      call foo (i, j)
+    end do
+    call foo (i, -2)
+  end do
+end subroutine bar
+
+subroutine baz
+  integer :: i, j, k, l
+  !$omp do collapse(2)
+  do i = 0, 31
+    call foo (i, -1)
+    !$omp tile sizes (2, 2)	! { dg-message "Imperfectly nested loop using generated loops" }
+    do j = 0, 31
+      !$omp tile sizes (2, 2)
+      do k = 0, 31
+        do l = 0, 31
+          call foo (i + k, j + l)
+        end do
+      end do
+    end do
+    call foo (i, -2)
+  end do
+end subroutine baz
+
+subroutine qux
+  integer :: i, j, k, l, m
+  !$omp do collapse(2)
+  do i = 0, 31
+    m = i + 6
+    call foo (i, -1)
+    !$omp tile sizes (2)	! { dg-message "Imperfectly nested loop using generated loops" }
+    do j = m, 31
+      call foo (i, j)
+    end do
+    call foo (i, -2)
+  end do
+end subroutine qux
+
+subroutine freddy
+  integer :: i, j, k, l, m
+  !$omp do collapse(2)
+  do i = 0, 31
+    block
+      integer :: m
+      m = i + 6
+      call foo (i, -1)
+      !$omp tile sizes (2, 2)	! { dg-message "Imperfectly nested loop using generated loops" }
+      do j = 0, 31
+        !$omp tile sizes (2, 2)
+        do k = 0, 31
+          do l = m, 31
+            call foo (i + k, j + l)
+          end do
+        end do
+      end do
+      call foo (i, -2)
+    end block
+  end do
+end subroutine freddy
--- gcc/testsuite/gfortran.dg/gomp/tile-imperfect-nest-1.f90.jj	2024-06-04 13:30:00.727872128 +0200
+++ gcc/testsuite/gfortran.dg/gomp/tile-imperfect-nest-1.f90	2024-06-04 13:30:00.727872128 +0200
@@ -0,0 +1,17 @@ 
+subroutine test0
+  integer, allocatable, dimension (:,:) :: a,b,c
+  integer :: i, j, k, inner
+  !$omp parallel do collapse(2) private(inner)
+  !$omp tile sizes (8, 1)
+  do i = 1,m
+    !$omp tile sizes (8, 1)
+    do j = 1,n
+      !$omp unroll partial(10)
+      do k = 1, n
+        if (k == 1) then
+          inner = 0
+        endif
+      end do
+    end do
+  end do
+end subroutine test0
--- gcc/testsuite/gfortran.dg/gomp/tile-imperfect-nest-2.f90.jj	2024-06-04 13:30:00.722872194 +0200
+++ gcc/testsuite/gfortran.dg/gomp/tile-imperfect-nest-2.f90	2024-06-04 13:30:00.722872194 +0200
@@ -0,0 +1,74 @@ 
+subroutine test0m
+  integer, allocatable, dimension (:,:) :: a,b,c
+  integer :: i, j, k, inner
+  !$omp parallel do collapse(2) private(inner)
+  do i = 1,m
+    !$omp tile sizes (8, 1) ! { dg-error "!\\\$OMP TILE inner loops must be perfectly nested" }
+    do j = 1,n
+      do k = 1, n
+        if (k == 1) then
+          inner = 0
+        endif
+        inner = inner + a(k, i) * b(j, k)
+      end do
+      c(j, i) = inner
+    end do
+  end do
+end subroutine test0m
+
+subroutine test1
+  integer, allocatable, dimension (:,:) :: a,b,c
+  integer :: i, j, k, inner
+  !$omp parallel do collapse(2) private(inner)
+  !$omp tile sizes (8, 1)
+  do i = 1,m
+    !$omp tile sizes (8, 1) ! { dg-error "!\\\$OMP TILE inner loops must be perfectly nested" }
+    do j = 1,n
+      !$omp unroll partial(10)
+      do k = 1, n
+        if (k == 1) then
+          inner = 0
+        endif
+        inner = inner + a(k, i) * b(j, k)
+      end do
+      c(j, i) = inner
+    end do
+  end do
+end subroutine test1
+
+subroutine test2
+  integer, allocatable, dimension (:,:) :: a,b,c
+  integer :: i, j, k, inner
+  !$omp parallel do collapse(2) private(inner)
+  !$omp tile sizes (8, 1)
+  do i = 1,m
+    !$omp tile sizes (8, 1) ! { dg-error "!\\\$OMP TILE inner loops must be perfectly nested" }
+    do j = 1,n
+      do k = 1, n
+        if (k == 1) then
+          inner = 0
+        endif
+        inner = inner + a(k, i) * b(j, k)
+      end do
+      c(j, i) = inner
+    end do
+  end do
+end subroutine test2
+
+subroutine test3
+  integer, allocatable, dimension (:,:) :: a,b,c
+  integer :: i, j, k, inner
+  !$omp parallel do collapse(2) private(inner)
+  do i = 1,m
+    !$omp tile sizes (8, 1) ! { dg-error "!\\\$OMP TILE inner loops must be perfectly nested" }
+    do j = 1,n
+      do k = 1, n
+        if (k == 1) then
+          inner = 0
+        endif
+        inner = inner + a(k, i) * b(j, k)
+      end do
+      c(j, i) = inner
+    end do
+  end do
+end subroutine test3
--- gcc/testsuite/gfortran.dg/gomp/tile-inner-loops-1.f90.jj	2024-06-04 13:30:00.709872367 +0200
+++ gcc/testsuite/gfortran.dg/gomp/tile-inner-loops-1.f90	2024-06-04 13:30:00.709872367 +0200
@@ -0,0 +1,16 @@ 
+! { dg-additional-options "-O2 -fdump-tree-original -fdump-tree-gimple" }
+
+subroutine test1
+  !$omp parallel do collapse(2)
+  do i=0,100
+    !$omp tile sizes(4)
+    do j=-300,100
+      call dummy (j)
+    end do
+  end do
+end subroutine test1
+
+! Collapse of the gimple_omp_for should be unaffacted by the transformation
+! { dg-final { scan-tree-dump-times "#pragma omp for nowait collapse\\\(2\\\)" 1 "original" } }
+! { dg-final { scan-tree-dump-times "#pragma omp tile sizes\\\(4\\\)" 1 "original" } }
+! { dg-final { scan-tree-dump-times "#pragma omp for nowait collapse\\\(2\\\)\[\n\r\]+ +for \\\(i = 0; i <= 100; i = i \\\+ 1\\\)\[\n\r\]+ +for \\\(j.\\\d = -300; j.\\\d <= 100; j.\\\d = j.\\\d \\\+ 4\\\)" 1 "gimple" } }
--- gcc/testsuite/gfortran.dg/gomp/tile-inner-loops-2.f90.jj	2024-06-04 13:30:00.711872340 +0200
+++ gcc/testsuite/gfortran.dg/gomp/tile-inner-loops-2.f90	2024-06-04 13:30:00.711872340 +0200
@@ -0,0 +1,20 @@ 
+! { dg-additional-options "-O2 -fdump-tree-original -fdump-tree-gimple" }
+
+subroutine test2
+  !$omp parallel do
+  !$omp tile sizes(3,3)
+  do i=0,100
+    do j=-300,100
+      !$omp tile sizes(3,3)
+      do k=-300,100
+        do l=0,100
+          call dummy (l)
+        end do
+      end do
+    end do
+  end do
+end subroutine test2
+
+! { dg-final { scan-tree-dump-times "#pragma omp for nowait" 1 "original" } }
+! { dg-final { scan-tree-dump-times "#pragma omp tile sizes\\\(3, 3\\\)" 2 "original" } }
+! { dg-final { scan-tree-dump-times "#pragma omp for nowait\[\n\r\]+ +for \\\(i.\\\d = 0; i.\\\d <= 100; i.\\\d = i.\\\d \\\+ 3\\\)" 1 "gimple" } }
--- gcc/testsuite/gfortran.dg/gomp/tile-inner-loops-3.f90.jj	2024-06-04 13:30:00.726872141 +0200
+++ gcc/testsuite/gfortran.dg/gomp/tile-inner-loops-3.f90	2024-06-04 13:30:00.726872141 +0200
@@ -0,0 +1,22 @@ 
+! { dg-additional-options "-O2 -fdump-tree-original -fdump-tree-gimple" }
+
+subroutine test3
+  !$omp parallel do
+  !$omp tile sizes(3,3,3)
+  do i=0,100
+    do j=-300,100
+      !$omp tile sizes(3,3)
+      do k=-300,100
+        do l=0,100
+          call dummy (l)
+        end do
+      end do
+    end do
+  end do
+end subroutine test3
+
+! { dg-final { scan-tree-dump-times "#pragma omp for nowait" 1 "original" } }
+! { dg-final { scan-tree-dump-times "#pragma omp tile sizes\\\(3, 3, 3\\\)" 1 "original" } }
+! { dg-final { scan-tree-dump-times "#pragma omp tile sizes\\\(3, 3\\\)" 1 "original" } }
+! { dg-final { scan-tree-dump-not "#pragma omp tile" "gimple" } }
+! { dg-final { scan-tree-dump-times "#pragma omp for nowait\[\n\r\]+ +for \\\(i.\\\d = 0; i.\\\d <= 100; i.\\\d = i.\\\d \\\+ 3\\\)" 1 "gimple" } }
--- gcc/testsuite/gfortran.dg/gomp/tile-inner-loops-4.f90.jj	2024-06-04 13:30:00.712872327 +0200
+++ gcc/testsuite/gfortran.dg/gomp/tile-inner-loops-4.f90	2024-06-04 13:30:00.712872327 +0200
@@ -0,0 +1,14 @@ 
+subroutine test3
+  !$omp parallel do
+  !$omp tile sizes(3)
+  do i=0,100
+    do j=-300,100
+      !$omp tile sizes(3,3)
+      do k=-300,100
+        do l=0,100
+          call dummy (l)
+        end do
+      end do
+    end do
+  end do
+end subroutine test3
--- gcc/testsuite/gfortran.dg/gomp/tile-inner-loops-5.f90.jj	2024-06-04 13:30:00.729872101 +0200
+++ gcc/testsuite/gfortran.dg/gomp/tile-inner-loops-5.f90	2024-06-04 13:30:00.729872101 +0200
@@ -0,0 +1,59 @@ 
+subroutine test1a
+  !$omp parallel do
+  !$omp tile sizes(3,3,3)
+  do i=0,100
+    do j=-300,100
+      !$omp tile sizes(5)
+      do k=-300,100
+        call dummy (k)
+      end do
+    end do
+  end do
+end subroutine test1a
+
+subroutine test2a
+  !$omp parallel do
+  !$omp tile sizes(3,3,3,3)
+  do i=0,100
+    do j=-300,100
+      !$omp tile sizes(5,5)
+      do k=-300,100
+        do l=-300,100
+          do m=-300,100
+            call dummy (m)
+          end do
+        end do
+      end do
+    end do
+  end do
+end subroutine test2a
+
+subroutine test1b
+  !$omp parallel do
+  !$omp tile sizes(3,3,3)
+  do i=0,100
+    do j=-300,100
+      !$omp tile sizes(5)
+      do k=-300,100
+        call dummy (k)
+      end do
+    end do
+  end do
+end subroutine test1b
+
+subroutine test2b
+  !$omp parallel do
+  !$omp tile sizes(3,3,3,3)
+  do i=0,100
+    do j=-300,100
+      !$omp tile sizes(5,5)
+      do k=-300,100
+        do l=-300,100
+          do m=-300,100
+            call dummy (m)
+          end do
+        end do
+      end do
+    end do
+  end do
+end subroutine test2b
--- gcc/testsuite/gfortran.dg/gomp/tile-inner-loops-6.f90.jj	2024-06-04 13:30:00.711872340 +0200
+++ gcc/testsuite/gfortran.dg/gomp/tile-inner-loops-6.f90	2024-06-04 13:30:00.711872340 +0200
@@ -0,0 +1,13 @@ 
+subroutine test
+  !$omp tile sizes(3,3,3)
+  do i=0,100
+    do j=-300,100
+      !$omp tile sizes(3,3)
+      do k=-300,100
+        do l=0,100
+          call dummy (l)
+        end do
+      end do
+    end do
+  end do
+end subroutine test
--- gcc/testsuite/gfortran.dg/gomp/tile-inner-loops-7.f90.jj	2024-06-04 13:30:00.728872114 +0200
+++ gcc/testsuite/gfortran.dg/gomp/tile-inner-loops-7.f90	2024-06-04 13:30:00.728872114 +0200
@@ -0,0 +1,13 @@ 
+subroutine test3
+  !$omp tile sizes(3)
+  do i=0,100
+    do j=-300,100
+      !$omp tile sizes(3,3)
+      do k=-300,100
+        do l=0,100
+          call dummy (l)
+        end do
+      end do
+    end do
+  end do
+end subroutine test3
--- gcc/testsuite/gfortran.dg/gomp/tile-inner-loops-8.f90.jj	2024-06-04 13:30:00.722872194 +0200
+++ gcc/testsuite/gfortran.dg/gomp/tile-inner-loops-8.f90	2024-06-04 13:30:00.722872194 +0200
@@ -0,0 +1,63 @@ 
+subroutine test3a
+  !$omp parallel do
+  !$omp tile sizes(3,3,3,3)
+  do i=0,100
+    do j=-300,100
+      !$omp tile sizes(5) ! { dg-error "TILE construct at \\\(1\\\) generates 1 loops with canonical form but 2 loops are needed" }
+      do k=-300,100
+        do l=-300,100
+          call dummy (l)
+        end do
+      end do
+    end do
+  end do
+end subroutine test3a
+
+subroutine test4a
+  !$omp parallel do
+  !$omp tile sizes(3,3,3,3,3)
+  do i=0,100
+     do j=-300,100
+        !$omp tile sizes(5,5) ! { dg-error "TILE construct at \\\(1\\\) generates 2 loops with canonical form but 3 loops are needed" }
+        do k=-300,100
+           do l=-300,100
+           do m=-300,100
+              call dummy (m)
+           end do
+           end do
+        end do
+     end do
+  end do
+end subroutine test4a
+
+subroutine test3b
+  !$omp parallel do
+  !$omp tile sizes(3,3,3,3)
+  do i=0,100
+    do j=-300,100
+      !$omp tile sizes(5) ! { dg-error "TILE construct at \\\(1\\\) generates 1 loops with canonical form but 2 loops are needed" }
+      do k=-300,100
+        do l=-300,100
+          call dummy (l)
+        end do
+      end do
+    end do
+  end do
+end subroutine test3b
+
+subroutine test4b
+  !$omp parallel do
+  !$omp tile sizes(3,3,3,3,3)
+  do i=0,100
+    do j=-300,100
+      !$omp tile sizes(5,5) ! { dg-error "TILE construct at \\\(1\\\) generates 2 loops with canonical form but 3 loops are needed" }
+      do k=-300,100
+        do l=-300,100
+          do m=-300,100
+            call dummy (m)
+          end do
+        end do
+      end do
+    end do
+  end do
+end subroutine test4b
--- gcc/testsuite/gfortran.dg/gomp/tile-non-rectangular-1.f90.jj	2024-06-04 13:30:00.729872101 +0200
+++ gcc/testsuite/gfortran.dg/gomp/tile-non-rectangular-1.f90	2024-06-04 13:30:00.729872101 +0200
@@ -0,0 +1,23 @@ 
+subroutine test1
+  !$omp tile sizes(1)
+  do i = 1,100
+    do j = 1,i
+      do k = 1,100
+        call dummy(i)
+      end do
+    end do
+  end do
+  !$omp end tile
+end subroutine test1
+
+subroutine test5
+  !$omp tile sizes(1,2)
+  do i = 1,100
+    do j = 1,100
+      do k = 1,j
+        call dummy(i)
+      end do
+    end do
+  end do
+  !$omp end tile
+end subroutine test5
--- gcc/testsuite/gfortran.dg/gomp/tile-non-rectangular-2.f90.jj	2024-06-04 13:30:00.726872141 +0200
+++ gcc/testsuite/gfortran.dg/gomp/tile-non-rectangular-2.f90	2024-06-04 13:30:00.726872141 +0200
@@ -0,0 +1,11 @@ 
+subroutine test
+  !$omp tile sizes(1,2,1) ! { dg-error "non-rectangular 'tile'" }
+  do i = 1,100
+    do j = 1,100
+      do k = 1,i
+        call dummy(i)
+      end do
+    end do
+  end do
+  !$end omp tile
+end subroutine test
--- gcc/testsuite/gfortran.dg/gomp/tile-non-rectangular-3.f90.jj	2024-06-04 13:30:00.709872367 +0200
+++ gcc/testsuite/gfortran.dg/gomp/tile-non-rectangular-3.f90	2024-06-04 13:30:00.709872367 +0200
@@ -0,0 +1,47 @@ 
+subroutine test2
+  !$omp tile sizes(1,2) ! { dg-error "non-rectangular 'tile'" }
+  do i = 1,100
+    do j = 1,i
+      do k = 1,100
+        call dummy(i)
+      end do
+    end do
+  end do
+  !$omp end tile
+end subroutine test2
+
+subroutine test3
+  !$omp tile sizes(1,2,1) ! { dg-error "non-rectangular 'tile'" }
+  do i = 1,100
+    do j = 1,i
+      do k = 1,100
+        call dummy(i)
+      end do
+    end do
+  end do
+  !$omp end tile
+end subroutine test3
+
+subroutine test4
+  !$omp tile sizes(1,2,1) ! { dg-error "non-rectangular 'tile'" }
+  do i = 1,100
+    do j = 1,100
+      do k = 1,i
+        call dummy(i)
+      end do
+    end do
+  end do
+  !$omp end tile
+end subroutine test4
+
+subroutine test6
+  !$omp tile sizes(1,2,1) ! { dg-error "non-rectangular 'tile'" }
+  do i = 1,100
+    do j = 1,100
+      do k = 1,j
+        call dummy(i)
+      end do
+    end do
+  end do
+  !$omp end tile
+end subroutine test6
--- gcc/testsuite/gfortran.dg/gomp/tile-unroll-1.f90.jj	2024-06-04 13:30:00.711872340 +0200
+++ gcc/testsuite/gfortran.dg/gomp/tile-unroll-1.f90	2024-06-04 13:30:00.711872340 +0200
@@ -0,0 +1,18 @@ 
+function mult (a, b) result (c)
+  integer, allocatable, dimension (:,:) :: a,b,c
+  integer :: i, j, k, inner
+
+  allocate(c( n, m ))
+
+  !$omp tile sizes (8)
+  !$omp unroll partial(1)
+  do i = 1,m
+    do j = 1,n
+      inner = 0
+      do k = 1, n
+        inner = inner + a(k, i) * b(j, k)
+      end do
+      c(j, i) = inner
+    end do
+  end do
+end function mult
--- gcc/testsuite/gfortran.dg/gomp/tile-unroll-2.f90.jj	2024-06-04 13:30:00.726872141 +0200
+++ gcc/testsuite/gfortran.dg/gomp/tile-unroll-2.f90	2024-06-04 13:30:00.726872141 +0200
@@ -0,0 +1,44 @@ 
+function mult (a, b) result (c)
+  integer, allocatable, dimension (:,:) :: a,b,c
+  integer :: i, j, k, inner
+
+  allocate(c( n, m ))
+
+  !$omp parallel do collapse(2)
+  !$omp tile sizes (8,8)
+  !$omp unroll partial(2) ! { dg-error "UNROLL construct at \\\(1\\\) with PARTIAL clause generates just one loop with canonical form but 2 loops are needed" }
+  do i = 1,m
+    do j = 1,n
+      inner = 0
+      do k = 1, n
+        inner = inner + a(k, i) * b(j, k)
+      end do
+    c(j, i) = inner
+    end do
+  end do
+
+  !$omp tile sizes (8,8)
+  !$omp unroll partial(2) ! { dg-error "UNROLL construct at \\\(1\\\) with PARTIAL clause generates just one loop with canonical form but 2 loops are needed" }
+  do i = 1,m
+    do j = 1,n
+      inner = 0
+      do k = 1, n
+        inner = inner + a(k, i) * b(j, k)
+      end do
+      c(j, i) = inner
+    end do
+  end do
+
+  !$omp parallel do collapse(2)
+  !$omp tile sizes (8,8)
+  !$omp unroll full ! { dg-error "Generated loop of UNROLL construct at \\\(1\\\) without PARTIAL clause does not have canonical form" }
+  do i = 1,m
+    do j = 1,n
+      inner = 0
+      do k = 1, n
+        inner = inner + a(k, i) * b(j, k)
+      end do
+      c(j, i) = inner
+    end do
+  end do
+end function mult
--- gcc/testsuite/gfortran.dg/gomp/unroll-1.f90.jj	2024-06-04 13:30:00.711872340 +0200
+++ gcc/testsuite/gfortran.dg/gomp/unroll-1.f90	2024-06-04 13:30:00.711872340 +0200
@@ -0,0 +1,35 @@ 
+subroutine test16
+  implicit none
+  integer :: i
+
+  !$omp do
+  !$omp unroll partial(1)
+  do i = 1,100
+    call dummy(i)
+  end do
+  !$omp end unroll
+end subroutine test16
+
+subroutine test17
+  implicit none
+  integer :: i
+
+  !$omp do
+  !$omp unroll partial(2)
+  do i = 1,100
+    call dummy(i)
+  end do
+  !$omp end unroll
+end subroutine test17
+
+subroutine test20
+  implicit none
+  integer :: i
+
+  !$omp do
+  !$omp unroll partial
+  do i = 1,100
+    call dummy(i)
+  end do
+  !$omp end unroll
+end subroutine test20
--- gcc/testsuite/gfortran.dg/gomp/unroll-2.f90.jj	2024-06-04 13:30:00.711872340 +0200
+++ gcc/testsuite/gfortran.dg/gomp/unroll-2.f90	2024-06-04 13:30:00.711872340 +0200
@@ -0,0 +1,22 @@ 
+! { dg-additional-options "-fdump-tree-original" }
+
+subroutine test1
+  implicit none
+  integer :: i
+  !$omp unroll
+  do i = 1,10
+    call dummy(i)
+  end do
+end subroutine test1
+
+subroutine test2
+  implicit none
+  integer :: i
+  !$omp unroll full
+  do i = 1,10
+    call dummy(i)
+  end do
+end subroutine test2
+
+! { dg-final { scan-tree-dump-times "#pragma omp unroll" 2 "original" } }
+! { dg-final { scan-tree-dump-times "#pragma omp unroll full" 1 "original" } }
--- gcc/testsuite/gfortran.dg/gomp/unroll-3.f90.jj	2024-06-04 13:30:00.710872353 +0200
+++ gcc/testsuite/gfortran.dg/gomp/unroll-3.f90	2024-06-04 13:30:00.710872353 +0200
@@ -0,0 +1,15 @@ 
+! { dg-additional-options "-O2 -fdump-tree-original -fdump-tree-gimple" }
+
+subroutine test1
+  implicit none
+  integer :: i
+  !$omp unroll full
+  do i = 1,10
+    call dummy(i)
+  end do
+end subroutine test1
+
+! Loop should be removed with 10 copies of the body remaining
+! { dg-final { scan-tree-dump "#pragma omp unroll full" "original" } }
+! { dg-final { scan-tree-dump-not "#pragma omp" "gimple" } }
+! { dg-final { scan-tree-dump "\.ANNOTATE \\\(\[^\n\r\]*, 1, 10\\\);" "gimple" } }
--- gcc/testsuite/gfortran.dg/gomp/unroll-4.f90.jj	2024-06-04 13:30:00.723872181 +0200
+++ gcc/testsuite/gfortran.dg/gomp/unroll-4.f90	2024-06-04 13:30:00.723872181 +0200
@@ -0,0 +1,15 @@ 
+! { dg-additional-options "-O2 -fdump-tree-original -fdump-tree-gimple" }
+
+subroutine test1
+  implicit none
+  integer :: i
+  !$omp unroll
+  do i = 1,100
+    call dummy(i)
+  end do
+end subroutine test1
+
+! { dg-final { scan-tree-dump "#pragma omp unroll" "original" } }
+! { dg-final { scan-tree-dump-not "#pragma omp" "gimple" } }
+! { dg-final { scan-tree-dump-times "dummy" 1 "gimple" } }
+! { dg-final { scan-tree-dump "\.ANNOTATE \\\(\[^\n\r\]*, 1, 8\\\);" "gimple" } }
--- gcc/testsuite/gfortran.dg/gomp/unroll-5.f90.jj	2024-06-04 13:30:00.728872114 +0200
+++ gcc/testsuite/gfortran.dg/gomp/unroll-5.f90	2024-06-04 13:30:00.728872114 +0200
@@ -0,0 +1,14 @@ 
+! { dg-additional-options "-O2 -fdump-tree-original -fdump-tree-gimple" }
+
+subroutine test1
+  implicit none
+  integer :: i
+  !$omp unroll partial
+  do i = 1,100
+    call dummy(i)
+  end do
+end subroutine test1
+
+! { dg-final { scan-tree-dump "#pragma omp unroll partial" "original" } }
+! { dg-final { scan-tree-dump-not "#pragma omp" "gimple" } }
+! { dg-final { scan-tree-dump "\.ANNOTATE \\\(\[^\n\r\]*, 1, 8\\\);" "gimple" } }
--- gcc/testsuite/gfortran.dg/gomp/unroll-6.f90.jj	2024-06-04 13:30:00.710872353 +0200
+++ gcc/testsuite/gfortran.dg/gomp/unroll-6.f90	2024-06-04 13:30:00.710872353 +0200
@@ -0,0 +1,241 @@ 
+subroutine test1
+  implicit none
+  integer :: i
+
+  !$omp do
+  !$omp unroll ! { dg-error "Generated loop of UNROLL construct at \\\(1\\\) without PARTIAL clause does not have canonical form" }
+  do i = 1,100
+    call dummy(i)
+  end do
+end subroutine test1
+
+subroutine test2
+  implicit none
+  integer :: i
+
+  !$omp do
+  !$omp unroll ! { dg-error "Generated loop of UNROLL construct at \\\(1\\\) without PARTIAL clause does not have canonical form" }
+  do i = 1,100
+    call dummy(i)
+  end do
+  !$omp end unroll
+end subroutine test2
+
+subroutine test3
+  implicit none
+  integer :: i
+
+  !$omp do
+  !$omp unroll ! { dg-error "Generated loop of UNROLL construct at \\\(1\\\) without PARTIAL clause does not have canonical form" }
+  do i = 1,100
+    call dummy(i)
+  end do
+  !$omp end do
+end subroutine test3
+
+subroutine test4
+  implicit none
+  integer :: i
+
+  !$omp do
+  !$omp unroll ! { dg-error "Generated loop of UNROLL construct at \\\(1\\\) without PARTIAL clause does not have canonical form" }
+  do i = 1,100
+    call dummy(i)
+  end do
+  !$omp end unroll
+  !$omp end do
+end subroutine test4
+
+subroutine test5
+  implicit none
+  integer :: i
+
+  !$omp unroll
+  !$omp unroll ! { dg-error "Generated loop of UNROLL construct at \\\(1\\\) without PARTIAL clause does not have canonical form" }
+  do i = 1,100
+    call dummy(i)
+  end do
+end subroutine test5
+
+subroutine test6
+  implicit none
+  integer :: i
+
+  !$omp unroll
+  !$omp unroll ! { dg-error "Generated loop of UNROLL construct at \\\(1\\\) without PARTIAL clause does not have canonical form" }
+  do i = 1,100
+    call dummy(i)
+  end do
+  !$omp end unroll
+end subroutine test6
+
+subroutine test7
+  implicit none
+  integer :: i
+
+  !$omp unroll
+  !$omp unroll ! { dg-error "Generated loop of UNROLL construct at \\\(1\\\) without PARTIAL clause does not have canonical form" }
+  do i = 1,100
+    call dummy(i)
+  end do
+  !$omp end unroll
+end subroutine test7
+
+subroutine test8
+  implicit none
+  integer :: i
+
+  !$omp unroll
+  !$omp unroll ! { dg-error "Generated loop of UNROLL construct at \\\(1\\\) without PARTIAL clause does not have canonical form" }
+  do i = 1,100
+    call dummy(i)
+  end do
+  !$omp end unroll
+  !$omp end unroll
+end subroutine test8
+
+subroutine test9
+  implicit none
+  integer :: i
+
+  !$omp do
+  !$omp unroll full ! { dg-error "Generated loop of UNROLL construct at \\\(1\\\) without PARTIAL clause does not have canonical form" }
+  do i = 1,100
+    call dummy(i)
+  end do
+end subroutine test9
+
+subroutine test10
+  implicit none
+  integer :: i
+
+  !$omp unroll full
+  !$omp unroll full ! { dg-error "Generated loop of UNROLL construct at \\\(1\\\) without PARTIAL clause does not have canonical form" }
+  do i = 1,100
+    call dummy(i)
+  end do
+end subroutine test10
+
+subroutine test11
+  implicit none
+  integer :: i,j
+
+  !$omp do
+  !$omp unroll ! { dg-error "Generated loop of UNROLL construct at \\\(1\\\) without PARTIAL clause does not have canonical form" }
+  do i = 1,100
+    call dummy(i)
+    do j = 1,100
+      call dummy2(i,j)
+    end do
+  end do
+end subroutine test11
+
+subroutine test12
+  implicit none
+  integer :: i,j
+
+  !$omp do
+  !$omp unroll ! { dg-error "Generated loop of UNROLL construct at \\\(1\\\) without PARTIAL clause does not have canonical form" }
+  do i = 1,100
+    !$omp unroll
+    !$omp unroll ! { dg-error "Generated loop of UNROLL construct at \\\(1\\\) without PARTIAL clause does not have canonical form" }
+    call dummy(i) ! { dg-error "Unexpected CALL statement at \\\(1\\\)" }
+    !$omp unroll ! { dg-error "Generated loop of UNROLL construct at \\\(1\\\) without PARTIAL clause does not have canonical form" }
+    do j = 1,100
+      call dummy2(i,j)
+    end do
+  end do
+end subroutine test12
+
+subroutine test13
+  implicit none
+  integer :: i,j
+
+  !$omp do
+  !$omp unroll ! { dg-error "Generated loop of UNROLL construct at \\\(1\\\) without PARTIAL clause does not have canonical form" }
+  do i = 1,100
+    !$omp unroll
+    !$omp unroll ! { dg-error "Generated loop of UNROLL construct at \\\(1\\\) without PARTIAL clause does not have canonical form" }
+    !$omp unroll ! { dg-error "Generated loop of UNROLL construct at \\\(1\\\) without PARTIAL clause does not have canonical form" }
+    do j = 1,100
+      call dummy2(i,j)
+    end do
+    call dummy(i)
+  end do
+end subroutine test13
+
+subroutine test14
+  implicit none
+  integer :: i
+
+  !$omp unroll
+  !$omp unroll ! { dg-error "Generated loop of UNROLL construct at \\\(1\\\) without PARTIAL clause does not have canonical form" }
+  do i = 1,100
+    call dummy(i)
+  end do
+  !$omp end unroll
+  !$omp end unroll
+  !$omp end unroll ! { dg-error "Unexpected !\\\$OMP END UNROLL statement at \\\(1\\\)" }
+end subroutine test14
+
+subroutine test15
+  implicit none
+  integer :: i
+
+  !$omp do
+  !$omp unroll ! { dg-error "Generated loop of UNROLL construct at \\\(1\\\) without PARTIAL clause does not have canonical form" }
+  !$omp unroll ! { dg-error "Generated loop of UNROLL construct at \\\(1\\\) without PARTIAL clause does not have canonical form" }
+  do i = 1,100
+    call dummy(i)
+  end do
+  !$omp end unroll
+  !$omp end unroll
+  !$omp end unroll ! { dg-error "Unexpected !\\\$OMP END UNROLL statement at \\\(1\\\)" }
+end subroutine test15
+
+subroutine test18
+  implicit none
+  integer :: i
+
+  !$omp do
+  !$omp unroll partial(0) ! { dg-error "PARTIAL clause argument not constant positive integer at \\\(1\\\)" }
+  do i = 1,100
+    call dummy(i)
+  end do
+  !$omp end unroll
+end subroutine test18
+
+subroutine test19
+  implicit none
+  integer :: i
+
+  !$omp do
+  !$omp unroll partial(-10) ! { dg-error "PARTIAL clause argument not constant positive integer at \\\(1\\\)" }
+  do i = 1,100
+    call dummy(i)
+  end do
+  !$omp end unroll
+end subroutine test19
+
+subroutine test21
+  implicit none
+  integer :: i
+
+  !$omp unroll partial
+  do concurrent  (i = 1:100) ! { dg-error "!\\\$OMP UNROLL cannot be a DO CONCURRENT loop at \\\(1\\\)" }
+    call dummy(i) ! { dg-error "Subroutine call to 'dummy' in DO CONCURRENT block at \\\(1\\\) is not PURE" }
+  end do
+  !$omp end unroll
+end subroutine test21
+
+subroutine test22
+  implicit none
+  integer :: i
+
+  !$omp do
+  !$omp unroll partial
+  do concurrent  (i = 1:100) ! { dg-error "!\\\$OMP UNROLL cannot be a DO CONCURRENT loop at \\\(1\\\)" }
+    call dummy(i) ! { dg-error "Subroutine call to 'dummy' in DO CONCURRENT block at \\\(1\\\) is not PURE" }
+  end do
+  !$omp end unroll
+end subroutine test22
--- gcc/testsuite/gfortran.dg/gomp/unroll-7.f90.jj	2024-06-04 13:30:00.729872101 +0200
+++ gcc/testsuite/gfortran.dg/gomp/unroll-7.f90	2024-06-04 13:30:00.729872101 +0200
@@ -0,0 +1,35 @@ 
+subroutine foo
+  integer :: i
+  !$omp do
+  !$omp unroll partial ( 3 )
+  do i = 1, 512
+  end do
+  !$omp end unroll
+  !$omp end do
+end subroutine foo
+
+subroutine bar
+  integer :: i
+  !$omp do
+  !$omp unroll partial(3)
+  do i = 1, 512
+  end do
+  !$omp end do
+end subroutine bar
+
+subroutine baz
+  integer :: i
+  !$omp do
+  !$omp unroll partial (3)
+  do i = 1, 512
+  end do
+end subroutine baz
+
+subroutine qux
+  integer :: i
+  !$omp do
+  !$omp unroll partial (3)
+  do i = 1, 512
+  end do
+  !$omp end unroll
+end subroutine qux
--- gcc/testsuite/gfortran.dg/gomp/unroll-8.f90.jj	2024-06-04 13:30:00.710872353 +0200
+++ gcc/testsuite/gfortran.dg/gomp/unroll-8.f90	2024-06-04 13:30:00.710872353 +0200
@@ -0,0 +1,26 @@ 
+! { dg-additional-options "-O2 -fdump-tree-original -fdump-tree-gimple" } */
+
+subroutine test1
+  implicit none
+  integer :: i
+  !$omp parallel do collapse(1)
+  !$omp unroll partial(4)
+  !$omp unroll partial(3)
+  !$omp unroll partial(2)
+  !$omp unroll partial(1)
+  do i = 1,100
+    call dummy(i)
+  end do
+end subroutine test1
+
+! Loop should be unrolled 1 * 2 * 3 * 4 = 24 times
+! { dg-final { scan-tree-dump "#pragma omp for nowait collapse\\\(1\\\)" "original" } }
+! { dg-final { scan-tree-dump "#pragma omp unroll partial\\\(1\\\)" "original" } }
+! { dg-final { scan-tree-dump "#pragma omp unroll partial\\\(2\\\)" "original" } }
+! { dg-final { scan-tree-dump "#pragma omp unroll partial\\\(3\\\)" "original" } }
+! { dg-final { scan-tree-dump "#pragma omp unroll partial\\\(4\\\)" "original" } }
+! { dg-final { scan-tree-dump-not "#pragma omp unroll" "gimple" } }
+! { dg-final { scan-tree-dump-times "\.ANNOTATE \\\(\[^\n\r\]*, 1, 2\\\);" 1 "gimple" } }
+! { dg-final { scan-tree-dump-times "\.ANNOTATE \\\(\[^\n\r\]*, 1, 3\\\);" 1 "gimple" } }
+! { dg-final { scan-tree-dump-times "\.ANNOTATE \\\(\[^\n\r\]*, 1, 4\\\);" 1 "gimple" } }
+! { dg-final { scan-tree-dump-times "#pragma omp for" 1 "gimple" } }
--- gcc/testsuite/gfortran.dg/gomp/unroll-9.f90.jj	2024-06-04 13:30:00.728872114 +0200
+++ gcc/testsuite/gfortran.dg/gomp/unroll-9.f90	2024-06-04 13:30:00.728872114 +0200
@@ -0,0 +1,22 @@ 
+! { dg-additional-options "-O2 -fdump-tree-original -fdump-tree-gimple" }
+
+subroutine test1
+  implicit none
+  integer :: i
+  !$omp unroll full
+  !$omp unroll partial(3)
+  !$omp unroll partial(2)
+  !$omp unroll partial(1)
+  do i = 1,100
+    call dummy(i)
+  end do
+end subroutine test1
+
+! { dg-final { scan-tree-dump "#pragma omp unroll full" "original" } }
+! { dg-final { scan-tree-dump "#pragma omp unroll partial\\\(1\\\)" "original" } }
+! { dg-final { scan-tree-dump "#pragma omp unroll partial\\\(2\\\)" "original" } }
+! { dg-final { scan-tree-dump "#pragma omp unroll partial\\\(3\\\)" "original" } }
+! { dg-final { scan-tree-dump-not "#pragma omp unroll" "gimple" } }
+! { dg-final { scan-tree-dump "\.ANNOTATE \\\(\[^\n\r]*, 1, 2\\\);" "gimple" } }
+! { dg-final { scan-tree-dump "\.ANNOTATE \\\(\[^\n\r]*, 1, 3\\\);" "gimple" } }
+! { dg-final { scan-tree-dump "\.ANNOTATE \\\(\[^\n\r]*, 1, 17\\\);" "gimple" } }
--- gcc/testsuite/gfortran.dg/gomp/unroll-10.f90.jj	2024-06-04 13:30:00.727872128 +0200
+++ gcc/testsuite/gfortran.dg/gomp/unroll-10.f90	2024-06-04 13:30:00.727872128 +0200
@@ -0,0 +1,6 @@ 
+subroutine test(i)
+  !$omp unroll full
+  call dummy0 ! { dg-error "Unexpected CALL statement at \\\(1\\\)" }
+end subroutine test ! { dg-error "Unexpected END statement at \\\(1\\\)" }
+
+! { dg-error "Unexpected end of file" "" { target "*-*-*" } 0 }
--- gcc/testsuite/gfortran.dg/gomp/unroll-11.f90.jj	2024-06-04 13:30:00.710872353 +0200
+++ gcc/testsuite/gfortran.dg/gomp/unroll-11.f90	2024-06-04 13:30:00.710872353 +0200
@@ -0,0 +1,75 @@ 
+subroutine test1(i)
+  implicit none
+  integer :: i
+  !$omp unroll
+  !$omp unroll full ! { dg-error "Generated loop of UNROLL construct at \\\(1\\\) without PARTIAL clause does not have canonical form" }
+  do i = 1,10
+    call dummy(i)
+  end do
+end subroutine test1
+
+subroutine test2(i)
+  implicit none
+  integer :: i
+  !$omp unroll full
+  !$omp unroll full ! { dg-error "Generated loop of UNROLL construct at \\\(1\\\) without PARTIAL clause does not have canonical form" }
+  !$omp unroll ! { dg-error "Generated loop of UNROLL construct at \\\(1\\\) without PARTIAL clause does not have canonical form" }
+  do i = 1,10
+    call dummy(i)
+  end do
+end subroutine test2
+
+subroutine test3(i)
+  implicit none
+  integer :: i
+  !$omp unroll full
+  !$omp unroll ! { dg-error "Generated loop of UNROLL construct at \\\(1\\\) without PARTIAL clause does not have canonical form" }
+  !$omp unroll full ! { dg-error "Generated loop of UNROLL construct at \\\(1\\\) without PARTIAL clause does not have canonical form" }
+  !$omp unroll ! { dg-error "Generated loop of UNROLL construct at \\\(1\\\) without PARTIAL clause does not have canonical form" }
+  do i = 1,10
+    call dummy(i)
+  end do
+end subroutine test3
+
+subroutine test4(i)
+  implicit none
+  integer :: i
+  !$omp do
+  !$omp unroll full ! { dg-error "Generated loop of UNROLL construct at \\\(1\\\) without PARTIAL clause does not have canonical form" }
+  do i = 1,10
+    call dummy(i)
+  end do
+end subroutine test4
+
+subroutine test5(i)
+  implicit none
+  integer :: i
+  !$omp do
+  !$omp unroll full ! { dg-error "Generated loop of UNROLL construct at \\\(1\\\) without PARTIAL clause does not have canonical form" }
+  !$omp unroll ! { dg-error "Generated loop of UNROLL construct at \\\(1\\\) without PARTIAL clause does not have canonical form" }
+  do i = 1,10
+    call dummy(i)
+  end do
+end subroutine test5
+
+subroutine test6(i)
+  implicit none
+  integer :: i
+  !$omp do
+  !$omp unroll full ! { dg-error "Generated loop of UNROLL construct at \\\(1\\\) without PARTIAL clause does not have canonical form" }
+  !$omp unroll ! { dg-error "Generated loop of UNROLL construct at \\\(1\\\) without PARTIAL clause does not have canonical form" }
+  do i = 1,10
+    call dummy(i)
+  end do
+end subroutine test6
+
+subroutine test7(i)
+  implicit none
+  integer :: i
+  !$omp loop
+  !$omp unroll full ! { dg-error "Generated loop of UNROLL construct at \\\(1\\\) without PARTIAL clause does not have canonical form" }
+  !$omp unroll ! { dg-error "Generated loop of UNROLL construct at \\\(1\\\) without PARTIAL clause does not have canonical form" }
+  do i = 1,10
+    call dummy(i)
+  end do
+end subroutine test7
--- gcc/testsuite/gfortran.dg/gomp/unroll-12.f90.jj	2024-06-04 13:30:00.727872128 +0200
+++ gcc/testsuite/gfortran.dg/gomp/unroll-12.f90	2024-06-04 13:30:00.727872128 +0200
@@ -0,0 +1,29 @@ 
+subroutine test1
+  implicit none
+  integer :: i
+  !$omp unroll
+  do while (i < 10) ! { dg-error "!\\\$OMP UNROLL cannot be a DO WHILE or DO without loop control at \\\(1\\\)" }
+    call dummy(i)
+    i = i + 1
+  end do
+end subroutine test1
+
+subroutine test2
+  implicit none
+  integer :: i
+  !$omp unroll
+  do ! { dg-error "!\\\$OMP UNROLL cannot be a DO WHILE or DO without loop control at \\\(1\\\)" }
+    call dummy(i)
+    i = i + 1
+    if (i >= 10) exit
+  end do
+end subroutine test2
+
+subroutine test3
+  implicit none
+  integer :: i
+  !$omp unroll
+  do concurrent (i=1:10) ! { dg-error "!\\\$OMP UNROLL cannot be a DO CONCURRENT loop at \\\(1\\\)" }
+    call dummy(i) ! { dg-error "Subroutine call to 'dummy' in DO CONCURRENT block at \\\(1\\\) is not PURE" }
+  end do
+end subroutine test3
--- gcc/testsuite/gfortran.dg/gomp/unroll-13.f90.jj	2024-06-04 13:30:00.711872340 +0200
+++ gcc/testsuite/gfortran.dg/gomp/unroll-13.f90	2024-06-04 13:30:00.711872340 +0200
@@ -0,0 +1,43 @@ 
+subroutine foo
+  integer :: i, j
+  !$omp do collapse(2)
+  do i = 1, 512
+    !$omp unroll partial (3)
+    do j = 1, 512
+    end do
+    !$omp end unroll
+  end do
+  !$omp end do
+end subroutine foo
+
+subroutine bar
+  integer :: i, j
+  !$omp do collapse(2)
+  do i = 1, 512
+    !$omp unroll partial (3)
+    do j = 1, 512
+    end do
+  end do
+  !$omp end do
+end subroutine bar
+
+subroutine baz
+  integer :: i, j
+  !$omp do collapse(2)
+  do i = 1, 512
+    !$omp unroll partial (3)
+    do j = 1, 512
+    end do
+    !$omp end unroll
+  end do
+end subroutine baz
+
+subroutine qux
+  integer :: i, j
+  !$omp do collapse(2)
+  do i = 1, 512
+    !$omp unroll partial (3)
+    do j = 1, 512
+    end do
+  end do
+end subroutine qux
--- gcc/testsuite/gfortran.dg/gomp/unroll-inner-loop-1.f90.jj	2024-06-04 13:30:00.726872141 +0200
+++ gcc/testsuite/gfortran.dg/gomp/unroll-inner-loop-1.f90	2024-06-04 13:30:00.726872141 +0200
@@ -0,0 +1,28 @@ 
+subroutine test1a
+  !$omp parallel do
+  !$omp tile sizes(3,3,3)
+  do i=0,100
+    do j=-300,100
+      !$omp unroll partial(5)
+      do k=-300,100
+        do l=0,100
+          call dummy (l)
+        end do
+      end do
+    end do
+  end do
+end subroutine test1a
+
+subroutine test1b
+  !$omp tile sizes(3,3,3)
+  do i=0,100
+    do j=-300,100
+      !$omp unroll partial(5)
+      do k=-300,100
+        do l=0,100
+          call dummy (l)
+        end do
+      end do
+    end do
+  end do
+end subroutine test1b
--- gcc/testsuite/gfortran.dg/gomp/unroll-inner-loop-2.f90.jj	2024-06-04 13:30:00.729872101 +0200
+++ gcc/testsuite/gfortran.dg/gomp/unroll-inner-loop-2.f90	2024-06-04 13:30:00.728872114 +0200
@@ -0,0 +1,28 @@ 
+subroutine test2a
+  !$omp parallel do
+  !$omp tile sizes(3,3,3,3)
+  do i=0,100
+    do j=-300,100
+      !$omp unroll partial(5)  ! { dg-error "UNROLL construct at \\\(1\\\) with PARTIAL clause generates just one loop with canonical form but 2 loops are needed" }
+      do k=-300,100
+        do l=0,100
+          call dummy (l)
+        end do
+      end do
+    end do
+  end do
+end subroutine test2a
+
+subroutine test2b
+  !$omp tile sizes(3,3,3,3)
+  do i=0,100
+    do j=-300,100
+      !$omp unroll partial(5)  ! { dg-error "UNROLL construct at \\\(1\\\) with PARTIAL clause generates just one loop with canonical form but 2 loops are needed" }
+      do k=-300,100
+        do l=0,100
+          call dummy (l)
+        end do
+      end do
+    end do
+  end do
+end subroutine test2b
--- gcc/testsuite/gfortran.dg/gomp/unroll-no-clause-1.f90.jj	2024-06-04 13:30:00.726872141 +0200
+++ gcc/testsuite/gfortran.dg/gomp/unroll-no-clause-1.f90	2024-06-04 13:30:00.726872141 +0200
@@ -0,0 +1,21 @@ 
+! { dg-additional-options "-O2 -fdump-tree-gimple" }
+
+subroutine test
+  !$omp unroll
+  do i = 1,5
+    do j = 1,10
+      call dummy3(i,j)
+    end do
+  end do
+  !$omp end unroll
+
+  !$omp unroll
+  do i = 1,6
+    do j = 1,6
+      call dummy3(i,j)
+    end do
+  end do
+  !$omp end unroll
+end subroutine test
+
+! { dg-final { scan-tree-dump-times "\.ANNOTATE \\\(\[^\n\r\]*, 1, 8\\\);" 2 "gimple" } }
--- gcc/testsuite/gfortran.dg/gomp/unroll-non-rect-1.f90.jj	2024-06-04 13:30:00.722872194 +0200
+++ gcc/testsuite/gfortran.dg/gomp/unroll-non-rect-1.f90	2024-06-04 13:30:00.722872194 +0200
@@ -0,0 +1,13 @@ 
+subroutine test
+  implicit none
+
+  integer :: i, j, k
+  !$omp unroll full
+  do i = -3, 5
+    do j = 1,10
+      do k = j,j*2 + 1
+        call dummy (i)
+      end do
+    end do
+  end do
+end subroutine
--- gcc/testsuite/gfortran.dg/gomp/unroll-non-rect-2.f90.jj	2024-06-04 13:30:00.728872114 +0200
+++ gcc/testsuite/gfortran.dg/gomp/unroll-non-rect-2.f90	2024-06-05 14:18:40.006454511 +0200
@@ -0,0 +1,22 @@ 
+subroutine test
+  implicit none
+
+  integer :: i, j, k
+  !$omp target parallel do collapse(2)
+  do i = -300, 100
+    !$omp unroll partial
+    do j = i,i*2 ! { dg-message "Non-rectangular loops from generated loops unsupported" }
+      call dummy (i)
+    end do
+  end do
+
+  !$omp target parallel do collapse(3)
+  do i = -300, 100
+    do j = 1,10
+      !$omp unroll partial
+      do k = j,j*2 + 1 ! { dg-message "Non-rectangular loops from generated loops unsupported" }
+        call dummy (i)
+      end do
+    end do
+  end do
+end subroutine
--- gcc/testsuite/gfortran.dg/gomp/unroll-simd-1.f90.jj	2024-06-04 13:30:00.727872128 +0200
+++ gcc/testsuite/gfortran.dg/gomp/unroll-simd-1.f90	2024-06-04 13:30:00.727872128 +0200
@@ -0,0 +1,37 @@ 
+! { dg-options "-fno-openmp -fopenmp-simd" }
+
+subroutine test15
+  implicit none
+  integer :: i
+
+  !$omp simd
+  !$omp unroll partial(1)
+  do i = 1,100
+    call dummy(i)
+  end do
+  !$omp end unroll
+end subroutine test15
+
+subroutine test16
+  implicit none
+  integer :: i
+
+  !$omp simd
+  !$omp unroll partial(2)
+  do i = 1,100
+    call dummy(i)
+  end do
+  !$omp end unroll
+end subroutine test16
+
+subroutine test19
+  implicit none
+  integer :: i
+
+  !$omp simd
+  !$omp unroll partial
+  do i = 1,100
+    call dummy(i)
+  end do
+  !$omp end unroll
+end subroutine test19
--- gcc/testsuite/gfortran.dg/gomp/unroll-simd-2.f90.jj	2024-06-04 13:30:00.712872327 +0200
+++ gcc/testsuite/gfortran.dg/gomp/unroll-simd-2.f90	2024-06-04 13:30:00.712872327 +0200
@@ -0,0 +1,56 @@ 
+! { dg-do run }
+! { dg-options "-O2 -fopenmp-simd -fdump-tree-original -fdump-tree-gimple" }
+
+module test_functions
+  contains
+  integer function compute_sum() result(sum)
+    implicit none
+
+    integer :: i,j
+
+    !$omp simd
+    do i = 1,10,3
+      !$omp unroll full
+      do j = 1,10,3
+        sum = sum + 1
+      end do
+    end do
+  end function
+
+  integer function compute_sum2() result(sum)
+    implicit none
+
+    integer :: i,j
+
+    !$omp simd
+    !$omp unroll partial(2)
+    do i = 1,10,3
+      do j = 1,10,3
+        sum = sum + 1
+      end do
+    end do
+  end function
+end module test_functions
+
+program test
+  use test_functions
+  implicit none
+
+  integer :: result
+
+  result = compute_sum ()
+  write (*,*) result
+  if (result .ne. 16) then
+    call abort
+  end if
+
+  result = compute_sum2 ()
+  write (*,*) result
+  if (result .ne. 16) then
+    call abort
+  end if
+end program
+
+! { dg-final { scan-tree-dump "omp unroll full" "original" } }
+! { dg-final { scan-tree-dump "omp unroll partial\\\(2\\\)" "original" } }
+! { dg-final { scan-tree-dump-not "omp unroll" "gimple" } }
--- gcc/testsuite/gfortran.dg/gomp/unroll-simd-3.f90.jj	2024-06-04 13:30:00.722872194 +0200
+++ gcc/testsuite/gfortran.dg/gomp/unroll-simd-3.f90	2024-06-04 13:30:00.722872194 +0200
@@ -0,0 +1,208 @@ 
+! { dg-options "-fno-openmp -fopenmp-simd" }
+
+subroutine test1
+  implicit none
+  integer :: i
+
+  !$omp simd
+  !$omp unroll ! { dg-error "Generated loop of UNROLL construct at \\\(1\\\) without PARTIAL clause does not have canonical form" }
+  do i = 1,100
+    call dummy(i)
+  end do
+end subroutine test1
+
+subroutine test2
+  implicit none
+  integer :: i
+
+  !$omp simd
+  !$omp unroll ! { dg-error "Generated loop of UNROLL construct at \\\(1\\\) without PARTIAL clause does not have canonical form" }
+  do i = 1,100
+    call dummy(i)
+  end do
+  !$omp end unroll
+end subroutine test2
+
+subroutine test3
+  implicit none
+  integer :: i
+
+  !$omp simd
+  !$omp unroll ! { dg-error "Generated loop of UNROLL construct at \\\(1\\\) without PARTIAL clause does not have canonical form" }
+  do i = 1,100
+    call dummy(i)
+  end do
+  !$omp end do
+end subroutine test3
+
+subroutine test4
+  implicit none
+  integer :: i
+
+  !$omp simd
+  !$omp unroll ! { dg-error "Generated loop of UNROLL construct at \\\(1\\\) without PARTIAL clause does not have canonical form" }
+  do i = 1,100
+    call dummy(i)
+  end do
+  !$omp end unroll
+  !$omp end do
+end subroutine test4
+
+subroutine test5
+  implicit none
+  integer :: i
+
+  !$omp unroll
+  !$omp unroll ! { dg-error "Generated loop of UNROLL construct at \\\(1\\\) without PARTIAL clause does not have canonical form" }
+  do i = 1,100
+    call dummy(i)
+  end do
+end subroutine test5
+
+subroutine test6
+  implicit none
+  integer :: i
+
+  !$omp unroll
+  !$omp unroll ! { dg-error "Generated loop of UNROLL construct at \\\(1\\\) without PARTIAL clause does not have canonical form" }
+  do i = 1,100
+    call dummy(i)
+  end do
+  !$omp end unroll
+end subroutine test6
+
+subroutine test7
+  implicit none
+  integer :: i
+
+  !$omp unroll
+  !$omp unroll ! { dg-error "Generated loop of UNROLL construct at \\\(1\\\) without PARTIAL clause does not have canonical form" }
+  do i = 1,100
+    call dummy(i)
+  end do
+  !$omp end unroll
+  !$omp end unroll
+end subroutine test7
+
+subroutine test8
+  implicit none
+  integer :: i
+
+  !$omp simd
+  !$omp unroll full ! { dg-error "Generated loop of UNROLL construct at \\\(1\\\) without PARTIAL clause does not have canonical form" }
+  do i = 1,100
+    call dummy(i)
+  end do
+end subroutine test8
+
+subroutine test9
+  implicit none
+  integer :: i
+
+  !$omp unroll full
+  !$omp unroll full ! { dg-error "Generated loop of UNROLL construct at \\\(1\\\) without PARTIAL clause does not have canonical form" }
+  do i = 1,100
+    call dummy(i)
+  end do
+end subroutine test9
+
+subroutine test10
+  implicit none
+  integer :: i,j
+
+  !$omp simd
+  !$omp unroll ! { dg-error "Generated loop of UNROLL construct at \\\(1\\\) without PARTIAL clause does not have canonical form" }
+  do i = 1,100
+    call dummy(i)
+    do j = 1,100
+      call dummy2(i,j)
+    end do
+  end do
+end subroutine test10
+
+subroutine test11
+  implicit none
+  integer :: i,j
+
+  !$omp simd
+  !$omp unroll ! { dg-error "Generated loop of UNROLL construct at \\\(1\\\) without PARTIAL clause does not have canonical form" }
+  do i = 1,100
+    !$omp unroll
+    !$omp unroll ! { dg-error "Generated loop of UNROLL construct at \\\(1\\\) without PARTIAL clause does not have canonical form" }
+    call dummy(i) ! { dg-error "Unexpected CALL statement at \\\(1\\\)" }
+    !$omp unroll ! { dg-error "Generated loop of UNROLL construct at \\\(1\\\) without PARTIAL clause does not have canonical form" }
+    do j = 1,100
+      call dummy2(i,j)
+    end do
+  end do
+end subroutine test11
+
+subroutine test12
+  implicit none
+  integer :: i,j
+
+  !$omp simd
+  !$omp unroll ! { dg-error "Generated loop of UNROLL construct at \\\(1\\\) without PARTIAL clause does not have canonical form" }
+  do i = 1,100
+    !$omp unroll
+    !$omp unroll ! { dg-error "Generated loop of UNROLL construct at \\\(1\\\) without PARTIAL clause does not have canonical form" }
+    !$omp unroll ! { dg-error "Generated loop of UNROLL construct at \\\(1\\\) without PARTIAL clause does not have canonical form" }
+    do j = 1,100
+      call dummy2(i,j)
+    end do
+    call dummy(i)
+  end do
+end subroutine test12
+
+subroutine test13
+  implicit none
+  integer :: i
+
+  !$omp unroll
+  !$omp unroll ! { dg-error "Generated loop of UNROLL construct at \\\(1\\\) without PARTIAL clause does not have canonical form" }
+  do i = 1,100
+    call dummy(i)
+  end do
+  !$omp end unroll
+  !$omp end unroll
+  !$omp end unroll ! { dg-error "Unexpected !\\\$OMP END UNROLL statement at \\\(1\\\)" }
+end subroutine test13
+
+subroutine test14
+  implicit none
+  integer :: i
+
+  !$omp simd
+  !$omp unroll ! { dg-error "Generated loop of UNROLL construct at \\\(1\\\) without PARTIAL clause does not have canonical form" }
+  !$omp unroll ! { dg-error "Generated loop of UNROLL construct at \\\(1\\\) without PARTIAL clause does not have canonical form" }
+  do i = 1,100
+    call dummy(i)
+  end do
+  !$omp end unroll
+  !$omp end unroll
+  !$omp end unroll ! { dg-error "Unexpected !\\\$OMP END UNROLL statement at \\\(1\\\)" }
+end subroutine test14
+
+subroutine test17
+  implicit none
+  integer :: i
+
+  !$omp simd
+  !$omp unroll partial(0) ! { dg-error "PARTIAL clause argument not constant positive integer at \\\(1\\\)" }
+  do i = 1,100
+    call dummy(i)
+  end do
+  !$omp end unroll
+end subroutine test17
+
+subroutine test18
+  implicit none
+  integer :: i
+
+  !$omp simd
+  !$omp unroll partial(-10) ! { dg-error "PARTIAL clause argument not constant positive integer at \\\(1\\\)" }
+  do i = 1,100
+    call dummy(i)
+  end do
+  !$omp end unroll
+end subroutine test18
--- gcc/testsuite/gfortran.dg/gomp/unroll-tile-1.f90.jj	2024-06-04 13:30:00.729872101 +0200
+++ gcc/testsuite/gfortran.dg/gomp/unroll-tile-1.f90	2024-06-04 13:30:00.729872101 +0200
@@ -0,0 +1,35 @@ 
+! { dg-additional-options "-O2 -fdump-tree-original -fdump-tree-gimple" }
+
+function mult (a, b) result (c)
+  integer, allocatable, dimension (:,:) :: a,b,c
+  integer :: i, j, k, inner
+
+  allocate(c( n, m ))
+
+  !$omp parallel do
+  !$omp unroll partial(1)
+  !$omp tile sizes (8,8)
+  do i = 1,m
+    do j = 1,n
+      inner = 0
+      do k = 1, n
+        inner = inner + a(k, i) * b(j, k)
+      end do
+      c(j, i) = inner
+    end do
+  end do
+end function mult
+
+! { dg-final { scan-tree-dump-times "#pragma omp for nowait" 1 "original" } }
+! { dg-final { scan-tree-dump-times "#pragma omp unroll partial\\\(1\\\)" 1 "original" } }
+! { dg-final { scan-tree-dump-times "#pragma omp tile sizes\\\(8, 8\\\)" 1 "original" } }
+! { dg-final { scan-tree-dump-not "#pragma omp unroll" "gimple" } }
+! { dg-final { scan-tree-dump-not "#pragma omp tile" "gimple" } }
+
+! Tiling adds two floor and two tile loops.
+! Unroll with partial(1) is effectively ignored and the innermost
+! loop isn't associated with anything.  So that means 5 loops,
+! with the outermost associated with !$omp parallel do, where
+! the innermost loop gimplifies condition into a boolean temporary.
+
+! { dg-final { scan-tree-dump-times "if \\\(\[A-Za-z0-9_.\]+ <" 3 "gimple" } }
--- gcc/testsuite/gfortran.dg/gomp/unroll-tile-2.f90.jj	2024-06-04 13:30:00.729872101 +0200
+++ gcc/testsuite/gfortran.dg/gomp/unroll-tile-2.f90	2024-06-04 13:30:00.729872101 +0200
@@ -0,0 +1,40 @@ 
+! { dg-additional-options "-O2 -fdump-tree-original -fdump-tree-gimple" }
+
+function mult (a, b) result (c)
+  integer, allocatable, dimension (:,:) :: a,b,c
+  integer :: i, j, k, inner
+
+  allocate(c( n, m ))
+  c = 0
+
+  !$omp target
+  !$omp parallel do
+  !$omp unroll partial(2)
+  !$omp tile sizes (8,8,4)
+  do i = 1,m
+    do j = 1,n
+      do k = 1, n
+        c(j,i) = c(j,i) + a(k, i) * b(j, k)
+      end do
+    end do
+  end do
+  !$omp end target
+end function mult
+
+! { dg-final { scan-tree-dump-times "#pragma omp for nowait" 1 "original" } }
+! { dg-final { scan-tree-dump-times "#pragma omp unroll partial\\\(2\\\)" 1 "original" } }
+! { dg-final { scan-tree-dump-times "#pragma omp tile sizes\\\(8, 8, 4\\\)" 1 "original" } }
+! { dg-final { scan-tree-dump-not "#pragma omp unroll" "gimple" } }
+! { dg-final { scan-tree-dump-not "#pragma omp tile" "gimple" } }
+
+! Check the number of loops
+
+! Tiling adds three tile and three floor loops.
+! The outermost tile loop is then partially unrolled, turning it
+! into one tile and one floor loop, so now 7 loops in total, one
+! of them being fully unrolled.  And finally the outermost loop is
+! associated with the !$omp parallel do and so not lowered during
+! gimplification.
+
+! { dg-final { scan-tree-dump-times "if \\\(\[A-Za-z0-9_.\]+ <" 5 "gimple" } }
+! { dg-final { scan-tree-dump-times "\.ANNOTATE \\\(\[^\n\r\]*, 1, 2\\\);" 1 "gimple" } }
--- gcc/testsuite/gfortran.dg/gomp/unroll-tile-inner-1.f90.jj	2024-06-04 13:30:00.728872114 +0200
+++ gcc/testsuite/gfortran.dg/gomp/unroll-tile-inner-1.f90	2024-06-04 13:30:00.728872114 +0200
@@ -0,0 +1,24 @@ 
+! { dg-additional-options "-fdump-tree-original -fdump-tree-gimple" }
+
+function mult (a, b) result (c)
+  integer, allocatable, dimension (:,:) :: a,b,c
+  integer :: i, j, k, inner
+
+  allocate(c( n, m ))
+
+  !$omp parallel do collapse(2)
+  !$omp tile sizes (8,8)
+  do i = 1,m
+    do j = 1,n
+      inner = 0
+      !$omp unroll partial(10)
+      do k = 1, n
+        inner = inner + a(k, i) * b(j, k)
+      end do
+      c(j, i) = inner
+    end do
+  end do
+end function mult
+
+! { dg-final { scan-tree-dump-times "#pragma omp unroll partial" 1 "original" } }
+! { dg-final { scan-tree-dump-not "#pragma omp unroll partial" "gimple" } }