[clang] 721558a - [OpenACC] Implement 'cache' construct parsing (#74324)

via cfe-commits cfe-commits at lists.llvm.org
Wed Dec 6 06:32:49 PST 2023


Author: Erich Keane
Date: 2023-12-06T06:32:45-08:00
New Revision: 721558ae40ebc996b6a8d51e3d5147f5a8c7d008

URL: https://github.com/llvm/llvm-project/commit/721558ae40ebc996b6a8d51e3d5147f5a8c7d008
DIFF: https://github.com/llvm/llvm-project/commit/721558ae40ebc996b6a8d51e3d5147f5a8c7d008.diff

LOG: [OpenACC] Implement 'cache' construct parsing (#74324)

The 'cache' construct takes a list of 'vars', which are array-section
style definitions. This patch implements the parsing, leaving the lower
bound and length of the bound as expressions, so that we can validate
they are the correct 'thing' in sema.

Added: 
    clang/test/ParserOpenACC/parse-cache-construct.c
    clang/test/ParserOpenACC/parse-cache-construct.cpp

Modified: 
    clang/include/clang/Basic/OpenACCKinds.h
    clang/include/clang/Parse/Parser.h
    clang/lib/Parse/ParseOpenACC.cpp

Removed: 
    


################################################################################
diff  --git a/clang/include/clang/Basic/OpenACCKinds.h b/clang/include/clang/Basic/OpenACCKinds.h
index 1a5bf7e0e831c..449a75638b43f 100644
--- a/clang/include/clang/Basic/OpenACCKinds.h
+++ b/clang/include/clang/Basic/OpenACCKinds.h
@@ -34,7 +34,7 @@ enum class OpenACCDirectiveKind {
 
   // Misc.
   Loop,
-  // FIXME: 'cache'
+  Cache,
 
   // Combined Constructs.
   ParallelLoop,

diff  --git a/clang/include/clang/Parse/Parser.h b/clang/include/clang/Parse/Parser.h
index ca29ce46873e8..32d0b76c35b0d 100644
--- a/clang/include/clang/Parse/Parser.h
+++ b/clang/include/clang/Parse/Parser.h
@@ -3538,7 +3538,12 @@ class Parser : public CodeCompletionHandler {
 
 private:
   void ParseOpenACCDirective();
-  ExprResult ParseOpenACCRoutineName();
+  /// Helper that parses an ID Expression based on the language options.
+  ExprResult ParseOpenACCIDExpression();
+  /// Parses the variable list for the `cache` construct.
+  void ParseOpenACCCacheVarList();
+  /// Parses a single variable in a variable list for the 'cache' construct.
+  bool ParseOpenACCCacheVar();
 
 private:
   //===--------------------------------------------------------------------===//

diff  --git a/clang/lib/Parse/ParseOpenACC.cpp b/clang/lib/Parse/ParseOpenACC.cpp
index 4021d50318561..fb292f270d916 100644
--- a/clang/lib/Parse/ParseOpenACC.cpp
+++ b/clang/lib/Parse/ParseOpenACC.cpp
@@ -45,6 +45,7 @@ OpenACCDirectiveKindEx getOpenACCDirectiveKind(StringRef Name) {
           .Case("data", OpenACCDirectiveKind::Data)
           .Case("host_data", OpenACCDirectiveKind::HostData)
           .Case("loop", OpenACCDirectiveKind::Loop)
+          .Case("cache", OpenACCDirectiveKind::Cache)
           .Case("atomic", OpenACCDirectiveKind::Atomic)
           .Case("routine", OpenACCDirectiveKind::Routine)
           .Case("declare", OpenACCDirectiveKind::Declare)
@@ -88,6 +89,8 @@ bool isOpenACCDirectiveKind(OpenACCDirectiveKind Kind, StringRef Tok) {
     return Tok == "host_data";
   case OpenACCDirectiveKind::Loop:
     return Tok == "loop";
+  case OpenACCDirectiveKind::Cache:
+    return Tok == "cache";
 
   case OpenACCDirectiveKind::ParallelLoop:
   case OpenACCDirectiveKind::SerialLoop:
@@ -237,10 +240,7 @@ void ParseOpenACCClauseList(Parser &P) {
 
 } // namespace
 
-// Routine has an optional paren-wrapped name of a function in the local scope.
-// We parse the name, emitting any diagnostics
-ExprResult Parser::ParseOpenACCRoutineName() {
-
+ExprResult Parser::ParseOpenACCIDExpression() {
   ExprResult Res;
   if (getLangOpts().CPlusPlus) {
     Res = ParseCXXIdExpression(/*isAddressOfOperand=*/false);
@@ -248,8 +248,10 @@ ExprResult Parser::ParseOpenACCRoutineName() {
     // There isn't anything quite the same as ParseCXXIdExpression for C, so we
     // need to get the identifier, then call into Sema ourselves.
 
-    if (expectIdentifier())
+    if (Tok.isNot(tok::identifier)) {
+      Diag(Tok, diag::err_expected) << tok::identifier;
       return ExprError();
+    }
 
     Token FuncName = getCurToken();
     UnqualifiedId Name;
@@ -268,6 +270,86 @@ ExprResult Parser::ParseOpenACCRoutineName() {
   return getActions().CorrectDelayedTyposInExpr(Res);
 }
 
+/// OpenACC 3.3, section 2.10:
+/// A 'var' in a cache directive must be a single array element or a simple
+/// subarray.  In C and C++, a simple subarray is an array name followed by an
+/// extended array range specification in brackets, with a start and length such
+/// as:
+///
+/// arr[lower:length]
+///
+bool Parser::ParseOpenACCCacheVar() {
+  ExprResult ArrayName = ParseOpenACCIDExpression();
+  if (ArrayName.isInvalid())
+    return true;
+
+  // If the expression is invalid, just continue parsing the brackets, there
+  // is likely other useful diagnostics we can emit inside of those.
+
+  BalancedDelimiterTracker SquareBrackets(*this, tok::l_square,
+                                          tok::annot_pragma_openacc_end);
+
+  // Square brackets are required, so error here, and try to recover by moving
+  // until the next comma, or the close paren/end of pragma.
+  if (SquareBrackets.expectAndConsume()) {
+    SkipUntil(tok::comma, tok::r_paren, tok::annot_pragma_openacc_end,
+              Parser::StopBeforeMatch);
+    return true;
+  }
+
+  ExprResult Lower = getActions().CorrectDelayedTyposInExpr(ParseExpression());
+  if (Lower.isInvalid())
+    return true;
+
+  // The 'length' expression is optional, as this could be a single array
+  // element. If there is no colon, we can treat it as that.
+  if (getCurToken().is(tok::colon)) {
+    ConsumeToken();
+    ExprResult Length =
+        getActions().CorrectDelayedTyposInExpr(ParseExpression());
+    if (Length.isInvalid())
+      return true;
+  }
+
+  // Diagnose the square bracket being in the wrong place and continue.
+  return SquareBrackets.consumeClose();
+}
+
+/// OpenACC 3.3, section 2.10:
+/// In C and C++, the syntax of the cache directive is:
+///
+/// #pragma acc cache ([readonly:]var-list) new-line
+void Parser::ParseOpenACCCacheVarList() {
+  // If this is the end of the line, just return 'false' and count on the close
+  // paren diagnostic to catch the issue.
+  if (getCurToken().isAnnotation())
+    return;
+
+  // The VarList is an optional `readonly:` followed by a list of a variable
+  // specifications.  First, see if we have `readonly:`, else we back-out and
+  // treat it like the beginning of a reference to a potentially-existing
+  // `readonly` variable.
+  if (getCurToken().is(tok::identifier) &&
+      getCurToken().getIdentifierInfo()->isStr("readonly") &&
+      NextToken().is(tok::colon)) {
+    // Consume both tokens.
+    ConsumeToken();
+    ConsumeToken();
+    // FIXME: Record that this is a 'readonly' so that we can use that during
+    // Sema/AST generation.
+  }
+
+  bool FirstArray = true;
+  while (!getCurToken().isOneOf(tok::r_paren, tok::annot_pragma_openacc_end)) {
+    if (!FirstArray)
+      ExpectAndConsume(tok::comma);
+    FirstArray = false;
+    if (ParseOpenACCCacheVar())
+      SkipUntil(tok::r_paren, tok::annot_pragma_openacc_end, tok::comma,
+                StopBeforeMatch);
+  }
+}
+
 void Parser::ParseOpenACCDirective() {
   OpenACCDirectiveKind DirKind = ParseOpenACCDirectiveKind(*this);
 
@@ -289,7 +371,9 @@ void Parser::ParseOpenACCDirective() {
       T.skipToEnd();
       break;
     case OpenACCDirectiveKind::Routine: {
-      ExprResult RoutineName = ParseOpenACCRoutineName();
+      // Routine has an optional paren-wrapped name of a function in the local
+      // scope. We parse the name, emitting any diagnostics
+      ExprResult RoutineName = ParseOpenACCIDExpression();
       // If the routine name is invalid, just skip until the closing paren to
       // recover more gracefully.
       if (RoutineName.isInvalid())
@@ -298,7 +382,18 @@ void Parser::ParseOpenACCDirective() {
         T.consumeClose();
       break;
     }
+    case OpenACCDirectiveKind::Cache:
+      ParseOpenACCCacheVarList();
+      // The ParseOpenACCCacheVarList function manages to recover from failures,
+      // so we can always consume the close.
+      T.consumeClose();
+      break;
     }
+  } else if (DirKind == OpenACCDirectiveKind::Cache) {
+    // Cache's paren var-list is required, so error here if it isn't provided.
+    // We know that the consumeOpen above left the first non-paren here, so
+    // diagnose, then continue as if it was completely omitted.
+    Diag(Tok, diag::err_expected) << tok::l_paren;
   }
 
   // Parses the list of clauses, if present.

diff  --git a/clang/test/ParserOpenACC/parse-cache-construct.c b/clang/test/ParserOpenACC/parse-cache-construct.c
new file mode 100644
index 0000000000000..10976627ca95c
--- /dev/null
+++ b/clang/test/ParserOpenACC/parse-cache-construct.c
@@ -0,0 +1,166 @@
+// RUN: %clang_cc1 %s -verify -fopenacc
+
+char *getArrayPtr();
+void func() {
+  char Array[10];
+  char *ArrayPtr = getArrayPtr();
+  int *readonly;
+
+  for (int i = 0; i < 10; ++i) {
+    // expected-error at +2{{expected '('}}
+    // expected-warning at +1{{OpenACC directives not yet implemented, pragma ignored}}
+    #pragma acc cache
+  }
+
+  for (int i = 0; i < 10; ++i) {
+    // expected-error at +3{{expected '('}}
+    // expected-warning at +2{{OpenACC clause parsing not yet implemented}}
+    // expected-warning at +1{{OpenACC directives not yet implemented, pragma ignored}}
+    #pragma acc cache clause list
+  }
+
+  for (int i = 0; i < 10; ++i) {
+    // expected-warning at +1{{OpenACC directives not yet implemented, pragma ignored}}
+    #pragma acc cache()
+  }
+
+  for (int i = 0; i < 10; ++i) {
+    // expected-warning at +2{{OpenACC clause parsing not yet implemented}}
+    // expected-warning at +1{{OpenACC directives not yet implemented, pragma ignored}}
+    #pragma acc cache() clause-list
+  }
+
+  for (int i = 0; i < 10; ++i) {
+    // expected-error at +3{{expected ')'}}
+    // expected-note at +2{{to match this '('}}
+    // expected-warning at +1{{OpenACC directives not yet implemented, pragma ignored}}
+    #pragma acc cache(
+  }
+
+  for (int i = 0; i < 10; ++i) {
+    // expected-error at +4{{use of undeclared identifier 'invalid'}}
+    // expected-error at +3{{expected ')'}}
+    // expected-note at +2{{to match this '('}}
+    // expected-warning at +1{{OpenACC directives not yet implemented, pragma ignored}}
+    #pragma acc cache(invalid
+  }
+
+  for (int i = 0; i < 10; ++i) {
+    // expected-error at +4{{expected '['}}
+    // expected-error at +3{{expected ')'}}
+    // expected-note at +2{{to match this '('}}
+    // expected-warning at +1{{OpenACC directives not yet implemented, pragma ignored}}
+    #pragma acc cache(ArrayPtr
+  }
+
+  for (int i = 0; i < 10; ++i) {
+    // expected-error at +2{{use of undeclared identifier 'invalid'}}
+    // expected-warning at +1{{OpenACC directives not yet implemented, pragma ignored}}
+    #pragma acc cache(invalid)
+  }
+
+  for (int i = 0; i < 10; ++i) {
+    // expected-error at +2{{expected '['}}
+    // expected-warning at +1{{OpenACC directives not yet implemented, pragma ignored}}
+    #pragma acc cache(ArrayPtr)
+  }
+
+  for (int i = 0; i < 10; ++i) {
+    // expected-error at +4{{expected expression}}
+    // expected-error at +3{{expected ')'}}
+    // expected-note at +2{{to match this '('}}
+    // expected-warning at +1{{OpenACC directives not yet implemented, pragma ignored}}
+    #pragma acc cache(ArrayPtr[
+  }
+
+  for (int i = 0; i < 10; ++i) {
+    // expected-error at +2{{expected expression}}
+    // expected-warning at +1{{OpenACC directives not yet implemented, pragma ignored}}
+    #pragma acc cache(ArrayPtr[, 5)
+  }
+
+  for (int i = 0; i < 10; ++i) {
+    // expected-error at +2{{expected expression}}
+    // expected-warning at +1{{OpenACC directives not yet implemented, pragma ignored}}
+    #pragma acc cache(Array[)
+  }
+
+  for (int i = 0; i < 10; ++i) {
+    // expected-warning at +1{{OpenACC directives not yet implemented, pragma ignored}}
+    #pragma acc cache(Array[*readonly])
+  }
+
+  for (int i = 0; i < 10; ++i) {
+    // expected-error at +4{{expected expression}}
+    // expected-error at +3{{expected ')'}}
+    // expected-note at +2{{to match this '('}}
+    // expected-warning at +1{{OpenACC directives not yet implemented, pragma ignored}}
+    #pragma acc cache(Array[*readonly:
+  }
+
+  for (int i = 0; i < 10; ++i) {
+    // expected-error at +2{{expected '['}}
+    // expected-warning at +1{{OpenACC directives not yet implemented, pragma ignored}}
+    #pragma acc cache(readonly)
+  }
+
+  for (int i = 0; i < 10; ++i) {
+    // expected-error at +2{{expected '['}}
+    // expected-warning at +1{{OpenACC directives not yet implemented, pragma ignored}}
+    #pragma acc cache(readonly:ArrayPtr)
+  }
+
+  for (int i = 0; i < 10; ++i) {
+    // expected-error at +2{{expected expression}}
+    // expected-warning at +1{{OpenACC directives not yet implemented, pragma ignored}}
+    #pragma acc cache(readonly:ArrayPtr[5:])
+  }
+
+  for (int i = 0; i < 10; ++i) {
+    // expected-warning at +1{{OpenACC directives not yet implemented, pragma ignored}}
+    #pragma acc cache(readonly:ArrayPtr[5:*readonly])
+  }
+
+  for (int i = 0; i < 10; ++i) {
+    // expected-error at +2{{expected '['}}
+    // expected-warning at +1{{OpenACC directives not yet implemented, pragma ignored}}
+    #pragma acc cache(readonly:ArrayPtr[5:*readonly], Array)
+  }
+
+  for (int i = 0; i < 10; ++i) {
+    // expected-warning at +1{{OpenACC directives not yet implemented, pragma ignored}}
+    #pragma acc cache(readonly:ArrayPtr[5:*readonly], Array[*readonly:3])
+  }
+
+  for (int i = 0; i < 10; ++i) {
+    // expected-warning at +1{{OpenACC directives not yet implemented, pragma ignored}}
+    #pragma acc cache(readonly:ArrayPtr[5 + i:*readonly], Array[*readonly + i:3])
+  }
+
+  for (int i = 0; i < 10; ++i) {
+    // expected-error at +4{{expected identifier}}
+    // expected-error at +3{{expected ')'}}
+    // expected-note at +2{{to match this '('}}
+    // expected-warning at +1{{OpenACC directives not yet implemented, pragma ignored}}
+    #pragma acc cache(readonly:ArrayPtr[5:*readonly],
+  }
+
+  for (int i = 0; i < 10; ++i) {
+    // expected-error at +2{{expected identifier}}
+    // expected-warning at +1{{OpenACC directives not yet implemented, pragma ignored}}
+    #pragma acc cache(readonly:ArrayPtr[5:*readonly],)
+  }
+
+  for (int i = 0; i < 10; ++i) {
+    // expected-warning at +2{{left operand of comma operator has no effect}}
+    // expected-warning at +1{{OpenACC directives not yet implemented, pragma ignored}}
+    #pragma acc cache(readonly:ArrayPtr[5,6:*readonly])
+  }
+
+  for (int i = 0; i < 10; ++i) {
+    // expected-warning at +2{{left operand of comma operator has no effect}}
+    // expected-warning at +1{{OpenACC directives not yet implemented, pragma ignored}}
+    #pragma acc cache(readonly:ArrayPtr[5:3, *readonly], ArrayPtr[0])
+  }
+
+}

diff  --git a/clang/test/ParserOpenACC/parse-cache-construct.cpp b/clang/test/ParserOpenACC/parse-cache-construct.cpp
new file mode 100644
index 0000000000000..3b2230cabae32
--- /dev/null
+++ b/clang/test/ParserOpenACC/parse-cache-construct.cpp
@@ -0,0 +1,51 @@
+// RUN: %clang_cc1 %s -verify -fopenacc
+
+namespace NS {
+  static char* NSArray;// expected-note{{declared here}}
+  static int NSInt;// expected-note 2{{declared here}}
+}
+char *getArrayPtr();
+template<typename T, int I>
+void func() {
+  char *ArrayPtr = getArrayPtr();
+  for (int i = 0; i < 10; ++i) {
+    // expected-warning at +1{{OpenACC directives not yet implemented, pragma ignored}}
+    #pragma acc cache(ArrayPtr[T::value + I:I + 5], T::array[(i + T::value, 5): 6])
+  }
+  for (int i = 0; i < 10; ++i) {
+    // expected-warning at +1{{OpenACC directives not yet implemented, pragma ignored}}
+    #pragma acc cache(NS::NSArray[NS::NSInt])
+  }
+
+  for (int i = 0; i < 10; ++i) {
+    // expected-warning at +1{{OpenACC directives not yet implemented, pragma ignored}}
+    #pragma acc cache(NS::NSArray[NS::NSInt : NS::NSInt])
+  }
+
+  for (int i = 0; i < 10; ++i) {
+    // expected-error at +2{{use of undeclared identifier 'NSArray'; did you mean 'NS::NSArray'}}
+    // expected-warning at +1{{OpenACC directives not yet implemented, pragma ignored}}
+    #pragma acc cache(NSArray[NS::NSInt : NS::NSInt])
+  }
+
+  for (int i = 0; i < 10; ++i) {
+    // expected-error at +2{{use of undeclared identifier 'NSInt'; did you mean 'NS::NSInt'}}
+    // expected-warning at +1{{OpenACC directives not yet implemented, pragma ignored}}
+    #pragma acc cache(NS::NSArray[NSInt : NS::NSInt])
+  }
+
+  for (int i = 0; i < 10; ++i) {
+    // expected-error at +2{{use of undeclared identifier 'NSInt'; did you mean 'NS::NSInt'}}
+    // expected-warning at +1{{OpenACC directives not yet implemented, pragma ignored}}
+    #pragma acc cache(NS::NSArray[NS::NSInt : NSInt])
+  }
+}
+
+struct S {
+  static constexpr int value = 5;
+  static constexpr char array[] ={1,2,3,4,5};
+};
+
+void use() {
+  func<S, 5>();
+}


        


More information about the cfe-commits mailing list