Skip to content

Commit 721558a

Browse files
authored
[OpenACC] Implement 'cache' construct parsing (#74324)
The 'cache' construct takes a list of 'vars', which are array-section style definitions. This patch implements the parsing, leaving the lower bound and length of the bound as expressions, so that we can validate they are the correct 'thing' in sema.
1 parent b0b69fd commit 721558a

File tree

5 files changed

+325
-8
lines changed

5 files changed

+325
-8
lines changed

clang/include/clang/Basic/OpenACCKinds.h

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -34,7 +34,7 @@ enum class OpenACCDirectiveKind {
3434

3535
// Misc.
3636
Loop,
37-
// FIXME: 'cache'
37+
Cache,
3838

3939
// Combined Constructs.
4040
ParallelLoop,

clang/include/clang/Parse/Parser.h

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3538,7 +3538,12 @@ class Parser : public CodeCompletionHandler {
35383538

35393539
private:
35403540
void ParseOpenACCDirective();
3541-
ExprResult ParseOpenACCRoutineName();
3541+
/// Helper that parses an ID Expression based on the language options.
3542+
ExprResult ParseOpenACCIDExpression();
3543+
/// Parses the variable list for the `cache` construct.
3544+
void ParseOpenACCCacheVarList();
3545+
/// Parses a single variable in a variable list for the 'cache' construct.
3546+
bool ParseOpenACCCacheVar();
35423547

35433548
private:
35443549
//===--------------------------------------------------------------------===//

clang/lib/Parse/ParseOpenACC.cpp

Lines changed: 101 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -45,6 +45,7 @@ OpenACCDirectiveKindEx getOpenACCDirectiveKind(StringRef Name) {
4545
.Case("data", OpenACCDirectiveKind::Data)
4646
.Case("host_data", OpenACCDirectiveKind::HostData)
4747
.Case("loop", OpenACCDirectiveKind::Loop)
48+
.Case("cache", OpenACCDirectiveKind::Cache)
4849
.Case("atomic", OpenACCDirectiveKind::Atomic)
4950
.Case("routine", OpenACCDirectiveKind::Routine)
5051
.Case("declare", OpenACCDirectiveKind::Declare)
@@ -88,6 +89,8 @@ bool isOpenACCDirectiveKind(OpenACCDirectiveKind Kind, StringRef Tok) {
8889
return Tok == "host_data";
8990
case OpenACCDirectiveKind::Loop:
9091
return Tok == "loop";
92+
case OpenACCDirectiveKind::Cache:
93+
return Tok == "cache";
9194

9295
case OpenACCDirectiveKind::ParallelLoop:
9396
case OpenACCDirectiveKind::SerialLoop:
@@ -237,19 +240,18 @@ void ParseOpenACCClauseList(Parser &P) {
237240

238241
} // namespace
239242

240-
// Routine has an optional paren-wrapped name of a function in the local scope.
241-
// We parse the name, emitting any diagnostics
242-
ExprResult Parser::ParseOpenACCRoutineName() {
243-
243+
ExprResult Parser::ParseOpenACCIDExpression() {
244244
ExprResult Res;
245245
if (getLangOpts().CPlusPlus) {
246246
Res = ParseCXXIdExpression(/*isAddressOfOperand=*/false);
247247
} else {
248248
// There isn't anything quite the same as ParseCXXIdExpression for C, so we
249249
// need to get the identifier, then call into Sema ourselves.
250250

251-
if (expectIdentifier())
251+
if (Tok.isNot(tok::identifier)) {
252+
Diag(Tok, diag::err_expected) << tok::identifier;
252253
return ExprError();
254+
}
253255

254256
Token FuncName = getCurToken();
255257
UnqualifiedId Name;
@@ -268,6 +270,86 @@ ExprResult Parser::ParseOpenACCRoutineName() {
268270
return getActions().CorrectDelayedTyposInExpr(Res);
269271
}
270272

273+
/// OpenACC 3.3, section 2.10:
274+
/// A 'var' in a cache directive must be a single array element or a simple
275+
/// subarray. In C and C++, a simple subarray is an array name followed by an
276+
/// extended array range specification in brackets, with a start and length such
277+
/// as:
278+
///
279+
/// arr[lower:length]
280+
///
281+
bool Parser::ParseOpenACCCacheVar() {
282+
ExprResult ArrayName = ParseOpenACCIDExpression();
283+
if (ArrayName.isInvalid())
284+
return true;
285+
286+
// If the expression is invalid, just continue parsing the brackets, there
287+
// is likely other useful diagnostics we can emit inside of those.
288+
289+
BalancedDelimiterTracker SquareBrackets(*this, tok::l_square,
290+
tok::annot_pragma_openacc_end);
291+
292+
// Square brackets are required, so error here, and try to recover by moving
293+
// until the next comma, or the close paren/end of pragma.
294+
if (SquareBrackets.expectAndConsume()) {
295+
SkipUntil(tok::comma, tok::r_paren, tok::annot_pragma_openacc_end,
296+
Parser::StopBeforeMatch);
297+
return true;
298+
}
299+
300+
ExprResult Lower = getActions().CorrectDelayedTyposInExpr(ParseExpression());
301+
if (Lower.isInvalid())
302+
return true;
303+
304+
// The 'length' expression is optional, as this could be a single array
305+
// element. If there is no colon, we can treat it as that.
306+
if (getCurToken().is(tok::colon)) {
307+
ConsumeToken();
308+
ExprResult Length =
309+
getActions().CorrectDelayedTyposInExpr(ParseExpression());
310+
if (Length.isInvalid())
311+
return true;
312+
}
313+
314+
// Diagnose the square bracket being in the wrong place and continue.
315+
return SquareBrackets.consumeClose();
316+
}
317+
318+
/// OpenACC 3.3, section 2.10:
319+
/// In C and C++, the syntax of the cache directive is:
320+
///
321+
/// #pragma acc cache ([readonly:]var-list) new-line
322+
void Parser::ParseOpenACCCacheVarList() {
323+
// If this is the end of the line, just return 'false' and count on the close
324+
// paren diagnostic to catch the issue.
325+
if (getCurToken().isAnnotation())
326+
return;
327+
328+
// The VarList is an optional `readonly:` followed by a list of a variable
329+
// specifications. First, see if we have `readonly:`, else we back-out and
330+
// treat it like the beginning of a reference to a potentially-existing
331+
// `readonly` variable.
332+
if (getCurToken().is(tok::identifier) &&
333+
getCurToken().getIdentifierInfo()->isStr("readonly") &&
334+
NextToken().is(tok::colon)) {
335+
// Consume both tokens.
336+
ConsumeToken();
337+
ConsumeToken();
338+
// FIXME: Record that this is a 'readonly' so that we can use that during
339+
// Sema/AST generation.
340+
}
341+
342+
bool FirstArray = true;
343+
while (!getCurToken().isOneOf(tok::r_paren, tok::annot_pragma_openacc_end)) {
344+
if (!FirstArray)
345+
ExpectAndConsume(tok::comma);
346+
FirstArray = false;
347+
if (ParseOpenACCCacheVar())
348+
SkipUntil(tok::r_paren, tok::annot_pragma_openacc_end, tok::comma,
349+
StopBeforeMatch);
350+
}
351+
}
352+
271353
void Parser::ParseOpenACCDirective() {
272354
OpenACCDirectiveKind DirKind = ParseOpenACCDirectiveKind(*this);
273355

@@ -289,7 +371,9 @@ void Parser::ParseOpenACCDirective() {
289371
T.skipToEnd();
290372
break;
291373
case OpenACCDirectiveKind::Routine: {
292-
ExprResult RoutineName = ParseOpenACCRoutineName();
374+
// Routine has an optional paren-wrapped name of a function in the local
375+
// scope. We parse the name, emitting any diagnostics
376+
ExprResult RoutineName = ParseOpenACCIDExpression();
293377
// If the routine name is invalid, just skip until the closing paren to
294378
// recover more gracefully.
295379
if (RoutineName.isInvalid())
@@ -298,7 +382,18 @@ void Parser::ParseOpenACCDirective() {
298382
T.consumeClose();
299383
break;
300384
}
385+
case OpenACCDirectiveKind::Cache:
386+
ParseOpenACCCacheVarList();
387+
// The ParseOpenACCCacheVarList function manages to recover from failures,
388+
// so we can always consume the close.
389+
T.consumeClose();
390+
break;
301391
}
392+
} else if (DirKind == OpenACCDirectiveKind::Cache) {
393+
// Cache's paren var-list is required, so error here if it isn't provided.
394+
// We know that the consumeOpen above left the first non-paren here, so
395+
// diagnose, then continue as if it was completely omitted.
396+
Diag(Tok, diag::err_expected) << tok::l_paren;
302397
}
303398

304399
// Parses the list of clauses, if present.
Lines changed: 166 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,166 @@
1+
// RUN: %clang_cc1 %s -verify -fopenacc
2+
3+
char *getArrayPtr();
4+
void func() {
5+
char Array[10];
6+
char *ArrayPtr = getArrayPtr();
7+
int *readonly;
8+
9+
for (int i = 0; i < 10; ++i) {
10+
// expected-error@+2{{expected '('}}
11+
// expected-warning@+1{{OpenACC directives not yet implemented, pragma ignored}}
12+
#pragma acc cache
13+
}
14+
15+
for (int i = 0; i < 10; ++i) {
16+
// expected-error@+3{{expected '('}}
17+
// expected-warning@+2{{OpenACC clause parsing not yet implemented}}
18+
// expected-warning@+1{{OpenACC directives not yet implemented, pragma ignored}}
19+
#pragma acc cache clause list
20+
}
21+
22+
for (int i = 0; i < 10; ++i) {
23+
// expected-warning@+1{{OpenACC directives not yet implemented, pragma ignored}}
24+
#pragma acc cache()
25+
}
26+
27+
for (int i = 0; i < 10; ++i) {
28+
// expected-warning@+2{{OpenACC clause parsing not yet implemented}}
29+
// expected-warning@+1{{OpenACC directives not yet implemented, pragma ignored}}
30+
#pragma acc cache() clause-list
31+
}
32+
33+
for (int i = 0; i < 10; ++i) {
34+
// expected-error@+3{{expected ')'}}
35+
// expected-note@+2{{to match this '('}}
36+
// expected-warning@+1{{OpenACC directives not yet implemented, pragma ignored}}
37+
#pragma acc cache(
38+
}
39+
40+
for (int i = 0; i < 10; ++i) {
41+
// expected-error@+4{{use of undeclared identifier 'invalid'}}
42+
// expected-error@+3{{expected ')'}}
43+
// expected-note@+2{{to match this '('}}
44+
// expected-warning@+1{{OpenACC directives not yet implemented, pragma ignored}}
45+
#pragma acc cache(invalid
46+
}
47+
48+
for (int i = 0; i < 10; ++i) {
49+
// expected-error@+4{{expected '['}}
50+
// expected-error@+3{{expected ')'}}
51+
// expected-note@+2{{to match this '('}}
52+
// expected-warning@+1{{OpenACC directives not yet implemented, pragma ignored}}
53+
#pragma acc cache(ArrayPtr
54+
}
55+
56+
for (int i = 0; i < 10; ++i) {
57+
// expected-error@+2{{use of undeclared identifier 'invalid'}}
58+
// expected-warning@+1{{OpenACC directives not yet implemented, pragma ignored}}
59+
#pragma acc cache(invalid)
60+
}
61+
62+
for (int i = 0; i < 10; ++i) {
63+
// expected-error@+2{{expected '['}}
64+
// expected-warning@+1{{OpenACC directives not yet implemented, pragma ignored}}
65+
#pragma acc cache(ArrayPtr)
66+
}
67+
68+
for (int i = 0; i < 10; ++i) {
69+
// expected-error@+4{{expected expression}}
70+
// expected-error@+3{{expected ')'}}
71+
// expected-note@+2{{to match this '('}}
72+
// expected-warning@+1{{OpenACC directives not yet implemented, pragma ignored}}
73+
#pragma acc cache(ArrayPtr[
74+
}
75+
76+
for (int i = 0; i < 10; ++i) {
77+
// expected-error@+2{{expected expression}}
78+
// expected-warning@+1{{OpenACC directives not yet implemented, pragma ignored}}
79+
#pragma acc cache(ArrayPtr[, 5)
80+
}
81+
82+
for (int i = 0; i < 10; ++i) {
83+
// expected-error@+2{{expected expression}}
84+
// expected-warning@+1{{OpenACC directives not yet implemented, pragma ignored}}
85+
#pragma acc cache(Array[)
86+
}
87+
88+
for (int i = 0; i < 10; ++i) {
89+
// expected-warning@+1{{OpenACC directives not yet implemented, pragma ignored}}
90+
#pragma acc cache(Array[*readonly])
91+
}
92+
93+
for (int i = 0; i < 10; ++i) {
94+
// expected-error@+4{{expected expression}}
95+
// expected-error@+3{{expected ')'}}
96+
// expected-note@+2{{to match this '('}}
97+
// expected-warning@+1{{OpenACC directives not yet implemented, pragma ignored}}
98+
#pragma acc cache(Array[*readonly:
99+
}
100+
101+
for (int i = 0; i < 10; ++i) {
102+
// expected-error@+2{{expected '['}}
103+
// expected-warning@+1{{OpenACC directives not yet implemented, pragma ignored}}
104+
#pragma acc cache(readonly)
105+
}
106+
107+
for (int i = 0; i < 10; ++i) {
108+
// expected-error@+2{{expected '['}}
109+
// expected-warning@+1{{OpenACC directives not yet implemented, pragma ignored}}
110+
#pragma acc cache(readonly:ArrayPtr)
111+
}
112+
113+
for (int i = 0; i < 10; ++i) {
114+
// expected-error@+2{{expected expression}}
115+
// expected-warning@+1{{OpenACC directives not yet implemented, pragma ignored}}
116+
#pragma acc cache(readonly:ArrayPtr[5:])
117+
}
118+
119+
for (int i = 0; i < 10; ++i) {
120+
// expected-warning@+1{{OpenACC directives not yet implemented, pragma ignored}}
121+
#pragma acc cache(readonly:ArrayPtr[5:*readonly])
122+
}
123+
124+
for (int i = 0; i < 10; ++i) {
125+
// expected-error@+2{{expected '['}}
126+
// expected-warning@+1{{OpenACC directives not yet implemented, pragma ignored}}
127+
#pragma acc cache(readonly:ArrayPtr[5:*readonly], Array)
128+
}
129+
130+
for (int i = 0; i < 10; ++i) {
131+
// expected-warning@+1{{OpenACC directives not yet implemented, pragma ignored}}
132+
#pragma acc cache(readonly:ArrayPtr[5:*readonly], Array[*readonly:3])
133+
}
134+
135+
for (int i = 0; i < 10; ++i) {
136+
// expected-warning@+1{{OpenACC directives not yet implemented, pragma ignored}}
137+
#pragma acc cache(readonly:ArrayPtr[5 + i:*readonly], Array[*readonly + i:3])
138+
}
139+
140+
for (int i = 0; i < 10; ++i) {
141+
// expected-error@+4{{expected identifier}}
142+
// expected-error@+3{{expected ')'}}
143+
// expected-note@+2{{to match this '('}}
144+
// expected-warning@+1{{OpenACC directives not yet implemented, pragma ignored}}
145+
#pragma acc cache(readonly:ArrayPtr[5:*readonly],
146+
}
147+
148+
for (int i = 0; i < 10; ++i) {
149+
// expected-error@+2{{expected identifier}}
150+
// expected-warning@+1{{OpenACC directives not yet implemented, pragma ignored}}
151+
#pragma acc cache(readonly:ArrayPtr[5:*readonly],)
152+
}
153+
154+
for (int i = 0; i < 10; ++i) {
155+
// expected-warning@+2{{left operand of comma operator has no effect}}
156+
// expected-warning@+1{{OpenACC directives not yet implemented, pragma ignored}}
157+
#pragma acc cache(readonly:ArrayPtr[5,6:*readonly])
158+
}
159+
160+
for (int i = 0; i < 10; ++i) {
161+
// expected-warning@+2{{left operand of comma operator has no effect}}
162+
// expected-warning@+1{{OpenACC directives not yet implemented, pragma ignored}}
163+
#pragma acc cache(readonly:ArrayPtr[5:3, *readonly], ArrayPtr[0])
164+
}
165+
166+
}
Lines changed: 51 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,51 @@
1+
// RUN: %clang_cc1 %s -verify -fopenacc
2+
3+
namespace NS {
4+
static char* NSArray;// expected-note{{declared here}}
5+
static int NSInt;// expected-note 2{{declared here}}
6+
}
7+
char *getArrayPtr();
8+
template<typename T, int I>
9+
void func() {
10+
char *ArrayPtr = getArrayPtr();
11+
for (int i = 0; i < 10; ++i) {
12+
// expected-warning@+1{{OpenACC directives not yet implemented, pragma ignored}}
13+
#pragma acc cache(ArrayPtr[T::value + I:I + 5], T::array[(i + T::value, 5): 6])
14+
}
15+
for (int i = 0; i < 10; ++i) {
16+
// expected-warning@+1{{OpenACC directives not yet implemented, pragma ignored}}
17+
#pragma acc cache(NS::NSArray[NS::NSInt])
18+
}
19+
20+
for (int i = 0; i < 10; ++i) {
21+
// expected-warning@+1{{OpenACC directives not yet implemented, pragma ignored}}
22+
#pragma acc cache(NS::NSArray[NS::NSInt : NS::NSInt])
23+
}
24+
25+
for (int i = 0; i < 10; ++i) {
26+
// expected-error@+2{{use of undeclared identifier 'NSArray'; did you mean 'NS::NSArray'}}
27+
// expected-warning@+1{{OpenACC directives not yet implemented, pragma ignored}}
28+
#pragma acc cache(NSArray[NS::NSInt : NS::NSInt])
29+
}
30+
31+
for (int i = 0; i < 10; ++i) {
32+
// expected-error@+2{{use of undeclared identifier 'NSInt'; did you mean 'NS::NSInt'}}
33+
// expected-warning@+1{{OpenACC directives not yet implemented, pragma ignored}}
34+
#pragma acc cache(NS::NSArray[NSInt : NS::NSInt])
35+
}
36+
37+
for (int i = 0; i < 10; ++i) {
38+
// expected-error@+2{{use of undeclared identifier 'NSInt'; did you mean 'NS::NSInt'}}
39+
// expected-warning@+1{{OpenACC directives not yet implemented, pragma ignored}}
40+
#pragma acc cache(NS::NSArray[NS::NSInt : NSInt])
41+
}
42+
}
43+
44+
struct S {
45+
static constexpr int value = 5;
46+
static constexpr char array[] ={1,2,3,4,5};
47+
};
48+
49+
void use() {
50+
func<S, 5>();
51+
}

0 commit comments

Comments
 (0)