Support preprocessing directive "#pragma unroll N" as an alias for "#pragma GCC unroll N".
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] only with slightly different limitations: * The upper limit of GCC is (USHRT_MAX - 1). This is decided in the implementation prior to these changes. * Clang skips all limitation regarding size or trip counts. [2] The only limitation left is asserting (AllowRemainder || trip_count % N == 0) [2] * The upper limit of NVCC is (INT_MAX). [3] * The upper limit of IBM XL C/C++ is not documented publicly. [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 gcc/ChangeLog: PR c++/91840 * doc/extend.texi: document #pragma unroll (n) gcc/c-family/ChangeLog: PR c++/91840 * c-pragma.cc (init_pragma): Register the unqualified "<pragma unroll N>". * c-pragma.h (pragma_kind): Add "PRAGMA_UNROLL_UNQUALIFIED". gcc/c/ChangeLog: PR c++/91840 * c-parser.cc (c_parser_pragma): Handle new case PRAGMA_UNROLL_UNQUALIFIED for #pragma unroll. #pragma unroll (n) is now an alias for #pragma GCC unroll (n). gcc/cp/ChangeLog: PR c++/91840 * parser.cc (cp_parser_pragma): Handle new case PRAGMA_UNROLL_UNQUALIFIED for #pragma unroll. #pragma unroll (n) is now an alias for #pragma GCC unroll (n). gcc/testsuite/ChangeLog: PR c++/91840 * c-c++-common/unroll-1-unqualified-constant.c: New test like unroll-1.c (basic unrolling) but for #pragma unroll N * c-c++-common/unroll-4-unqualified-constant.c: New test like unroll-4.c (stacked pragmas and no unrolling) but for #pragma unroll N * c-c++-common/unroll-5-unqualified-constant.c: New test like unroll-5.c (rolling factor syntax) but for #pragma unroll N * g++.dg/parse/pr126323-unroll-unqualified-constant.C: New test like pr126323.C (stacked pragmas outside function) but for #pragma unroll N Signed-off-by: Yueh-Shun Li <[email protected]> --- gcc/c-family/c-pragma.cc | 4 ++ gcc/c-family/c-pragma.h | 1 + gcc/c/c-parser.cc | 9 +++- gcc/cp/parser.cc | 3 ++ gcc/doc/extend.texi | 6 +++ .../unroll-1-unqualified-constant.c | 46 +++++++++++++++++++ .../unroll-4-unqualified-constant.c | 31 +++++++++++++ .../unroll-5-unqualified-constant.c | 35 ++++++++++++++ .../pr126323-unroll-unqualified-constant.C | 12 +++++ 9 files changed, 146 insertions(+), 1 deletion(-) create mode 100644 gcc/testsuite/c-c++-common/unroll-1-unqualified-constant.c create mode 100644 gcc/testsuite/c-c++-common/unroll-4-unqualified-constant.c create mode 100644 gcc/testsuite/c-c++-common/unroll-5-unqualified-constant.c create mode 100644 gcc/testsuite/g++.dg/parse/pr126323-unroll-unqualified-constant.C diff --git a/gcc/c-family/c-pragma.cc b/gcc/c-family/c-pragma.cc index d0d4ff69fd7..bcdc81fd0a4 100644 --- a/gcc/c-family/c-pragma.cc +++ b/gcc/c-family/c-pragma.cc @@ -1862,6 +1862,10 @@ init_pragma (void) cpp_register_deferred_pragma (parse_in, "GCC", "unroll", PRAGMA_UNROLL, false, false); + if (!flag_preprocess_only) + cpp_register_deferred_pragma (parse_in, NULL, "unroll", + PRAGMA_UNROLL_UNQUALIFIED, false, false); + if (!flag_preprocess_only) cpp_register_deferred_pragma (parse_in, "GCC", "novector", PRAGMA_NOVECTOR, false, false); diff --git a/gcc/c-family/c-pragma.h b/gcc/c-family/c-pragma.h index 4d1afe93959..b158ee86347 100644 --- a/gcc/c-family/c-pragma.h +++ b/gcc/c-family/c-pragma.h @@ -93,6 +93,7 @@ enum pragma_kind { PRAGMA_GCC_PCH_PREPROCESS, PRAGMA_IVDEP, PRAGMA_UNROLL, + PRAGMA_UNROLL_UNQUALIFIED, PRAGMA_NOVECTOR, PRAGMA_FIRST_EXTERNAL diff --git a/gcc/c/c-parser.cc b/gcc/c/c-parser.cc index 927aff6d5b8..28f098e2c33 100644 --- a/gcc/c/c-parser.cc +++ b/gcc/c/c-parser.cc @@ -16027,7 +16027,7 @@ c_parse_pragma_novector (c_parser *parser) return true; } -/* Parse a pragma GCC unroll. */ +/* Parse a pragma GCC unroll or a pragma unroll. */ static unsigned short c_parser_pragma_unroll (c_parser *parser) @@ -16291,6 +16291,7 @@ c_parser_pragma (c_parser *parser, enum pragma_context context, bool *if_p, case PRAGMA_NOVECTOR: case PRAGMA_UNROLL: + case PRAGMA_UNROLL_UNQUALIFIED: case PRAGMA_IVDEP: { bool novector = false; @@ -16305,6 +16306,9 @@ c_parser_pragma (c_parser *parser, enum pragma_context context, bool *if_p, case PRAGMA_UNROLL: unroll = c_parser_pragma_unroll (parser); break; + case PRAGMA_UNROLL_UNQUALIFIED: + unroll = c_parser_pragma_unroll (parser); + break; case PRAGMA_IVDEP: ivdep = c_parse_pragma_ivdep (parser); break; @@ -16324,6 +16328,9 @@ c_parser_pragma (c_parser *parser, enum pragma_context context, bool *if_p, case PRAGMA_UNROLL: unroll = c_parser_pragma_unroll (parser); break; + case PRAGMA_UNROLL_UNQUALIFIED: + unroll = c_parser_pragma_unroll (parser); + break; case PRAGMA_NOVECTOR: novector = c_parse_pragma_novector (parser); break; diff --git a/gcc/cp/parser.cc b/gcc/cp/parser.cc index 373d3358c25..bcdc422fd1c 100644 --- a/gcc/cp/parser.cc +++ b/gcc/cp/parser.cc @@ -57771,6 +57771,7 @@ cp_parser_pragma (cp_parser *parser, enum pragma_context context, bool *if_p) case PRAGMA_IVDEP: case PRAGMA_UNROLL: + case PRAGMA_UNROLL_UNQUALIFIED: case PRAGMA_NOVECTOR: { bool ivdep = false; @@ -57783,6 +57784,7 @@ cp_parser_pragma (cp_parser *parser, enum pragma_context context, bool *if_p) case PRAGMA_IVDEP: pragma_str = "ivdep"; break; + case PRAGMA_UNROLL_UNQUALIFIED: case PRAGMA_UNROLL: pragma_str = "unroll"; break; @@ -57814,6 +57816,7 @@ cp_parser_pragma (cp_parser *parser, enum pragma_context context, bool *if_p) ivdep = cp_parser_pragma_ivdep (parser, tok); break; } + case PRAGMA_UNROLL_UNQUALIFIED: case PRAGMA_UNROLL: { if (tok != pragma_tok) diff --git a/gcc/doc/extend.texi b/gcc/doc/extend.texi index 88213d25a89..a380bb70ac2 100644 --- a/gcc/doc/extend.texi +++ b/gcc/doc/extend.texi @@ -10889,6 +10889,12 @@ loop is unrolled or not will be determined by target costing. The resulting vectorized loop may still be unrolled more in later passes depending on the target costing. +@cindex pragma, unroll +@item #pragma unroll @var{n} + +This pragma is an alias for @code{#pragma GCC unroll @var{n}} for +inter-compiler compatibility. + @end table @node Thread-Local diff --git a/gcc/testsuite/c-c++-common/unroll-1-unqualified-constant.c b/gcc/testsuite/c-c++-common/unroll-1-unqualified-constant.c new file mode 100644 index 00000000000..e963e8a6d31 --- /dev/null +++ b/gcc/testsuite/c-c++-common/unroll-1-unqualified-constant.c @@ -0,0 +1,46 @@ +/* + <#pragma unroll N> 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 8 + 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 8 + for (unsigned long i = 1; i <= 7; ++i) + bar(i); + /* { dg-final { scan-tree-dump "21:.*: loop with 7 iterations completely unrolled" "cunrolli" } } */ + + #pragma unroll 8 + for (unsigned long i = 1; i <= 15; ++i) + bar(i); + /* { dg-final { scan-rtl-dump "26:.*: optimized: loop unrolled 7 times" "loop2_unroll" } } */ + + #pragma unroll 8 + for (unsigned long i = 1; i <= j; ++i) + bar(i); + /* { dg-final { scan-rtl-dump "31:.*: optimized: loop unrolled 7 times" "loop2_unroll" } } */ + + #pragma unroll 7 + for (unsigned long i = 1; i <= j; ++i) + bar(i); + /* { dg-final { scan-rtl-dump "36:.*: optimized: loop unrolled 3 times" "loop2_unroll" } } */ + + unsigned long i = 0; + #pragma unroll 3 + do { + bar(i); + } while (++i < 9); + /* { dg-final { scan-rtl-dump "4\[24\]:.*: optimized: loop unrolled 2 times" "loop2_unroll" } } */ +} diff --git a/gcc/testsuite/c-c++-common/unroll-4-unqualified-constant.c b/gcc/testsuite/c-c++-common/unroll-4-unqualified-constant.c new file mode 100644 index 00000000000..58922cdd9ab --- /dev/null +++ b/gcc/testsuite/c-c++-common/unroll-4-unqualified-constant.c @@ -0,0 +1,31 @@ +/* + <#pragma unroll N> no-unrolling tests + taken from unroll-4.c + */ + +/* { 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 unroll 0 + #pragma GCC ivdep + for (unsigned long i = 1; i <= 3; ++i) + bar(i); + + #pragma GCC ivdep + #pragma unroll 0 + for (unsigned long i = 1; i <= j; ++i) + bar(i); + + #pragma unroll 1 + 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" 3 "loop2_unroll" } } */ +} diff --git a/gcc/testsuite/c-c++-common/unroll-5-unqualified-constant.c b/gcc/testsuite/c-c++-common/unroll-5-unqualified-constant.c new file mode 100644 index 00000000000..f7240a50d3a --- /dev/null +++ b/gcc/testsuite/c-c++-common/unroll-5-unqualified-constant.c @@ -0,0 +1,35 @@ +/* + <#pragma unroll N> syntax tests + taken from unroll-5.c + */ + +/* { dg-do compile } */ + +extern void bar (int); + +int j; + +void test (void) +{ + #pragma unroll 4+4 + for (unsigned long i = 1; i <= 8; ++i) + bar(i); + + #pragma unroll -1 /* { dg-error "requires an assignment-expression that evaluates to a non-negative integral constant less than" } */ + for (unsigned long i = 1; i <= 8; ++i) + bar(i); + + #pragma unroll 20000000000 /* { dg-error "requires an assignment-expression that evaluates to a non-negative integral constant less than" } */ + for (unsigned long i = 1; i <= 8; ++i) + bar(i); + + #pragma unroll j /* { dg-error "requires an assignment-expression that evaluates to a non-negative integral constant less than" } */ + /* { dg-error "cannot appear in a constant-expression|is not usable in a constant expression" "" { target c++ } .-1 } */ + for (unsigned long i = 1; i <= 8; ++i) + bar(i); + + #pragma unroll 4.2 /* { dg-error "requires an assignment-expression that evaluates to a non-negative integral constant less than" } */ + for (unsigned long i = 1; i <= 8; ++i) + bar(i); +} + diff --git a/gcc/testsuite/g++.dg/parse/pr126323-unroll-unqualified-constant.C b/gcc/testsuite/g++.dg/parse/pr126323-unroll-unqualified-constant.C new file mode 100644 index 00000000000..d9fc5aff379 --- /dev/null +++ b/gcc/testsuite/g++.dg/parse/pr126323-unroll-unqualified-constant.C @@ -0,0 +1,12 @@ +/* + Derived from pr126323.C but tests `#pragma unroll N` +*/ + +class C { +#pragma GCC novector // { dg-error "must be inside a function" } +#pragma unroll(0) // { 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
