[clang] [OpenACC] Implement initial parsing for Construct/Directive Names (PR #72661)

Erich Keane via cfe-commits cfe-commits at lists.llvm.org
Fri Nov 17 07:25:28 PST 2023


================
@@ -10,18 +10,240 @@
 //
 //===----------------------------------------------------------------------===//
 
+#include "clang/Basic/OpenACCKinds.h"
 #include "clang/Parse/ParseDiagnostic.h"
 #include "clang/Parse/Parser.h"
+#include "clang/Parse/RAIIObjectsForParser.h"
+#include "llvm/ADT/StringRef.h"
+#include "llvm/ADT/StringSwitch.h"
 
 using namespace clang;
+using namespace llvm;
+
+namespace {
+// An enum that contains the extended 'partial' parsed variants. This type
+// should never escape the initial parse functionality, but is useful for
+// simplifying the implementation.
+enum class OpenACCDirectiveKindEx {
+  Invalid = static_cast<int>(OpenACCDirectiveKind::Invalid),
+  // 'enter data' and 'exit data'
+  Enter,
+  Exit,
+  // 'atomic read', 'atomic write', 'atomic update', and 'atomic capture'.
+  Atomic,
+};
+
+// Translate single-token string representations to the OpenACC Directive Kind.
+// This doesn't completely comprehend 'Compound Constructs' (as it just
+// identifies the first token), and doesn't fully handle 'enter data', 'exit
+// data', nor any of the 'atomic' variants, just the first token of each.  So
+// this should only be used by `ParseOpenACCDirectiveKind`.
+OpenACCDirectiveKindEx GetOpenACCDirectiveKind(StringRef Name) {
+  OpenACCDirectiveKind DirKind =
+      llvm::StringSwitch<OpenACCDirectiveKind>(Name)
+          .Case("parallel", OpenACCDirectiveKind::Parallel)
+          .Case("serial", OpenACCDirectiveKind::Serial)
+          .Case("kernels", OpenACCDirectiveKind::Kernels)
+          .Case("data", OpenACCDirectiveKind::Data)
+          .Case("host_data", OpenACCDirectiveKind::HostData)
+          .Case("loop", OpenACCDirectiveKind::Loop)
+          .Case("cache", OpenACCDirectiveKind::Cache)
+          .Case("declare", OpenACCDirectiveKind::Declare)
+          .Case("init", OpenACCDirectiveKind::Init)
+          .Case("shutdown", OpenACCDirectiveKind::Shutdown)
+          .Case("set", OpenACCDirectiveKind::Shutdown)
+          .Case("update", OpenACCDirectiveKind::Update)
+          .Case("wait", OpenACCDirectiveKind::Wait)
+          .Case("routine", OpenACCDirectiveKind::Routine)
+          .Default(OpenACCDirectiveKind::Invalid);
+
+  if (DirKind != OpenACCDirectiveKind::Invalid)
+    return static_cast<OpenACCDirectiveKindEx>(DirKind);
+
+  return llvm::StringSwitch<OpenACCDirectiveKindEx>(Name)
+      .Case("enter", OpenACCDirectiveKindEx::Enter)
+      .Case("exit", OpenACCDirectiveKindEx::Exit)
+      .Case("atomic", OpenACCDirectiveKindEx::Atomic)
+      .Default(OpenACCDirectiveKindEx::Invalid);
+}
+
+// "enter data" and "exit data" are permitted as their own constructs. Handle
+// these, knowing the previous token is either 'enter' or 'exit'. The current
+// token should be the one after the "enter" or "exit".
+OpenACCDirectiveKind
+ParseOpenACCEnterExitDataDirective(Parser &P, Token FirstTok,
+                                   StringRef FirstTokSpelling,
+                                   OpenACCDirectiveKindEx ExtDirKind) {
+  Token SecondTok = P.getCurToken();
+  std::string SecondTokSpelling = P.getPreprocessor().getSpelling(SecondTok);
+
+  if (SecondTokSpelling != "data") {
+    P.Diag(FirstTok, diag::err_acc_invalid_directive)
+        << 1 << FirstTokSpelling << SecondTokSpelling;
+    return OpenACCDirectiveKind::Invalid;
+  }
+
+  P.ConsumeToken();
+  return ExtDirKind == OpenACCDirectiveKindEx::Enter
+             ? OpenACCDirectiveKind::EnterData
+             : OpenACCDirectiveKind::ExitData;
+}
+
+OpenACCDirectiveKind ParseOpenACCAtomicDirective(Parser &P) {
+  Token AtomicClauseToken = P.getCurToken();
+  std::string AtomicClauseSpelling =
+      P.getPreprocessor().getSpelling(AtomicClauseToken);
+
+  OpenACCDirectiveKind DirKind =
+      llvm::StringSwitch<OpenACCDirectiveKind>(AtomicClauseSpelling)
+          .Case("read", OpenACCDirectiveKind::AtomicRead)
+          .Case("write", OpenACCDirectiveKind::AtomicWrite)
+          .Case("update", OpenACCDirectiveKind::AtomicUpdate)
+          .Case("capture", OpenACCDirectiveKind::AtomicCapture)
+          .Default(OpenACCDirectiveKind::Invalid);
+
+  if (DirKind == OpenACCDirectiveKind::Invalid)
+    P.Diag(AtomicClauseToken, diag::err_acc_invalid_atomic_clause)
+        << AtomicClauseSpelling;
+
+  P.ConsumeToken();
+  return DirKind;
+}
+
+// Parse and consume the tokens for OpenACC Directive/Construct kinds.
+OpenACCDirectiveKind ParseOpenACCDirectiveKind(Parser &P) {
+  Token FirstTok = P.getCurToken();
+  P.ConsumeToken();
+  std::string FirstTokSpelling = P.getPreprocessor().getSpelling(FirstTok);
+
+  OpenACCDirectiveKindEx ExDirKind = GetOpenACCDirectiveKind(FirstTokSpelling);
+
+  Token SecondTok = P.getCurToken();
+  // Go through the Extended kinds to see if we can convert this to the
+  // non-Extended kinds, and handle invalid.
+  switch (ExDirKind) {
+  case OpenACCDirectiveKindEx::Invalid:
+    P.Diag(FirstTok, diag::err_acc_invalid_directive) << 0 << FirstTokSpelling;
+    return OpenACCDirectiveKind::Invalid;
+  case OpenACCDirectiveKindEx::Enter:
+  case OpenACCDirectiveKindEx::Exit:
+    return ParseOpenACCEnterExitDataDirective(P, FirstTok, FirstTokSpelling,
+                                              ExDirKind);
+  case OpenACCDirectiveKindEx::Atomic:
+    return ParseOpenACCAtomicDirective(P);
+  }
+
+  // Combined Constructs allows parallel loop, serial loop, or kernels loop. Any
+  // other attempt at a combined constructwill be diagnosed as an invalid
+  // clause.
+
+  switch (static_cast<OpenACCDirectiveKind>(ExDirKind)) {
+  default:
----------------
erichkeane wrote:

Eh?  We do this sort of thing all the time, the whole point is that we're intentionally not handling EVERYTHING else, since these are the only ones needing special treatment.

https://github.com/llvm/llvm-project/pull/72661


More information about the cfe-commits mailing list