Skip to content

Commit fdee0a3

Browse files
authored
[OpenACC] Add 'clause' parsing infrastructure plus a few clauses (llvm#75052)
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.
1 parent cdc0392 commit fdee0a3

File tree

9 files changed

+225
-67
lines changed

9 files changed

+225
-67
lines changed

clang/include/clang/Basic/DiagnosticParseKinds.td

Lines changed: 1 addition & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1358,11 +1358,9 @@ def err_acc_unexpected_directive
13581358
def warn_pragma_acc_unimplemented
13591359
: Warning<"OpenACC directives not yet implemented, pragma ignored">,
13601360
InGroup<SourceUsesOpenACC>;
1361-
def warn_pragma_acc_unimplemented_clause_parsing
1362-
: Warning<"OpenACC clause parsing not yet implemented">,
1363-
InGroup<SourceUsesOpenACC>;
13641361
def err_acc_invalid_directive
13651362
: Error<"invalid OpenACC directive %select{%1|'%1 %2'}0">;
1363+
def err_acc_invalid_clause : Error<"invalid OpenACC clause %0">;
13661364
def err_acc_missing_directive : Error<"expected OpenACC directive">;
13671365
def err_acc_invalid_open_paren
13681366
: Error<"expected clause-list or newline in OpenACC directive">;

clang/include/clang/Basic/OpenACCKinds.h

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -69,6 +69,30 @@ enum class OpenACCAtomicKind {
6969
Capture,
7070
Invalid,
7171
};
72+
73+
/// Represents the kind of an OpenACC clause.
74+
enum class OpenACCClauseKind {
75+
// 'finalize' clause, allowed on 'exit data' directive.
76+
Finalize,
77+
// 'if_present' clause, allowed on 'host_data' and 'update' directives.
78+
IfPresent,
79+
// 'seq' clause, allowed on 'loop' and 'routine' directives.
80+
Seq,
81+
// 'independent' clause, allowed on 'loop' directives.
82+
Independent,
83+
// 'auto' clause, allowed on 'loop' directives.
84+
Auto,
85+
// 'worker' clause, allowed on 'loop' and 'routine' directives.
86+
Worker,
87+
// 'vector' clause, allowed on 'loop' and 'routine' directives. Takes no
88+
// arguments for 'routine', so the 'loop' version is not yet implemented
89+
// completely.
90+
Vector,
91+
// 'nohost' clause, allowed on 'routine' directives.
92+
NoHost,
93+
// Represents an invalid clause, for the purposes of parsing.
94+
Invalid,
95+
};
7296
} // namespace clang
7397

7498
#endif // LLVM_CLANG_BASIC_OPENACCKINDS_H

clang/lib/Parse/ParseOpenACC.cpp

Lines changed: 84 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -69,6 +69,29 @@ OpenACCDirectiveKindEx getOpenACCDirectiveKind(Token Tok) {
6969
.Default(OpenACCDirectiveKindEx::Invalid);
7070
}
7171

72+
// Translate single-token string representations to the OpenCC Clause Kind.
73+
OpenACCClauseKind getOpenACCClauseKind(Token Tok) {
74+
// auto is a keyword in some language modes, so make sure we parse it
75+
// correctly.
76+
if (Tok.is(tok::kw_auto))
77+
return OpenACCClauseKind::Auto;
78+
79+
if (!Tok.is(tok::identifier))
80+
return OpenACCClauseKind::Invalid;
81+
82+
return llvm::StringSwitch<OpenACCClauseKind>(
83+
Tok.getIdentifierInfo()->getName())
84+
.Case("auto", OpenACCClauseKind::Auto)
85+
.Case("finalize", OpenACCClauseKind::Finalize)
86+
.Case("if_present", OpenACCClauseKind::IfPresent)
87+
.Case("independent", OpenACCClauseKind::Independent)
88+
.Case("nohost", OpenACCClauseKind::NoHost)
89+
.Case("seq", OpenACCClauseKind::Seq)
90+
.Case("vector", OpenACCClauseKind::Vector)
91+
.Case("worker", OpenACCClauseKind::Worker)
92+
.Default(OpenACCClauseKind::Invalid);
93+
}
94+
7295
// Since 'atomic' is effectively a compound directive, this will decode the
7396
// second part of the directive.
7497
OpenACCAtomicKind getOpenACCAtomicKind(Token Tok) {
@@ -164,6 +187,10 @@ ParseOpenACCEnterExitDataDirective(Parser &P, Token FirstTok,
164187
return OpenACCDirectiveKind::Invalid;
165188
}
166189

190+
// Consume the second name anyway, this way we can continue on without making
191+
// this oddly look like a clause.
192+
P.ConsumeAnyToken();
193+
167194
if (!isOpenACCDirectiveKind(OpenACCDirectiveKind::Data, SecondTok)) {
168195
if (!SecondTok.is(tok::identifier))
169196
P.Diag(SecondTok, diag::err_expected) << tok::identifier;
@@ -174,8 +201,6 @@ ParseOpenACCEnterExitDataDirective(Parser &P, Token FirstTok,
174201
return OpenACCDirectiveKind::Invalid;
175202
}
176203

177-
P.ConsumeToken();
178-
179204
return ExtDirKind == OpenACCDirectiveKindEx::Enter
180205
? OpenACCDirectiveKind::EnterData
181206
: OpenACCDirectiveKind::ExitData;
@@ -208,6 +233,10 @@ OpenACCDirectiveKind ParseOpenACCDirectiveKind(Parser &P) {
208233
// introspect on the spelling before then.
209234
if (FirstTok.isNot(tok::identifier)) {
210235
P.Diag(FirstTok, diag::err_acc_missing_directive);
236+
237+
if (P.getCurToken().isNot(tok::annot_pragma_openacc_end))
238+
P.ConsumeAnyToken();
239+
211240
return OpenACCDirectiveKind::Invalid;
212241
}
213242

@@ -262,12 +291,57 @@ OpenACCDirectiveKind ParseOpenACCDirectiveKind(Parser &P) {
262291
return DirKind;
263292
}
264293

294+
// The OpenACC Clause List is a comma or space-delimited list of clauses (see
295+
// the comment on ParseOpenACCClauseList). The concept of a 'clause' doesn't
296+
// really have its owner grammar and each individual one has its own definition.
297+
// However, they all are named with a single-identifier (or auto!) token,
298+
// followed in some cases by either braces or parens.
299+
bool ParseOpenACCClause(Parser &P) {
300+
if (!P.getCurToken().isOneOf(tok::identifier, tok::kw_auto))
301+
return P.Diag(P.getCurToken(), diag::err_expected) << tok::identifier;
302+
303+
OpenACCClauseKind Kind = getOpenACCClauseKind(P.getCurToken());
304+
305+
if (Kind == OpenACCClauseKind::Invalid)
306+
return P.Diag(P.getCurToken(), diag::err_acc_invalid_clause)
307+
<< P.getCurToken().getIdentifierInfo();
308+
309+
// Consume the clause name.
310+
P.ConsumeToken();
311+
312+
// FIXME: For future clauses, we need to handle parens/etc below.
313+
return false;
314+
}
315+
316+
// Skip until we see the end of pragma token, but don't consume it. This is us
317+
// just giving up on the rest of the pragma so we can continue executing. We
318+
// have to do this because 'SkipUntil' considers paren balancing, which isn't
319+
// what we want.
320+
void SkipUntilEndOfDirective(Parser &P) {
321+
while (P.getCurToken().isNot(tok::annot_pragma_openacc_end))
322+
P.ConsumeAnyToken();
323+
}
324+
325+
// OpenACC 3.3, section 1.7:
326+
// To simplify the specification and convey appropriate constraint information,
327+
// a pqr-list is a comma-separated list of pdr items. The one exception is a
328+
// clause-list, which is a list of one or more clauses optionally separated by
329+
// commas.
265330
void ParseOpenACCClauseList(Parser &P) {
266-
// FIXME: In the future, we'll start parsing the clauses here, but for now we
267-
// haven't implemented that, so just emit the unimplemented diagnostic and
268-
// fail reasonably.
269-
if (P.getCurToken().isNot(tok::annot_pragma_openacc_end))
270-
P.Diag(P.getCurToken(), diag::warn_pragma_acc_unimplemented_clause_parsing);
331+
bool FirstClause = true;
332+
while (P.getCurToken().isNot(tok::annot_pragma_openacc_end)) {
333+
// Comma is optional in a clause-list.
334+
if (!FirstClause && P.getCurToken().is(tok::comma))
335+
P.ConsumeToken();
336+
FirstClause = false;
337+
338+
// Recovering from a bad clause is really difficult, so we just give up on
339+
// error.
340+
if (ParseOpenACCClause(P)) {
341+
SkipUntilEndOfDirective(P);
342+
return;
343+
}
344+
}
271345
}
272346

273347
} // namespace
@@ -499,7 +573,9 @@ void Parser::ParseOpenACCDirective() {
499573
ParseOpenACCClauseList(*this);
500574

501575
Diag(getCurToken(), diag::warn_pragma_acc_unimplemented);
502-
SkipUntil(tok::annot_pragma_openacc_end);
576+
assert(Tok.is(tok::annot_pragma_openacc_end) &&
577+
"Didn't parse all OpenACC Clauses");
578+
ConsumeAnnotationToken();
503579
}
504580

505581
// Parse OpenACC directive on a declaration.

clang/test/ParserOpenACC/parse-cache-construct.c

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,7 @@ void func() {
1414

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

2727
for (int i = 0; i < 10; ++i) {
28-
// expected-warning@+2{{OpenACC clause parsing not yet implemented}}
28+
// expected-error@+2{{invalid OpenACC clause 'clause'}}
2929
// expected-warning@+1{{OpenACC directives not yet implemented, pragma ignored}}
3030
#pragma acc cache() clause-list
3131
}
Lines changed: 64 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,64 @@
1+
// RUN: %clang_cc1 %s -verify -fopenacc -std=c99
2+
// RUNX: %clang_cc1 %s -verify -fopenacc
3+
// RUNX: %clang_cc1 %s -verify -fopenacc -x c++
4+
5+
void func() {
6+
7+
// expected-warning@+1{{OpenACC directives not yet implemented, pragma ignored}}
8+
#pragma acc enter data finalize
9+
10+
// expected-warning@+1{{OpenACC directives not yet implemented, pragma ignored}}
11+
#pragma acc enter data finalize finalize
12+
13+
// expected-error@+2{{invalid OpenACC clause 'invalid'}}
14+
// expected-warning@+1{{OpenACC directives not yet implemented, pragma ignored}}
15+
#pragma acc enter data finalize invalid
16+
17+
// expected-error@+2{{invalid OpenACC clause 'invalid'}}
18+
// expected-warning@+1{{OpenACC directives not yet implemented, pragma ignored}}
19+
#pragma acc enter data finalize invalid invalid finalize
20+
21+
// expected-warning@+1{{OpenACC directives not yet implemented, pragma ignored}}
22+
#pragma acc enter data seq finalize
23+
24+
// expected-warning@+1{{OpenACC directives not yet implemented, pragma ignored}}
25+
#pragma acc host_data if_present
26+
27+
// expected-warning@+1{{OpenACC directives not yet implemented, pragma ignored}}
28+
#pragma acc host_data if_present, if_present
29+
30+
// expected-warning@+1{{OpenACC directives not yet implemented, pragma ignored}}
31+
#pragma acc loop seq independent auto
32+
33+
// expected-warning@+1{{OpenACC directives not yet implemented, pragma ignored}}
34+
#pragma acc loop seq, independent auto
35+
36+
// expected-warning@+1{{OpenACC directives not yet implemented, pragma ignored}}
37+
#pragma acc loop seq independent, auto
38+
39+
// expected-warning@+1{{OpenACC directives not yet implemented, pragma ignored}}
40+
#pragma acc kernels loop seq independent auto
41+
42+
// expected-warning@+1{{OpenACC directives not yet implemented, pragma ignored}}
43+
#pragma acc serial loop seq, independent auto
44+
45+
// expected-warning@+1{{OpenACC directives not yet implemented, pragma ignored}}
46+
#pragma acc parallel loop seq independent, auto
47+
48+
49+
// expected-error@+2{{expected identifier}}
50+
// expected-warning@+1{{OpenACC directives not yet implemented, pragma ignored}}
51+
#pragma acc loop , seq
52+
53+
// expected-error@+2{{expected identifier}}
54+
// expected-warning@+1{{OpenACC directives not yet implemented, pragma ignored}}
55+
#pragma acc loop seq,
56+
57+
}
58+
59+
// expected-warning@+1{{OpenACC directives not yet implemented, pragma ignored}}
60+
#pragma acc routine worker, vector, seq, nohost
61+
void bar();
62+
63+
// expected-warning@+1{{OpenACC directives not yet implemented, pragma ignored}}
64+
#pragma acc routine(bar) worker, vector, seq, nohost

0 commit comments

Comments
 (0)