2023-11-17 06:29:02 -08:00
|
|
|
//===--- 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.
|
|
|
|
//
|
|
|
|
//===----------------------------------------------------------------------===//
|
|
|
|
|
2023-11-17 10:47:42 -08:00
|
|
|
#include "clang/Basic/OpenACCKinds.h"
|
2023-11-17 06:29:02 -08:00
|
|
|
#include "clang/Parse/ParseDiagnostic.h"
|
|
|
|
#include "clang/Parse/Parser.h"
|
2023-11-17 10:47:42 -08:00
|
|
|
#include "clang/Parse/RAIIObjectsForParser.h"
|
|
|
|
#include "llvm/ADT/StringRef.h"
|
|
|
|
#include "llvm/ADT/StringSwitch.h"
|
2023-11-17 06:29:02 -08:00
|
|
|
|
|
|
|
using namespace clang;
|
2023-11-17 10:47:42 -08:00
|
|
|
using namespace llvm;
|
2023-11-17 06:29:02 -08:00
|
|
|
|
2023-11-17 10:47:42 -08:00
|
|
|
namespace {
|
2023-11-21 07:46:12 -08:00
|
|
|
// 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,
|
|
|
|
};
|
2023-11-17 10:47:42 -08:00
|
|
|
|
2023-11-21 07:46:12 -08:00
|
|
|
// 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`.
|
2023-12-05 12:41:57 -08:00
|
|
|
OpenACCDirectiveKindEx getOpenACCDirectiveKind(Token Tok) {
|
|
|
|
if (!Tok.is(tok::identifier))
|
|
|
|
return OpenACCDirectiveKindEx::Invalid;
|
2023-11-21 07:46:12 -08:00
|
|
|
OpenACCDirectiveKind DirKind =
|
2023-12-05 12:41:57 -08:00
|
|
|
llvm::StringSwitch<OpenACCDirectiveKind>(
|
|
|
|
Tok.getIdentifierInfo()->getName())
|
2023-11-21 07:46:12 -08:00
|
|
|
.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)
|
2023-12-06 06:32:45 -08:00
|
|
|
.Case("cache", OpenACCDirectiveKind::Cache)
|
2023-11-21 11:08:48 -08:00
|
|
|
.Case("atomic", OpenACCDirectiveKind::Atomic)
|
2023-11-27 06:49:29 -08:00
|
|
|
.Case("routine", OpenACCDirectiveKind::Routine)
|
2023-11-21 07:46:12 -08:00
|
|
|
.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);
|
|
|
|
|
2023-12-05 12:41:57 -08:00
|
|
|
return llvm::StringSwitch<OpenACCDirectiveKindEx>(
|
|
|
|
Tok.getIdentifierInfo()->getName())
|
2023-11-21 07:46:12 -08:00
|
|
|
.Case("enter", OpenACCDirectiveKindEx::Enter)
|
|
|
|
.Case("exit", OpenACCDirectiveKindEx::Exit)
|
|
|
|
.Default(OpenACCDirectiveKindEx::Invalid);
|
2023-11-17 10:47:42 -08:00
|
|
|
}
|
|
|
|
|
2023-11-21 11:08:48 -08:00
|
|
|
// Since 'atomic' is effectively a compound directive, this will decode the
|
|
|
|
// second part of the directive.
|
2023-12-05 12:41:57 -08:00
|
|
|
OpenACCAtomicKind getOpenACCAtomicKind(Token Tok) {
|
|
|
|
if (!Tok.is(tok::identifier))
|
|
|
|
return OpenACCAtomicKind::Invalid;
|
|
|
|
return llvm::StringSwitch<OpenACCAtomicKind>(
|
|
|
|
Tok.getIdentifierInfo()->getName())
|
2023-11-21 11:08:48 -08:00
|
|
|
.Case("read", OpenACCAtomicKind::Read)
|
|
|
|
.Case("write", OpenACCAtomicKind::Write)
|
|
|
|
.Case("update", OpenACCAtomicKind::Update)
|
|
|
|
.Case("capture", OpenACCAtomicKind::Capture)
|
|
|
|
.Default(OpenACCAtomicKind::Invalid);
|
|
|
|
}
|
|
|
|
|
2023-12-05 12:41:57 -08:00
|
|
|
bool isOpenACCDirectiveKind(OpenACCDirectiveKind Kind, Token Tok) {
|
|
|
|
if (!Tok.is(tok::identifier))
|
|
|
|
return false;
|
|
|
|
|
2023-11-20 11:46:07 -08:00
|
|
|
switch (Kind) {
|
|
|
|
case OpenACCDirectiveKind::Parallel:
|
2023-12-05 12:41:57 -08:00
|
|
|
return Tok.getIdentifierInfo()->isStr("parallel");
|
2023-11-20 11:46:07 -08:00
|
|
|
case OpenACCDirectiveKind::Serial:
|
2023-12-05 12:41:57 -08:00
|
|
|
return Tok.getIdentifierInfo()->isStr("serial");
|
2023-11-20 11:46:07 -08:00
|
|
|
case OpenACCDirectiveKind::Kernels:
|
2023-12-05 12:41:57 -08:00
|
|
|
return Tok.getIdentifierInfo()->isStr("kernels");
|
2023-11-20 11:46:07 -08:00
|
|
|
case OpenACCDirectiveKind::Data:
|
2023-12-05 12:41:57 -08:00
|
|
|
return Tok.getIdentifierInfo()->isStr("data");
|
2023-11-20 11:46:07 -08:00
|
|
|
case OpenACCDirectiveKind::HostData:
|
2023-12-05 12:41:57 -08:00
|
|
|
return Tok.getIdentifierInfo()->isStr("host_data");
|
2023-11-20 11:46:07 -08:00
|
|
|
case OpenACCDirectiveKind::Loop:
|
2023-12-05 12:41:57 -08:00
|
|
|
return Tok.getIdentifierInfo()->isStr("loop");
|
2023-12-06 06:32:45 -08:00
|
|
|
case OpenACCDirectiveKind::Cache:
|
2023-12-05 12:41:57 -08:00
|
|
|
return Tok.getIdentifierInfo()->isStr("cache");
|
2023-11-20 11:46:07 -08:00
|
|
|
|
|
|
|
case OpenACCDirectiveKind::ParallelLoop:
|
|
|
|
case OpenACCDirectiveKind::SerialLoop:
|
|
|
|
case OpenACCDirectiveKind::KernelsLoop:
|
2023-11-21 07:46:12 -08:00
|
|
|
case OpenACCDirectiveKind::EnterData:
|
|
|
|
case OpenACCDirectiveKind::ExitData:
|
2023-11-20 11:46:07 -08:00
|
|
|
return false;
|
|
|
|
|
2023-11-21 11:08:48 -08:00
|
|
|
case OpenACCDirectiveKind::Atomic:
|
2023-12-05 12:41:57 -08:00
|
|
|
return Tok.getIdentifierInfo()->isStr("atomic");
|
2023-11-27 06:49:29 -08:00
|
|
|
case OpenACCDirectiveKind::Routine:
|
2023-12-05 12:41:57 -08:00
|
|
|
return Tok.getIdentifierInfo()->isStr("routine");
|
2023-11-20 11:46:07 -08:00
|
|
|
case OpenACCDirectiveKind::Declare:
|
2023-12-05 12:41:57 -08:00
|
|
|
return Tok.getIdentifierInfo()->isStr("declare");
|
2023-11-20 11:46:07 -08:00
|
|
|
case OpenACCDirectiveKind::Init:
|
2023-12-05 12:41:57 -08:00
|
|
|
return Tok.getIdentifierInfo()->isStr("init");
|
2023-11-20 11:46:07 -08:00
|
|
|
case OpenACCDirectiveKind::Shutdown:
|
2023-12-05 12:41:57 -08:00
|
|
|
return Tok.getIdentifierInfo()->isStr("shutdown");
|
2023-11-20 11:46:07 -08:00
|
|
|
case OpenACCDirectiveKind::Set:
|
2023-12-05 12:41:57 -08:00
|
|
|
return Tok.getIdentifierInfo()->isStr("set");
|
2023-11-20 11:46:07 -08:00
|
|
|
case OpenACCDirectiveKind::Update:
|
2023-12-05 12:41:57 -08:00
|
|
|
return Tok.getIdentifierInfo()->isStr("update");
|
2023-11-20 11:46:07 -08:00
|
|
|
case OpenACCDirectiveKind::Invalid:
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
llvm_unreachable("Unknown 'Kind' Passed");
|
|
|
|
}
|
|
|
|
|
2023-11-21 07:46:12 -08:00
|
|
|
OpenACCDirectiveKind
|
|
|
|
ParseOpenACCEnterExitDataDirective(Parser &P, Token FirstTok,
|
|
|
|
OpenACCDirectiveKindEx ExtDirKind) {
|
|
|
|
Token SecondTok = P.getCurToken();
|
|
|
|
|
|
|
|
if (SecondTok.isAnnotation()) {
|
2023-12-05 12:41:57 -08:00
|
|
|
P.Diag(FirstTok, diag::err_acc_invalid_directive)
|
|
|
|
<< 0 << FirstTok.getIdentifierInfo();
|
2023-11-21 07:46:12 -08:00
|
|
|
return OpenACCDirectiveKind::Invalid;
|
|
|
|
}
|
|
|
|
|
2023-12-05 12:41:57 -08:00
|
|
|
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();
|
2023-11-21 07:46:12 -08:00
|
|
|
return OpenACCDirectiveKind::Invalid;
|
|
|
|
}
|
|
|
|
|
|
|
|
P.ConsumeToken();
|
|
|
|
|
|
|
|
return ExtDirKind == OpenACCDirectiveKindEx::Enter
|
|
|
|
? OpenACCDirectiveKind::EnterData
|
|
|
|
: OpenACCDirectiveKind::ExitData;
|
|
|
|
}
|
|
|
|
|
2023-11-21 11:08:48 -08:00
|
|
|
OpenACCAtomicKind ParseOpenACCAtomicKind(Parser &P) {
|
|
|
|
Token AtomicClauseToken = P.getCurToken();
|
|
|
|
|
|
|
|
// #pragma acc atomic is equivilent to update:
|
|
|
|
if (AtomicClauseToken.isAnnotation())
|
|
|
|
return OpenACCAtomicKind::Update;
|
|
|
|
|
2023-12-05 12:41:57 -08:00
|
|
|
OpenACCAtomicKind AtomicKind = getOpenACCAtomicKind(AtomicClauseToken);
|
2023-11-21 11:08:48 -08:00
|
|
|
|
|
|
|
// 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;
|
|
|
|
}
|
|
|
|
|
2023-11-17 10:47:42 -08:00
|
|
|
// Parse and consume the tokens for OpenACC Directive/Construct kinds.
|
|
|
|
OpenACCDirectiveKind ParseOpenACCDirectiveKind(Parser &P) {
|
|
|
|
Token FirstTok = P.getCurToken();
|
2023-11-17 13:47:36 -08:00
|
|
|
|
|
|
|
// 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;
|
|
|
|
}
|
|
|
|
|
2023-11-17 10:47:42 -08:00
|
|
|
P.ConsumeToken();
|
|
|
|
|
2023-12-05 12:41:57 -08:00
|
|
|
OpenACCDirectiveKindEx ExDirKind = getOpenACCDirectiveKind(FirstTok);
|
2023-11-21 07:46:12 -08:00
|
|
|
|
|
|
|
// 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) {
|
2023-12-05 12:41:57 -08:00
|
|
|
case OpenACCDirectiveKindEx::Invalid: {
|
|
|
|
if (!FirstTok.is(tok::identifier))
|
|
|
|
P.Diag(FirstTok, diag::err_expected) << tok::identifier;
|
|
|
|
else
|
|
|
|
P.Diag(FirstTok, diag::err_acc_invalid_directive)
|
|
|
|
<< 0 << FirstTok.getIdentifierInfo();
|
2023-11-21 07:46:12 -08:00
|
|
|
return OpenACCDirectiveKind::Invalid;
|
2023-12-05 12:41:57 -08:00
|
|
|
}
|
2023-11-21 07:46:12 -08:00
|
|
|
case OpenACCDirectiveKindEx::Enter:
|
|
|
|
case OpenACCDirectiveKindEx::Exit:
|
2023-12-05 12:41:57 -08:00
|
|
|
return ParseOpenACCEnterExitDataDirective(P, FirstTok, ExDirKind);
|
2023-11-21 07:46:12 -08:00
|
|
|
}
|
|
|
|
}
|
2023-11-17 10:47:42 -08:00
|
|
|
|
2023-11-21 07:46:12 -08:00
|
|
|
OpenACCDirectiveKind DirKind = static_cast<OpenACCDirectiveKind>(ExDirKind);
|
2023-11-17 10:47:42 -08:00
|
|
|
|
2023-11-20 11:46:07 -08:00
|
|
|
// 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() &&
|
2023-12-05 12:41:57 -08:00
|
|
|
isOpenACCDirectiveKind(OpenACCDirectiveKind::Loop, SecondTok)) {
|
2023-11-20 11:46:07 -08:00
|
|
|
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;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2023-11-17 10:47:42 -08:00
|
|
|
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);
|
|
|
|
}
|
|
|
|
|
2023-11-27 06:49:29 -08:00
|
|
|
} // namespace
|
|
|
|
|
2023-12-06 06:32:45 -08:00
|
|
|
ExprResult Parser::ParseOpenACCIDExpression() {
|
2023-11-27 06:49:29 -08:00
|
|
|
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.
|
|
|
|
|
2023-12-06 06:32:45 -08:00
|
|
|
if (Tok.isNot(tok::identifier)) {
|
|
|
|
Diag(Tok, diag::err_expected) << tok::identifier;
|
2023-11-27 06:49:29 -08:00
|
|
|
return ExprError();
|
2023-12-06 06:32:45 -08:00
|
|
|
}
|
2023-11-27 06:49:29 -08:00
|
|
|
|
|
|
|
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);
|
|
|
|
}
|
|
|
|
|
2023-12-06 06:32:45 -08:00
|
|
|
/// OpenACC 3.3, section 2.10:
|
|
|
|
/// A 'var' in a cache directive must be a single array element or a simple
|
|
|
|
/// subarray. In C and C++, a simple subarray is an array name followed by an
|
|
|
|
/// extended array range specification in brackets, with a start and length such
|
|
|
|
/// as:
|
|
|
|
///
|
|
|
|
/// arr[lower:length]
|
|
|
|
///
|
|
|
|
bool Parser::ParseOpenACCCacheVar() {
|
|
|
|
ExprResult ArrayName = ParseOpenACCIDExpression();
|
|
|
|
if (ArrayName.isInvalid())
|
|
|
|
return true;
|
|
|
|
|
|
|
|
// If the expression is invalid, just continue parsing the brackets, there
|
|
|
|
// is likely other useful diagnostics we can emit inside of those.
|
|
|
|
|
|
|
|
BalancedDelimiterTracker SquareBrackets(*this, tok::l_square,
|
|
|
|
tok::annot_pragma_openacc_end);
|
|
|
|
|
|
|
|
// Square brackets are required, so error here, and try to recover by moving
|
|
|
|
// until the next comma, or the close paren/end of pragma.
|
|
|
|
if (SquareBrackets.expectAndConsume()) {
|
|
|
|
SkipUntil(tok::comma, tok::r_paren, tok::annot_pragma_openacc_end,
|
|
|
|
Parser::StopBeforeMatch);
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
ExprResult Lower = getActions().CorrectDelayedTyposInExpr(ParseExpression());
|
|
|
|
if (Lower.isInvalid())
|
|
|
|
return true;
|
|
|
|
|
|
|
|
// The 'length' expression is optional, as this could be a single array
|
|
|
|
// element. If there is no colon, we can treat it as that.
|
|
|
|
if (getCurToken().is(tok::colon)) {
|
|
|
|
ConsumeToken();
|
|
|
|
ExprResult Length =
|
|
|
|
getActions().CorrectDelayedTyposInExpr(ParseExpression());
|
|
|
|
if (Length.isInvalid())
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
// Diagnose the square bracket being in the wrong place and continue.
|
|
|
|
return SquareBrackets.consumeClose();
|
|
|
|
}
|
|
|
|
|
|
|
|
/// OpenACC 3.3, section 2.10:
|
|
|
|
/// In C and C++, the syntax of the cache directive is:
|
|
|
|
///
|
|
|
|
/// #pragma acc cache ([readonly:]var-list) new-line
|
|
|
|
void Parser::ParseOpenACCCacheVarList() {
|
|
|
|
// If this is the end of the line, just return 'false' and count on the close
|
|
|
|
// paren diagnostic to catch the issue.
|
|
|
|
if (getCurToken().isAnnotation())
|
|
|
|
return;
|
|
|
|
|
|
|
|
// The VarList is an optional `readonly:` followed by a list of a variable
|
|
|
|
// specifications. First, see if we have `readonly:`, else we back-out and
|
|
|
|
// treat it like the beginning of a reference to a potentially-existing
|
|
|
|
// `readonly` variable.
|
|
|
|
if (getCurToken().is(tok::identifier) &&
|
|
|
|
getCurToken().getIdentifierInfo()->isStr("readonly") &&
|
|
|
|
NextToken().is(tok::colon)) {
|
|
|
|
// Consume both tokens.
|
|
|
|
ConsumeToken();
|
|
|
|
ConsumeToken();
|
|
|
|
// FIXME: Record that this is a 'readonly' so that we can use that during
|
|
|
|
// Sema/AST generation.
|
|
|
|
}
|
|
|
|
|
|
|
|
bool FirstArray = true;
|
|
|
|
while (!getCurToken().isOneOf(tok::r_paren, tok::annot_pragma_openacc_end)) {
|
|
|
|
if (!FirstArray)
|
|
|
|
ExpectAndConsume(tok::comma);
|
|
|
|
FirstArray = false;
|
|
|
|
if (ParseOpenACCCacheVar())
|
|
|
|
SkipUntil(tok::r_paren, tok::annot_pragma_openacc_end, tok::comma,
|
|
|
|
StopBeforeMatch);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2023-11-27 06:49:29 -08:00
|
|
|
void Parser::ParseOpenACCDirective() {
|
|
|
|
OpenACCDirectiveKind DirKind = ParseOpenACCDirectiveKind(*this);
|
2023-11-21 11:08:48 -08:00
|
|
|
|
|
|
|
// 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)
|
2023-11-27 06:49:29 -08:00
|
|
|
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: {
|
2023-12-06 06:32:45 -08:00
|
|
|
// Routine has an optional paren-wrapped name of a function in the local
|
|
|
|
// scope. We parse the name, emitting any diagnostics
|
|
|
|
ExprResult RoutineName = ParseOpenACCIDExpression();
|
2023-11-27 06:49:29 -08:00
|
|
|
// 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;
|
|
|
|
}
|
2023-12-06 06:32:45 -08:00
|
|
|
case OpenACCDirectiveKind::Cache:
|
|
|
|
ParseOpenACCCacheVarList();
|
|
|
|
// The ParseOpenACCCacheVarList function manages to recover from failures,
|
|
|
|
// so we can always consume the close.
|
|
|
|
T.consumeClose();
|
|
|
|
break;
|
2023-11-27 06:49:29 -08:00
|
|
|
}
|
2023-12-06 06:32:45 -08:00
|
|
|
} else if (DirKind == OpenACCDirectiveKind::Cache) {
|
|
|
|
// Cache's paren var-list is required, so error here if it isn't provided.
|
|
|
|
// We know that the consumeOpen above left the first non-paren here, so
|
|
|
|
// diagnose, then continue as if it was completely omitted.
|
|
|
|
Diag(Tok, diag::err_expected) << tok::l_paren;
|
2023-11-27 06:49:29 -08:00
|
|
|
}
|
2023-11-17 10:47:42 -08:00
|
|
|
|
|
|
|
// Parses the list of clauses, if present.
|
2023-11-27 06:49:29 -08:00
|
|
|
ParseOpenACCClauseList(*this);
|
2023-11-17 10:47:42 -08:00
|
|
|
|
2023-11-27 06:49:29 -08:00
|
|
|
Diag(getCurToken(), diag::warn_pragma_acc_unimplemented);
|
|
|
|
SkipUntil(tok::annot_pragma_openacc_end);
|
2023-11-17 10:47:42 -08:00
|
|
|
}
|
|
|
|
|
|
|
|
// Parse OpenACC directive on a declaration.
|
2023-11-17 07:28:33 -08:00
|
|
|
Parser::DeclGroupPtrTy Parser::ParseOpenACCDirectiveDecl() {
|
2023-11-17 10:47:42 -08:00
|
|
|
assert(Tok.is(tok::annot_pragma_openacc) && "expected OpenACC Start Token");
|
|
|
|
|
|
|
|
ParsingOpenACCDirectiveRAII DirScope(*this);
|
|
|
|
ConsumeAnnotationToken();
|
|
|
|
|
2023-11-27 06:49:29 -08:00
|
|
|
ParseOpenACCDirective();
|
2023-11-17 10:47:42 -08:00
|
|
|
|
2023-11-17 06:29:02 -08:00
|
|
|
return nullptr;
|
|
|
|
}
|
2023-11-17 10:47:42 -08:00
|
|
|
|
|
|
|
// Parse OpenACC Directive on a Statement.
|
2023-11-17 06:29:02 -08:00
|
|
|
StmtResult Parser::ParseOpenACCDirectiveStmt() {
|
2023-11-17 10:47:42 -08:00
|
|
|
assert(Tok.is(tok::annot_pragma_openacc) && "expected OpenACC Start Token");
|
|
|
|
|
|
|
|
ParsingOpenACCDirectiveRAII DirScope(*this);
|
|
|
|
ConsumeAnnotationToken();
|
|
|
|
|
2023-11-27 06:49:29 -08:00
|
|
|
ParseOpenACCDirective();
|
2023-11-17 10:47:42 -08:00
|
|
|
|
2023-11-17 06:29:02 -08:00
|
|
|
return StmtEmpty();
|
|
|
|
}
|