xref: /freebsd/contrib/llvm-project/clang/lib/Sema/SemaOpenACCClauseAppertainment.cpp (revision 9c77fb6aaa366cbabc80ee1b834bcfe4df135491)
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>(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(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 
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(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.
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.
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'.
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