Skip to content

Commit

Permalink
[OpenACC] Implement 'default' clause parsing. (#77002)
Browse files Browse the repository at this point in the history
A simple clause that is permitted on a few different constructs,
  'default' takes a required parameter of either 'none' or 'present'.
  This patch implements parsing for it.
  • Loading branch information
erichkeane committed Jan 5, 2024
1 parent 1259c05 commit 0e8b09c
Show file tree
Hide file tree
Showing 4 changed files with 189 additions and 5 deletions.
2 changes: 2 additions & 0 deletions clang/include/clang/Basic/DiagnosticParseKinds.td
Original file line number Diff line number Diff line change
Expand Up @@ -1364,6 +1364,8 @@ 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">;
def err_acc_invalid_default_clause_kind
: Error<"invalid value for 'default' clause; expected 'present' or 'none'">;

// OpenMP support.
def warn_pragma_omp_ignored : Warning<
Expand Down
12 changes: 12 additions & 0 deletions clang/include/clang/Basic/OpenACCKinds.h
Original file line number Diff line number Diff line change
Expand Up @@ -90,9 +90,21 @@ enum class OpenACCClauseKind {
Vector,
/// 'nohost' clause, allowed on 'routine' directives.
NoHost,
/// 'default' clause, allowed on parallel, serial, kernel (and compound)
/// constructs.
Default,
/// Represents an invalid clause, for the purposes of parsing.
Invalid,
};

enum class OpenACCDefaultClauseKind {
/// 'none' option.
None,
/// 'present' option.
Present,
/// Not a valid option.
Invalid,
};
} // namespace clang

#endif // LLVM_CLANG_BASIC_OPENACCKINDS_H
84 changes: 79 additions & 5 deletions clang/lib/Parse/ParseOpenACC.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -76,12 +76,17 @@ OpenACCClauseKind getOpenACCClauseKind(Token Tok) {
if (Tok.is(tok::kw_auto))
return OpenACCClauseKind::Auto;

// default is a keyword, so make sure we parse it correctly.
if (Tok.is(tok::kw_default))
return OpenACCClauseKind::Default;

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

return llvm::StringSwitch<OpenACCClauseKind>(
Tok.getIdentifierInfo()->getName())
.Case("auto", OpenACCClauseKind::Auto)
.Case("default", OpenACCClauseKind::Default)
.Case("finalize", OpenACCClauseKind::Finalize)
.Case("if_present", OpenACCClauseKind::IfPresent)
.Case("independent", OpenACCClauseKind::Independent)
Expand All @@ -106,6 +111,17 @@ OpenACCAtomicKind getOpenACCAtomicKind(Token Tok) {
.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,
Expand Down Expand Up @@ -176,6 +192,22 @@ bool isOpenACCDirectiveKind(OpenACCDirectiveKind Kind, Token Tok) {
llvm_unreachable("Unknown 'Kind' Passed");
}

/// Used for cases where we expect an identifier-like token, but don't want to
/// give awkward error messages in cases where it is accidentially a keyword.
bool expectIdentifierOrKeyword(Parser &P) {
Token Tok = P.getCurToken();

if (Tok.is(tok::identifier))
return false;

if (!Tok.isAnnotation() && Tok.getIdentifierInfo() &&
Tok.getIdentifierInfo()->isKeyword(P.getLangOpts()))
return false;

P.Diag(P.getCurToken(), diag::err_expected) << tok::identifier;
return true;
}

OpenACCDirectiveKind
ParseOpenACCEnterExitDataDirective(Parser &P, Token FirstTok,
OpenACCDirectiveKindEx ExtDirKind) {
Expand Down Expand Up @@ -291,13 +323,56 @@ OpenACCDirectiveKind ParseOpenACCDirectiveKind(Parser &P) {
return DirKind;
}

bool ClauseHasRequiredParens(OpenACCClauseKind Kind) {
return Kind == OpenACCClauseKind::Default;
}

bool ParseOpenACCClauseParams(Parser &P, OpenACCClauseKind Kind) {
BalancedDelimiterTracker Parens(P, tok::l_paren,
tok::annot_pragma_openacc_end);

if (ClauseHasRequiredParens(Kind)) {
if (Parens.expectAndConsume()) {
// We are missing a paren, so assume that the person just forgot the
// parameter. Return 'false' so we try to continue on and parse the next
// clause.
P.SkipUntil(tok::comma, tok::r_paren, tok::annot_pragma_openacc_end,
Parser::StopBeforeMatch);
return false;
}

switch (Kind) {
case OpenACCClauseKind::Default: {
Token DefKindTok = P.getCurToken();

if (expectIdentifierOrKeyword(P))
break;

P.ConsumeToken();

if (getOpenACCDefaultClauseKind(DefKindTok) ==
OpenACCDefaultClauseKind::Invalid)
P.Diag(DefKindTok, diag::err_acc_invalid_default_clause_kind);

break;
}
default:
llvm_unreachable("Not a required parens type?");
}

return Parens.consumeClose();
}
// FIXME: Handle optional parens
return false;
}

// 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.
// However, they all are named with a single-identifier (or auto/default!)
// token, followed in some cases by either braces or parens.
bool ParseOpenACCClause(Parser &P) {
if (!P.getCurToken().isOneOf(tok::identifier, tok::kw_auto))
if (!P.getCurToken().isOneOf(tok::identifier, tok::kw_auto, tok::kw_default))
return P.Diag(P.getCurToken(), diag::err_expected) << tok::identifier;

OpenACCClauseKind Kind = getOpenACCClauseKind(P.getCurToken());
Expand All @@ -309,8 +384,7 @@ bool ParseOpenACCClause(Parser &P) {
// Consume the clause name.
P.ConsumeToken();

// FIXME: For future clauses, we need to handle parens/etc below.
return false;
return ParseOpenACCClauseParams(P, Kind);
}

// Skip until we see the end of pragma token, but don't consume it. This is us
Expand Down
96 changes: 96 additions & 0 deletions clang/test/ParserOpenACC/parse-clauses.c
Original file line number Diff line number Diff line change
Expand Up @@ -54,6 +54,102 @@ void func() {
// expected-warning@+1{{OpenACC directives not yet implemented, pragma ignored}}
#pragma acc loop seq,

}

void DefaultClause() {
// expected-error@+2{{expected '('}}
// expected-warning@+1{{OpenACC directives not yet implemented, pragma ignored}}
#pragma acc serial loop default
for(;;){}

// expected-error@+2{{expected '('}}
// expected-warning@+1{{OpenACC directives not yet implemented, pragma ignored}}
#pragma acc serial default seq
for(;;){}

// expected-error@+2{{expected '('}}
// expected-warning@+1{{OpenACC directives not yet implemented, pragma ignored}}
#pragma acc serial default, seq
for(;;){}

// expected-error@+4{{expected identifier}}
// expected-error@+3{{expected ')'}}
// expected-note@+2{{to match this '('}}
// expected-warning@+1{{OpenACC directives not yet implemented, pragma ignored}}
#pragma acc serial default(
for(;;){}

// expected-error@+4{{invalid value for 'default' clause; expected 'present' or 'none'}}
// expected-error@+3{{expected ')'}}
// expected-note@+2{{to match this '('}}
// expected-warning@+1{{OpenACC directives not yet implemented, pragma ignored}}
#pragma acc serial default( seq
for(;;){}

// expected-error@+4{{expected identifier}}
// expected-error@+3{{expected ')'}}
// expected-note@+2{{to match this '('}}
// expected-warning@+1{{OpenACC directives not yet implemented, pragma ignored}}
#pragma acc serial default(, seq
for(;;){}

// expected-error@+3{{expected '('}}
// expected-error@+2{{expected identifier}}
// expected-warning@+1{{OpenACC directives not yet implemented, pragma ignored}}
#pragma acc serial default)
for(;;){}

// expected-error@+3{{expected '('}}
// expected-error@+2{{expected identifier}}
// expected-warning@+1{{OpenACC directives not yet implemented, pragma ignored}}
#pragma acc serial default) seq
for(;;){}

// expected-error@+3{{expected '('}}
// expected-error@+2{{expected identifier}}
// expected-warning@+1{{OpenACC directives not yet implemented, pragma ignored}}
#pragma acc serial default), seq
for(;;){}

// expected-error@+2{{expected identifier}}
// expected-warning@+1{{OpenACC directives not yet implemented, pragma ignored}}
#pragma acc serial default()
for(;;){}

// expected-error@+2{{expected identifier}}
// expected-warning@+1{{OpenACC directives not yet implemented, pragma ignored}}
#pragma acc serial default() seq
for(;;){}

// expected-error@+2{{expected identifier}}
// expected-warning@+1{{OpenACC directives not yet implemented, pragma ignored}}
#pragma acc serial default(), seq
for(;;){}

// expected-error@+2{{invalid value for 'default' clause; expected 'present' or 'none'}}
// expected-warning@+1{{OpenACC directives not yet implemented, pragma ignored}}
#pragma acc serial default(invalid)
for(;;){}

// expected-error@+2{{invalid value for 'default' clause; expected 'present' or 'none'}}
// expected-warning@+1{{OpenACC directives not yet implemented, pragma ignored}}
#pragma acc serial default(auto) seq
for(;;){}

// expected-error@+2{{invalid value for 'default' clause; expected 'present' or 'none'}}
// expected-warning@+1{{OpenACC directives not yet implemented, pragma ignored}}
#pragma acc serial default(invalid), seq
for(;;){}

// expected-warning@+1{{OpenACC directives not yet implemented, pragma ignored}}
#pragma acc serial default(none)
for(;;){}

// expected-warning@+1{{OpenACC directives not yet implemented, pragma ignored}}
#pragma acc serial default(present), seq
for(;;){}


}

// expected-warning@+1{{OpenACC directives not yet implemented, pragma ignored}}
Expand Down

0 comments on commit 0e8b09c

Please sign in to comment.