
The 'routine' construct applies either to a function directly, or, when provided a name, applies to the function named (and is visible in the current scope). This patch implements the parsing for this. The identifier provided (or Id Expression) is required to be a valid, declared identifier, though the semantic analysis portion of the Routine directive will need to enforce it being a function/overload set.
334 lines
12 KiB
C++
334 lines
12 KiB
C++
//===--- ParseOpenACC.cpp - OpenACC-specific parsing support --------------===//
|
|
//
|
|
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
|
|
// See https://llvm.org/LICENSE.txt for license information.
|
|
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
|
|
//
|
|
//===----------------------------------------------------------------------===//
|
|
//
|
|
// This file implements the parsing logic for OpenACC language features.
|
|
//
|
|
//===----------------------------------------------------------------------===//
|
|
|
|
#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,
|
|
};
|
|
|
|
// 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("atomic", OpenACCDirectiveKind::Atomic)
|
|
.Case("routine", OpenACCDirectiveKind::Routine)
|
|
.Case("declare", OpenACCDirectiveKind::Declare)
|
|
.Case("init", OpenACCDirectiveKind::Init)
|
|
.Case("shutdown", OpenACCDirectiveKind::Shutdown)
|
|
.Case("set", OpenACCDirectiveKind::Shutdown)
|
|
.Case("update", OpenACCDirectiveKind::Update)
|
|
.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)
|
|
.Default(OpenACCDirectiveKindEx::Invalid);
|
|
}
|
|
|
|
// Since 'atomic' is effectively a compound directive, this will decode the
|
|
// second part of the directive.
|
|
OpenACCAtomicKind getOpenACCAtomicKind(StringRef Name) {
|
|
return llvm::StringSwitch<OpenACCAtomicKind>(Name)
|
|
.Case("read", OpenACCAtomicKind::Read)
|
|
.Case("write", OpenACCAtomicKind::Write)
|
|
.Case("update", OpenACCAtomicKind::Update)
|
|
.Case("capture", OpenACCAtomicKind::Capture)
|
|
.Default(OpenACCAtomicKind::Invalid);
|
|
}
|
|
|
|
bool isOpenACCDirectiveKind(OpenACCDirectiveKind Kind, StringRef Tok) {
|
|
switch (Kind) {
|
|
case OpenACCDirectiveKind::Parallel:
|
|
return Tok == "parallel";
|
|
case OpenACCDirectiveKind::Serial:
|
|
return Tok == "serial";
|
|
case OpenACCDirectiveKind::Kernels:
|
|
return Tok == "kernels";
|
|
case OpenACCDirectiveKind::Data:
|
|
return Tok == "data";
|
|
case OpenACCDirectiveKind::HostData:
|
|
return Tok == "host_data";
|
|
case OpenACCDirectiveKind::Loop:
|
|
return Tok == "loop";
|
|
|
|
case OpenACCDirectiveKind::ParallelLoop:
|
|
case OpenACCDirectiveKind::SerialLoop:
|
|
case OpenACCDirectiveKind::KernelsLoop:
|
|
case OpenACCDirectiveKind::EnterData:
|
|
case OpenACCDirectiveKind::ExitData:
|
|
return false;
|
|
|
|
case OpenACCDirectiveKind::Atomic:
|
|
return Tok == "atomic";
|
|
case OpenACCDirectiveKind::Routine:
|
|
return Tok == "routine";
|
|
case OpenACCDirectiveKind::Declare:
|
|
return Tok == "declare";
|
|
case OpenACCDirectiveKind::Init:
|
|
return Tok == "init";
|
|
case OpenACCDirectiveKind::Shutdown:
|
|
return Tok == "shutdown";
|
|
case OpenACCDirectiveKind::Set:
|
|
return Tok == "set";
|
|
case OpenACCDirectiveKind::Update:
|
|
return Tok == "update";
|
|
case OpenACCDirectiveKind::Invalid:
|
|
return false;
|
|
}
|
|
llvm_unreachable("Unknown 'Kind' Passed");
|
|
}
|
|
|
|
OpenACCDirectiveKind
|
|
ParseOpenACCEnterExitDataDirective(Parser &P, Token FirstTok,
|
|
StringRef FirstTokSpelling,
|
|
OpenACCDirectiveKindEx ExtDirKind) {
|
|
Token SecondTok = P.getCurToken();
|
|
|
|
if (SecondTok.isAnnotation()) {
|
|
P.Diag(FirstTok, diag::err_acc_invalid_directive) << 0 << FirstTokSpelling;
|
|
return OpenACCDirectiveKind::Invalid;
|
|
}
|
|
|
|
std::string SecondTokSpelling = P.getPreprocessor().getSpelling(SecondTok);
|
|
|
|
if (!isOpenACCDirectiveKind(OpenACCDirectiveKind::Data, SecondTokSpelling)) {
|
|
P.Diag(FirstTok, diag::err_acc_invalid_directive)
|
|
<< 1 << FirstTokSpelling << SecondTokSpelling;
|
|
return OpenACCDirectiveKind::Invalid;
|
|
}
|
|
|
|
P.ConsumeToken();
|
|
|
|
return ExtDirKind == OpenACCDirectiveKindEx::Enter
|
|
? OpenACCDirectiveKind::EnterData
|
|
: OpenACCDirectiveKind::ExitData;
|
|
}
|
|
|
|
OpenACCAtomicKind ParseOpenACCAtomicKind(Parser &P) {
|
|
Token AtomicClauseToken = P.getCurToken();
|
|
|
|
// #pragma acc atomic is equivilent to update:
|
|
if (AtomicClauseToken.isAnnotation())
|
|
return OpenACCAtomicKind::Update;
|
|
|
|
std::string AtomicClauseSpelling =
|
|
P.getPreprocessor().getSpelling(AtomicClauseToken);
|
|
OpenACCAtomicKind AtomicKind = getOpenACCAtomicKind(AtomicClauseSpelling);
|
|
|
|
// If we don't know what this is, treat it as 'nothing', and treat the rest of
|
|
// this as a clause list, which, despite being invalid, is likely what the
|
|
// user was trying to do.
|
|
if (AtomicKind == OpenACCAtomicKind::Invalid)
|
|
return OpenACCAtomicKind::Update;
|
|
|
|
P.ConsumeToken();
|
|
return AtomicKind;
|
|
}
|
|
|
|
// Parse and consume the tokens for OpenACC Directive/Construct kinds.
|
|
OpenACCDirectiveKind ParseOpenACCDirectiveKind(Parser &P) {
|
|
Token FirstTok = P.getCurToken();
|
|
|
|
// Just #pragma acc can get us immediately to the end, make sure we don't
|
|
// introspect on the spelling before then.
|
|
if (FirstTok.isAnnotation()) {
|
|
P.Diag(FirstTok, diag::err_acc_missing_directive);
|
|
return OpenACCDirectiveKind::Invalid;
|
|
}
|
|
|
|
P.ConsumeToken();
|
|
std::string FirstTokSpelling = P.getPreprocessor().getSpelling(FirstTok);
|
|
|
|
OpenACCDirectiveKindEx ExDirKind = getOpenACCDirectiveKind(FirstTokSpelling);
|
|
|
|
// OpenACCDirectiveKindEx is meant to be an extended list
|
|
// over OpenACCDirectiveKind, so any value below Invalid is one of the
|
|
// OpenACCDirectiveKind values. This switch takes care of all of the extra
|
|
// parsing required for the Extended values. At the end of this block,
|
|
// ExDirKind can be assumed to be a valid OpenACCDirectiveKind, so we can
|
|
// immediately cast it and use it as that.
|
|
if (ExDirKind >= OpenACCDirectiveKindEx::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);
|
|
}
|
|
}
|
|
|
|
OpenACCDirectiveKind DirKind = static_cast<OpenACCDirectiveKind>(ExDirKind);
|
|
|
|
// Combined Constructs allows parallel loop, serial loop, or kernels loop. Any
|
|
// other attempt at a combined construct will be diagnosed as an invalid
|
|
// clause.
|
|
Token SecondTok = P.getCurToken();
|
|
if (!SecondTok.isAnnotation() &&
|
|
isOpenACCDirectiveKind(OpenACCDirectiveKind::Loop,
|
|
P.getPreprocessor().getSpelling(SecondTok))) {
|
|
switch (DirKind) {
|
|
default:
|
|
// Nothing to do except in the below cases, as they should be diagnosed as
|
|
// a clause.
|
|
break;
|
|
case OpenACCDirectiveKind::Parallel:
|
|
P.ConsumeToken();
|
|
return OpenACCDirectiveKind::ParallelLoop;
|
|
case OpenACCDirectiveKind::Serial:
|
|
P.ConsumeToken();
|
|
return OpenACCDirectiveKind::SerialLoop;
|
|
case OpenACCDirectiveKind::Kernels:
|
|
P.ConsumeToken();
|
|
return OpenACCDirectiveKind::KernelsLoop;
|
|
}
|
|
}
|
|
|
|
return DirKind;
|
|
}
|
|
|
|
void ParseOpenACCClauseList(Parser &P) {
|
|
// FIXME: In the future, we'll start parsing the clauses here, but for now we
|
|
// haven't implemented that, so just emit the unimplemented diagnostic and
|
|
// fail reasonably.
|
|
if (P.getCurToken().isNot(tok::annot_pragma_openacc_end))
|
|
P.Diag(P.getCurToken(), diag::warn_pragma_acc_unimplemented_clause_parsing);
|
|
}
|
|
|
|
} // 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 Res;
|
|
if (getLangOpts().CPlusPlus) {
|
|
Res = ParseCXXIdExpression(/*isAddressOfOperand=*/false);
|
|
} else {
|
|
// 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())
|
|
return ExprError();
|
|
|
|
Token FuncName = getCurToken();
|
|
UnqualifiedId Name;
|
|
CXXScopeSpec ScopeSpec;
|
|
SourceLocation TemplateKWLoc;
|
|
Name.setIdentifier(FuncName.getIdentifierInfo(), ConsumeToken());
|
|
|
|
// Ensure this is a valid identifier. We don't accept causing implicit
|
|
// function declarations per the spec, so always claim to not have trailing
|
|
// L Paren.
|
|
Res = Actions.ActOnIdExpression(getCurScope(), ScopeSpec, TemplateKWLoc,
|
|
Name, /*HasTrailingLParen=*/false,
|
|
/*isAddressOfOperand=*/false);
|
|
}
|
|
|
|
return getActions().CorrectDelayedTyposInExpr(Res);
|
|
}
|
|
|
|
void Parser::ParseOpenACCDirective() {
|
|
OpenACCDirectiveKind DirKind = ParseOpenACCDirectiveKind(*this);
|
|
|
|
// Once we've parsed the construct/directive name, some have additional
|
|
// specifiers that need to be taken care of. Atomic has an 'atomic-clause'
|
|
// that needs to be parsed.
|
|
if (DirKind == OpenACCDirectiveKind::Atomic)
|
|
ParseOpenACCAtomicKind(*this);
|
|
|
|
// We've successfully parsed the construct/directive name, however a few of
|
|
// the constructs have optional parens that contain further details.
|
|
BalancedDelimiterTracker T(*this, tok::l_paren,
|
|
tok::annot_pragma_openacc_end);
|
|
|
|
if (!T.consumeOpen()) {
|
|
switch (DirKind) {
|
|
default:
|
|
Diag(T.getOpenLocation(), diag::err_acc_invalid_open_paren);
|
|
T.skipToEnd();
|
|
break;
|
|
case OpenACCDirectiveKind::Routine: {
|
|
ExprResult RoutineName = ParseOpenACCRoutineName();
|
|
// If the routine name is invalid, just skip until the closing paren to
|
|
// recover more gracefully.
|
|
if (RoutineName.isInvalid())
|
|
T.skipToEnd();
|
|
else
|
|
T.consumeClose();
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
// Parses the list of clauses, if present.
|
|
ParseOpenACCClauseList(*this);
|
|
|
|
Diag(getCurToken(), diag::warn_pragma_acc_unimplemented);
|
|
SkipUntil(tok::annot_pragma_openacc_end);
|
|
}
|
|
|
|
// Parse OpenACC directive on a declaration.
|
|
Parser::DeclGroupPtrTy Parser::ParseOpenACCDirectiveDecl() {
|
|
assert(Tok.is(tok::annot_pragma_openacc) && "expected OpenACC Start Token");
|
|
|
|
ParsingOpenACCDirectiveRAII DirScope(*this);
|
|
ConsumeAnnotationToken();
|
|
|
|
ParseOpenACCDirective();
|
|
|
|
return nullptr;
|
|
}
|
|
|
|
// Parse OpenACC Directive on a Statement.
|
|
StmtResult Parser::ParseOpenACCDirectiveStmt() {
|
|
assert(Tok.is(tok::annot_pragma_openacc) && "expected OpenACC Start Token");
|
|
|
|
ParsingOpenACCDirectiveRAII DirScope(*this);
|
|
ConsumeAnnotationToken();
|
|
|
|
ParseOpenACCDirective();
|
|
|
|
return StmtEmpty();
|
|
}
|