openmp: Add support for omp attributes section and scan directives
authorJakub Jelinek <jakub@redhat.com>
Mon, 26 Jul 2021 07:13:47 +0000 (09:13 +0200)
committerJakub Jelinek <jakub@redhat.com>
Mon, 26 Jul 2021 07:13:47 +0000 (09:13 +0200)
This patch adds support for expressing the section and scan directives
using the attribute syntax and additionally fixes some bugs in the attribute
syntax directive handling.
For now it requires that the scan and section directives appear as the only
attribute, not combined with other OpenMP or non-OpenMP attributes on the same
statement.

2021-07-26  Jakub Jelinek  <jakub@redhat.com>

* parser.h (struct cp_lexer): Add orphan_p member.
* parser.c (cp_parser_statement): Don't change in_omp_attribute_pragma
upon restart from CPP_PRAGMA handling.  Fix up condition when a lexer
should be destroyed and adjust saved_tokens if it records tokens from
the to be destroyed lexer.
(cp_parser_omp_section_scan): New function.
(cp_parser_omp_scan_loop_body): Use it.  If
parser->lexer->in_omp_attribute_pragma, allow optional comma
after scan.
(cp_parser_omp_sections_scope): Use cp_parser_omp_section_scan.

* g++.dg/gomp/attrs-1.C: Use attribute syntax even for section
and scan directives.
* g++.dg/gomp/attrs-2.C: Likewise.
* g++.dg/gomp/attrs-6.C: New test.
* g++.dg/gomp/attrs-7.C: New test.
* g++.dg/gomp/attrs-8.C: New test.

gcc/cp/parser.c
gcc/cp/parser.h
gcc/testsuite/g++.dg/gomp/attrs-1.C
gcc/testsuite/g++.dg/gomp/attrs-2.C
gcc/testsuite/g++.dg/gomp/attrs-6.C [new file with mode: 0644]
gcc/testsuite/g++.dg/gomp/attrs-7.C [new file with mode: 0644]
gcc/testsuite/g++.dg/gomp/attrs-8.C [new file with mode: 0644]

index 18905cf..976e2e7 100644 (file)
@@ -11901,10 +11901,9 @@ cp_parser_statement (cp_parser* parser, tree in_statement_expr,
   tree statement, std_attrs = NULL_TREE;
   cp_token *token;
   location_t statement_location, attrs_loc;
-  bool in_omp_attribute_pragma;
+  bool in_omp_attribute_pragma = parser->lexer->in_omp_attribute_pragma;
 
  restart:
-  in_omp_attribute_pragma = parser->lexer->in_omp_attribute_pragma;
   if (if_p != NULL)
     *if_p = false;
   /* There is no statement yet.  */
@@ -11951,6 +11950,7 @@ cp_parser_statement (cp_parser* parser, tree in_statement_expr,
             the statement.  */
          cp_parser_label_for_labeled_statement (parser, std_attrs);
          in_compound = false;
+         in_omp_attribute_pragma = parser->lexer->in_omp_attribute_pragma;
          goto restart;
 
        case RID_IF:
@@ -12034,6 +12034,7 @@ cp_parser_statement (cp_parser* parser, tree in_statement_expr,
 
          cp_parser_label_for_labeled_statement (parser, std_attrs);
          in_compound = false;
+         in_omp_attribute_pragma = parser->lexer->in_omp_attribute_pragma;
          goto restart;
        }
     }
@@ -12058,13 +12059,28 @@ cp_parser_statement (cp_parser* parser, tree in_statement_expr,
        cp_parser_pragma (parser, pragma_compound, if_p);
       else if (!cp_parser_pragma (parser, pragma_stmt, if_p))
        do_restart = true;
-      if (lexer->in_omp_attribute_pragma && !in_omp_attribute_pragma)
+      if (parser->lexer != lexer
+         && lexer->in_omp_attribute_pragma
+         && (!in_omp_attribute_pragma || lexer->orphan_p))
        {
-         gcc_assert (parser->lexer != lexer);
+         if (saved_tokens.lexer == lexer)
+           {
+             if (saved_tokens.commit)
+               cp_lexer_commit_tokens (lexer);
+             gcc_assert (lexer->saved_tokens.length () == saved_tokens.len);
+             saved_tokens.lexer = parser->lexer;
+             saved_tokens.commit = false;
+             saved_tokens.len = parser->lexer->saved_tokens.length ();
+           }
          cp_lexer_destroy (lexer);
+         lexer = parser->lexer;
        }
       if (do_restart)
        goto restart;
+      if (parser->lexer == lexer
+         && lexer->in_omp_attribute_pragma
+         && !in_omp_attribute_pragma)
+       parser->lexer->orphan_p = true;
       return;
     }
   else if (token->type == CPP_EOF)
@@ -40775,6 +40791,77 @@ cp_finish_omp_range_for (tree orig, tree begin)
     cp_finish_decomp (decl, decomp_first_name, decomp_cnt);
 }
 
+/* Return true if next tokens contain a standard attribute that contains
+   omp::directive (DIRECTIVE).  */
+
+static bool
+cp_parser_omp_section_scan (cp_parser *parser, const char *directive,
+                           bool tentative)
+{
+  size_t n = cp_parser_skip_attributes_opt (parser, 1), i;
+  if (n < 10)
+    return false;
+  for (i = 5; i < n - 4; i++)
+    if (cp_lexer_nth_token_is (parser->lexer, i, CPP_NAME)
+       && cp_lexer_nth_token_is (parser->lexer, i + 1, CPP_OPEN_PAREN)
+       && cp_lexer_nth_token_is (parser->lexer, i + 2, CPP_NAME))
+      {
+       tree first = cp_lexer_peek_nth_token (parser->lexer, i)->u.value;
+       tree second = cp_lexer_peek_nth_token (parser->lexer, i + 2)->u.value;
+       if (strcmp (IDENTIFIER_POINTER (first), "directive"))
+         continue;
+       if (strcmp (IDENTIFIER_POINTER (second), directive) == 0)
+         break;
+      }
+  if (i == n - 4)
+    return false;
+  cp_parser_parse_tentatively (parser);
+  location_t first_loc = cp_lexer_peek_token (parser->lexer)->location;
+  location_t last_loc
+    = cp_lexer_peek_nth_token (parser->lexer, n - 1)->location;
+  location_t middle_loc = UNKNOWN_LOCATION;
+  tree std_attrs = cp_parser_std_attribute_spec_seq (parser);
+  int cnt = 0;
+  bool seen = false;
+  for (tree attr = std_attrs; attr; attr = TREE_CHAIN (attr))
+    if (get_attribute_namespace (attr) == omp_identifier
+       && is_attribute_p ("directive", get_attribute_name (attr)))
+      {
+       for (tree a = TREE_VALUE (attr); a; a = TREE_CHAIN (a))
+         {
+           tree d = TREE_VALUE (a);
+           gcc_assert (TREE_CODE (d) == DEFERRED_PARSE);
+           cp_token *first = DEFPARSE_TOKENS (d)->first;
+           cnt++;
+           if (first->type == CPP_NAME
+               && strcmp (IDENTIFIER_POINTER (first->u.value),
+                          directive) == 0)
+             {
+               seen = true;
+               if (middle_loc == UNKNOWN_LOCATION)
+                 middle_loc = first->location;
+             }
+         }
+      }
+  if (!seen || tentative)
+    {
+      cp_parser_abort_tentative_parse (parser);
+      return seen;
+    }
+  if (cnt != 1 || TREE_CHAIN (std_attrs))
+    {
+      error_at (make_location (first_loc, last_loc, middle_loc),
+               "%<[[omp::directive(%s)]]%> must be the only specified "
+               "attribute on a statement", directive);
+      cp_parser_abort_tentative_parse (parser);
+      return false;
+    }
+  if (!cp_parser_parse_definitely (parser))
+    return false;
+  cp_parser_handle_statement_omp_attributes (parser, std_attrs);
+  return true;
+}
+
 /* OpenMP 5.0:
 
    scan-loop-body:
@@ -40793,6 +40880,7 @@ cp_parser_omp_scan_loop_body (cp_parser *parser)
   substmt = build2 (OMP_SCAN, void_type_node, substmt, NULL_TREE);
   add_stmt (substmt);
 
+  cp_parser_omp_section_scan (parser, "scan", false);
   cp_token *tok = cp_lexer_peek_token (parser->lexer);
   if (cp_parser_pragma_kind (tok) == PRAGMA_OMP_SCAN)
     {
@@ -40800,6 +40888,10 @@ cp_parser_omp_scan_loop_body (cp_parser *parser)
 
       cp_lexer_consume_token (parser->lexer);
 
+      if (parser->lexer->in_omp_attribute_pragma
+         && cp_lexer_next_token_is (parser->lexer, CPP_COMMA))
+       cp_lexer_consume_token (parser->lexer);
+
       if (cp_lexer_next_token_is (parser->lexer, CPP_NAME))
        {
          tree id = cp_lexer_peek_token (parser->lexer)->u.value;
@@ -41623,7 +41715,8 @@ cp_parser_omp_sections_scope (cp_parser *parser)
   stmt = push_stmt_list ();
 
   if (cp_parser_pragma_kind (cp_lexer_peek_token (parser->lexer))
-      != PRAGMA_OMP_SECTION)
+      != PRAGMA_OMP_SECTION
+      && !cp_parser_omp_section_scan (parser, "section", true))
     {
       substmt = cp_parser_omp_structured_block (parser, NULL, false);
       substmt = build1 (OMP_SECTION, void_type_node, substmt);
@@ -41638,6 +41731,8 @@ cp_parser_omp_sections_scope (cp_parser *parser)
       if (tok->type == CPP_EOF)
        break;
 
+      if (cp_parser_omp_section_scan (parser, "section", false))
+       tok = cp_lexer_peek_token (parser->lexer);
       if (cp_parser_pragma_kind (tok) == PRAGMA_OMP_SECTION)
        {
          cp_lexer_consume_token (parser->lexer);
index 6fdd214..e62742d 100644 (file)
@@ -117,6 +117,10 @@ struct GTY (()) cp_lexer {
   /* True if we're in the context of OpenMP directives written as C++11
      attributes turned into pragma.  */
   bool in_omp_attribute_pragma;
+
+  /* True for in_omp_attribute_pragma lexer that should be destroyed
+     when it is no longer in use.  */
+  bool orphan_p;
 };
 
 
index c2734a1..6bbdcac 100644 (file)
@@ -146,17 +146,17 @@ bar (int d, int m, int i1, int i2, int i3, int p, int *idp, int s,
     private (p) firstprivate (f) if (parallel: i2) default(shared) shared(s) copyin(t) reduction(+:r) num_threads (nth) proc_bind(spread)
     lastprivate (l) allocate (f))]]
   {
-    #pragma omp section
+    [[omp::directive (section)]]
     {}
-    #pragma omp section
+    [[omp::sequence (omp::directive (section))]]
     {}
   }
   [[omp::directive (sections private (p) firstprivate (f) reduction(+:r) lastprivate (l) allocate (f) nowait)]]
   {
     ;
-    #pragma omp section
+    [[omp::sequence (sequence (directive (section)))]]
     ;
-    #pragma omp section
+    [[omp::directive (section)]]
     {}
   }
   [[omp::directive (barrier)]];
@@ -539,14 +539,14 @@ garply (int a, int *c, int *d, int *e, int *f)
   for (i = 0; i < 64; i++)
     {
       d[i] = a;
-      #pragma omp scan exclusive (a)
+      [[omp::directive (scan exclusive (a))]]
       a += c[i];
     }
   [[omp::directive (simd reduction (inscan, +: a))]]
   for (i = 0; i < 64; i++)
     {
       a += c[i];
-      #pragma omp scan inclusive (a)
+      [[omp::sequence (omp::sequence (omp::directive (scan inclusive (a))))]]
       d[i] = a;
     }
   return a;
index 1eb6263..189dc6b 100644 (file)
@@ -146,17 +146,17 @@ bar (int d, int m, int i1, int i2, int i3, int p, int *idp, int s,
     private (p),firstprivate (f),if (parallel: i2),default(shared),shared(s),copyin(t),reduction(+:r),num_threads (nth),proc_bind(spread),
     lastprivate (l),allocate (f))]]
   {
-    #pragma omp section
+    [[using omp:directive (section)]]
     {}
-    #pragma omp section
+    [[omp::sequence (omp::directive (section))]]
     {}
   }
   [[omp::directive (sections, private (p),firstprivate (f),reduction(+:r),lastprivate (l),allocate (f),nowait)]]
   {
     ;
-    #pragma omp section
+    [[omp::sequence (sequence (directive (section)))]]
     ;
-    #pragma omp section
+    [[omp::directive (section)]]
     {}
   }
   [[omp::directive (barrier)]];
@@ -539,14 +539,14 @@ garply (int a, int *c, int *d, int *e, int *f)
   for (i = 0; i < 64; i++)
     {
       d[i] = a;
-      #pragma omp scan exclusive (a)
+      [[omp::directive (scan, exclusive (a))]]
       a += c[i];
     }
   [[omp::directive (simd, reduction (inscan, +: a))]]
   for (i = 0; i < 64; i++)
     {
       a += c[i];
-      #pragma omp scan inclusive (a)
+      [[using omp : sequence (sequence (directive (scan inclusive (a))))]]
       d[i] = a;
     }
   return a;
diff --git a/gcc/testsuite/g++.dg/gomp/attrs-6.C b/gcc/testsuite/g++.dg/gomp/attrs-6.C
new file mode 100644 (file)
index 0000000..30b47e1
--- /dev/null
@@ -0,0 +1,50 @@
+// { dg-do compile { target c++11 } }
+
+void
+foo ()
+{
+  int a[10] = {};
+  #pragma omp parallel sections
+  {
+    #pragma omp section
+    a[0]++;
+    [[omp::directive (section)]] {
+    a[1]++;
+    } [[omp::directive (section)]]
+    a[2]++;
+    #pragma omp section
+    { a[3]++; }
+  }
+  [[omp::directive (parallel sections)]]
+  {
+    #pragma omp section
+    a[0]++;
+    [[omp::directive (section)]] {
+    a[1]++;
+    } [[omp::directive (section)]]
+    a[2]++;
+    #pragma omp section
+    { a[3]++; }
+  }
+}
+
+int
+bar (int a, int *c, int *d, int *e, int *f)
+{
+  int i;
+  #pragma omp simd reduction (inscan, +: a)
+  for (i = 0; i < 64; i++)
+    {
+      d[i] = a;
+      [[omp::directive (scan, exclusive (a))]]
+      a += c[i];
+    }
+  [[omp::directive (simd reduction (inscan, +: a))]]
+  for (i = 0; i < 64; i++)
+    {
+      a += c[i];
+      #pragma omp scan inclusive (a)
+      d[i] = a;
+    }
+  return a;
+}
diff --git a/gcc/testsuite/g++.dg/gomp/attrs-7.C b/gcc/testsuite/g++.dg/gomp/attrs-7.C
new file mode 100644 (file)
index 0000000..598c32a
--- /dev/null
@@ -0,0 +1,64 @@
+// { dg-do compile { target c++11 } }
+
+void
+foo ()
+{
+
+  [[omp::directive (parallel sections)]]
+  {
+    [[omp::directive (parallel)]];
+    [[omp::sequence (directive (section), directive (flush))]];                // { dg-error "must be the only specified attribute on a statement" }
+                                                                       // { dg-error "#pragma omp section" "" { target *-*-* } .-1 }
+                                                                       // { dg-error "#pragma omp flush" "" { target *-*-* } .-2 }
+    [[omp::sequence (directive (flush), omp::directive (section))]];   // { dg-error "must be the only specified attribute on a statement" }
+                                                                       // { dg-error "#pragma omp section" "" { target *-*-* } .-1 }
+                                                                       // { dg-error "#pragma omp flush" "" { target *-*-* } .-2 }
+    [[gnu::cold, omp::directive (section)]];                           // { dg-error "must be the only specified attribute on a statement" }
+                                                                       // { dg-error "#pragma omp section" "" { target *-*-* } .-1 }
+    [[omp::directive (section)]] [[gnu::cold]];                                // { dg-error "must be the only specified attribute on a statement" }
+                                                                       // { dg-error "#pragma omp section" "" { target *-*-* } .-1 }
+    [[omp::directive (section foo)]];                                  // { dg-error "expected end of line before 'foo'" }
+  }
+}
+
+int
+bar (int a, int *c, int *d, int *e, int *f)
+{
+  int i;
+  [[omp::directive (parallel for reduction (inscan, +: a))]]                           // { dg-error "'a' specified in 'inscan' 'reduction' clause but not in 'scan' directive clause" }
+  for (i = 0; i < 64; i++)
+    {
+      d[i] = a;
+      [[omp::sequence (omp::directive (parallel), omp::directive (scan, exclusive (a)))]]      // { dg-error "must be the only specified attribute on a statement" }
+      a += c[i];                                                                       // { dg-error "#pragma omp scan" "" { target *-*-* } .-1 }
+    }
+  [[omp::directive (parallel for reduction (inscan, +: a))]]                           // { dg-error "'a' specified in 'inscan' 'reduction' clause but not in 'scan' directive clause" }
+  for (i = 0; i < 64; i++)
+    {
+      a += c[i];
+      [[omp::sequence (directive (scan inclusive (a)), directive (critical))]]         // { dg-error "must be the only specified attribute on a statement" }
+      d[i] = a;                                                                                // { dg-error "#pragma omp scan" "" { target *-*-* } .-1 }
+    }
+  [[omp::directive (parallel for reduction (inscan, +: a))]]                           // { dg-error "'a' specified in 'inscan' 'reduction' clause but not in 'scan' directive clause" }
+  for (i = 0; i < 64; i++)
+    {
+      d[i] = a;
+      [[gnu::cold]] [[omp::directive (scan, exclusive (a))]]                           // { dg-error "must be the only specified attribute on a statement" }
+      a += c[i];                                                                       // { dg-error "#pragma omp scan" "" { target *-*-* } .-1 }
+    }
+  [[omp::directive (parallel for reduction (inscan, +: a))]]                           // { dg-error "'a' specified in 'inscan' 'reduction' clause but not in 'scan' directive clause" }
+  for (i = 0; i < 64; i++)
+    {
+      d[i] = a;
+      [[omp::directive (scan, exclusive (a)), gnu::cold]]                              // { dg-error "must be the only specified attribute on a statement" }
+      a += c[i];                                                                       // { dg-error "#pragma omp scan" "" { target *-*-* } .-1 }
+    }
+  [[omp::directive (parallel for reduction (inscan, +: a))]]                           // { dg-error "'a' specified in 'inscan' 'reduction' clause but not in 'scan' directive clause" }
+  for (i = 0; i < 64; i++)
+    {
+      d[i] = a;
+      [[omp::directive (scan)]]                                                                // { dg-error "expected 'inclusive' or 'exclusive' clause before end of line" }
+      a += c[i];
+    }
+  return a;
+}
diff --git a/gcc/testsuite/g++.dg/gomp/attrs-8.C b/gcc/testsuite/g++.dg/gomp/attrs-8.C
new file mode 100644 (file)
index 0000000..30cfe99
--- /dev/null
@@ -0,0 +1,10 @@
+// { dg-do compile { target c++11 } }
+
+void
+foo ()
+{
+  // Unsure if this shouldn't be invalid, whether we shouldn't require
+  // that each standalone directive sits on its own empty statement.
+  [[omp::sequence (omp::directive (barrier), omp::directive (barrier))]];
+  [[omp::sequence (omp::directive (taskyield), omp::directive (taskwait))]];
+}