1 | //===--- SemaOpenACC.cpp - Semantic Analysis for OpenACC constructs -------===// |
2 | // |
3 | // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. |
4 | // See https://llvm.org/LICENSE.txt for license information. |
5 | // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception |
6 | // |
7 | //===----------------------------------------------------------------------===// |
8 | /// \file |
9 | /// This file implements semantic analysis for OpenACC constructs and |
10 | /// clauses. |
11 | /// |
12 | //===----------------------------------------------------------------------===// |
13 | |
14 | #include "clang/Sema/SemaOpenACC.h" |
15 | #include "clang/AST/StmtOpenACC.h" |
16 | #include "clang/Basic/DiagnosticSema.h" |
17 | #include "clang/Basic/OpenACCKinds.h" |
18 | #include "clang/Sema/Sema.h" |
19 | #include "llvm/Support/Casting.h" |
20 | |
21 | using namespace clang; |
22 | |
23 | namespace { |
24 | bool diagnoseConstructAppertainment(SemaOpenACC &S, OpenACCDirectiveKind K, |
25 | SourceLocation StartLoc, bool IsStmt) { |
26 | switch (K) { |
27 | default: |
28 | case OpenACCDirectiveKind::Invalid: |
29 | // Nothing to do here, both invalid and unimplemented don't really need to |
30 | // do anything. |
31 | break; |
32 | case OpenACCDirectiveKind::Parallel: |
33 | case OpenACCDirectiveKind::Serial: |
34 | case OpenACCDirectiveKind::Kernels: |
35 | if (!IsStmt) |
36 | return S.Diag(StartLoc, diag::err_acc_construct_appertainment) << K; |
37 | break; |
38 | } |
39 | return false; |
40 | } |
41 | |
42 | bool doesClauseApplyToDirective(OpenACCDirectiveKind DirectiveKind, |
43 | OpenACCClauseKind ClauseKind) { |
44 | switch (ClauseKind) { |
45 | // FIXME: For each clause as we implement them, we can add the |
46 | // 'legalization' list here. |
47 | case OpenACCClauseKind::Default: |
48 | switch (DirectiveKind) { |
49 | case OpenACCDirectiveKind::Parallel: |
50 | case OpenACCDirectiveKind::Serial: |
51 | case OpenACCDirectiveKind::Kernels: |
52 | case OpenACCDirectiveKind::ParallelLoop: |
53 | case OpenACCDirectiveKind::SerialLoop: |
54 | case OpenACCDirectiveKind::KernelsLoop: |
55 | case OpenACCDirectiveKind::Data: |
56 | return true; |
57 | default: |
58 | return false; |
59 | } |
60 | case OpenACCClauseKind::If: |
61 | switch (DirectiveKind) { |
62 | case OpenACCDirectiveKind::Parallel: |
63 | case OpenACCDirectiveKind::Serial: |
64 | case OpenACCDirectiveKind::Kernels: |
65 | case OpenACCDirectiveKind::Data: |
66 | case OpenACCDirectiveKind::EnterData: |
67 | case OpenACCDirectiveKind::ExitData: |
68 | case OpenACCDirectiveKind::HostData: |
69 | case OpenACCDirectiveKind::Init: |
70 | case OpenACCDirectiveKind::Shutdown: |
71 | case OpenACCDirectiveKind::Set: |
72 | case OpenACCDirectiveKind::Update: |
73 | case OpenACCDirectiveKind::Wait: |
74 | case OpenACCDirectiveKind::ParallelLoop: |
75 | case OpenACCDirectiveKind::SerialLoop: |
76 | case OpenACCDirectiveKind::KernelsLoop: |
77 | return true; |
78 | default: |
79 | return false; |
80 | } |
81 | case OpenACCClauseKind::Self: |
82 | switch (DirectiveKind) { |
83 | case OpenACCDirectiveKind::Parallel: |
84 | case OpenACCDirectiveKind::Serial: |
85 | case OpenACCDirectiveKind::Kernels: |
86 | case OpenACCDirectiveKind::Update: |
87 | case OpenACCDirectiveKind::ParallelLoop: |
88 | case OpenACCDirectiveKind::SerialLoop: |
89 | case OpenACCDirectiveKind::KernelsLoop: |
90 | return true; |
91 | default: |
92 | return false; |
93 | } |
94 | case OpenACCClauseKind::NumGangs: |
95 | case OpenACCClauseKind::NumWorkers: |
96 | case OpenACCClauseKind::VectorLength: |
97 | switch (DirectiveKind) { |
98 | case OpenACCDirectiveKind::Parallel: |
99 | case OpenACCDirectiveKind::Kernels: |
100 | case OpenACCDirectiveKind::ParallelLoop: |
101 | case OpenACCDirectiveKind::KernelsLoop: |
102 | return true; |
103 | default: |
104 | return false; |
105 | } |
106 | default: |
107 | // Do nothing so we can go to the 'unimplemented' diagnostic instead. |
108 | return true; |
109 | } |
110 | llvm_unreachable("Invalid clause kind" ); |
111 | } |
112 | |
113 | bool checkAlreadyHasClauseOfKind( |
114 | SemaOpenACC &S, ArrayRef<const OpenACCClause *> ExistingClauses, |
115 | SemaOpenACC::OpenACCParsedClause &Clause) { |
116 | const auto *Itr = llvm::find_if(Range&: ExistingClauses, P: [&](const OpenACCClause *C) { |
117 | return C->getClauseKind() == Clause.getClauseKind(); |
118 | }); |
119 | if (Itr != ExistingClauses.end()) { |
120 | S.Diag(Clause.getBeginLoc(), diag::err_acc_duplicate_clause_disallowed) |
121 | << Clause.getDirectiveKind() << Clause.getClauseKind(); |
122 | S.Diag((*Itr)->getBeginLoc(), diag::note_acc_previous_clause_here); |
123 | return true; |
124 | } |
125 | return false; |
126 | } |
127 | |
128 | } // namespace |
129 | |
130 | SemaOpenACC::SemaOpenACC(Sema &S) : SemaBase(S) {} |
131 | |
132 | OpenACCClause * |
133 | SemaOpenACC::ActOnClause(ArrayRef<const OpenACCClause *> ExistingClauses, |
134 | OpenACCParsedClause &Clause) { |
135 | if (Clause.getClauseKind() == OpenACCClauseKind::Invalid) |
136 | return nullptr; |
137 | |
138 | // Diagnose that we don't support this clause on this directive. |
139 | if (!doesClauseApplyToDirective(DirectiveKind: Clause.getDirectiveKind(), |
140 | ClauseKind: Clause.getClauseKind())) { |
141 | Diag(Clause.getBeginLoc(), diag::err_acc_clause_appertainment) |
142 | << Clause.getDirectiveKind() << Clause.getClauseKind(); |
143 | return nullptr; |
144 | } |
145 | |
146 | switch (Clause.getClauseKind()) { |
147 | case OpenACCClauseKind::Default: { |
148 | // Restrictions only properly implemented on 'compute' constructs, and |
149 | // 'compute' constructs are the only construct that can do anything with |
150 | // this yet, so skip/treat as unimplemented in this case. |
151 | if (!isOpenACCComputeDirectiveKind(K: Clause.getDirectiveKind())) |
152 | break; |
153 | |
154 | // Don't add an invalid clause to the AST. |
155 | if (Clause.getDefaultClauseKind() == OpenACCDefaultClauseKind::Invalid) |
156 | return nullptr; |
157 | |
158 | // OpenACC 3.3, Section 2.5.4: |
159 | // At most one 'default' clause may appear, and it must have a value of |
160 | // either 'none' or 'present'. |
161 | // Second half of the sentence is diagnosed during parsing. |
162 | if (checkAlreadyHasClauseOfKind(S&: *this, ExistingClauses, Clause)) |
163 | return nullptr; |
164 | |
165 | return OpenACCDefaultClause::Create( |
166 | C: getASTContext(), K: Clause.getDefaultClauseKind(), BeginLoc: Clause.getBeginLoc(), |
167 | LParenLoc: Clause.getLParenLoc(), EndLoc: Clause.getEndLoc()); |
168 | } |
169 | |
170 | case OpenACCClauseKind::If: { |
171 | // Restrictions only properly implemented on 'compute' constructs, and |
172 | // 'compute' constructs are the only construct that can do anything with |
173 | // this yet, so skip/treat as unimplemented in this case. |
174 | if (!isOpenACCComputeDirectiveKind(K: Clause.getDirectiveKind())) |
175 | break; |
176 | |
177 | // There is no prose in the standard that says duplicates aren't allowed, |
178 | // but this diagnostic is present in other compilers, as well as makes |
179 | // sense. |
180 | if (checkAlreadyHasClauseOfKind(S&: *this, ExistingClauses, Clause)) |
181 | return nullptr; |
182 | |
183 | // The parser has ensured that we have a proper condition expr, so there |
184 | // isn't really much to do here. |
185 | |
186 | // If the 'if' clause is true, it makes the 'self' clause have no effect, |
187 | // diagnose that here. |
188 | // TODO OpenACC: When we add these two to other constructs, we might not |
189 | // want to warn on this (for example, 'update'). |
190 | const auto *Itr = |
191 | llvm::find_if(Range&: ExistingClauses, P: llvm::IsaPred<OpenACCSelfClause>); |
192 | if (Itr != ExistingClauses.end()) { |
193 | Diag(Clause.getBeginLoc(), diag::warn_acc_if_self_conflict); |
194 | Diag((*Itr)->getBeginLoc(), diag::note_acc_previous_clause_here); |
195 | } |
196 | |
197 | return OpenACCIfClause::Create( |
198 | C: getASTContext(), BeginLoc: Clause.getBeginLoc(), LParenLoc: Clause.getLParenLoc(), |
199 | ConditionExpr: Clause.getConditionExpr(), EndLoc: Clause.getEndLoc()); |
200 | } |
201 | |
202 | case OpenACCClauseKind::Self: { |
203 | // Restrictions only properly implemented on 'compute' constructs, and |
204 | // 'compute' constructs are the only construct that can do anything with |
205 | // this yet, so skip/treat as unimplemented in this case. |
206 | if (!isOpenACCComputeDirectiveKind(K: Clause.getDirectiveKind())) |
207 | break; |
208 | |
209 | // TODO OpenACC: When we implement this for 'update', this takes a |
210 | // 'var-list' instead of a condition expression, so semantics/handling has |
211 | // to happen differently here. |
212 | |
213 | // There is no prose in the standard that says duplicates aren't allowed, |
214 | // but this diagnostic is present in other compilers, as well as makes |
215 | // sense. |
216 | if (checkAlreadyHasClauseOfKind(S&: *this, ExistingClauses, Clause)) |
217 | return nullptr; |
218 | |
219 | // If the 'if' clause is true, it makes the 'self' clause have no effect, |
220 | // diagnose that here. |
221 | // TODO OpenACC: When we add these two to other constructs, we might not |
222 | // want to warn on this (for example, 'update'). |
223 | const auto *Itr = |
224 | llvm::find_if(Range&: ExistingClauses, P: llvm::IsaPred<OpenACCIfClause>); |
225 | if (Itr != ExistingClauses.end()) { |
226 | Diag(Clause.getBeginLoc(), diag::warn_acc_if_self_conflict); |
227 | Diag((*Itr)->getBeginLoc(), diag::note_acc_previous_clause_here); |
228 | } |
229 | |
230 | return OpenACCSelfClause::Create( |
231 | C: getASTContext(), BeginLoc: Clause.getBeginLoc(), LParenLoc: Clause.getLParenLoc(), |
232 | ConditionExpr: Clause.getConditionExpr(), EndLoc: Clause.getEndLoc()); |
233 | } |
234 | case OpenACCClauseKind::NumGangs: { |
235 | // Restrictions only properly implemented on 'compute' constructs, and |
236 | // 'compute' constructs are the only construct that can do anything with |
237 | // this yet, so skip/treat as unimplemented in this case. |
238 | if (!isOpenACCComputeDirectiveKind(K: Clause.getDirectiveKind())) |
239 | break; |
240 | |
241 | // There is no prose in the standard that says duplicates aren't allowed, |
242 | // but this diagnostic is present in other compilers, as well as makes |
243 | // sense. |
244 | if (checkAlreadyHasClauseOfKind(S&: *this, ExistingClauses, Clause)) |
245 | return nullptr; |
246 | |
247 | if (Clause.getIntExprs().empty()) |
248 | Diag(Clause.getBeginLoc(), diag::err_acc_num_gangs_num_args) |
249 | << /*NoArgs=*/0; |
250 | |
251 | unsigned MaxArgs = |
252 | (Clause.getDirectiveKind() == OpenACCDirectiveKind::Parallel || |
253 | Clause.getDirectiveKind() == OpenACCDirectiveKind::ParallelLoop) |
254 | ? 3 |
255 | : 1; |
256 | if (Clause.getIntExprs().size() > MaxArgs) |
257 | Diag(Clause.getBeginLoc(), diag::err_acc_num_gangs_num_args) |
258 | << /*NoArgs=*/1 << Clause.getDirectiveKind() << MaxArgs |
259 | << Clause.getIntExprs().size(); |
260 | |
261 | // Create the AST node for the clause even if the number of expressions is |
262 | // incorrect. |
263 | return OpenACCNumGangsClause::Create( |
264 | C: getASTContext(), BeginLoc: Clause.getBeginLoc(), LParenLoc: Clause.getLParenLoc(), |
265 | IntExprs: Clause.getIntExprs(), EndLoc: Clause.getEndLoc()); |
266 | break; |
267 | } |
268 | case OpenACCClauseKind::NumWorkers: { |
269 | // Restrictions only properly implemented on 'compute' constructs, and |
270 | // 'compute' constructs are the only construct that can do anything with |
271 | // this yet, so skip/treat as unimplemented in this case. |
272 | if (!isOpenACCComputeDirectiveKind(K: Clause.getDirectiveKind())) |
273 | break; |
274 | |
275 | // There is no prose in the standard that says duplicates aren't allowed, |
276 | // but this diagnostic is present in other compilers, as well as makes |
277 | // sense. |
278 | if (checkAlreadyHasClauseOfKind(S&: *this, ExistingClauses, Clause)) |
279 | return nullptr; |
280 | |
281 | assert(Clause.getIntExprs().size() == 1 && |
282 | "Invalid number of expressions for NumWorkers" ); |
283 | return OpenACCNumWorkersClause::Create( |
284 | C: getASTContext(), BeginLoc: Clause.getBeginLoc(), LParenLoc: Clause.getLParenLoc(), |
285 | IntExpr: Clause.getIntExprs()[0], EndLoc: Clause.getEndLoc()); |
286 | } |
287 | case OpenACCClauseKind::VectorLength: { |
288 | // Restrictions only properly implemented on 'compute' constructs, and |
289 | // 'compute' constructs are the only construct that can do anything with |
290 | // this yet, so skip/treat as unimplemented in this case. |
291 | if (!isOpenACCComputeDirectiveKind(K: Clause.getDirectiveKind())) |
292 | break; |
293 | |
294 | // There is no prose in the standard that says duplicates aren't allowed, |
295 | // but this diagnostic is present in other compilers, as well as makes |
296 | // sense. |
297 | if (checkAlreadyHasClauseOfKind(S&: *this, ExistingClauses, Clause)) |
298 | return nullptr; |
299 | |
300 | assert(Clause.getIntExprs().size() == 1 && |
301 | "Invalid number of expressions for VectorLength" ); |
302 | return OpenACCVectorLengthClause::Create( |
303 | C: getASTContext(), BeginLoc: Clause.getBeginLoc(), LParenLoc: Clause.getLParenLoc(), |
304 | IntExpr: Clause.getIntExprs()[0], EndLoc: Clause.getEndLoc()); |
305 | } |
306 | default: |
307 | break; |
308 | } |
309 | |
310 | Diag(Clause.getBeginLoc(), diag::warn_acc_clause_unimplemented) |
311 | << Clause.getClauseKind(); |
312 | return nullptr; |
313 | } |
314 | |
315 | void SemaOpenACC::ActOnConstruct(OpenACCDirectiveKind K, |
316 | SourceLocation StartLoc) { |
317 | switch (K) { |
318 | case OpenACCDirectiveKind::Invalid: |
319 | // Nothing to do here, an invalid kind has nothing we can check here. We |
320 | // want to continue parsing clauses as far as we can, so we will just |
321 | // ensure that we can still work and don't check any construct-specific |
322 | // rules anywhere. |
323 | break; |
324 | case OpenACCDirectiveKind::Parallel: |
325 | case OpenACCDirectiveKind::Serial: |
326 | case OpenACCDirectiveKind::Kernels: |
327 | // Nothing to do here, there is no real legalization that needs to happen |
328 | // here as these constructs do not take any arguments. |
329 | break; |
330 | default: |
331 | Diag(StartLoc, diag::warn_acc_construct_unimplemented) << K; |
332 | break; |
333 | } |
334 | } |
335 | |
336 | ExprResult SemaOpenACC::ActOnIntExpr(OpenACCDirectiveKind DK, |
337 | OpenACCClauseKind CK, SourceLocation Loc, |
338 | Expr *IntExpr) { |
339 | |
340 | assert(((DK != OpenACCDirectiveKind::Invalid && |
341 | CK == OpenACCClauseKind::Invalid) || |
342 | (DK == OpenACCDirectiveKind::Invalid && |
343 | CK != OpenACCClauseKind::Invalid)) && |
344 | "Only one of directive or clause kind should be provided" ); |
345 | |
346 | class IntExprConverter : public Sema::ICEConvertDiagnoser { |
347 | OpenACCDirectiveKind DirectiveKind; |
348 | OpenACCClauseKind ClauseKind; |
349 | Expr *IntExpr; |
350 | |
351 | public: |
352 | IntExprConverter(OpenACCDirectiveKind DK, OpenACCClauseKind CK, |
353 | Expr *IntExpr) |
354 | : ICEConvertDiagnoser(/*AllowScopedEnumerations=*/false, |
355 | /*Suppress=*/false, |
356 | /*SuppressConversion=*/true), |
357 | DirectiveKind(DK), ClauseKind(CK), IntExpr(IntExpr) {} |
358 | |
359 | bool match(QualType T) override { |
360 | // OpenACC spec just calls this 'integer expression' as having an |
361 | // 'integer type', so fall back on C99's 'integer type'. |
362 | return T->isIntegerType(); |
363 | } |
364 | SemaBase::SemaDiagnosticBuilder diagnoseNotInt(Sema &S, SourceLocation Loc, |
365 | QualType T) override { |
366 | if (ClauseKind != OpenACCClauseKind::Invalid) |
367 | return S.Diag(Loc, diag::err_acc_int_expr_requires_integer) << |
368 | /*Clause=*/0 << ClauseKind << T; |
369 | |
370 | return S.Diag(Loc, diag::err_acc_int_expr_requires_integer) << |
371 | /*Directive=*/1 << DirectiveKind << T; |
372 | } |
373 | |
374 | SemaBase::SemaDiagnosticBuilder |
375 | diagnoseIncomplete(Sema &S, SourceLocation Loc, QualType T) override { |
376 | return S.Diag(Loc, diag::err_acc_int_expr_incomplete_class_type) |
377 | << T << IntExpr->getSourceRange(); |
378 | } |
379 | |
380 | SemaBase::SemaDiagnosticBuilder |
381 | diagnoseExplicitConv(Sema &S, SourceLocation Loc, QualType T, |
382 | QualType ConvTy) override { |
383 | return S.Diag(Loc, diag::err_acc_int_expr_explicit_conversion) |
384 | << T << ConvTy; |
385 | } |
386 | |
387 | SemaBase::SemaDiagnosticBuilder noteExplicitConv(Sema &S, |
388 | CXXConversionDecl *Conv, |
389 | QualType ConvTy) override { |
390 | return S.Diag(Conv->getLocation(), diag::note_acc_int_expr_conversion) |
391 | << ConvTy->isEnumeralType() << ConvTy; |
392 | } |
393 | |
394 | SemaBase::SemaDiagnosticBuilder |
395 | diagnoseAmbiguous(Sema &S, SourceLocation Loc, QualType T) override { |
396 | return S.Diag(Loc, diag::err_acc_int_expr_multiple_conversions) << T; |
397 | } |
398 | |
399 | SemaBase::SemaDiagnosticBuilder |
400 | noteAmbiguous(Sema &S, CXXConversionDecl *Conv, QualType ConvTy) override { |
401 | return S.Diag(Conv->getLocation(), diag::note_acc_int_expr_conversion) |
402 | << ConvTy->isEnumeralType() << ConvTy; |
403 | } |
404 | |
405 | SemaBase::SemaDiagnosticBuilder |
406 | diagnoseConversion(Sema &S, SourceLocation Loc, QualType T, |
407 | QualType ConvTy) override { |
408 | llvm_unreachable("conversion functions are permitted" ); |
409 | } |
410 | } IntExprDiagnoser(DK, CK, IntExpr); |
411 | |
412 | ExprResult IntExprResult = SemaRef.PerformContextualImplicitConversion( |
413 | Loc, FromE: IntExpr, Converter&: IntExprDiagnoser); |
414 | if (IntExprResult.isInvalid()) |
415 | return ExprError(); |
416 | |
417 | IntExpr = IntExprResult.get(); |
418 | if (!IntExpr->isTypeDependent() && !IntExpr->getType()->isIntegerType()) |
419 | return ExprError(); |
420 | |
421 | // TODO OpenACC: Do we want to perform usual unary conversions here? When |
422 | // doing codegen we might find that is necessary, but skip it for now. |
423 | return IntExpr; |
424 | } |
425 | |
426 | bool SemaOpenACC::ActOnStartStmtDirective(OpenACCDirectiveKind K, |
427 | SourceLocation StartLoc) { |
428 | return diagnoseConstructAppertainment(S&: *this, K, StartLoc, /*IsStmt=*/true); |
429 | } |
430 | |
431 | StmtResult SemaOpenACC::ActOnEndStmtDirective(OpenACCDirectiveKind K, |
432 | SourceLocation StartLoc, |
433 | SourceLocation EndLoc, |
434 | ArrayRef<OpenACCClause *> Clauses, |
435 | StmtResult AssocStmt) { |
436 | switch (K) { |
437 | default: |
438 | return StmtEmpty(); |
439 | case OpenACCDirectiveKind::Invalid: |
440 | return StmtError(); |
441 | case OpenACCDirectiveKind::Parallel: |
442 | case OpenACCDirectiveKind::Serial: |
443 | case OpenACCDirectiveKind::Kernels: |
444 | // TODO OpenACC: Add clauses to the construct here. |
445 | return OpenACCComputeConstruct::Create( |
446 | C: getASTContext(), K, BeginLoc: StartLoc, EndLoc, Clauses, |
447 | StructuredBlock: AssocStmt.isUsable() ? AssocStmt.get() : nullptr); |
448 | } |
449 | llvm_unreachable("Unhandled case in directive handling?" ); |
450 | } |
451 | |
452 | StmtResult SemaOpenACC::ActOnAssociatedStmt(OpenACCDirectiveKind K, |
453 | StmtResult AssocStmt) { |
454 | switch (K) { |
455 | default: |
456 | llvm_unreachable("Unimplemented associated statement application" ); |
457 | case OpenACCDirectiveKind::Parallel: |
458 | case OpenACCDirectiveKind::Serial: |
459 | case OpenACCDirectiveKind::Kernels: |
460 | // There really isn't any checking here that could happen. As long as we |
461 | // have a statement to associate, this should be fine. |
462 | // OpenACC 3.3 Section 6: |
463 | // Structured Block: in C or C++, an executable statement, possibly |
464 | // compound, with a single entry at the top and a single exit at the |
465 | // bottom. |
466 | // FIXME: Should we reject DeclStmt's here? The standard isn't clear, and |
467 | // an interpretation of it is to allow this and treat the initializer as |
468 | // the 'structured block'. |
469 | return AssocStmt; |
470 | } |
471 | llvm_unreachable("Invalid associated statement application" ); |
472 | } |
473 | |
474 | bool SemaOpenACC::ActOnStartDeclDirective(OpenACCDirectiveKind K, |
475 | SourceLocation StartLoc) { |
476 | return diagnoseConstructAppertainment(S&: *this, K, StartLoc, /*IsStmt=*/false); |
477 | } |
478 | |
479 | DeclGroupRef SemaOpenACC::ActOnEndDeclDirective() { return DeclGroupRef{}; } |
480 | |