1 //===----------------------------------------------------------------------===//
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 conversions from the ACC.td from the backend to
10 /// determine appertainment, required/etc.
11 ///
12 //===----------------------------------------------------------------------===//
13
14 #include "clang/Basic/DiagnosticSema.h"
15 #include "clang/Sema/SemaOpenACC.h"
16
17 #include "llvm/ADT/STLExtras.h"
18 #include "llvm/ADT/bit.h"
19
20 using namespace clang;
21
22 namespace {
23 // Implements a simple 'enum-set' which stores enum values in a single 64 bit
24 // value. Flang has `EnumSet` which is pretty sizable/has a lot of dependencies,
25 // so likely not worth bringing in for this use.
26 class AccClauseSet {
27 // We're just using a uint64_t as our underlying rep, so if this size ever
28 // gets bigger than 64, we probably need a pair of uint64_ts.
29 static_assert(static_cast<unsigned>(OpenACCClauseKind::Invalid) < 64);
30 uint64_t Data;
31
setBit(OpenACCClauseKind C)32 void setBit(OpenACCClauseKind C) {
33 Data |= static_cast<uint64_t>(1) << static_cast<uint64_t>(C);
34 }
35
36 public:
AccClauseSet(std::initializer_list<OpenACCClauseKind> Clauses)37 constexpr AccClauseSet(std::initializer_list<OpenACCClauseKind> Clauses)
38 : Data(0) {
39 for (OpenACCClauseKind C : Clauses)
40 setBit(C);
41 }
42
isSet(OpenACCClauseKind C) const43 constexpr bool isSet(OpenACCClauseKind C) const {
44 return ((Data >> static_cast<uint64_t>(C)) & 1) != 0;
45 }
46
clearBit(OpenACCClauseKind C)47 void clearBit(OpenACCClauseKind C) {
48 Data &= ~(static_cast<uint64_t>(1) << static_cast<uint64_t>(C));
49 }
50
isEmpty() const51 constexpr bool isEmpty() const { return Data == 0; }
52
popcount() const53 unsigned popcount() const { return llvm::popcount<uint64_t>(Data); }
54 };
55
56 struct LLVMClauseLists {
57 AccClauseSet Allowed;
58 AccClauseSet AllowedOnce;
59 AccClauseSet AllowedExclusive;
60 AccClauseSet Required;
61 };
62 struct LLVMDirectiveClauseRelationships {
63 OpenACCDirectiveKind DirKind;
64 LLVMClauseLists Lists;
65 };
66
67 } // namespace
68
69 // This introduces these in a llvm::acc namespace, so make sure this stays in
70 // the global namespace.
71 #define GEN_CLANG_DIRECTIVE_CLAUSE_SETS
72 #include "llvm/Frontend/OpenACC/ACC.inc"
73
74 namespace {
75 LLVMDirectiveClauseRelationships Relations[] =
76 #define GEN_CLANG_DIRECTIVE_CLAUSE_MAP
77 #include "llvm/Frontend/OpenACC/ACC.inc"
78 ;
79
getListsForDirective(OpenACCDirectiveKind DK)80 const LLVMClauseLists &getListsForDirective(OpenACCDirectiveKind DK) {
81
82 auto Res = llvm::find_if(Relations,
83 [=](const LLVMDirectiveClauseRelationships &Rel) {
84 return Rel.DirKind == DK;
85 });
86 assert(Res != std::end(Relations) && "Unknown directive kind?");
87
88 return Res->Lists;
89 }
90
getListOfClauses(AccClauseSet Set)91 std::string getListOfClauses(AccClauseSet Set) {
92 // We could probably come up with a better way to do this smuggling, but this
93 // is good enough for now.
94 std::string Output;
95 llvm::raw_string_ostream OS{Output};
96
97 for (unsigned I = 0; I < static_cast<unsigned>(OpenACCClauseKind::Invalid);
98 ++I) {
99 OpenACCClauseKind CurClause = static_cast<OpenACCClauseKind>(I);
100 if (!Set.isSet(CurClause))
101 continue;
102
103 OS << '\'' << CurClause << '\'';
104
105 Set.clearBit(CurClause);
106
107 if (Set.isEmpty()) {
108 OS.flush();
109 return OS.str();
110 }
111
112 OS << ", ";
113
114 if (Set.popcount() == 1)
115 OS << "or ";
116 }
117 OS.flush();
118 return OS.str();
119 }
120
dealiasClauseKind(OpenACCClauseKind CK)121 OpenACCClauseKind dealiasClauseKind(OpenACCClauseKind CK) {
122 switch (CK) {
123 default:
124 return CK;
125 #define VISIT_CLAUSE(NAME)
126 #define CLAUSE_ALIAS(ALIAS, NAME, DEPRECATED) \
127 case OpenACCClauseKind::ALIAS: \
128 return OpenACCClauseKind::NAME;
129 #include "clang/Basic/OpenACCClauses.def"
130 }
131
132 return CK;
133 }
134 } // namespace
135
136 // Diagnoses if `Clauses` list doesn't have at least one of the required
137 // clauses.
DiagnoseRequiredClauses(OpenACCDirectiveKind DK,SourceLocation DirectiveLoc,ArrayRef<const OpenACCClause * > Clauses)138 bool SemaOpenACC::DiagnoseRequiredClauses(
139 OpenACCDirectiveKind DK, SourceLocation DirectiveLoc,
140 ArrayRef<const OpenACCClause *> Clauses) {
141 if (DK == OpenACCDirectiveKind::Invalid)
142 return false;
143
144 const LLVMClauseLists &Lists = getListsForDirective(DK);
145
146 if (Lists.Required.isEmpty())
147 return false;
148
149 for (auto *C : Clauses) {
150 if (Lists.Required.isSet(dealiasClauseKind(C->getClauseKind())))
151 return false;
152 }
153
154 return Diag(DirectiveLoc, diag::err_acc_construct_one_clause_of)
155 << DK << getListOfClauses(Lists.Required);
156 return true;
157 }
158
159 // Diagnoses a 'CK' on a 'DK' present more than once in a clause-list when it
160 // isn't allowed.
DiagnoseAllowedOnceClauses(OpenACCDirectiveKind DK,OpenACCClauseKind CK,SourceLocation ClauseLoc,ArrayRef<const OpenACCClause * > Clauses)161 bool SemaOpenACC::DiagnoseAllowedOnceClauses(
162 OpenACCDirectiveKind DK, OpenACCClauseKind CK, SourceLocation ClauseLoc,
163 ArrayRef<const OpenACCClause *> Clauses) {
164 if (DK == OpenACCDirectiveKind::Invalid || CK == OpenACCClauseKind::Invalid)
165 return false;
166
167 OpenACCClauseKind Dealiased = dealiasClauseKind(CK);
168
169 const LLVMClauseLists &Lists = getListsForDirective(DK);
170 if (!Lists.AllowedOnce.isSet(CK))
171 return false;
172
173 auto Res = llvm::find_if(Clauses, [=](const OpenACCClause *C) {
174 return dealiasClauseKind(C->getClauseKind()) == Dealiased;
175 });
176
177 if (Res == Clauses.end())
178 return false;
179
180 Diag(ClauseLoc, diag::err_acc_duplicate_clause_disallowed) << DK << CK;
181 Diag((*Res)->getBeginLoc(), diag::note_acc_previous_clause_here) << CK;
182 return true;
183 }
184
185 // Diagnoses a 'CK' on a 'DK' being added that isn't allowed to, because another
186 // clause in 'Clauses' already exists.
DiagnoseExclusiveClauses(OpenACCDirectiveKind DK,OpenACCClauseKind CK,SourceLocation ClauseLoc,ArrayRef<const OpenACCClause * > Clauses)187 bool SemaOpenACC::DiagnoseExclusiveClauses(
188 OpenACCDirectiveKind DK, OpenACCClauseKind CK, SourceLocation ClauseLoc,
189 ArrayRef<const OpenACCClause *> Clauses) {
190 if (DK == OpenACCDirectiveKind::Invalid || CK == OpenACCClauseKind::Invalid)
191 return false;
192
193 const LLVMClauseLists &Lists = getListsForDirective(DK);
194 OpenACCClauseKind Dealiased = dealiasClauseKind(CK);
195
196 // If this isn't on the list, this is fine.
197 if (!Lists.AllowedExclusive.isSet(Dealiased))
198 return false;
199
200 for (const OpenACCClause *C : Clauses) {
201 if (Lists.AllowedExclusive.isSet(dealiasClauseKind(C->getClauseKind()))) {
202 Diag(ClauseLoc, diag::err_acc_clause_cannot_combine)
203 << CK << C->getClauseKind() << DK;
204 Diag(C->getBeginLoc(), diag::note_acc_previous_clause_here)
205 << C->getClauseKind();
206
207 return true;
208 }
209 }
210
211 return false;
212 }
213
214 // Diagnoses if 'CK' is not allowed on a directive of 'DK'.
DiagnoseAllowedClauses(OpenACCDirectiveKind DK,OpenACCClauseKind CK,SourceLocation ClauseLoc)215 bool SemaOpenACC::DiagnoseAllowedClauses(OpenACCDirectiveKind DK,
216 OpenACCClauseKind CK,
217 SourceLocation ClauseLoc) {
218 if (DK == OpenACCDirectiveKind::Invalid || CK == OpenACCClauseKind::Invalid)
219 return false;
220 const LLVMClauseLists &Lists = getListsForDirective(DK);
221 OpenACCClauseKind Dealiased = dealiasClauseKind(CK);
222
223 if (!Lists.Allowed.isSet(Dealiased) && !Lists.AllowedOnce.isSet(Dealiased) &&
224 !Lists.AllowedExclusive.isSet(Dealiased) &&
225 !Lists.Required.isSet(Dealiased))
226 return Diag(ClauseLoc, diag::err_acc_clause_appertainment) << DK << CK;
227
228 return false;
229 }
230