Support preprocessing directive "#pragma unroll" without explicit n_unroll as an alias for "pragma GCC unroll (USHRT_MAX - 1)", completely unrolling loops with trip count <= USHRT_MAX - 1 .
Provide an interface for GCC's unrolling pragma with inter-compiler support. Compilers like Clang, NVCC, and IBM XL supports "#pragma unroll N" with the N being the unroll factor applying to loops with compile-time constant trip counts, [1][3][4] but with some notable behavioural difference: * GCC, if this patch is applied, will only unroll loops completely if (trip_count <= USHRT_MAX - 1), and will otherwise decide on the unroll factor heuristically. * Clang has upper limits guarding [2] * unrolled body size (PragmaUnrollThreshold) * trip counts (PragmaUnrollFullMaxIterations (default 1,000,000)) When exceeded, the loop won't be completely unrolled. * IBM Open XL C/C++ Compiler will decide on the unroll factor instead of unrolling completely when n is not specified. [5] [1]: https://clang.llvm.org/docs/AttributeReference.html#pragma-unroll-pragma-nounroll [2]: https://github.com/llvm/llvm-project/blob/afb54cc942d5fccf63f5594a6808f21eb179db6f/llvm/lib/Transforms/Scalar/LoopUnrollPass.cpp [3]: https://docs.nvidia.com/cuda/cuda-programming-guide/05-appendices/cpp-language-extensions.html#pragma-unroll [4]: https://www.ibm.com/docs/en/ent-metalc-zos/3.1.0?topic=descriptions-pragma-unroll [5]: https://www.ibm.com/docs/en/SSRZSMX_17.1.3/pdf/user.pdf gcc/ChangeLog: PR c++/91840 * doc/extend.texi: document complete-unrolling #pragma unroll gcc/c/ChangeLog: PR c++/91840 * c-parser.cc (c_parser_pragma_unroll): Add bool parameter is_unqualified to differentiate PRAGMA_UNROLL and PRAGMA_UNROLL_UNQUALIFIED. #pragma unroll without the constant is now an alias for #pragma GCC unroll (USHRT_MAX - 1), which completely unrolls any loop with a build-time constant trip count less than USHRT_MAX. gcc/cp/ChangeLog: PR c++/91840 * parser.cc (cp_parser_pragma_unroll): Add bool parameter is_unqualified to differentiate PRAGMA_UNROLL and PRAGMA_UNROLL_UNQUALIFIED. #pragma unroll without the constant is now an alias for #pragma GCC unroll (USHRT_MAX - 1), which completely unrolls any loop with a build-time constant trip count less than USHRT_MAX. gcc/testsuite/ChangeLog: PR c++/91840 * c-c++-common/unroll-1-unqualified-complete.c: New test from the complete unrolling part of unroll-1.c but for #pragma unroll * c-c++-common/unroll-8-unqualified-complete.c: New test complete unrolling trip count limit for #pragma unroll * g++.dg/parse/pr126323-unroll-unqualified-complete.C: New test like pr126323.C (stacked pragmas outside function) but for #pragma unroll Signed-off-by: Yueh-Shun Li <[email protected]> --- gcc/c/c-parser.cc | 16 ++++++---- gcc/cp/parser.cc | 17 +++++++++-- gcc/doc/extend.texi | 12 ++++++++ .../unroll-1-unqualified-complete.c | 24 +++++++++++++++ .../unroll-8-unqualified-complete.c | 29 +++++++++++++++++++ .../pr126323-unroll-unqualified-complete.C | 13 +++++++++ 6 files changed, 104 insertions(+), 7 deletions(-) create mode 100644 gcc/testsuite/c-c++-common/unroll-1-unqualified-complete.c create mode 100644 gcc/testsuite/c-c++-common/unroll-8-unqualified-complete.c create mode 100644 gcc/testsuite/g++.dg/parse/pr126323-unroll-unqualified-complete.C diff --git a/gcc/c/c-parser.cc b/gcc/c/c-parser.cc index 28f098e2c33..360ed8b57d0 100644 --- a/gcc/c/c-parser.cc +++ b/gcc/c/c-parser.cc @@ -16030,10 +16030,16 @@ c_parse_pragma_novector (c_parser *parser) /* Parse a pragma GCC unroll or a pragma unroll. */ static unsigned short -c_parser_pragma_unroll (c_parser *parser) +c_parser_pragma_unroll (c_parser *parser, bool is_unqualified) { unsigned short unroll; c_parser_consume_pragma (parser); + if (is_unqualified && c_parser_peek_token (parser)->type == CPP_PRAGMA_EOL) + { + c_parser_skip_to_pragma_eol (parser); + unroll = USHRT_MAX - 1; + return unroll; + } location_t location = c_parser_peek_token (parser)->location; tree expr = c_parser_expr_no_commas (parser, NULL).value; mark_exp_read (expr); @@ -16304,10 +16310,10 @@ c_parser_pragma (c_parser *parser, enum pragma_context context, bool *if_p, novector = c_parse_pragma_novector (parser); break; case PRAGMA_UNROLL: - unroll = c_parser_pragma_unroll (parser); + unroll = c_parser_pragma_unroll (parser, false); break; case PRAGMA_UNROLL_UNQUALIFIED: - unroll = c_parser_pragma_unroll (parser); + unroll = c_parser_pragma_unroll (parser, true); break; case PRAGMA_IVDEP: ivdep = c_parse_pragma_ivdep (parser); @@ -16326,10 +16332,10 @@ c_parser_pragma (c_parser *parser, enum pragma_context context, bool *if_p, ivdep = c_parse_pragma_ivdep (parser); break; case PRAGMA_UNROLL: - unroll = c_parser_pragma_unroll (parser); + unroll = c_parser_pragma_unroll (parser, false); break; case PRAGMA_UNROLL_UNQUALIFIED: - unroll = c_parser_pragma_unroll (parser); + unroll = c_parser_pragma_unroll (parser, true); break; case PRAGMA_NOVECTOR: novector = c_parse_pragma_novector (parser); diff --git a/gcc/cp/parser.cc b/gcc/cp/parser.cc index bcdc422fd1c..458fe99fd08 100644 --- a/gcc/cp/parser.cc +++ b/gcc/cp/parser.cc @@ -57389,8 +57389,15 @@ cp_parser_pragma_ivdep (cp_parser *parser, cp_token *pragma_tok) /* Parse a pragma GCC unroll. */ static tree -cp_parser_pragma_unroll (cp_parser *parser, cp_token *pragma_tok) +cp_parser_pragma_unroll (cp_parser *parser, cp_token *pragma_tok, + bool is_unqualified) { + if (is_unqualified + && cp_lexer_peek_token (parser->lexer)->type == CPP_PRAGMA_EOL) + { + cp_parser_skip_to_pragma_eol (parser, pragma_tok); + return build_int_cst (integer_type_node, USHRT_MAX - 1); + } location_t location = cp_lexer_peek_token (parser->lexer)->location; tree unroll = cp_parser_constant_expression (parser); unroll = cp_check_pragma_unroll (location, fold_non_dependent_expr (unroll)); @@ -57817,11 +57824,17 @@ cp_parser_pragma (cp_parser *parser, enum pragma_context context, bool *if_p) break; } case PRAGMA_UNROLL_UNQUALIFIED: + { + if (tok != pragma_tok) + tok = cp_lexer_consume_token (parser->lexer); + unroll = cp_parser_pragma_unroll (parser, tok, true); + break; + } case PRAGMA_UNROLL: { if (tok != pragma_tok) tok = cp_lexer_consume_token (parser->lexer); - unroll = cp_parser_pragma_unroll (parser, tok); + unroll = cp_parser_pragma_unroll (parser, tok, false); break; } case PRAGMA_NOVECTOR: diff --git a/gcc/doc/extend.texi b/gcc/doc/extend.texi index a380bb70ac2..b440e6f5cdb 100644 --- a/gcc/doc/extend.texi +++ b/gcc/doc/extend.texi @@ -10895,6 +10895,18 @@ target costing. This pragma is an alias for @code{#pragma GCC unroll @var{n}} for inter-compiler compatibility. +@cindex pragma, unroll +@item #pragma unroll + +This pragma completely unrolls a loop with a compile-time constant trip count +less than @code{USHRT_MAX - 1}. + +Loops with even larger trip counts will be unrolled +with heuristic unrolling factors instead of complete unrolling. + +Note that no code-size limit is applied on the presence of this pragma -- +use with care on loops with large trip counts or loop bodies. + @end table @node Thread-Local diff --git a/gcc/testsuite/c-c++-common/unroll-1-unqualified-complete.c b/gcc/testsuite/c-c++-common/unroll-1-unqualified-complete.c new file mode 100644 index 00000000000..feba40dfd93 --- /dev/null +++ b/gcc/testsuite/c-c++-common/unroll-1-unqualified-complete.c @@ -0,0 +1,24 @@ +/* + <#pragma unroll> basic tests + taken from unroll-1.c + */ + +/* { 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 unroll + for (unsigned long i = 1; i <= 8; ++i) + bar(i); + /* { dg-final { scan-tree-dump "16:.*: loop with 8 iterations completely unrolled" "cunrolli" } } */ + + #pragma unroll + for (unsigned long i = 1; i <= 7; ++i) + bar(i); + /* { dg-final { scan-tree-dump "21:.*: loop with 7 iterations completely unrolled" "cunrolli" } } */ +} diff --git a/gcc/testsuite/c-c++-common/unroll-8-unqualified-complete.c b/gcc/testsuite/c-c++-common/unroll-8-unqualified-complete.c new file mode 100644 index 00000000000..03bdc98bc8e --- /dev/null +++ b/gcc/testsuite/c-c++-common/unroll-8-unqualified-complete.c @@ -0,0 +1,29 @@ +/* + <#pragma unroll> trip count limit test + Ensure that "#pragma unroll" completely unrolls a loop with trip count 0xFFFE +*/ +/* { dg-do compile } */ +/* { dg-options "-O2 -fdump-tree-cunrolli-details -fdump-rtl-loop2_unroll-details" } */ + +/* + It takes ~5min to run on an Intel N200 processor. + Request 4x time in case it runs slow. +*/ +/* { dg-timeout-factor 4 } */ +/* { dg-require-effective-target run_expensive_tests } */ + +extern void bar (int); + +int j; + +void test (void) +{ + /* + USHRT_MAX - 1 >= 0xFFFE + 0xFFFE == 65534 + */ + #pragma unroll + for (unsigned long i = 1; i <= 0xFFFE; ++i) + bar(i); + /* { dg-final { scan-tree-dump "26:.*: loop with 65534 iterations completely unrolled" "cunrolli" } } */ +} diff --git a/gcc/testsuite/g++.dg/parse/pr126323-unroll-unqualified-complete.C b/gcc/testsuite/g++.dg/parse/pr126323-unroll-unqualified-complete.C new file mode 100644 index 00000000000..e0a8ee39013 --- /dev/null +++ b/gcc/testsuite/g++.dg/parse/pr126323-unroll-unqualified-complete.C @@ -0,0 +1,13 @@ +/* + Derived from pr126323.C but tests + `#pragma unroll` (complete-unrolling pragma) +*/ + +class C { +#pragma GCC novector // { dg-error "must be inside a function" } +#pragma unroll // { dg-error "must be inside a function" } +#pragma GCC ivdep // { dg-error "must be inside a function" } + for (int i = 0; i < 2; i++) // { dg-error "expected unqualified-id" } + // { dg-error "does not name a type" "" { target *-*-* } .-1 } + ; +}; -- 2.54.0
