================ @@ -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: + break; + case OpenACCDirectiveKind::Parallel: + if (P.getPreprocessor().getSpelling(SecondTok) == "loop") { ---------------- alexey-bataev wrote:
Same, start with a simple parallel at first https://github.com/llvm/llvm-project/pull/72661 _______________________________________________ cfe-commits mailing list cfe-commits@lists.llvm.org https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits