#include "clang/AST/OpenACCClause.h"
#include "clang/Basic/OpenACCKinds.h"
#include "clang/Parse/ParseDiagnostic.h"
#include "clang/Parse/Parser.h"
#include "clang/Parse/RAIIObjectsForParser.h"
#include "clang/Sema/SemaOpenACC.h"
#include "llvm/ADT/StringRef.h"
#include "llvm/ADT/StringSwitch.h"
using namespace clang;
using namespace llvm;
namespace {
enum class OpenACCDirectiveKindEx {
Invalid = static_cast<int>(OpenACCDirectiveKind::Invalid),
Enter,
Exit,
};
OpenACCDirectiveKindEx getOpenACCDirectiveKind(Token Tok) {
if (!Tok.is(tok::identifier))
return OpenACCDirectiveKindEx::Invalid;
OpenACCDirectiveKind DirKind =
llvm::StringSwitch<OpenACCDirectiveKind>(
Tok.getIdentifierInfo()->getName())
.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("atomic", OpenACCDirectiveKind::Atomic)
.Case("routine", OpenACCDirectiveKind::Routine)
.Case("declare", OpenACCDirectiveKind::Declare)
.Case("init", OpenACCDirectiveKind::Init)
.Case("shutdown", OpenACCDirectiveKind::Shutdown)
.Case("set", OpenACCDirectiveKind::Set)
.Case("update", OpenACCDirectiveKind::Update)
.Case("wait", OpenACCDirectiveKind::Wait)
.Default(OpenACCDirectiveKind::Invalid);
if (DirKind != OpenACCDirectiveKind::Invalid)
return static_cast<OpenACCDirectiveKindEx>(DirKind);
return llvm::StringSwitch<OpenACCDirectiveKindEx>(
Tok.getIdentifierInfo()->getName())
.Case("enter", OpenACCDirectiveKindEx::Enter)
.Case("exit", OpenACCDirectiveKindEx::Exit)
.Default(OpenACCDirectiveKindEx::Invalid);
}
OpenACCClauseKind getOpenACCClauseKind(Token Tok) {
if (Tok.is(tok::kw_auto))
return OpenACCClauseKind::Auto;
if (Tok.is(tok::kw_default))
return OpenACCClauseKind::Default;
if (Tok.is(tok::kw_if))
return OpenACCClauseKind::If;
if (Tok.is(tok::kw_private))
return OpenACCClauseKind::Private;
if (!Tok.is(tok::identifier))
return OpenACCClauseKind::Invalid;
return llvm::StringSwitch<OpenACCClauseKind>(
Tok.getIdentifierInfo()->getName())
.Case("async", OpenACCClauseKind::Async)
.Case("attach", OpenACCClauseKind::Attach)
.Case("auto", OpenACCClauseKind::Auto)
.Case("bind", OpenACCClauseKind::Bind)
.Case("create", OpenACCClauseKind::Create)
.Case("pcreate", OpenACCClauseKind::PCreate)
.Case("present_or_create", OpenACCClauseKind::PresentOrCreate)
.Case("collapse", OpenACCClauseKind::Collapse)
.Case("copy", OpenACCClauseKind::Copy)
.Case("pcopy", OpenACCClauseKind::PCopy)
.Case("present_or_copy", OpenACCClauseKind::PresentOrCopy)
.Case("copyin", OpenACCClauseKind::CopyIn)
.Case("pcopyin", OpenACCClauseKind::PCopyIn)
.Case("present_or_copyin", OpenACCClauseKind::PresentOrCopyIn)
.Case("copyout", OpenACCClauseKind::CopyOut)
.Case("pcopyout", OpenACCClauseKind::PCopyOut)
.Case("present_or_copyout", OpenACCClauseKind::PresentOrCopyOut)
.Case("default", OpenACCClauseKind::Default)
.Case("default_async", OpenACCClauseKind::DefaultAsync)
.Case("delete", OpenACCClauseKind::Delete)
.Case("detach", OpenACCClauseKind::Detach)
.Case("device", OpenACCClauseKind::Device)
.Case("device_num", OpenACCClauseKind::DeviceNum)
.Case("device_resident", OpenACCClauseKind::DeviceResident)
.Case("device_type", OpenACCClauseKind::DeviceType)
.Case("deviceptr", OpenACCClauseKind::DevicePtr)
.Case("dtype", OpenACCClauseKind::DType)
.Case("finalize", OpenACCClauseKind::Finalize)
.Case("firstprivate", OpenACCClauseKind::FirstPrivate)
.Case("gang", OpenACCClauseKind::Gang)
.Case("host", OpenACCClauseKind::Host)
.Case("if", OpenACCClauseKind::If)
.Case("if_present", OpenACCClauseKind::IfPresent)
.Case("independent", OpenACCClauseKind::Independent)
.Case("link", OpenACCClauseKind::Link)
.Case("no_create", OpenACCClauseKind::NoCreate)
.Case("num_gangs", OpenACCClauseKind::NumGangs)
.Case("num_workers", OpenACCClauseKind::NumWorkers)
.Case("nohost", OpenACCClauseKind::NoHost)
.Case("present", OpenACCClauseKind::Present)
.Case("private", OpenACCClauseKind::Private)
.Case("reduction", OpenACCClauseKind::Reduction)
.Case("self", OpenACCClauseKind::Self)
.Case("seq", OpenACCClauseKind::Seq)
.Case("tile", OpenACCClauseKind::Tile)
.Case("use_device", OpenACCClauseKind::UseDevice)
.Case("vector", OpenACCClauseKind::Vector)
.Case("vector_length", OpenACCClauseKind::VectorLength)
.Case("wait", OpenACCClauseKind::Wait)
.Case("worker", OpenACCClauseKind::Worker)
.Default(OpenACCClauseKind::Invalid);
}
OpenACCAtomicKind getOpenACCAtomicKind(Token Tok) {
if (!Tok.is(tok::identifier))
return OpenACCAtomicKind::Invalid;
return llvm::StringSwitch<OpenACCAtomicKind>(
Tok.getIdentifierInfo()->getName())
.Case("read", OpenACCAtomicKind::Read)
.Case("write", OpenACCAtomicKind::Write)
.Case("update", OpenACCAtomicKind::Update)
.Case("capture", OpenACCAtomicKind::Capture)
.Default(OpenACCAtomicKind::Invalid);
}
OpenACCDefaultClauseKind getOpenACCDefaultClauseKind(Token Tok) {
if (!Tok.is(tok::identifier))
return OpenACCDefaultClauseKind::Invalid;
return llvm::StringSwitch<OpenACCDefaultClauseKind>(
Tok.getIdentifierInfo()->getName())
.Case("none", OpenACCDefaultClauseKind::None)
.Case("present", OpenACCDefaultClauseKind::Present)
.Default(OpenACCDefaultClauseKind::Invalid);
}
enum class OpenACCSpecialTokenKind {
ReadOnly,
DevNum,
Queues,
Zero,
Force,
Num,
Length,
Dim,
Static,
};
bool isOpenACCSpecialToken(OpenACCSpecialTokenKind Kind, Token Tok) {
if (Tok.is(tok::kw_static) && Kind == OpenACCSpecialTokenKind::Static)
return true;
if (!Tok.is(tok::identifier))
return false;
switch (Kind) {
case OpenACCSpecialTokenKind::ReadOnly:
return Tok.getIdentifierInfo()->isStr("readonly");
case OpenACCSpecialTokenKind::DevNum:
return Tok.getIdentifierInfo()->isStr("devnum");
case OpenACCSpecialTokenKind::Queues:
return Tok.getIdentifierInfo()->isStr("queues");
case OpenACCSpecialTokenKind::Zero:
return Tok.getIdentifierInfo()->isStr("zero");
case OpenACCSpecialTokenKind::Force:
return Tok.getIdentifierInfo()->isStr("force");
case OpenACCSpecialTokenKind::Num:
return Tok.getIdentifierInfo()->isStr("num");
case OpenACCSpecialTokenKind::Length:
return Tok.getIdentifierInfo()->isStr("length");
case OpenACCSpecialTokenKind::Dim:
return Tok.getIdentifierInfo()->isStr("dim");
case OpenACCSpecialTokenKind::Static:
return Tok.getIdentifierInfo()->isStr("static");
}
llvm_unreachable("Unknown 'Kind' Passed");
}
bool isTokenIdentifierOrKeyword(Parser &P, Token Tok) {
if (Tok.is(tok::identifier))
return true;
if (!Tok.isAnnotation() && Tok.getIdentifierInfo() &&
Tok.getIdentifierInfo()->isKeyword(P.getLangOpts()))
return true;
return false;
}
template <typename DirOrClauseTy>
bool tryParseAndConsumeSpecialTokenKind(Parser &P, OpenACCSpecialTokenKind Kind,
DirOrClauseTy DirOrClause) {
Token IdentTok = P.getCurToken();
if (isTokenIdentifierOrKeyword(P, IdentTok) && P.NextToken().is(tok::colon)) {
P.ConsumeToken();
P.ConsumeToken();
if (!isOpenACCSpecialToken(Kind, IdentTok)) {
P.Diag(IdentTok, diag::err_acc_invalid_tag_kind)
<< IdentTok.getIdentifierInfo() << DirOrClause
<< std::is_same_v<DirOrClauseTy, OpenACCClauseKind>;
return false;
}
return true;
}
return false;
}
bool isOpenACCDirectiveKind(OpenACCDirectiveKind Kind, Token Tok) {
if (!Tok.is(tok::identifier))
return false;
switch (Kind) {
case OpenACCDirectiveKind::Parallel:
return Tok.getIdentifierInfo()->isStr("parallel");
case OpenACCDirectiveKind::Serial:
return Tok.getIdentifierInfo()->isStr("serial");
case OpenACCDirectiveKind::Kernels:
return Tok.getIdentifierInfo()->isStr("kernels");
case OpenACCDirectiveKind::Data:
return Tok.getIdentifierInfo()->isStr("data");
case OpenACCDirectiveKind::HostData:
return Tok.getIdentifierInfo()->isStr("host_data");
case OpenACCDirectiveKind::Loop:
return Tok.getIdentifierInfo()->isStr("loop");
case OpenACCDirectiveKind::Cache:
return Tok.getIdentifierInfo()->isStr("cache");
case OpenACCDirectiveKind::ParallelLoop:
case OpenACCDirectiveKind::SerialLoop:
case OpenACCDirectiveKind::KernelsLoop:
case OpenACCDirectiveKind::EnterData:
case OpenACCDirectiveKind::ExitData:
return false;
case OpenACCDirectiveKind::Atomic:
return Tok.getIdentifierInfo()->isStr("atomic");
case OpenACCDirectiveKind::Routine:
return Tok.getIdentifierInfo()->isStr("routine");
case OpenACCDirectiveKind::Declare:
return Tok.getIdentifierInfo()->isStr("declare");
case OpenACCDirectiveKind::Init:
return Tok.getIdentifierInfo()->isStr("init");
case OpenACCDirectiveKind::Shutdown:
return Tok.getIdentifierInfo()->isStr("shutdown");
case OpenACCDirectiveKind::Set:
return Tok.getIdentifierInfo()->isStr("set");
case OpenACCDirectiveKind::Update:
return Tok.getIdentifierInfo()->isStr("update");
case OpenACCDirectiveKind::Wait:
return Tok.getIdentifierInfo()->isStr("wait");
case OpenACCDirectiveKind::Invalid:
return false;
}
llvm_unreachable("Unknown 'Kind' Passed");
}
OpenACCReductionOperator ParseReductionOperator(Parser &P) {
if (P.NextToken().isNot(tok::colon)) {
P.Diag(P.getCurToken(), diag::err_acc_expected_reduction_operator);
return OpenACCReductionOperator::Invalid;
}
Token ReductionKindTok = P.getCurToken();
P.ConsumeToken();
P.ConsumeToken();
switch (ReductionKindTok.getKind()) {
case tok::plus:
return OpenACCReductionOperator::Addition;
case tok::star:
return OpenACCReductionOperator::Multiplication;
case tok::amp:
return OpenACCReductionOperator::BitwiseAnd;
case tok::pipe:
return OpenACCReductionOperator::BitwiseOr;
case tok::caret:
return OpenACCReductionOperator::BitwiseXOr;
case tok::ampamp:
return OpenACCReductionOperator::And;
case tok::pipepipe:
return OpenACCReductionOperator::Or;
case tok::identifier:
if (ReductionKindTok.getIdentifierInfo()->isStr("max"))
return OpenACCReductionOperator::Max;
if (ReductionKindTok.getIdentifierInfo()->isStr("min"))
return OpenACCReductionOperator::Min;
[[fallthrough]];
default:
P.Diag(ReductionKindTok, diag::err_acc_invalid_reduction_operator);
return OpenACCReductionOperator::Invalid;
}
llvm_unreachable("Reduction op token kind not caught by 'default'?");
}
bool expectIdentifierOrKeyword(Parser &P) {
Token Tok = P.getCurToken();
if (isTokenIdentifierOrKeyword(P, Tok))
return false;
P.Diag(P.getCurToken(), diag::err_expected) << tok::identifier;
return true;
}
OpenACCDirectiveKind
ParseOpenACCEnterExitDataDirective(Parser &P, Token FirstTok,
OpenACCDirectiveKindEx ExtDirKind) {
Token SecondTok = P.getCurToken();
if (SecondTok.isAnnotation()) {
P.Diag(FirstTok, diag::err_acc_invalid_directive)
<< 0 << FirstTok.getIdentifierInfo();
return OpenACCDirectiveKind::Invalid;
}
P.ConsumeAnyToken();
if (!isOpenACCDirectiveKind(OpenACCDirectiveKind::Data, SecondTok)) {
if (!SecondTok.is(tok::identifier))
P.Diag(SecondTok, diag::err_expected) << tok::identifier;
else
P.Diag(FirstTok, diag::err_acc_invalid_directive)
<< 1 << FirstTok.getIdentifierInfo()->getName()
<< SecondTok.getIdentifierInfo()->getName();
return OpenACCDirectiveKind::Invalid;
}
return ExtDirKind == OpenACCDirectiveKindEx::Enter
? OpenACCDirectiveKind::EnterData
: OpenACCDirectiveKind::ExitData;
}
OpenACCAtomicKind ParseOpenACCAtomicKind(Parser &P) {
Token AtomicClauseToken = P.getCurToken();
if (AtomicClauseToken.isAnnotation())
return OpenACCAtomicKind::Update;
OpenACCAtomicKind AtomicKind = getOpenACCAtomicKind(AtomicClauseToken);
if (AtomicKind == OpenACCAtomicKind::Invalid)
return OpenACCAtomicKind::Update;
P.ConsumeToken();
return AtomicKind;
}
OpenACCDirectiveKind ParseOpenACCDirectiveKind(Parser &P) {
Token FirstTok = P.getCurToken();
if (FirstTok.isNot(tok::identifier)) {
P.Diag(FirstTok, diag::err_acc_missing_directive);
if (P.getCurToken().isNot(tok::annot_pragma_openacc_end))
P.ConsumeAnyToken();
return OpenACCDirectiveKind::Invalid;
}
P.ConsumeToken();
OpenACCDirectiveKindEx ExDirKind = getOpenACCDirectiveKind(FirstTok);
if (ExDirKind >= OpenACCDirectiveKindEx::Invalid) {
switch (ExDirKind) {
case OpenACCDirectiveKindEx::Invalid: {
P.Diag(FirstTok, diag::err_acc_invalid_directive)
<< 0 << FirstTok.getIdentifierInfo();
return OpenACCDirectiveKind::Invalid;
}
case OpenACCDirectiveKindEx::Enter:
case OpenACCDirectiveKindEx::Exit:
return ParseOpenACCEnterExitDataDirective(P, FirstTok, ExDirKind);
}
}
OpenACCDirectiveKind DirKind = static_cast<OpenACCDirectiveKind>(ExDirKind);
Token SecondTok = P.getCurToken();
if (!SecondTok.isAnnotation() &&
isOpenACCDirectiveKind(OpenACCDirectiveKind::Loop, SecondTok)) {
switch (DirKind) {
default:
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;
}
enum ClauseParensKind {
None,
Optional,
Required
};
ClauseParensKind getClauseParensKind(OpenACCDirectiveKind DirKind,
OpenACCClauseKind Kind) {
switch (Kind) {
case OpenACCClauseKind::Self:
return DirKind == OpenACCDirectiveKind::Update ? ClauseParensKind::Required
: ClauseParensKind::Optional;
case OpenACCClauseKind::Async:
case OpenACCClauseKind::Worker:
case OpenACCClauseKind::Vector:
case OpenACCClauseKind::Gang:
case OpenACCClauseKind::Wait:
return ClauseParensKind::Optional;
case OpenACCClauseKind::Default:
case OpenACCClauseKind::If:
case OpenACCClauseKind::Create:
case OpenACCClauseKind::PCreate:
case OpenACCClauseKind::PresentOrCreate:
case OpenACCClauseKind::Copy:
case OpenACCClauseKind::PCopy:
case OpenACCClauseKind::PresentOrCopy:
case OpenACCClauseKind::CopyIn:
case OpenACCClauseKind::PCopyIn:
case OpenACCClauseKind::PresentOrCopyIn:
case OpenACCClauseKind::CopyOut:
case OpenACCClauseKind::PCopyOut:
case OpenACCClauseKind::PresentOrCopyOut:
case OpenACCClauseKind::UseDevice:
case OpenACCClauseKind::NoCreate:
case OpenACCClauseKind::Present:
case OpenACCClauseKind::DevicePtr:
case OpenACCClauseKind::Attach:
case OpenACCClauseKind::Detach:
case OpenACCClauseKind::Private:
case OpenACCClauseKind::FirstPrivate:
case OpenACCClauseKind::Delete:
case OpenACCClauseKind::DeviceResident:
case OpenACCClauseKind::Device:
case OpenACCClauseKind::Link:
case OpenACCClauseKind::Host:
case OpenACCClauseKind::Reduction:
case OpenACCClauseKind::Collapse:
case OpenACCClauseKind::Bind:
case OpenACCClauseKind::VectorLength:
case OpenACCClauseKind::NumGangs:
case OpenACCClauseKind::NumWorkers:
case OpenACCClauseKind::DeviceNum:
case OpenACCClauseKind::DefaultAsync:
case OpenACCClauseKind::DeviceType:
case OpenACCClauseKind::DType:
case OpenACCClauseKind::Tile:
return ClauseParensKind::Required;
case OpenACCClauseKind::Auto:
case OpenACCClauseKind::Finalize:
case OpenACCClauseKind::IfPresent:
case OpenACCClauseKind::Independent:
case OpenACCClauseKind::Invalid:
case OpenACCClauseKind::NoHost:
case OpenACCClauseKind::Seq:
return ClauseParensKind::None;
}
llvm_unreachable("Unhandled clause kind");
}
bool ClauseHasOptionalParens(OpenACCDirectiveKind DirKind,
OpenACCClauseKind Kind) {
return getClauseParensKind(DirKind, Kind) == ClauseParensKind::Optional;
}
bool ClauseHasRequiredParens(OpenACCDirectiveKind DirKind,
OpenACCClauseKind Kind) {
return getClauseParensKind(DirKind, Kind) == ClauseParensKind::Required;
}
void SkipUntilEndOfDirective(Parser &P) {
while (P.getCurToken().isNot(tok::annot_pragma_openacc_end))
P.ConsumeAnyToken();
}
bool doesDirectiveHaveAssociatedStmt(OpenACCDirectiveKind DirKind) {
switch (DirKind) {
default:
return false;
case OpenACCDirectiveKind::Parallel:
case OpenACCDirectiveKind::Serial:
case OpenACCDirectiveKind::Kernels:
case OpenACCDirectiveKind::Loop:
return true;
}
llvm_unreachable("Unhandled directive->assoc stmt");
}
unsigned getOpenACCScopeFlags(OpenACCDirectiveKind DirKind) {
switch (DirKind) {
case OpenACCDirectiveKind::Parallel:
case OpenACCDirectiveKind::Serial:
case OpenACCDirectiveKind::Kernels:
return Scope::BreakScope | Scope::ContinueScope |
Scope::OpenACCComputeConstructScope;
case OpenACCDirectiveKind::Invalid:
llvm_unreachable("Shouldn't be creating a scope for an invalid construct");
default:
break;
}
return 0;
}
}
Parser::OpenACCClauseParseResult Parser::OpenACCCanContinue() {
return {nullptr, OpenACCParseCanContinue::Can};
}
Parser::OpenACCClauseParseResult Parser::OpenACCCannotContinue() {
return {nullptr, OpenACCParseCanContinue::Cannot};
}
Parser::OpenACCClauseParseResult Parser::OpenACCSuccess(OpenACCClause *Clause) {
return {Clause, OpenACCParseCanContinue::Can};
}
ExprResult Parser::ParseOpenACCConditionExpr() {
ExprResult ER = getActions().CorrectDelayedTyposInExpr(ParseExpression());
if (!ER.isUsable())
return ER;
Sema::ConditionResult R =
getActions().ActOnCondition(getCurScope(), ER.get()->getExprLoc(),
ER.get(), Sema::ConditionKind::Boolean);
return R.isInvalid() ? ExprError() : R.get().second;
}
SmallVector<OpenACCClause *>
Parser::ParseOpenACCClauseList(OpenACCDirectiveKind DirKind) {
SmallVector<OpenACCClause *> Clauses;
bool FirstClause = true;
while (getCurToken().isNot(tok::annot_pragma_openacc_end)) {
if (!FirstClause && getCurToken().is(tok::comma))
ConsumeToken();
FirstClause = false;
OpenACCClauseParseResult Result = ParseOpenACCClause(Clauses, DirKind);
if (OpenACCClause *Clause = Result.getPointer()) {
Clauses.push_back(Clause);
} else if (Result.getInt() == OpenACCParseCanContinue::Cannot) {
SkipUntilEndOfDirective(*this);
return Clauses;
}
}
return Clauses;
}
Parser::OpenACCIntExprParseResult
Parser::ParseOpenACCIntExpr(OpenACCDirectiveKind DK, OpenACCClauseKind CK,
SourceLocation Loc) {
ExprResult ER = ParseAssignmentExpression();
if (!ER.isUsable())
return {ER, OpenACCParseCanContinue::Cannot};
ER = getActions().CorrectDelayedTyposInExpr(ER);
if (!ER.isUsable())
return {ER, OpenACCParseCanContinue::Can};
return {getActions().OpenACC().ActOnIntExpr(DK, CK, Loc, ER.get()),
OpenACCParseCanContinue::Can};
}
bool Parser::ParseOpenACCIntExprList(OpenACCDirectiveKind DK,
OpenACCClauseKind CK, SourceLocation Loc,
llvm::SmallVectorImpl<Expr *> &IntExprs) {
OpenACCIntExprParseResult CurResult = ParseOpenACCIntExpr(DK, CK, Loc);
if (!CurResult.first.isUsable() &&
CurResult.second == OpenACCParseCanContinue::Cannot) {
SkipUntil(tok::r_paren, tok::annot_pragma_openacc_end,
Parser::StopBeforeMatch);
return true;
}
IntExprs.push_back(CurResult.first.get());
while (!getCurToken().isOneOf(tok::r_paren, tok::annot_pragma_openacc_end)) {
ExpectAndConsume(tok::comma);
CurResult = ParseOpenACCIntExpr(DK, CK, Loc);
if (!CurResult.first.isUsable() &&
CurResult.second == OpenACCParseCanContinue::Cannot) {
SkipUntil(tok::r_paren, tok::annot_pragma_openacc_end,
Parser::StopBeforeMatch);
return true;
}
IntExprs.push_back(CurResult.first.get());
}
return false;
}
bool Parser::ParseOpenACCDeviceTypeList(
llvm::SmallVector<std::pair<IdentifierInfo *, SourceLocation>> &Archs) {
if (expectIdentifierOrKeyword(*this)) {
SkipUntil(tok::r_paren, tok::annot_pragma_openacc_end,
Parser::StopBeforeMatch);
return true;
}
IdentifierInfo *Ident = getCurToken().getIdentifierInfo();
Archs.emplace_back(Ident, ConsumeToken());
while (!getCurToken().isOneOf(tok::r_paren, tok::annot_pragma_openacc_end)) {
ExpectAndConsume(tok::comma);
if (expectIdentifierOrKeyword(*this)) {
SkipUntil(tok::r_paren, tok::annot_pragma_openacc_end,
Parser::StopBeforeMatch);
return true;
}
Ident = getCurToken().getIdentifierInfo();
Archs.emplace_back(Ident, ConsumeToken());
}
return false;
}
bool Parser::ParseOpenACCSizeExpr() {
if (getCurToken().is(tok::star) &&
NextToken().isOneOf(tok::comma, tok::r_paren,
tok::annot_pragma_openacc_end)) {
ConsumeToken();
return false;
}
return getActions()
.CorrectDelayedTyposInExpr(ParseAssignmentExpression())
.isInvalid();
}
bool Parser::ParseOpenACCSizeExprList() {
if (ParseOpenACCSizeExpr()) {
SkipUntil(tok::r_paren, tok::annot_pragma_openacc_end,
Parser::StopBeforeMatch);
return false;
}
while (!getCurToken().isOneOf(tok::r_paren, tok::annot_pragma_openacc_end)) {
ExpectAndConsume(tok::comma);
if (ParseOpenACCSizeExpr()) {
SkipUntil(tok::r_paren, tok::annot_pragma_openacc_end,
Parser::StopBeforeMatch);
return false;
}
}
return false;
}
bool Parser::ParseOpenACCGangArg(SourceLocation GangLoc) {
if (isOpenACCSpecialToken(OpenACCSpecialTokenKind::Static, getCurToken()) &&
NextToken().is(tok::colon)) {
ConsumeToken();
ConsumeToken();
return ParseOpenACCSizeExpr();
}
if (isOpenACCSpecialToken(OpenACCSpecialTokenKind::Dim, getCurToken()) &&
NextToken().is(tok::colon)) {
ConsumeToken();
ConsumeToken();
return ParseOpenACCIntExpr(OpenACCDirectiveKind::Invalid,
OpenACCClauseKind::Gang, GangLoc)
.first.isInvalid();
}
if (isOpenACCSpecialToken(OpenACCSpecialTokenKind::Num, getCurToken()) &&
NextToken().is(tok::colon)) {
ConsumeToken();
ConsumeToken();
}
return ParseOpenACCIntExpr(OpenACCDirectiveKind::Invalid,
OpenACCClauseKind::Gang, GangLoc)
.first.isInvalid();
}
bool Parser::ParseOpenACCGangArgList(SourceLocation GangLoc) {
if (ParseOpenACCGangArg(GangLoc)) {
SkipUntil(tok::r_paren, tok::annot_pragma_openacc_end,
Parser::StopBeforeMatch);
return false;
}
while (!getCurToken().isOneOf(tok::r_paren, tok::annot_pragma_openacc_end)) {
ExpectAndConsume(tok::comma);
if (ParseOpenACCGangArg(GangLoc)) {
SkipUntil(tok::r_paren, tok::annot_pragma_openacc_end,
Parser::StopBeforeMatch);
return false;
}
}
return false;
}
Parser::OpenACCClauseParseResult
Parser::ParseOpenACCClause(ArrayRef<const OpenACCClause *> ExistingClauses,
OpenACCDirectiveKind DirKind) {
if (expectIdentifierOrKeyword(*this))
return OpenACCCannotContinue();
OpenACCClauseKind Kind = getOpenACCClauseKind(getCurToken());
if (Kind == OpenACCClauseKind::Invalid) {
Diag(getCurToken(), diag::err_acc_invalid_clause)
<< getCurToken().getIdentifierInfo();
return OpenACCCannotContinue();
}
SourceLocation ClauseLoc = ConsumeToken();
return ParseOpenACCClauseParams(ExistingClauses, DirKind, Kind, ClauseLoc);
}
Parser::OpenACCClauseParseResult Parser::ParseOpenACCClauseParams(
ArrayRef<const OpenACCClause *> ExistingClauses,
OpenACCDirectiveKind DirKind, OpenACCClauseKind ClauseKind,
SourceLocation ClauseLoc) {
BalancedDelimiterTracker Parens(*this, tok::l_paren,
tok::annot_pragma_openacc_end);
SemaOpenACC::OpenACCParsedClause ParsedClause(DirKind, ClauseKind, ClauseLoc);
if (ClauseHasRequiredParens(DirKind, ClauseKind)) {
if (Parens.expectAndConsume()) {
SkipUntil(tok::comma, tok::r_paren, tok::annot_pragma_openacc_end,
Parser::StopBeforeMatch);
return OpenACCCanContinue();
}
ParsedClause.setLParenLoc(Parens.getOpenLocation());
switch (ClauseKind) {
case OpenACCClauseKind::Default: {
Token DefKindTok = getCurToken();
if (expectIdentifierOrKeyword(*this)) {
Parens.skipToEnd();
return OpenACCCanContinue();
}
ConsumeToken();
OpenACCDefaultClauseKind DefKind =
getOpenACCDefaultClauseKind(DefKindTok);
if (DefKind == OpenACCDefaultClauseKind::Invalid) {
Diag(DefKindTok, diag::err_acc_invalid_default_clause_kind);
Parens.skipToEnd();
return OpenACCCanContinue();
}
ParsedClause.setDefaultDetails(DefKind);
break;
}
case OpenACCClauseKind::If: {
ExprResult CondExpr = ParseOpenACCConditionExpr();
ParsedClause.setConditionDetails(CondExpr.isUsable() ? CondExpr.get()
: nullptr);
if (CondExpr.isInvalid()) {
Parens.skipToEnd();
return OpenACCCanContinue();
}
break;
}
case OpenACCClauseKind::CopyIn:
case OpenACCClauseKind::PCopyIn:
case OpenACCClauseKind::PresentOrCopyIn: {
bool IsReadOnly = tryParseAndConsumeSpecialTokenKind(
*this, OpenACCSpecialTokenKind::ReadOnly, ClauseKind);
ParsedClause.setVarListDetails(ParseOpenACCVarList(ClauseKind),
IsReadOnly,
false);
break;
}
case OpenACCClauseKind::Create:
case OpenACCClauseKind::PCreate:
case OpenACCClauseKind::PresentOrCreate:
case OpenACCClauseKind::CopyOut:
case OpenACCClauseKind::PCopyOut:
case OpenACCClauseKind::PresentOrCopyOut: {
bool IsZero = tryParseAndConsumeSpecialTokenKind(
*this, OpenACCSpecialTokenKind::Zero, ClauseKind);
ParsedClause.setVarListDetails(ParseOpenACCVarList(ClauseKind),
false, IsZero);
break;
}
case OpenACCClauseKind::Reduction: {
OpenACCReductionOperator Op = ParseReductionOperator(*this);
ParsedClause.setReductionDetails(Op, ParseOpenACCVarList(ClauseKind));
break;
}
case OpenACCClauseKind::Self:
assert(DirKind == OpenACCDirectiveKind::Update);
[[fallthrough]];
case OpenACCClauseKind::Delete:
case OpenACCClauseKind::Detach:
case OpenACCClauseKind::Device:
case OpenACCClauseKind::DeviceResident:
case OpenACCClauseKind::Host:
case OpenACCClauseKind::Link:
case OpenACCClauseKind::UseDevice:
ParseOpenACCVarList(ClauseKind);
break;
case OpenACCClauseKind::Attach:
case OpenACCClauseKind::DevicePtr:
ParsedClause.setVarListDetails(ParseOpenACCVarList(ClauseKind),
false, false);
break;
case OpenACCClauseKind::Copy:
case OpenACCClauseKind::PCopy:
case OpenACCClauseKind::PresentOrCopy:
case OpenACCClauseKind::FirstPrivate:
case OpenACCClauseKind::NoCreate:
case OpenACCClauseKind::Present:
case OpenACCClauseKind::Private:
ParsedClause.setVarListDetails(ParseOpenACCVarList(ClauseKind),
false, false);
break;
case OpenACCClauseKind::Collapse: {
tryParseAndConsumeSpecialTokenKind(*this, OpenACCSpecialTokenKind::Force,
ClauseKind);
ExprResult NumLoops =
getActions().CorrectDelayedTyposInExpr(ParseConstantExpression());
if (NumLoops.isInvalid()) {
Parens.skipToEnd();
return OpenACCCanContinue();
}
break;
}
case OpenACCClauseKind::Bind: {
ExprResult BindArg = ParseOpenACCBindClauseArgument();
if (BindArg.isInvalid()) {
Parens.skipToEnd();
return OpenACCCanContinue();
}
break;
}
case OpenACCClauseKind::NumGangs: {
llvm::SmallVector<Expr *> IntExprs;
if (ParseOpenACCIntExprList(OpenACCDirectiveKind::Invalid,
OpenACCClauseKind::NumGangs, ClauseLoc,
IntExprs)) {
Parens.skipToEnd();
return OpenACCCanContinue();
}
ParsedClause.setIntExprDetails(std::move(IntExprs));
break;
}
case OpenACCClauseKind::NumWorkers:
case OpenACCClauseKind::DeviceNum:
case OpenACCClauseKind::DefaultAsync:
case OpenACCClauseKind::VectorLength: {
ExprResult IntExpr = ParseOpenACCIntExpr(OpenACCDirectiveKind::Invalid,
ClauseKind, ClauseLoc)
.first;
if (IntExpr.isInvalid()) {
Parens.skipToEnd();
return OpenACCCanContinue();
}
if (ClauseKind == OpenACCClauseKind::NumWorkers ||
ClauseKind == OpenACCClauseKind::VectorLength)
ParsedClause.setIntExprDetails(IntExpr.get());
break;
}
case OpenACCClauseKind::DType:
case OpenACCClauseKind::DeviceType: {
llvm::SmallVector<std::pair<IdentifierInfo *, SourceLocation>> Archs;
if (getCurToken().is(tok::star)) {
ParsedClause.setDeviceTypeDetails({{nullptr, ConsumeToken()}});
} else if (!ParseOpenACCDeviceTypeList(Archs)) {
ParsedClause.setDeviceTypeDetails(std::move(Archs));
} else {
Parens.skipToEnd();
return OpenACCCanContinue();
}
break;
}
case OpenACCClauseKind::Tile:
if (ParseOpenACCSizeExprList()) {
Parens.skipToEnd();
return OpenACCCanContinue();
}
break;
default:
llvm_unreachable("Not a required parens type?");
}
ParsedClause.setEndLoc(getCurToken().getLocation());
if (Parens.consumeClose())
return OpenACCCannotContinue();
} else if (ClauseHasOptionalParens(DirKind, ClauseKind)) {
if (!Parens.consumeOpen()) {
ParsedClause.setLParenLoc(Parens.getOpenLocation());
switch (ClauseKind) {
case OpenACCClauseKind::Self: {
assert(DirKind != OpenACCDirectiveKind::Update);
ExprResult CondExpr = ParseOpenACCConditionExpr();
ParsedClause.setConditionDetails(CondExpr.isUsable() ? CondExpr.get()
: nullptr);
if (CondExpr.isInvalid()) {
Parens.skipToEnd();
return OpenACCCanContinue();
}
break;
}
case OpenACCClauseKind::Vector:
case OpenACCClauseKind::Worker: {
tryParseAndConsumeSpecialTokenKind(*this,
ClauseKind ==
OpenACCClauseKind::Vector
? OpenACCSpecialTokenKind::Length
: OpenACCSpecialTokenKind::Num,
ClauseKind);
ExprResult IntExpr = ParseOpenACCIntExpr(OpenACCDirectiveKind::Invalid,
ClauseKind, ClauseLoc)
.first;
if (IntExpr.isInvalid()) {
Parens.skipToEnd();
return OpenACCCanContinue();
}
break;
}
case OpenACCClauseKind::Async: {
ExprResult AsyncArg =
ParseOpenACCAsyncArgument(OpenACCDirectiveKind::Invalid,
OpenACCClauseKind::Async, ClauseLoc)
.first;
ParsedClause.setIntExprDetails(AsyncArg.isUsable() ? AsyncArg.get()
: nullptr);
if (AsyncArg.isInvalid()) {
Parens.skipToEnd();
return OpenACCCanContinue();
}
break;
}
case OpenACCClauseKind::Gang:
if (ParseOpenACCGangArgList(ClauseLoc)) {
Parens.skipToEnd();
return OpenACCCanContinue();
}
break;
case OpenACCClauseKind::Wait: {
OpenACCWaitParseInfo Info =
ParseOpenACCWaitArgument(ClauseLoc,
false);
if (Info.Failed) {
Parens.skipToEnd();
return OpenACCCanContinue();
}
ParsedClause.setWaitDetails(Info.DevNumExpr, Info.QueuesLoc,
std::move(Info.QueueIdExprs));
break;
}
default:
llvm_unreachable("Not an optional parens type?");
}
ParsedClause.setEndLoc(getCurToken().getLocation());
if (Parens.consumeClose())
return OpenACCCannotContinue();
} else {
ParsedClause.setEndLoc(ClauseLoc);
}
} else {
ParsedClause.setEndLoc(ClauseLoc);
}
return OpenACCSuccess(
Actions.OpenACC().ActOnClause(ExistingClauses, ParsedClause));
}
Parser::OpenACCIntExprParseResult
Parser::ParseOpenACCAsyncArgument(OpenACCDirectiveKind DK, OpenACCClauseKind CK,
SourceLocation Loc) {
return ParseOpenACCIntExpr(DK, CK, Loc);
}
Parser::OpenACCWaitParseInfo
Parser::ParseOpenACCWaitArgument(SourceLocation Loc, bool IsDirective) {
OpenACCWaitParseInfo Result;
if (isOpenACCSpecialToken(OpenACCSpecialTokenKind::DevNum, Tok) &&
NextToken().is(tok::colon)) {
ConsumeToken();
ConsumeToken();
OpenACCIntExprParseResult Res = ParseOpenACCIntExpr(
IsDirective ? OpenACCDirectiveKind::Wait
: OpenACCDirectiveKind::Invalid,
IsDirective ? OpenACCClauseKind::Invalid : OpenACCClauseKind::Wait,
Loc);
if (Res.first.isInvalid() &&
Res.second == OpenACCParseCanContinue::Cannot) {
Result.Failed = true;
return Result;
}
if (ExpectAndConsume(tok::colon)) {
Result.Failed = true;
return Result;
}
Result.DevNumExpr = Res.first.get();
}
if (isOpenACCSpecialToken(OpenACCSpecialTokenKind::Queues, Tok) &&
NextToken().is(tok::colon)) {
Result.QueuesLoc = ConsumeToken();
ConsumeToken();
}
bool FirstArg = true;
while (!getCurToken().isOneOf(tok::r_paren, tok::annot_pragma_openacc_end)) {
if (!FirstArg) {
if (ExpectAndConsume(tok::comma)) {
Result.Failed = true;
return Result;
}
}
FirstArg = false;
OpenACCIntExprParseResult Res = ParseOpenACCAsyncArgument(
IsDirective ? OpenACCDirectiveKind::Wait
: OpenACCDirectiveKind::Invalid,
IsDirective ? OpenACCClauseKind::Invalid : OpenACCClauseKind::Wait,
Loc);
if (Res.first.isInvalid() &&
Res.second == OpenACCParseCanContinue::Cannot) {
Result.Failed = true;
return Result;
}
Result.QueueIdExprs.push_back(Res.first.get());
}
return Result;
}
ExprResult Parser::ParseOpenACCIDExpression() {
ExprResult Res;
if (getLangOpts().CPlusPlus) {
Res = ParseCXXIdExpression(true);
} else {
if (Tok.isNot(tok::identifier)) {
Diag(Tok, diag::err_expected) << tok::identifier;
return ExprError();
}
Token FuncName = getCurToken();
UnqualifiedId Name;
CXXScopeSpec ScopeSpec;
SourceLocation TemplateKWLoc;
Name.setIdentifier(FuncName.getIdentifierInfo(), ConsumeToken());
Res = Actions.ActOnIdExpression(getCurScope(), ScopeSpec, TemplateKWLoc,
Name, false,
false);
}
return getActions().CorrectDelayedTyposInExpr(Res);
}
ExprResult Parser::ParseOpenACCBindClauseArgument() {
if (getCurToken().is(tok::r_paren)) {
Diag(getCurToken(), diag::err_acc_incorrect_bind_arg);
return ExprError();
}
if (tok::isStringLiteral(getCurToken().getKind()))
return getActions().CorrectDelayedTyposInExpr(ParseStringLiteralExpression(
false, true));
return ParseOpenACCIDExpression();
}
Parser::OpenACCVarParseResult Parser::ParseOpenACCVar(OpenACCClauseKind CK) {
OpenACCArraySectionRAII ArraySections(*this);
ExprResult Res = ParseAssignmentExpression();
if (!Res.isUsable())
return {Res, OpenACCParseCanContinue::Cannot};
Res = getActions().CorrectDelayedTyposInExpr(Res.get());
if (!Res.isUsable())
return {Res, OpenACCParseCanContinue::Can};
Res = getActions().OpenACC().ActOnVar(CK, Res.get());
return {Res, OpenACCParseCanContinue::Can};
}
llvm::SmallVector<Expr *> Parser::ParseOpenACCVarList(OpenACCClauseKind CK) {
llvm::SmallVector<Expr *> Vars;
auto [Res, CanContinue] = ParseOpenACCVar(CK);
if (Res.isUsable()) {
Vars.push_back(Res.get());
} else if (CanContinue == OpenACCParseCanContinue::Cannot) {
SkipUntil(tok::r_paren, tok::annot_pragma_openacc_end, StopBeforeMatch);
return Vars;
}
while (!getCurToken().isOneOf(tok::r_paren, tok::annot_pragma_openacc_end)) {
ExpectAndConsume(tok::comma);
auto [Res, CanContinue] = ParseOpenACCVar(CK);
if (Res.isUsable()) {
Vars.push_back(Res.get());
} else if (CanContinue == OpenACCParseCanContinue::Cannot) {
SkipUntil(tok::r_paren, tok::annot_pragma_openacc_end, StopBeforeMatch);
return Vars;
}
}
return Vars;
}
void Parser::ParseOpenACCCacheVarList() {
if (getCurToken().isAnnotation())
return;
if (tryParseAndConsumeSpecialTokenKind(*this,
OpenACCSpecialTokenKind::ReadOnly,
OpenACCDirectiveKind::Cache)) {
}
ParseOpenACCVarList(OpenACCClauseKind::Invalid);
}
Parser::OpenACCDirectiveParseInfo
Parser::ParseOpenACCDirective() {
SourceLocation StartLoc = ConsumeAnnotationToken();
SourceLocation DirLoc = getCurToken().getLocation();
OpenACCDirectiveKind DirKind = ParseOpenACCDirectiveKind(*this);
getActions().OpenACC().ActOnConstruct(DirKind, DirLoc);
if (DirKind == OpenACCDirectiveKind::Atomic)
ParseOpenACCAtomicKind(*this);
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 = ParseOpenACCIDExpression();
if (RoutineName.isInvalid())
T.skipToEnd();
else
T.consumeClose();
break;
}
case OpenACCDirectiveKind::Cache:
ParseOpenACCCacheVarList();
T.consumeClose();
break;
case OpenACCDirectiveKind::Wait:
if (ParseOpenACCWaitArgument(DirLoc, true).Failed)
T.skipToEnd();
else
T.consumeClose();
break;
}
} else if (DirKind == OpenACCDirectiveKind::Cache) {
Diag(Tok, diag::err_expected) << tok::l_paren;
}
OpenACCDirectiveParseInfo ParseInfo{DirKind, StartLoc, DirLoc,
SourceLocation{},
ParseOpenACCClauseList(DirKind)};
assert(Tok.is(tok::annot_pragma_openacc_end) &&
"Didn't parse all OpenACC Clauses");
ParseInfo.EndLoc = ConsumeAnnotationToken();
assert(ParseInfo.EndLoc.isValid() &&
"Terminating annotation token not present");
return ParseInfo;
}
Parser::DeclGroupPtrTy Parser::ParseOpenACCDirectiveDecl() {
assert(Tok.is(tok::annot_pragma_openacc) && "expected OpenACC Start Token");
ParsingOpenACCDirectiveRAII DirScope(*this);
OpenACCDirectiveParseInfo DirInfo = ParseOpenACCDirective();
if (getActions().OpenACC().ActOnStartDeclDirective(DirInfo.DirKind,
DirInfo.StartLoc))
return nullptr;
return DeclGroupPtrTy::make(getActions().OpenACC().ActOnEndDeclDirective());
}
StmtResult Parser::ParseOpenACCDirectiveStmt() {
assert(Tok.is(tok::annot_pragma_openacc) && "expected OpenACC Start Token");
ParsingOpenACCDirectiveRAII DirScope(*this);
OpenACCDirectiveParseInfo DirInfo = ParseOpenACCDirective();
if (getActions().OpenACC().ActOnStartStmtDirective(DirInfo.DirKind,
DirInfo.StartLoc))
return StmtError();
StmtResult AssocStmt;
SemaOpenACC::AssociatedStmtRAII AssocStmtRAII(getActions().OpenACC(),
DirInfo.DirKind);
if (doesDirectiveHaveAssociatedStmt(DirInfo.DirKind)) {
ParsingOpenACCDirectiveRAII DirScope(*this, false);
ParseScope ACCScope(this, getOpenACCScopeFlags(DirInfo.DirKind));
AssocStmt = getActions().OpenACC().ActOnAssociatedStmt(
DirInfo.StartLoc, DirInfo.DirKind, ParseStatement());
}
return getActions().OpenACC().ActOnEndStmtDirective(
DirInfo.DirKind, DirInfo.StartLoc, DirInfo.DirLoc, DirInfo.EndLoc,
DirInfo.Clauses, AssocStmt);
}