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