blob: 5aef24512a6ecbcd6ea21e133e57d0f900ed7328 [file] [log] [blame]
//===----------------------------------------------------------------------===//
//
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
// See https://llvm.org/LICENSE.txt for license information.
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
//
//===----------------------------------------------------------------------===//
/// \file
/// This file implements conversions from the ACC.td from the backend to
/// determine appertainment, required/etc.
///
//===----------------------------------------------------------------------===//
#include "clang/Basic/DiagnosticSema.h"
#include "clang/Sema/SemaOpenACC.h"
#include "llvm/ADT/STLExtras.h"
#include "llvm/ADT/bit.h"
using namespace clang;
namespace {
// Implements a simple 'enum-set' which stores enum values in a single 64 bit
// value. Flang has `EnumSet` which is pretty sizable/has a lot of dependencies,
// so likely not worth bringing in for this use.
class AccClauseSet {
// We're just using a uint64_t as our underlying rep, so if this size ever
// gets bigger than 64, we probably need a pair of uint64_ts.
static_assert(static_cast<unsigned>(OpenACCClauseKind::Invalid) < 64);
uint64_t Data;
void setBit(OpenACCClauseKind C) {
Data |= static_cast<uint64_t>(1) << static_cast<uint64_t>(C);
}
public:
constexpr AccClauseSet(std::initializer_list<OpenACCClauseKind> Clauses)
: Data(0) {
for (OpenACCClauseKind C : Clauses)
setBit(C);
}
constexpr bool isSet(OpenACCClauseKind C) const {
return ((Data >> static_cast<uint64_t>(C)) & 1) != 0;
}
void clearBit(OpenACCClauseKind C) {
Data &= ~(static_cast<uint64_t>(1) << static_cast<uint64_t>(C));
}
constexpr bool isEmpty() const { return Data == 0; }
unsigned popcount() const { return llvm::popcount<uint64_t>(Data); }
};
struct LLVMClauseLists {
AccClauseSet Allowed;
AccClauseSet AllowedOnce;
AccClauseSet AllowedExclusive;
AccClauseSet Required;
};
struct LLVMDirectiveClauseRelationships {
OpenACCDirectiveKind DirKind;
LLVMClauseLists Lists;
};
} // namespace
// This introduces these in a llvm::acc namespace, so make sure this stays in
// the global namespace.
#define GEN_CLANG_DIRECTIVE_CLAUSE_SETS
#include "llvm/Frontend/OpenACC/ACC.inc"
namespace {
LLVMDirectiveClauseRelationships Relations[] =
#define GEN_CLANG_DIRECTIVE_CLAUSE_MAP
#include "llvm/Frontend/OpenACC/ACC.inc"
;
const LLVMClauseLists &getListsForDirective(OpenACCDirectiveKind DK) {
auto Res = llvm::find_if(Relations,
[=](const LLVMDirectiveClauseRelationships &Rel) {
return Rel.DirKind == DK;
});
assert(Res != std::end(Relations) && "Unknown directive kind?");
return Res->Lists;
}
std::string getListOfClauses(AccClauseSet Set) {
// We could probably come up with a better way to do this smuggling, but this
// is good enough for now.
std::string Output;
llvm::raw_string_ostream OS{Output};
for (unsigned I = 0; I < static_cast<unsigned>(OpenACCClauseKind::Invalid);
++I) {
OpenACCClauseKind CurClause = static_cast<OpenACCClauseKind>(I);
if (!Set.isSet(CurClause))
continue;
OS << '\'' << CurClause << '\'';
Set.clearBit(CurClause);
if (Set.isEmpty()) {
OS.flush();
return OS.str();
}
OS << ", ";
if (Set.popcount() == 1)
OS << "or ";
}
OS.flush();
return OS.str();
}
OpenACCClauseKind dealiasClauseKind(OpenACCClauseKind CK) {
switch (CK) {
default:
return CK;
#define VISIT_CLAUSE(NAME)
#define CLAUSE_ALIAS(ALIAS, NAME, DEPRECATED) \
case OpenACCClauseKind::ALIAS: \
return OpenACCClauseKind::NAME;
#include "clang/Basic/OpenACCClauses.def"
}
return CK;
}
} // namespace
// Diagnoses if `Clauses` list doesn't have at least one of the required
// clauses.
bool SemaOpenACC::DiagnoseRequiredClauses(
OpenACCDirectiveKind DK, SourceLocation DirectiveLoc,
ArrayRef<const OpenACCClause *> Clauses) {
if (DK == OpenACCDirectiveKind::Invalid)
return false;
const LLVMClauseLists &Lists = getListsForDirective(DK);
if (Lists.Required.isEmpty())
return false;
for (auto *C : Clauses) {
if (Lists.Required.isSet(dealiasClauseKind(C->getClauseKind())))
return false;
}
return Diag(DirectiveLoc, diag::err_acc_construct_one_clause_of)
<< DK << getListOfClauses(Lists.Required);
return true;
}
// Diagnoses a 'CK' on a 'DK' present more than once in a clause-list when it
// isn't allowed.
bool SemaOpenACC::DiagnoseAllowedOnceClauses(
OpenACCDirectiveKind DK, OpenACCClauseKind CK, SourceLocation ClauseLoc,
ArrayRef<const OpenACCClause *> Clauses) {
if (DK == OpenACCDirectiveKind::Invalid || CK == OpenACCClauseKind::Invalid)
return false;
OpenACCClauseKind Dealiased = dealiasClauseKind(CK);
const LLVMClauseLists &Lists = getListsForDirective(DK);
if (!Lists.AllowedOnce.isSet(CK))
return false;
auto Res = llvm::find_if(Clauses, [=](const OpenACCClause *C) {
return dealiasClauseKind(C->getClauseKind()) == Dealiased;
});
if (Res == Clauses.end())
return false;
Diag(ClauseLoc, diag::err_acc_duplicate_clause_disallowed) << DK << CK;
Diag((*Res)->getBeginLoc(), diag::note_acc_previous_clause_here) << CK;
return true;
}
// Diagnoses a 'CK' on a 'DK' being added that isn't allowed to, because another
// clause in 'Clauses' already exists.
bool SemaOpenACC::DiagnoseExclusiveClauses(
OpenACCDirectiveKind DK, OpenACCClauseKind CK, SourceLocation ClauseLoc,
ArrayRef<const OpenACCClause *> Clauses) {
if (DK == OpenACCDirectiveKind::Invalid || CK == OpenACCClauseKind::Invalid)
return false;
const LLVMClauseLists &Lists = getListsForDirective(DK);
OpenACCClauseKind Dealiased = dealiasClauseKind(CK);
// If this isn't on the list, this is fine.
if (!Lists.AllowedExclusive.isSet(Dealiased))
return false;
for (const OpenACCClause *C : Clauses) {
if (Lists.AllowedExclusive.isSet(dealiasClauseKind(C->getClauseKind()))) {
Diag(ClauseLoc, diag::err_acc_clause_cannot_combine)
<< CK << C->getClauseKind() << DK;
Diag(C->getBeginLoc(), diag::note_acc_previous_clause_here)
<< C->getClauseKind();
return true;
}
}
return false;
}
// Diagnoses if 'CK' is not allowed on a directive of 'DK'.
bool SemaOpenACC::DiagnoseAllowedClauses(OpenACCDirectiveKind DK,
OpenACCClauseKind CK,
SourceLocation ClauseLoc) {
if (DK == OpenACCDirectiveKind::Invalid || CK == OpenACCClauseKind::Invalid)
return false;
const LLVMClauseLists &Lists = getListsForDirective(DK);
OpenACCClauseKind Dealiased = dealiasClauseKind(CK);
if (!Lists.Allowed.isSet(Dealiased) && !Lists.AllowedOnce.isSet(Dealiased) &&
!Lists.AllowedExclusive.isSet(Dealiased) &&
!Lists.Required.isSet(Dealiased))
return Diag(ClauseLoc, diag::err_acc_clause_appertainment) << DK << CK;
return false;
}