[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