cb424d7448
verify-patch-sanity.py validates every active recipe .patch has internally- consistent hunk line counts — catching the 'malformed patch at line N' failure at commit/CI/preflight time instead of hours into a cook. This cycle hit that class three times (qtwaylandscanner, sddm, xwayland), each only discovered when cookbook tried to apply the patch. Running it across the repo found 29 latent malformed patches (validated against GNU patch: e.g. relibc/P3-sysv-ipc reproduces 'malformed patch at line 22'). They were harmless only because they sit in vendored recipes (baked, not re- applied) — but would fail on any version-bump re-derivation. --fix recounts the hunk headers (body untouched) and repaired all 29. Wired into build-preflight.sh (Phase 1.0D) and redbear-ci.yml, with a unit test (test-patch-sanity.sh). Skips archived/legacy trees and unvalidatable formats (empty placeholders, bare-@@ git hunks).
835 lines
33 KiB
C++
835 lines
33 KiB
C++
//===-- lib/Semantics/check-acc-structure.cpp -----------------------------===//
|
|
//
|
|
// 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
|
|
//
|
|
//===----------------------------------------------------------------------===//
|
|
#include "check-acc-structure.h"
|
|
#include "flang/Common/enum-set.h"
|
|
#include "flang/Parser/parse-tree.h"
|
|
#include "flang/Semantics/tools.h"
|
|
|
|
#define CHECK_SIMPLE_CLAUSE(X, Y) \
|
|
void AccStructureChecker::Enter(const parser::AccClause::X &) { \
|
|
CheckAllowed(llvm::acc::Clause::Y); \
|
|
}
|
|
|
|
#define CHECK_REQ_SCALAR_INT_CONSTANT_CLAUSE(X, Y) \
|
|
void AccStructureChecker::Enter(const parser::AccClause::X &c) { \
|
|
CheckAllowed(llvm::acc::Clause::Y); \
|
|
RequiresConstantPositiveParameter(llvm::acc::Clause::Y, c.v); \
|
|
}
|
|
|
|
using ReductionOpsSet =
|
|
Fortran::common::EnumSet<Fortran::parser::ReductionOperator::Operator,
|
|
Fortran::parser::ReductionOperator::Operator_enumSize>;
|
|
|
|
static ReductionOpsSet reductionIntegerSet{
|
|
Fortran::parser::ReductionOperator::Operator::Plus,
|
|
Fortran::parser::ReductionOperator::Operator::Multiply,
|
|
Fortran::parser::ReductionOperator::Operator::Max,
|
|
Fortran::parser::ReductionOperator::Operator::Min,
|
|
Fortran::parser::ReductionOperator::Operator::Iand,
|
|
Fortran::parser::ReductionOperator::Operator::Ior,
|
|
Fortran::parser::ReductionOperator::Operator::Ieor};
|
|
|
|
static ReductionOpsSet reductionRealSet{
|
|
Fortran::parser::ReductionOperator::Operator::Plus,
|
|
Fortran::parser::ReductionOperator::Operator::Multiply,
|
|
Fortran::parser::ReductionOperator::Operator::Max,
|
|
Fortran::parser::ReductionOperator::Operator::Min};
|
|
|
|
static ReductionOpsSet reductionComplexSet{
|
|
Fortran::parser::ReductionOperator::Operator::Plus,
|
|
Fortran::parser::ReductionOperator::Operator::Multiply};
|
|
|
|
static ReductionOpsSet reductionLogicalSet{
|
|
Fortran::parser::ReductionOperator::Operator::And,
|
|
Fortran::parser::ReductionOperator::Operator::Or,
|
|
Fortran::parser::ReductionOperator::Operator::Eqv,
|
|
Fortran::parser::ReductionOperator::Operator::Neqv};
|
|
|
|
namespace Fortran::semantics {
|
|
|
|
static constexpr inline AccClauseSet
|
|
computeConstructOnlyAllowedAfterDeviceTypeClauses{
|
|
llvm::acc::Clause::ACCC_async, llvm::acc::Clause::ACCC_wait,
|
|
llvm::acc::Clause::ACCC_num_gangs, llvm::acc::Clause::ACCC_num_workers,
|
|
llvm::acc::Clause::ACCC_vector_length};
|
|
|
|
static constexpr inline AccClauseSet loopOnlyAllowedAfterDeviceTypeClauses{
|
|
llvm::acc::Clause::ACCC_auto, llvm::acc::Clause::ACCC_collapse,
|
|
llvm::acc::Clause::ACCC_independent, llvm::acc::Clause::ACCC_gang,
|
|
llvm::acc::Clause::ACCC_seq, llvm::acc::Clause::ACCC_tile,
|
|
llvm::acc::Clause::ACCC_vector, llvm::acc::Clause::ACCC_worker};
|
|
|
|
static constexpr inline AccClauseSet updateOnlyAllowedAfterDeviceTypeClauses{
|
|
llvm::acc::Clause::ACCC_async, llvm::acc::Clause::ACCC_wait};
|
|
|
|
static constexpr inline AccClauseSet routineOnlyAllowedAfterDeviceTypeClauses{
|
|
llvm::acc::Clause::ACCC_bind, llvm::acc::Clause::ACCC_gang,
|
|
llvm::acc::Clause::ACCC_vector, llvm::acc::Clause::ACCC_worker,
|
|
llvm::acc::Clause::ACCC_seq};
|
|
|
|
static constexpr inline AccClauseSet routineMutuallyExclusiveClauses{
|
|
llvm::acc::Clause::ACCC_gang, llvm::acc::Clause::ACCC_worker,
|
|
llvm::acc::Clause::ACCC_vector, llvm::acc::Clause::ACCC_seq};
|
|
|
|
bool AccStructureChecker::CheckAllowedModifier(llvm::acc::Clause clause) {
|
|
if (GetContext().directive == llvm::acc::ACCD_enter_data ||
|
|
GetContext().directive == llvm::acc::ACCD_exit_data) {
|
|
context_.Say(GetContext().clauseSource,
|
|
"Modifier is not allowed for the %s clause "
|
|
"on the %s directive"_err_en_US,
|
|
parser::ToUpperCaseLetters(getClauseName(clause).str()),
|
|
ContextDirectiveAsFortran());
|
|
return true;
|
|
}
|
|
return false;
|
|
}
|
|
|
|
bool AccStructureChecker::IsComputeConstruct(
|
|
llvm::acc::Directive directive) const {
|
|
return directive == llvm::acc::ACCD_parallel ||
|
|
directive == llvm::acc::ACCD_parallel_loop ||
|
|
directive == llvm::acc::ACCD_serial ||
|
|
directive == llvm::acc::ACCD_serial_loop ||
|
|
directive == llvm::acc::ACCD_kernels ||
|
|
directive == llvm::acc::ACCD_kernels_loop;
|
|
}
|
|
|
|
bool AccStructureChecker::IsInsideComputeConstruct() const {
|
|
if (dirContext_.size() <= 1) {
|
|
return false;
|
|
}
|
|
|
|
// Check all nested context skipping the first one.
|
|
for (std::size_t i = dirContext_.size() - 1; i > 0; --i) {
|
|
if (IsComputeConstruct(dirContext_[i - 1].directive)) {
|
|
return true;
|
|
}
|
|
}
|
|
return false;
|
|
}
|
|
|
|
void AccStructureChecker::CheckNotInComputeConstruct() {
|
|
if (IsInsideComputeConstruct()) {
|
|
context_.Say(GetContext().directiveSource,
|
|
"Directive %s may not be called within a compute region"_err_en_US,
|
|
ContextDirectiveAsFortran());
|
|
}
|
|
}
|
|
|
|
void AccStructureChecker::Enter(const parser::AccClause &x) {
|
|
SetContextClause(x);
|
|
}
|
|
|
|
void AccStructureChecker::Leave(const parser::AccClauseList &) {}
|
|
|
|
void AccStructureChecker::Enter(const parser::OpenACCBlockConstruct &x) {
|
|
const auto &beginBlockDir{std::get<parser::AccBeginBlockDirective>(x.t)};
|
|
const auto &endBlockDir{std::get<parser::AccEndBlockDirective>(x.t)};
|
|
const auto &beginAccBlockDir{
|
|
std::get<parser::AccBlockDirective>(beginBlockDir.t)};
|
|
|
|
CheckMatching(beginAccBlockDir, endBlockDir.v);
|
|
PushContextAndClauseSets(beginAccBlockDir.source, beginAccBlockDir.v);
|
|
}
|
|
|
|
void AccStructureChecker::Leave(const parser::OpenACCBlockConstruct &x) {
|
|
const auto &beginBlockDir{std::get<parser::AccBeginBlockDirective>(x.t)};
|
|
const auto &blockDir{std::get<parser::AccBlockDirective>(beginBlockDir.t)};
|
|
const parser::Block &block{std::get<parser::Block>(x.t)};
|
|
switch (blockDir.v) {
|
|
case llvm::acc::Directive::ACCD_kernels:
|
|
case llvm::acc::Directive::ACCD_parallel:
|
|
case llvm::acc::Directive::ACCD_serial:
|
|
// Restriction - line 1004-1005
|
|
CheckOnlyAllowedAfter(llvm::acc::Clause::ACCC_device_type,
|
|
computeConstructOnlyAllowedAfterDeviceTypeClauses);
|
|
// Restriction - line 1001
|
|
CheckNoBranching(block, GetContext().directive, blockDir.source);
|
|
break;
|
|
case llvm::acc::Directive::ACCD_data:
|
|
// Restriction - 2.6.5 pt 1
|
|
// Only a warning is emitted here for portability reason.
|
|
CheckRequireAtLeastOneOf(/*warnInsteadOfError=*/true);
|
|
// Restriction is not formally in the specification but all compilers emit
|
|
// an error and it is likely to be omitted from the spec.
|
|
CheckNoBranching(block, GetContext().directive, blockDir.source);
|
|
break;
|
|
case llvm::acc::Directive::ACCD_host_data:
|
|
// Restriction - line 1746
|
|
CheckRequireAtLeastOneOf();
|
|
break;
|
|
default:
|
|
break;
|
|
}
|
|
dirContext_.pop_back();
|
|
}
|
|
|
|
void AccStructureChecker::Enter(
|
|
const parser::OpenACCStandaloneDeclarativeConstruct &x) {
|
|
const auto &declarativeDir{std::get<parser::AccDeclarativeDirective>(x.t)};
|
|
PushContextAndClauseSets(declarativeDir.source, declarativeDir.v);
|
|
}
|
|
|
|
void AccStructureChecker::Leave(
|
|
const parser::OpenACCStandaloneDeclarativeConstruct &x) {
|
|
// Restriction - line 2409
|
|
CheckAtLeastOneClause();
|
|
|
|
// Restriction - line 2417-2418 - In a Fortran module declaration section,
|
|
// only create, copyin, device_resident, and link clauses are allowed.
|
|
const auto &declarativeDir{std::get<parser::AccDeclarativeDirective>(x.t)};
|
|
const auto &scope{context_.FindScope(declarativeDir.source)};
|
|
const Scope &containingScope{GetProgramUnitContaining(scope)};
|
|
if (containingScope.kind() == Scope::Kind::Module) {
|
|
for (auto cl : GetContext().actualClauses) {
|
|
if (cl != llvm::acc::Clause::ACCC_create &&
|
|
cl != llvm::acc::Clause::ACCC_copyin &&
|
|
cl != llvm::acc::Clause::ACCC_device_resident &&
|
|
cl != llvm::acc::Clause::ACCC_link) {
|
|
context_.Say(GetContext().directiveSource,
|
|
"%s clause is not allowed on the %s directive in module "
|
|
"declaration "
|
|
"section"_err_en_US,
|
|
parser::ToUpperCaseLetters(
|
|
llvm::acc::getOpenACCClauseName(cl).str()),
|
|
ContextDirectiveAsFortran());
|
|
}
|
|
}
|
|
}
|
|
dirContext_.pop_back();
|
|
}
|
|
|
|
void AccStructureChecker::Enter(const parser::OpenACCCombinedConstruct &x) {
|
|
const auto &beginCombinedDir{
|
|
std::get<parser::AccBeginCombinedDirective>(x.t)};
|
|
const auto &combinedDir{
|
|
std::get<parser::AccCombinedDirective>(beginCombinedDir.t)};
|
|
|
|
// check matching, End directive is optional
|
|
if (const auto &endCombinedDir{
|
|
std::get<std::optional<parser::AccEndCombinedDirective>>(x.t)}) {
|
|
CheckMatching<parser::AccCombinedDirective>(combinedDir, endCombinedDir->v);
|
|
}
|
|
|
|
PushContextAndClauseSets(combinedDir.source, combinedDir.v);
|
|
}
|
|
|
|
void AccStructureChecker::Leave(const parser::OpenACCCombinedConstruct &x) {
|
|
const auto &beginBlockDir{std::get<parser::AccBeginCombinedDirective>(x.t)};
|
|
const auto &combinedDir{
|
|
std::get<parser::AccCombinedDirective>(beginBlockDir.t)};
|
|
auto &doCons{std::get<std::optional<parser::DoConstruct>>(x.t)};
|
|
switch (combinedDir.v) {
|
|
case llvm::acc::Directive::ACCD_kernels_loop:
|
|
case llvm::acc::Directive::ACCD_parallel_loop:
|
|
case llvm::acc::Directive::ACCD_serial_loop:
|
|
// Restriction - line 1004-1005
|
|
CheckOnlyAllowedAfter(llvm::acc::Clause::ACCC_device_type,
|
|
computeConstructOnlyAllowedAfterDeviceTypeClauses |
|
|
loopOnlyAllowedAfterDeviceTypeClauses);
|
|
if (doCons) {
|
|
const parser::Block &block{std::get<parser::Block>(doCons->t)};
|
|
CheckNoBranching(block, GetContext().directive, beginBlockDir.source);
|
|
}
|
|
break;
|
|
default:
|
|
break;
|
|
}
|
|
dirContext_.pop_back();
|
|
}
|
|
|
|
void AccStructureChecker::Enter(const parser::OpenACCLoopConstruct &x) {
|
|
const auto &beginDir{std::get<parser::AccBeginLoopDirective>(x.t)};
|
|
const auto &loopDir{std::get<parser::AccLoopDirective>(beginDir.t)};
|
|
PushContextAndClauseSets(loopDir.source, loopDir.v);
|
|
}
|
|
|
|
void AccStructureChecker::Leave(const parser::OpenACCLoopConstruct &x) {
|
|
const auto &beginDir{std::get<parser::AccBeginLoopDirective>(x.t)};
|
|
const auto &loopDir{std::get<parser::AccLoopDirective>(beginDir.t)};
|
|
if (loopDir.v == llvm::acc::Directive::ACCD_loop) {
|
|
// Restriction - line 1818-1819
|
|
CheckOnlyAllowedAfter(llvm::acc::Clause::ACCC_device_type,
|
|
loopOnlyAllowedAfterDeviceTypeClauses);
|
|
// Restriction - line 1834
|
|
CheckNotAllowedIfClause(llvm::acc::Clause::ACCC_seq,
|
|
{llvm::acc::Clause::ACCC_gang, llvm::acc::Clause::ACCC_vector,
|
|
llvm::acc::Clause::ACCC_worker});
|
|
}
|
|
dirContext_.pop_back();
|
|
}
|
|
|
|
void AccStructureChecker::Enter(const parser::OpenACCStandaloneConstruct &x) {
|
|
const auto &standaloneDir{std::get<parser::AccStandaloneDirective>(x.t)};
|
|
PushContextAndClauseSets(standaloneDir.source, standaloneDir.v);
|
|
}
|
|
|
|
void AccStructureChecker::Leave(const parser::OpenACCStandaloneConstruct &x) {
|
|
const auto &standaloneDir{std::get<parser::AccStandaloneDirective>(x.t)};
|
|
switch (standaloneDir.v) {
|
|
case llvm::acc::Directive::ACCD_enter_data:
|
|
case llvm::acc::Directive::ACCD_exit_data:
|
|
// Restriction - line 1310-1311 (ENTER DATA)
|
|
// Restriction - line 1312-1313 (EXIT DATA)
|
|
CheckRequireAtLeastOneOf();
|
|
break;
|
|
case llvm::acc::Directive::ACCD_set:
|
|
// Restriction - line 2610
|
|
CheckRequireAtLeastOneOf();
|
|
// Restriction - line 2602
|
|
CheckNotInComputeConstruct();
|
|
break;
|
|
case llvm::acc::Directive::ACCD_update:
|
|
// Restriction - line 2636
|
|
CheckRequireAtLeastOneOf();
|
|
// Restriction - line 2669
|
|
CheckOnlyAllowedAfter(llvm::acc::Clause::ACCC_device_type,
|
|
updateOnlyAllowedAfterDeviceTypeClauses);
|
|
break;
|
|
case llvm::acc::Directive::ACCD_init:
|
|
case llvm::acc::Directive::ACCD_shutdown:
|
|
// Restriction - line 2525 (INIT)
|
|
// Restriction - line 2561 (SHUTDOWN)
|
|
CheckNotInComputeConstruct();
|
|
break;
|
|
default:
|
|
break;
|
|
}
|
|
dirContext_.pop_back();
|
|
}
|
|
|
|
void AccStructureChecker::Enter(const parser::OpenACCRoutineConstruct &x) {
|
|
PushContextAndClauseSets(x.source, llvm::acc::Directive::ACCD_routine);
|
|
const auto &optName{std::get<std::optional<parser::Name>>(x.t)};
|
|
if (!optName) {
|
|
const auto &verbatim{std::get<parser::Verbatim>(x.t)};
|
|
const auto &scope{context_.FindScope(verbatim.source)};
|
|
const Scope &containingScope{GetProgramUnitContaining(scope)};
|
|
if (containingScope.kind() == Scope::Kind::Module) {
|
|
context_.Say(GetContext().directiveSource,
|
|
"ROUTINE directive without name must appear within the specification "
|
|
"part of a subroutine or function definition, or within an interface "
|
|
"body for a subroutine or function in an interface block"_err_en_US);
|
|
}
|
|
}
|
|
}
|
|
void AccStructureChecker::Leave(const parser::OpenACCRoutineConstruct &) {
|
|
// Restriction - line 2790
|
|
CheckRequireAtLeastOneOf();
|
|
// Restriction - line 2788-2789
|
|
CheckOnlyAllowedAfter(llvm::acc::Clause::ACCC_device_type,
|
|
routineOnlyAllowedAfterDeviceTypeClauses);
|
|
dirContext_.pop_back();
|
|
}
|
|
|
|
void AccStructureChecker::Enter(const parser::OpenACCWaitConstruct &x) {
|
|
const auto &verbatim{std::get<parser::Verbatim>(x.t)};
|
|
PushContextAndClauseSets(verbatim.source, llvm::acc::Directive::ACCD_wait);
|
|
}
|
|
void AccStructureChecker::Leave(const parser::OpenACCWaitConstruct &x) {
|
|
dirContext_.pop_back();
|
|
}
|
|
|
|
void AccStructureChecker::Enter(const parser::OpenACCAtomicConstruct &x) {
|
|
PushContextAndClauseSets(x.source, llvm::acc::Directive::ACCD_atomic);
|
|
}
|
|
void AccStructureChecker::Leave(const parser::OpenACCAtomicConstruct &x) {
|
|
dirContext_.pop_back();
|
|
}
|
|
|
|
void AccStructureChecker::Enter(const parser::AccAtomicUpdate &x) {
|
|
const parser::AssignmentStmt &assignment{
|
|
std::get<parser::Statement<parser::AssignmentStmt>>(x.t).statement};
|
|
const auto &var{std::get<parser::Variable>(assignment.t)};
|
|
const auto &expr{std::get<parser::Expr>(assignment.t)};
|
|
const auto *rhs{GetExpr(context_, expr)};
|
|
const auto *lhs{GetExpr(context_, var)};
|
|
if (lhs && rhs) {
|
|
if (lhs->Rank() != 0)
|
|
context_.Say(expr.source,
|
|
"LHS of atomic update statement must be scalar"_err_en_US);
|
|
if (rhs->Rank() != 0)
|
|
context_.Say(var.GetSource(),
|
|
"RHS of atomic update statement must be scalar"_err_en_US);
|
|
}
|
|
}
|
|
|
|
void AccStructureChecker::Enter(const parser::OpenACCCacheConstruct &x) {
|
|
const auto &verbatim = std::get<parser::Verbatim>(x.t);
|
|
PushContextAndClauseSets(verbatim.source, llvm::acc::Directive::ACCD_cache);
|
|
SetContextDirectiveSource(verbatim.source);
|
|
if (loopNestLevel == 0) {
|
|
context_.Say(verbatim.source,
|
|
"The CACHE directive must be inside a loop"_err_en_US);
|
|
}
|
|
}
|
|
void AccStructureChecker::Leave(const parser::OpenACCCacheConstruct &x) {
|
|
dirContext_.pop_back();
|
|
}
|
|
|
|
// Clause checkers
|
|
CHECK_SIMPLE_CLAUSE(Auto, ACCC_auto)
|
|
CHECK_SIMPLE_CLAUSE(Attach, ACCC_attach)
|
|
CHECK_SIMPLE_CLAUSE(Bind, ACCC_bind)
|
|
CHECK_SIMPLE_CLAUSE(Capture, ACCC_capture)
|
|
CHECK_SIMPLE_CLAUSE(Default, ACCC_default)
|
|
CHECK_SIMPLE_CLAUSE(DefaultAsync, ACCC_default_async)
|
|
CHECK_SIMPLE_CLAUSE(Delete, ACCC_delete)
|
|
CHECK_SIMPLE_CLAUSE(Detach, ACCC_detach)
|
|
CHECK_SIMPLE_CLAUSE(Device, ACCC_device)
|
|
CHECK_SIMPLE_CLAUSE(DeviceNum, ACCC_device_num)
|
|
CHECK_SIMPLE_CLAUSE(Finalize, ACCC_finalize)
|
|
CHECK_SIMPLE_CLAUSE(Firstprivate, ACCC_firstprivate)
|
|
CHECK_SIMPLE_CLAUSE(Host, ACCC_host)
|
|
CHECK_SIMPLE_CLAUSE(IfPresent, ACCC_if_present)
|
|
CHECK_SIMPLE_CLAUSE(Independent, ACCC_independent)
|
|
CHECK_SIMPLE_CLAUSE(NoCreate, ACCC_no_create)
|
|
CHECK_SIMPLE_CLAUSE(Nohost, ACCC_nohost)
|
|
CHECK_SIMPLE_CLAUSE(Private, ACCC_private)
|
|
CHECK_SIMPLE_CLAUSE(Read, ACCC_read)
|
|
CHECK_SIMPLE_CLAUSE(UseDevice, ACCC_use_device)
|
|
CHECK_SIMPLE_CLAUSE(Wait, ACCC_wait)
|
|
CHECK_SIMPLE_CLAUSE(Write, ACCC_write)
|
|
CHECK_SIMPLE_CLAUSE(Unknown, ACCC_unknown)
|
|
|
|
void AccStructureChecker::CheckMultipleOccurrenceInDeclare(
|
|
const parser::AccObjectList &list, llvm::acc::Clause clause) {
|
|
if (GetContext().directive != llvm::acc::Directive::ACCD_declare)
|
|
return;
|
|
for (const auto &object : list.v) {
|
|
common::visit(
|
|
common::visitors{
|
|
[&](const parser::Designator &designator) {
|
|
if (const auto *name = getDesignatorNameIfDataRef(designator)) {
|
|
if (declareSymbols.contains(&name->symbol->GetUltimate())) {
|
|
if (declareSymbols[&name->symbol->GetUltimate()] == clause) {
|
|
context_.Warn(common::UsageWarning::OpenAccUsage,
|
|
GetContext().clauseSource,
|
|
"'%s' in the %s clause is already present in the same clause in this module"_warn_en_US,
|
|
name->symbol->name(),
|
|
parser::ToUpperCaseLetters(
|
|
llvm::acc::getOpenACCClauseName(clause).str()));
|
|
} else {
|
|
context_.Say(GetContext().clauseSource,
|
|
"'%s' in the %s clause is already present in another "
|
|
"%s clause in this module"_err_en_US,
|
|
name->symbol->name(),
|
|
parser::ToUpperCaseLetters(
|
|
llvm::acc::getOpenACCClauseName(clause).str()),
|
|
parser::ToUpperCaseLetters(
|
|
llvm::acc::getOpenACCClauseName(
|
|
declareSymbols[&name->symbol->GetUltimate()])
|
|
.str()));
|
|
}
|
|
}
|
|
declareSymbols.insert({&name->symbol->GetUltimate(), clause});
|
|
}
|
|
},
|
|
[&](const parser::Name &name) {
|
|
// TODO: check common block
|
|
}},
|
|
object.u);
|
|
}
|
|
}
|
|
|
|
void AccStructureChecker::CheckMultipleOccurrenceInDeclare(
|
|
const parser::AccObjectListWithModifier &list, llvm::acc::Clause clause) {
|
|
const auto &objectList = std::get<Fortran::parser::AccObjectList>(list.t);
|
|
CheckMultipleOccurrenceInDeclare(objectList, clause);
|
|
}
|
|
|
|
void AccStructureChecker::Enter(const parser::AccClause::Async &c) {
|
|
llvm::acc::Clause crtClause = llvm::acc::Clause::ACCC_async;
|
|
CheckAllowed(crtClause);
|
|
CheckAllowedOncePerGroup(crtClause, llvm::acc::Clause::ACCC_device_type);
|
|
}
|
|
|
|
void AccStructureChecker::Enter(const parser::AccClause::Create &c) {
|
|
CheckAllowed(llvm::acc::Clause::ACCC_create);
|
|
const auto &modifierClause{c.v};
|
|
if (const auto &modifier{
|
|
std::get<std::optional<parser::AccDataModifier>>(modifierClause.t)}) {
|
|
if (modifier->v != parser::AccDataModifier::Modifier::Zero) {
|
|
context_.Say(GetContext().clauseSource,
|
|
"Only the ZERO modifier is allowed for the %s clause "
|
|
"on the %s directive"_err_en_US,
|
|
parser::ToUpperCaseLetters(
|
|
llvm::acc::getOpenACCClauseName(llvm::acc::Clause::ACCC_create)
|
|
.str()),
|
|
ContextDirectiveAsFortran());
|
|
}
|
|
if (GetContext().directive == llvm::acc::Directive::ACCD_declare) {
|
|
context_.Say(GetContext().clauseSource,
|
|
"The ZERO modifier is not allowed for the %s clause "
|
|
"on the %s directive"_err_en_US,
|
|
parser::ToUpperCaseLetters(
|
|
llvm::acc::getOpenACCClauseName(llvm::acc::Clause::ACCC_create)
|
|
.str()),
|
|
ContextDirectiveAsFortran());
|
|
}
|
|
}
|
|
CheckMultipleOccurrenceInDeclare(
|
|
modifierClause, llvm::acc::Clause::ACCC_create);
|
|
}
|
|
|
|
void AccStructureChecker::Enter(const parser::AccClause::Copyin &c) {
|
|
CheckAllowed(llvm::acc::Clause::ACCC_copyin);
|
|
const auto &modifierClause{c.v};
|
|
if (const auto &modifier{
|
|
std::get<std::optional<parser::AccDataModifier>>(modifierClause.t)}) {
|
|
if (CheckAllowedModifier(llvm::acc::Clause::ACCC_copyin)) {
|
|
return;
|
|
}
|
|
if (modifier->v != parser::AccDataModifier::Modifier::ReadOnly) {
|
|
context_.Say(GetContext().clauseSource,
|
|
"Only the READONLY modifier is allowed for the %s clause "
|
|
"on the %s directive"_err_en_US,
|
|
parser::ToUpperCaseLetters(
|
|
llvm::acc::getOpenACCClauseName(llvm::acc::Clause::ACCC_copyin)
|
|
.str()),
|
|
ContextDirectiveAsFortran());
|
|
}
|
|
}
|
|
CheckMultipleOccurrenceInDeclare(
|
|
modifierClause, llvm::acc::Clause::ACCC_copyin);
|
|
}
|
|
|
|
void AccStructureChecker::Enter(const parser::AccClause::Copyout &c) {
|
|
CheckAllowed(llvm::acc::Clause::ACCC_copyout);
|
|
const auto &modifierClause{c.v};
|
|
if (const auto &modifier{
|
|
std::get<std::optional<parser::AccDataModifier>>(modifierClause.t)}) {
|
|
if (CheckAllowedModifier(llvm::acc::Clause::ACCC_copyout)) {
|
|
return;
|
|
}
|
|
if (modifier->v != parser::AccDataModifier::Modifier::Zero) {
|
|
context_.Say(GetContext().clauseSource,
|
|
"Only the ZERO modifier is allowed for the %s clause "
|
|
"on the %s directive"_err_en_US,
|
|
parser::ToUpperCaseLetters(
|
|
llvm::acc::getOpenACCClauseName(llvm::acc::Clause::ACCC_copyout)
|
|
.str()),
|
|
ContextDirectiveAsFortran());
|
|
}
|
|
if (GetContext().directive == llvm::acc::Directive::ACCD_declare) {
|
|
context_.Say(GetContext().clauseSource,
|
|
"The ZERO modifier is not allowed for the %s clause "
|
|
"on the %s directive"_err_en_US,
|
|
parser::ToUpperCaseLetters(
|
|
llvm::acc::getOpenACCClauseName(llvm::acc::Clause::ACCC_copyout)
|
|
.str()),
|
|
ContextDirectiveAsFortran());
|
|
}
|
|
}
|
|
CheckMultipleOccurrenceInDeclare(
|
|
modifierClause, llvm::acc::Clause::ACCC_copyout);
|
|
}
|
|
|
|
void AccStructureChecker::Enter(const parser::AccClause::DeviceType &d) {
|
|
CheckAllowed(llvm::acc::Clause::ACCC_device_type);
|
|
if (GetContext().directive == llvm::acc::Directive::ACCD_set &&
|
|
d.v.v.size() > 1) {
|
|
context_.Say(GetContext().clauseSource,
|
|
"The %s clause on the %s directive accepts only one value"_err_en_US,
|
|
parser::ToUpperCaseLetters(
|
|
llvm::acc::getOpenACCClauseName(llvm::acc::Clause::ACCC_device_type)
|
|
.str()),
|
|
ContextDirectiveAsFortran());
|
|
}
|
|
ResetCrtGroup();
|
|
}
|
|
|
|
void AccStructureChecker::Enter(const parser::AccClause::Seq &g) {
|
|
llvm::acc::Clause crtClause = llvm::acc::Clause::ACCC_seq;
|
|
if (GetContext().directive == llvm::acc::Directive::ACCD_routine) {
|
|
CheckMutuallyExclusivePerGroup(crtClause,
|
|
llvm::acc::Clause::ACCC_device_type, routineMutuallyExclusiveClauses);
|
|
}
|
|
CheckAllowed(crtClause);
|
|
}
|
|
|
|
void AccStructureChecker::Enter(const parser::AccClause::Vector &g) {
|
|
llvm::acc::Clause crtClause = llvm::acc::Clause::ACCC_vector;
|
|
if (GetContext().directive == llvm::acc::Directive::ACCD_routine) {
|
|
CheckMutuallyExclusivePerGroup(crtClause,
|
|
llvm::acc::Clause::ACCC_device_type, routineMutuallyExclusiveClauses);
|
|
}
|
|
CheckAllowed(crtClause);
|
|
if (GetContext().directive != llvm::acc::Directive::ACCD_routine) {
|
|
CheckAllowedOncePerGroup(crtClause, llvm::acc::Clause::ACCC_device_type);
|
|
}
|
|
}
|
|
|
|
void AccStructureChecker::Enter(const parser::AccClause::Worker &g) {
|
|
llvm::acc::Clause crtClause = llvm::acc::Clause::ACCC_worker;
|
|
if (GetContext().directive == llvm::acc::Directive::ACCD_routine) {
|
|
CheckMutuallyExclusivePerGroup(crtClause,
|
|
llvm::acc::Clause::ACCC_device_type, routineMutuallyExclusiveClauses);
|
|
}
|
|
CheckAllowed(crtClause);
|
|
if (GetContext().directive != llvm::acc::Directive::ACCD_routine) {
|
|
CheckAllowedOncePerGroup(crtClause, llvm::acc::Clause::ACCC_device_type);
|
|
}
|
|
}
|
|
|
|
void AccStructureChecker::Enter(const parser::AccClause::Tile &g) {
|
|
CheckAllowed(llvm::acc::Clause::ACCC_tile);
|
|
CheckAllowedOncePerGroup(
|
|
llvm::acc::Clause::ACCC_tile, llvm::acc::Clause::ACCC_device_type);
|
|
}
|
|
|
|
void AccStructureChecker::Enter(const parser::AccClause::Gang &g) {
|
|
llvm::acc::Clause crtClause = llvm::acc::Clause::ACCC_gang;
|
|
if (GetContext().directive == llvm::acc::Directive::ACCD_routine) {
|
|
CheckMutuallyExclusivePerGroup(crtClause,
|
|
llvm::acc::Clause::ACCC_device_type, routineMutuallyExclusiveClauses);
|
|
}
|
|
CheckAllowed(crtClause);
|
|
if (GetContext().directive != llvm::acc::Directive::ACCD_routine) {
|
|
CheckAllowedOncePerGroup(crtClause, llvm::acc::Clause::ACCC_device_type);
|
|
}
|
|
|
|
if (g.v) {
|
|
bool hasNum = false;
|
|
bool hasDim = false;
|
|
bool hasStatic = false;
|
|
const Fortran::parser::AccGangArgList &x = *g.v;
|
|
for (const Fortran::parser::AccGangArg &gangArg : x.v) {
|
|
if (std::get_if<Fortran::parser::AccGangArg::Num>(&gangArg.u)) {
|
|
hasNum = true;
|
|
} else if (std::get_if<Fortran::parser::AccGangArg::Dim>(&gangArg.u)) {
|
|
hasDim = true;
|
|
} else if (std::get_if<Fortran::parser::AccGangArg::Static>(&gangArg.u)) {
|
|
hasStatic = true;
|
|
}
|
|
}
|
|
|
|
if (GetContext().directive == llvm::acc::Directive::ACCD_routine &&
|
|
(hasStatic || hasNum)) {
|
|
context_.Say(GetContext().clauseSource,
|
|
"Only the dim argument is allowed on the %s clause on the %s directive"_err_en_US,
|
|
parser::ToUpperCaseLetters(
|
|
llvm::acc::getOpenACCClauseName(llvm::acc::Clause::ACCC_gang)
|
|
.str()),
|
|
ContextDirectiveAsFortran());
|
|
}
|
|
|
|
if (hasDim && hasNum) {
|
|
context_.Say(GetContext().clauseSource,
|
|
"The num argument is not allowed when dim is specified"_err_en_US);
|
|
}
|
|
}
|
|
}
|
|
|
|
void AccStructureChecker::Enter(const parser::AccClause::NumGangs &n) {
|
|
CheckAllowed(llvm::acc::Clause::ACCC_num_gangs,
|
|
/*warnInsteadOfError=*/GetContext().directive ==
|
|
llvm::acc::Directive::ACCD_serial ||
|
|
GetContext().directive == llvm::acc::Directive::ACCD_serial_loop);
|
|
CheckAllowedOncePerGroup(
|
|
llvm::acc::Clause::ACCC_num_gangs, llvm::acc::Clause::ACCC_device_type);
|
|
|
|
if (n.v.size() > 3)
|
|
context_.Say(GetContext().clauseSource,
|
|
"NUM_GANGS clause accepts a maximum of 3 arguments"_err_en_US);
|
|
}
|
|
|
|
void AccStructureChecker::Enter(const parser::AccClause::NumWorkers &n) {
|
|
CheckAllowed(llvm::acc::Clause::ACCC_num_workers,
|
|
/*warnInsteadOfError=*/GetContext().directive ==
|
|
llvm::acc::Directive::ACCD_serial ||
|
|
GetContext().directive == llvm::acc::Directive::ACCD_serial_loop);
|
|
CheckAllowedOncePerGroup(
|
|
llvm::acc::Clause::ACCC_num_workers, llvm::acc::Clause::ACCC_device_type);
|
|
}
|
|
|
|
void AccStructureChecker::Enter(const parser::AccClause::VectorLength &n) {
|
|
CheckAllowed(llvm::acc::Clause::ACCC_vector_length,
|
|
/*warnInsteadOfError=*/GetContext().directive ==
|
|
llvm::acc::Directive::ACCD_serial ||
|
|
GetContext().directive == llvm::acc::Directive::ACCD_serial_loop);
|
|
CheckAllowedOncePerGroup(llvm::acc::Clause::ACCC_vector_length,
|
|
llvm::acc::Clause::ACCC_device_type);
|
|
}
|
|
|
|
void AccStructureChecker::Enter(const parser::AccClause::Reduction &reduction) {
|
|
CheckAllowed(llvm::acc::Clause::ACCC_reduction);
|
|
|
|
// From OpenACC 3.3
|
|
// At a minimum, the supported data types include Fortran logical as well as
|
|
// the numerical data types (e.g. integer, real, double precision, complex).
|
|
// However, for each reduction operator, the supported data types include only
|
|
// the types permitted as operands to the corresponding operator in the base
|
|
// language where (1) for max and min, the corresponding operator is less-than
|
|
// and (2) for other operators, the operands and the result are the same type.
|
|
//
|
|
// The following check that the reduction operator is supported with the given
|
|
// type.
|
|
const parser::AccObjectListWithReduction &list{reduction.v};
|
|
const auto &op{std::get<parser::ReductionOperator>(list.t)};
|
|
const auto &objects{std::get<parser::AccObjectList>(list.t)};
|
|
|
|
for (const auto &object : objects.v) {
|
|
common::visit(
|
|
common::visitors{
|
|
[&](const parser::Designator &designator) {
|
|
if (const auto *name = getDesignatorNameIfDataRef(designator)) {
|
|
if (name->symbol) {
|
|
const auto *type{name->symbol->GetType()};
|
|
if (type->IsNumeric(TypeCategory::Integer) &&
|
|
!reductionIntegerSet.test(op.v)) {
|
|
context_.Say(GetContext().clauseSource,
|
|
"reduction operator not supported for integer type"_err_en_US);
|
|
} else if (type->IsNumeric(TypeCategory::Real) &&
|
|
!reductionRealSet.test(op.v)) {
|
|
context_.Say(GetContext().clauseSource,
|
|
"reduction operator not supported for real type"_err_en_US);
|
|
} else if (type->IsNumeric(TypeCategory::Complex) &&
|
|
!reductionComplexSet.test(op.v)) {
|
|
context_.Say(GetContext().clauseSource,
|
|
"reduction operator not supported for complex type"_err_en_US);
|
|
} else if (type->category() ==
|
|
Fortran::semantics::DeclTypeSpec::Category::Logical &&
|
|
!reductionLogicalSet.test(op.v)) {
|
|
context_.Say(GetContext().clauseSource,
|
|
"reduction operator not supported for logical type"_err_en_US);
|
|
}
|
|
// TODO: check composite type.
|
|
}
|
|
}
|
|
},
|
|
[&](const Fortran::parser::Name &name) {
|
|
// TODO: check common block
|
|
}},
|
|
object.u);
|
|
}
|
|
}
|
|
|
|
void AccStructureChecker::Enter(const parser::AccClause::Self &x) {
|
|
CheckAllowed(llvm::acc::Clause::ACCC_self);
|
|
const std::optional<parser::AccSelfClause> &accSelfClause = x.v;
|
|
if (GetContext().directive == llvm::acc::Directive::ACCD_update &&
|
|
((accSelfClause &&
|
|
std::holds_alternative<std::optional<parser::ScalarLogicalExpr>>(
|
|
(*accSelfClause).u)) ||
|
|
!accSelfClause)) {
|
|
context_.Say(GetContext().clauseSource,
|
|
"SELF clause on the %s directive must have a var-list"_err_en_US,
|
|
ContextDirectiveAsFortran());
|
|
} else if (GetContext().directive != llvm::acc::Directive::ACCD_update &&
|
|
accSelfClause &&
|
|
std::holds_alternative<parser::AccObjectList>((*accSelfClause).u)) {
|
|
const auto &accObjectList =
|
|
std::get<parser::AccObjectList>((*accSelfClause).u);
|
|
if (accObjectList.v.size() != 1) {
|
|
context_.Say(GetContext().clauseSource,
|
|
"SELF clause on the %s directive only accepts optional scalar logical"
|
|
" expression"_err_en_US,
|
|
ContextDirectiveAsFortran());
|
|
}
|
|
}
|
|
}
|
|
|
|
void AccStructureChecker::Enter(const parser::AccClause::Collapse &x) {
|
|
CheckAllowed(llvm::acc::Clause::ACCC_collapse);
|
|
CheckAllowedOncePerGroup(
|
|
llvm::acc::Clause::ACCC_collapse, llvm::acc::Clause::ACCC_device_type);
|
|
const parser::AccCollapseArg &accCollapseArg = x.v;
|
|
const auto &collapseValue{
|
|
std::get<parser::ScalarIntConstantExpr>(accCollapseArg.t)};
|
|
RequiresConstantPositiveParameter(
|
|
llvm::acc::Clause::ACCC_collapse, collapseValue);
|
|
}
|
|
|
|
void AccStructureChecker::Enter(const parser::AccClause::Present &x) {
|
|
CheckAllowed(llvm::acc::Clause::ACCC_present);
|
|
CheckMultipleOccurrenceInDeclare(x.v, llvm::acc::Clause::ACCC_present);
|
|
}
|
|
|
|
void AccStructureChecker::Enter(const parser::AccClause::Copy &x) {
|
|
CheckAllowed(llvm::acc::Clause::ACCC_copy);
|
|
CheckMultipleOccurrenceInDeclare(x.v, llvm::acc::Clause::ACCC_copy);
|
|
}
|
|
|
|
void AccStructureChecker::Enter(const parser::AccClause::Deviceptr &x) {
|
|
CheckAllowed(llvm::acc::Clause::ACCC_deviceptr);
|
|
CheckMultipleOccurrenceInDeclare(x.v, llvm::acc::Clause::ACCC_deviceptr);
|
|
}
|
|
|
|
void AccStructureChecker::Enter(const parser::AccClause::DeviceResident &x) {
|
|
CheckAllowed(llvm::acc::Clause::ACCC_device_resident);
|
|
CheckMultipleOccurrenceInDeclare(
|
|
x.v, llvm::acc::Clause::ACCC_device_resident);
|
|
}
|
|
|
|
void AccStructureChecker::Enter(const parser::AccClause::Link &x) {
|
|
CheckAllowed(llvm::acc::Clause::ACCC_link);
|
|
CheckMultipleOccurrenceInDeclare(x.v, llvm::acc::Clause::ACCC_link);
|
|
}
|
|
|
|
void AccStructureChecker::Enter(const parser::AccClause::Shortloop &x) {
|
|
if (CheckAllowed(llvm::acc::Clause::ACCC_shortloop)) {
|
|
context_.Warn(common::UsageWarning::OpenAccUsage, GetContext().clauseSource,
|
|
"Non-standard shortloop clause ignored"_warn_en_US);
|
|
}
|
|
}
|
|
|
|
void AccStructureChecker::Enter(const parser::AccClause::If &x) {
|
|
CheckAllowed(llvm::acc::Clause::ACCC_if);
|
|
if (const auto *expr{GetExpr(x.v)}) {
|
|
if (auto type{expr->GetType()}) {
|
|
if (type->category() == TypeCategory::Integer ||
|
|
type->category() == TypeCategory::Logical) {
|
|
return; // LOGICAL and INTEGER type supported for the if clause.
|
|
}
|
|
}
|
|
}
|
|
context_.Say(
|
|
GetContext().clauseSource, "Must have LOGICAL or INTEGER type"_err_en_US);
|
|
}
|
|
|
|
void AccStructureChecker::Enter(const parser::OpenACCEndConstruct &x) {
|
|
context_.Warn(common::UsageWarning::OpenAccUsage, x.source,
|
|
"Misplaced OpenACC end directive"_warn_en_US);
|
|
}
|
|
|
|
void AccStructureChecker::Enter(const parser::Module &) {
|
|
declareSymbols.clear();
|
|
}
|
|
|
|
void AccStructureChecker::Enter(const parser::FunctionSubprogram &x) {
|
|
declareSymbols.clear();
|
|
}
|
|
|
|
void AccStructureChecker::Enter(const parser::SubroutineSubprogram &) {
|
|
declareSymbols.clear();
|
|
}
|
|
|
|
void AccStructureChecker::Enter(const parser::SeparateModuleSubprogram &) {
|
|
declareSymbols.clear();
|
|
}
|
|
|
|
void AccStructureChecker::Enter(const parser::DoConstruct &) {
|
|
++loopNestLevel;
|
|
}
|
|
|
|
void AccStructureChecker::Leave(const parser::DoConstruct &) {
|
|
--loopNestLevel;
|
|
}
|
|
|
|
llvm::StringRef AccStructureChecker::getDirectiveName(
|
|
llvm::acc::Directive directive) {
|
|
return llvm::acc::getOpenACCDirectiveName(directive);
|
|
}
|
|
|
|
llvm::StringRef AccStructureChecker::getClauseName(llvm::acc::Clause clause) {
|
|
return llvm::acc::getOpenACCClauseName(clause);
|
|
}
|
|
|
|
} // namespace Fortran::semantics
|