aboutsummaryrefslogtreecommitdiff
path: root/gcc/cp/parser.cc
diff options
context:
space:
mode:
authorJakub Jelinek <jakub@redhat.com>2024-06-05 19:10:26 +0200
committerJakub Jelinek <jakub@redhat.com>2024-06-05 19:10:26 +0200
commit804c0f35a6b1d75bafc550b4b42155744d77f990 (patch)
treea0f68633aa578a16cc13798077cad9d32148a70f /gcc/cp/parser.cc
parentd7cbcfe7c33645eaf95f175f19884d443817857b (diff)
downloadgcc-804c0f35a6b1d75bafc550b4b42155744d77f990.zip
gcc-804c0f35a6b1d75bafc550b4b42155744d77f990.tar.gz
gcc-804c0f35a6b1d75bafc550b4b42155744d77f990.tar.bz2
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. 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.
Diffstat (limited to 'gcc/cp/parser.cc')
-rw-r--r--gcc/cp/parser.cc483
1 files changed, 428 insertions, 55 deletions
diff --git a/gcc/cp/parser.cc b/gcc/cp/parser.cc
index 3b2ad25..bc4a235 100644
--- a/gcc/cp/parser.cc
+++ b/gcc/cp/parser.cc
@@ -3039,6 +3039,8 @@ static bool cp_parser_skip_up_to_closing_square_bracket
static bool cp_parser_skip_to_closing_square_bracket
(cp_parser *);
static size_t cp_parser_skip_balanced_tokens (cp_parser *, size_t);
+static bool cp_parser_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 *parser)
"%<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* parser, tree in_statement_expr)
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 *parser)
result = PRAGMA_OMP_CLAUSE_FIRSTPRIVATE;
else if (!strcmp ("from", p))
result = PRAGMA_OMP_CLAUSE_FROM;
+ else if (!strcmp ("full", p))
+ result = PRAGMA_OMP_CLAUSE_FULL;
break;
case 'g':
if (!strcmp ("gang", p))
@@ -38278,6 +38289,8 @@ cp_parser_omp_clause_name (cp_parser *parser)
case 'p':
if (!strcmp ("parallel", p))
result = PRAGMA_OMP_CLAUSE_PARALLEL;
+ if (!strcmp ("partial", p))
+ result = PRAGMA_OMP_CLAUSE_PARTIAL;
else if (!strcmp ("present", p))
result = PRAGMA_OACC_CLAUSE_PRESENT;
else if (!strcmp ("present_or_copy", p)
@@ -40658,6 +40671,48 @@ cp_parser_omp_clause_thread_limit (cp_parser *parser, tree list,
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 *parser, omp_clause_mask mask,
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 *parser, omp_clause_mask mask,
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 *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 *parser, bool *if_p)
gcc_assert (omp_for_parse_state);
int depth = omp_for_parse_state->depth;
- /* We have already matched the FOR token but not consumed it yet. */
- gcc_assert (cp_lexer_next_token_is_keyword (parser->lexer, RID_FOR));
+ /* Handle loop transformations first. Note that when we get here
+ omp_for_parse_state->depth has already been incremented to indicate
+ the depth of the *next* loop, not the level of the loop body the
+ transformation directive appears in. */
+
+ /* Arrange for C++ standard attribute syntax to be parsed as regular
+ pragmas. Give an error if there are other random attributes present. */
+ cp_token *token = cp_lexer_peek_token (parser->lexer);
+ tree std_attrs = cp_parser_std_attribute_spec_seq (parser);
+ std_attrs = cp_parser_handle_statement_omp_attributes (parser, std_attrs);
+ if (std_attrs)
+ error_at (token->location,
+ "attributes other than OpenMP directives "
+ "are not allowed on %<for%> in loop nest");
+
+ if (cp_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 *parser, bool *if_p)
moreloops = depth < omp_for_parse_state->count - 1;
omp_for_parse_state->want_nested_loop = moreloops;
- if (moreloops && cp_lexer_next_token_is_keyword (parser->lexer, RID_FOR))
+ if (moreloops
+ && cp_parser_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 *parser, enum tree_code code, tree clauses,
tree cl, ordered_cl = NULL_TREE;
int collapse = 1, ordered = 0;
unsigned int count;
- bool tiling = false;
+ bool oacc_tiling = false;
bool inscan = false;
struct omp_for_parse_data data;
struct omp_for_parse_data *save_data = parser->omp_for_parse_state;
@@ -45584,9 +45831,11 @@ cp_parser_omp_for_loop (cp_parser *parser, enum tree_code code, tree clauses,
collapse = tree_to_shwi (OMP_CLAUSE_COLLAPSE_EXPR (cl));
else if (OMP_CLAUSE_CODE (cl) == OMP_CLAUSE_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 *parser, enum tree_code code, tree clauses,
ordered = collapse;
}
- gcc_assert (tiling || (collapse >= 1 && ordered >= 0));
+ gcc_assert (oacc_tiling || (collapse >= 1 && ordered >= 0));
count = ordered ? ordered : collapse;
- if (!cp_lexer_next_token_is_keyword (parser->lexer, RID_FOR))
- {
- cp_parser_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 *parser, enum tree_code code, tree clauses,
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 *parser, enum tree_code code, tree clauses,
/* 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);
+
+ /* 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]);
- }
+ /* 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, cp_token *pragma_tok,
return true;
}
+
+/* OpenMP 5.1: Parse sizes list for "omp tile sizes"
+ sizes ( size-expr-list ) */
+static tree
+cp_parser_omp_tile_sizes (cp_parser *parser, location_t loc)
+{
+ tree sizes = NULL_TREE;
+
+ 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 *parser, cp_token *pragma_tok, bool *if_p)
case PRAGMA_OMP_ASSUME:
cp_parser_omp_assume (parser, pragma_tok, if_p);
return;
+ case PRAGMA_OMP_TILE:
+ stmt = cp_parser_omp_tile (parser, pragma_tok, if_p);
+ break;
+ case PRAGMA_OMP_UNROLL:
+ stmt = cp_parser_omp_unroll (parser, pragma_tok, if_p);
+ break;
default:
gcc_unreachable ();
}
@@ -51278,6 +51649,8 @@ cp_parser_pragma (cp_parser *parser, enum pragma_context context, bool *if_p)
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);