| 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(Range&: Relations, |
| 83 | P: [=](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(Loc: DirectiveLoc, DiagID: 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(Loc: ClauseLoc, DiagID: diag::err_acc_duplicate_clause_disallowed) << DK << CK; |
| 181 | Diag(Loc: (*Res)->getBeginLoc(), DiagID: 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(Loc: ClauseLoc, DiagID: diag::err_acc_clause_cannot_combine) |
| 203 | << CK << C->getClauseKind() << DK; |
| 204 | Diag(Loc: C->getBeginLoc(), DiagID: 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(Loc: ClauseLoc, DiagID: diag::err_acc_clause_appertainment) << DK << CK; |
| 227 | |
| 228 | return false; |
| 229 | } |
| 230 | |