This is the mail archive of the
gcc-patches@gcc.gnu.org
mailing list for the GCC project.
Re: [C++] Add support for #pragma GCC unroll v4
- From: Eric Botcazou <ebotcazou at adacore dot com>
- To: Jason Merrill <jason at redhat dot com>
- Cc: gcc-patches at gcc dot gnu dot org
- Date: Wed, 20 Dec 2017 15:20:57 +0100
- Subject: Re: [C++] Add support for #pragma GCC unroll v4
- Authentication-results: sourceware.org; auth=none
- References: <12311728.lxHGmYTTQG@polaris> <7df39da7-99c8-2eba-0486-e17e602d46e3@redhat.com>
> This needs some C++ tests, particularly with templates and range-for. I
> suspect that using the pragma in a template will ICE.
No, that wasn't the case, but the combination template/range-for was indeed
not working; fixed by adding a 5th parameter to the RANGE_FOR_STMT node.
Tested on x86_64-suse-linux, OK for the mainline?
2017-12-20 Mike Stump <mikestump@comcast.net>
Eric Botcazou <ebotcazou@adacore.com>
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.
testsuite/
* g++.dg/ext/unroll-1.C: New test.
* g++.dg/ext/unroll-2.C: Likewise.
* g++.dg/ext/unroll-3.C: Likewise.
--
Eric Botcazou
Index: cp/constexpr.c
===================================================================
--- cp/constexpr.c (revision 255850)
+++ cp/constexpr.c (working copy)
@@ -4689,7 +4689,6 @@ cxx_eval_constant_expression (const cons
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,
}
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: cp/cp-tree.def
===================================================================
--- cp/cp-tree.def (revision 255850)
+++ cp/cp-tree.def (working copy)
@@ -302,8 +302,8 @@ DEFTREECODE (FOR_STMT, "for_stmt", tcc_s
/* 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: cp/cp-tree.h
===================================================================
--- cp/cp-tree.h (revision 255850)
+++ cp/cp-tree.h (working copy)
@@ -4844,6 +4844,7 @@ more_aggr_init_expr_args_p (const aggr_i
#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
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: cp/init.c
===================================================================
--- cp/init.c (revision 255850)
+++ cp/init.c (working copy)
@@ -4323,7 +4323,7 @@ build_vec_init (tree base, tree maxindex
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: cp/parser.c
===================================================================
--- cp/parser.c (revision 255850)
+++ cp/parser.c (working copy)
@@ -2112,15 +2112,15 @@ static tree cp_parser_selection_statemen
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
@@ -10798,7 +10798,7 @@ cp_parser_statement (cp_parser* parser,
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:
@@ -11635,7 +11635,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;
@@ -11647,13 +11647,14 @@ cp_parser_for (cp_parser *parser, bool i
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;
@@ -11674,7 +11675,13 @@ cp_parser_c_for (cp_parser *parser, tree
"%<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);
@@ -11698,7 +11705,7 @@ cp_parser_c_for (cp_parser *parser, tree
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;
@@ -11767,6 +11774,8 @@ cp_parser_range_for (cp_parser *parser,
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. */
@@ -11777,7 +11786,8 @@ cp_parser_range_for (cp_parser *parser,
{
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;
}
@@ -11871,7 +11881,7 @@ do_range_for_auto_deduction (tree decl,
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;
@@ -11932,7 +11942,7 @@ cp_convert_range_for (tree statement, tr
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,
@@ -12110,7 +12120,8 @@ cp_parser_range_for_member_function (tre
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;
@@ -12144,7 +12155,7 @@ cp_parser_iteration_statement (cp_parser
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. */
@@ -12179,7 +12190,7 @@ cp_parser_iteration_statement (cp_parser
/* 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 `;'. */
@@ -12193,7 +12204,7 @@ cp_parser_iteration_statement (cp_parser
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);
@@ -38433,6 +38444,45 @@ cp_parser_initial_pragma (cp_token *firs
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. */
@@ -38674,17 +38724,60 @@ cp_parser_pragma (cp_parser *parser, enu
"%<#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: cp/pt.c
===================================================================
--- cp/pt.c (revision 255850)
+++ cp/pt.c (working copy)
@@ -16127,7 +16127,7 @@ tsubst_expr (tree t, tree args, tsubst_f
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_f
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_f
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_f
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_f
}
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: cp/semantics.c
===================================================================
--- cp/semantics.c (revision 255850)
+++ cp/semantics.c (working copy)
@@ -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
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,
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_stm
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 i
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: testsuite/g++.dg/ext/unroll-1.C
===================================================================
--- testsuite/g++.dg/ext/unroll-1.C (revision 0)
+++ testsuite/g++.dg/ext/unroll-1.C (working copy)
@@ -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" } }
Index: testsuite/g++.dg/ext/unroll-2.C
===================================================================
--- testsuite/g++.dg/ext/unroll-2.C (revision 0)
+++ testsuite/g++.dg/ext/unroll-2.C (working copy)
@@ -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" } }
Index: testsuite/g++.dg/ext/unroll-3.C
===================================================================
--- testsuite/g++.dg/ext/unroll-3.C (revision 0)
+++ testsuite/g++.dg/ext/unroll-3.C (working copy)
@@ -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" } }