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
20using namespace clang;
21
22namespace {
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.
26class 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
36public:
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
56struct LLVMClauseLists {
57 AccClauseSet Allowed;
58 AccClauseSet AllowedOnce;
59 AccClauseSet AllowedExclusive;
60 AccClauseSet Required;
61};
62struct 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
74namespace {
75LLVMDirectiveClauseRelationships Relations[] =
76#define GEN_CLANG_DIRECTIVE_CLAUSE_MAP
77#include "llvm/Frontend/OpenACC/ACC.inc"
78 ;
79
80const 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
91std::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
121OpenACCClauseKind 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.
138bool 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.
161bool 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.
187bool 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'.
215bool 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

Provided by KDAB

Privacy Policy
Improve your Profiling and Debugging skills
Find out more

source code of clang/lib/Sema/SemaOpenACCClauseAppertainment.cpp