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 | |
32 | void setBit(OpenACCClauseKind C) { |
33 | Data |= static_cast<uint64_t>(1) << static_cast<uint64_t>(C); |
34 | } |
35 | |
36 | public: |
37 | constexpr AccClauseSet(std::initializer_list<OpenACCClauseKind> Clauses) |
38 | : Data(0) { |
39 | for (OpenACCClauseKind C : Clauses) |
40 | setBit(C); |
41 | } |
42 | |
43 | constexpr bool isSet(OpenACCClauseKind C) const { |
44 | return ((Data >> static_cast<uint64_t>(C)) & 1) != 0; |
45 | } |
46 | |
47 | void clearBit(OpenACCClauseKind C) { |
48 | Data &= ~(static_cast<uint64_t>(1) << static_cast<uint64_t>(C)); |
49 | } |
50 | |
51 | constexpr bool isEmpty() const { return Data == 0; } |
52 | |
53 | unsigned popcount() const { return llvm::popcount<uint64_t>(Value: 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 | |
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 | |
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(C: CurClause)) |
101 | continue; |
102 | |
103 | OS << '\'' << CurClause << '\''; |
104 | |
105 | Set.clearBit(C: 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 | |
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. |
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(C: dealiasClauseKind(CK: C->getClauseKind()))) |
151 | return false; |
152 | } |
153 | |
154 | return Diag(DirectiveLoc, diag::err_acc_construct_one_clause_of) |
155 | << DK << getListOfClauses(Set: 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. |
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(C: CK)) |
171 | return false; |
172 | |
173 | auto Res = llvm::find_if(Range&: Clauses, P: [=](const OpenACCClause *C) { |
174 | return dealiasClauseKind(CK: 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. |
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(C: Dealiased)) |
198 | return false; |
199 | |
200 | for (const OpenACCClause *C : Clauses) { |
201 | if (Lists.AllowedExclusive.isSet(C: dealiasClauseKind(CK: 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'. |
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(C: Dealiased) && !Lists.AllowedOnce.isSet(C: Dealiased) && |
224 | !Lists.AllowedExclusive.isSet(C: Dealiased) && |
225 | !Lists.Required.isSet(C: Dealiased)) |
226 | return Diag(ClauseLoc, diag::err_acc_clause_appertainment) << DK << CK; |
227 | |
228 | return false; |
229 | } |
230 | |