From patchwork Sat Nov 25 10:15:54 2017 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Eric Botcazou X-Patchwork-Id: 841219 Return-Path: X-Original-To: incoming@patchwork.ozlabs.org Delivered-To: patchwork-incoming@bilbo.ozlabs.org Authentication-Results: ozlabs.org; spf=pass (mailfrom) smtp.mailfrom=gcc.gnu.org (client-ip=209.132.180.131; helo=sourceware.org; envelope-from=gcc-patches-return-467893-incoming=patchwork.ozlabs.org@gcc.gnu.org; receiver=) Authentication-Results: ozlabs.org; dkim=pass (1024-bit key; unprotected) header.d=gcc.gnu.org header.i=@gcc.gnu.org header.b="jT5pd1Tq"; dkim-atps=neutral Received: from sourceware.org (server1.sourceware.org [209.132.180.131]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by ozlabs.org (Postfix) with ESMTPS id 3ykTVc3bXMz9s83 for ; Sat, 25 Nov 2017 21:16:21 +1100 (AEDT) DomainKey-Signature: a=rsa-sha1; c=nofws; d=gcc.gnu.org; h=list-id :list-unsubscribe:list-archive:list-post:list-help:sender:from :to:cc:subject:date:message-id:mime-version:content-type :content-transfer-encoding; q=dns; s=default; b=qfcUsy9h0N9TQt35 HyE1xG4Y2R593MEsf2ymM7I5yjuDOw3w2EOR2PGbEUVbptq/KOPI3LPPYmIDBVVf ALODMlrg2A3ZuupZjzIYtOorYuPyuH0oTO/ixdylQU2rKAPIch2hmMnwwgot3rXm 8PxsLI88TtS1quNsN3OrVTO21kQ= DKIM-Signature: v=1; a=rsa-sha1; c=relaxed; d=gcc.gnu.org; h=list-id :list-unsubscribe:list-archive:list-post:list-help:sender:from :to:cc:subject:date:message-id:mime-version:content-type :content-transfer-encoding; s=default; bh=FzRdo9BZoFR8X7eDiSQear Wt2Ag=; b=jT5pd1TqfdJ0hQtICklQscv+US6HERoRVAMNQDeN3qVGu0UTla1Ppt FnlIGKVNv0JLdzO3nCER4YZQtR4LAvcgixTT+9Sn852VviD8sPHzX7o2O55UZYHS RTGavA5p+WIk3dJVO5Lo0sQ/kspDbXQie5TaCI8OpFSJ+V85N0Pyc= Received: (qmail 14063 invoked by alias); 25 Nov 2017 10:16:12 -0000 Mailing-List: contact gcc-patches-help@gcc.gnu.org; run by ezmlm Precedence: bulk List-Id: List-Unsubscribe: List-Archive: List-Post: List-Help: Sender: gcc-patches-owner@gcc.gnu.org Delivered-To: mailing list gcc-patches@gcc.gnu.org Received: (qmail 14025 invoked by uid 89); 25 Nov 2017 10:16:07 -0000 Authentication-Results: sourceware.org; auth=none X-Virus-Found: No X-Spam-SWARE-Status: No, score=-12.8 required=5.0 tests=AWL, BAYES_00, GIT_PATCH_1, GIT_PATCH_2, GIT_PATCH_3, KAM_ASCII_DIVIDERS, KAM_NUMSUBJECT, KB_WAM_FROM_NAME_SINGLEWORD, RCVD_IN_BRBL_LASTEXT, RCVD_IN_DNSWL_NONE, SPF_PASS autolearn=ham version=3.3.2 spammy=255147, build3, 59237 X-HELO: smtp.eu.adacore.com Received: from mel.act-europe.fr (HELO smtp.eu.adacore.com) (194.98.77.210) by sourceware.org (qpsmtpd/0.93/v0.84-503-g423c35a) with ESMTP; Sat, 25 Nov 2017 10:16:01 +0000 Received: from localhost (localhost [127.0.0.1]) by filtered-smtp.eu.adacore.com (Postfix) with ESMTP id 113BF822A9; Sat, 25 Nov 2017 11:15:57 +0100 (CET) Received: from smtp.eu.adacore.com ([127.0.0.1]) by localhost (smtp.eu.adacore.com [127.0.0.1]) (amavisd-new, port 10024) with ESMTP id xl4JAZY3WrMf; Sat, 25 Nov 2017 11:15:56 +0100 (CET) Received: from polaris.localnet (bon31-6-88-161-99-133.fbx.proxad.net [88.161.99.133]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by smtp.eu.adacore.com (Postfix) with ESMTPSA id DD28B81F5E; Sat, 25 Nov 2017 11:15:55 +0100 (CET) From: Eric Botcazou To: gcc-patches@gcc.gnu.org Cc: Joseph Myers , Jason Merrill Subject: [C/C++] Add support for #pragma GCC unroll v3 Date: Sat, 25 Nov 2017 11:15:54 +0100 Message-ID: <1908294.BNiOnM3a76@polaris> User-Agent: KMail/4.14.10 (Linux/3.16.7-53-desktop; KDE/4.14.9; x86_64; ; ) MIME-Version: 1.0 Hi, this is the (hopefully) final implementation of the support for the unrolling pragma in the C and C++ front-ends. It contains a couple of fixes for the C++ front-ends to make it correctly handle unroll and ivdep for the same loop. Tested on x86_64-suse-linux, OK for the mainline? 2017-11-25 Mike Stump Eric Botcazou ChangeLog/ * doc/extend.texi (Loop-Specific Pragmas): Document pragma GCC unroll. c-family/ChangeLog: * c-pragma.c (init_pragma): Register pragma GCC unroll. * c-pragma.h (enum pragma_kind): Add PRAGMA_UNROLL. c/ChangeLog: * c-parser.c (c_parser_while_statement): Add unroll parameter and build ANNOTATE_EXPR if present. Add 3rd operand to ANNOTATE_EXPR. (c_parser_do_statement): Likewise. (c_parser_for_statement): Likewise. (c_parser_statement_after_labels): Adjust calls to above. (c_parse_pragma_ivdep): New static function. (c_parser_pragma_unroll): Likewise. (c_parser_pragma) : Add support for pragma Unroll. : New case. cp/ChangeLog: * constexpr.c (cxx_eval_constant_expression) : Remove assertion on 2nd operand. (potential_constant_expression_1): Likewise. * cp-array-notation.c (create_an_loop): Adjut call to finish_for_cond. * cp-tree.h (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 and pass it in call to cp_convert_range_for. (cp_convert_range_for): Add unroll parameter and pass it in call to finish_for_cond. (cp_parser_iteration_statement): Add unroll parameter and pass it in calls to finish_while_stmt_cond, finish_do_stmt and cp_parser_for. (cp_parser_pragma_ivdep): New static function. (cp_parser_pragma_unroll): Likewise. (cp_parser_pragma) : Add support for pragma Unroll. : New case. * pt.c (tsubst_expr): Adjut calls to finish_for_cond, cp_convert_range_for, finish_while_stmt_cond and finish_do_stmt. : Propagate 3rd operand. * semantics.c (finish_while_stmt_cond): Add unroll parameter and build ANNOTATE_EXPR if present. Add 3rd operand to ANNOTATE_EXPR. (finish_do_stmt): Likewise. (finish_for_cond): Likewise. testsuite/ChangeLog: * 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. Index: doc/extend.texi =================================================================== --- doc/extend.texi (revision 255147) +++ doc/extend.texi (working copy) @@ -22343,9 +22343,7 @@ function. The parenthesis around the op 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 @@ -22356,9 +22354,7 @@ if @code{attribute((optimize("STRING"))) 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 @@ -22369,15 +22365,14 @@ options. It is intended for include fil 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 @@ -22386,7 +22381,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 @@ -22421,6 +22415,16 @@ void ignore_vec_dep (int *a, int k, int @} @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: c/c-parser.c =================================================================== --- c/c-parser.c (revision 255147) +++ c/c-parser.c (working copy) @@ -1410,9 +1410,9 @@ static tree c_parser_c99_block_statement location_t * = NULL); static void c_parser_if_statement (c_parser *, bool *, vec *); static void c_parser_switch_statement (c_parser *, bool *); -static void c_parser_while_statement (c_parser *, bool, bool *); -static void c_parser_do_statement (c_parser *, bool); -static void c_parser_for_statement (c_parser *, bool, bool *); +static void c_parser_while_statement (c_parser *, bool, unsigned short, bool *); +static void c_parser_do_statement (c_parser *, bool, unsigned short); +static void c_parser_for_statement (c_parser *, bool, unsigned short, bool *); static tree c_parser_asm_statement (c_parser *); static tree c_parser_asm_operands (c_parser *); static tree c_parser_asm_goto_operands (c_parser *); @@ -5499,13 +5499,13 @@ c_parser_statement_after_labels (c_parse 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_CILK_FOR: if (!flag_cilkplus) @@ -6039,7 +6039,8 @@ c_parser_switch_statement (c_parser *par 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; @@ -6059,6 +6060,11 @@ c_parser_while_statement (c_parser *pars 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; @@ -6093,7 +6099,7 @@ c_parser_while_statement (c_parser *pars */ 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; @@ -6125,6 +6131,11 @@ c_parser_do_statement (c_parser *parser, 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); @@ -6191,7 +6202,8 @@ c_parser_do_statement (c_parser *parser, 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. */ @@ -6312,6 +6324,12 @@ c_parser_for_statement (c_parser *parser "% pragma"); cond = error_mark_node; } + else if (unroll) + { + c_parser_error (parser, "missing loop condition in loop with " + "% pragma"); + cond = error_mark_node; + } else { c_parser_consume_token (parser); @@ -6333,6 +6351,11 @@ c_parser_for_statement (c_parser *parser 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 @@ -11042,6 +11065,49 @@ c_parser_objc_at_dynamic_declaration (c_ } +/* 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 or equal to %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 @@ -11184,21 +11250,51 @@ c_parser_pragma (c_parser *parser, enum 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: c-family/c-pragma.c =================================================================== --- c-family/c-pragma.c (revision 255147) +++ c-family/c-pragma.c (working copy) @@ -1544,6 +1544,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); + if (flag_cilkplus) cpp_register_deferred_pragma (parse_in, "cilk", "grainsize", PRAGMA_CILK_GRAINSIZE, true, false); Index: c-family/c-pragma.h =================================================================== --- c-family/c-pragma.h (revision 255147) +++ c-family/c-pragma.h (working copy) @@ -75,6 +75,7 @@ enum pragma_kind { PRAGMA_GCC_PCH_PREPROCESS, PRAGMA_IVDEP, + PRAGMA_UNROLL, PRAGMA_FIRST_EXTERNAL }; Index: cp/constexpr.c =================================================================== --- cp/constexpr.c (revision 255147) +++ cp/constexpr.c (working copy) @@ -4674,7 +4674,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, @@ -5923,7 +5922,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-array-notation.c =================================================================== --- cp/cp-array-notation.c (revision 255147) +++ cp/cp-array-notation.c (working copy) @@ -67,7 +67,7 @@ create_an_loop (tree init, tree cond, tr finish_expr_stmt (init); for_stmt = begin_for_stmt (NULL_TREE, NULL_TREE); finish_init_stmt (for_stmt); - finish_for_cond (cond, for_stmt, false); + finish_for_cond (cond, for_stmt, false, 0); finish_for_expr (incr, for_stmt); finish_expr_stmt (body); finish_for_stmt (for_stmt); Index: cp/cp-tree.h =================================================================== --- cp/cp-tree.h (revision 255147) +++ cp/cp-tree.h (working copy) @@ -6409,7 +6409,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); @@ -6694,16 +6695,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 255147) +++ cp/init.c (working copy) @@ -4319,7 +4319,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 255147) +++ cp/parser.c (working copy) @@ -2121,15 +2121,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 @@ -10878,7 +10878,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_CILK_FOR: @@ -11745,7 +11745,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; @@ -11757,13 +11757,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; @@ -11784,7 +11785,13 @@ cp_parser_c_for (cp_parser *parser, tree "% pragma"); condition = error_mark_node; } - finish_for_cond (condition, stmt, ivdep); + else if (unroll) + { + cp_parser_error (parser, "missing loop condition in loop with " + "% pragma"); + condition = error_mark_node; + } + finish_for_cond (condition, stmt, ivdep, unroll); /* Look for the `;'. */ cp_parser_require (parser, CPP_SEMICOLON, RT_SEMICOLON); @@ -11808,7 +11815,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 bindings; @@ -11877,6 +11884,8 @@ cp_parser_range_for (cp_parser *parser, stmt = begin_range_for_stmt (scope, init); if (ivdep) RANGE_FOR_IVDEP (stmt) = 1; + if (unroll) + /* TODO */(void)0; 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. */ @@ -11887,7 +11896,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; } @@ -11981,7 +11991,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; @@ -12042,7 +12052,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, @@ -12217,7 +12227,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; @@ -12251,7 +12262,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. */ @@ -12282,7 +12293,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 `;'. */ @@ -12296,7 +12307,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); @@ -38749,6 +38760,45 @@ cp_parser_cilk_grainsize (cp_parser *par cp_parser_skip_to_pragma_eol (parser, pragma_tok); } +/* 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 or equal to %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. */ @@ -38990,17 +39040,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 255147) +++ cp/pt.c (working copy) @@ -16119,7 +16119,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); RECUR (FOR_BODY (t)); @@ -16141,11 +16141,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), 0); } else stmt = cp_convert_range_for (stmt, decl, expr, NULL_TREE, 0, - RANGE_FOR_IVDEP (t)); + RANGE_FOR_IVDEP (t), 0); RECUR (RANGE_FOR_BODY (t)); finish_for_stmt (stmt); } @@ -16154,7 +16154,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); RECUR (WHILE_BODY (t)); finish_while_stmt (stmt); break; @@ -16164,7 +16164,7 @@ tsubst_expr (tree t, tree args, tsubst_f RECUR (DO_BODY (t)); 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 255147) +++ cp/semantics.c (working copy) @@ -802,7 +802,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) { if (check_no_cilk (cond, "Cilk array notation cannot be used as a condition for while statement", @@ -818,6 +819,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)); } @@ -862,7 +871,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) { if (check_no_cilk (cond, "Cilk array notation cannot be used as a condition for a do-while statement", @@ -874,6 +883,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; } @@ -982,7 +995,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) { if (check_no_cilk (cond, "Cilk array notation cannot be used in a condition for a for-loop", @@ -998,6 +1011,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)); } Index: testsuite/c-c++-common/unroll-1.c =================================================================== --- testsuite/c-c++-common/unroll-1.c (revision 0) +++ testsuite/c-c++-common/unroll-1.c (working copy) @@ -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" } } */ +} Index: testsuite/c-c++-common/unroll-2.c =================================================================== --- testsuite/c-c++-common/unroll-2.c (revision 0) +++ testsuite/c-c++-common/unroll-2.c (working copy) @@ -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" } } */ +} Index: testsuite/c-c++-common/unroll-3.c =================================================================== --- testsuite/c-c++-common/unroll-3.c (revision 0) +++ testsuite/c-c++-common/unroll-3.c (working copy) @@ -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" } } */ +} Index: testsuite/c-c++-common/unroll-4.c =================================================================== --- testsuite/c-c++-common/unroll-4.c (revision 0) +++ testsuite/c-c++-common/unroll-4.c (working copy) @@ -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" } } */ +} Index: testsuite/c-c++-common/unroll-5.c =================================================================== --- testsuite/c-c++-common/unroll-5.c (revision 0) +++ testsuite/c-c++-common/unroll-5.c (working copy) @@ -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 or equal to" } */ + 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 or equal to" } */ + 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 or equal to" } */ + /* { 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 or equal to" } */ + for (unsigned long i = 1; i <= 8; ++i) + bar(i); +}