From 170a8bd604a4c960b8a378d50f7cc6c66cbdaf5c Mon Sep 17 00:00:00 2001 From: Eric Botcazou Date: Fri, 22 Dec 2017 10:22:15 +0000 Subject: [PATCH] extend.texi (Loop-Specific Pragmas): Document pragma GCC unroll. * doc/extend.texi (Loop-Specific Pragmas): Document pragma GCC unroll. c-family/ * c-pragma.c (init_pragma): Register pragma GCC unroll. * c-pragma.h (enum pragma_kind): Add PRAGMA_UNROLL. c/ * c-parser.c (c_parser_while_statement): Add unroll parameter and build ANNOTATE_EXPR if present. Add 3rd operand to ANNOTATE_EXPR. (c_parser_do_statement): Likewise. (c_parser_for_statement): Likewise. (c_parser_statement_after_labels): Adjust calls to above. (c_parse_pragma_ivdep): New static function. (c_parser_pragma_unroll): Likewise. (c_parser_pragma) : Add support for pragma Unroll. : New case. cp/ * constexpr.c (cxx_eval_constant_expression) : Remove assertion on 2nd operand. (potential_constant_expression_1): Likewise. * cp-tree.def (RANGE_FOR_STMT): Take a 5th operand. * cp-tree.h (RANGE_FOR_UNROLL): New macro. (cp_convert_range_for): Adjust prototype. (finish_while_stmt_cond): Likewise. (finish_do_stmt): Likewise. (finish_for_cond): Likewise. * init.c (build_vec_init): Adjut call to finish_for_cond. * parser.c (cp_parser_statement): Adjust call to cp_parser_iteration_statement. (cp_parser_for): Add unroll parameter and pass it in calls to cp_parser_range_for and cp_parser_c_for. (cp_parser_c_for): Add unroll parameter and pass it in call to finish_for_cond. (cp_parser_range_for): Add unroll parameter, set in on RANGE_FOR_STMT and pass it in call to cp_convert_range_for. (cp_convert_range_for): Add unroll parameter and pass it in call to finish_for_cond. (cp_parser_iteration_statement): Add unroll parameter and pass it in calls to finish_while_stmt_cond, finish_do_stmt and cp_parser_for. (cp_parser_pragma_ivdep): New static function. (cp_parser_pragma_unroll): Likewise. (cp_parser_pragma) : Add support for pragma Unroll. : New case. * pt.c (tsubst_expr) : Adjust call to finish_for_cond. : Pass unrolling factor to cp_convert_range_for. : Adjust call to finish_while_stmt_cond. : Adjust call to finish_do_stmt. * semantics.c (finish_while_stmt_cond): Add unroll parameter and build ANNOTATE_EXPR if present. (finish_do_stmt): Likewise. (finish_for_cond): Likewise. (begin_range_for_stmt): Build RANGE_FOR_STMT with 5th operand. fortran/ * array.c (gfc_copy_iterator): Copy unroll field. * decl.c (directive_unroll): New global variable. (gfc_match_gcc_unroll): New function. * gfortran.h (gfc_iterator]): Add unroll field. (directive_unroll): Declare: * match.c (gfc_match_do): Use memset to initialize the iterator. * match.h (gfc_match_gcc_unroll): New prototype. * parse.c (decode_gcc_attribute): Match "unroll". (parse_do_block): Set iterator's unroll. (parse_executable): Diagnose misplaced unroll directive. * trans-stmt.c (gfc_trans_simple_do) Annotate loop condition with annot_expr_unroll_kind. (gfc_trans_do): Likewise. * gfortran.texi (GNU Fortran Compiler Directives): Split section into subections 'ATTRIBUTES directive' and 'UNROLL directive'. From-SVN: r255973 --- gcc/ChangeLog | 5 + gcc/c-family/ChangeLog | 6 + gcc/c-family/c-pragma.c | 4 + gcc/c-family/c-pragma.h | 1 + gcc/c/ChangeLog | 13 ++ gcc/c/c-parser.c | 144 +++++++++++++++--- gcc/cp/ChangeLog | 39 +++++ gcc/cp/constexpr.c | 2 - gcc/cp/cp-tree.def | 4 +- gcc/cp/cp-tree.h | 10 +- gcc/cp/init.c | 2 +- gcc/cp/parser.c | 139 ++++++++++++++--- gcc/cp/pt.c | 12 +- gcc/cp/semantics.c | 29 +++- gcc/doc/extend.texi | 18 ++- gcc/fortran/ChangeLog | 19 +++ gcc/fortran/array.c | 1 + gcc/fortran/decl.c | 38 ++++- gcc/fortran/gfortran.h | 2 + gcc/fortran/gfortran.texi | 21 +++ gcc/fortran/match.c | 2 +- gcc/fortran/match.h | 1 + gcc/fortran/parse.c | 13 +- gcc/fortran/trans-stmt.c | 10 ++ gcc/testsuite/ChangeLog | 17 +++ gcc/testsuite/c-c++-common/unroll-1.c | 41 +++++ gcc/testsuite/c-c++-common/unroll-2.c | 41 +++++ gcc/testsuite/c-c++-common/unroll-3.c | 41 +++++ gcc/testsuite/c-c++-common/unroll-4.c | 22 +++ gcc/testsuite/c-c++-common/unroll-5.c | 29 ++++ gcc/testsuite/g++.dg/ext/unroll-1.C | 19 +++ gcc/testsuite/g++.dg/ext/unroll-2.C | 13 ++ gcc/testsuite/g++.dg/ext/unroll-3.C | 20 +++ .../gfortran.dg/directive_unroll_1.f90 | 52 +++++++ .../gfortran.dg/directive_unroll_2.f90 | 52 +++++++ .../gfortran.dg/directive_unroll_3.f90 | 52 +++++++ .../gfortran.dg/directive_unroll_4.f90 | 29 ++++ .../gfortran.dg/directive_unroll_5.f90 | 38 +++++ 38 files changed, 926 insertions(+), 75 deletions(-) create mode 100644 gcc/testsuite/c-c++-common/unroll-1.c create mode 100644 gcc/testsuite/c-c++-common/unroll-2.c create mode 100644 gcc/testsuite/c-c++-common/unroll-3.c create mode 100644 gcc/testsuite/c-c++-common/unroll-4.c create mode 100644 gcc/testsuite/c-c++-common/unroll-5.c create mode 100644 gcc/testsuite/g++.dg/ext/unroll-1.C create mode 100644 gcc/testsuite/g++.dg/ext/unroll-2.C create mode 100644 gcc/testsuite/g++.dg/ext/unroll-3.C create mode 100644 gcc/testsuite/gfortran.dg/directive_unroll_1.f90 create mode 100644 gcc/testsuite/gfortran.dg/directive_unroll_2.f90 create mode 100644 gcc/testsuite/gfortran.dg/directive_unroll_3.f90 create mode 100644 gcc/testsuite/gfortran.dg/directive_unroll_4.f90 create mode 100644 gcc/testsuite/gfortran.dg/directive_unroll_5.f90 diff --git a/gcc/ChangeLog b/gcc/ChangeLog index 585a70b9f8d..3d36602d777 100644 --- a/gcc/ChangeLog +++ b/gcc/ChangeLog @@ -1,3 +1,8 @@ +2017-12-22 Mike Stump + Eric Botcazou + + * doc/extend.texi (Loop-Specific Pragmas): Document pragma GCC unroll. + 2017-12-21 Jakub Jelinek PR middle-end/83487 diff --git a/gcc/c-family/ChangeLog b/gcc/c-family/ChangeLog index ba61fcef8a9..f4efa6818e8 100644 --- a/gcc/c-family/ChangeLog +++ b/gcc/c-family/ChangeLog @@ -1,3 +1,9 @@ +2017-12-22 Mike Stump + Eric Botcazou + + * c-pragma.c (init_pragma): Register pragma GCC unroll. + * c-pragma.h (enum pragma_kind): Add PRAGMA_UNROLL. + 2017-12-22 Alexandre Oliva PR debug/83527 diff --git a/gcc/c-family/c-pragma.c b/gcc/c-family/c-pragma.c index daa9e5ade38..a4e9b745adb 100644 --- a/gcc/c-family/c-pragma.c +++ b/gcc/c-family/c-pragma.c @@ -1526,6 +1526,10 @@ init_pragma (void) cpp_register_deferred_pragma (parse_in, "GCC", "ivdep", PRAGMA_IVDEP, false, false); + if (!flag_preprocess_only) + cpp_register_deferred_pragma (parse_in, "GCC", "unroll", PRAGMA_UNROLL, + false, false); + #ifdef HANDLE_PRAGMA_PACK_WITH_EXPANSION c_register_pragma_with_expansion (0, "pack", handle_pragma_pack); #else diff --git a/gcc/c-family/c-pragma.h b/gcc/c-family/c-pragma.h index e035b3a8eca..f55fe4fefda 100644 --- a/gcc/c-family/c-pragma.h +++ b/gcc/c-family/c-pragma.h @@ -69,6 +69,7 @@ enum pragma_kind { PRAGMA_GCC_PCH_PREPROCESS, PRAGMA_IVDEP, + PRAGMA_UNROLL, PRAGMA_FIRST_EXTERNAL }; diff --git a/gcc/c/ChangeLog b/gcc/c/ChangeLog index bfa62268166..f4756c800a1 100644 --- a/gcc/c/ChangeLog +++ b/gcc/c/ChangeLog @@ -1,3 +1,16 @@ +2017-12-22 Mike Stump + Eric Botcazou + + * c-parser.c (c_parser_while_statement): Add unroll parameter and + build ANNOTATE_EXPR if present. Add 3rd operand to ANNOTATE_EXPR. + (c_parser_do_statement): Likewise. + (c_parser_for_statement): Likewise. + (c_parser_statement_after_labels): Adjust calls to above. + (c_parse_pragma_ivdep): New static function. + (c_parser_pragma_unroll): Likewise. + (c_parser_pragma) : Add support for pragma Unroll. + : New case. + 2017-12-19 Jakub Jelinek * c-typeck.c (comptypes_internal, function_types_compatible_p, diff --git a/gcc/c/c-parser.c b/gcc/c/c-parser.c index f1bae8abdf3..05d1e0fe46e 100644 --- a/gcc/c/c-parser.c +++ b/gcc/c/c-parser.c @@ -1406,9 +1406,9 @@ static tree c_parser_c99_block_statement (c_parser *, bool *, location_t * = NULL); static void c_parser_if_statement (c_parser *, bool *, vec *); static void c_parser_switch_statement (c_parser *, bool *); -static void c_parser_while_statement (c_parser *, bool, bool *); -static void c_parser_do_statement (c_parser *, bool); -static void c_parser_for_statement (c_parser *, bool, bool *); +static void c_parser_while_statement (c_parser *, bool, unsigned short, bool *); +static void c_parser_do_statement (c_parser *, bool, unsigned short); +static void c_parser_for_statement (c_parser *, bool, unsigned short, bool *); static tree c_parser_asm_statement (c_parser *); static tree c_parser_asm_operands (c_parser *); static tree c_parser_asm_goto_operands (c_parser *); @@ -5400,13 +5400,13 @@ c_parser_statement_after_labels (c_parser *parser, bool *if_p, c_parser_switch_statement (parser, if_p); break; case RID_WHILE: - c_parser_while_statement (parser, false, if_p); + c_parser_while_statement (parser, false, 0, if_p); break; case RID_DO: - c_parser_do_statement (parser, false); + c_parser_do_statement (parser, 0, false); break; case RID_FOR: - c_parser_for_statement (parser, false, if_p); + c_parser_for_statement (parser, false, 0, if_p); break; case RID_GOTO: c_parser_consume_token (parser); @@ -5896,7 +5896,8 @@ c_parser_switch_statement (c_parser *parser, bool *if_p) implement -Wparentheses. */ static void -c_parser_while_statement (c_parser *parser, bool ivdep, bool *if_p) +c_parser_while_statement (c_parser *parser, bool ivdep, unsigned short unroll, + bool *if_p) { tree block, cond, body, save_break, save_cont; location_t loc; @@ -5912,6 +5913,11 @@ c_parser_while_statement (c_parser *parser, bool ivdep, bool *if_p) build_int_cst (integer_type_node, annot_expr_ivdep_kind), integer_zero_node); + if (unroll && cond != error_mark_node) + cond = build3 (ANNOTATE_EXPR, TREE_TYPE (cond), cond, + build_int_cst (integer_type_node, + annot_expr_unroll_kind), + build_int_cst (integer_type_node, unroll)); save_break = c_break_label; c_break_label = NULL_TREE; save_cont = c_cont_label; @@ -5946,7 +5952,7 @@ c_parser_while_statement (c_parser *parser, bool ivdep, bool *if_p) */ static void -c_parser_do_statement (c_parser *parser, bool ivdep) +c_parser_do_statement (c_parser *parser, bool ivdep, unsigned short unroll) { tree block, cond, body, save_break, save_cont, new_break, new_cont; location_t loc; @@ -5974,6 +5980,11 @@ c_parser_do_statement (c_parser *parser, bool ivdep) build_int_cst (integer_type_node, annot_expr_ivdep_kind), integer_zero_node); + if (unroll && cond != error_mark_node) + cond = build3 (ANNOTATE_EXPR, TREE_TYPE (cond), cond, + build_int_cst (integer_type_node, + annot_expr_unroll_kind), + build_int_cst (integer_type_node, unroll)); if (!c_parser_require (parser, CPP_SEMICOLON, "expected %<;%>")) c_parser_skip_to_end_of_block_or_statement (parser); c_finish_loop (loc, cond, NULL, body, new_break, new_cont, false); @@ -6040,7 +6051,8 @@ c_parser_do_statement (c_parser *parser, bool ivdep) implement -Wparentheses. */ static void -c_parser_for_statement (c_parser *parser, bool ivdep, bool *if_p) +c_parser_for_statement (c_parser *parser, bool ivdep, unsigned short unroll, + bool *if_p) { tree block, cond, incr, save_break, save_cont, body; /* The following are only used when parsing an ObjC foreach statement. */ @@ -6159,6 +6171,12 @@ c_parser_for_statement (c_parser *parser, bool ivdep, bool *if_p) "% pragma"); cond = error_mark_node; } + else if (unroll) + { + c_parser_error (parser, "missing loop condition in loop with " + "% pragma"); + cond = error_mark_node; + } else { c_parser_consume_token (parser); @@ -6176,6 +6194,11 @@ c_parser_for_statement (c_parser *parser, bool ivdep, bool *if_p) build_int_cst (integer_type_node, annot_expr_ivdep_kind), integer_zero_node); + if (unroll && cond != error_mark_node) + cond = build3 (ANNOTATE_EXPR, TREE_TYPE (cond), cond, + build_int_cst (integer_type_node, + annot_expr_unroll_kind), + build_int_cst (integer_type_node, unroll)); } /* Parse the increment expression (the third expression in a for-statement). In the case of a foreach-statement, this is @@ -10833,6 +10856,49 @@ c_parser_objc_at_dynamic_declaration (c_parser *parser) } +/* Parse a pragma GCC ivdep. */ + +static bool +c_parse_pragma_ivdep (c_parser *parser) +{ + c_parser_consume_pragma (parser); + c_parser_skip_to_pragma_eol (parser); + return true; +} + +/* Parse a pragma GCC unroll. */ + +static unsigned short +c_parser_pragma_unroll (c_parser *parser) +{ + unsigned short unroll; + c_parser_consume_pragma (parser); + location_t location = c_parser_peek_token (parser)->location; + tree expr = c_parser_expr_no_commas (parser, NULL).value; + mark_exp_read (expr); + expr = c_fully_fold (expr, false, NULL); + HOST_WIDE_INT lunroll = 0; + if (!INTEGRAL_TYPE_P (TREE_TYPE (expr)) + || TREE_CODE (expr) != INTEGER_CST + || (lunroll = tree_to_shwi (expr)) < 0 + || lunroll >= USHRT_MAX) + { + error_at (location, "%<#pragma GCC unroll%> requires an" + " assignment-expression that evaluates to a non-negative" + " integral constant less than %u", USHRT_MAX); + unroll = 0; + } + else + { + unroll = (unsigned short)lunroll; + if (unroll == 0) + unroll = 1; + } + + c_parser_skip_to_pragma_eol (parser); + return unroll; +} + /* Handle pragmas. Some OpenMP pragmas are associated with, and therefore should be considered, statements. ALLOW_STMT is true if we're within the context of a function and such pragmas are to be allowed. Returns @@ -10975,21 +11041,51 @@ c_parser_pragma (c_parser *parser, enum pragma_context context, bool *if_p) return c_parser_omp_ordered (parser, context, if_p); case PRAGMA_IVDEP: - c_parser_consume_pragma (parser); - c_parser_skip_to_pragma_eol (parser); - if (!c_parser_next_token_is_keyword (parser, RID_FOR) - && !c_parser_next_token_is_keyword (parser, RID_WHILE) - && !c_parser_next_token_is_keyword (parser, RID_DO)) - { - c_parser_error (parser, "for, while or do statement expected"); - return false; - } - if (c_parser_next_token_is_keyword (parser, RID_FOR)) - c_parser_for_statement (parser, true, if_p); - else if (c_parser_next_token_is_keyword (parser, RID_WHILE)) - c_parser_while_statement (parser, true, if_p); - else - c_parser_do_statement (parser, true); + { + const bool ivdep = c_parse_pragma_ivdep (parser); + unsigned short unroll; + if (c_parser_peek_token (parser)->pragma_kind == PRAGMA_UNROLL) + unroll = c_parser_pragma_unroll (parser); + else + unroll = 0; + if (!c_parser_next_token_is_keyword (parser, RID_FOR) + && !c_parser_next_token_is_keyword (parser, RID_WHILE) + && !c_parser_next_token_is_keyword (parser, RID_DO)) + { + c_parser_error (parser, "for, while or do statement expected"); + return false; + } + if (c_parser_next_token_is_keyword (parser, RID_FOR)) + c_parser_for_statement (parser, ivdep, unroll, if_p); + else if (c_parser_next_token_is_keyword (parser, RID_WHILE)) + c_parser_while_statement (parser, ivdep, unroll, if_p); + else + c_parser_do_statement (parser, ivdep, unroll); + } + return false; + + case PRAGMA_UNROLL: + { + unsigned short unroll = c_parser_pragma_unroll (parser); + bool ivdep; + if (c_parser_peek_token (parser)->pragma_kind == PRAGMA_IVDEP) + ivdep = c_parse_pragma_ivdep (parser); + else + ivdep = false; + if (!c_parser_next_token_is_keyword (parser, RID_FOR) + && !c_parser_next_token_is_keyword (parser, RID_WHILE) + && !c_parser_next_token_is_keyword (parser, RID_DO)) + { + c_parser_error (parser, "for, while or do statement expected"); + return false; + } + if (c_parser_next_token_is_keyword (parser, RID_FOR)) + c_parser_for_statement (parser, ivdep, unroll, if_p); + else if (c_parser_next_token_is_keyword (parser, RID_WHILE)) + c_parser_while_statement (parser, ivdep, unroll, if_p); + else + c_parser_do_statement (parser, ivdep, unroll); + } return false; case PRAGMA_GCC_PCH_PREPROCESS: diff --git a/gcc/cp/ChangeLog b/gcc/cp/ChangeLog index e8295507c56..fbffe0c3390 100644 --- a/gcc/cp/ChangeLog +++ b/gcc/cp/ChangeLog @@ -1,3 +1,42 @@ +2017-12-22 Mike Stump + Eric Botcazou + + * constexpr.c (cxx_eval_constant_expression) : Remove + assertion on 2nd operand. + (potential_constant_expression_1): Likewise. + * cp-tree.def (RANGE_FOR_STMT): Take a 5th operand. + * cp-tree.h (RANGE_FOR_UNROLL): New macro. + (cp_convert_range_for): Adjust prototype. + (finish_while_stmt_cond): Likewise. + (finish_do_stmt): Likewise. + (finish_for_cond): Likewise. + * init.c (build_vec_init): Adjut call to finish_for_cond. + * parser.c (cp_parser_statement): Adjust call to + cp_parser_iteration_statement. + (cp_parser_for): Add unroll parameter and pass it in calls to + cp_parser_range_for and cp_parser_c_for. + (cp_parser_c_for): Add unroll parameter and pass it in call to + finish_for_cond. + (cp_parser_range_for): Add unroll parameter, set in on RANGE_FOR_STMT + and pass it in call to cp_convert_range_for. + (cp_convert_range_for): Add unroll parameter and pass it in call to + finish_for_cond. + (cp_parser_iteration_statement): Add unroll parameter and pass it in + calls to finish_while_stmt_cond, finish_do_stmt and cp_parser_for. + (cp_parser_pragma_ivdep): New static function. + (cp_parser_pragma_unroll): Likewise. + (cp_parser_pragma) : Add support for pragma Unroll. + : New case. + * pt.c (tsubst_expr) : Adjust call to finish_for_cond. + : Pass unrolling factor to cp_convert_range_for. + : Adjust call to finish_while_stmt_cond. + : Adjust call to finish_do_stmt. + * semantics.c (finish_while_stmt_cond): Add unroll parameter and + build ANNOTATE_EXPR if present. + (finish_do_stmt): Likewise. + (finish_for_cond): Likewise. + (begin_range_for_stmt): Build RANGE_FOR_STMT with 5th operand. + 2017-12-21 Nathan Sidwell PR c++/83406 diff --git a/gcc/cp/constexpr.c b/gcc/cp/constexpr.c index 6845ca407c1..25b19fa0229 100644 --- a/gcc/cp/constexpr.c +++ b/gcc/cp/constexpr.c @@ -4689,7 +4689,6 @@ cxx_eval_constant_expression (const constexpr_ctx *ctx, tree t, return t; case ANNOTATE_EXPR: - gcc_assert (tree_to_uhwi (TREE_OPERAND (t, 1)) == annot_expr_ivdep_kind); r = cxx_eval_constant_expression (ctx, TREE_OPERAND (t, 0), lval, non_constant_p, overflow_p, @@ -5940,7 +5939,6 @@ potential_constant_expression_1 (tree t, bool want_rval, bool strict, bool now, } case ANNOTATE_EXPR: - gcc_assert (tree_to_uhwi (TREE_OPERAND (t, 1)) == annot_expr_ivdep_kind); return RECUR (TREE_OPERAND (t, 0), rval); default: diff --git a/gcc/cp/cp-tree.def b/gcc/cp/cp-tree.def index 890723f20ef..d04624a5bd2 100644 --- a/gcc/cp/cp-tree.def +++ b/gcc/cp/cp-tree.def @@ -302,8 +302,8 @@ DEFTREECODE (FOR_STMT, "for_stmt", tcc_statement, 5) /* Used to represent a range-based `for' statement. The operands are RANGE_FOR_DECL, RANGE_FOR_EXPR, RANGE_FOR_BODY, and RANGE_FOR_SCOPE, - respectively. Only used in templates. */ -DEFTREECODE (RANGE_FOR_STMT, "range_for_stmt", tcc_statement, 4) + RANGE_FOR_UNROLL respectively. Only used in templates. */ +DEFTREECODE (RANGE_FOR_STMT, "range_for_stmt", tcc_statement, 5) /* Used to represent a 'while' statement. The operands are WHILE_COND and WHILE_BODY, respectively. */ diff --git a/gcc/cp/cp-tree.h b/gcc/cp/cp-tree.h index 1a7ef9e7d0d..d408370f898 100644 --- a/gcc/cp/cp-tree.h +++ b/gcc/cp/cp-tree.h @@ -4844,6 +4844,7 @@ more_aggr_init_expr_args_p (const aggr_init_expr_arg_iterator *iter) #define RANGE_FOR_EXPR(NODE) TREE_OPERAND (RANGE_FOR_STMT_CHECK (NODE), 1) #define RANGE_FOR_BODY(NODE) TREE_OPERAND (RANGE_FOR_STMT_CHECK (NODE), 2) #define RANGE_FOR_SCOPE(NODE) TREE_OPERAND (RANGE_FOR_STMT_CHECK (NODE), 3) +#define RANGE_FOR_UNROLL(NODE) TREE_OPERAND (RANGE_FOR_STMT_CHECK (NODE), 4) #define RANGE_FOR_IVDEP(NODE) TREE_LANG_FLAG_6 (RANGE_FOR_STMT_CHECK (NODE)) #define SWITCH_STMT_COND(NODE) TREE_OPERAND (SWITCH_STMT_CHECK (NODE), 0) @@ -6433,7 +6434,8 @@ extern tree implicitly_declare_fn (special_function_kind, tree, extern bool maybe_clone_body (tree); /* In parser.c */ -extern tree cp_convert_range_for (tree, tree, tree, tree, unsigned int, bool); +extern tree cp_convert_range_for (tree, tree, tree, tree, unsigned int, bool, + unsigned short); extern bool parsing_nsdmi (void); extern bool parsing_default_capturing_generic_lambda_in_template (void); extern void inject_this_parameter (tree, cp_cv_quals); @@ -6718,16 +6720,16 @@ extern void begin_else_clause (tree); extern void finish_else_clause (tree); extern void finish_if_stmt (tree); extern tree begin_while_stmt (void); -extern void finish_while_stmt_cond (tree, tree, bool); +extern void finish_while_stmt_cond (tree, tree, bool, unsigned short); extern void finish_while_stmt (tree); extern tree begin_do_stmt (void); extern void finish_do_body (tree); -extern void finish_do_stmt (tree, tree, bool); +extern void finish_do_stmt (tree, tree, bool, unsigned short); extern tree finish_return_stmt (tree); extern tree begin_for_scope (tree *); extern tree begin_for_stmt (tree, tree); extern void finish_init_stmt (tree); -extern void finish_for_cond (tree, tree, bool); +extern void finish_for_cond (tree, tree, bool, unsigned short); extern void finish_for_expr (tree, tree); extern void finish_for_stmt (tree); extern tree begin_range_for_stmt (tree, tree); diff --git a/gcc/cp/init.c b/gcc/cp/init.c index daa62392857..6e11cf3423c 100644 --- a/gcc/cp/init.c +++ b/gcc/cp/init.c @@ -4323,7 +4323,7 @@ build_vec_init (tree base, tree maxindex, tree init, finish_init_stmt (for_stmt); finish_for_cond (build2 (GT_EXPR, boolean_type_node, iterator, build_int_cst (TREE_TYPE (iterator), -1)), - for_stmt, false); + for_stmt, false, 0); elt_init = cp_build_unary_op (PREDECREMENT_EXPR, iterator, false, complain); if (elt_init == error_mark_node) diff --git a/gcc/cp/parser.c b/gcc/cp/parser.c index 57467bda596..b04ed9adce0 100644 --- a/gcc/cp/parser.c +++ b/gcc/cp/parser.c @@ -2112,15 +2112,15 @@ static tree cp_parser_selection_statement static tree cp_parser_condition (cp_parser *); static tree cp_parser_iteration_statement - (cp_parser *, bool *, bool); + (cp_parser *, bool *, bool, unsigned short); static bool cp_parser_init_statement (cp_parser *, tree *decl); static tree cp_parser_for - (cp_parser *, bool); + (cp_parser *, bool, unsigned short); static tree cp_parser_c_for - (cp_parser *, tree, tree, bool); + (cp_parser *, tree, tree, bool, unsigned short); static tree cp_parser_range_for - (cp_parser *, tree, tree, tree, bool); + (cp_parser *, tree, tree, tree, bool, unsigned short); static void do_range_for_auto_deduction (tree, tree); static tree cp_parser_perform_range_for_lookup @@ -10742,7 +10742,7 @@ cp_parser_statement (cp_parser* parser, tree in_statement_expr, case RID_WHILE: case RID_DO: case RID_FOR: - statement = cp_parser_iteration_statement (parser, if_p, false); + statement = cp_parser_iteration_statement (parser, if_p, false, 0); break; case RID_BREAK: @@ -11579,7 +11579,7 @@ cp_parser_condition (cp_parser* parser) not included. */ static tree -cp_parser_for (cp_parser *parser, bool ivdep) +cp_parser_for (cp_parser *parser, bool ivdep, unsigned short unroll) { tree init, scope, decl; bool is_range_for; @@ -11591,13 +11591,14 @@ cp_parser_for (cp_parser *parser, bool ivdep) is_range_for = cp_parser_init_statement (parser, &decl); if (is_range_for) - return cp_parser_range_for (parser, scope, init, decl, ivdep); + return cp_parser_range_for (parser, scope, init, decl, ivdep, unroll); else - return cp_parser_c_for (parser, scope, init, ivdep); + return cp_parser_c_for (parser, scope, init, ivdep, unroll); } static tree -cp_parser_c_for (cp_parser *parser, tree scope, tree init, bool ivdep) +cp_parser_c_for (cp_parser *parser, tree scope, tree init, bool ivdep, + unsigned short unroll) { /* Normal for loop */ tree condition = NULL_TREE; @@ -11618,7 +11619,13 @@ cp_parser_c_for (cp_parser *parser, tree scope, tree init, bool ivdep) "% pragma"); condition = error_mark_node; } - finish_for_cond (condition, stmt, ivdep); + else if (unroll) + { + cp_parser_error (parser, "missing loop condition in loop with " + "% pragma"); + condition = error_mark_node; + } + finish_for_cond (condition, stmt, ivdep, unroll); /* Look for the `;'. */ cp_parser_require (parser, CPP_SEMICOLON, RT_SEMICOLON); @@ -11642,7 +11649,7 @@ cp_parser_c_for (cp_parser *parser, tree scope, tree init, bool ivdep) static tree cp_parser_range_for (cp_parser *parser, tree scope, tree init, tree range_decl, - bool ivdep) + bool ivdep, unsigned short unroll) { tree stmt, range_expr; auto_vec bindings; @@ -11711,6 +11718,8 @@ cp_parser_range_for (cp_parser *parser, tree scope, tree init, tree range_decl, stmt = begin_range_for_stmt (scope, init); if (ivdep) RANGE_FOR_IVDEP (stmt) = 1; + if (unroll) + RANGE_FOR_UNROLL (stmt) = build_int_cst (integer_type_node, unroll); finish_range_for_decl (stmt, range_decl, range_expr); if (!type_dependent_expression_p (range_expr) /* do_auto_deduction doesn't mess with template init-lists. */ @@ -11721,7 +11730,8 @@ cp_parser_range_for (cp_parser *parser, tree scope, tree init, tree range_decl, { stmt = begin_for_stmt (scope, init); stmt = cp_convert_range_for (stmt, range_decl, range_expr, - decomp_first_name, decomp_cnt, ivdep); + decomp_first_name, decomp_cnt, ivdep, + unroll); } return stmt; } @@ -11815,7 +11825,7 @@ do_range_for_auto_deduction (tree decl, tree range_expr) tree cp_convert_range_for (tree statement, tree range_decl, tree range_expr, tree decomp_first_name, unsigned int decomp_cnt, - bool ivdep) + bool ivdep, unsigned short unroll) { tree begin, end; tree iter_type, begin_expr, end_expr; @@ -11876,7 +11886,7 @@ cp_convert_range_for (tree statement, tree range_decl, tree range_expr, begin, ERROR_MARK, end, ERROR_MARK, NULL, tf_warning_or_error); - finish_for_cond (condition, statement, ivdep); + finish_for_cond (condition, statement, ivdep, unroll); /* The new increment expression. */ expression = finish_unary_op_expr (input_location, @@ -12054,7 +12064,8 @@ cp_parser_range_for_member_function (tree range, tree identifier) Returns the new WHILE_STMT, DO_STMT, FOR_STMT or RANGE_FOR_STMT. */ static tree -cp_parser_iteration_statement (cp_parser* parser, bool *if_p, bool ivdep) +cp_parser_iteration_statement (cp_parser* parser, bool *if_p, bool ivdep, + unsigned short unroll) { cp_token *token; enum rid keyword; @@ -12088,7 +12099,7 @@ cp_parser_iteration_statement (cp_parser* parser, bool *if_p, bool ivdep) parens.require_open (parser); /* Parse the condition. */ condition = cp_parser_condition (parser); - finish_while_stmt_cond (condition, statement, ivdep); + finish_while_stmt_cond (condition, statement, ivdep, unroll); /* Look for the `)'. */ parens.require_close (parser); /* Parse the dependent statement. */ @@ -12123,7 +12134,7 @@ cp_parser_iteration_statement (cp_parser* parser, bool *if_p, bool ivdep) /* Parse the expression. */ expression = cp_parser_expression (parser); /* We're done with the do-statement. */ - finish_do_stmt (expression, statement, ivdep); + finish_do_stmt (expression, statement, ivdep, unroll); /* Look for the `)'. */ parens.require_close (parser); /* Look for the `;'. */ @@ -12137,7 +12148,7 @@ cp_parser_iteration_statement (cp_parser* parser, bool *if_p, bool ivdep) matching_parens parens; parens.require_open (parser); - statement = cp_parser_for (parser, ivdep); + statement = cp_parser_for (parser, ivdep, unroll); /* Look for the `)'. */ parens.require_close (parser); @@ -38377,6 +38388,45 @@ cp_parser_initial_pragma (cp_token *first_token) cp_lexer_get_preprocessor_token (NULL, first_token); } +/* Parse a pragma GCC ivdep. */ + +static bool +cp_parser_pragma_ivdep (cp_parser *parser, cp_token *pragma_tok) +{ + cp_parser_skip_to_pragma_eol (parser, pragma_tok); + return true; +} + +/* Parse a pragma GCC unroll. */ + +static unsigned short +cp_parser_pragma_unroll (cp_parser *parser, cp_token *pragma_tok) +{ + location_t location = cp_lexer_peek_token (parser->lexer)->location; + tree expr = cp_parser_constant_expression (parser); + unsigned short unroll; + expr = maybe_constant_value (expr); + HOST_WIDE_INT lunroll = 0; + if (!INTEGRAL_TYPE_P (TREE_TYPE (expr)) + || TREE_CODE (expr) != INTEGER_CST + || (lunroll = tree_to_shwi (expr)) < 0 + || lunroll >= USHRT_MAX) + { + error_at (location, "%<#pragma GCC unroll%> requires an" + " assignment-expression that evaluates to a non-negative" + " integral constant less than %u", USHRT_MAX); + unroll = 0; + } + else + { + unroll = (unsigned short)lunroll; + if (unroll == 0) + unroll = 1; + } + cp_parser_skip_to_pragma_eol (parser, pragma_tok); + return unroll; +} + /* Normal parsing of a pragma token. Here we can (and must) use the regular lexer. */ @@ -38618,17 +38668,60 @@ cp_parser_pragma (cp_parser *parser, enum pragma_context context, bool *if_p) "%<#pragma GCC ivdep%> must be inside a function"); break; } - cp_parser_skip_to_pragma_eol (parser, pragma_tok); - cp_token *tok; - tok = cp_lexer_peek_token (the_parser->lexer); + const bool ivdep = cp_parser_pragma_ivdep (parser, pragma_tok); + unsigned short unroll; + cp_token *tok = cp_lexer_peek_token (the_parser->lexer); + if (tok->type == CPP_PRAGMA + && cp_parser_pragma_kind (tok) == PRAGMA_UNROLL) + { + tok = cp_lexer_consume_token (parser->lexer); + unroll = cp_parser_pragma_unroll (parser, tok); + tok = cp_lexer_peek_token (the_parser->lexer); + } + else + unroll = 0; + if (tok->type != CPP_KEYWORD + || (tok->keyword != RID_FOR + && tok->keyword != RID_WHILE + && tok->keyword != RID_DO)) + { + cp_parser_error (parser, "for, while or do statement expected"); + return false; + } + cp_parser_iteration_statement (parser, if_p, ivdep, unroll); + return true; + } + + case PRAGMA_UNROLL: + { + if (context == pragma_external) + { + error_at (pragma_tok->location, + "%<#pragma GCC unroll%> must be inside a function"); + break; + } + const unsigned short unroll + = cp_parser_pragma_unroll (parser, pragma_tok); + bool ivdep; + cp_token *tok = cp_lexer_peek_token (the_parser->lexer); + if (tok->type == CPP_PRAGMA + && cp_parser_pragma_kind (tok) == PRAGMA_IVDEP) + { + tok = cp_lexer_consume_token (parser->lexer); + ivdep = cp_parser_pragma_ivdep (parser, tok); + tok = cp_lexer_peek_token (the_parser->lexer); + } + else + ivdep = false; if (tok->type != CPP_KEYWORD - || (tok->keyword != RID_FOR && tok->keyword != RID_WHILE + || (tok->keyword != RID_FOR + && tok->keyword != RID_WHILE && tok->keyword != RID_DO)) { cp_parser_error (parser, "for, while or do statement expected"); return false; } - cp_parser_iteration_statement (parser, if_p, true); + cp_parser_iteration_statement (parser, if_p, ivdep, unroll); return true; } diff --git a/gcc/cp/pt.c b/gcc/cp/pt.c index 71f45de1dd9..a8144e85a39 100644 --- a/gcc/cp/pt.c +++ b/gcc/cp/pt.c @@ -16127,7 +16127,7 @@ tsubst_expr (tree t, tree args, tsubst_flags_t complain, tree in_decl, RECUR (FOR_INIT_STMT (t)); finish_init_stmt (stmt); tmp = RECUR (FOR_COND (t)); - finish_for_cond (tmp, stmt, false); + finish_for_cond (tmp, stmt, false, 0); tmp = RECUR (FOR_EXPR (t)); finish_for_expr (tmp, stmt); { @@ -16146,6 +16146,8 @@ tsubst_expr (tree t, tree args, tsubst_flags_t complain, tree in_decl, decl = tsubst (decl, args, complain, in_decl); maybe_push_decl (decl); expr = RECUR (RANGE_FOR_EXPR (t)); + const unsigned short unroll + = RANGE_FOR_UNROLL (t) ? tree_to_uhwi (RANGE_FOR_UNROLL (t)) : 0; if (VAR_P (decl) && DECL_DECOMPOSITION_P (decl)) { unsigned int cnt; @@ -16153,11 +16155,11 @@ tsubst_expr (tree t, tree args, tsubst_flags_t complain, tree in_decl, decl = tsubst_decomp_names (decl, RANGE_FOR_DECL (t), args, complain, in_decl, &first, &cnt); stmt = cp_convert_range_for (stmt, decl, expr, first, cnt, - RANGE_FOR_IVDEP (t)); + RANGE_FOR_IVDEP (t), unroll); } else stmt = cp_convert_range_for (stmt, decl, expr, NULL_TREE, 0, - RANGE_FOR_IVDEP (t)); + RANGE_FOR_IVDEP (t), unroll); bool prev = note_iteration_stmt_body_start (); RECUR (RANGE_FOR_BODY (t)); note_iteration_stmt_body_end (prev); @@ -16168,7 +16170,7 @@ tsubst_expr (tree t, tree args, tsubst_flags_t complain, tree in_decl, case WHILE_STMT: stmt = begin_while_stmt (); tmp = RECUR (WHILE_COND (t)); - finish_while_stmt_cond (tmp, stmt, false); + finish_while_stmt_cond (tmp, stmt, false, 0); { bool prev = note_iteration_stmt_body_start (); RECUR (WHILE_BODY (t)); @@ -16186,7 +16188,7 @@ tsubst_expr (tree t, tree args, tsubst_flags_t complain, tree in_decl, } finish_do_body (stmt); tmp = RECUR (DO_COND (t)); - finish_do_stmt (tmp, stmt, false); + finish_do_stmt (tmp, stmt, false, 0); break; case IF_STMT: diff --git a/gcc/cp/semantics.c b/gcc/cp/semantics.c index c6726324ae6..95b20c5b162 100644 --- a/gcc/cp/semantics.c +++ b/gcc/cp/semantics.c @@ -798,7 +798,8 @@ begin_while_stmt (void) WHILE_STMT. */ void -finish_while_stmt_cond (tree cond, tree while_stmt, bool ivdep) +finish_while_stmt_cond (tree cond, tree while_stmt, bool ivdep, + unsigned short unroll) { cond = maybe_convert_cond (cond); finish_cond (&WHILE_COND (while_stmt), cond); @@ -810,6 +811,14 @@ finish_while_stmt_cond (tree cond, tree while_stmt, bool ivdep) build_int_cst (integer_type_node, annot_expr_ivdep_kind), integer_zero_node); + if (unroll && cond != error_mark_node) + WHILE_COND (while_stmt) = build3 (ANNOTATE_EXPR, + TREE_TYPE (WHILE_COND (while_stmt)), + WHILE_COND (while_stmt), + build_int_cst (integer_type_node, + annot_expr_unroll_kind), + build_int_cst (integer_type_node, + unroll)); simplify_loop_decl_cond (&WHILE_COND (while_stmt), WHILE_BODY (while_stmt)); } @@ -854,7 +863,7 @@ finish_do_body (tree do_stmt) COND is as indicated. */ void -finish_do_stmt (tree cond, tree do_stmt, bool ivdep) +finish_do_stmt (tree cond, tree do_stmt, bool ivdep, unsigned short unroll) { cond = maybe_convert_cond (cond); end_maybe_infinite_loop (cond); @@ -862,6 +871,10 @@ finish_do_stmt (tree cond, tree do_stmt, bool ivdep) cond = build3 (ANNOTATE_EXPR, TREE_TYPE (cond), cond, build_int_cst (integer_type_node, annot_expr_ivdep_kind), integer_zero_node); + if (unroll && cond != error_mark_node) + cond = build3 (ANNOTATE_EXPR, TREE_TYPE (cond), cond, + build_int_cst (integer_type_node, annot_expr_unroll_kind), + build_int_cst (integer_type_node, unroll)); DO_COND (do_stmt) = cond; } @@ -970,7 +983,7 @@ finish_init_stmt (tree for_stmt) FOR_STMT. */ void -finish_for_cond (tree cond, tree for_stmt, bool ivdep) +finish_for_cond (tree cond, tree for_stmt, bool ivdep, unsigned short unroll) { cond = maybe_convert_cond (cond); finish_cond (&FOR_COND (for_stmt), cond); @@ -982,6 +995,14 @@ finish_for_cond (tree cond, tree for_stmt, bool ivdep) build_int_cst (integer_type_node, annot_expr_ivdep_kind), integer_zero_node); + if (unroll && cond != error_mark_node) + FOR_COND (for_stmt) = build3 (ANNOTATE_EXPR, + TREE_TYPE (FOR_COND (for_stmt)), + FOR_COND (for_stmt), + build_int_cst (integer_type_node, + annot_expr_unroll_kind), + build_int_cst (integer_type_node, + unroll)); simplify_loop_decl_cond (&FOR_COND (for_stmt), FOR_BODY (for_stmt)); } @@ -1057,7 +1078,7 @@ begin_range_for_stmt (tree scope, tree init) begin_maybe_infinite_loop (boolean_false_node); r = build_stmt (input_location, RANGE_FOR_STMT, - NULL_TREE, NULL_TREE, NULL_TREE, NULL_TREE); + NULL_TREE, NULL_TREE, NULL_TREE, NULL_TREE, NULL_TREE); if (scope == NULL_TREE) { diff --git a/gcc/doc/extend.texi b/gcc/doc/extend.texi index 677897bba38..2a553ad3e16 100644 --- a/gcc/doc/extend.texi +++ b/gcc/doc/extend.texi @@ -22381,9 +22381,7 @@ function. The parenthesis around the options is optional. The @code{#pragma GCC target} pragma is presently implemented for x86, ARM, AArch64, PowerPC, S/390, and Nios II targets only. -@end table -@table @code @item #pragma GCC optimize (@var{"string"}...) @cindex pragma GCC optimize @@ -22394,9 +22392,7 @@ if @code{attribute((optimize("STRING")))} was specified for that function. The parenthesis around the options is optional. @xref{Function Attributes}, for more information about the @code{optimize} attribute and the attribute syntax. -@end table -@table @code @item #pragma GCC push_options @itemx #pragma GCC pop_options @cindex pragma GCC push_options @@ -22407,15 +22403,14 @@ options. It is intended for include files where you temporarily want to switch to using a different @samp{#pragma GCC target} or @samp{#pragma GCC optimize} and then to pop back to the previous options. -@end table -@table @code @item #pragma GCC reset_options @cindex pragma GCC reset_options This pragma clears the current @code{#pragma GCC target} and @code{#pragma GCC optimize} to use the default switches as specified on the command line. + @end table @node Loop-Specific Pragmas @@ -22424,7 +22419,6 @@ on the command line. @table @code @item #pragma GCC ivdep @cindex pragma GCC ivdep -@end table With this pragma, the programmer asserts that there are no loop-carried dependencies which would prevent consecutive iterations of @@ -22459,6 +22453,16 @@ void ignore_vec_dep (int *a, int k, int c, int m) @} @end smallexample +@item #pragma GCC unroll @var{n} +@cindex pragma GCC unroll @var{n} + +You can use this pragma to control how many times a loop should be unrolled. +It must be placed immediately before a @code{for}, @code{while} or @code{do} +loop or a @code{#pragma GCC ivdep}, and applies only to the loop that follows. +@var{n} is an integer constant expression specifying the unrolling factor. +The values of @math{0} and @math{1} block any unrolling of the loop. + +@end table @node Unnamed Fields @section Unnamed Structure and Union Fields diff --git a/gcc/fortran/ChangeLog b/gcc/fortran/ChangeLog index a601cfaf0cf..d0cbc37bf93 100644 --- a/gcc/fortran/ChangeLog +++ b/gcc/fortran/ChangeLog @@ -1,3 +1,22 @@ +2017-12-22 Bernhard Reutner-Fischer + Eric Botcazou + + * array.c (gfc_copy_iterator): Copy unroll field. + * decl.c (directive_unroll): New global variable. + (gfc_match_gcc_unroll): New function. + * gfortran.h (gfc_iterator]): Add unroll field. + (directive_unroll): Declare: + * match.c (gfc_match_do): Use memset to initialize the iterator. + * match.h (gfc_match_gcc_unroll): New prototype. + * parse.c (decode_gcc_attribute): Match "unroll". + (parse_do_block): Set iterator's unroll. + (parse_executable): Diagnose misplaced unroll directive. + * trans-stmt.c (gfc_trans_simple_do) Annotate loop condition with + annot_expr_unroll_kind. + (gfc_trans_do): Likewise. + * gfortran.texi (GNU Fortran Compiler Directives): Split section into + subections 'ATTRIBUTES directive' and 'UNROLL directive'. + 2017-12-19 Jakub Jelinek * scanner.c (preprocessor_line): Replace Yoda conditions with typical diff --git a/gcc/fortran/array.c b/gcc/fortran/array.c index 46642bb5d97..81476b2f6fa 100644 --- a/gcc/fortran/array.c +++ b/gcc/fortran/array.c @@ -2123,6 +2123,7 @@ gfc_copy_iterator (gfc_iterator *src) dest->start = gfc_copy_expr (src->start); dest->end = gfc_copy_expr (src->end); dest->step = gfc_copy_expr (src->step); + dest->unroll = src->unroll; return dest; } diff --git a/gcc/fortran/decl.c b/gcc/fortran/decl.c index 53a87b67eff..d2c794fc2ae 100644 --- a/gcc/fortran/decl.c +++ b/gcc/fortran/decl.c @@ -95,6 +95,9 @@ gfc_symbol *gfc_new_block; bool gfc_matching_function; +/* Set upon parsing a !GCC$ unroll n directive for use in the next loop. */ +int directive_unroll = -1; + /* If a kind expression of a component of a parameterized derived type is parameterized, temporarily store the expression here. */ static gfc_expr *saved_kind_expr = NULL; @@ -104,7 +107,6 @@ static gfc_expr *saved_kind_expr = NULL; static gfc_actual_arglist *decl_type_param_list; static gfc_actual_arglist *type_param_spec_list; - /********************* DATA statement subroutines *********************/ static bool in_match_data = false; @@ -10958,3 +10960,37 @@ syntax: gfc_error ("Syntax error in !GCC$ ATTRIBUTES statement at %C"); return MATCH_ERROR; } + + +/* Match a !GCC$ UNROLL statement of the form: + !GCC$ UNROLL n + + The parameter n is the number of times we are supposed to unroll. + + When we come here, we have already matched the !GCC$ UNROLL string. */ +match +gfc_match_gcc_unroll (void) +{ + int value; + + if (gfc_match_small_int (&value) == MATCH_YES) + { + if (value < 0 || value > USHRT_MAX) + { + gfc_error ("% directive requires a" + " non-negative integral constant" + " less than or equal to %u at %C", + USHRT_MAX + ); + return MATCH_ERROR; + } + if (gfc_match_eos () == MATCH_YES) + { + directive_unroll = value == 0 ? 1 : value; + return MATCH_YES; + } + } + + gfc_error ("Syntax error in !GCC$ UNROLL directive at %C"); + return MATCH_ERROR; +} diff --git a/gcc/fortran/gfortran.h b/gcc/fortran/gfortran.h index c5e62d7200a..7b837c9fe50 100644 --- a/gcc/fortran/gfortran.h +++ b/gcc/fortran/gfortran.h @@ -2350,6 +2350,7 @@ gfc_case; typedef struct { gfc_expr *var, *start, *end, *step; + unsigned short unroll; } gfc_iterator; @@ -2724,6 +2725,7 @@ gfc_finalizer; /* decl.c */ bool gfc_in_match_data (void); match gfc_match_char_spec (gfc_typespec *); +extern int directive_unroll; /* Handling Parameterized Derived Types */ bool gfc_insert_kind_parameter_exprs (gfc_expr *); diff --git a/gcc/fortran/gfortran.texi b/gcc/fortran/gfortran.texi index aabf26851fd..fc95ec0bf74 100644 --- a/gcc/fortran/gfortran.texi +++ b/gcc/fortran/gfortran.texi @@ -3447,6 +3447,14 @@ as this requires the new array descriptor. @node GNU Fortran Compiler Directives @section GNU Fortran Compiler Directives +@menu +* ATTRIBUTES directive:: +* UNROLL directive:: +@end menu + +@node ATTRIBUTES directive +@subsection ATTRIBUTES directive + The Fortran standard describes how a conforming program shall behave; however, the exact implementation is not standardized. In order to allow the user to choose specific implementation details, compiler @@ -3520,6 +3528,19 @@ of the procedure; for variables and procedure pointers, they shall be in the same declaration part as the variable or procedure pointer. +@node UNROLL directive +@subsection UNROLL directive + +The syntax of the directive is + +@code{!GCC$ unroll N} + +You can use this directive to control how many times a loop should be unrolled. +It must be placed immediately before a @code{DO} loop and applies only to the +loop that follows. N is an integer constant specifying the unrolling factor. +The values of 0 and 1 block any unrolling of the loop. + + @node Non-Fortran Main Program @section Non-Fortran Main Program diff --git a/gcc/fortran/match.c b/gcc/fortran/match.c index d63b11c2080..f7de5d501fc 100644 --- a/gcc/fortran/match.c +++ b/gcc/fortran/match.c @@ -2540,8 +2540,8 @@ gfc_match_do (void) old_loc = gfc_current_locus; + memset (&iter, '\0', sizeof (gfc_iterator)); label = NULL; - iter.var = iter.start = iter.end = iter.step = NULL; m = gfc_match_label (); if (m == MATCH_ERROR) diff --git a/gcc/fortran/match.h b/gcc/fortran/match.h index d6df349532c..a5f62913a85 100644 --- a/gcc/fortran/match.h +++ b/gcc/fortran/match.h @@ -241,6 +241,7 @@ match gfc_match_contiguous (void); match gfc_match_dimension (void); match gfc_match_external (void); match gfc_match_gcc_attributes (void); +match gfc_match_gcc_unroll (void); match gfc_match_import (void); match gfc_match_intent (void); match gfc_match_intrinsic (void); diff --git a/gcc/fortran/parse.c b/gcc/fortran/parse.c index 08bff3f8615..91be6da7236 100644 --- a/gcc/fortran/parse.c +++ b/gcc/fortran/parse.c @@ -1063,6 +1063,7 @@ decode_gcc_attribute (void) old_locus = gfc_current_locus; match ("attributes", gfc_match_gcc_attributes, ST_ATTR_DECL); + match ("unroll", gfc_match_gcc_unroll, ST_NONE); /* All else has failed, so give up. See if any of the matchers has stored an error message of some sort. */ @@ -4635,7 +4636,14 @@ parse_do_block (void) s.ext.end_do_label = new_st.label1; if (new_st.ext.iterator != NULL) - stree = new_st.ext.iterator->var->symtree; + { + stree = new_st.ext.iterator->var->symtree; + if (directive_unroll != -1) + { + new_st.ext.iterator->unroll = directive_unroll; + directive_unroll = -1; + } + } else stree = NULL; @@ -5393,6 +5401,9 @@ parse_executable (gfc_statement st) return st; } + if (directive_unroll != -1) + gfc_error ("% directive does not commence a loop at %C"); + st = next_statement (); } } diff --git a/gcc/fortran/trans-stmt.c b/gcc/fortran/trans-stmt.c index df29b78e3f1..e6569e02036 100644 --- a/gcc/fortran/trans-stmt.c +++ b/gcc/fortran/trans-stmt.c @@ -1979,6 +1979,11 @@ gfc_trans_simple_do (gfc_code * code, stmtblock_t *pblock, tree dovar, fold_convert (type, to)); cond = gfc_evaluate_now_loc (loc, cond, &body); + if (code->ext.iterator->unroll && cond != error_mark_node) + cond + = build3 (ANNOTATE_EXPR, TREE_TYPE (cond), cond, + build_int_cst (integer_type_node, annot_expr_unroll_kind), + build_int_cst (integer_type_node, code->ext.iterator->unroll)); /* The loop exit. */ tmp = fold_build1_loc (loc, GOTO_EXPR, void_type_node, exit_label); @@ -2305,6 +2310,11 @@ gfc_trans_do (gfc_code * code, tree exit_cond) /* End with the loop condition. Loop until countm1t == 0. */ cond = fold_build2_loc (loc, EQ_EXPR, logical_type_node, countm1t, build_int_cst (utype, 0)); + if (code->ext.iterator->unroll && cond != error_mark_node) + cond + = build3 (ANNOTATE_EXPR, TREE_TYPE (cond), cond, + build_int_cst (integer_type_node, annot_expr_unroll_kind), + build_int_cst (integer_type_node, code->ext.iterator->unroll)); tmp = fold_build1_loc (loc, GOTO_EXPR, void_type_node, exit_label); tmp = fold_build3_loc (loc, COND_EXPR, void_type_node, cond, tmp, build_empty_stmt (loc)); diff --git a/gcc/testsuite/ChangeLog b/gcc/testsuite/ChangeLog index 3969bd2771e..91d1102e206 100644 --- a/gcc/testsuite/ChangeLog +++ b/gcc/testsuite/ChangeLog @@ -1,3 +1,20 @@ +2017-12-22 Mike Stump + Eric Botcazou + + * c-c++-common/unroll-1.c: New test. + * c-c++-common/unroll-2.c: Likewise. + * c-c++-common/unroll-3.c: Likewise. + * c-c++-common/unroll-4.c: Likewise. + * c-c++-common/unroll-5.c: Likewise. + * g++.dg/ext/unroll-1.C: Likewise. + * g++.dg/ext/unroll-2.C: Likewise. + * g++.dg/ext/unroll-3.C: Likewise. + * gfortran.dg/directive_unroll_1.f90: Likewise. + * gfortran.dg/directive_unroll_2.f90: Likewise. + * gfortran.dg/directive_unroll_3.f90: Likewise. + * gfortran.dg/directive_unroll_4.f90: Likewise. + * gfortran.dg/directive_unroll_5.f90: Likewise. + 2017-12-22 Alexandre Oliva PR debug/83527 diff --git a/gcc/testsuite/c-c++-common/unroll-1.c b/gcc/testsuite/c-c++-common/unroll-1.c new file mode 100644 index 00000000000..ccae250af53 --- /dev/null +++ b/gcc/testsuite/c-c++-common/unroll-1.c @@ -0,0 +1,41 @@ +/* { dg-do compile } */ +/* { dg-options "-O2 -fdump-tree-cunrolli-details -fdump-rtl-loop2_unroll-details" } */ + +extern void bar (int); + +int j; + +void test (void) +{ + #pragma GCC unroll 8 + for (unsigned long i = 1; i <= 8; ++i) + bar(i); + /* { dg-final { scan-tree-dump "11:.*: note: loop with 8 iterations completely unrolled" "cunrolli" } } */ + + #pragma GCC unroll 8 + for (unsigned long i = 1; i <= 7; ++i) + bar(i); + /* { dg-final { scan-tree-dump "16:.*: note: loop with 7 iterations completely unrolled" "cunrolli" } } */ + + #pragma GCC unroll 8 + for (unsigned long i = 1; i <= 15; ++i) + bar(i); + /* { dg-final { scan-rtl-dump "21:.*: note: loop unrolled 7 times" "loop2_unroll" } } */ + + #pragma GCC unroll 8 + for (unsigned long i = 1; i <= j; ++i) + bar(i); + /* { dg-final { scan-rtl-dump "26:.*: note: loop unrolled 7 times" "loop2_unroll" } } */ + + #pragma GCC unroll 7 + for (unsigned long i = 1; i <= j; ++i) + bar(i); + /* { dg-final { scan-rtl-dump "31:.*: note: loop unrolled 3 times" "loop2_unroll" } } */ + + unsigned long i = 0; + #pragma GCC unroll 3 + do { + bar(i); + } while (++i < 9); + /* { dg-final { scan-rtl-dump "3\[79\]:.*: note: loop unrolled 2 times" "loop2_unroll" } } */ +} diff --git a/gcc/testsuite/c-c++-common/unroll-2.c b/gcc/testsuite/c-c++-common/unroll-2.c new file mode 100644 index 00000000000..635b6c25349 --- /dev/null +++ b/gcc/testsuite/c-c++-common/unroll-2.c @@ -0,0 +1,41 @@ +/* { dg-do compile } */ +/* { dg-options "-O -fdump-tree-cunroll-details -fdump-rtl-loop2_unroll-details" } */ + +extern void bar (int); + +int j; + +void test (void) +{ + #pragma GCC unroll 8 + for (unsigned long i = 1; i <= 8; ++i) + bar(i); + /* { dg-final { scan-tree-dump "11:.*: note: loop with 7 iterations completely unrolled" "cunroll" } } */ + + #pragma GCC unroll 8 + for (unsigned long i = 1; i <= 7; ++i) + bar(i); + /* { dg-final { scan-tree-dump "16:.*: note: loop with 6 iterations completely unrolled" "cunroll" } } */ + + #pragma GCC unroll 8 + for (unsigned long i = 1; i <= 15; ++i) + bar(i); + /* { dg-final { scan-rtl-dump "21:.*: note: loop unrolled 7 times" "loop2_unroll" } } */ + + #pragma GCC unroll 8 + for (unsigned long i = 1; i <= j; ++i) + bar(i); + /* { dg-final { scan-rtl-dump "26:.*: note: loop unrolled 7 times" "loop2_unroll" } } */ + + #pragma GCC unroll 7 + for (unsigned long i = 1; i <= j; ++i) + bar(i); + /* { dg-final { scan-rtl-dump "31:.*: note: loop unrolled 3 times" "loop2_unroll" } } */ + + unsigned long i = 0; + #pragma GCC unroll 3 + do { + bar(i); + } while (++i < 9); + /* { dg-final { scan-rtl-dump "3\[79\]:.*: note: loop unrolled 2 times" "loop2_unroll" } } */ +} diff --git a/gcc/testsuite/c-c++-common/unroll-3.c b/gcc/testsuite/c-c++-common/unroll-3.c new file mode 100644 index 00000000000..6cefa7583ba --- /dev/null +++ b/gcc/testsuite/c-c++-common/unroll-3.c @@ -0,0 +1,41 @@ +/* { dg-do compile } */ +/* { dg-options "-O -fdisable-tree-cunroll -fdump-rtl-loop2_unroll-details" } */ + +extern void bar (int); + +int j; + +void test (void) +{ + #pragma GCC unroll 8 + for (unsigned long i = 1; i <= 8; ++i) + bar(i); + /* { dg-final { scan-rtl-dump-not "11:.*: note: loop unrolled" "loop2_unroll" } } */ + + #pragma GCC unroll 8 + for (unsigned long i = 1; i <= 7; ++i) + bar(i); + /* { dg-final { scan-rtl-dump-not "16:.*: note: loop unrolled" "loop2_unroll" } } */ + + #pragma GCC unroll 8 + for (unsigned long i = 1; i <= 15; ++i) + bar(i); + /* { dg-final { scan-rtl-dump "21:.*: note: loop unrolled 7 times" "loop2_unroll" } } */ + + #pragma GCC unroll 8 + for (unsigned long i = 1; i <= j; ++i) + bar(i); + /* { dg-final { scan-rtl-dump "26:.*: note: loop unrolled 7 times" "loop2_unroll" } } */ + + #pragma GCC unroll 7 + for (unsigned long i = 1; i <= j; ++i) + bar(i); + /* { dg-final { scan-rtl-dump "31:.*: note: loop unrolled 3 times" "loop2_unroll" } } */ + + unsigned long i = 0; + #pragma GCC unroll 3 + do { + bar(i); + } while (++i < 9); + /* { dg-final { scan-rtl-dump "3\[79\]:.*: note: loop unrolled 2 times" "loop2_unroll" } } */ +} diff --git a/gcc/testsuite/c-c++-common/unroll-4.c b/gcc/testsuite/c-c++-common/unroll-4.c new file mode 100644 index 00000000000..1c1988174ba --- /dev/null +++ b/gcc/testsuite/c-c++-common/unroll-4.c @@ -0,0 +1,22 @@ +/* { dg-do compile } */ +/* { dg-options "-O2 -funroll-all-loops -fdump-rtl-loop2_unroll-details -fdump-tree-cunrolli-details" } */ + +extern void bar (int); + +int j; + +void test (void) +{ + #pragma GCC unroll 0 + #pragma GCC ivdep + for (unsigned long i = 1; i <= 3; ++i) + bar(i); + + #pragma GCC ivdep + #pragma GCC unroll 0 + for (unsigned long i = 1; i <= j; ++i) + bar(i); + + /* { dg-final { scan-tree-dump "Not unrolling loop .: user didn't want it unrolled completely" "cunrolli" } } */ + /* { dg-final { scan-rtl-dump-times "Not unrolling loop, user didn't want it unrolled" 2 "loop2_unroll" } } */ +} diff --git a/gcc/testsuite/c-c++-common/unroll-5.c b/gcc/testsuite/c-c++-common/unroll-5.c new file mode 100644 index 00000000000..754f3b18d57 --- /dev/null +++ b/gcc/testsuite/c-c++-common/unroll-5.c @@ -0,0 +1,29 @@ +/* { dg-do compile } */ + +extern void bar (int); + +int j; + +void test (void) +{ + #pragma GCC unroll 4+4 + for (unsigned long i = 1; i <= 8; ++i) + bar(i); + + #pragma GCC unroll -1 /* { dg-error "requires an assignment-expression that evaluates to a non-negative integral constant less than" } */ + for (unsigned long i = 1; i <= 8; ++i) + bar(i); + + #pragma GCC unroll 20000000000 /* { dg-error "requires an assignment-expression that evaluates to a non-negative integral constant less than" } */ + for (unsigned long i = 1; i <= 8; ++i) + bar(i); + + #pragma GCC unroll j /* { dg-error "requires an assignment-expression that evaluates to a non-negative integral constant less than" } */ + /* { dg-error "cannot appear in a constant-expression|is not usable in a constant expression" "" { target c++ } 21 } */ + for (unsigned long i = 1; i <= 8; ++i) + bar(i); + + #pragma GCC unroll 4.2 /* { dg-error "requires an assignment-expression that evaluates to a non-negative integral constant less than" } */ + for (unsigned long i = 1; i <= 8; ++i) + bar(i); +} diff --git a/gcc/testsuite/g++.dg/ext/unroll-1.C b/gcc/testsuite/g++.dg/ext/unroll-1.C new file mode 100644 index 00000000000..2d58a6a7b0e --- /dev/null +++ b/gcc/testsuite/g++.dg/ext/unroll-1.C @@ -0,0 +1,19 @@ +// { dg-do compile } +// { dg-options "-O2 -fdump-tree-cunrolli-details" } + +template +void +foo (T *a, T *b, T *c) +{ +#pragma GCC unroll 8 + for (int i = 0; i < 8; i++) + a[i] = b[i] * c[i]; +} + +void +bar (int *a, int *b, int *c) +{ + foo (a, b, c); +} + +// { dg-final { scan-tree-dump "note: loop with 8 iterations completely unrolled" "cunrolli" } } diff --git a/gcc/testsuite/g++.dg/ext/unroll-2.C b/gcc/testsuite/g++.dg/ext/unroll-2.C new file mode 100644 index 00000000000..e68cc317675 --- /dev/null +++ b/gcc/testsuite/g++.dg/ext/unroll-2.C @@ -0,0 +1,13 @@ +// { dg-do compile } +// { dg-options "-O2 -fdump-tree-cunrolli-details" } +// { dg-skip-if "range for" { *-*-* } { "-std=gnu++98" } { "" } } + +void +foo (int (&a)[8], int *b, int *c) +{ +#pragma GCC unroll 8 + for (int i : a) + a[i] = b[i] * c[i]; +} + +// { dg-final { scan-tree-dump "note: loop with 8 iterations completely unrolled" "cunrolli" } } diff --git a/gcc/testsuite/g++.dg/ext/unroll-3.C b/gcc/testsuite/g++.dg/ext/unroll-3.C new file mode 100644 index 00000000000..6516ee97c96 --- /dev/null +++ b/gcc/testsuite/g++.dg/ext/unroll-3.C @@ -0,0 +1,20 @@ +// { dg-do compile } +// { dg-options "-O2 -fdump-tree-cunrolli-details" } +// { dg-skip-if "range for" { *-*-* } { "-std=gnu++98" } { "" } } + +template +void +foo (T (&a)[8], T *b, T *c) +{ +#pragma GCC unroll 8 + for (int i : a) + a[i] = b[i] * c[i]; +} + +void +bar (int (&a)[8], int *b, int *c) +{ + foo (a, b, c); +} + +// { dg-final { scan-tree-dump "note: loop with 8 iterations completely unrolled" "cunrolli" } } diff --git a/gcc/testsuite/gfortran.dg/directive_unroll_1.f90 b/gcc/testsuite/gfortran.dg/directive_unroll_1.f90 new file mode 100644 index 00000000000..00fe7dcb345 --- /dev/null +++ b/gcc/testsuite/gfortran.dg/directive_unroll_1.f90 @@ -0,0 +1,52 @@ +! { dg-do compile } +! { dg-options "-O2 -fdump-tree-cunrolli-details -fdump-rtl-loop2_unroll-details" } +! Test that +! #pragma GCC unroll n +! works + +subroutine test1(a) + implicit NONE + integer :: a(8) + integer (kind=4) :: i +!GCC$ unroll 8 + DO i=1, 8, 1 + call dummy(a(i)) + ENDDO +! { dg-final { scan-tree-dump "12:.*: note: loop with 8 iterations completely unrolled" "cunrolli" } } */ +end subroutine test1 + +subroutine test2(a, n) + implicit NONE + integer :: a(n) + integer (kind=1), intent(in) :: n + integer (kind=4) :: i +!GCC$ unroll 8 + DO i=1, n, 1 + call dummy(a(i)) + ENDDO +! { dg-final { scan-rtl-dump "24:.: note: loop unrolled 7 times" "loop2_unroll" } } +end subroutine test2 + +subroutine test3(a, n) + implicit NONE + integer (kind=1), intent(in) :: n + integer :: a(n) + integer (kind=4) :: i +!GCC$ unroll 8 + DO i=n, 1, -1 + call dummy(a(i)) + ENDDO +! { dg-final { scan-rtl-dump "36:.: note: loop unrolled 7 times" "loop2_unroll" } } +end subroutine test3 + +subroutine test4(a, n) + implicit NONE + integer (kind=1), intent(in) :: n + integer :: a(n) + integer (kind=4) :: i +!GCC$ unroll 8 + DO i=1, n, 2 + call dummy(a(i)) + ENDDO +! { dg-final { scan-rtl-dump "48:.: note: loop unrolled 7 times" "loop2_unroll" } } +end subroutine test4 diff --git a/gcc/testsuite/gfortran.dg/directive_unroll_2.f90 b/gcc/testsuite/gfortran.dg/directive_unroll_2.f90 new file mode 100644 index 00000000000..bc93f91afa7 --- /dev/null +++ b/gcc/testsuite/gfortran.dg/directive_unroll_2.f90 @@ -0,0 +1,52 @@ +! { dg-do compile } +! { dg-options "-O -fdump-tree-cunroll-details -fdump-rtl-loop2_unroll-details" } +! Test that +! #pragma GCC unroll n +! works + +subroutine test1(a) + implicit NONE + integer :: a(8) + integer (kind=4) :: i +!GCC$ unroll 8 + DO i=1, 8, 1 + call dummy(a(i)) + ENDDO +! { dg-final { scan-tree-dump "12:.*: note: loop with 7 iterations completely unrolled" "cunroll" } } */ +end subroutine test1 + +subroutine test2(a, n) + implicit NONE + integer :: a(n) + integer (kind=1), intent(in) :: n + integer (kind=4) :: i +!GCC$ unroll 8 + DO i=1, n, 1 + call dummy(a(i)) + ENDDO +! { dg-final { scan-rtl-dump "24:.: note: loop unrolled 7 times" "loop2_unroll" } } +end subroutine test2 + +subroutine test3(a, n) + implicit NONE + integer (kind=1), intent(in) :: n + integer :: a(n) + integer (kind=4) :: i +!GCC$ unroll 8 + DO i=n, 1, -1 + call dummy(a(i)) + ENDDO +! { dg-final { scan-rtl-dump "36:.: note: loop unrolled 7 times" "loop2_unroll" } } +end subroutine test3 + +subroutine test4(a, n) + implicit NONE + integer (kind=1), intent(in) :: n + integer :: a(n) + integer (kind=4) :: i +!GCC$ unroll 8 + DO i=1, n, 2 + call dummy(a(i)) + ENDDO +! { dg-final { scan-rtl-dump "48:.: note: loop unrolled 7 times" "loop2_unroll" } } +end subroutine test4 diff --git a/gcc/testsuite/gfortran.dg/directive_unroll_3.f90 b/gcc/testsuite/gfortran.dg/directive_unroll_3.f90 new file mode 100644 index 00000000000..4e3ec09547a --- /dev/null +++ b/gcc/testsuite/gfortran.dg/directive_unroll_3.f90 @@ -0,0 +1,52 @@ +! { dg-do compile } +! { dg-options "-O -fdisable-tree-cunroll -fdump-rtl-loop2_unroll-details" } +! Test that +! #pragma GCC unroll n +! works + +subroutine test1(a) + implicit NONE + integer :: a(8) + integer (kind=4) :: i +!GCC$ unroll 8 + DO i=1, 8, 1 + call dummy(a(i)) + ENDDO +! { dg-final { scan-rtl-dump-not "12:.: note: loop unrolled" "loop2_unroll" } } +end subroutine test1 + +subroutine test2(a, n) + implicit NONE + integer :: a(n) + integer (kind=1), intent(in) :: n + integer (kind=4) :: i +!GCC$ unroll 8 + DO i=1, n, 1 + call dummy(a(i)) + ENDDO +! { dg-final { scan-rtl-dump "24:.: note: loop unrolled 7 times" "loop2_unroll" } } +end subroutine test2 + +subroutine test3(a, n) + implicit NONE + integer (kind=1), intent(in) :: n + integer :: a(n) + integer (kind=4) :: i +!GCC$ unroll 8 + DO i=n, 1, -1 + call dummy(a(i)) + ENDDO +! { dg-final { scan-rtl-dump "36:.: note: loop unrolled 7 times" "loop2_unroll" } } +end subroutine test3 + +subroutine test4(a, n) + implicit NONE + integer (kind=1), intent(in) :: n + integer :: a(n) + integer (kind=4) :: i +!GCC$ unroll 8 + DO i=1, n, 2 + call dummy(a(i)) + ENDDO +! { dg-final { scan-rtl-dump "48:.: note: loop unrolled 7 times" "loop2_unroll" } } +end subroutine test4 diff --git a/gcc/testsuite/gfortran.dg/directive_unroll_4.f90 b/gcc/testsuite/gfortran.dg/directive_unroll_4.f90 new file mode 100644 index 00000000000..fbb5f24e76f --- /dev/null +++ b/gcc/testsuite/gfortran.dg/directive_unroll_4.f90 @@ -0,0 +1,29 @@ +! { dg-do compile } +! { dg-options "-O2 -funroll-all-loops -fdump-rtl-loop2_unroll-details -fdump-tree-cunrolli-details" } +! Test that +! #pragma GCC unroll n +! works + +subroutine test1(a) + implicit NONE + integer :: a(8) + integer (kind=4) :: i +!GCC$ unroll 0 + DO i=1, 8, 1 + call dummy(a(i)) + ENDDO +end subroutine test1 + +subroutine test2(a, n) + implicit NONE + integer :: a(n) + integer (kind=1), intent(in) :: n + integer (kind=4) :: i +!GCC$ unroll 0 + DO i=1, n, 1 + call dummy(a(i)) + ENDDO +end subroutine test2 + +! { dg-final { scan-tree-dump "Not unrolling loop .: user didn't want it unrolled completely" "cunrolli" } } */ +! { dg-final { scan-rtl-dump-times "Not unrolling loop, user didn't want it unrolled" 2 "loop2_unroll" } } */ diff --git a/gcc/testsuite/gfortran.dg/directive_unroll_5.f90 b/gcc/testsuite/gfortran.dg/directive_unroll_5.f90 new file mode 100644 index 00000000000..b88b4b2c380 --- /dev/null +++ b/gcc/testsuite/gfortran.dg/directive_unroll_5.f90 @@ -0,0 +1,38 @@ +! { dg-do compile } + +! Test that +! #pragma GCC unroll n +! rejects invalid n and improper use + +subroutine wrong1(n) + implicit NONE + integer (kind=1), intent(in) :: n + integer (kind=4) :: i +!GCC$ unroll 999999999 ! { dg-error "non-negative integral constant less than" } + DO i=0, n, 1 + call dummy1(i) + ENDDO +end subroutine wrong1 + +subroutine wrong2(a, b, n) + implicit NONE + integer (kind=1), intent(in) :: n + integer :: a(n), b(n) + integer (kind=4) :: i +!GCC$ unroll -1 ! { dg-error "non-negative integral constant less than" } + DO i=1, n, 2 + call dummy2(a(i), b(i), i) + ENDDO +end subroutine wrong2 + +subroutine wrong3(a, b, n) + implicit NONE + integer (kind=1), intent(in) :: n + integer :: a(n), b(n) + integer (kind=4) :: i +!GCC$ unroll 8 + write (*,*) "wrong"! { dg-error "directive does not commence a loop" } + DO i=n, 1, -1 + call dummy2(a(i), b(i), i) + ENDDO +end subroutine wrong3 -- 2.30.2