Skip to content

Commit

Permalink
[OpenACC] Add 'clause' parsing infrastructure plus a few clauses (#75052
Browse files Browse the repository at this point in the history
)

As we've now finished parsing the constructs, we're moving onto
implementing 'clause' parsing. While some are complicated and require
their own patch, the handful added here are simple to parse (that is,
    they are a single identifier).

This patch adds the infrastructure to parse these and a clause-list in
its entirety. This adds some complication to how we are diagnosing
parsing errors elsewhere, so a few changes were made to better recover
from errors.
  • Loading branch information
erichkeane committed Dec 19, 2023
1 parent cdc0392 commit fdee0a3
Show file tree
Hide file tree
Showing 9 changed files with 225 additions and 67 deletions.
4 changes: 1 addition & 3 deletions clang/include/clang/Basic/DiagnosticParseKinds.td
Original file line number Diff line number Diff line change
Expand Up @@ -1358,11 +1358,9 @@ def err_acc_unexpected_directive
def warn_pragma_acc_unimplemented
: Warning<"OpenACC directives not yet implemented, pragma ignored">,
InGroup<SourceUsesOpenACC>;
def warn_pragma_acc_unimplemented_clause_parsing
: Warning<"OpenACC clause parsing not yet implemented">,
InGroup<SourceUsesOpenACC>;
def err_acc_invalid_directive
: Error<"invalid OpenACC directive %select{%1|'%1 %2'}0">;
def err_acc_invalid_clause : Error<"invalid OpenACC clause %0">;
def err_acc_missing_directive : Error<"expected OpenACC directive">;
def err_acc_invalid_open_paren
: Error<"expected clause-list or newline in OpenACC directive">;
Expand Down
24 changes: 24 additions & 0 deletions clang/include/clang/Basic/OpenACCKinds.h
Original file line number Diff line number Diff line change
Expand Up @@ -69,6 +69,30 @@ enum class OpenACCAtomicKind {
Capture,
Invalid,
};

/// Represents the kind of an OpenACC clause.
enum class OpenACCClauseKind {
// 'finalize' clause, allowed on 'exit data' directive.
Finalize,
// 'if_present' clause, allowed on 'host_data' and 'update' directives.
IfPresent,
// 'seq' clause, allowed on 'loop' and 'routine' directives.
Seq,
// 'independent' clause, allowed on 'loop' directives.
Independent,
// 'auto' clause, allowed on 'loop' directives.
Auto,
// 'worker' clause, allowed on 'loop' and 'routine' directives.
Worker,
// 'vector' clause, allowed on 'loop' and 'routine' directives. Takes no
// arguments for 'routine', so the 'loop' version is not yet implemented
// completely.
Vector,
// 'nohost' clause, allowed on 'routine' directives.
NoHost,
// Represents an invalid clause, for the purposes of parsing.
Invalid,
};
} // namespace clang

#endif // LLVM_CLANG_BASIC_OPENACCKINDS_H
92 changes: 84 additions & 8 deletions clang/lib/Parse/ParseOpenACC.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -69,6 +69,29 @@ OpenACCDirectiveKindEx getOpenACCDirectiveKind(Token Tok) {
.Default(OpenACCDirectiveKindEx::Invalid);
}

// Translate single-token string representations to the OpenCC Clause Kind.
OpenACCClauseKind getOpenACCClauseKind(Token Tok) {
// auto is a keyword in some language modes, so make sure we parse it
// correctly.
if (Tok.is(tok::kw_auto))
return OpenACCClauseKind::Auto;

if (!Tok.is(tok::identifier))
return OpenACCClauseKind::Invalid;

return llvm::StringSwitch<OpenACCClauseKind>(
Tok.getIdentifierInfo()->getName())
.Case("auto", OpenACCClauseKind::Auto)
.Case("finalize", OpenACCClauseKind::Finalize)
.Case("if_present", OpenACCClauseKind::IfPresent)
.Case("independent", OpenACCClauseKind::Independent)
.Case("nohost", OpenACCClauseKind::NoHost)
.Case("seq", OpenACCClauseKind::Seq)
.Case("vector", OpenACCClauseKind::Vector)
.Case("worker", OpenACCClauseKind::Worker)
.Default(OpenACCClauseKind::Invalid);
}

// Since 'atomic' is effectively a compound directive, this will decode the
// second part of the directive.
OpenACCAtomicKind getOpenACCAtomicKind(Token Tok) {
Expand Down Expand Up @@ -164,6 +187,10 @@ ParseOpenACCEnterExitDataDirective(Parser &P, Token FirstTok,
return OpenACCDirectiveKind::Invalid;
}

// Consume the second name anyway, this way we can continue on without making
// this oddly look like a clause.
P.ConsumeAnyToken();

if (!isOpenACCDirectiveKind(OpenACCDirectiveKind::Data, SecondTok)) {
if (!SecondTok.is(tok::identifier))
P.Diag(SecondTok, diag::err_expected) << tok::identifier;
Expand All @@ -174,8 +201,6 @@ ParseOpenACCEnterExitDataDirective(Parser &P, Token FirstTok,
return OpenACCDirectiveKind::Invalid;
}

P.ConsumeToken();

return ExtDirKind == OpenACCDirectiveKindEx::Enter
? OpenACCDirectiveKind::EnterData
: OpenACCDirectiveKind::ExitData;
Expand Down Expand Up @@ -208,6 +233,10 @@ OpenACCDirectiveKind ParseOpenACCDirectiveKind(Parser &P) {
// introspect on the spelling before then.
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;
}

Expand Down Expand Up @@ -262,12 +291,57 @@ OpenACCDirectiveKind ParseOpenACCDirectiveKind(Parser &P) {
return DirKind;
}

// The OpenACC Clause List is a comma or space-delimited list of clauses (see
// the comment on ParseOpenACCClauseList). The concept of a 'clause' doesn't
// really have its owner grammar and each individual one has its own definition.
// However, they all are named with a single-identifier (or auto!) token,
// followed in some cases by either braces or parens.
bool ParseOpenACCClause(Parser &P) {
if (!P.getCurToken().isOneOf(tok::identifier, tok::kw_auto))
return P.Diag(P.getCurToken(), diag::err_expected) << tok::identifier;

OpenACCClauseKind Kind = getOpenACCClauseKind(P.getCurToken());

if (Kind == OpenACCClauseKind::Invalid)
return P.Diag(P.getCurToken(), diag::err_acc_invalid_clause)
<< P.getCurToken().getIdentifierInfo();

// Consume the clause name.
P.ConsumeToken();

// FIXME: For future clauses, we need to handle parens/etc below.
return false;
}

// Skip until we see the end of pragma token, but don't consume it. This is us
// just giving up on the rest of the pragma so we can continue executing. We
// have to do this because 'SkipUntil' considers paren balancing, which isn't
// what we want.
void SkipUntilEndOfDirective(Parser &P) {
while (P.getCurToken().isNot(tok::annot_pragma_openacc_end))
P.ConsumeAnyToken();
}

// OpenACC 3.3, section 1.7:
// To simplify the specification and convey appropriate constraint information,
// a pqr-list is a comma-separated list of pdr items. The one exception is a
// clause-list, which is a list of one or more clauses optionally separated by
// commas.
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);
bool FirstClause = true;
while (P.getCurToken().isNot(tok::annot_pragma_openacc_end)) {
// Comma is optional in a clause-list.
if (!FirstClause && P.getCurToken().is(tok::comma))
P.ConsumeToken();
FirstClause = false;

// Recovering from a bad clause is really difficult, so we just give up on
// error.
if (ParseOpenACCClause(P)) {
SkipUntilEndOfDirective(P);
return;
}
}
}

} // namespace
Expand Down Expand Up @@ -499,7 +573,9 @@ void Parser::ParseOpenACCDirective() {
ParseOpenACCClauseList(*this);

Diag(getCurToken(), diag::warn_pragma_acc_unimplemented);
SkipUntil(tok::annot_pragma_openacc_end);
assert(Tok.is(tok::annot_pragma_openacc_end) &&
"Didn't parse all OpenACC Clauses");
ConsumeAnnotationToken();
}

// Parse OpenACC directive on a declaration.
Expand Down
4 changes: 2 additions & 2 deletions clang/test/ParserOpenACC/parse-cache-construct.c
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ void func() {

for (int i = 0; i < 10; ++i) {
// expected-error@+3{{expected '('}}
// expected-warning@+2{{OpenACC clause parsing not yet implemented}}
// expected-error@+2{{invalid OpenACC clause 'clause'}}
// expected-warning@+1{{OpenACC directives not yet implemented, pragma ignored}}
#pragma acc cache clause list
}
Expand All @@ -25,7 +25,7 @@ void func() {
}

for (int i = 0; i < 10; ++i) {
// expected-warning@+2{{OpenACC clause parsing not yet implemented}}
// expected-error@+2{{invalid OpenACC clause 'clause'}}
// expected-warning@+1{{OpenACC directives not yet implemented, pragma ignored}}
#pragma acc cache() clause-list
}
Expand Down
64 changes: 64 additions & 0 deletions clang/test/ParserOpenACC/parse-clauses.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,64 @@
// RUN: %clang_cc1 %s -verify -fopenacc -std=c99
// RUNX: %clang_cc1 %s -verify -fopenacc
// RUNX: %clang_cc1 %s -verify -fopenacc -x c++

void func() {

// expected-warning@+1{{OpenACC directives not yet implemented, pragma ignored}}
#pragma acc enter data finalize

// expected-warning@+1{{OpenACC directives not yet implemented, pragma ignored}}
#pragma acc enter data finalize finalize

// expected-error@+2{{invalid OpenACC clause 'invalid'}}
// expected-warning@+1{{OpenACC directives not yet implemented, pragma ignored}}
#pragma acc enter data finalize invalid

// expected-error@+2{{invalid OpenACC clause 'invalid'}}
// expected-warning@+1{{OpenACC directives not yet implemented, pragma ignored}}
#pragma acc enter data finalize invalid invalid finalize

// expected-warning@+1{{OpenACC directives not yet implemented, pragma ignored}}
#pragma acc enter data seq finalize

// expected-warning@+1{{OpenACC directives not yet implemented, pragma ignored}}
#pragma acc host_data if_present

// expected-warning@+1{{OpenACC directives not yet implemented, pragma ignored}}
#pragma acc host_data if_present, if_present

// expected-warning@+1{{OpenACC directives not yet implemented, pragma ignored}}
#pragma acc loop seq independent auto

// expected-warning@+1{{OpenACC directives not yet implemented, pragma ignored}}
#pragma acc loop seq, independent auto

// expected-warning@+1{{OpenACC directives not yet implemented, pragma ignored}}
#pragma acc loop seq independent, auto

// expected-warning@+1{{OpenACC directives not yet implemented, pragma ignored}}
#pragma acc kernels loop seq independent auto

// expected-warning@+1{{OpenACC directives not yet implemented, pragma ignored}}
#pragma acc serial loop seq, independent auto

// expected-warning@+1{{OpenACC directives not yet implemented, pragma ignored}}
#pragma acc parallel loop seq independent, auto


// expected-error@+2{{expected identifier}}
// expected-warning@+1{{OpenACC directives not yet implemented, pragma ignored}}
#pragma acc loop , seq

// expected-error@+2{{expected identifier}}
// expected-warning@+1{{OpenACC directives not yet implemented, pragma ignored}}
#pragma acc loop seq,

}

// expected-warning@+1{{OpenACC directives not yet implemented, pragma ignored}}
#pragma acc routine worker, vector, seq, nohost
void bar();

// expected-warning@+1{{OpenACC directives not yet implemented, pragma ignored}}
#pragma acc routine(bar) worker, vector, seq, nohost

0 comments on commit fdee0a3

Please sign in to comment.