230 lines · cpp
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-exception6//7//===----------------------------------------------------------------------===//8/// \file9/// This file implements conversions from the ACC.td from the backend to10/// 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 bit24// 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 ever28 // 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>(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} // namespace68 69// This introduces these in a llvm::acc namespace, so make sure this stays in70// the global namespace.71#define GEN_CLANG_DIRECTIVE_CLAUSE_SETS72#include "llvm/Frontend/OpenACC/ACC.inc"73 74namespace {75LLVMDirectiveClauseRelationships Relations[] =76#define GEN_CLANG_DIRECTIVE_CLAUSE_MAP77#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 this93 // 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 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} // namespace135 136// Diagnoses if `Clauses` list doesn't have at least one of the required137// 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(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 it160// 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(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 another186// 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(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'.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(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