]> git.ipfire.org Git - thirdparty/gcc.git/commitdiff
openmp: Add C/C++ support for "omp unroll" directive
authorFrederik Harwath <frederik@codesourcery.com>
Fri, 24 Mar 2023 17:14:23 +0000 (18:14 +0100)
committerFrederik Harwath <frederik@codesourcery.com>
Mon, 27 Mar 2023 10:44:11 +0000 (12:44 +0200)
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.

24 files changed:
gcc/c-family/c-gimplify.cc
gcc/c-family/c-omp.cc
gcc/c-family/c-pragma.cc
gcc/c-family/c-pragma.h
gcc/c/c-parser.cc
gcc/c/c-typeck.cc
gcc/cp/cp-gimplify.cc
gcc/cp/parser.cc
gcc/cp/pt.cc
gcc/cp/semantics.cc
gcc/testsuite/c-c++-common/gomp/loop-transforms/unroll-1.c [new file with mode: 0644]
gcc/testsuite/c-c++-common/gomp/loop-transforms/unroll-2.c [new file with mode: 0644]
gcc/testsuite/c-c++-common/gomp/loop-transforms/unroll-3.c [new file with mode: 0644]
gcc/testsuite/c-c++-common/gomp/loop-transforms/unroll-4.c [new file with mode: 0644]
gcc/testsuite/c-c++-common/gomp/loop-transforms/unroll-5.c [new file with mode: 0644]
gcc/testsuite/c-c++-common/gomp/loop-transforms/unroll-6.c [new file with mode: 0644]
gcc/testsuite/c-c++-common/gomp/loop-transforms/unroll-7.c [new file with mode: 0644]
gcc/testsuite/c-c++-common/gomp/loop-transforms/unroll-simd-1.c [new file with mode: 0644]
gcc/testsuite/g++.dg/gomp/loop-transforms/unroll-1.C [new file with mode: 0644]
gcc/testsuite/g++.dg/gomp/loop-transforms/unroll-2.C [new file with mode: 0644]
gcc/testsuite/g++.dg/gomp/loop-transforms/unroll-3.C [new file with mode: 0644]
libgomp/testsuite/libgomp.c++/loop-transforms/unroll-1.C [new file with mode: 0644]
libgomp/testsuite/libgomp.c++/loop-transforms/unroll-2.C [new file with mode: 0644]
libgomp/testsuite/libgomp.c-c++-common/loop-transforms/unroll-1.c [new file with mode: 0644]

index 9fe035f21e99beccc4fa5e67abee35fa6da665c7..0970705ef18e751737d665bf34244b8151692194 100644 (file)
@@ -513,6 +513,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;
index 3e95e77d4872abb547bfda038d5ac60665e05033..440e3b7bd6f9f3b3f752277ed213dc42438d3fb2 100644 (file)
@@ -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
index b0c0f9bb896e4a9cf6a0a08280b998b88f4fa813..542b438953264b4e9ff61110104f8582b870f4f9 100644 (file)
@@ -1399,6 +1399,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
index f56acf485743d9db617f121da7bc8b0ae436dd94..8e26feffd40486d0c560f2e3a08f08348098fee3 100644 (file)
@@ -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,
index 25693fc3708fab4d530bd84cd79921e1b4c67de8..e32e6a0d6aaf9715e41910d10f6dea9cd649e38f 100644 (file)
@@ -12850,6 +12850,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))
@@ -12924,6 +12926,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
@@ -13020,12 +13024,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
@@ -17094,6 +17101,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 ) */
 
@@ -17692,6 +17758,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;
@@ -19782,6 +19856,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.
@@ -19841,6 +19917,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");
@@ -23559,6 +23642,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  */
 
@@ -24311,6 +24464,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:
@@ -24404,6 +24558,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 ();
     }
index 9dadbc1743bc4cf76fe81f4b538b74c72703129d..9454e9d4bf46dedb477feb0d4e205f6490dfa3f8 100644 (file)
@@ -15659,6 +15659,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)
index c0a79e20749b3e2009ce904e840d179b9adf0bac..5c6d0db7deb7847a54e9074bad61a9dc11480f2b 100644 (file)
@@ -614,6 +614,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;
 
@@ -1045,6 +1046,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);
@@ -1764,6 +1766,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.  */
index 56d563073c63bcb38e268c9d8f6969a5cd42cec3..c1e0a4f0b9e104ed2b875f7fd6a498ac41cd3b37 100644 (file)
@@ -36395,6 +36395,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))
@@ -36469,6 +36471,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)
@@ -36561,12 +36565,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:
@@ -38684,6 +38691,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 )  */
@@ -40922,6 +40979,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;
@@ -42939,6 +43005,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
@@ -42996,6 +43064,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;
@@ -45060,6 +45137,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
 */
@@ -48578,6 +48728,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 ();
     }
@@ -49208,6 +49361,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)
index 2fb0f361f888fbf6a046ab29a281ed0fbdcb7302..2e4fa7e795379d1e99ea0a5e89b2f112cee029c9 100644 (file)
@@ -17832,6 +17832,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, /*integral_constant_expression_p=*/false);
@@ -17920,6 +17921,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 ();
@@ -19147,6 +19150,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;
index 2b3504d10c21ac72c27baf4e6f85661b039e03c6..fa79cb49f61a144a4892efbd2e2c520c1a1848d0 100644 (file)
@@ -6799,6 +6799,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);
@@ -8933,6 +8934,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/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 (file)
index 0000000..d496dc2
--- /dev/null
@@ -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 (file)
index 0000000..8f7c308
--- /dev/null
@@ -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 (file)
index 0000000..7ace565
--- /dev/null
@@ -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 (file)
index 0000000..5e473a0
--- /dev/null
@@ -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 (file)
index 0000000..9d5101b
--- /dev/null
@@ -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 (file)
index 0000000..ee2d000
--- /dev/null
@@ -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 (file)
index 0000000..0458cb0
--- /dev/null
@@ -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 (file)
index 0000000..1cd4d6e
--- /dev/null
@@ -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 (file)
index 0000000..cba37c8
--- /dev/null
@@ -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 (file)
index 0000000..f606f3d
--- /dev/null
@@ -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 (file)
index 0000000..ae9f550
--- /dev/null
@@ -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/testsuite/libgomp.c++/loop-transforms/unroll-1.C b/libgomp/testsuite/libgomp.c++/loop-transforms/unroll-1.C
new file mode 100644 (file)
index 0000000..004eef9
--- /dev/null
@@ -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 (file)
index 0000000..90d2775
--- /dev/null
@@ -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 (file)
index 0000000..2ac0fff
--- /dev/null
@@ -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;
+}