extend.texi (Loop-Specific Pragmas): Document pragma GCC unroll.
authorEric Botcazou <ebotcazou@gcc.gnu.org>
Fri, 22 Dec 2017 10:22:15 +0000 (10:22 +0000)
committerEric Botcazou <ebotcazou@gcc.gnu.org>
Fri, 22 Dec 2017 10:22:15 +0000 (10:22 +0000)
* 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) <PRAGMA_IVDEP>: Add support for pragma Unroll.
<PRAGMA_UNROLL>: New case.
cp/
* constexpr.c (cxx_eval_constant_expression) <ANNOTATE_EXPR>: 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) <PRAGMA_IVDEP>: Add support for pragma Unroll.
<PRAGMA_UNROLL>: New case.
* pt.c (tsubst_expr) <FOR_STMT>: Adjust call to finish_for_cond.
<RANGE_FOR_STMT>: Pass unrolling factor to cp_convert_range_for.
<WHILE_STMT>: Adjust call to finish_while_stmt_cond.
<DO_STMT>: 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

38 files changed:
gcc/ChangeLog
gcc/c-family/ChangeLog
gcc/c-family/c-pragma.c
gcc/c-family/c-pragma.h
gcc/c/ChangeLog
gcc/c/c-parser.c
gcc/cp/ChangeLog
gcc/cp/constexpr.c
gcc/cp/cp-tree.def
gcc/cp/cp-tree.h
gcc/cp/init.c
gcc/cp/parser.c
gcc/cp/pt.c
gcc/cp/semantics.c
gcc/doc/extend.texi
gcc/fortran/ChangeLog
gcc/fortran/array.c
gcc/fortran/decl.c
gcc/fortran/gfortran.h
gcc/fortran/gfortran.texi
gcc/fortran/match.c
gcc/fortran/match.h
gcc/fortran/parse.c
gcc/fortran/trans-stmt.c
gcc/testsuite/ChangeLog
gcc/testsuite/c-c++-common/unroll-1.c [new file with mode: 0644]
gcc/testsuite/c-c++-common/unroll-2.c [new file with mode: 0644]
gcc/testsuite/c-c++-common/unroll-3.c [new file with mode: 0644]
gcc/testsuite/c-c++-common/unroll-4.c [new file with mode: 0644]
gcc/testsuite/c-c++-common/unroll-5.c [new file with mode: 0644]
gcc/testsuite/g++.dg/ext/unroll-1.C [new file with mode: 0644]
gcc/testsuite/g++.dg/ext/unroll-2.C [new file with mode: 0644]
gcc/testsuite/g++.dg/ext/unroll-3.C [new file with mode: 0644]
gcc/testsuite/gfortran.dg/directive_unroll_1.f90 [new file with mode: 0644]
gcc/testsuite/gfortran.dg/directive_unroll_2.f90 [new file with mode: 0644]
gcc/testsuite/gfortran.dg/directive_unroll_3.f90 [new file with mode: 0644]
gcc/testsuite/gfortran.dg/directive_unroll_4.f90 [new file with mode: 0644]
gcc/testsuite/gfortran.dg/directive_unroll_5.f90 [new file with mode: 0644]

index 585a70b..3d36602 100644 (file)
@@ -1,3 +1,8 @@
+2017-12-22  Mike Stump  <mikestump@comcast.net>
+            Eric Botcazou  <ebotcazou@adacore.com>
+
+       * doc/extend.texi (Loop-Specific Pragmas): Document pragma GCC unroll.
+
 2017-12-21  Jakub Jelinek  <jakub@redhat.com>
 
        PR middle-end/83487
index ba61fce..f4efa68 100644 (file)
@@ -1,3 +1,9 @@
+2017-12-22  Mike Stump  <mikestump@comcast.net>
+            Eric Botcazou  <ebotcazou@adacore.com>
+
+       * c-pragma.c (init_pragma): Register pragma GCC unroll.
+       * c-pragma.h (enum pragma_kind): Add PRAGMA_UNROLL.
+
 2017-12-22  Alexandre Oliva <aoliva@redhat.com>
 
        PR debug/83527
index daa9e5a..a4e9b74 100644 (file)
@@ -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
index e035b3a..f55fe4f 100644 (file)
@@ -69,6 +69,7 @@ enum pragma_kind {
 
   PRAGMA_GCC_PCH_PREPROCESS,
   PRAGMA_IVDEP,
+  PRAGMA_UNROLL,
 
   PRAGMA_FIRST_EXTERNAL
 };
index bfa6226..f4756c8 100644 (file)
@@ -1,3 +1,16 @@
+2017-12-22  Mike Stump  <mikestump@comcast.net>
+            Eric Botcazou  <ebotcazou@adacore.com>
+
+       * 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) <PRAGMA_IVDEP>: Add support for pragma Unroll.
+       <PRAGMA_UNROLL>: New case.
+
 2017-12-19  Jakub Jelinek  <jakub@redhat.com>
 
        * c-typeck.c (comptypes_internal, function_types_compatible_p,
index f1bae8a..05d1e0f 100644 (file)
@@ -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<tree> *);
 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)
                                  "%<GCC ivdep%> pragma");
                  cond = error_mark_node;
                }
+             else if (unroll)
+               {
+                 c_parser_error (parser, "missing loop condition in loop with "
+                                 "%<GCC unroll%> 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)
 }
 
 \f
+/* 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:
index e829550..fbffe0c 100644 (file)
@@ -1,3 +1,42 @@
+2017-12-22  Mike Stump  <mikestump@comcast.net>
+            Eric Botcazou  <ebotcazou@adacore.com>
+
+       * constexpr.c (cxx_eval_constant_expression) <ANNOTATE_EXPR>: 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) <PRAGMA_IVDEP>: Add support for pragma Unroll.
+       <PRAGMA_UNROLL>: New case.
+       * pt.c (tsubst_expr) <FOR_STMT>: Adjust call to finish_for_cond.
+       <RANGE_FOR_STMT>: Pass unrolling factor to cp_convert_range_for.
+       <WHILE_STMT>: Adjust call to finish_while_stmt_cond.
+       <DO_STMT>: 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  <nathan@acm.org>
 
        PR c++/83406
index 6845ca4..25b19fa 100644 (file)
@@ -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:
index 890723f..d04624a 100644 (file)
@@ -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.  */
index 1a7ef9e..d408370 100644 (file)
@@ -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);
index daa6239..6e11cf3 100644 (file)
@@ -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)
index 57467bd..b04ed9a 100644 (file)
@@ -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)
                       "%<GCC 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 "
+                      "%<GCC unroll%> 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 <cxx_binding *, 16> 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;
       }
 
index 71f45de..a8144e8 100644 (file)
@@ -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:
index c672632..95b20c5 100644 (file)
@@ -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)
     {
index 677897b..2a553ad 100644 (file)
@@ -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
index a601cfa..d0cbc37 100644 (file)
@@ -1,3 +1,22 @@
+2017-12-22  Bernhard Reutner-Fischer  <aldot@gcc.gnu.org>
+            Eric Botcazou  <ebotcazou@adacore.com>
+
+       * 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  <jakub@redhat.com>
 
        * scanner.c (preprocessor_line): Replace Yoda conditions with typical
index 46642bb..81476b2 100644 (file)
@@ -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;
 }
index 53a87b6..d2c794f 100644 (file)
@@ -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 ("%<GCC unroll%> 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;
+}
index c5e62d7..7b837c9 100644 (file)
@@ -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 *);
index aabf268..fc95ec0 100644 (file)
@@ -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
index d63b11c..f7de5d5 100644 (file)
@@ -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)
index d6df349..a5f6291 100644 (file)
@@ -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);
index 08bff3f..91be6da 100644 (file)
@@ -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 ("%<GCC unroll%> directive does not commence a loop at %C");
+
       st = next_statement ();
     }
 }
index df29b78..e6569e0 100644 (file)
@@ -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));
index 3969bd2..91d1102 100644 (file)
@@ -1,3 +1,20 @@
+2017-12-22  Mike Stump  <mikestump@comcast.net>
+            Eric Botcazou  <ebotcazou@adacore.com>
+
+       * 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 <aoliva@redhat.com>
 
        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 (file)
index 0000000..ccae250
--- /dev/null
@@ -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 (file)
index 0000000..635b6c2
--- /dev/null
@@ -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 (file)
index 0000000..6cefa75
--- /dev/null
@@ -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 (file)
index 0000000..1c19881
--- /dev/null
@@ -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 (file)
index 0000000..754f3b1
--- /dev/null
@@ -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 (file)
index 0000000..2d58a6a
--- /dev/null
@@ -0,0 +1,19 @@
+// { dg-do compile }
+// { dg-options "-O2 -fdump-tree-cunrolli-details" }
+
+template <typename T>
+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 <int> (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 (file)
index 0000000..e68cc31
--- /dev/null
@@ -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 (file)
index 0000000..6516ee9
--- /dev/null
@@ -0,0 +1,20 @@
+// { dg-do compile }
+// { dg-options "-O2 -fdump-tree-cunrolli-details" }
+// { dg-skip-if "range for" { *-*-* } { "-std=gnu++98" } { "" } }
+
+template <typename T>
+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 <int> (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 (file)
index 0000000..00fe7dc
--- /dev/null
@@ -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 (file)
index 0000000..bc93f91
--- /dev/null
@@ -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 (file)
index 0000000..4e3ec09
--- /dev/null
@@ -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 (file)
index 0000000..fbb5f24
--- /dev/null
@@ -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 (file)
index 0000000..b88b4b2
--- /dev/null
@@ -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