public inbox for gcc-cvs@sourceware.org
help / color / mirror / Atom feed
* [gcc/devel/omp/gcc-13] openmp: Add C/C++ support for "omp unroll" directive
@ 2023-05-19 16:54 Kwok Yeung
0 siblings, 0 replies; only message in thread
From: Kwok Yeung @ 2023-05-19 16:54 UTC (permalink / raw)
To: gcc-cvs
https://gcc.gnu.org/g:c17983cd052ccdf71ca2a2ba7cff60769046b0cf
commit c17983cd052ccdf71ca2a2ba7cff60769046b0cf
Author: Frederik Harwath <frederik@codesourcery.com>
Date: Fri Mar 24 18:14:23 2023 +0100
openmp: Add C/C++ support for "omp unroll" directive
This commit implements the C and the C++ front end changes to support
the "omp unroll" directive. The execution of the loop transformation
relies on the pass that has been added as a part of the earlier
Fortran patch.
gcc/c-family/ChangeLog:
* c-gimplify.cc (c_genericize_control_stmt): Handle OMP_UNROLL.
* c-omp.cc: Add "unroll" to omp_directives[].
* c-pragma.cc: Add "unroll" to omp_pragmas_simd[].
* c-pragma.h (enum pragma_kind): Add PRAGMA_OMP_UNROLL to
pragma_kind and adjust PRAGMA_OMP__LAST_.
(enum pragma_omp_clause): Add PRAGMA_OMP_CLAUSE_FULL and
PRAGMA_OMP_CLAUSE_PARTIAL.
gcc/c/ChangeLog:
* c-parser.cc (c_parser_omp_clause_name): Handle "full" and
"partial" clauses.
(check_no_duplicate_clause): Change return type to bool and
return check result.
(c_parser_omp_clause_unroll_full): New function for parsing
the "unroll clause".
(c_parser_omp_clause_unroll_partial): New function for
parsing the "partial" clause.
(c_parser_omp_all_clauses): Handle PRAGMA_OMP_CLAUSE_FULL
and PRAGMA_OMP_CLAUSE_PARTIAL.
(c_parser_nested_omp_unroll_clauses): New function for parsing
"omp unroll" directives following another directive.
(OMP_UNROLL_CLAUSE_MASK): New definition.
(c_parser_omp_unroll): New function for parsing "omp unroll"
loops that are not associated with another directive.
(c_parser_omp_construct): Handle PRAGMA_OMP_UNROLL.
* c-typeck.cc (c_finish_omp_clauses): Handle
OMP_CLAUSE_UNROLL_FULL, OMP_CLAUSE_UNROLL_PARTIAL,
and OMP_CLAUSE_UNROLL_NONE.
gcc/cp/ChangeLog:
* cp-gimplify.cc (cp_gimplify_expr): Handle OMP_UNROLL.
(cp_fold_r): Likewise.
(cp_genericize_r): Likewise.
* parser.cc (cp_parser_omp_clause_name): Handle "full" clause.
(check_no_duplicate_clause): Change return type to bool and
return check result.
(cp_parser_omp_clause_unroll_full): New function for parsing
the "unroll clause".
(cp_parser_omp_clause_unroll_partial): New function for
parsing the "partial" clause.
(cp_parser_omp_all_clauses): Handle OMP_CLAUSE_UNROLL and
OMP_CLAUSE_FULL.
(cp_parser_nested_omp_unroll_clauses): New function for parsing
"omp unroll" directives following another directive.
(cp_parser_omp_for_loop): Handle "omp unroll" directives
between directive and loop.
(OMP_UNROLL_CLAUSE_MASK): New definition.
(cp_parser_omp_unroll): New function for parsing "omp unroll"
loops that are not associated with another directive.
(cp_parser_omp_construct): Handle PRAGMA_OMP_UNROLL.
(cp_parser_pragma): Handle PRAGMA_OMP_UNROLL.
* pt.cc (tsubst_omp_clauses): Handle
OMP_CLAUSE_UNROLL_PARTIAL, OMP_CLAUSE_UNROLL_FULL, and
OMP_CLAUSE_UNROLL_NONE.
(tsubst_expr): Handle OMP_UNROLL.
* semantics.cc (finish_omp_clauses): Handle
OMP_CLAUSE_UNROLL_FULL, OMP_CLAUSE_UNROLL_PARTIAL,
and OMP_CLAUSE_UNROLL_NONE.
libgomp/ChangeLog:
* testsuite/libgomp.c++/loop-transforms/unroll-1.C: New test.
* testsuite/libgomp.c++/loop-transforms/unroll-2.C: New test.
* testsuite/libgomp.c-c++-common/loop-transforms/unroll-1.c: New test.
gcc/testsuite/ChangeLog:
* c-c++-common/gomp/loop-transforms/unroll-1.c: New test.
* c-c++-common/gomp/loop-transforms/unroll-2.c: New test.
* c-c++-common/gomp/loop-transforms/unroll-3.c: New test.
* c-c++-common/gomp/loop-transforms/unroll-4.c: New test.
* c-c++-common/gomp/loop-transforms/unroll-5.c: New test.
* c-c++-common/gomp/loop-transforms/unroll-6.c: New test.
* g++.dg/gomp/loop-transforms/unroll-1.C: New test.
* g++.dg/gomp/loop-transforms/unroll-2.C: New test.
* g++.dg/gomp/loop-transforms/unroll-3.C: New test.
Diff:
---
gcc/c-family/ChangeLog.omp | 10 ++
gcc/c-family/c-gimplify.cc | 1 +
gcc/c-family/c-omp.cc | 6 +-
gcc/c-family/c-pragma.cc | 1 +
gcc/c-family/c-pragma.h | 5 +-
gcc/c/ChangeLog.omp | 22 +++
gcc/c/c-parser.cc | 161 +++++++++++++++++++-
gcc/c/c-typeck.cc | 8 +
gcc/cp/ChangeLog.omp | 32 ++++
gcc/cp/cp-gimplify.cc | 3 +
gcc/cp/parser.cc | 164 ++++++++++++++++++++-
gcc/cp/pt.cc | 4 +
gcc/cp/semantics.cc | 55 +++++++
gcc/testsuite/ChangeLog.omp | 12 ++
.../c-c++-common/gomp/loop-transforms/unroll-1.c | 133 +++++++++++++++++
.../c-c++-common/gomp/loop-transforms/unroll-2.c | 99 +++++++++++++
.../c-c++-common/gomp/loop-transforms/unroll-3.c | 18 +++
.../c-c++-common/gomp/loop-transforms/unroll-4.c | 19 +++
.../c-c++-common/gomp/loop-transforms/unroll-5.c | 19 +++
.../c-c++-common/gomp/loop-transforms/unroll-6.c | 20 +++
.../c-c++-common/gomp/loop-transforms/unroll-7.c | 144 ++++++++++++++++++
.../gomp/loop-transforms/unroll-simd-1.c | 84 +++++++++++
.../g++.dg/gomp/loop-transforms/unroll-1.C | 42 ++++++
.../g++.dg/gomp/loop-transforms/unroll-2.C | 47 ++++++
.../g++.dg/gomp/loop-transforms/unroll-3.C | 37 +++++
libgomp/ChangeLog.omp | 6 +
.../libgomp.c++/loop-transforms/unroll-1.C | 73 +++++++++
.../libgomp.c++/loop-transforms/unroll-2.C | 34 +++++
.../loop-transforms/unroll-1.c | 76 ++++++++++
29 files changed, 1327 insertions(+), 8 deletions(-)
diff --git a/gcc/c-family/ChangeLog.omp b/gcc/c-family/ChangeLog.omp
index 8690eb06694..ac8f6e61a5d 100644
--- a/gcc/c-family/ChangeLog.omp
+++ b/gcc/c-family/ChangeLog.omp
@@ -1,3 +1,13 @@
+2023-03-24 Frederik Harwath <frederik@codesourcery.com>
+
+ * c-gimplify.cc (c_genericize_control_stmt): Handle OMP_UNROLL.
+ * c-omp.cc: Add "unroll" to omp_directives[].
+ * c-pragma.cc: Add "unroll" to omp_pragmas_simd[].
+ * c-pragma.h (enum pragma_kind): Add PRAGMA_OMP_UNROLL to
+ pragma_kind and adjust PRAGMA_OMP__LAST_.
+ (enum pragma_omp_clause): Add PRAGMA_OMP_CLAUSE_FULL and
+ PRAGMA_OMP_CLAUSE_PARTIAL.
+
2022-04-15 Kwok Cheung Yeung <kcy@codesourcery.com>
gcc/c-family/
diff --git a/gcc/c-family/c-gimplify.cc b/gcc/c-family/c-gimplify.cc
index 67657e0da72..25e6587e3a1 100644
--- a/gcc/c-family/c-gimplify.cc
+++ b/gcc/c-family/c-gimplify.cc
@@ -526,6 +526,7 @@ c_genericize_control_stmt (tree *stmt_p, int *walk_subtrees, void *data,
case OMP_DISTRIBUTE:
case OMP_LOOP:
case OMP_TASKLOOP:
+ case OMP_LOOP_TRANS:
case OACC_LOOP:
genericize_omp_for_stmt (stmt_p, walk_subtrees, data, func, lh);
break;
diff --git a/gcc/c-family/c-omp.cc b/gcc/c-family/c-omp.cc
index 7eb3b3f1721..f0d13f62822 100644
--- a/gcc/c-family/c-omp.cc
+++ b/gcc/c-family/c-omp.cc
@@ -4010,9 +4010,9 @@ const struct c_omp_directive c_omp_directives[] = {
{ "teams", nullptr, nullptr, PRAGMA_OMP_TEAMS,
C_OMP_DIR_CONSTRUCT, true },
{ "threadprivate", nullptr, nullptr, PRAGMA_OMP_THREADPRIVATE,
- C_OMP_DIR_DECLARATIVE, false }
- /* { "unroll", nullptr, nullptr, PRAGMA_OMP_UNROLL,
- C_OMP_DIR_CONSTRUCT, false }, */
+ C_OMP_DIR_DECLARATIVE, false },
+ { "unroll", nullptr, nullptr, PRAGMA_OMP_UNROLL,
+ C_OMP_DIR_CONSTRUCT, false },
};
/* Find (non-combined/composite) OpenMP directive (if any) which starts
diff --git a/gcc/c-family/c-pragma.cc b/gcc/c-family/c-pragma.cc
index af4965b31cd..1ab8e51885d 100644
--- a/gcc/c-family/c-pragma.cc
+++ b/gcc/c-family/c-pragma.cc
@@ -1594,6 +1594,7 @@ static const struct omp_pragma_def omp_pragmas_simd[] = {
{ "target", PRAGMA_OMP_TARGET },
{ "taskloop", PRAGMA_OMP_TASKLOOP },
{ "teams", PRAGMA_OMP_TEAMS },
+ { "unroll", PRAGMA_OMP_UNROLL },
};
void
diff --git a/gcc/c-family/c-pragma.h b/gcc/c-family/c-pragma.h
index 4ea043d009f..4a7df86bc7a 100644
--- a/gcc/c-family/c-pragma.h
+++ b/gcc/c-family/c-pragma.h
@@ -82,8 +82,9 @@ enum pragma_kind {
PRAGMA_OMP_TASKYIELD,
PRAGMA_OMP_THREADPRIVATE,
PRAGMA_OMP_TEAMS,
+ PRAGMA_OMP_UNROLL,
/* PRAGMA_OMP__LAST_ should be equal to the last PRAGMA_OMP_* code. */
- PRAGMA_OMP__LAST_ = PRAGMA_OMP_TEAMS,
+ PRAGMA_OMP__LAST_ = PRAGMA_OMP_UNROLL,
PRAGMA_GCC_PCH_PREPROCESS,
PRAGMA_IVDEP,
@@ -119,6 +120,7 @@ enum pragma_omp_clause {
PRAGMA_OMP_CLAUSE_FIRSTPRIVATE,
PRAGMA_OMP_CLAUSE_FOR,
PRAGMA_OMP_CLAUSE_FROM,
+ PRAGMA_OMP_CLAUSE_FULL,
PRAGMA_OMP_CLAUSE_GRAINSIZE,
PRAGMA_OMP_CLAUSE_HAS_DEVICE_ADDR,
PRAGMA_OMP_CLAUSE_HINT,
@@ -141,6 +143,7 @@ enum pragma_omp_clause {
PRAGMA_OMP_CLAUSE_ORDER,
PRAGMA_OMP_CLAUSE_ORDERED,
PRAGMA_OMP_CLAUSE_PARALLEL,
+ PRAGMA_OMP_CLAUSE_PARTIAL,
PRAGMA_OMP_CLAUSE_PRIORITY,
PRAGMA_OMP_CLAUSE_PRIVATE,
PRAGMA_OMP_CLAUSE_PROC_BIND,
diff --git a/gcc/c/ChangeLog.omp b/gcc/c/ChangeLog.omp
index 423e95a5171..4fba39ca5b0 100644
--- a/gcc/c/ChangeLog.omp
+++ b/gcc/c/ChangeLog.omp
@@ -1,3 +1,25 @@
+2023-03-24 Frederik Harwath <frederik@codesourcery.com>
+
+ * c-parser.cc (c_parser_omp_clause_name): Handle "full" and
+ "partial" clauses.
+ (check_no_duplicate_clause): Change return type to bool and
+ return check result.
+ (c_parser_omp_clause_unroll_full): New function for parsing
+ the "unroll clause".
+ (c_parser_omp_clause_unroll_partial): New function for
+ parsing the "partial" clause.
+ (c_parser_omp_all_clauses): Handle PRAGMA_OMP_CLAUSE_FULL
+ and PRAGMA_OMP_CLAUSE_PARTIAL.
+ (c_parser_nested_omp_unroll_clauses): New function for parsing
+ "omp unroll" directives following another directive.
+ (OMP_UNROLL_CLAUSE_MASK): New definition.
+ (c_parser_omp_unroll): New function for parsing "omp unroll"
+ loops that are not associated with another directive.
+ (c_parser_omp_construct): Handle PRAGMA_OMP_UNROLL.
+ * c-typeck.cc (c_finish_omp_clauses): Handle
+ OMP_CLAUSE_UNROLL_FULL, OMP_CLAUSE_UNROLL_PARTIAL,
+ and OMP_CLAUSE_UNROLL_NONE.
+
2023-02-01 Kwok Cheung Yeung <kcy@codesourcery.com>
* c-parser.cc (c_parser_omp_variable_list): Set default motion
diff --git a/gcc/c/c-parser.cc b/gcc/c/c-parser.cc
index 64be7732ef9..a2f5386a253 100644
--- a/gcc/c/c-parser.cc
+++ b/gcc/c/c-parser.cc
@@ -13490,6 +13490,8 @@ c_parser_omp_clause_name (c_parser *parser)
result = PRAGMA_OMP_CLAUSE_FIRSTPRIVATE;
else if (!strcmp ("from", p))
result = PRAGMA_OMP_CLAUSE_FROM;
+ else if (!strcmp ("full", p))
+ result = PRAGMA_OMP_CLAUSE_FULL;
break;
case 'g':
if (!strcmp ("gang", p))
@@ -13564,6 +13566,8 @@ c_parser_omp_clause_name (c_parser *parser)
case 'p':
if (!strcmp ("parallel", p))
result = PRAGMA_OMP_CLAUSE_PARALLEL;
+ else if (!strcmp ("partial", p))
+ result = PRAGMA_OMP_CLAUSE_PARTIAL;
else if (!strcmp ("present", p))
result = PRAGMA_OACC_CLAUSE_PRESENT;
/* As of OpenACC 2.5, these are now aliases of the non-present_or
@@ -13660,12 +13664,15 @@ c_parser_omp_clause_name (c_parser *parser)
/* Validate that a clause of the given type does not already exist. */
-static void
+static bool
check_no_duplicate_clause (tree clauses, enum omp_clause_code code,
const char *name)
{
- if (tree c = omp_find_clause (clauses, code))
+ tree c = omp_find_clause (clauses, code);
+ if (c)
error_at (OMP_CLAUSE_LOCATION (c), "too many %qs clauses", name);
+
+ return c == NULL_TREE;
}
/* OpenACC 2.0
@@ -17737,6 +17744,65 @@ c_parser_omp_clause_uniform (c_parser *parser, tree list)
return list;
}
+/* OpenMP 5.1
+ full */
+
+static tree
+c_parser_omp_clause_unroll_full (c_parser *parser, tree list)
+{
+ if (!check_no_duplicate_clause (list, OMP_CLAUSE_UNROLL_FULL, "full"))
+ return list;
+
+ location_t loc = c_parser_peek_token (parser)->location;
+ tree c = build_omp_clause (loc, OMP_CLAUSE_UNROLL_FULL);
+ OMP_CLAUSE_CHAIN (c) = list;
+ return c;
+}
+
+/* OpenMP 5.1
+ partial ( constant-expression ) */
+
+static tree
+c_parser_omp_clause_unroll_partial (c_parser *parser, tree list)
+{
+ if (!check_no_duplicate_clause (list, OMP_CLAUSE_UNROLL_PARTIAL, "partial"))
+ return list;
+
+ tree c, num = error_mark_node;
+ HOST_WIDE_INT n;
+ location_t loc;
+
+ loc = c_parser_peek_token (parser)->location;
+ c = build_omp_clause (loc, OMP_CLAUSE_UNROLL_PARTIAL);
+ OMP_CLAUSE_UNROLL_PARTIAL_EXPR (c) = NULL_TREE;
+ OMP_CLAUSE_CHAIN (c) = list;
+
+ if (!c_parser_next_token_is (parser, CPP_OPEN_PAREN))
+ return c;
+
+ matching_parens parens;
+ parens.consume_open (parser);
+ num = c_parser_expr_no_commas (parser, NULL).value;
+ parens.skip_until_found_close (parser);
+
+ if (num == error_mark_node)
+ return list;
+
+ mark_exp_read (num);
+ num = c_fully_fold (num, false, NULL);
+ if (!INTEGRAL_TYPE_P (TREE_TYPE (num)) || !tree_fits_shwi_p (num)
+ || (n = tree_to_shwi (num)) <= 0 || (int)n != n)
+ {
+ error_at (loc,
+ "partial argument needs positive constant integer expression");
+ return list;
+ }
+
+ OMP_CLAUSE_UNROLL_PARTIAL_EXPR (c) = num;
+
+ return c;
+}
+
/* OpenMP 5.0:
detach ( event-handle ) */
@@ -18335,6 +18401,14 @@ c_parser_omp_all_clauses (c_parser *parser, omp_clause_mask mask,
clauses);
c_name = "enter";
break;
+ case PRAGMA_OMP_CLAUSE_FULL:
+ c_name = "full";
+ clauses = c_parser_omp_clause_unroll_full (parser, clauses);
+ break;
+ case PRAGMA_OMP_CLAUSE_PARTIAL:
+ c_name = "partial";
+ clauses = c_parser_omp_clause_unroll_partial (parser, clauses);
+ break;
default:
c_parser_error (parser, "expected %<#pragma omp%> clause");
goto saw_error;
@@ -20466,6 +20540,8 @@ c_parser_omp_scan_loop_body (c_parser *parser, bool open_brace_parsed)
"expected %<}%>");
}
+static bool c_parser_nested_omp_unroll_clauses (c_parser *, tree &);
+
/* Parse the restricted form of loop statements allowed by OpenACC and OpenMP.
The real trick here is to determine the loop control variable early
so that we can push a new decl if necessary to make it private.
@@ -20525,6 +20601,13 @@ c_parser_omp_for_loop (location_t loc, c_parser *parser, enum tree_code code,
condv = make_tree_vec (count);
incrv = make_tree_vec (count);
+ if (c_parser_nested_omp_unroll_clauses (parser, clauses)
+ && count > 1)
+ {
+ error_at (loc, "collapse cannot be larger than 1 on an unrolled loop");
+ return NULL;
+ }
+
if (!c_parser_next_token_is_keyword (parser, RID_FOR))
{
c_parser_error (parser, "for statement expected");
@@ -24243,6 +24326,76 @@ c_parser_omp_taskloop (location_t loc, c_parser *parser,
return ret;
}
+#define OMP_UNROLL_CLAUSE_MASK \
+ ( (OMP_CLAUSE_MASK_1 << PRAGMA_OMP_CLAUSE_PARTIAL) \
+ | (OMP_CLAUSE_MASK_1 << PRAGMA_OMP_CLAUSE_FULL) )
+
+/* Parse zero or more '#pragma omp unroll' that follow
+ another directive that requires a canonical loop nest. */
+
+static bool
+c_parser_nested_omp_unroll_clauses (c_parser *parser, tree &clauses)
+{
+ static const char *p_name = "#pragma omp unroll";
+ c_token *tok;
+ bool found_unroll = false;
+ while (c_parser_next_token_is (parser, CPP_PRAGMA)
+ && (tok = c_parser_peek_token (parser),
+ tok->pragma_kind == PRAGMA_OMP_UNROLL))
+ {
+ c_parser_consume_pragma (parser);
+ tree c = c_parser_omp_all_clauses (parser, OMP_UNROLL_CLAUSE_MASK,
+ p_name, true);
+ if (c)
+ {
+ gcc_assert (!TREE_CHAIN (c));
+ found_unroll = true;
+ if (OMP_CLAUSE_CODE (c) == OMP_CLAUSE_UNROLL_FULL)
+ {
+ error_at (tok->location, "%<full%> clause is invalid here; "
+ "turns loop into non-loop");
+ continue;
+ }
+ }
+ else
+ {
+ error_at (tok->location, "%<#pragma omp unroll%> without "
+ "%<partial%> clause is invalid here; "
+ "turns loop into non-loop");
+ continue;
+ }
+
+ clauses = chainon (clauses, c);
+ }
+
+ return found_unroll;
+}
+
+static tree
+c_parser_omp_unroll (location_t loc, c_parser *parser, bool *if_p)
+{
+ tree block, ret;
+ static const char *p_name = "#pragma omp unroll";
+ omp_clause_mask mask = OMP_UNROLL_CLAUSE_MASK;
+
+ tree clauses = c_parser_omp_all_clauses (parser, mask, p_name, false);
+ c_parser_nested_omp_unroll_clauses (parser, clauses);
+
+ if (!clauses)
+ {
+ tree c = build_omp_clause (loc, OMP_CLAUSE_UNROLL_NONE);
+ OMP_CLAUSE_CHAIN (c) = clauses;
+ clauses = c;
+ }
+
+ block = c_begin_compound_stmt (true);
+ ret = c_parser_omp_for_loop (loc, parser, OMP_LOOP_TRANS, clauses, NULL, if_p);
+ block = c_end_compound_stmt (loc, block, true);
+ add_stmt (block);
+
+ return ret;
+}
+
/* OpenMP 5.1
#pragma omp nothing new-line */
@@ -24997,6 +25150,7 @@ c_parser_omp_construct (c_parser *parser, bool *if_p)
p_kind = c_parser_peek_token (parser)->pragma_kind;
c_parser_consume_pragma (parser);
+ gcc_assert (parser->in_pragma);
switch (p_kind)
{
case PRAGMA_OACC_ATOMIC:
@@ -25090,6 +25244,9 @@ c_parser_omp_construct (c_parser *parser, bool *if_p)
case PRAGMA_OMP_ASSUME:
c_parser_omp_assume (parser, if_p);
return;
+ case PRAGMA_OMP_UNROLL:
+ stmt = c_parser_omp_unroll (loc, parser, if_p);
+ break;
default:
gcc_unreachable ();
}
diff --git a/gcc/c/c-typeck.cc b/gcc/c/c-typeck.cc
index b927b8571e2..7f2d8ccc65d 100644
--- a/gcc/c/c-typeck.cc
+++ b/gcc/c/c-typeck.cc
@@ -16099,6 +16099,14 @@ c_finish_omp_clauses (tree clauses, enum c_omp_region_type ort)
pc = &OMP_CLAUSE_CHAIN (c);
continue;
+ case OMP_CLAUSE_UNROLL_FULL:
+ pc = &OMP_CLAUSE_CHAIN (c);
+ continue;
+
+ case OMP_CLAUSE_UNROLL_PARTIAL:
+ pc = &OMP_CLAUSE_CHAIN (c);
+ continue;
+
case OMP_CLAUSE_INBRANCH:
case OMP_CLAUSE_NOTINBRANCH:
if (branch_seen)
diff --git a/gcc/cp/ChangeLog.omp b/gcc/cp/ChangeLog.omp
index 09c143c0872..1f0735c926c 100644
--- a/gcc/cp/ChangeLog.omp
+++ b/gcc/cp/ChangeLog.omp
@@ -1,3 +1,35 @@
+2023-03-24 Frederik Harwath <frederik@codesourcery.com>
+
+ * cp-gimplify.cc (cp_gimplify_expr): Handle OMP_UNROLL.
+ (cp_fold_r): Likewise.
+ (cp_genericize_r): Likewise.
+ * parser.cc (cp_parser_omp_clause_name): Handle "full" clause.
+ (check_no_duplicate_clause): Change return type to bool and
+ return check result.
+ (cp_parser_omp_clause_unroll_full): New function for parsing
+ the "unroll clause".
+ (cp_parser_omp_clause_unroll_partial): New function for
+ parsing the "partial" clause.
+ (cp_parser_omp_all_clauses): Handle OMP_CLAUSE_UNROLL and
+ OMP_CLAUSE_FULL.
+ (cp_parser_nested_omp_unroll_clauses): New function for parsing
+ "omp unroll" directives following another directive.
+ (cp_parser_omp_for_loop): Handle "omp unroll" directives
+ between directive and loop.
+ (OMP_UNROLL_CLAUSE_MASK): New definition.
+ (cp_parser_omp_unroll): New function for parsing "omp unroll"
+ loops that are not associated with another directive.
+
+ (cp_parser_omp_construct): Handle PRAGMA_OMP_UNROLL.
+ (cp_parser_pragma): Handle PRAGMA_OMP_UNROLL.
+ * pt.cc (tsubst_omp_clauses): Handle
+ OMP_CLAUSE_UNROLL_PARTIAL, OMP_CLAUSE_UNROLL_FULL, and
+ OMP_CLAUSE_UNROLL_NONE.
+ (tsubst_expr): Handle OMP_UNROLL.
+ * semantics.cc (finish_omp_clauses): Handle
+ OMP_CLAUSE_UNROLL_FULL, OMP_CLAUSE_UNROLL_PARTIAL,
+ and OMP_CLAUSE_UNROLL_NONE.
+
2023-02-01 Kwok Cheung Yeung <kcy@codesourcery.com>
* parser.cc (cp_parser_omp_var_list_no_open): Add new parameter with
diff --git a/gcc/cp/cp-gimplify.cc b/gcc/cp/cp-gimplify.cc
index 4fecd5616bd..bf81097d780 100644
--- a/gcc/cp/cp-gimplify.cc
+++ b/gcc/cp/cp-gimplify.cc
@@ -638,6 +638,7 @@ cp_gimplify_expr (tree *expr_p, gimple_seq *pre_p, gimple_seq *post_p)
case OMP_DISTRIBUTE:
case OMP_LOOP:
case OMP_TASKLOOP:
+ case OMP_LOOP_TRANS:
ret = cp_gimplify_omp_for (expr_p, pre_p);
break;
@@ -1097,6 +1098,7 @@ cp_fold_r (tree *stmt_p, int *walk_subtrees, void *data_)
case OMP_DISTRIBUTE:
case OMP_LOOP:
case OMP_TASKLOOP:
+ case OMP_LOOP_TRANS:
case OACC_LOOP:
cp_walk_tree (&OMP_FOR_BODY (stmt), cp_fold_r, data, NULL);
cp_walk_tree (&OMP_FOR_CLAUSES (stmt), cp_fold_r, data, NULL);
@@ -1855,6 +1857,7 @@ cp_genericize_r (tree *stmt_p, int *walk_subtrees, void *data)
case OMP_FOR:
case OMP_SIMD:
case OMP_LOOP:
+ case OMP_LOOP_TRANS:
case OACC_LOOP:
case STATEMENT_LIST:
/* These cases are handled by shared code. */
diff --git a/gcc/cp/parser.cc b/gcc/cp/parser.cc
index 5690505b016..0a0b2087dfb 100644
--- a/gcc/cp/parser.cc
+++ b/gcc/cp/parser.cc
@@ -37225,6 +37225,8 @@ cp_parser_omp_clause_name (cp_parser *parser)
result = PRAGMA_OMP_CLAUSE_FIRSTPRIVATE;
else if (!strcmp ("from", p))
result = PRAGMA_OMP_CLAUSE_FROM;
+ else if (!strcmp ("full", p))
+ result = PRAGMA_OMP_CLAUSE_FULL;
break;
case 'g':
if (!strcmp ("gang", p))
@@ -37299,6 +37301,8 @@ cp_parser_omp_clause_name (cp_parser *parser)
case 'p':
if (!strcmp ("parallel", p))
result = PRAGMA_OMP_CLAUSE_PARALLEL;
+ if (!strcmp ("partial", p))
+ result = PRAGMA_OMP_CLAUSE_PARTIAL;
else if (!strcmp ("present", p))
result = PRAGMA_OACC_CLAUSE_PRESENT;
else if (!strcmp ("present_or_copy", p)
@@ -37391,12 +37395,15 @@ cp_parser_omp_clause_name (cp_parser *parser)
/* Validate that a clause of the given type does not already exist. */
-static void
+static bool
check_no_duplicate_clause (tree clauses, enum omp_clause_code code,
const char *name, location_t location)
{
- if (omp_find_clause (clauses, code))
+ bool found = omp_find_clause (clauses, code);
+ if (found)
error_at (location, "too many %qs clauses", name);
+
+ return !found;
}
/* OpenMP 2.5:
@@ -39513,6 +39520,56 @@ cp_parser_omp_clause_thread_limit (cp_parser *parser, tree list,
return c;
}
+/* OpenMP 5.1
+ full */
+
+static tree
+cp_parser_omp_clause_unroll_full (tree list, location_t loc)
+{
+ if (!check_no_duplicate_clause (list, OMP_CLAUSE_UNROLL_FULL, "full", loc))
+ return list;
+
+ tree c = build_omp_clause (loc, OMP_CLAUSE_UNROLL_FULL);
+ OMP_CLAUSE_CHAIN (c) = list;
+ return c;
+}
+
+/* OpenMP 5.1
+ partial ( constant-expression ) */
+
+static tree
+cp_parser_omp_clause_unroll_partial (cp_parser *parser, tree list,
+ location_t loc)
+{
+ if (!check_no_duplicate_clause (list, OMP_CLAUSE_UNROLL_PARTIAL, "partial",
+ loc))
+ return list;
+
+ tree c, num = error_mark_node;
+ c = build_omp_clause (loc, OMP_CLAUSE_UNROLL_PARTIAL);
+ OMP_CLAUSE_UNROLL_PARTIAL_EXPR (c) = NULL_TREE;
+ OMP_CLAUSE_CHAIN (c) = list;
+
+ if (!cp_lexer_next_token_is (parser->lexer, CPP_OPEN_PAREN))
+ return c;
+
+ matching_parens parens;
+ parens.consume_open (parser);
+ num = cp_parser_constant_expression (parser);
+ cp_parser_skip_to_closing_parenthesis (parser, /*recovering=*/true,
+ /*or_comma=*/false,
+ /*consume_paren=*/true);
+
+ if (num == error_mark_node)
+ return list;
+
+ mark_exp_read (num);
+ num = fold_non_dependent_expr (num);
+
+ OMP_CLAUSE_UNROLL_PARTIAL_EXPR (c) = num;
+ return c;
+}
+
/* OpenMP 4.0:
aligned ( variable-list )
aligned ( variable-list : constant-expression ) */
@@ -41751,6 +41808,15 @@ cp_parser_omp_all_clauses (cp_parser *parser, omp_clause_mask mask,
clauses);
c_name = "enter";
break;
+ case PRAGMA_OMP_CLAUSE_PARTIAL:
+ clauses = cp_parser_omp_clause_unroll_partial (parser, clauses,
+ token->location);
+ c_name = "partial";
+ break;
+ case PRAGMA_OMP_CLAUSE_FULL:
+ clauses = cp_parser_omp_clause_unroll_full(clauses, token->location);
+ c_name = "full";
+ break;
default:
cp_parser_error (parser, "expected %<#pragma omp%> clause");
goto saw_error;
@@ -43875,6 +43941,8 @@ cp_parser_omp_scan_loop_body (cp_parser *parser)
braces.require_close (parser);
}
+static bool cp_parser_nested_omp_unroll_clauses (cp_parser *, tree &);
+
/* Parse the restricted form of the for statement allowed by OpenMP. */
static tree
@@ -43932,6 +44000,15 @@ cp_parser_omp_for_loop (cp_parser *parser, enum tree_code code, tree clauses,
loc_first = cp_lexer_peek_token (parser->lexer)->location;
+ if (cp_parser_nested_omp_unroll_clauses (parser, clauses)
+ && count > 1)
+ {
+ error_at (loc_first,
+ "collapse cannot be larger than 1 on an unrolled loop");
+ return NULL;
+ }
+
+
for (i = 0; i < count; i++)
{
int bracecount = 0;
@@ -45996,6 +46073,79 @@ cp_parser_omp_target (cp_parser *parser, cp_token *pragma_tok,
return true;
}
+#define OMP_UNROLL_CLAUSE_MASK \
+ ( (OMP_CLAUSE_MASK_1 << PRAGMA_OMP_CLAUSE_PARTIAL) \
+ | (OMP_CLAUSE_MASK_1 << PRAGMA_OMP_CLAUSE_FULL) )
+
+/* Parse zero or more '#pragma omp unroll' that follow
+ another directive that requires a canonical loop nest. */
+
+static bool
+cp_parser_nested_omp_unroll_clauses (cp_parser *parser, tree &clauses)
+{
+ static const char *p_name = "#pragma omp unroll";
+ cp_token *tok;
+ bool unroll_found = false;
+ while (cp_lexer_next_token_is (parser->lexer, CPP_PRAGMA)
+ && (tok = cp_lexer_peek_token (parser->lexer),
+ cp_parser_pragma_kind (tok) == PRAGMA_OMP_UNROLL))
+ {
+ cp_lexer_consume_token (parser->lexer);
+ gcc_assert (tok->type == CPP_PRAGMA);
+ parser->lexer->in_pragma = true;
+ tree c = cp_parser_omp_all_clauses (parser, OMP_UNROLL_CLAUSE_MASK,
+ p_name, tok);
+ if (c)
+ {
+ gcc_assert (!TREE_CHAIN (c));
+ unroll_found = true;
+ if (OMP_CLAUSE_CODE (c) == OMP_CLAUSE_UNROLL_FULL)
+ {
+ error_at (tok->location, "%<full%> clause is invalid here; "
+ "turns loop into non-loop");
+ continue;
+ }
+
+ c = finish_omp_clauses (c, C_ORT_OMP);
+ }
+ else
+ {
+ error_at (tok->location, "%<#pragma omp unroll%> without "
+ "%<partial%> clause is invalid here; "
+ "turns loop into non-loop");
+ continue;
+ }
+ clauses = chainon (clauses, c);
+ }
+ return unroll_found;
+}
+
+static tree
+cp_parser_omp_unroll (cp_parser *parser, cp_token *tok, bool *if_p)
+{
+ tree block, ret;
+ static const char *p_name = "#pragma omp unroll";
+ omp_clause_mask mask = OMP_UNROLL_CLAUSE_MASK;
+
+ tree clauses = cp_parser_omp_all_clauses (parser, mask, p_name, tok, false);
+
+ if (!clauses)
+ {
+ tree c = build_omp_clause (tok->location, OMP_CLAUSE_UNROLL_NONE);
+ OMP_CLAUSE_CHAIN (c) = clauses;
+ clauses = c;
+ }
+
+ cp_parser_nested_omp_unroll_clauses (parser, clauses);
+
+ block = begin_omp_structured_block ();
+ ret = cp_parser_omp_for_loop (parser, OMP_LOOP_TRANS, clauses, NULL, if_p);
+ block = finish_omp_structured_block (block);
+ add_stmt (block);
+
+ return ret;
+}
+
/* OpenACC 2.0:
# pragma acc cache (variable-list) new-line
*/
@@ -49514,6 +49664,9 @@ cp_parser_omp_construct (cp_parser *parser, cp_token *pragma_tok, bool *if_p)
case PRAGMA_OMP_ASSUME:
cp_parser_omp_assume (parser, pragma_tok, if_p);
return;
+ case PRAGMA_OMP_UNROLL:
+ stmt = cp_parser_omp_unroll (parser, pragma_tok, if_p);
+ break;
default:
gcc_unreachable ();
}
@@ -50140,6 +50293,13 @@ cp_parser_pragma (cp_parser *parser, enum pragma_context context, bool *if_p)
cp_parser_omp_construct (parser, pragma_tok, if_p);
pop_omp_privatization_clauses (stmt);
return true;
+ case PRAGMA_OMP_UNROLL:
+ if (context != pragma_stmt && context != pragma_compound)
+ goto bad_stmt;
+ stmt = push_omp_privatization_clauses (false);
+ cp_parser_omp_construct (parser, pragma_tok, if_p);
+ pop_omp_privatization_clauses (stmt);
+ return true;
case PRAGMA_OMP_REQUIRES:
if (context != pragma_external)
diff --git a/gcc/cp/pt.cc b/gcc/cp/pt.cc
index fcc8e0d1d57..bc15f5d8e5f 100644
--- a/gcc/cp/pt.cc
+++ b/gcc/cp/pt.cc
@@ -18098,6 +18098,7 @@ tsubst_omp_clauses (tree clauses, enum c_omp_region_type ort,
case OMP_CLAUSE_ASYNC:
case OMP_CLAUSE_WAIT:
case OMP_CLAUSE_DETACH:
+ case OMP_CLAUSE_UNROLL_PARTIAL:
OMP_CLAUSE_OPERAND (nc, 0)
= tsubst_expr (OMP_CLAUSE_OPERAND (oc, 0), args, complain, in_decl);
break;
@@ -18181,6 +18182,8 @@ tsubst_omp_clauses (tree clauses, enum c_omp_region_type ort,
case OMP_CLAUSE_IF_PRESENT:
case OMP_CLAUSE_FINALIZE:
case OMP_CLAUSE_NOHOST:
+ case OMP_CLAUSE_UNROLL_FULL:
+ case OMP_CLAUSE_UNROLL_NONE:
break;
default:
gcc_unreachable ();
@@ -19449,6 +19452,7 @@ tsubst_expr (tree t, tree args, tsubst_flags_t complain, tree in_decl)
case OMP_SIMD:
case OMP_DISTRIBUTE:
case OMP_TASKLOOP:
+ case OMP_LOOP_TRANS:
case OACC_LOOP:
{
tree clauses, body, pre_body;
diff --git a/gcc/cp/semantics.cc b/gcc/cp/semantics.cc
index 38f97431359..67109f16285 100644
--- a/gcc/cp/semantics.cc
+++ b/gcc/cp/semantics.cc
@@ -6856,6 +6856,7 @@ finish_omp_clauses (tree clauses, enum c_omp_region_type ort)
bool mergeable_seen = false;
bool implicit_moved = false;
bool target_in_reduction_seen = false;
+ bool unroll_full_seen = false;
bitmap_obstack_initialize (NULL);
bitmap_initialize (&generic_head, &bitmap_default_obstack);
@@ -9008,6 +9009,60 @@ finish_omp_clauses (tree clauses, enum c_omp_region_type ort)
}
break;
+ case OMP_CLAUSE_UNROLL_FULL:
+ if (unroll_full_seen)
+ {
+ error_at (OMP_CLAUSE_LOCATION (c),
+ "%<full%> appears more than once");
+ remove = true;
+ }
+ unroll_full_seen = true;
+ break;
+
+ case OMP_CLAUSE_UNROLL_PARTIAL:
+ {
+ tree t = OMP_CLAUSE_UNROLL_PARTIAL_EXPR (c);
+
+ if (!t)
+ break;
+
+ if (t == error_mark_node)
+ remove = true;
+ else if (!type_dependent_expression_p (t)
+ && !INTEGRAL_TYPE_P (TREE_TYPE (t)))
+ {
+ error_at (OMP_CLAUSE_LOCATION (c),
+ "partial argument needs integral type");
+ remove = true;
+ }
+ else
+ {
+ t = mark_rvalue_use (t);
+ if (!processing_template_decl)
+ {
+ t = maybe_constant_value (t);
+
+ int n;
+ if (!INTEGRAL_TYPE_P (TREE_TYPE (t))
+ || !tree_fits_shwi_p (t)
+ || (n = tree_to_shwi (t)) <= 0 || (int)n != n)
+ {
+ error_at (OMP_CLAUSE_LOCATION (c),
+ "partial argument needs positive constant "
+ "integer expression");
+ remove = true;
+ }
+ t = fold_build_cleanup_point_expr (TREE_TYPE (t), t);
+ }
+ }
+
+ OMP_CLAUSE_UNROLL_PARTIAL_EXPR (c) = t;
+ }
+ break;
+
+ case OMP_CLAUSE_UNROLL_NONE:
+ break;
+
default:
gcc_unreachable ();
}
diff --git a/gcc/testsuite/ChangeLog.omp b/gcc/testsuite/ChangeLog.omp
index f1c17e619e9..abd7790b41d 100644
--- a/gcc/testsuite/ChangeLog.omp
+++ b/gcc/testsuite/ChangeLog.omp
@@ -1,3 +1,15 @@
+2023-03-24 Frederik Harwath <frederik@codesourcery.com>
+
+ * c-c++-common/gomp/loop-transforms/unroll-1.c: New test.
+ * c-c++-common/gomp/loop-transforms/unroll-2.c: New test.
+ * c-c++-common/gomp/loop-transforms/unroll-3.c: New test.
+ * c-c++-common/gomp/loop-transforms/unroll-4.c: New test.
+ * c-c++-common/gomp/loop-transforms/unroll-5.c: New test.
+ * c-c++-common/gomp/loop-transforms/unroll-6.c: New test.
+ * g++.dg/gomp/loop-transforms/unroll-1.C: New test.
+ * g++.dg/gomp/loop-transforms/unroll-2.C: New test.
+ * g++.dg/gomp/loop-transforms/unroll-3.C: New test.
+
2023-03-24 Frederik Harwath <frederik@codesourcery.com>
* gfortran.dg/gomp/loop-transforms/unroll-1.f90: New test.
diff --git a/gcc/testsuite/c-c++-common/gomp/loop-transforms/unroll-1.c b/gcc/testsuite/c-c++-common/gomp/loop-transforms/unroll-1.c
new file mode 100644
index 00000000000..d496dc29053
--- /dev/null
+++ b/gcc/testsuite/c-c++-common/gomp/loop-transforms/unroll-1.c
@@ -0,0 +1,133 @@
+extern void dummy (int);
+
+void
+test1 ()
+{
+#pragma omp unroll partial
+ for (int i = 0; i < 100; ++i)
+ dummy (i);
+}
+
+void
+test2 ()
+{
+#pragma omp unroll partial(10)
+ for (int i = 0; i < 100; ++i)
+ dummy (i);
+}
+
+void
+test3 ()
+{
+#pragma omp unroll full
+ for (int i = 0; i < 100; ++i)
+ dummy (i);
+}
+
+void
+test4 ()
+{
+#pragma omp unroll full
+ for (int i = 0; i > 100; ++i)
+ dummy (i);
+}
+
+void
+test5 ()
+{
+#pragma omp unroll full
+ for (int i = 1; i <= 100; ++i)
+ dummy (i);
+}
+
+void
+test6 ()
+{
+#pragma omp unroll full
+ for (int i = 200; i >= 100; i--)
+ dummy (i);
+}
+
+void
+test7 ()
+{
+#pragma omp unroll full
+ for (int i = -100; i > 100; ++i)
+ dummy (i);
+}
+
+void
+test8 ()
+{
+#pragma omp unroll full
+ for (int i = 100; i > -200; --i)
+ dummy (i);
+}
+
+void
+test9 ()
+{
+#pragma omp unroll full
+ for (int i = -300; i != 100; ++i)
+ dummy (i);
+}
+
+void
+test10 ()
+{
+#pragma omp unroll full
+ for (int i = -300; i != 100; ++i)
+ dummy (i);
+}
+
+void
+test12 ()
+{
+#pragma omp unroll full
+#pragma omp unroll partial
+#pragma omp unroll partial
+ for (int i = -300; i != 100; ++i)
+ dummy (i);
+}
+
+void
+test13 ()
+{
+ for (int i = 0; i < 100; ++i)
+#pragma omp unroll full
+#pragma omp unroll partial
+#pragma omp unroll partial
+ for (int j = -300; j != 100; ++j)
+ dummy (i);
+}
+
+void
+test14 ()
+{
+ #pragma omp for
+ for (int i = 0; i < 100; ++i)
+#pragma omp unroll full
+#pragma omp unroll partial
+#pragma omp unroll partial
+ for (int j = -300; j != 100; ++j)
+ dummy (i);
+}
+
+void
+test15 ()
+{
+ #pragma omp for
+ for (int i = 0; i < 100; ++i)
+ {
+
+ dummy (i);
+
+#pragma omp unroll full
+#pragma omp unroll partial
+#pragma omp unroll partial
+ for (int j = -300; j != 100; ++j)
+ dummy (j);
+
+ dummy (i);
+ }
+ }
diff --git a/gcc/testsuite/c-c++-common/gomp/loop-transforms/unroll-2.c b/gcc/testsuite/c-c++-common/gomp/loop-transforms/unroll-2.c
new file mode 100644
index 00000000000..8f7c3088a2e
--- /dev/null
+++ b/gcc/testsuite/c-c++-common/gomp/loop-transforms/unroll-2.c
@@ -0,0 +1,99 @@
+/* { dg-prune-output "error: invalid controlling predicate" } */
+/* { dg-additional-options "-std=c++11" { target c++} } */
+
+extern void dummy (int);
+
+void
+test ()
+{
+#pragma omp unroll partial
+#pragma omp unroll full /* { dg-error {'full' clause is invalid here; turns loop into non-loop} } */
+ for (int i = -300; i != 100; ++i)
+ dummy (i);
+
+#pragma omp for
+#pragma omp unroll full /* { dg-error {'full' clause is invalid here; turns loop into non-loop} } */
+#pragma omp unroll partial
+ for (int i = -300; i != 100; ++i)
+ dummy (i);
+
+#pragma omp for
+#pragma omp unroll full /* { dg-error {'full' clause is invalid here; turns loop into non-loop} } */
+#pragma omp unroll full /* { dg-error {'full' clause is invalid here; turns loop into non-loop} } */
+ for (int i = -300; i != 100; ++i)
+ dummy (i);
+
+#pragma omp for
+#pragma omp unroll partial partial /* { dg-error {too many 'partial' clauses} } */
+ for (int i = -300; i != 100; ++i)
+ dummy (i);
+
+#pragma omp unroll full full /* { dg-error {too many 'full' clauses} } */
+ for (int i = -300; i != 100; ++i)
+ dummy (i);
+
+#pragma omp unroll partial
+#pragma omp unroll /* { dg-error {'#pragma omp unroll' without 'partial' clause is invalid here; turns loop into non-loop} } */
+ for (int i = -300; i != 100; ++i)
+ dummy (i);
+
+#pragma omp for
+#pragma omp unroll /* { dg-error {'#pragma omp unroll' without 'partial' clause is invalid here; turns loop into non-loop} } */
+ for (int i = -300; i != 100; ++i)
+ dummy (i);
+
+ int i;
+#pragma omp for
+#pragma omp unroll( /* { dg-error {expected '#pragma omp' clause before '\(' token} } */
+ /* { dg-error {'#pragma omp unroll' without 'partial' clause is invalid here; turns loop into non-loop} "" { target *-*-* } .-1 } */
+ for (int i = -300; i != 100; ++i)
+ dummy (i);
+
+#pragma omp for
+#pragma omp unroll foo /* { dg-error {expected '#pragma omp' clause before 'foo'} } */
+ /* { dg-error {'#pragma omp unroll' without 'partial' clause is invalid here; turns loop into non-loop} "" { target *-*-* } .-1 } */
+ for (int i = -300; i != 100; ++i)
+ dummy (i);
+
+#pragma omp unroll partial( /* { dg-error {expected expression before end of line} "" { target c } } */
+ /* { dg-error {expected primary-expression before end of line} "" { target c++ } .-1 } */
+ for (int i = -300; i != 100; ++i)
+ dummy (i);
+
+#pragma omp unroll partial() /* { dg-error {expected expression before '\)' token} "" { target c } } */
+ /* { dg-error {expected primary-expression before '\)' token} "" { target c++ } .-1 } */
+ for (int i = -300; i != 100; ++i)
+ dummy (i);
+
+#pragma omp unroll partial(i)
+ /* { dg-error {the value of 'i' is not usable in a constant expression} "" { target c++ } .-1 } */
+ /* { dg-error {partial argument needs positive constant integer expression} "" { target c } .-2 } */
+ for (int i = -300; i != 100; ++i)
+ dummy (i);
+
+#pragma omp unroll parti /* { dg-error {expected '#pragma omp' clause before 'parti'} } */
+ for (int i = -300; i != 100; ++i)
+ dummy (i);
+
+#pragma omp for
+#pragma omp unroll partial(1)
+#pragma omp unroll parti /* { dg-error {expected '#pragma omp' clause before 'parti'} } */
+ /* { dg-error {'#pragma omp unroll' without 'partial' clause is invalid here; turns loop into non-loop} "" { target *-*-* } .-1 } */
+ for (int i = -300; i != 100; ++i)
+ dummy (i);
+
+#pragma omp for
+#pragma omp unroll partial(1)
+#pragma omp unroll parti /* { dg-error {expected '#pragma omp' clause before 'parti'} } */
+ /* { dg-error {'#pragma omp unroll' without 'partial' clause is invalid here; turns loop into non-loop} "" { target *-*-* } .-1 } */
+ for (int i = -300; i != 100; ++i)
+ dummy (i);
+
+int sum = 0;
+#pragma omp parallel for reduction(+ : sum) collapse(2) /* { dg-error {collapse cannot be larger than 1 on an unrolled loop} "" { target c } } */
+#pragma omp unroll partial(1) /* { dg-error {collapse cannot be larger than 1 on an unrolled loop} "" { target c++ } } */
+ for (int i = 3; i < 10; ++i)
+ for (int j = -2; j < 7; ++j)
+ sum++;
+}
+
diff --git a/gcc/testsuite/c-c++-common/gomp/loop-transforms/unroll-3.c b/gcc/testsuite/c-c++-common/gomp/loop-transforms/unroll-3.c
new file mode 100644
index 00000000000..7ace5657b26
--- /dev/null
+++ b/gcc/testsuite/c-c++-common/gomp/loop-transforms/unroll-3.c
@@ -0,0 +1,18 @@
+/* { dg-additional-options "-fdump-tree-omp_transform_loops" }
+ * { dg-additional-options "-fdump-tree-original" } */
+
+extern void dummy (int);
+
+void
+test1 ()
+{
+ int i;
+#pragma omp unroll full
+ for (int i = 0; i < 10; i++)
+ dummy (i);
+}
+
+ /* Loop should be removed with 10 copies of the body remaining
+ * { dg-final { scan-tree-dump-times "dummy" 10 "omp_transform_loops" } }
+ * { dg-final { scan-tree-dump "#pragma omp loop_transform" "original" } }
+ * { dg-final { scan-tree-dump-not "#pragma omp" "omp_transform_loops" } } */
diff --git a/gcc/testsuite/c-c++-common/gomp/loop-transforms/unroll-4.c b/gcc/testsuite/c-c++-common/gomp/loop-transforms/unroll-4.c
new file mode 100644
index 00000000000..5e473a099d3
--- /dev/null
+++ b/gcc/testsuite/c-c++-common/gomp/loop-transforms/unroll-4.c
@@ -0,0 +1,19 @@
+/* { dg-additional-options "-fdump-tree-omp_transform_loops" }
+ * { dg-additional-options "-fdump-tree-original" } */
+
+extern void dummy (int);
+
+void
+test1 ()
+{
+ int i;
+#pragma omp unroll
+ for (int i = 0; i < 100; i++)
+ dummy (i);
+}
+
+/* Loop should not be unrolled, but the internal representation should be lowered
+ * { dg-final { scan-tree-dump "#pragma omp loop_transform" "original" } }
+ * { dg-final { scan-tree-dump-not "#pragma omp" "omp_transform_loops" } }
+ * { dg-final { scan-tree-dump-times "dummy" 1 "omp_transform_loops" } }
+ * { dg-final { scan-tree-dump-times {if \(i < .+?.+goto.+else goto.*?$} 1 "omp_transform_loops" } } */
diff --git a/gcc/testsuite/c-c++-common/gomp/loop-transforms/unroll-5.c b/gcc/testsuite/c-c++-common/gomp/loop-transforms/unroll-5.c
new file mode 100644
index 00000000000..9d5101bdc60
--- /dev/null
+++ b/gcc/testsuite/c-c++-common/gomp/loop-transforms/unroll-5.c
@@ -0,0 +1,19 @@
+/* { dg-additional-options "-fdump-tree-omp_transform_loops -fopt-info-omp-optimized-missed" }
+ * { dg-additional-options "-fdump-tree-original" } */
+
+extern void dummy (int);
+
+void
+test1 ()
+{
+ int i;
+#pragma omp unroll partial /* { dg-optimized {'partial' clause without unrolling factor turned into 'partial\(5\)' clause} } */
+ for (int i = 0; i < 100; i++)
+ dummy (i);
+}
+
+/* Loop should be unrolled 5 times and the internal representation should be lowered
+ * { dg-final { scan-tree-dump "#pragma omp loop_transform" "original" } }
+ * { dg-final { scan-tree-dump-not "#pragma omp" "omp_transform_loops" } }
+ * { dg-final { scan-tree-dump-times "dummy" 5 "omp_transform_loops" } }
+ * { dg-final { scan-tree-dump-times {if \(i < .+?.+goto.+else goto.*?$} 1 "omp_transform_loops" } } */
diff --git a/gcc/testsuite/c-c++-common/gomp/loop-transforms/unroll-6.c b/gcc/testsuite/c-c++-common/gomp/loop-transforms/unroll-6.c
new file mode 100644
index 00000000000..ee2d000239d
--- /dev/null
+++ b/gcc/testsuite/c-c++-common/gomp/loop-transforms/unroll-6.c
@@ -0,0 +1,20 @@
+/* { dg-additional-options "--param=omp-unroll-default-factor=100" }
+ * { dg-additional-options "-fdump-tree-omp_transform_loops -fopt-info-omp-optimized-missed" }
+ * { dg-additional-options "-fdump-tree-original" } */
+
+extern void dummy (int);
+
+void
+test1 ()
+{
+ int i;
+#pragma omp unroll /* { dg-optimized {added 'partial\(100\)' clause to 'omp unroll' directive} } */
+ for (int i = 0; i < 100; i++)
+ dummy (i);
+}
+
+/* Loop should be unrolled 5 times and the internal representation should be lowered
+ * { dg-final { scan-tree-dump "#pragma omp loop_transform unroll_none" "original" } }
+ * { dg-final { scan-tree-dump-not "#pragma omp" "omp_transform_loops" } }
+ * { dg-final { scan-tree-dump-times "dummy" 100 "omp_transform_loops" } }
+ * { dg-final { scan-tree-dump-times {if \(i < .+?.+goto.+else goto.*?$} 1 "omp_transform_loops" } } */
diff --git a/gcc/testsuite/c-c++-common/gomp/loop-transforms/unroll-7.c b/gcc/testsuite/c-c++-common/gomp/loop-transforms/unroll-7.c
new file mode 100644
index 00000000000..0458cb030a9
--- /dev/null
+++ b/gcc/testsuite/c-c++-common/gomp/loop-transforms/unroll-7.c
@@ -0,0 +1,144 @@
+/* { dg-do run } */
+/* { dg-options "-O0 -fopenmp-simd" } */
+
+#include <stdio.h>
+
+#define ASSERT_EQ(var, val) if (var != val) { fprintf (stderr, "%s:%d: Unexpected value %d\n", __FILE__, __LINE__, var); \
+ __builtin_abort (); }
+
+#define ASSERT_EQ_PTR(var, ptr) if (var != ptr) { fprintf (stderr, "%s:%d: Unexpected value %p\n", __FILE__, __LINE__, var); \
+ __builtin_abort (); }
+
+int
+test1 (int data[10])
+{
+ int iter = 0;
+ int *i;
+ #pragma omp unroll partial(8)
+ for (i = data; i < data + 10 ; i++)
+ {
+ ASSERT_EQ (*i, data[iter]);
+ ASSERT_EQ_PTR (i, data + iter);
+ iter++;
+ }
+
+ return iter;
+}
+
+int
+test2 (int data[10])
+{
+ int iter = 0;
+ int *i;
+ #pragma omp unroll partial(8)
+ for (i = data; i < data + 10 ; i=i+2)
+ {
+ ASSERT_EQ_PTR (i, data + 2 * iter);
+ ASSERT_EQ (*i, data[2 * iter]);
+ iter++;
+ }
+
+ return iter;
+}
+
+int
+test3 (int data[10])
+{
+ int iter = 0;
+ int *i;
+ #pragma omp unroll partial(8)
+ for (i = data; i <= data + 9 ; i=i+2)
+ {
+ ASSERT_EQ (*i, data[2 * iter]);
+ iter++;
+ }
+
+ return iter;
+}
+
+int
+test4 (int data[10])
+{
+ int iter = 0;
+ int *i;
+ #pragma omp unroll partial(8)
+ for (i = data; i != data + 10 ; i=i+1)
+ {
+ ASSERT_EQ (*i, data[iter]);
+ iter++;
+ }
+
+ return iter;
+}
+
+int
+test5 (int data[10])
+{
+ int iter = 0;
+ int *i;
+ #pragma omp unroll partial(7)
+ for (i = data + 9; i >= data ; i--)
+ {
+ ASSERT_EQ (*i, data[9 - iter]);
+ iter++;
+ }
+
+ return iter;
+}
+
+int
+test6 (int data[10])
+{
+ int iter = 0;
+ int *i;
+ #pragma omp unroll partial(7)
+ for (i = data + 9; i > data - 1 ; i--)
+ {
+ ASSERT_EQ (*i, data[9 - iter]);
+ iter++;
+ }
+
+ return iter;
+}
+
+int
+test7 (int data[10])
+{
+ int iter = 0;
+ #pragma omp unroll partial(7)
+ for (int *i = data + 9; i != data - 1 ; i--)
+ {
+ ASSERT_EQ (*i, data[9 - iter]);
+ iter++;
+ }
+
+ return iter;
+}
+
+int
+main ()
+{
+ int iter_count;
+ int data[10] = { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9 };
+
+ iter_count = test1 (data);
+ ASSERT_EQ (iter_count, 10);
+
+ iter_count = test2 (data);
+ ASSERT_EQ (iter_count, 5);
+
+ iter_count = test3 (data);
+ ASSERT_EQ (iter_count, 5);
+
+ iter_count = test4 (data);
+ ASSERT_EQ (iter_count, 10);
+
+ iter_count = test5 (data);
+ ASSERT_EQ (iter_count, 10);
+
+ iter_count = test6 (data);
+ ASSERT_EQ (iter_count, 10);
+
+ iter_count = test7 (data);
+ ASSERT_EQ (iter_count, 10);
+}
diff --git a/gcc/testsuite/c-c++-common/gomp/loop-transforms/unroll-simd-1.c b/gcc/testsuite/c-c++-common/gomp/loop-transforms/unroll-simd-1.c
new file mode 100644
index 00000000000..1cd4d6e7322
--- /dev/null
+++ b/gcc/testsuite/c-c++-common/gomp/loop-transforms/unroll-simd-1.c
@@ -0,0 +1,84 @@
+/* { dg-options "-fno-openmp -fopenmp-simd" } */
+/* { dg-do run } */
+/* { dg-additional-options "-fdump-tree-original" } */
+/* { dg-additional-options "-fdump-tree-omp_transform_loops" } */
+
+#include <stdio.h>
+
+int compute_sum1 ()
+{
+ int sum = 0;
+ int i,j;
+
+#pragma omp simd reduction(+:sum)
+ for (i = 3; i < 10; ++i)
+ #pragma omp unroll full
+ for (j = -2; j < 7; ++j)
+ sum++;
+
+ if (j != 7)
+ __builtin_abort;
+
+ return sum;
+}
+
+int compute_sum2()
+{
+ int sum = 0;
+ int i,j;
+#pragma omp simd reduction(+:sum)
+#pragma omp unroll partial(5)
+ for (i = 3; i < 10; ++i)
+ for (j = -2; j < 7; ++j)
+ sum++;
+
+ if (j != 7)
+ __builtin_abort;
+
+ return sum;
+}
+
+int compute_sum3()
+{
+ int sum = 0;
+ int i,j;
+#pragma omp simd reduction(+:sum)
+#pragma omp unroll partial(1)
+ for (i = 3; i < 10; ++i)
+ for (j = -2; j < 7; ++j)
+ sum++;
+
+ if (j != 7)
+ __builtin_abort;
+
+ return sum;
+}
+
+int main ()
+{
+ int result = compute_sum1 ();
+ if (result != 7 * 9)
+ {
+ fprintf (stderr, "%d: Wrong result %d\n", __LINE__, result);
+ __builtin_abort ();
+ }
+
+ result = compute_sum1 ();
+ if (result != 7 * 9)
+ {
+ fprintf (stderr, "%d: Wrong result %d\n", __LINE__, result);
+ __builtin_abort ();
+ }
+
+ result = compute_sum3 ();
+ if (result != 7 * 9)
+ {
+ fprintf (stderr, "%d: Wrong result %d\n", __LINE__, result);
+ __builtin_abort ();
+ }
+
+ return 0;
+}
+
+/* { dg-final { scan-tree-dump {omp loop_transform} "original" } } */
+/* { dg-final { scan-tree-dump-not {omp loop_transform} "omp_transform_loops" } } */
diff --git a/gcc/testsuite/g++.dg/gomp/loop-transforms/unroll-1.C b/gcc/testsuite/g++.dg/gomp/loop-transforms/unroll-1.C
new file mode 100644
index 00000000000..cba37c88ebe
--- /dev/null
+++ b/gcc/testsuite/g++.dg/gomp/loop-transforms/unroll-1.C
@@ -0,0 +1,42 @@
+// { dg-do compile }
+// { dg-additional-options "-std=c++11" }
+#include <vector>
+
+extern void dummy (int);
+
+void
+test1 ()
+{
+ std::vector<int> v;
+
+ for (unsigned i = 0; i < 1000; i++)
+ v.push_back (i);
+
+#pragma omp for
+ for (int i : v)
+ dummy (i);
+
+#pragma omp unroll partial(5)
+ for (int i : v)
+ dummy (i);
+}
+
+void
+test2 ()
+{
+ std::vector<std::vector<int>> v;
+
+ for (unsigned i = 0; i < 10; i++)
+ {
+ std::vector<int> u;
+ for (unsigned j = 0; j < 10; j++)
+ u.push_back (j);
+ v.push_back (u);
+ }
+
+#pragma omp for
+#pragma omp unroll partial(5)
+ for (auto u : v)
+ for (int i : u)
+ dummy (i);
+}
diff --git a/gcc/testsuite/g++.dg/gomp/loop-transforms/unroll-2.C b/gcc/testsuite/g++.dg/gomp/loop-transforms/unroll-2.C
new file mode 100644
index 00000000000..f606f3de757
--- /dev/null
+++ b/gcc/testsuite/g++.dg/gomp/loop-transforms/unroll-2.C
@@ -0,0 +1,47 @@
+// { dg-do link }
+// { dg-additional-options "-std=c++11" }
+#include <vector>
+
+extern void dummy (int);
+
+template<class T, int U1, int U2, int U3> void
+test_template ()
+{
+ std::vector<int> v;
+
+ for (unsigned i = 0; i < 1000; i++)
+ v.push_back (i);
+
+#pragma omp for
+ for (int i : v)
+ dummy (i);
+
+#pragma omp unroll partial(U1)
+ for (T i : v)
+ dummy (i);
+
+#pragma omp unroll partial(U2) // { dg-error {partial argument needs positive constant integer expression} }
+ for (T i : v)
+ dummy (i);
+
+#pragma omp unroll partial(U3) // { dg-error {partial argument needs positive constant integer expression} }
+ for (T i : v)
+ dummy (i);
+
+#pragma omp for
+#pragma omp unroll partial(U1)
+ for (T i : v)
+ dummy (i);
+
+#pragma omp for
+#pragma omp unroll partial(U2) // { dg-error {partial argument needs positive constant integer expression} }
+ for (T i : v)
+ dummy (i);
+
+#pragma omp for
+#pragma omp unroll partial(U3) // { dg-error {partial argument needs positive constant integer expression} }
+ for (T i : v)
+ dummy (i);
+}
+
+void test () { test_template <long, 5,-2, 0> (); };
diff --git a/gcc/testsuite/g++.dg/gomp/loop-transforms/unroll-3.C b/gcc/testsuite/g++.dg/gomp/loop-transforms/unroll-3.C
new file mode 100644
index 00000000000..ae9f5500360
--- /dev/null
+++ b/gcc/testsuite/g++.dg/gomp/loop-transforms/unroll-3.C
@@ -0,0 +1,37 @@
+// { dg-do compile }
+// { dg-additional-options "-std=c++11" }
+// { dg-additional-options "-fdump-tree-omp_transform_loops -fopt-info-omp-optimized-missed" }
+// { dg-additional-options "-fdump-tree-original" }
+#include <vector>
+
+extern void dummy (int);
+
+constexpr unsigned fib (unsigned n)
+{
+ return n <= 2 ? 1 : fib (n-1) + fib (n-2);
+}
+
+void
+test1 ()
+{
+ std::vector<int> v;
+
+ for (unsigned i = 0; i < 1000; i++)
+ v.push_back (i);
+
+#pragma omp unroll partial(fib(10))
+ for (int i : v)
+ dummy (i);
+}
+
+
+// Loop should be unrolled fib(10) = 55 times
+// ! { dg-final { scan-tree-dump {#pragma omp loop_transform unroll_partial\(55\)} "original" } }
+// ! { dg-final { scan-tree-dump-not "#pragma omp" "omp_transform_loops" } }
+// ! { dg-final { scan-tree-dump-times "dummy" 55 "omp_transform_loops" } }
+
+// There should be one loop that fills the vector ...
+// ! { dg-final { scan-tree-dump-times {if \(i.*? <= .+?.+goto.+else goto.*?$} 1 "omp_transform_loops" } }
+
+// ... and one resulting from the lowering of the unrolled loop
+// ! { dg-final { scan-tree-dump-times {if \(D\.[0-9]+ < retval.+?.+goto.+else goto.*?$} 1 "omp_transform_loops" } }
diff --git a/libgomp/ChangeLog.omp b/libgomp/ChangeLog.omp
index e113e8f904e..0f84bf7a7f7 100644
--- a/libgomp/ChangeLog.omp
+++ b/libgomp/ChangeLog.omp
@@ -1,3 +1,9 @@
+2023-03-24 Frederik Harwath <frederik@codesourcery.com>
+
+ * testsuite/libgomp.c++/loop-transforms/unroll-1.C: New test.
+ * testsuite/libgomp.c++/loop-transforms/unroll-2.C: New test.
+ * testsuite/libgomp.c-c++-common/loop-transforms/unroll-1.c: New test.
+
2023-03-24 Frederik Harwath <frederik@codesourcery.com>
* testsuite/libgomp.fortran/loop-transforms/unroll-1.f90: New test.
diff --git a/libgomp/testsuite/libgomp.c++/loop-transforms/unroll-1.C b/libgomp/testsuite/libgomp.c++/loop-transforms/unroll-1.C
new file mode 100644
index 00000000000..004eef91649
--- /dev/null
+++ b/libgomp/testsuite/libgomp.c++/loop-transforms/unroll-1.C
@@ -0,0 +1,73 @@
+// { dg-additional-options "-std=c++11" }
+// { dg-additional-options "-O0" }
+
+#include <vector>
+#include <stdio.h>
+
+constexpr unsigned fib (unsigned n)
+{
+ return n <= 2 ? 1 : fib (n-1) + fib (n-2);
+}
+
+int
+test1 ()
+{
+ std::vector<int> v;
+
+ for (unsigned i = 0; i <= 9; i++)
+ v.push_back (1);
+
+ int sum = 0;
+ for (int k = 0; k < 10; k++)
+#pragma omp unroll partial(fib(3))
+ for (int i : v) {
+ for (int j = 8; j != -2; --j)
+ sum = sum + i;
+ }
+
+ return sum;
+}
+
+int
+test2 ()
+{
+ std::vector<int> v;
+
+ for (unsigned i = 0; i <= 10; i++)
+ v.push_back (i);
+
+ int sum = 0;
+#pragma omp parallel for reduction(+:sum)
+ for (int k = 0; k < 10; k++)
+#pragma omp unroll
+#pragma omp unroll partial(fib(4))
+ for (int i : v)
+ {
+ #pragma omp unroll full
+ for (int j = 8; j != -2; --j)
+ sum = sum + i;
+ }
+
+ return sum;
+}
+
+int
+main ()
+{
+ int result = test1 ();
+
+ if (result != 1000)
+ {
+ fprintf (stderr, "Wrong result: %d\n", result);
+ __builtin_abort ();
+ }
+
+ result = test2 ();
+ if (result != 5500)
+ {
+ fprintf (stderr, "Wrong result: %d\n", result);
+ __builtin_abort ();
+ }
+
+ return 0;
+}
diff --git a/libgomp/testsuite/libgomp.c++/loop-transforms/unroll-2.C b/libgomp/testsuite/libgomp.c++/loop-transforms/unroll-2.C
new file mode 100644
index 00000000000..90d2775c95b
--- /dev/null
+++ b/libgomp/testsuite/libgomp.c++/loop-transforms/unroll-2.C
@@ -0,0 +1,34 @@
+// { dg-do run }
+// { dg-additional-options "-std=c++11" }
+#include <vector>
+#include <iostream>
+
+int
+main ()
+{
+ std::vector<std::vector<int>> v;
+ std::vector<int> w;
+
+ for (unsigned i = 0; i < 10; i++)
+ {
+ std::vector<int> u;
+ for (unsigned j = 0; j < 10; j++)
+ u.push_back (j);
+ v.push_back (u);
+ }
+
+#pragma omp for
+#pragma omp unroll partial(7)
+ for (auto u : v)
+ for (int x : u)
+ w.push_back (x);
+
+ std::size_t l = w.size ();
+ for (std::size_t i = 0; i < l; i++)
+ {
+ if (w[i] != i % 10)
+ __builtin_abort ();
+ }
+
+ return 0;
+}
diff --git a/libgomp/testsuite/libgomp.c-c++-common/loop-transforms/unroll-1.c b/libgomp/testsuite/libgomp.c-c++-common/loop-transforms/unroll-1.c
new file mode 100644
index 00000000000..2ac0fff16af
--- /dev/null
+++ b/libgomp/testsuite/libgomp.c-c++-common/loop-transforms/unroll-1.c
@@ -0,0 +1,76 @@
+#include <stdio.h>
+
+int compute_sum1 ()
+{
+ int sum = 0;
+ int i,j;
+#pragma omp parallel for reduction(+:sum) lastprivate(j)
+#pragma omp unroll partial
+ for (i = 3; i < 10; ++i)
+ for (j = -2; j < 7; ++j)
+ sum++;
+
+ if (j != 7)
+ __builtin_abort;
+
+ return sum;
+}
+
+int compute_sum2()
+{
+ int sum = 0;
+ int i,j;
+#pragma omp parallel for reduction(+:sum) lastprivate(j)
+#pragma omp unroll partial(5)
+ for (i = 3; i < 10; ++i)
+ for (j = -2; j < 7; ++j)
+ sum++;
+
+ if (j != 7)
+ __builtin_abort;
+
+ return sum;
+}
+
+int compute_sum3()
+{
+ int sum = 0;
+ int i,j;
+#pragma omp parallel for reduction(+:sum)
+#pragma omp unroll partial(1)
+ for (i = 3; i < 10; ++i)
+ for (j = -2; j < 7; ++j)
+ sum++;
+
+ if (j != 7)
+ __builtin_abort;
+
+ return sum;
+}
+
+int main ()
+{
+ int result;
+ result = compute_sum1 ();
+ if (result != 7 * 9)
+ {
+ fprintf (stderr, "%d: Wrong result %d\n", __LINE__, result);
+ __builtin_abort ();
+ }
+
+ result = compute_sum2 ();
+ if (result != 7 * 9)
+ {
+ fprintf (stderr, "%d: Wrong result %d\n", __LINE__, result);
+ __builtin_abort ();
+ }
+
+ result = compute_sum3 ();
+ if (result != 7 * 9)
+ {
+ fprintf (stderr, "%d: Wrong result %d\n", __LINE__, result);
+ __builtin_abort ();
+ }
+
+ return 0;
+}
^ permalink raw reply [flat|nested] only message in thread
only message in thread, other threads:[~2023-05-19 16:54 UTC | newest]
Thread overview: (only message) (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2023-05-19 16:54 [gcc/devel/omp/gcc-13] openmp: Add C/C++ support for "omp unroll" directive Kwok Yeung
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for read-only IMAP folder(s) and NNTP newsgroup(s).