Mercurial > hg > CbC > CbC_llvm
view flang/lib/Semantics/check-acc-structure.cpp @ 207:2e18cbf3894f
LLVM12
author | Shinji KONO <kono@ie.u-ryukyu.ac.jp> |
---|---|
date | Tue, 08 Jun 2021 06:07:14 +0900 |
parents | |
children | 5f17cb93ff66 |
line wrap: on
line source
//===-- 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/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); \ } 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}; 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 - line 1249-1250 CheckRequireAtLeastOneOf(); 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)}; 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); 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::OpenACCCacheConstruct &x) { const auto &verbatim = std::get<parser::Verbatim>(x.t); PushContextAndClauseSets(verbatim.source, llvm::acc::Directive::ACCD_cache); SetContextDirectiveSource(verbatim.source); } void AccStructureChecker::Leave(const parser::OpenACCCacheConstruct &x) { dirContext_.pop_back(); } // Clause checkers CHECK_REQ_SCALAR_INT_CONSTANT_CLAUSE(Collapse, ACCC_collapse) CHECK_SIMPLE_CLAUSE(Auto, ACCC_auto) CHECK_SIMPLE_CLAUSE(Async, ACCC_async) CHECK_SIMPLE_CLAUSE(Attach, ACCC_attach) CHECK_SIMPLE_CLAUSE(Bind, ACCC_bind) CHECK_SIMPLE_CLAUSE(Capture, ACCC_capture) CHECK_SIMPLE_CLAUSE(Copy, ACCC_copy) 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(Deviceptr, ACCC_deviceptr) CHECK_SIMPLE_CLAUSE(DeviceResident, ACCC_device_resident) CHECK_SIMPLE_CLAUSE(DeviceType, ACCC_device_type) CHECK_SIMPLE_CLAUSE(Finalize, ACCC_finalize) CHECK_SIMPLE_CLAUSE(Firstprivate, ACCC_firstprivate) CHECK_SIMPLE_CLAUSE(Gang, ACCC_gang) CHECK_SIMPLE_CLAUSE(Host, ACCC_host) CHECK_SIMPLE_CLAUSE(If, ACCC_if) CHECK_SIMPLE_CLAUSE(IfPresent, ACCC_if_present) CHECK_SIMPLE_CLAUSE(Independent, ACCC_independent) CHECK_SIMPLE_CLAUSE(Link, ACCC_link) CHECK_SIMPLE_CLAUSE(NoCreate, ACCC_no_create) CHECK_SIMPLE_CLAUSE(Nohost, ACCC_nohost) CHECK_SIMPLE_CLAUSE(NumGangs, ACCC_num_gangs) CHECK_SIMPLE_CLAUSE(NumWorkers, ACCC_num_workers) CHECK_SIMPLE_CLAUSE(Present, ACCC_present) CHECK_SIMPLE_CLAUSE(Private, ACCC_private) CHECK_SIMPLE_CLAUSE(Read, ACCC_read) CHECK_SIMPLE_CLAUSE(Reduction, ACCC_reduction) CHECK_SIMPLE_CLAUSE(Seq, ACCC_seq) CHECK_SIMPLE_CLAUSE(Tile, ACCC_tile) CHECK_SIMPLE_CLAUSE(UseDevice, ACCC_use_device) CHECK_SIMPLE_CLAUSE(Vector, ACCC_vector) CHECK_SIMPLE_CLAUSE(VectorLength, ACCC_vector_length) CHECK_SIMPLE_CLAUSE(Wait, ACCC_wait) CHECK_SIMPLE_CLAUSE(Worker, ACCC_worker) CHECK_SIMPLE_CLAUSE(Write, ACCC_write) CHECK_SIMPLE_CLAUSE(Unknown, ACCC_unknown) 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()); } } } 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()); } } } 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()); } } } void AccStructureChecker::Enter(const parser::AccClause::Self &x) { CheckAllowed(llvm::acc::Clause::ACCC_self); const parser::AccSelfClause &accSelfClause = x.v; if (GetContext().directive == llvm::acc::Directive::ACCD_update && std::holds_alternative<std::optional<parser::ScalarLogicalExpr>>( accSelfClause.u)) { 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 && 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()); } } } llvm::StringRef AccStructureChecker::getClauseName(llvm::acc::Clause clause) { return llvm::acc::getOpenACCClauseName(clause); } llvm::StringRef AccStructureChecker::getDirectiveName( llvm::acc::Directive directive) { return llvm::acc::getOpenACCDirectiveName(directive); } } // namespace Fortran::semantics