[3/3] c-family: support complete unrolling with #pragma unroll [PR91840]
Checks
| Context |
Check |
Description |
| linaro-tcwg-bot/tcwg_gcc_build--master-arm |
success
|
Build passed
|
| linaro-tcwg-bot/tcwg_simplebootstrap_build--master-arm-bootstrap |
success
|
Build passed
|
| linaro-tcwg-bot/tcwg_gcc_check--master-arm |
success
|
Test passed
|
Commit Message
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 <shamrocklee@posteo.net>
---
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
@@ -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);
@@ -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:
@@ -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
new file mode 100644
@@ -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" } } */
+}
new file mode 100644
@@ -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" } } */
+}
new file mode 100644
@@ -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 }
+ ;
+};