To avoid modifications to global state that does not currently need to be modified, this patch makes a selection of trivial cases const. This aims to help preserve the intention of these variables and reduce the potential mutable global state surface of clang. --------- Signed-off-by: Steffen Holst Larsen <sholstla@amd.com>
230 lines
7.0 KiB
C++
230 lines
7.0 KiB
C++
//===----------------------------------------------------------------------===//
|
|
//
|
|
// 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 {
|
|
const 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;
|
|
}
|