c++: #pragma GCC unroll C++ fixes [PR112795]
Checks
Commit Message
Hi!
foo in the unroll-5.C testcase ICEs because cp_parser_pragma_unroll
during parsing calls maybe_constant_value unconditionally, which is
fine if !processing_template_decl, but can ICE otherwise.
While just calling fold_non_dependent_expr there instead could be enough
to fix the ICE (and I guess the right thing to do for backports if any),
I don't see a reason why we couldn't handle a dependent #pragma GCC unroll
argument as well, the unrolling isn't done in the FE and all the middle-end
cares about is that ANNOTATE_EXPR has a 1..65534 last operand when it is
annot_expr_unroll_kind.
So, the following patch changes all the unsigned short unroll arguments
to tree unroll (and thus avoids the tree -> unsigned short -> tree
conversions), does the type and value checking during parsing only if
the argument isn't dependent and repeats it during instantiation.
Bootstrapped/regtested on x86_64-linux and i686-linux, ok for trunk?
2023-12-02 Jakub Jelinek <jakub@redhat.com>
PR c++/112795
gcc/cp/
* cp-tree.h (cp_convert_range_for): Change UNROLL type from
unsigned short to tree.
(finish_while_stmt_cond, finish_do_stmt, finish_for_cond): Likewise.
* parser.cc (cp_parser_statement): Pass NULL_TREE rather than 0 to
cp_parser_iteration_statement UNROLL argument.
(cp_parser_for, cp_parser_c_for): Change UNROLL type from
unsigned short to tree.
(cp_parser_range_for): Likewise. Set RANGE_FOR_UNROLL to just UNROLL
rather than build_int_cst from it.
(cp_convert_range_for, cp_parser_iteration_statement): Change UNROLL
type from unsigned short to tree.
(cp_parser_omp_loop_nest): Pass NULL_TREE rather than 0 to
cp_parser_range_for UNROLL argument.
(cp_parser_pragma_unroll): Return tree rather than unsigned short.
If parsed expression is type dependent, just return it, don't diagnose
issues with value if it is value dependent.
(cp_parser_pragma): Change UNROLL type from unsigned short to tree.
* semantics.cc (finish_while_stmt_cond): Change UNROLL type from
unsigned short to tree. Build ANNOTATE_EXPR with UNROLL as its last
operand rather than build_int_cst from it.
(finish_do_stmt, finish_for_cond): Likewise.
* pt.cc (tsubst_stmt) <case RANGE_FOR_STMT>: Change UNROLL type from
unsigned short to tree and set it to RECUR on RANGE_FOR_UNROLL (t).
(tsubst_expr) <case ANNOTATE_EXPR>: For annot_expr_unroll_kind repeat
checks on UNROLL value from cp_parser_pragma_unroll.
gcc/testsuite/
* g++.dg/ext/unroll-5.C: New test.
* g++.dg/ext/unroll-6.C: New test.
Jakub
Comments
On 12/2/23 05:51, Jakub Jelinek wrote:
> Hi!
>
> foo in the unroll-5.C testcase ICEs because cp_parser_pragma_unroll
> during parsing calls maybe_constant_value unconditionally, which is
> fine if !processing_template_decl, but can ICE otherwise.
>
> While just calling fold_non_dependent_expr there instead could be enough
> to fix the ICE (and I guess the right thing to do for backports if any),
> I don't see a reason why we couldn't handle a dependent #pragma GCC unroll
> argument as well, the unrolling isn't done in the FE and all the middle-end
> cares about is that ANNOTATE_EXPR has a 1..65534 last operand when it is
> annot_expr_unroll_kind.
>
> So, the following patch changes all the unsigned short unroll arguments
> to tree unroll (and thus avoids the tree -> unsigned short -> tree
> conversions), does the type and value checking during parsing only if
> the argument isn't dependent and repeats it during instantiation.
>
> Bootstrapped/regtested on x86_64-linux and i686-linux, ok for trunk?
OK.
> 2023-12-02 Jakub Jelinek <jakub@redhat.com>
>
> PR c++/112795
> gcc/cp/
> * cp-tree.h (cp_convert_range_for): Change UNROLL type from
> unsigned short to tree.
> (finish_while_stmt_cond, finish_do_stmt, finish_for_cond): Likewise.
> * parser.cc (cp_parser_statement): Pass NULL_TREE rather than 0 to
> cp_parser_iteration_statement UNROLL argument.
> (cp_parser_for, cp_parser_c_for): Change UNROLL type from
> unsigned short to tree.
> (cp_parser_range_for): Likewise. Set RANGE_FOR_UNROLL to just UNROLL
> rather than build_int_cst from it.
> (cp_convert_range_for, cp_parser_iteration_statement): Change UNROLL
> type from unsigned short to tree.
> (cp_parser_omp_loop_nest): Pass NULL_TREE rather than 0 to
> cp_parser_range_for UNROLL argument.
> (cp_parser_pragma_unroll): Return tree rather than unsigned short.
> If parsed expression is type dependent, just return it, don't diagnose
> issues with value if it is value dependent.
> (cp_parser_pragma): Change UNROLL type from unsigned short to tree.
> * semantics.cc (finish_while_stmt_cond): Change UNROLL type from
> unsigned short to tree. Build ANNOTATE_EXPR with UNROLL as its last
> operand rather than build_int_cst from it.
> (finish_do_stmt, finish_for_cond): Likewise.
> * pt.cc (tsubst_stmt) <case RANGE_FOR_STMT>: Change UNROLL type from
> unsigned short to tree and set it to RECUR on RANGE_FOR_UNROLL (t).
> (tsubst_expr) <case ANNOTATE_EXPR>: For annot_expr_unroll_kind repeat
> checks on UNROLL value from cp_parser_pragma_unroll.
> gcc/testsuite/
> * g++.dg/ext/unroll-5.C: New test.
> * g++.dg/ext/unroll-6.C: New test.
>
> --- gcc/cp/cp-tree.h.jj 2023-12-01 08:10:42.707324577 +0100
> +++ gcc/cp/cp-tree.h 2023-12-01 16:08:20.152165244 +0100
> @@ -7371,7 +7371,7 @@ extern bool maybe_clone_body (tree);
>
> /* In parser.cc */
> extern tree cp_convert_range_for (tree, tree, tree, cp_decomp *, bool,
> - unsigned short, bool);
> + tree, bool);
> extern void cp_convert_omp_range_for (tree &, tree &, tree &,
> tree &, tree &, tree &, tree &, tree &);
> extern void cp_finish_omp_range_for (tree, tree);
> @@ -7692,19 +7692,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, unsigned short,
> - bool);
> +extern void finish_while_stmt_cond (tree, tree, bool, tree, bool);
> 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, unsigned short,
> - bool);
> +extern void finish_do_stmt (tree, tree, bool, tree, bool);
> 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, unsigned short,
> - bool);
> +extern void finish_for_cond (tree, tree, bool, tree, bool);
> extern void finish_for_expr (tree, tree);
> extern void finish_for_stmt (tree);
> extern tree begin_range_for_stmt (tree, tree);
> --- gcc/cp/parser.cc.jj 2023-12-01 08:10:42.800323262 +0100
> +++ gcc/cp/parser.cc 2023-12-02 08:52:45.254387503 +0100
> @@ -2391,15 +2391,15 @@ static tree cp_parser_selection_statemen
> static tree cp_parser_condition
> (cp_parser *);
> static tree cp_parser_iteration_statement
> - (cp_parser *, bool *, bool, unsigned short, bool);
> + (cp_parser *, bool *, bool, tree, bool);
> static bool cp_parser_init_statement
> (cp_parser *, tree *decl);
> static tree cp_parser_for
> - (cp_parser *, bool, unsigned short, bool);
> + (cp_parser *, bool, tree, bool);
> static tree cp_parser_c_for
> - (cp_parser *, tree, tree, bool, unsigned short, bool);
> + (cp_parser *, tree, tree, bool, tree, bool);
> static tree cp_parser_range_for
> - (cp_parser *, tree, tree, tree, bool, unsigned short, bool, bool);
> + (cp_parser *, tree, tree, tree, bool, tree, bool, bool);
> static void do_range_for_auto_deduction
> (tree, tree, cp_decomp *);
> static tree cp_parser_perform_range_for_lookup
> @@ -12521,8 +12521,8 @@ cp_parser_statement (cp_parser* parser,
> case RID_DO:
> case RID_FOR:
> std_attrs = process_stmt_hotness_attribute (std_attrs, attrs_loc);
> - statement = cp_parser_iteration_statement (parser, if_p, false, 0,
> - false);
> + statement = cp_parser_iteration_statement (parser, if_p, false,
> + NULL_TREE, false);
> break;
>
> case RID_BREAK:
> @@ -13806,8 +13806,7 @@ cp_parser_condition (cp_parser* parser)
> not included. */
>
> static tree
> -cp_parser_for (cp_parser *parser, bool ivdep, unsigned short unroll,
> - bool novector)
> +cp_parser_for (cp_parser *parser, bool ivdep, tree unroll, bool novector)
> {
> tree init, scope, decl;
> bool is_range_for;
> @@ -13844,7 +13843,7 @@ cp_parser_for (cp_parser *parser, bool i
>
> static tree
> cp_parser_c_for (cp_parser *parser, tree scope, tree init, bool ivdep,
> - unsigned short unroll, bool novector)
> + tree unroll, bool novector)
> {
> /* Normal for loop */
> tree condition = NULL_TREE;
> @@ -13895,8 +13894,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, unsigned short unroll, bool novector,
> - bool is_omp)
> + bool ivdep, tree unroll, bool novector, bool is_omp)
> {
> tree stmt, range_expr;
> auto_vec <cxx_binding *, 16> bindings;
> @@ -13969,7 +13967,7 @@ cp_parser_range_for (cp_parser *parser,
> if (ivdep)
> RANGE_FOR_IVDEP (stmt) = 1;
> if (unroll)
> - RANGE_FOR_UNROLL (stmt) = build_int_cst (integer_type_node, unroll);
> + RANGE_FOR_UNROLL (stmt) = unroll;
> if (novector)
> RANGE_FOR_NOVECTOR (stmt) = 1;
> finish_range_for_decl (stmt, range_decl, range_expr);
> @@ -14158,7 +14156,7 @@ warn_for_range_copy (tree decl, tree exp
>
> tree
> cp_convert_range_for (tree statement, tree range_decl, tree range_expr,
> - cp_decomp *decomp, bool ivdep, unsigned short unroll,
> + cp_decomp *decomp, bool ivdep, tree unroll,
> bool novector)
> {
> tree begin, end;
> @@ -14383,7 +14381,7 @@ cp_parser_range_for_member_function (tre
>
> static tree
> cp_parser_iteration_statement (cp_parser* parser, bool *if_p, bool ivdep,
> - unsigned short unroll, bool novector)
> + tree unroll, bool novector)
> {
> cp_token *token;
> enum rid keyword;
> @@ -44303,7 +44301,7 @@ cp_parser_omp_loop_nest (cp_parser *pars
> cp_parser_require (parser, CPP_COLON, RT_COLON);
>
> init = cp_parser_range_for (parser, NULL_TREE, NULL_TREE, decl,
> - false, 0, false, true);
> + false, NULL_TREE, false, true);
>
> cp_convert_omp_range_for (this_pre_body, sl, decl,
> orig_decl, init, orig_init,
> @@ -50240,29 +50238,31 @@ cp_parser_pragma_ivdep (cp_parser *parse
>
> /* Parse a pragma GCC unroll. */
>
> -static unsigned short
> +static tree
> 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);
> + tree unroll = cp_parser_constant_expression (parser);
> + unroll = fold_non_dependent_expr (unroll);
> 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)
> + if (type_dependent_expression_p (unroll))
> + ;
> + else if (!INTEGRAL_TYPE_P (TREE_TYPE (unroll))
> + || (!value_dependent_expression_p (unroll)
> + && (!tree_fits_shwi_p (unroll)
> + || (lunroll = tree_to_shwi (unroll)) < 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;
> + unroll = NULL_TREE;
> }
> - else
> + else if (TREE_CODE (unroll) == INTEGER_CST)
> {
> - unroll = (unsigned short)lunroll;
> - if (unroll == 0)
> - unroll = 1;
> + unroll = fold_convert (integer_type_node, unroll);
> + if (integer_zerop (unroll))
> + unroll = integer_one_node;
> }
> cp_parser_skip_to_pragma_eol (parser, pragma_tok);
> return unroll;
> @@ -50597,7 +50597,7 @@ cp_parser_pragma (cp_parser *parser, enu
> case PRAGMA_NOVECTOR:
> {
> bool ivdep;
> - unsigned short unroll = 0;
> + tree unroll = NULL_TREE;
> bool novector = false;
> const char *pragma_str;
>
> --- gcc/cp/semantics.cc.jj 2023-12-01 08:10:42.826322894 +0100
> +++ gcc/cp/semantics.cc 2023-12-01 15:55:58.998529436 +0100
> @@ -1166,7 +1166,7 @@ begin_while_stmt (void)
>
> void
> finish_while_stmt_cond (tree cond, tree while_stmt, bool ivdep,
> - unsigned short unroll, bool novector)
> + tree unroll, bool novector)
> {
> cond = maybe_convert_cond (cond);
> finish_cond (&WHILE_COND (while_stmt), cond);
> @@ -1184,8 +1184,7 @@ finish_while_stmt_cond (tree cond, tree
> WHILE_COND (while_stmt),
> build_int_cst (integer_type_node,
> annot_expr_unroll_kind),
> - build_int_cst (integer_type_node,
> - unroll));
> + unroll);
> if (novector && cond != error_mark_node)
> WHILE_COND (while_stmt) = build3 (ANNOTATE_EXPR,
> TREE_TYPE (WHILE_COND (while_stmt)),
> @@ -1237,7 +1236,7 @@ finish_do_body (tree do_stmt)
> COND is as indicated. */
>
> void
> -finish_do_stmt (tree cond, tree do_stmt, bool ivdep, unsigned short unroll,
> +finish_do_stmt (tree cond, tree do_stmt, bool ivdep, tree unroll,
> bool novector)
> {
> cond = maybe_convert_cond (cond);
> @@ -1254,7 +1253,7 @@ finish_do_stmt (tree cond, tree do_stmt,
> 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));
> + unroll);
> if (novector && cond != error_mark_node)
> cond = build3 (ANNOTATE_EXPR, TREE_TYPE (cond), cond,
> build_int_cst (integer_type_node, annot_expr_no_vector_kind),
> @@ -1357,7 +1356,8 @@ finish_init_stmt (tree for_stmt)
> FOR_STMT. */
>
> void
> -finish_for_cond (tree cond, tree for_stmt, bool ivdep, unsigned short unroll, bool novector)
> +finish_for_cond (tree cond, tree for_stmt, bool ivdep, tree unroll,
> + bool novector)
> {
> cond = maybe_convert_cond (cond);
> finish_cond (&FOR_COND (for_stmt), cond);
> @@ -1375,8 +1375,7 @@ finish_for_cond (tree cond, tree for_stm
> FOR_COND (for_stmt),
> build_int_cst (integer_type_node,
> annot_expr_unroll_kind),
> - build_int_cst (integer_type_node,
> - unroll));
> + unroll);
> if (novector && cond != error_mark_node)
> FOR_COND (for_stmt) = build3 (ANNOTATE_EXPR,
> TREE_TYPE (FOR_COND (for_stmt)),
> --- gcc/cp/pt.cc.jj 2023-12-01 08:10:42.819322994 +0100
> +++ gcc/cp/pt.cc 2023-12-01 16:43:26.589676880 +0100
> @@ -18418,8 +18418,7 @@ tsubst_stmt (tree t, tree args, tsubst_f
> }
> else
> {
> - unsigned short unroll = (RANGE_FOR_UNROLL (t)
> - ? tree_to_uhwi (RANGE_FOR_UNROLL (t)) : 0);
> + tree unroll = RECUR (RANGE_FOR_UNROLL (t));
> stmt = cp_convert_range_for (stmt, decl, expr, decomp,
> RANGE_FOR_IVDEP (t), unroll,
> RANGE_FOR_NOVECTOR (t));
> @@ -21501,11 +21500,39 @@ tsubst_expr (tree t, tree args, tsubst_f
> }
>
> case ANNOTATE_EXPR:
> - op1 = RECUR (TREE_OPERAND (t, 0));
> - RETURN (build3_loc (EXPR_LOCATION (t), ANNOTATE_EXPR,
> - TREE_TYPE (op1), op1,
> - RECUR (TREE_OPERAND (t, 1)),
> - RECUR (TREE_OPERAND (t, 2))));
> + {
> + op1 = RECUR (TREE_OPERAND (t, 0));
> + tree op2 = RECUR (TREE_OPERAND (t, 1));
> + tree op3 = RECUR (TREE_OPERAND (t, 2));
> + if (TREE_CODE (op2) == INTEGER_CST
> + && wi::to_widest (op2) == (int) annot_expr_unroll_kind)
> + {
> + HOST_WIDE_INT lunroll;
> + if (type_dependent_expression_p (op3))
> + ;
> + else if (!INTEGRAL_TYPE_P (TREE_TYPE (op3))
> + || (!value_dependent_expression_p (op3)
> + && (!tree_fits_shwi_p (op3)
> + || (lunroll = tree_to_shwi (op3)) < 0
> + || lunroll >= USHRT_MAX)))
> + {
> + error_at (EXPR_LOCATION (TREE_OPERAND (t, 2)),
> + "%<#pragma GCC unroll%> requires an "
> + "assignment-expression that evaluates to a "
> + "non-negative integral constant less than %u",
> + USHRT_MAX);
> + op3 = integer_one_node;
> + }
> + else if (TREE_CODE (op3) == INTEGER_CST)
> + {
> + op3 = fold_convert (integer_type_node, op3);
> + if (integer_zerop (op3))
> + op3 = integer_one_node;
> + }
> + }
> + RETURN (build3_loc (EXPR_LOCATION (t), ANNOTATE_EXPR,
> + TREE_TYPE (op1), op1, op2, op3));
> + }
>
> default:
> /* Handle Objective-C++ constructs, if appropriate. */
> --- gcc/testsuite/g++.dg/ext/unroll-5.C.jj 2023-12-01 16:26:10.507186363 +0100
> +++ gcc/testsuite/g++.dg/ext/unroll-5.C 2023-12-01 16:32:52.341560326 +0100
> @@ -0,0 +1,36 @@
> +// PR c++/112795
> +// { dg-do compile { target c++11 } }
> +// { dg-options "-O2 -fdump-tree-cunrolli-details" }
> +
> +void baz (int);
> +constexpr int n = 3;
> +
> +template <int N>
> +void
> +foo ()
> +{
> +#pragma GCC unroll(n)
> + for (int i = 0; i != n; ++i)
> + baz (i);
> +}
> +
> +template <int N>
> +void
> +bar ()
> +{
> +#pragma GCC unroll(N)
> + for (int i = 0; i != N; ++i)
> + baz (i);
> +}
> +
> +void
> +qux ()
> +{
> + foo <2> ();
> + bar <6> ();
> + bar <10> ();
> +}
> +
> +// { dg-final { scan-tree-dump "loop with 3 iterations completely unrolled" "cunrolli" } }
> +// { dg-final { scan-tree-dump "loop with 6 iterations completely unrolled" "cunrolli" } }
> +// { dg-final { scan-tree-dump "loop with 10 iterations completely unrolled" "cunrolli" } }
> --- gcc/testsuite/g++.dg/ext/unroll-6.C.jj 2023-12-01 16:28:13.738461284 +0100
> +++ gcc/testsuite/g++.dg/ext/unroll-6.C 2023-12-01 16:45:40.599799896 +0100
> @@ -0,0 +1,85 @@
> +// PR c++/112795
> +// { dg-do compile { target c++11 } }
> +
> +void
> +foo ()
> +{
> + #pragma GCC unroll 1.0f // { dg-error "'#pragma GCC unroll' requires an assignment-expression that evaluates to a non-negative integral constant less than" }
> + for (int i = 0; i < 2; ++i)
> + ;
> + #pragma GCC unroll 0xffffffffffffffffULL // { dg-error "'#pragma GCC unroll' requires an assignment-expression that evaluates to a non-negative integral constant less than" }
> + for (int i = 0; i < 2; ++i)
> + ;
> + #pragma GCC unroll -42 // { dg-error "'#pragma GCC unroll' requires an assignment-expression that evaluates to a non-negative integral constant less than" }
> + for (int i = 0; i < 2; ++i)
> + ;
> +}
> +
> +template <int N>
> +void
> +bar ()
> +{
> + #pragma GCC unroll 1.0f // { dg-error "'#pragma GCC unroll' requires an assignment-expression that evaluates to a non-negative integral constant less than" }
> + for (int i = 0; i < 2; ++i)
> + ;
> + #pragma GCC unroll 0xffffffffffffffffULL // { dg-error "'#pragma GCC unroll' requires an assignment-expression that evaluates to a non-negative integral constant less than" }
> + for (int i = 0; i < 2; ++i)
> + ;
> + #pragma GCC unroll -42 // { dg-error "'#pragma GCC unroll' requires an assignment-expression that evaluates to a non-negative integral constant less than" }
> + for (int i = 0; i < 2; ++i)
> + ;
> +}
> +
> +template <typename T, int N>
> +void
> +baz ()
> +{
> + #pragma GCC unroll (N + 1.0f) // { dg-error "'#pragma GCC unroll' requires an assignment-expression that evaluates to a non-negative integral constant less than" }
> + for (int i = 0; i < 2; ++i)
> + ;
> + #pragma GCC unroll (N + 0xffffffffffffffffULL)
> + for (int i = 0; i < 2; ++i)
> + ;
> + #pragma GCC unroll (N - 42)
> + for (int i = 0; i < 2; ++i)
> + ;
> + #pragma GCC unroll ((T) 1.0f)
> + for (int i = 0; i < 2; ++i)
> + ;
> + #pragma GCC unroll ((T) 0xffffffffffffffffULL)
> + for (int i = 0; i < 2; ++i)
> + ;
> + #pragma GCC unroll ((T) -42)
> + for (int i = 0; i < 2; ++i)
> + ;
> +}
> +
> +template <typename T, int N>
> +void
> +qux ()
> +{
> + #pragma GCC unroll (N + 1.0f) // { dg-error "'#pragma GCC unroll' requires an assignment-expression that evaluates to a non-negative integral constant less than" }
> + for (int i = 0; i < 2; ++i)
> + ;
> + #pragma GCC unroll (N + 0xffffffffffffffffULL)// { dg-error "'#pragma GCC unroll' requires an assignment-expression that evaluates to a non-negative integral constant less than" }
> + for (int i = 0; i < 2; ++i)
> + ;
> + #pragma GCC unroll (N - 42) // { dg-error "'#pragma GCC unroll' requires an assignment-expression that evaluates to a non-negative integral constant less than" }
> + for (int i = 0; i < 2; ++i)
> + ;
> + #pragma GCC unroll ((T) 1.0f) // { dg-error "'#pragma GCC unroll' requires an assignment-expression that evaluates to a non-negative integral constant less than" }
> + for (int i = 0; i < 2; ++i)
> + ;
> + #pragma GCC unroll ((T) 0xffffffffffffffffULL)// { dg-error "'#pragma GCC unroll' requires an assignment-expression that evaluates to a non-negative integral constant less than" }
> + for (int i = 0; i < 2; ++i)
> + ;
> + #pragma GCC unroll ((T) -42) // { dg-error "'#pragma GCC unroll' requires an assignment-expression that evaluates to a non-negative integral constant less than" }
> + for (int i = 0; i < 2; ++i)
> + ;
> +}
> +
> +void
> +corge ()
> +{
> + qux <float, 0> ();
> +}
>
> Jakub
>
@@ -7371,7 +7371,7 @@ extern bool maybe_clone_body (tree);
/* In parser.cc */
extern tree cp_convert_range_for (tree, tree, tree, cp_decomp *, bool,
- unsigned short, bool);
+ tree, bool);
extern void cp_convert_omp_range_for (tree &, tree &, tree &,
tree &, tree &, tree &, tree &, tree &);
extern void cp_finish_omp_range_for (tree, tree);
@@ -7692,19 +7692,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, unsigned short,
- bool);
+extern void finish_while_stmt_cond (tree, tree, bool, tree, bool);
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, unsigned short,
- bool);
+extern void finish_do_stmt (tree, tree, bool, tree, bool);
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, unsigned short,
- bool);
+extern void finish_for_cond (tree, tree, bool, tree, bool);
extern void finish_for_expr (tree, tree);
extern void finish_for_stmt (tree);
extern tree begin_range_for_stmt (tree, tree);
@@ -2391,15 +2391,15 @@ static tree cp_parser_selection_statemen
static tree cp_parser_condition
(cp_parser *);
static tree cp_parser_iteration_statement
- (cp_parser *, bool *, bool, unsigned short, bool);
+ (cp_parser *, bool *, bool, tree, bool);
static bool cp_parser_init_statement
(cp_parser *, tree *decl);
static tree cp_parser_for
- (cp_parser *, bool, unsigned short, bool);
+ (cp_parser *, bool, tree, bool);
static tree cp_parser_c_for
- (cp_parser *, tree, tree, bool, unsigned short, bool);
+ (cp_parser *, tree, tree, bool, tree, bool);
static tree cp_parser_range_for
- (cp_parser *, tree, tree, tree, bool, unsigned short, bool, bool);
+ (cp_parser *, tree, tree, tree, bool, tree, bool, bool);
static void do_range_for_auto_deduction
(tree, tree, cp_decomp *);
static tree cp_parser_perform_range_for_lookup
@@ -12521,8 +12521,8 @@ cp_parser_statement (cp_parser* parser,
case RID_DO:
case RID_FOR:
std_attrs = process_stmt_hotness_attribute (std_attrs, attrs_loc);
- statement = cp_parser_iteration_statement (parser, if_p, false, 0,
- false);
+ statement = cp_parser_iteration_statement (parser, if_p, false,
+ NULL_TREE, false);
break;
case RID_BREAK:
@@ -13806,8 +13806,7 @@ cp_parser_condition (cp_parser* parser)
not included. */
static tree
-cp_parser_for (cp_parser *parser, bool ivdep, unsigned short unroll,
- bool novector)
+cp_parser_for (cp_parser *parser, bool ivdep, tree unroll, bool novector)
{
tree init, scope, decl;
bool is_range_for;
@@ -13844,7 +13843,7 @@ cp_parser_for (cp_parser *parser, bool i
static tree
cp_parser_c_for (cp_parser *parser, tree scope, tree init, bool ivdep,
- unsigned short unroll, bool novector)
+ tree unroll, bool novector)
{
/* Normal for loop */
tree condition = NULL_TREE;
@@ -13895,8 +13894,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, unsigned short unroll, bool novector,
- bool is_omp)
+ bool ivdep, tree unroll, bool novector, bool is_omp)
{
tree stmt, range_expr;
auto_vec <cxx_binding *, 16> bindings;
@@ -13969,7 +13967,7 @@ cp_parser_range_for (cp_parser *parser,
if (ivdep)
RANGE_FOR_IVDEP (stmt) = 1;
if (unroll)
- RANGE_FOR_UNROLL (stmt) = build_int_cst (integer_type_node, unroll);
+ RANGE_FOR_UNROLL (stmt) = unroll;
if (novector)
RANGE_FOR_NOVECTOR (stmt) = 1;
finish_range_for_decl (stmt, range_decl, range_expr);
@@ -14158,7 +14156,7 @@ warn_for_range_copy (tree decl, tree exp
tree
cp_convert_range_for (tree statement, tree range_decl, tree range_expr,
- cp_decomp *decomp, bool ivdep, unsigned short unroll,
+ cp_decomp *decomp, bool ivdep, tree unroll,
bool novector)
{
tree begin, end;
@@ -14383,7 +14381,7 @@ cp_parser_range_for_member_function (tre
static tree
cp_parser_iteration_statement (cp_parser* parser, bool *if_p, bool ivdep,
- unsigned short unroll, bool novector)
+ tree unroll, bool novector)
{
cp_token *token;
enum rid keyword;
@@ -44303,7 +44301,7 @@ cp_parser_omp_loop_nest (cp_parser *pars
cp_parser_require (parser, CPP_COLON, RT_COLON);
init = cp_parser_range_for (parser, NULL_TREE, NULL_TREE, decl,
- false, 0, false, true);
+ false, NULL_TREE, false, true);
cp_convert_omp_range_for (this_pre_body, sl, decl,
orig_decl, init, orig_init,
@@ -50240,29 +50238,31 @@ cp_parser_pragma_ivdep (cp_parser *parse
/* Parse a pragma GCC unroll. */
-static unsigned short
+static tree
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);
+ tree unroll = cp_parser_constant_expression (parser);
+ unroll = fold_non_dependent_expr (unroll);
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)
+ if (type_dependent_expression_p (unroll))
+ ;
+ else if (!INTEGRAL_TYPE_P (TREE_TYPE (unroll))
+ || (!value_dependent_expression_p (unroll)
+ && (!tree_fits_shwi_p (unroll)
+ || (lunroll = tree_to_shwi (unroll)) < 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;
+ unroll = NULL_TREE;
}
- else
+ else if (TREE_CODE (unroll) == INTEGER_CST)
{
- unroll = (unsigned short)lunroll;
- if (unroll == 0)
- unroll = 1;
+ unroll = fold_convert (integer_type_node, unroll);
+ if (integer_zerop (unroll))
+ unroll = integer_one_node;
}
cp_parser_skip_to_pragma_eol (parser, pragma_tok);
return unroll;
@@ -50597,7 +50597,7 @@ cp_parser_pragma (cp_parser *parser, enu
case PRAGMA_NOVECTOR:
{
bool ivdep;
- unsigned short unroll = 0;
+ tree unroll = NULL_TREE;
bool novector = false;
const char *pragma_str;
@@ -1166,7 +1166,7 @@ begin_while_stmt (void)
void
finish_while_stmt_cond (tree cond, tree while_stmt, bool ivdep,
- unsigned short unroll, bool novector)
+ tree unroll, bool novector)
{
cond = maybe_convert_cond (cond);
finish_cond (&WHILE_COND (while_stmt), cond);
@@ -1184,8 +1184,7 @@ finish_while_stmt_cond (tree cond, tree
WHILE_COND (while_stmt),
build_int_cst (integer_type_node,
annot_expr_unroll_kind),
- build_int_cst (integer_type_node,
- unroll));
+ unroll);
if (novector && cond != error_mark_node)
WHILE_COND (while_stmt) = build3 (ANNOTATE_EXPR,
TREE_TYPE (WHILE_COND (while_stmt)),
@@ -1237,7 +1236,7 @@ finish_do_body (tree do_stmt)
COND is as indicated. */
void
-finish_do_stmt (tree cond, tree do_stmt, bool ivdep, unsigned short unroll,
+finish_do_stmt (tree cond, tree do_stmt, bool ivdep, tree unroll,
bool novector)
{
cond = maybe_convert_cond (cond);
@@ -1254,7 +1253,7 @@ finish_do_stmt (tree cond, tree do_stmt,
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));
+ unroll);
if (novector && cond != error_mark_node)
cond = build3 (ANNOTATE_EXPR, TREE_TYPE (cond), cond,
build_int_cst (integer_type_node, annot_expr_no_vector_kind),
@@ -1357,7 +1356,8 @@ finish_init_stmt (tree for_stmt)
FOR_STMT. */
void
-finish_for_cond (tree cond, tree for_stmt, bool ivdep, unsigned short unroll, bool novector)
+finish_for_cond (tree cond, tree for_stmt, bool ivdep, tree unroll,
+ bool novector)
{
cond = maybe_convert_cond (cond);
finish_cond (&FOR_COND (for_stmt), cond);
@@ -1375,8 +1375,7 @@ finish_for_cond (tree cond, tree for_stm
FOR_COND (for_stmt),
build_int_cst (integer_type_node,
annot_expr_unroll_kind),
- build_int_cst (integer_type_node,
- unroll));
+ unroll);
if (novector && cond != error_mark_node)
FOR_COND (for_stmt) = build3 (ANNOTATE_EXPR,
TREE_TYPE (FOR_COND (for_stmt)),
@@ -18418,8 +18418,7 @@ tsubst_stmt (tree t, tree args, tsubst_f
}
else
{
- unsigned short unroll = (RANGE_FOR_UNROLL (t)
- ? tree_to_uhwi (RANGE_FOR_UNROLL (t)) : 0);
+ tree unroll = RECUR (RANGE_FOR_UNROLL (t));
stmt = cp_convert_range_for (stmt, decl, expr, decomp,
RANGE_FOR_IVDEP (t), unroll,
RANGE_FOR_NOVECTOR (t));
@@ -21501,11 +21500,39 @@ tsubst_expr (tree t, tree args, tsubst_f
}
case ANNOTATE_EXPR:
- op1 = RECUR (TREE_OPERAND (t, 0));
- RETURN (build3_loc (EXPR_LOCATION (t), ANNOTATE_EXPR,
- TREE_TYPE (op1), op1,
- RECUR (TREE_OPERAND (t, 1)),
- RECUR (TREE_OPERAND (t, 2))));
+ {
+ op1 = RECUR (TREE_OPERAND (t, 0));
+ tree op2 = RECUR (TREE_OPERAND (t, 1));
+ tree op3 = RECUR (TREE_OPERAND (t, 2));
+ if (TREE_CODE (op2) == INTEGER_CST
+ && wi::to_widest (op2) == (int) annot_expr_unroll_kind)
+ {
+ HOST_WIDE_INT lunroll;
+ if (type_dependent_expression_p (op3))
+ ;
+ else if (!INTEGRAL_TYPE_P (TREE_TYPE (op3))
+ || (!value_dependent_expression_p (op3)
+ && (!tree_fits_shwi_p (op3)
+ || (lunroll = tree_to_shwi (op3)) < 0
+ || lunroll >= USHRT_MAX)))
+ {
+ error_at (EXPR_LOCATION (TREE_OPERAND (t, 2)),
+ "%<#pragma GCC unroll%> requires an "
+ "assignment-expression that evaluates to a "
+ "non-negative integral constant less than %u",
+ USHRT_MAX);
+ op3 = integer_one_node;
+ }
+ else if (TREE_CODE (op3) == INTEGER_CST)
+ {
+ op3 = fold_convert (integer_type_node, op3);
+ if (integer_zerop (op3))
+ op3 = integer_one_node;
+ }
+ }
+ RETURN (build3_loc (EXPR_LOCATION (t), ANNOTATE_EXPR,
+ TREE_TYPE (op1), op1, op2, op3));
+ }
default:
/* Handle Objective-C++ constructs, if appropriate. */
@@ -0,0 +1,36 @@
+// PR c++/112795
+// { dg-do compile { target c++11 } }
+// { dg-options "-O2 -fdump-tree-cunrolli-details" }
+
+void baz (int);
+constexpr int n = 3;
+
+template <int N>
+void
+foo ()
+{
+#pragma GCC unroll(n)
+ for (int i = 0; i != n; ++i)
+ baz (i);
+}
+
+template <int N>
+void
+bar ()
+{
+#pragma GCC unroll(N)
+ for (int i = 0; i != N; ++i)
+ baz (i);
+}
+
+void
+qux ()
+{
+ foo <2> ();
+ bar <6> ();
+ bar <10> ();
+}
+
+// { dg-final { scan-tree-dump "loop with 3 iterations completely unrolled" "cunrolli" } }
+// { dg-final { scan-tree-dump "loop with 6 iterations completely unrolled" "cunrolli" } }
+// { dg-final { scan-tree-dump "loop with 10 iterations completely unrolled" "cunrolli" } }
@@ -0,0 +1,85 @@
+// PR c++/112795
+// { dg-do compile { target c++11 } }
+
+void
+foo ()
+{
+ #pragma GCC unroll 1.0f // { dg-error "'#pragma GCC unroll' requires an assignment-expression that evaluates to a non-negative integral constant less than" }
+ for (int i = 0; i < 2; ++i)
+ ;
+ #pragma GCC unroll 0xffffffffffffffffULL // { dg-error "'#pragma GCC unroll' requires an assignment-expression that evaluates to a non-negative integral constant less than" }
+ for (int i = 0; i < 2; ++i)
+ ;
+ #pragma GCC unroll -42 // { dg-error "'#pragma GCC unroll' requires an assignment-expression that evaluates to a non-negative integral constant less than" }
+ for (int i = 0; i < 2; ++i)
+ ;
+}
+
+template <int N>
+void
+bar ()
+{
+ #pragma GCC unroll 1.0f // { dg-error "'#pragma GCC unroll' requires an assignment-expression that evaluates to a non-negative integral constant less than" }
+ for (int i = 0; i < 2; ++i)
+ ;
+ #pragma GCC unroll 0xffffffffffffffffULL // { dg-error "'#pragma GCC unroll' requires an assignment-expression that evaluates to a non-negative integral constant less than" }
+ for (int i = 0; i < 2; ++i)
+ ;
+ #pragma GCC unroll -42 // { dg-error "'#pragma GCC unroll' requires an assignment-expression that evaluates to a non-negative integral constant less than" }
+ for (int i = 0; i < 2; ++i)
+ ;
+}
+
+template <typename T, int N>
+void
+baz ()
+{
+ #pragma GCC unroll (N + 1.0f) // { dg-error "'#pragma GCC unroll' requires an assignment-expression that evaluates to a non-negative integral constant less than" }
+ for (int i = 0; i < 2; ++i)
+ ;
+ #pragma GCC unroll (N + 0xffffffffffffffffULL)
+ for (int i = 0; i < 2; ++i)
+ ;
+ #pragma GCC unroll (N - 42)
+ for (int i = 0; i < 2; ++i)
+ ;
+ #pragma GCC unroll ((T) 1.0f)
+ for (int i = 0; i < 2; ++i)
+ ;
+ #pragma GCC unroll ((T) 0xffffffffffffffffULL)
+ for (int i = 0; i < 2; ++i)
+ ;
+ #pragma GCC unroll ((T) -42)
+ for (int i = 0; i < 2; ++i)
+ ;
+}
+
+template <typename T, int N>
+void
+qux ()
+{
+ #pragma GCC unroll (N + 1.0f) // { dg-error "'#pragma GCC unroll' requires an assignment-expression that evaluates to a non-negative integral constant less than" }
+ for (int i = 0; i < 2; ++i)
+ ;
+ #pragma GCC unroll (N + 0xffffffffffffffffULL)// { dg-error "'#pragma GCC unroll' requires an assignment-expression that evaluates to a non-negative integral constant less than" }
+ for (int i = 0; i < 2; ++i)
+ ;
+ #pragma GCC unroll (N - 42) // { dg-error "'#pragma GCC unroll' requires an assignment-expression that evaluates to a non-negative integral constant less than" }
+ for (int i = 0; i < 2; ++i)
+ ;
+ #pragma GCC unroll ((T) 1.0f) // { dg-error "'#pragma GCC unroll' requires an assignment-expression that evaluates to a non-negative integral constant less than" }
+ for (int i = 0; i < 2; ++i)
+ ;
+ #pragma GCC unroll ((T) 0xffffffffffffffffULL)// { dg-error "'#pragma GCC unroll' requires an assignment-expression that evaluates to a non-negative integral constant less than" }
+ for (int i = 0; i < 2; ++i)
+ ;
+ #pragma GCC unroll ((T) -42) // { dg-error "'#pragma GCC unroll' requires an assignment-expression that evaluates to a non-negative integral constant less than" }
+ for (int i = 0; i < 2; ++i)
+ ;
+}
+
+void
+corge ()
+{
+ qux <float, 0> ();
+}