Skip to content

Commit 6133878

Browse files
authored
[OpenACC] Implement self clause for compute constructs (#88760)
`self` clauses on compute constructs take an optional condition expression. We again limit the implementation to ONLY compute constructs to ensure we get all the rules correct for others. However, this one will be particularly complicated, as it takes a `var-list` for `update`, so when we get to that construct/clause combination, we need to do that as well. This patch also furthers uses of the `OpenACCClauses.def` as it became useful while implementing this (as well as some other minor refactors as I went through). Finally, `self` and `if` clauses have an interaction with each other, if an `if` clause evaluates to `true`, the `self` clause has no effect. While this is intended and can be used 'meaningfully', we are warning on this with a very granular warning, so that this edge case will be noticed by newer users, but can be disabled trivially.
1 parent 254df2e commit 6133878

19 files changed

+513
-98
lines changed

clang/include/clang/AST/OpenACCClause.h

Lines changed: 18 additions & 47 deletions
Original file line numberDiff line numberDiff line change
@@ -145,6 +145,17 @@ class OpenACCIfClause : public OpenACCClauseWithCondition {
145145
SourceLocation EndLoc);
146146
};
147147

148+
/// A 'self' clause, which has an optional condition expression.
149+
class OpenACCSelfClause : public OpenACCClauseWithCondition {
150+
OpenACCSelfClause(SourceLocation BeginLoc, SourceLocation LParenLoc,
151+
Expr *ConditionExpr, SourceLocation EndLoc);
152+
153+
public:
154+
static OpenACCSelfClause *Create(const ASTContext &C, SourceLocation BeginLoc,
155+
SourceLocation LParenLoc,
156+
Expr *ConditionExpr, SourceLocation EndLoc);
157+
};
158+
148159
template <class Impl> class OpenACCClauseVisitor {
149160
Impl &getDerived() { return static_cast<Impl &>(*this); }
150161

@@ -159,53 +170,13 @@ template <class Impl> class OpenACCClauseVisitor {
159170
return;
160171

161172
switch (C->getClauseKind()) {
162-
case OpenACCClauseKind::Default:
163-
VisitDefaultClause(*cast<OpenACCDefaultClause>(C));
164-
return;
165-
case OpenACCClauseKind::If:
166-
VisitIfClause(*cast<OpenACCIfClause>(C));
167-
return;
168-
case OpenACCClauseKind::Finalize:
169-
case OpenACCClauseKind::IfPresent:
170-
case OpenACCClauseKind::Seq:
171-
case OpenACCClauseKind::Independent:
172-
case OpenACCClauseKind::Auto:
173-
case OpenACCClauseKind::Worker:
174-
case OpenACCClauseKind::Vector:
175-
case OpenACCClauseKind::NoHost:
176-
case OpenACCClauseKind::Self:
177-
case OpenACCClauseKind::Copy:
178-
case OpenACCClauseKind::UseDevice:
179-
case OpenACCClauseKind::Attach:
180-
case OpenACCClauseKind::Delete:
181-
case OpenACCClauseKind::Detach:
182-
case OpenACCClauseKind::Device:
183-
case OpenACCClauseKind::DevicePtr:
184-
case OpenACCClauseKind::DeviceResident:
185-
case OpenACCClauseKind::FirstPrivate:
186-
case OpenACCClauseKind::Host:
187-
case OpenACCClauseKind::Link:
188-
case OpenACCClauseKind::NoCreate:
189-
case OpenACCClauseKind::Present:
190-
case OpenACCClauseKind::Private:
191-
case OpenACCClauseKind::CopyOut:
192-
case OpenACCClauseKind::CopyIn:
193-
case OpenACCClauseKind::Create:
194-
case OpenACCClauseKind::Reduction:
195-
case OpenACCClauseKind::Collapse:
196-
case OpenACCClauseKind::Bind:
197-
case OpenACCClauseKind::VectorLength:
198-
case OpenACCClauseKind::NumGangs:
199-
case OpenACCClauseKind::NumWorkers:
200-
case OpenACCClauseKind::DeviceNum:
201-
case OpenACCClauseKind::DefaultAsync:
202-
case OpenACCClauseKind::DeviceType:
203-
case OpenACCClauseKind::DType:
204-
case OpenACCClauseKind::Async:
205-
case OpenACCClauseKind::Tile:
206-
case OpenACCClauseKind::Gang:
207-
case OpenACCClauseKind::Wait:
208-
case OpenACCClauseKind::Invalid:
173+
#define VISIT_CLAUSE(CLAUSE_NAME) \
174+
case OpenACCClauseKind::CLAUSE_NAME: \
175+
Visit##CLAUSE_NAME##Clause(*cast<OpenACC##CLAUSE_NAME##Clause>(C)); \
176+
return;
177+
#include "clang/Basic/OpenACCClauses.def"
178+
179+
default:
209180
llvm_unreachable("Clause visitor not yet implemented");
210181
}
211182
llvm_unreachable("Invalid Clause kind");

clang/include/clang/AST/StmtOpenACC.h

Lines changed: 1 addition & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -142,9 +142,7 @@ class OpenACCComputeConstruct final
142142
Stmt *StructuredBlock)
143143
: OpenACCAssociatedStmtConstruct(OpenACCComputeConstructClass, K, Start,
144144
End, StructuredBlock) {
145-
assert((K == OpenACCDirectiveKind::Parallel ||
146-
K == OpenACCDirectiveKind::Serial ||
147-
K == OpenACCDirectiveKind::Kernels) &&
145+
assert(isOpenACCComputeDirectiveKind(K) &&
148146
"Only parallel, serial, and kernels constructs should be "
149147
"represented by this type");
150148

clang/include/clang/Basic/DiagnosticSemaKinds.td

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12274,4 +12274,8 @@ def note_acc_branch_into_compute_construct
1227412274
: Note<"invalid branch into OpenACC Compute Construct">;
1227512275
def note_acc_branch_out_of_compute_construct
1227612276
: Note<"invalid branch out of OpenACC Compute Construct">;
12277+
def warn_acc_if_self_conflict
12278+
: Warning<"OpenACC construct 'self' has no effect when an 'if' clause "
12279+
"evaluates to true">,
12280+
InGroup<DiagGroup<"openacc-self-if-potential-conflict">>;
1227712281
} // end of sema component.

clang/include/clang/Basic/OpenACCClauses.def

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,5 +17,6 @@
1717

1818
VISIT_CLAUSE(Default)
1919
VISIT_CLAUSE(If)
20+
VISIT_CLAUSE(Self)
2021

2122
#undef VISIT_CLAUSE

clang/include/clang/Basic/OpenACCKinds.h

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -146,6 +146,12 @@ inline llvm::raw_ostream &operator<<(llvm::raw_ostream &Out,
146146
return printOpenACCDirectiveKind(Out, K);
147147
}
148148

149+
inline bool isOpenACCComputeDirectiveKind(OpenACCDirectiveKind K) {
150+
return K == OpenACCDirectiveKind::Parallel ||
151+
K == OpenACCDirectiveKind::Serial ||
152+
K == OpenACCDirectiveKind::Kernels;
153+
}
154+
149155
enum class OpenACCAtomicKind {
150156
Read,
151157
Write,

clang/include/clang/Sema/SemaOpenACC.h

Lines changed: 15 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -44,7 +44,8 @@ class SemaOpenACC : public SemaBase {
4444
Expr *ConditionExpr;
4545
};
4646

47-
std::variant<DefaultDetails, ConditionDetails> Details;
47+
std::variant<std::monostate, DefaultDetails, ConditionDetails> Details =
48+
std::monostate{};
4849

4950
public:
5051
OpenACCParsedClause(OpenACCDirectiveKind DirKind,
@@ -72,8 +73,17 @@ class SemaOpenACC : public SemaBase {
7273
}
7374

7475
Expr *getConditionExpr() {
75-
assert(ClauseKind == OpenACCClauseKind::If &&
76+
assert((ClauseKind == OpenACCClauseKind::If ||
77+
(ClauseKind == OpenACCClauseKind::Self &&
78+
DirKind != OpenACCDirectiveKind::Update)) &&
7679
"Parsed clause kind does not have a condition expr");
80+
81+
// 'self' has an optional ConditionExpr, so be tolerant of that. This will
82+
// assert in variant otherwise.
83+
if (ClauseKind == OpenACCClauseKind::Self &&
84+
std::holds_alternative<std::monostate>(Details))
85+
return nullptr;
86+
7787
return std::get<ConditionDetails>(Details).ConditionExpr;
7888
}
7989

@@ -87,7 +97,9 @@ class SemaOpenACC : public SemaBase {
8797
}
8898

8999
void setConditionDetails(Expr *ConditionExpr) {
90-
assert(ClauseKind == OpenACCClauseKind::If &&
100+
assert((ClauseKind == OpenACCClauseKind::If ||
101+
(ClauseKind == OpenACCClauseKind::Self &&
102+
DirKind != OpenACCDirectiveKind::Update)) &&
91103
"Parsed clause kind does not have a condition expr");
92104
// In C++ we can count on this being a 'bool', but in C this gets left as
93105
// some sort of scalar that codegen will have to take care of converting.

clang/lib/AST/OpenACCClause.cpp

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -48,6 +48,26 @@ OpenACCIfClause::OpenACCIfClause(SourceLocation BeginLoc,
4848
"Condition expression type not scalar/dependent");
4949
}
5050

51+
OpenACCSelfClause *OpenACCSelfClause::Create(const ASTContext &C,
52+
SourceLocation BeginLoc,
53+
SourceLocation LParenLoc,
54+
Expr *ConditionExpr,
55+
SourceLocation EndLoc) {
56+
void *Mem = C.Allocate(sizeof(OpenACCIfClause), alignof(OpenACCIfClause));
57+
return new (Mem)
58+
OpenACCSelfClause(BeginLoc, LParenLoc, ConditionExpr, EndLoc);
59+
}
60+
61+
OpenACCSelfClause::OpenACCSelfClause(SourceLocation BeginLoc,
62+
SourceLocation LParenLoc,
63+
Expr *ConditionExpr, SourceLocation EndLoc)
64+
: OpenACCClauseWithCondition(OpenACCClauseKind::Self, BeginLoc, LParenLoc,
65+
ConditionExpr, EndLoc) {
66+
assert((!ConditionExpr || ConditionExpr->isInstantiationDependent() ||
67+
ConditionExpr->getType()->isScalarType()) &&
68+
"Condition expression type not scalar/dependent");
69+
}
70+
5171
OpenACCClause::child_range OpenACCClause::children() {
5272
switch (getClauseKind()) {
5373
default:
@@ -72,3 +92,9 @@ void OpenACCClausePrinter::VisitDefaultClause(const OpenACCDefaultClause &C) {
7292
void OpenACCClausePrinter::VisitIfClause(const OpenACCIfClause &C) {
7393
OS << "if(" << C.getConditionExpr() << ")";
7494
}
95+
96+
void OpenACCClausePrinter::VisitSelfClause(const OpenACCSelfClause &C) {
97+
OS << "self";
98+
if (const Expr *CondExpr = C.getConditionExpr())
99+
OS << "(" << CondExpr << ")";
100+
}

clang/lib/AST/StmtProfile.cpp

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2491,6 +2491,11 @@ void OpenACCClauseProfiler::VisitIfClause(const OpenACCIfClause &Clause) {
24912491
"if clause requires a valid condition expr");
24922492
Profiler.VisitStmt(Clause.getConditionExpr());
24932493
}
2494+
2495+
void OpenACCClauseProfiler::VisitSelfClause(const OpenACCSelfClause &Clause) {
2496+
if (Clause.hasConditionExpr())
2497+
Profiler.VisitStmt(Clause.getConditionExpr());
2498+
}
24942499
} // namespace
24952500

24962501
void StmtProfiler::VisitOpenACCComputeConstruct(

clang/lib/AST/TextNodeDumper.cpp

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -398,6 +398,7 @@ void TextNodeDumper::Visit(const OpenACCClause *C) {
398398
OS << '(' << cast<OpenACCDefaultClause>(C)->getDefaultClauseKind() << ')';
399399
break;
400400
case OpenACCClauseKind::If:
401+
case OpenACCClauseKind::Self:
401402
// The condition expression will be printed as a part of the 'children',
402403
// but print 'clause' here so it is clear what is happening from the dump.
403404
OS << " clause";

clang/lib/Parse/ParseOpenACC.cpp

Lines changed: 11 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -835,19 +835,23 @@ Parser::OpenACCClauseParseResult Parser::ParseOpenACCClauseParams(
835835
case OpenACCClauseKind::Default: {
836836
Token DefKindTok = getCurToken();
837837

838-
if (expectIdentifierOrKeyword(*this))
839-
break;
838+
if (expectIdentifierOrKeyword(*this)) {
839+
Parens.skipToEnd();
840+
return OpenACCCanContinue();
841+
}
840842

841843
ConsumeToken();
842844

843845
OpenACCDefaultClauseKind DefKind =
844846
getOpenACCDefaultClauseKind(DefKindTok);
845847

846-
if (DefKind == OpenACCDefaultClauseKind::Invalid)
848+
if (DefKind == OpenACCDefaultClauseKind::Invalid) {
847849
Diag(DefKindTok, diag::err_acc_invalid_default_clause_kind);
848-
else
849-
ParsedClause.setDefaultDetails(DefKind);
850+
Parens.skipToEnd();
851+
return OpenACCCanContinue();
852+
}
850853

854+
ParsedClause.setDefaultDetails(DefKind);
851855
break;
852856
}
853857
case OpenACCClauseKind::If: {
@@ -977,6 +981,8 @@ Parser::OpenACCClauseParseResult Parser::ParseOpenACCClauseParams(
977981
case OpenACCClauseKind::Self: {
978982
assert(DirKind != OpenACCDirectiveKind::Update);
979983
ExprResult CondExpr = ParseOpenACCConditionExpr();
984+
ParsedClause.setConditionDetails(CondExpr.isUsable() ? CondExpr.get()
985+
: nullptr);
980986

981987
if (CondExpr.isInvalid()) {
982988
Parens.skipToEnd();

clang/lib/Sema/SemaOpenACC.cpp

Lines changed: 60 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@
1515
#include "clang/AST/StmtOpenACC.h"
1616
#include "clang/Basic/DiagnosticSema.h"
1717
#include "clang/Sema/Sema.h"
18+
#include "llvm/Support/Casting.h"
1819

1920
using namespace clang;
2021

@@ -76,6 +77,19 @@ bool doesClauseApplyToDirective(OpenACCDirectiveKind DirectiveKind,
7677
default:
7778
return false;
7879
}
80+
case OpenACCClauseKind::Self:
81+
switch (DirectiveKind) {
82+
case OpenACCDirectiveKind::Parallel:
83+
case OpenACCDirectiveKind::Serial:
84+
case OpenACCDirectiveKind::Kernels:
85+
case OpenACCDirectiveKind::Update:
86+
case OpenACCDirectiveKind::ParallelLoop:
87+
case OpenACCDirectiveKind::SerialLoop:
88+
case OpenACCDirectiveKind::KernelsLoop:
89+
return true;
90+
default:
91+
return false;
92+
}
7993
default:
8094
// Do nothing so we can go to the 'unimplemented' diagnostic instead.
8195
return true;
@@ -121,9 +135,7 @@ SemaOpenACC::ActOnClause(ArrayRef<const OpenACCClause *> ExistingClauses,
121135
// Restrictions only properly implemented on 'compute' constructs, and
122136
// 'compute' constructs are the only construct that can do anything with
123137
// this yet, so skip/treat as unimplemented in this case.
124-
if (Clause.getDirectiveKind() != OpenACCDirectiveKind::Parallel &&
125-
Clause.getDirectiveKind() != OpenACCDirectiveKind::Serial &&
126-
Clause.getDirectiveKind() != OpenACCDirectiveKind::Kernels)
138+
if (!isOpenACCComputeDirectiveKind(Clause.getDirectiveKind()))
127139
break;
128140

129141
// Don't add an invalid clause to the AST.
@@ -146,9 +158,7 @@ SemaOpenACC::ActOnClause(ArrayRef<const OpenACCClause *> ExistingClauses,
146158
// Restrictions only properly implemented on 'compute' constructs, and
147159
// 'compute' constructs are the only construct that can do anything with
148160
// this yet, so skip/treat as unimplemented in this case.
149-
if (Clause.getDirectiveKind() != OpenACCDirectiveKind::Parallel &&
150-
Clause.getDirectiveKind() != OpenACCDirectiveKind::Serial &&
151-
Clause.getDirectiveKind() != OpenACCDirectiveKind::Kernels)
161+
if (!isOpenACCComputeDirectiveKind(Clause.getDirectiveKind()))
152162
break;
153163

154164
// There is no prose in the standard that says duplicates aren't allowed,
@@ -160,12 +170,54 @@ SemaOpenACC::ActOnClause(ArrayRef<const OpenACCClause *> ExistingClauses,
160170
// The parser has ensured that we have a proper condition expr, so there
161171
// isn't really much to do here.
162172

163-
// TODO OpenACC: When we implement 'self', this clauses causes us to
164-
// 'ignore' the self clause, so we should implement a warning here.
173+
// If the 'if' clause is true, it makes the 'self' clause have no effect,
174+
// diagnose that here.
175+
// TODO OpenACC: When we add these two to other constructs, we might not
176+
// want to warn on this (for example, 'update').
177+
const auto *Itr =
178+
llvm::find_if(ExistingClauses, llvm::IsaPred<OpenACCSelfClause>);
179+
if (Itr != ExistingClauses.end()) {
180+
Diag(Clause.getBeginLoc(), diag::warn_acc_if_self_conflict);
181+
Diag((*Itr)->getBeginLoc(), diag::note_acc_previous_clause_here);
182+
}
183+
165184
return OpenACCIfClause::Create(
166185
getASTContext(), Clause.getBeginLoc(), Clause.getLParenLoc(),
167186
Clause.getConditionExpr(), Clause.getEndLoc());
168187
}
188+
189+
case OpenACCClauseKind::Self: {
190+
// Restrictions only properly implemented on 'compute' constructs, and
191+
// 'compute' constructs are the only construct that can do anything with
192+
// this yet, so skip/treat as unimplemented in this case.
193+
if (!isOpenACCComputeDirectiveKind(Clause.getDirectiveKind()))
194+
break;
195+
196+
// TODO OpenACC: When we implement this for 'update', this takes a
197+
// 'var-list' instead of a condition expression, so semantics/handling has
198+
// to happen differently here.
199+
200+
// There is no prose in the standard that says duplicates aren't allowed,
201+
// but this diagnostic is present in other compilers, as well as makes
202+
// sense.
203+
if (checkAlreadyHasClauseOfKind(*this, ExistingClauses, Clause))
204+
return nullptr;
205+
206+
// If the 'if' clause is true, it makes the 'self' clause have no effect,
207+
// diagnose that here.
208+
// TODO OpenACC: When we add these two to other constructs, we might not
209+
// want to warn on this (for example, 'update').
210+
const auto *Itr =
211+
llvm::find_if(ExistingClauses, llvm::IsaPred<OpenACCIfClause>);
212+
if (Itr != ExistingClauses.end()) {
213+
Diag(Clause.getBeginLoc(), diag::warn_acc_if_self_conflict);
214+
Diag((*Itr)->getBeginLoc(), diag::note_acc_previous_clause_here);
215+
}
216+
217+
return OpenACCSelfClause::Create(
218+
getASTContext(), Clause.getBeginLoc(), Clause.getLParenLoc(),
219+
Clause.getConditionExpr(), Clause.getEndLoc());
220+
}
169221
default:
170222
break;
171223
}

0 commit comments

Comments
 (0)