diff --git a/flang/lib/Semantics/CMakeLists.txt b/flang/lib/Semantics/CMakeLists.txt --- a/flang/lib/Semantics/CMakeLists.txt +++ b/flang/lib/Semantics/CMakeLists.txt @@ -31,8 +31,9 @@ pointer-assignment.cpp program-tree.cpp resolve-labels.cpp - resolve-names.cpp + resolve-directives.cpp resolve-names-utils.cpp + resolve-names.cpp rewrite-parse-tree.cpp scope.cpp semantics.cpp diff --git a/flang/lib/Semantics/resolve-directives.h b/flang/lib/Semantics/resolve-directives.h new file mode 100644 --- /dev/null +++ b/flang/lib/Semantics/resolve-directives.h @@ -0,0 +1,26 @@ +//===----------------------------------------------------------------------===// +// +// 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 +// +//===----------------------------------------------------------------------===// + +#ifndef FORTRAN_SEMANTICS_RESOLVE_DIRECTIVES_H_ +#define FORTRAN_SEMANTICS_RESOLVE_DIRECTIVES_H_ + +namespace Fortran::parser { +struct Name; +struct ProgramUnit; +} // namespace Fortran::parser + +namespace Fortran::semantics { + +class SemanticsContext; + +// Name resolution for OpenACC and OpenMP directives +void ResolveAccParts(SemanticsContext &, const parser::ProgramUnit &); +void ResolveOmpParts(SemanticsContext &, const parser::ProgramUnit &); + +} // namespace Fortran::semantics +#endif diff --git a/flang/lib/Semantics/resolve-directives.cpp b/flang/lib/Semantics/resolve-directives.cpp new file mode 100644 --- /dev/null +++ b/flang/lib/Semantics/resolve-directives.cpp @@ -0,0 +1,1004 @@ +//===----------------------------------------------------------------------===// +// +// 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 "resolve-directives.h" + +#include "check-acc-structure.h" +#include "check-omp-structure.h" +#include "resolve-names-utils.h" +#include "flang/Common/idioms.h" +#include "flang/Evaluate/fold.h" +#include "flang/Parser/parse-tree-visitor.h" +#include "flang/Parser/parse-tree.h" +#include "flang/Parser/tools.h" +#include "flang/Semantics/expression.h" +#include +#include + +namespace Fortran::semantics { + +template class DirectiveAttributeVisitor { +public: + explicit DirectiveAttributeVisitor(SemanticsContext &context) + : context_{context} {} + + template bool Pre(const A &) { return true; } + template void Post(const A &) {} + +protected: + struct DirContext { + DirContext(const parser::CharBlock &source, T d, Scope &s) + : directiveSource{source}, directive{d}, scope{s} {} + parser::CharBlock directiveSource; + T directive; + Scope &scope; + Symbol::Flag defaultDSA{Symbol::Flag::AccShared}; // TODOACC + std::map objectWithDSA; + bool withinConstruct{false}; + std::int64_t associatedLoopLevel{0}; + }; + + DirContext &GetContext() { + CHECK(!dirContext_.empty()); + return dirContext_.back(); + } + void PushContext(const parser::CharBlock &source, T dir) { + dirContext_.emplace_back(source, dir, context_.FindScope(source)); + } + void PopContext() { dirContext_.pop_back(); } + void SetContextDirectiveSource(parser::CharBlock &dir) { + GetContext().directiveSource = dir; + } + Scope &currScope() { return GetContext().scope; } + void SetContextDefaultDSA(Symbol::Flag flag) { + GetContext().defaultDSA = flag; + } + void AddToContextObjectWithDSA( + const Symbol &symbol, Symbol::Flag flag, DirContext &context) { + context.objectWithDSA.emplace(&symbol, flag); + } + void AddToContextObjectWithDSA(const Symbol &symbol, Symbol::Flag flag) { + AddToContextObjectWithDSA(symbol, flag, GetContext()); + } + bool IsObjectWithDSA(const Symbol &symbol) { + auto it{GetContext().objectWithDSA.find(&symbol)}; + return it != GetContext().objectWithDSA.end(); + } + void SetContextAssociatedLoopLevel(std::int64_t level) { + GetContext().associatedLoopLevel = level; + } + Symbol &MakeAssocSymbol(const SourceName &name, Symbol &prev, Scope &scope) { + const auto pair{scope.try_emplace(name, Attrs{}, HostAssocDetails{prev})}; + return *pair.first->second; + } + Symbol &MakeAssocSymbol(const SourceName &name, Symbol &prev) { + return MakeAssocSymbol(name, prev, currScope()); + } + static const parser::Name *GetDesignatorNameIfDataRef( + const parser::Designator &designator) { + const auto *dataRef{std::get_if(&designator.u)}; + return dataRef ? std::get_if(&dataRef->u) : nullptr; + } + void AddDataSharingAttributeObject(SymbolRef object) { + dataSharingAttributeObjects_.insert(object); + } + void ClearDataSharingAttributeObjects() { + dataSharingAttributeObjects_.clear(); + } + bool HasDataSharingAttributeObject(const Symbol &); + const parser::Name &GetLoopIndex(const parser::DoConstruct &); + const parser::DoConstruct *GetDoConstructIf( + const parser::ExecutionPartConstruct &); + Symbol *DeclarePrivateAccessEntity( + const parser::Name &, Symbol::Flag, Scope &); + Symbol *DeclarePrivateAccessEntity(Symbol &, Symbol::Flag, Scope &); + Symbol *DeclareOrMarkOtherAccessEntity(const parser::Name &, Symbol::Flag); + + SymbolSet dataSharingAttributeObjects_; // on one directive + SemanticsContext &context_; + std::vector dirContext_; // used as a stack +}; + +class AccAttributeVisitor : DirectiveAttributeVisitor { +public: + explicit AccAttributeVisitor(SemanticsContext &context) + : DirectiveAttributeVisitor(context) {} + + template void Walk(const A &x) { parser::Walk(x, *this); } + template bool Pre(const A &) { return true; } + template void Post(const A &) {} + + bool Pre(const parser::SpecificationPart &x) { + Walk(std::get>(x.t)); + return false; + } + + bool Pre(const parser::OpenACCBlockConstruct &); + void Post(const parser::OpenACCBlockConstruct &) { PopContext(); } + bool Pre(const parser::OpenACCCombinedConstruct &); + void Post(const parser::OpenACCCombinedConstruct &) { PopContext(); } + + void Post(const parser::AccBeginBlockDirective &) { + GetContext().withinConstruct = true; + } + + bool Pre(const parser::OpenACCLoopConstruct &); + void Post(const parser::OpenACCLoopConstruct &) { PopContext(); } + void Post(const parser::AccLoopDirective &) { + GetContext().withinConstruct = true; + } + + bool Pre(const parser::OpenACCStandaloneConstruct &); + void Post(const parser::OpenACCStandaloneConstruct &) { PopContext(); } + void Post(const parser::AccStandaloneDirective &) { + GetContext().withinConstruct = true; + } + + void Post(const parser::AccDefaultClause &); + + bool Pre(const parser::AccClause::Copy &x) { + ResolveAccObjectList(x.v, Symbol::Flag::AccCopyIn); + ResolveAccObjectList(x.v, Symbol::Flag::AccCopyOut); + return false; + } + + bool Pre(const parser::AccClause::Create &x) { + const auto &objectList{std::get(x.v.t)}; + ResolveAccObjectList(objectList, Symbol::Flag::AccCreate); + return false; + } + + bool Pre(const parser::AccClause::Copyin &x) { + const auto &objectList{std::get(x.v.t)}; + ResolveAccObjectList(objectList, Symbol::Flag::AccCopyIn); + return false; + } + + bool Pre(const parser::AccClause::Copyout &x) { + const auto &objectList{std::get(x.v.t)}; + ResolveAccObjectList(objectList, Symbol::Flag::AccCopyOut); + return false; + } + + bool Pre(const parser::AccClause::Present &x) { + ResolveAccObjectList(x.v, Symbol::Flag::AccPresent); + return false; + } + bool Pre(const parser::AccClause::Private &x) { + ResolveAccObjectList(x.v, Symbol::Flag::AccPrivate); + return false; + } + bool Pre(const parser::AccClause::FirstPrivate &x) { + ResolveAccObjectList(x.v, Symbol::Flag::AccFirstPrivate); + return false; + } + + void Post(const parser::Name &); + +private: + std::int64_t GetAssociatedLoopLevelFromClauses(const parser::AccClauseList &); + + static constexpr Symbol::Flags dataSharingAttributeFlags{ + Symbol::Flag::AccShared, Symbol::Flag::AccPrivate, + Symbol::Flag::AccPresent, Symbol::Flag::AccFirstPrivate, + Symbol::Flag::AccReduction}; + + static constexpr Symbol::Flags dataMappingAttributeFlags{ + Symbol::Flag::AccCreate, Symbol::Flag::AccCopyIn, + Symbol::Flag::AccCopyOut, Symbol::Flag::AccDelete}; + + static constexpr Symbol::Flags accFlagsRequireNewSymbol{ + Symbol::Flag::AccPrivate, Symbol::Flag::AccFirstPrivate, + Symbol::Flag::AccReduction}; + + static constexpr Symbol::Flags accFlagsRequireMark{}; + + void PrivatizeAssociatedLoopIndex(const parser::OpenACCLoopConstruct &); + void ResolveAccObjectList(const parser::AccObjectList &, Symbol::Flag); + void ResolveAccObject(const parser::AccObject &, Symbol::Flag); + Symbol *ResolveAcc(const parser::Name &, Symbol::Flag, Scope &); + Symbol *ResolveAcc(Symbol &, Symbol::Flag, Scope &); + Symbol *ResolveAccCommonBlockName(const parser::Name *); + Symbol *DeclareOrMarkOtherAccessEntity(const parser::Name &, Symbol::Flag); + Symbol *DeclareOrMarkOtherAccessEntity(Symbol &, Symbol::Flag); + void CheckMultipleAppearances( + const parser::Name &, const Symbol &, Symbol::Flag); +}; + +// Data-sharing and Data-mapping attributes for data-refs in OpenMP construct +class OmpAttributeVisitor : DirectiveAttributeVisitor { +public: + explicit OmpAttributeVisitor(SemanticsContext &context) + : DirectiveAttributeVisitor(context) {} + + template void Walk(const A &x) { parser::Walk(x, *this); } + template bool Pre(const A &) { return true; } + template void Post(const A &) {} + + bool Pre(const parser::SpecificationPart &x) { + Walk(std::get>(x.t)); + return false; + } + + bool Pre(const parser::OpenMPBlockConstruct &); + void Post(const parser::OpenMPBlockConstruct &) { PopContext(); } + void Post(const parser::OmpBeginBlockDirective &) { + GetContext().withinConstruct = true; + } + + bool Pre(const parser::OpenMPLoopConstruct &); + void Post(const parser::OpenMPLoopConstruct &) { PopContext(); } + void Post(const parser::OmpBeginLoopDirective &) { + GetContext().withinConstruct = true; + } + bool Pre(const parser::DoConstruct &); + + bool Pre(const parser::OpenMPSectionsConstruct &); + void Post(const parser::OpenMPSectionsConstruct &) { PopContext(); } + + bool Pre(const parser::OpenMPThreadprivate &); + void Post(const parser::OpenMPThreadprivate &) { PopContext(); } + + // 2.15.3 Data-Sharing Attribute Clauses + void Post(const parser::OmpDefaultClause &); + bool Pre(const parser::OmpClause::Shared &x) { + ResolveOmpObjectList(x.v, Symbol::Flag::OmpShared); + return false; + } + bool Pre(const parser::OmpClause::Private &x) { + ResolveOmpObjectList(x.v, Symbol::Flag::OmpPrivate); + return false; + } + bool Pre(const parser::OmpClause::Firstprivate &x) { + ResolveOmpObjectList(x.v, Symbol::Flag::OmpFirstPrivate); + return false; + } + bool Pre(const parser::OmpClause::Lastprivate &x) { + ResolveOmpObjectList(x.v, Symbol::Flag::OmpLastPrivate); + return false; + } + + void Post(const parser::Name &); + +private: + std::int64_t GetAssociatedLoopLevelFromClauses(const parser::OmpClauseList &); + + static constexpr Symbol::Flags dataSharingAttributeFlags{ + Symbol::Flag::OmpShared, Symbol::Flag::OmpPrivate, + Symbol::Flag::OmpFirstPrivate, Symbol::Flag::OmpLastPrivate, + Symbol::Flag::OmpReduction, Symbol::Flag::OmpLinear}; + + static constexpr Symbol::Flags ompFlagsRequireNewSymbol{ + Symbol::Flag::OmpPrivate, Symbol::Flag::OmpLinear, + Symbol::Flag::OmpFirstPrivate, Symbol::Flag::OmpLastPrivate, + Symbol::Flag::OmpReduction}; + + static constexpr Symbol::Flags ompFlagsRequireMark{ + Symbol::Flag::OmpThreadprivate}; + + // Predetermined DSA rules + void PrivatizeAssociatedLoopIndex(const parser::OpenMPLoopConstruct &); + void ResolveSeqLoopIndexInParallelOrTaskConstruct(const parser::Name &); + + void ResolveOmpObjectList(const parser::OmpObjectList &, Symbol::Flag); + void ResolveOmpObject(const parser::OmpObject &, Symbol::Flag); + Symbol *ResolveOmp(const parser::Name &, Symbol::Flag, Scope &); + Symbol *ResolveOmp(Symbol &, Symbol::Flag, Scope &); + Symbol *ResolveOmpCommonBlockName(const parser::Name *); + Symbol *DeclareOrMarkOtherAccessEntity(const parser::Name &, Symbol::Flag); + Symbol *DeclareOrMarkOtherAccessEntity(Symbol &, Symbol::Flag); + void CheckMultipleAppearances( + const parser::Name &, const Symbol &, Symbol::Flag); +}; + +template +bool DirectiveAttributeVisitor::HasDataSharingAttributeObject( + const Symbol &object) { + auto it{dataSharingAttributeObjects_.find(object)}; + return it != dataSharingAttributeObjects_.end(); +} + +template +const parser::Name &DirectiveAttributeVisitor::GetLoopIndex( + const parser::DoConstruct &x) { + using Bounds = parser::LoopControl::Bounds; + return std::get(x.GetLoopControl()->u).name.thing; +} + +template +const parser::DoConstruct *DirectiveAttributeVisitor::GetDoConstructIf( + const parser::ExecutionPartConstruct &x) { + return parser::Unwrap(x); +} + +template +Symbol *DirectiveAttributeVisitor::DeclarePrivateAccessEntity( + const parser::Name &name, Symbol::Flag flag, Scope &scope) { + if (!name.symbol) { + return nullptr; // not resolved by Name Resolution step, do nothing + } + name.symbol = DeclarePrivateAccessEntity(*name.symbol, flag, scope); + return name.symbol; +} + +template +Symbol *DirectiveAttributeVisitor::DeclarePrivateAccessEntity( + Symbol &object, Symbol::Flag flag, Scope &scope) { + if (object.owner() != currScope()) { + auto &symbol{MakeAssocSymbol(object.name(), object, scope)}; + symbol.set(flag); + return &symbol; + } else { + object.set(flag); + return &object; + } +} + +bool AccAttributeVisitor::Pre(const parser::OpenACCBlockConstruct &x) { + const auto &beginBlockDir{std::get(x.t)}; + const auto &blockDir{std::get(beginBlockDir.t)}; + switch (blockDir.v) { + case llvm::acc::Directive::ACCD_data: + case llvm::acc::Directive::ACCD_host_data: + case llvm::acc::Directive::ACCD_kernels: + case llvm::acc::Directive::ACCD_parallel: + case llvm::acc::Directive::ACCD_serial: + PushContext(blockDir.source, blockDir.v); + break; + default: + break; + } + ClearDataSharingAttributeObjects(); + return true; +} + +bool AccAttributeVisitor::Pre(const parser::OpenACCLoopConstruct &x) { + const auto &beginDir{std::get(x.t)}; + const auto &loopDir{std::get(beginDir.t)}; + const auto &clauseList{std::get(beginDir.t)}; + if (loopDir.v == llvm::acc::Directive::ACCD_loop) { + PushContext(loopDir.source, loopDir.v); + } + ClearDataSharingAttributeObjects(); + SetContextAssociatedLoopLevel(GetAssociatedLoopLevelFromClauses(clauseList)); + PrivatizeAssociatedLoopIndex(x); + return true; +} + +bool AccAttributeVisitor::Pre(const parser::OpenACCStandaloneConstruct &x) { + const auto &standaloneDir{std::get(x.t)}; + switch (standaloneDir.v) { + case llvm::acc::Directive::ACCD_cache: + case llvm::acc::Directive::ACCD_enter_data: + case llvm::acc::Directive::ACCD_exit_data: + case llvm::acc::Directive::ACCD_init: + case llvm::acc::Directive::ACCD_set: + case llvm::acc::Directive::ACCD_shutdown: + case llvm::acc::Directive::ACCD_update: + PushContext(standaloneDir.source, standaloneDir.v); + break; + default: + break; + } + ClearDataSharingAttributeObjects(); + return true; +} + +bool AccAttributeVisitor::Pre(const parser::OpenACCCombinedConstruct &x) { + const auto &beginBlockDir{std::get(x.t)}; + const auto &combinedDir{ + std::get(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: + PushContext(combinedDir.source, combinedDir.v); + break; + default: + break; + } + ClearDataSharingAttributeObjects(); + return true; +} + +std::int64_t AccAttributeVisitor::GetAssociatedLoopLevelFromClauses( + const parser::AccClauseList &x) { + std::int64_t collapseLevel{0}; + for (const auto &clause : x.v) { + if (const auto *collapseClause{ + std::get_if(&clause.u)}) { + if (const auto v{EvaluateInt64(context_, collapseClause->v)}) { + collapseLevel = *v; + } + } + } + + if (collapseLevel) { + return collapseLevel; + } + return 1; // default is outermost loop +} + +void AccAttributeVisitor::PrivatizeAssociatedLoopIndex( + const parser::OpenACCLoopConstruct &x) { + std::int64_t level{GetContext().associatedLoopLevel}; + if (level <= 0) { // collpase value was negative or 0 + return; + } + Symbol::Flag ivDSA{Symbol::Flag::AccPrivate}; + + const auto &outer{std::get>(x.t)}; + for (const parser::DoConstruct *loop{&*outer}; loop && level > 0; --level) { + // go through all the nested do-loops and resolve index variables + const parser::Name &iv{GetLoopIndex(*loop)}; + if (auto *symbol{ResolveAcc(iv, ivDSA, currScope())}) { + symbol->set(Symbol::Flag::AccPreDetermined); + iv.symbol = symbol; // adjust the symbol within region + AddToContextObjectWithDSA(*symbol, ivDSA); + } + + const auto &block{std::get(loop->t)}; + const auto it{block.begin()}; + loop = it != block.end() ? GetDoConstructIf(*it) : nullptr; + } + CHECK(level == 0); +} + +void AccAttributeVisitor::Post(const parser::AccDefaultClause &x) { + if (!dirContext_.empty()) { + switch (x.v) { + case parser::AccDefaultClause::Arg::Present: + SetContextDefaultDSA(Symbol::Flag::AccPresent); + break; + case parser::AccDefaultClause::Arg::None: + SetContextDefaultDSA(Symbol::Flag::AccNone); + break; + } + } +} + +// For OpenACC constructs, check all the data-refs within the constructs +// and adjust the symbol for each Name if necessary +void AccAttributeVisitor::Post(const parser::Name &name) { + auto *symbol{name.symbol}; + if (symbol && !dirContext_.empty() && GetContext().withinConstruct) { + if (!symbol->owner().IsDerivedType() && !symbol->has() && + !IsObjectWithDSA(*symbol)) { + if (Symbol * found{currScope().FindSymbol(name.source)}) { + if (symbol != found) { + name.symbol = found; // adjust the symbol within region + } else if (GetContext().defaultDSA == Symbol::Flag::AccNone) { + // 2.5.14. + context_.Say(name.source, + "The DEFAULT(NONE) clause requires that '%s' must be listed in " + "a data-mapping clause"_err_en_US, + symbol->name()); + } + } + } + } // within OpenACC construct +} + +Symbol *AccAttributeVisitor::ResolveAccCommonBlockName( + const parser::Name *name) { + if (!name) { + return nullptr; + } else if (auto *prev{ + GetContext().scope.parent().FindCommonBlock(name->source)}) { + name->symbol = prev; + return prev; + } else { + return nullptr; + } +} + +void AccAttributeVisitor::ResolveAccObjectList( + const parser::AccObjectList &accObjectList, Symbol::Flag accFlag) { + for (const auto &accObject : accObjectList.v) { + ResolveAccObject(accObject, accFlag); + } +} + +void AccAttributeVisitor::ResolveAccObject( + const parser::AccObject &accObject, Symbol::Flag accFlag) { + std::visit( + common::visitors{ + [&](const parser::Designator &designator) { + if (const auto *name{GetDesignatorNameIfDataRef(designator)}) { + if (auto *symbol{ResolveAcc(*name, accFlag, currScope())}) { + AddToContextObjectWithDSA(*symbol, accFlag); + if (dataSharingAttributeFlags.test(accFlag)) { + CheckMultipleAppearances(*name, *symbol, accFlag); + } + } + } else { + // Array sections to be changed to substrings as needed + if (AnalyzeExpr(context_, designator)) { + if (std::holds_alternative(designator.u)) { + context_.Say(designator.source, + "Substrings are not allowed on OpenACC " + "directives or clauses"_err_en_US); + } + } + // other checks, more TBD + } + }, + [&](const parser::Name &name) { // common block + if (auto *symbol{ResolveAccCommonBlockName(&name)}) { + CheckMultipleAppearances( + name, *symbol, Symbol::Flag::AccCommonBlock); + for (auto &object : symbol->get().objects()) { + if (auto *resolvedObject{ + ResolveAcc(*object, accFlag, currScope())}) { + AddToContextObjectWithDSA(*resolvedObject, accFlag); + } + } + } else { + context_.Say(name.source, + "COMMON block must be declared in the same scoping unit " + "in which the OpenACC directive or clause appears"_err_en_US); + } + }, + }, + accObject.u); +} + +Symbol *AccAttributeVisitor::ResolveAcc( + const parser::Name &name, Symbol::Flag accFlag, Scope &scope) { + if (accFlagsRequireNewSymbol.test(accFlag)) { + return DeclarePrivateAccessEntity(name, accFlag, scope); + } else { + return DeclareOrMarkOtherAccessEntity(name, accFlag); + } +} + +Symbol *AccAttributeVisitor::ResolveAcc( + Symbol &symbol, Symbol::Flag accFlag, Scope &scope) { + if (accFlagsRequireNewSymbol.test(accFlag)) { + return DeclarePrivateAccessEntity(symbol, accFlag, scope); + } else { + return DeclareOrMarkOtherAccessEntity(symbol, accFlag); + } +} + +Symbol *AccAttributeVisitor::DeclareOrMarkOtherAccessEntity( + const parser::Name &name, Symbol::Flag accFlag) { + Symbol *prev{currScope().FindSymbol(name.source)}; + if (!name.symbol || !prev) { + return nullptr; + } else if (prev != name.symbol) { + name.symbol = prev; + } + return DeclareOrMarkOtherAccessEntity(*prev, accFlag); +} + +Symbol *AccAttributeVisitor::DeclareOrMarkOtherAccessEntity( + Symbol &object, Symbol::Flag accFlag) { + if (accFlagsRequireMark.test(accFlag)) { + object.set(accFlag); + } + return &object; +} + +static bool WithMultipleAppearancesAccException( + const Symbol &symbol, Symbol::Flag flag) { + return false; // Place holder +} + +void AccAttributeVisitor::CheckMultipleAppearances( + const parser::Name &name, const Symbol &symbol, Symbol::Flag accFlag) { + const auto *target{&symbol}; + if (accFlagsRequireNewSymbol.test(accFlag)) { + if (const auto *details{symbol.detailsIf()}) { + target = &details->symbol(); + } + } + if (HasDataSharingAttributeObject(*target) && + !WithMultipleAppearancesAccException(symbol, accFlag)) { + context_.Say(name.source, + "'%s' appears in more than one data-sharing clause " + "on the same OpenACC directive"_err_en_US, + name.ToString()); + } else { + AddDataSharingAttributeObject(*target); + } +} + +bool OmpAttributeVisitor::Pre(const parser::OpenMPBlockConstruct &x) { + const auto &beginBlockDir{std::get(x.t)}; + const auto &beginDir{std::get(beginBlockDir.t)}; + switch (beginDir.v) { + case llvm::omp::Directive::OMPD_master: + case llvm::omp::Directive::OMPD_ordered: + case llvm::omp::Directive::OMPD_parallel: + case llvm::omp::Directive::OMPD_single: + case llvm::omp::Directive::OMPD_target: + case llvm::omp::Directive::OMPD_target_data: + case llvm::omp::Directive::OMPD_task: + case llvm::omp::Directive::OMPD_teams: + case llvm::omp::Directive::OMPD_workshare: + case llvm::omp::Directive::OMPD_parallel_workshare: + case llvm::omp::Directive::OMPD_target_teams: + case llvm::omp::Directive::OMPD_target_parallel: + PushContext(beginDir.source, beginDir.v); + break; + default: + // TODO others + break; + } + ClearDataSharingAttributeObjects(); + return true; +} + +bool OmpAttributeVisitor::Pre(const parser::OpenMPLoopConstruct &x) { + const auto &beginLoopDir{std::get(x.t)}; + const auto &beginDir{std::get(beginLoopDir.t)}; + const auto &clauseList{std::get(beginLoopDir.t)}; + switch (beginDir.v) { + case llvm::omp::Directive::OMPD_distribute: + case llvm::omp::Directive::OMPD_distribute_parallel_do: + case llvm::omp::Directive::OMPD_distribute_parallel_do_simd: + case llvm::omp::Directive::OMPD_distribute_simd: + case llvm::omp::Directive::OMPD_do: + case llvm::omp::Directive::OMPD_do_simd: + case llvm::omp::Directive::OMPD_parallel_do: + case llvm::omp::Directive::OMPD_parallel_do_simd: + case llvm::omp::Directive::OMPD_simd: + case llvm::omp::Directive::OMPD_target_parallel_do: + case llvm::omp::Directive::OMPD_target_parallel_do_simd: + case llvm::omp::Directive::OMPD_target_teams_distribute: + case llvm::omp::Directive::OMPD_target_teams_distribute_parallel_do: + case llvm::omp::Directive::OMPD_target_teams_distribute_parallel_do_simd: + case llvm::omp::Directive::OMPD_target_teams_distribute_simd: + case llvm::omp::Directive::OMPD_target_simd: + case llvm::omp::Directive::OMPD_taskloop: + case llvm::omp::Directive::OMPD_taskloop_simd: + case llvm::omp::Directive::OMPD_teams_distribute: + case llvm::omp::Directive::OMPD_teams_distribute_parallel_do: + case llvm::omp::Directive::OMPD_teams_distribute_parallel_do_simd: + case llvm::omp::Directive::OMPD_teams_distribute_simd: + PushContext(beginDir.source, beginDir.v); + break; + default: + break; + } + ClearDataSharingAttributeObjects(); + SetContextAssociatedLoopLevel(GetAssociatedLoopLevelFromClauses(clauseList)); + PrivatizeAssociatedLoopIndex(x); + return true; +} + +void OmpAttributeVisitor::ResolveSeqLoopIndexInParallelOrTaskConstruct( + const parser::Name &iv) { + auto targetIt{dirContext_.rbegin()}; + for (;; ++targetIt) { + if (targetIt == dirContext_.rend()) { + return; + } + if (llvm::omp::parallelSet.test(targetIt->directive) || + llvm::omp::taskGeneratingSet.test(targetIt->directive)) { + break; + } + } + if (auto *symbol{ResolveOmp(iv, Symbol::Flag::OmpPrivate, targetIt->scope)}) { + targetIt++; + symbol->set(Symbol::Flag::OmpPreDetermined); + iv.symbol = symbol; // adjust the symbol within region + for (auto it{dirContext_.rbegin()}; it != targetIt; ++it) { + AddToContextObjectWithDSA(*symbol, Symbol::Flag::OmpPrivate, *it); + } + } +} + +// 2.15.1.1 Data-sharing Attribute Rules - Predetermined +// - A loop iteration variable for a sequential loop in a parallel +// or task generating construct is private in the innermost such +// construct that encloses the loop +bool OmpAttributeVisitor::Pre(const parser::DoConstruct &x) { + if (!dirContext_.empty() && GetContext().withinConstruct) { + if (const auto &iv{GetLoopIndex(x)}; iv.symbol) { + if (!iv.symbol->test(Symbol::Flag::OmpPreDetermined)) { + ResolveSeqLoopIndexInParallelOrTaskConstruct(iv); + } else { + // TODO: conflict checks with explicitly determined DSA + } + } + } + return true; +} + +std::int64_t OmpAttributeVisitor::GetAssociatedLoopLevelFromClauses( + const parser::OmpClauseList &x) { + std::int64_t orderedLevel{0}; + std::int64_t collapseLevel{0}; + for (const auto &clause : x.v) { + if (const auto *orderedClause{ + std::get_if(&clause.u)}) { + if (const auto v{EvaluateInt64(context_, orderedClause->v)}) { + orderedLevel = *v; + } + } + if (const auto *collapseClause{ + std::get_if(&clause.u)}) { + if (const auto v{EvaluateInt64(context_, collapseClause->v)}) { + collapseLevel = *v; + } + } + } + + if (orderedLevel && (!collapseLevel || orderedLevel >= collapseLevel)) { + return orderedLevel; + } else if (!orderedLevel && collapseLevel) { + return collapseLevel; + } // orderedLevel < collapseLevel is an error handled in structural checks + return 1; // default is outermost loop +} + +// 2.15.1.1 Data-sharing Attribute Rules - Predetermined +// - The loop iteration variable(s) in the associated do-loop(s) of a do, +// parallel do, taskloop, or distribute construct is (are) private. +// - The loop iteration variable in the associated do-loop of a simd construct +// with just one associated do-loop is linear with a linear-step that is the +// increment of the associated do-loop. +// - The loop iteration variables in the associated do-loops of a simd +// construct with multiple associated do-loops are lastprivate. +// +// TODO: revisit after semantics checks are completed for do-loop association of +// collapse and ordered +void OmpAttributeVisitor::PrivatizeAssociatedLoopIndex( + const parser::OpenMPLoopConstruct &x) { + std::int64_t level{GetContext().associatedLoopLevel}; + if (level <= 0) { + return; + } + Symbol::Flag ivDSA; + if (!llvm::omp::simdSet.test(GetContext().directive)) { + ivDSA = Symbol::Flag::OmpPrivate; + } else if (level == 1) { + ivDSA = Symbol::Flag::OmpLinear; + } else { + ivDSA = Symbol::Flag::OmpLastPrivate; + } + + const auto &outer{std::get>(x.t)}; + for (const parser::DoConstruct *loop{&*outer}; loop && level > 0; --level) { + // go through all the nested do-loops and resolve index variables + const parser::Name &iv{GetLoopIndex(*loop)}; + if (auto *symbol{ResolveOmp(iv, ivDSA, currScope())}) { + symbol->set(Symbol::Flag::OmpPreDetermined); + iv.symbol = symbol; // adjust the symbol within region + AddToContextObjectWithDSA(*symbol, ivDSA); + } + + const auto &block{std::get(loop->t)}; + const auto it{block.begin()}; + loop = it != block.end() ? GetDoConstructIf(*it) : nullptr; + } + CHECK(level == 0); +} + +bool OmpAttributeVisitor::Pre(const parser::OpenMPSectionsConstruct &x) { + const auto &beginSectionsDir{ + std::get(x.t)}; + const auto &beginDir{ + std::get(beginSectionsDir.t)}; + switch (beginDir.v) { + case llvm::omp::Directive::OMPD_parallel_sections: + case llvm::omp::Directive::OMPD_sections: + PushContext(beginDir.source, beginDir.v); + break; + default: + break; + } + ClearDataSharingAttributeObjects(); + return true; +} + +bool OmpAttributeVisitor::Pre(const parser::OpenMPThreadprivate &x) { + PushContext(x.source, llvm::omp::Directive::OMPD_threadprivate); + const auto &list{std::get(x.t)}; + ResolveOmpObjectList(list, Symbol::Flag::OmpThreadprivate); + return false; +} + +void OmpAttributeVisitor::Post(const parser::OmpDefaultClause &x) { + if (!dirContext_.empty()) { + switch (x.v) { + case parser::OmpDefaultClause::Type::Private: + SetContextDefaultDSA(Symbol::Flag::OmpPrivate); + break; + case parser::OmpDefaultClause::Type::Firstprivate: + SetContextDefaultDSA(Symbol::Flag::OmpFirstPrivate); + break; + case parser::OmpDefaultClause::Type::Shared: + SetContextDefaultDSA(Symbol::Flag::OmpShared); + break; + case parser::OmpDefaultClause::Type::None: + SetContextDefaultDSA(Symbol::Flag::OmpNone); + break; + } + } +} + +// For OpenMP constructs, check all the data-refs within the constructs +// and adjust the symbol for each Name if necessary +void OmpAttributeVisitor::Post(const parser::Name &name) { + auto *symbol{name.symbol}; + if (symbol && !dirContext_.empty() && GetContext().withinConstruct) { + if (!symbol->owner().IsDerivedType() && !symbol->has() && + !IsObjectWithDSA(*symbol)) { + // TODO: create a separate function to go through the rules for + // predetermined, explicitly determined, and implicitly + // determined data-sharing attributes (2.15.1.1). + if (Symbol * found{currScope().FindSymbol(name.source)}) { + if (symbol != found) { + name.symbol = found; // adjust the symbol within region + } else if (GetContext().defaultDSA == Symbol::Flag::OmpNone) { + context_.Say(name.source, + "The DEFAULT(NONE) clause requires that '%s' must be listed in " + "a data-sharing attribute clause"_err_en_US, + symbol->name()); + } + } + } + } // within OpenMP construct +} + +Symbol *OmpAttributeVisitor::ResolveOmpCommonBlockName( + const parser::Name *name) { + if (auto *prev{name + ? GetContext().scope.parent().FindCommonBlock(name->source) + : nullptr}) { + name->symbol = prev; + return prev; + } else { + return nullptr; + } +} + +void OmpAttributeVisitor::ResolveOmpObjectList( + const parser::OmpObjectList &ompObjectList, Symbol::Flag ompFlag) { + for (const auto &ompObject : ompObjectList.v) { + ResolveOmpObject(ompObject, ompFlag); + } +} + +void OmpAttributeVisitor::ResolveOmpObject( + const parser::OmpObject &ompObject, Symbol::Flag ompFlag) { + std::visit( + common::visitors{ + [&](const parser::Designator &designator) { + if (const auto *name{GetDesignatorNameIfDataRef(designator)}) { + if (auto *symbol{ResolveOmp(*name, ompFlag, currScope())}) { + AddToContextObjectWithDSA(*symbol, ompFlag); + if (dataSharingAttributeFlags.test(ompFlag)) { + CheckMultipleAppearances(*name, *symbol, ompFlag); + } + } + } else { + // Array sections to be changed to substrings as needed + if (AnalyzeExpr(context_, designator)) { + if (std::holds_alternative(designator.u)) { + context_.Say(designator.source, + "Substrings are not allowed on OpenMP " + "directives or clauses"_err_en_US); + } + } + // other checks, more TBD + } + }, + [&](const parser::Name &name) { // common block + if (auto *symbol{ResolveOmpCommonBlockName(&name)}) { + CheckMultipleAppearances( + name, *symbol, Symbol::Flag::OmpCommonBlock); + // 2.15.3 When a named common block appears in a list, it has the + // same meaning as if every explicit member of the common block + // appeared in the list + for (auto &object : symbol->get().objects()) { + if (auto *resolvedObject{ + ResolveOmp(*object, ompFlag, currScope())}) { + AddToContextObjectWithDSA(*resolvedObject, ompFlag); + } + } + } else { + context_.Say(name.source, // 2.15.3 + "COMMON block must be declared in the same scoping unit " + "in which the OpenMP directive or clause appears"_err_en_US); + } + }, + }, + ompObject.u); +} + +Symbol *OmpAttributeVisitor::ResolveOmp( + const parser::Name &name, Symbol::Flag ompFlag, Scope &scope) { + if (ompFlagsRequireNewSymbol.test(ompFlag)) { + return DeclarePrivateAccessEntity(name, ompFlag, scope); + } else { + return DeclareOrMarkOtherAccessEntity(name, ompFlag); + } +} + +Symbol *OmpAttributeVisitor::ResolveOmp( + Symbol &symbol, Symbol::Flag ompFlag, Scope &scope) { + if (ompFlagsRequireNewSymbol.test(ompFlag)) { + return DeclarePrivateAccessEntity(symbol, ompFlag, scope); + } else { + return DeclareOrMarkOtherAccessEntity(symbol, ompFlag); + } +} + +Symbol *OmpAttributeVisitor::DeclareOrMarkOtherAccessEntity( + const parser::Name &name, Symbol::Flag ompFlag) { + Symbol *prev{currScope().FindSymbol(name.source)}; + if (!name.symbol || !prev) { + return nullptr; + } else if (prev != name.symbol) { + name.symbol = prev; + } + return DeclareOrMarkOtherAccessEntity(*prev, ompFlag); +} + +Symbol *OmpAttributeVisitor::DeclareOrMarkOtherAccessEntity( + Symbol &object, Symbol::Flag ompFlag) { + if (ompFlagsRequireMark.test(ompFlag)) { + object.set(ompFlag); + } + return &object; +} + +static bool WithMultipleAppearancesOmpException( + const Symbol &symbol, Symbol::Flag flag) { + return (flag == Symbol::Flag::OmpFirstPrivate && + symbol.test(Symbol::Flag::OmpLastPrivate)) || + (flag == Symbol::Flag::OmpLastPrivate && + symbol.test(Symbol::Flag::OmpFirstPrivate)); +} + +void OmpAttributeVisitor::CheckMultipleAppearances( + const parser::Name &name, const Symbol &symbol, Symbol::Flag ompFlag) { + const auto *target{&symbol}; + if (ompFlagsRequireNewSymbol.test(ompFlag)) { + if (const auto *details{symbol.detailsIf()}) { + target = &details->symbol(); + } + } + if (HasDataSharingAttributeObject(*target) && + !WithMultipleAppearancesOmpException(symbol, ompFlag)) { + context_.Say(name.source, + "'%s' appears in more than one data-sharing clause " + "on the same OpenMP directive"_err_en_US, + name.ToString()); + } else { + AddDataSharingAttributeObject(*target); + } +} + +void ResolveAccParts( + SemanticsContext &context, const parser::ProgramUnit &node) { + if (context.IsEnabled(common::LanguageFeature::OpenACC)) { + AccAttributeVisitor{context}.Walk(node); + } +} + +void ResolveOmpParts( + SemanticsContext &context, const parser::ProgramUnit &node) { + if (context.IsEnabled(common::LanguageFeature::OpenMP)) { + OmpAttributeVisitor{context}.Walk(node); + if (!context.AnyFatalError()) { + // The data-sharing attribute of the loop iteration variable for a + // sequential loop (2.15.1.1) can only be determined when visiting + // the corresponding DoConstruct, a second walk is to adjust the + // symbols for all the data-refs of that loop iteration variable + // prior to the DoConstruct. + OmpAttributeVisitor{context}.Walk(node); + } + } +} + +} // namespace Fortran::semantics diff --git a/flang/lib/Semantics/resolve-names-utils.h b/flang/lib/Semantics/resolve-names-utils.h --- a/flang/lib/Semantics/resolve-names-utils.h +++ b/flang/lib/Semantics/resolve-names-utils.h @@ -11,8 +11,12 @@ // Utility functions and class for use in resolve-names.cpp. +#include "flang/Evaluate/fold.h" #include "flang/Parser/message.h" +#include "flang/Parser/tools.h" +#include "flang/Semantics/expression.h" #include "flang/Semantics/scope.h" +#include "flang/Semantics/semantics.h" #include "flang/Semantics/symbol.h" #include "flang/Semantics/type.h" #include @@ -48,6 +52,23 @@ bool IsIntrinsicOperator(const SemanticsContext &, const SourceName &); bool IsLogicalConstant(const SemanticsContext &, const SourceName &); +template +MaybeIntExpr EvaluateIntExpr(SemanticsContext &context, const T &expr) { + if (MaybeExpr maybeExpr{ + Fold(context.foldingContext(), AnalyzeExpr(context, expr))}) { + if (auto *intExpr{evaluate::UnwrapExpr(*maybeExpr)}) { + return std::move(*intExpr); + } + } + return std::nullopt; +} + +template +std::optional EvaluateInt64( + SemanticsContext &context, const T &expr) { + return evaluate::ToInt64(EvaluateIntExpr(context, expr)); +} + // Analyze a generic-spec and generate a symbol name and GenericKind for it. class GenericSpecInfo { public: diff --git a/flang/lib/Semantics/resolve-names.cpp b/flang/lib/Semantics/resolve-names.cpp --- a/flang/lib/Semantics/resolve-names.cpp +++ b/flang/lib/Semantics/resolve-names.cpp @@ -7,11 +7,10 @@ #include "resolve-names.h" #include "assignment.h" -#include "check-acc-structure.h" -#include "check-omp-structure.h" #include "mod-file.h" #include "pointer-assignment.h" #include "program-tree.h" +#include "resolve-directives.h" #include "resolve-names-utils.h" #include "rewrite-parse-tree.h" #include "flang/Common/Fortran.h" @@ -198,12 +197,7 @@ } template MaybeIntExpr EvaluateIntExpr(const T &expr) { - if (MaybeExpr maybeExpr{EvaluateExpr(expr)}) { - if (auto *intExpr{evaluate::UnwrapExpr(*maybeExpr)}) { - return std::move(*intExpr); - } - } - return std::nullopt; + return semantics::EvaluateIntExpr(*context_, expr); } template @@ -846,7 +840,6 @@ const parser::Name *ResolveStructureComponent( const parser::StructureComponent &); const parser::Name *ResolveDataRef(const parser::DataRef &); - const parser::Name *ResolveVariable(const parser::Variable &); const parser::Name *ResolveName(const parser::Name &); bool PassesSharedLocalityChecks(const parser::Name &name, Symbol &symbol); Symbol *NoteInterfaceName(const parser::Name &); @@ -1082,133 +1075,6 @@ void PopAssociation(); }; -template class DirectiveAttributeVisitor { -public: - explicit DirectiveAttributeVisitor( - SemanticsContext &context, ResolveNamesVisitor &resolver) - : context_{context}, resolver_{resolver} {} - -protected: - struct DirContext { - DirContext(const parser::CharBlock &source, T d, Scope &s) - : directiveSource{source}, directive{d}, scope{s} {} - parser::CharBlock directiveSource; - T directive; - Scope &scope; - Symbol::Flag defaultDSA{Symbol::Flag::AccShared}; // TODOACC - std::map objectWithDSA; - bool withinConstruct{false}; - int64_t associatedLoopLevel{0}; - }; - - DirContext &GetContext() { - CHECK(!dirContext_.empty()); - return dirContext_.back(); - } - void PushContext(const parser::CharBlock &source, T dir) { - dirContext_.emplace_back(source, dir, context_.FindScope(source)); - } - void PopContext() { dirContext_.pop_back(); } - void SetContextDirectiveSource(parser::CharBlock &dir) { - GetContext().directiveSource = dir; - } - void SetContextDirectiveEnum(T dir) { GetContext().directive = dir; } - Scope &currScope() { return GetContext().scope; } - void SetContextDefaultDSA(Symbol::Flag flag) { - GetContext().defaultDSA = flag; - } - void AddToContextObjectWithDSA( - const Symbol &symbol, Symbol::Flag flag, DirContext &context) { - context.objectWithDSA.emplace(&symbol, flag); - } - void AddToContextObjectWithDSA(const Symbol &symbol, Symbol::Flag flag) { - AddToContextObjectWithDSA(symbol, flag, GetContext()); - } - bool IsObjectWithDSA(const Symbol &symbol) { - auto it{GetContext().objectWithDSA.find(&symbol)}; - return it != GetContext().objectWithDSA.end(); - } - void SetContextAssociatedLoopLevel(int64_t level) { - GetContext().associatedLoopLevel = level; - } - Symbol &MakeAssocSymbol(const SourceName &name, Symbol &prev, Scope &scope) { - const auto pair{scope.try_emplace(name, Attrs{}, HostAssocDetails{prev})}; - return *pair.first->second; - } - Symbol &MakeAssocSymbol(const SourceName &name, Symbol &prev) { - return MakeAssocSymbol(name, prev, currScope()); - } - static const parser::Name *GetDesignatorNameIfDataRef( - const parser::Designator &designator) { - const auto *dataRef{std::get_if(&designator.u)}; - return dataRef ? std::get_if(&dataRef->u) : nullptr; - } - void AddDataSharingAttributeObject(SymbolRef object) { - dataSharingAttributeObjects_.insert(object); - } - void ClearDataSharingAttributeObjects() { - dataSharingAttributeObjects_.clear(); - } - bool HasDataSharingAttributeObject(const Symbol &); - const parser::Name &GetLoopIndex(const parser::DoConstruct &); - const parser::DoConstruct *GetDoConstructIf( - const parser::ExecutionPartConstruct &); - Symbol *DeclarePrivateAccessEntity( - const parser::Name &, Symbol::Flag, Scope &); - Symbol *DeclarePrivateAccessEntity(Symbol &, Symbol::Flag, Scope &); - Symbol *DeclareOrMarkOtherAccessEntity(const parser::Name &, Symbol::Flag); - - SymbolSet dataSharingAttributeObjects_; // on one directive - SemanticsContext &context_; - ResolveNamesVisitor &resolver_; - std::vector dirContext_; // used as a stack -}; - -template -bool DirectiveAttributeVisitor::HasDataSharingAttributeObject( - const Symbol &object) { - auto it{dataSharingAttributeObjects_.find(object)}; - return it != dataSharingAttributeObjects_.end(); -} - -template -const parser::Name &DirectiveAttributeVisitor::GetLoopIndex( - const parser::DoConstruct &x) { - auto &loopControl{x.GetLoopControl().value()}; - using Bounds = parser::LoopControl::Bounds; - const Bounds &bounds{std::get(loopControl.u)}; - return bounds.name.thing; -} - -template -const parser::DoConstruct *DirectiveAttributeVisitor::GetDoConstructIf( - const parser::ExecutionPartConstruct &x) { - return parser::Unwrap(x); -} - -template -Symbol *DirectiveAttributeVisitor::DeclarePrivateAccessEntity( - const parser::Name &name, Symbol::Flag flag, Scope &scope) { - if (!name.symbol) { - return nullptr; // not resolved by Name Resolution step, do nothing - } - name.symbol = DeclarePrivateAccessEntity(*name.symbol, flag, scope); - return name.symbol; -} - -template -Symbol *DirectiveAttributeVisitor::DeclarePrivateAccessEntity( - Symbol &object, Symbol::Flag flag, Scope &scope) { - if (object.owner() != currScope()) { - auto &symbol{MakeAssocSymbol(object.name(), object, scope)}; - symbol.set(flag); - return &symbol; - } else { - object.set(flag); - return &object; - } -} - // Create scopes for OpenACC constructs class AccVisitor : public virtual DeclarationVisitor { public: @@ -1274,113 +1140,6 @@ } } -class AccAttributeVisitor : DirectiveAttributeVisitor { -public: - explicit AccAttributeVisitor( - SemanticsContext &context, ResolveNamesVisitor &resolver) - : DirectiveAttributeVisitor(context, resolver) {} - - template void Walk(const A &x) { parser::Walk(x, *this); } - template bool Pre(const A &) { return true; } - template void Post(const A &) {} - - bool Pre(const parser::SpecificationPart &x) { - Walk(std::get>(x.t)); - return false; - } - - bool Pre(const parser::OpenACCBlockConstruct &); - void Post(const parser::OpenACCBlockConstruct &) { PopContext(); } - bool Pre(const parser::OpenACCCombinedConstruct &); - void Post(const parser::OpenACCCombinedConstruct &) { PopContext(); } - - void Post(const parser::AccBeginBlockDirective &) { - GetContext().withinConstruct = true; - } - - bool Pre(const parser::OpenACCLoopConstruct &); - void Post(const parser::OpenACCLoopConstruct &) { PopContext(); } - void Post(const parser::AccLoopDirective &) { - GetContext().withinConstruct = true; - } - - bool Pre(const parser::OpenACCStandaloneConstruct &); - void Post(const parser::OpenACCStandaloneConstruct &) { PopContext(); } - void Post(const parser::AccStandaloneDirective &) { - GetContext().withinConstruct = true; - } - - void Post(const parser::AccDefaultClause &); - - bool Pre(const parser::AccClause::Copy &x) { - ResolveAccObjectList(x.v, Symbol::Flag::AccCopyIn); - ResolveAccObjectList(x.v, Symbol::Flag::AccCopyOut); - return false; - } - - bool Pre(const parser::AccClause::Create &x) { - const auto &objectList{std::get(x.v.t)}; - ResolveAccObjectList(objectList, Symbol::Flag::AccCreate); - return false; - } - - bool Pre(const parser::AccClause::Copyin &x) { - const auto &objectList{std::get(x.v.t)}; - ResolveAccObjectList(objectList, Symbol::Flag::AccCopyIn); - return false; - } - - bool Pre(const parser::AccClause::Copyout &x) { - const auto &objectList{std::get(x.v.t)}; - ResolveAccObjectList(objectList, Symbol::Flag::AccCopyOut); - return false; - } - - bool Pre(const parser::AccClause::Present &x) { - ResolveAccObjectList(x.v, Symbol::Flag::AccPresent); - return false; - } - bool Pre(const parser::AccClause::Private &x) { - ResolveAccObjectList(x.v, Symbol::Flag::AccPrivate); - return false; - } - bool Pre(const parser::AccClause::FirstPrivate &x) { - ResolveAccObjectList(x.v, Symbol::Flag::AccFirstPrivate); - return false; - } - - void Post(const parser::Name &); - -private: - int64_t GetAssociatedLoopLevelFromClauses(const parser::AccClauseList &); - - static constexpr Symbol::Flags dataSharingAttributeFlags{ - Symbol::Flag::AccShared, Symbol::Flag::AccPrivate, - Symbol::Flag::AccPresent, Symbol::Flag::AccFirstPrivate, - Symbol::Flag::AccReduction}; - - static constexpr Symbol::Flags dataMappingAttributeFlags{ - Symbol::Flag::AccCreate, Symbol::Flag::AccCopyIn, - Symbol::Flag::AccCopyOut, Symbol::Flag::AccDelete}; - - static constexpr Symbol::Flags accFlagsRequireNewSymbol{ - Symbol::Flag::AccPrivate, Symbol::Flag::AccFirstPrivate, - Symbol::Flag::AccReduction}; - - static constexpr Symbol::Flags accFlagsRequireMark{}; - - void PrivatizeAssociatedLoopIndex(const parser::OpenACCLoopConstruct &); - void ResolveAccObjectList(const parser::AccObjectList &, Symbol::Flag); - void ResolveAccObject(const parser::AccObject &, Symbol::Flag); - Symbol *ResolveAcc(const parser::Name &, Symbol::Flag, Scope &); - Symbol *ResolveAcc(Symbol &, Symbol::Flag, Scope &); - Symbol *ResolveAccCommonBlockName(const parser::Name *); - Symbol *DeclareOrMarkOtherAccessEntity(const parser::Name &, Symbol::Flag); - Symbol *DeclareOrMarkOtherAccessEntity(Symbol &, Symbol::Flag); - void CheckMultipleAppearances( - const parser::Name &, const Symbol &, Symbol::Flag); -}; - // Create scopes for OpenMP constructs class OmpVisitor : public virtual DeclarationVisitor { public: @@ -1477,94 +1236,6 @@ } } -// Data-sharing and Data-mapping attributes for data-refs in OpenMP construct -class OmpAttributeVisitor : DirectiveAttributeVisitor { -public: - explicit OmpAttributeVisitor( - SemanticsContext &context, ResolveNamesVisitor &resolver) - : DirectiveAttributeVisitor(context, resolver) {} - - template void Walk(const A &x) { parser::Walk(x, *this); } - - template bool Pre(const A &) { return true; } - template void Post(const A &) {} - - bool Pre(const parser::SpecificationPart &x) { - Walk(std::get>(x.t)); - return false; - } - - bool Pre(const parser::OpenMPBlockConstruct &); - void Post(const parser::OpenMPBlockConstruct &) { PopContext(); } - void Post(const parser::OmpBeginBlockDirective &) { - GetContext().withinConstruct = true; - } - - bool Pre(const parser::OpenMPLoopConstruct &); - void Post(const parser::OpenMPLoopConstruct &) { PopContext(); } - void Post(const parser::OmpBeginLoopDirective &) { - GetContext().withinConstruct = true; - } - bool Pre(const parser::DoConstruct &); - - bool Pre(const parser::OpenMPSectionsConstruct &); - void Post(const parser::OpenMPSectionsConstruct &) { PopContext(); } - - bool Pre(const parser::OpenMPThreadprivate &); - void Post(const parser::OpenMPThreadprivate &) { PopContext(); } - - // 2.15.3 Data-Sharing Attribute Clauses - void Post(const parser::OmpDefaultClause &); - bool Pre(const parser::OmpClause::Shared &x) { - ResolveOmpObjectList(x.v, Symbol::Flag::OmpShared); - return false; - } - bool Pre(const parser::OmpClause::Private &x) { - ResolveOmpObjectList(x.v, Symbol::Flag::OmpPrivate); - return false; - } - bool Pre(const parser::OmpClause::Firstprivate &x) { - ResolveOmpObjectList(x.v, Symbol::Flag::OmpFirstPrivate); - return false; - } - bool Pre(const parser::OmpClause::Lastprivate &x) { - ResolveOmpObjectList(x.v, Symbol::Flag::OmpLastPrivate); - return false; - } - - void Post(const parser::Name &); - -private: - std::int64_t GetAssociatedLoopLevelFromClauses(const parser::OmpClauseList &); - - static constexpr Symbol::Flags dataSharingAttributeFlags{ - Symbol::Flag::OmpShared, Symbol::Flag::OmpPrivate, - Symbol::Flag::OmpFirstPrivate, Symbol::Flag::OmpLastPrivate, - Symbol::Flag::OmpReduction, Symbol::Flag::OmpLinear}; - - static constexpr Symbol::Flags ompFlagsRequireNewSymbol{ - Symbol::Flag::OmpPrivate, Symbol::Flag::OmpLinear, - Symbol::Flag::OmpFirstPrivate, Symbol::Flag::OmpLastPrivate, - Symbol::Flag::OmpReduction}; - - static constexpr Symbol::Flags ompFlagsRequireMark{ - Symbol::Flag::OmpThreadprivate}; - - // Predetermined DSA rules - void PrivatizeAssociatedLoopIndex(const parser::OpenMPLoopConstruct &); - void ResolveSeqLoopIndexInParallelOrTaskConstruct(const parser::Name &); - - void ResolveOmpObjectList(const parser::OmpObjectList &, Symbol::Flag); - void ResolveOmpObject(const parser::OmpObject &, Symbol::Flag); - Symbol *ResolveOmp(const parser::Name &, Symbol::Flag, Scope &); - Symbol *ResolveOmp(Symbol &, Symbol::Flag, Scope &); - Symbol *ResolveOmpCommonBlockName(const parser::Name *); - Symbol *DeclareOrMarkOtherAccessEntity(const parser::Name &, Symbol::Flag); - Symbol *DeclareOrMarkOtherAccessEntity(Symbol &, Symbol::Flag); - void CheckMultipleAppearances( - const parser::Name &, const Symbol &, Symbol::Flag); -}; - // Walk the parse tree and resolve names to symbols. class ResolveNamesVisitor : public virtual ScopeHandler, public ModuleVisitor, @@ -1672,8 +1343,6 @@ void FinishSpecificationParts(const ProgramTree &); void FinishDerivedTypeInstantiation(Scope &); void ResolveExecutionParts(const ProgramTree &); - void ResolveAccParts(const parser::ProgramUnit &); - void ResolveOmpParts(const parser::ProgramUnit &); }; // ImplicitRules implementation @@ -3510,8 +3179,7 @@ if (auto &init{std::get>( enumerator.t)}) { Walk(*init); // Resolve names in expression before evaluation. - MaybeIntExpr expr{EvaluateIntExpr(*init)}; - if (auto value{evaluate::ToInt64(expr)}) { + if (auto value{EvaluateInt64(context(), *init)}) { // Cast all init expressions to C_INT so that they can then be // safely incremented (see 7.6 Note 2). enumerationState_.value = static_cast(*value); @@ -5475,8 +5143,7 @@ SetTypeFromAssociation(*symbol); SetAttrsFromAssociation(*symbol); if (const auto *init{std::get_if(&x.u)}) { - MaybeIntExpr expr{EvaluateIntExpr(*init)}; - if (auto val{evaluate::ToInt64(expr)}) { + if (auto val{EvaluateInt64(context(), *init)}) { auto &details{symbol->get()}; details.set_rank(*val); } @@ -5752,28 +5419,6 @@ x.u); } -const parser::Name *DeclarationVisitor::ResolveVariable( - const parser::Variable &x) { - return std::visit( - common::visitors{ - [&](const Indirection &y) { - return ResolveDesignator(y.value()); - }, - [&](const Indirection &y) { - const auto &proc{ - std::get(y.value().v.t)}; - return std::visit(common::visitors{ - [&](const parser::Name &z) { return &z; }, - [&](const parser::ProcComponentRef &z) { - return ResolveStructureComponent(z.v.thing); - }, - }, - proc.u); - }, - }, - x.u); -} - // If implicit types are allowed, ensure name is in the symbol table. // Otherwise, report an error if it hasn't been declared. const parser::Name *DeclarationVisitor::ResolveName(const parser::Name &name) { @@ -6498,12 +6143,8 @@ inExecutionPart_ = true; ResolveExecutionParts(root); inExecutionPart_ = false; - if (context().IsEnabled(common::LanguageFeature::OpenACC)) { - ResolveAccParts(x); - } - if (context().IsEnabled(common::LanguageFeature::OpenMP)) { - ResolveOmpParts(x); - } + ResolveAccParts(context(), x); + ResolveOmpParts(context(), x); return false; } @@ -6696,668 +6337,6 @@ bool pushedScope_{false}; }; -bool AccAttributeVisitor::Pre(const parser::OpenACCBlockConstruct &x) { - const auto &beginBlockDir{std::get(x.t)}; - const auto &blockDir{std::get(beginBlockDir.t)}; - switch (blockDir.v) { - case llvm::acc::Directive::ACCD_data: - case llvm::acc::Directive::ACCD_host_data: - case llvm::acc::Directive::ACCD_kernels: - case llvm::acc::Directive::ACCD_parallel: - case llvm::acc::Directive::ACCD_serial: - PushContext(blockDir.source, blockDir.v); - break; - default: - break; - } - ClearDataSharingAttributeObjects(); - return true; -} - -bool AccAttributeVisitor::Pre(const parser::OpenACCLoopConstruct &x) { - const auto &beginDir{std::get(x.t)}; - const auto &loopDir{std::get(beginDir.t)}; - const auto &clauseList{std::get(beginDir.t)}; - if (loopDir.v == llvm::acc::Directive::ACCD_loop) { - PushContext(loopDir.source, loopDir.v); - } - ClearDataSharingAttributeObjects(); - SetContextAssociatedLoopLevel(GetAssociatedLoopLevelFromClauses(clauseList)); - PrivatizeAssociatedLoopIndex(x); - return true; -} - -bool AccAttributeVisitor::Pre(const parser::OpenACCStandaloneConstruct &x) { - const auto &standaloneDir{std::get(x.t)}; - switch (standaloneDir.v) { - case llvm::acc::Directive::ACCD_cache: - case llvm::acc::Directive::ACCD_enter_data: - case llvm::acc::Directive::ACCD_exit_data: - case llvm::acc::Directive::ACCD_init: - case llvm::acc::Directive::ACCD_set: - case llvm::acc::Directive::ACCD_shutdown: - case llvm::acc::Directive::ACCD_update: - PushContext(standaloneDir.source, standaloneDir.v); - break; - default: - break; - } - ClearDataSharingAttributeObjects(); - return true; -} - -bool AccAttributeVisitor::Pre(const parser::OpenACCCombinedConstruct &x) { - const auto &beginBlockDir{std::get(x.t)}; - const auto &combinedDir{ - std::get(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: - PushContext(combinedDir.source, combinedDir.v); - break; - default: - break; - } - ClearDataSharingAttributeObjects(); - return true; -} - -int64_t AccAttributeVisitor::GetAssociatedLoopLevelFromClauses( - const parser::AccClauseList &x) { - int64_t collapseLevel{0}; - for (const auto &clause : x.v) { - if (const auto *collapseClause{ - std::get_if(&clause.u)}) { - if (const auto v{evaluate::ToInt64( - resolver_.EvaluateIntExpr(collapseClause->v))}) { - collapseLevel = *v; - } - } - } - - if (collapseLevel) { - return collapseLevel; - } - return 1; // default is outermost loop -} - -void AccAttributeVisitor::PrivatizeAssociatedLoopIndex( - const parser::OpenACCLoopConstruct &x) { - int64_t level{GetContext().associatedLoopLevel}; - if (level <= 0) { // collpase value was negative or 0 - return; - } - Symbol::Flag ivDSA{Symbol::Flag::AccPrivate}; - - const auto &outer{std::get>(x.t)}; - for (const parser::DoConstruct *loop{&*outer}; loop && level > 0; --level) { - // go through all the nested do-loops and resolve index variables - const parser::Name &iv{GetLoopIndex(*loop)}; - if (auto *symbol{ResolveAcc(iv, ivDSA, currScope())}) { - symbol->set(Symbol::Flag::AccPreDetermined); - iv.symbol = symbol; // adjust the symbol within region - AddToContextObjectWithDSA(*symbol, ivDSA); - } - - const auto &block{std::get(loop->t)}; - const auto it{block.begin()}; - loop = it != block.end() ? GetDoConstructIf(*it) : nullptr; - } - CHECK(level == 0); -} - -void AccAttributeVisitor::Post(const parser::AccDefaultClause &x) { - if (!dirContext_.empty()) { - switch (x.v) { - case parser::AccDefaultClause::Arg::Present: - SetContextDefaultDSA(Symbol::Flag::AccPresent); - break; - case parser::AccDefaultClause::Arg::None: - SetContextDefaultDSA(Symbol::Flag::AccNone); - break; - } - } -} - -// For OpenACC constructs, check all the data-refs within the constructs -// and adjust the symbol for each Name if necessary -void AccAttributeVisitor::Post(const parser::Name &name) { - auto *symbol{name.symbol}; - if (symbol && !dirContext_.empty() && GetContext().withinConstruct) { - if (!symbol->owner().IsDerivedType() && !symbol->has() && - !IsObjectWithDSA(*symbol)) { - if (Symbol * found{currScope().FindSymbol(name.source)}) { - if (symbol != found) { - name.symbol = found; // adjust the symbol within region - } else if (GetContext().defaultDSA == Symbol::Flag::AccNone) { - // 2.5.14. - context_.Say(name.source, - "The DEFAULT(NONE) clause requires that '%s' must be listed in " - "a data-mapping clause"_err_en_US, - symbol->name()); - } - } - } - } // within OpenACC construct -} - -Symbol *AccAttributeVisitor::ResolveAccCommonBlockName( - const parser::Name *name) { - if (!name) { - return nullptr; - } else if (auto *prev{ - GetContext().scope.parent().FindCommonBlock(name->source)}) { - name->symbol = prev; - return prev; - } else { - return nullptr; - } -} - -void AccAttributeVisitor::ResolveAccObjectList( - const parser::AccObjectList &accObjectList, Symbol::Flag accFlag) { - for (const auto &accObject : accObjectList.v) { - ResolveAccObject(accObject, accFlag); - } -} - -void AccAttributeVisitor::ResolveAccObject( - const parser::AccObject &accObject, Symbol::Flag accFlag) { - std::visit( - common::visitors{ - [&](const parser::Designator &designator) { - if (const auto *name{GetDesignatorNameIfDataRef(designator)}) { - if (auto *symbol{ResolveAcc(*name, accFlag, currScope())}) { - AddToContextObjectWithDSA(*symbol, accFlag); - if (dataSharingAttributeFlags.test(accFlag)) { - CheckMultipleAppearances(*name, *symbol, accFlag); - } - } - } else if (const auto *designatorName{ - resolver_.ResolveDesignator(designator)}; - designatorName->symbol) { - // Array sections to be changed to substrings as needed - if (AnalyzeExpr(context_, designator)) { - if (std::holds_alternative(designator.u)) { - context_.Say(designator.source, - "Substrings are not allowed on OpenACC " - "directives or clauses"_err_en_US); - } - } - // other checks, more TBD - if (const auto *details{designatorName->symbol - ->detailsIf()}) { - if (details->IsArray()) { - // TODO: check Array Sections - } else if (designatorName->symbol->owner().IsDerivedType()) { - // TODO: check Structure Component - } - } - } - }, - [&](const parser::Name &name) { // common block - if (auto *symbol{ResolveAccCommonBlockName(&name)}) { - CheckMultipleAppearances( - name, *symbol, Symbol::Flag::AccCommonBlock); - for (auto &object : symbol->get().objects()) { - if (auto *resolvedObject{ - ResolveAcc(*object, accFlag, currScope())}) { - AddToContextObjectWithDSA(*resolvedObject, accFlag); - } - } - } else { - context_.Say(name.source, - "COMMON block must be declared in the same scoping unit " - "in which the OpenACC directive or clause appears"_err_en_US); - } - }, - }, - accObject.u); -} - -Symbol *AccAttributeVisitor::ResolveAcc( - const parser::Name &name, Symbol::Flag accFlag, Scope &scope) { - if (accFlagsRequireNewSymbol.test(accFlag)) { - return DeclarePrivateAccessEntity(name, accFlag, scope); - } else { - return DeclareOrMarkOtherAccessEntity(name, accFlag); - } -} - -Symbol *AccAttributeVisitor::ResolveAcc( - Symbol &symbol, Symbol::Flag accFlag, Scope &scope) { - if (accFlagsRequireNewSymbol.test(accFlag)) { - return DeclarePrivateAccessEntity(symbol, accFlag, scope); - } else { - return DeclareOrMarkOtherAccessEntity(symbol, accFlag); - } -} - -Symbol *AccAttributeVisitor::DeclareOrMarkOtherAccessEntity( - const parser::Name &name, Symbol::Flag accFlag) { - Symbol *prev{currScope().FindSymbol(name.source)}; - if (!name.symbol || !prev) { - return nullptr; - } else if (prev != name.symbol) { - name.symbol = prev; - } - return DeclareOrMarkOtherAccessEntity(*prev, accFlag); -} - -Symbol *AccAttributeVisitor::DeclareOrMarkOtherAccessEntity( - Symbol &object, Symbol::Flag accFlag) { - if (accFlagsRequireMark.test(accFlag)) { - object.set(accFlag); - } - return &object; -} - -static bool WithMultipleAppearancesAccException( - const Symbol &symbol, Symbol::Flag flag) { - return false; // Place holder -} - -void AccAttributeVisitor::CheckMultipleAppearances( - const parser::Name &name, const Symbol &symbol, Symbol::Flag accFlag) { - const auto *target{&symbol}; - if (accFlagsRequireNewSymbol.test(accFlag)) { - if (const auto *details{symbol.detailsIf()}) { - target = &details->symbol(); - } - } - if (HasDataSharingAttributeObject(*target) && - !WithMultipleAppearancesAccException(symbol, accFlag)) { - context_.Say(name.source, - "'%s' appears in more than one data-sharing clause " - "on the same OpenACC directive"_err_en_US, - name.ToString()); - } else { - AddDataSharingAttributeObject(*target); - } -} - -bool OmpAttributeVisitor::Pre(const parser::OpenMPBlockConstruct &x) { - const auto &beginBlockDir{std::get(x.t)}; - const auto &beginDir{std::get(beginBlockDir.t)}; - switch (beginDir.v) { - case llvm::omp::Directive::OMPD_master: - case llvm::omp::Directive::OMPD_ordered: - case llvm::omp::Directive::OMPD_parallel: - case llvm::omp::Directive::OMPD_single: - case llvm::omp::Directive::OMPD_target: - case llvm::omp::Directive::OMPD_target_data: - case llvm::omp::Directive::OMPD_task: - case llvm::omp::Directive::OMPD_teams: - case llvm::omp::Directive::OMPD_workshare: - case llvm::omp::Directive::OMPD_parallel_workshare: - case llvm::omp::Directive::OMPD_target_teams: - case llvm::omp::Directive::OMPD_target_parallel: - PushContext(beginDir.source, beginDir.v); - break; - default: - // TODO others - break; - } - ClearDataSharingAttributeObjects(); - return true; -} - -bool OmpAttributeVisitor::Pre(const parser::OpenMPLoopConstruct &x) { - const auto &beginLoopDir{std::get(x.t)}; - const auto &beginDir{std::get(beginLoopDir.t)}; - const auto &clauseList{std::get(beginLoopDir.t)}; - switch (beginDir.v) { - case llvm::omp::Directive::OMPD_distribute: - case llvm::omp::Directive::OMPD_distribute_parallel_do: - case llvm::omp::Directive::OMPD_distribute_parallel_do_simd: - case llvm::omp::Directive::OMPD_distribute_simd: - case llvm::omp::Directive::OMPD_do: - case llvm::omp::Directive::OMPD_do_simd: - case llvm::omp::Directive::OMPD_parallel_do: - case llvm::omp::Directive::OMPD_parallel_do_simd: - case llvm::omp::Directive::OMPD_simd: - case llvm::omp::Directive::OMPD_target_parallel_do: - case llvm::omp::Directive::OMPD_target_parallel_do_simd: - case llvm::omp::Directive::OMPD_target_teams_distribute: - case llvm::omp::Directive::OMPD_target_teams_distribute_parallel_do: - case llvm::omp::Directive::OMPD_target_teams_distribute_parallel_do_simd: - case llvm::omp::Directive::OMPD_target_teams_distribute_simd: - case llvm::omp::Directive::OMPD_target_simd: - case llvm::omp::Directive::OMPD_taskloop: - case llvm::omp::Directive::OMPD_taskloop_simd: - case llvm::omp::Directive::OMPD_teams_distribute: - case llvm::omp::Directive::OMPD_teams_distribute_parallel_do: - case llvm::omp::Directive::OMPD_teams_distribute_parallel_do_simd: - case llvm::omp::Directive::OMPD_teams_distribute_simd: - PushContext(beginDir.source, beginDir.v); - break; - default: - break; - } - ClearDataSharingAttributeObjects(); - SetContextAssociatedLoopLevel(GetAssociatedLoopLevelFromClauses(clauseList)); - PrivatizeAssociatedLoopIndex(x); - return true; -} - -void OmpAttributeVisitor::ResolveSeqLoopIndexInParallelOrTaskConstruct( - const parser::Name &iv) { - auto targetIt{dirContext_.rbegin()}; - for (;; ++targetIt) { - if (targetIt == dirContext_.rend()) { - return; - } - if (llvm::omp::parallelSet.test(targetIt->directive) || - llvm::omp::taskGeneratingSet.test(targetIt->directive)) { - break; - } - } - if (auto *symbol{ResolveOmp(iv, Symbol::Flag::OmpPrivate, targetIt->scope)}) { - targetIt++; - symbol->set(Symbol::Flag::OmpPreDetermined); - iv.symbol = symbol; // adjust the symbol within region - for (auto it{dirContext_.rbegin()}; it != targetIt; ++it) { - AddToContextObjectWithDSA(*symbol, Symbol::Flag::OmpPrivate, *it); - } - } -} - -// 2.15.1.1 Data-sharing Attribute Rules - Predetermined -// - A loop iteration variable for a sequential loop in a parallel -// or task generating construct is private in the innermost such -// construct that encloses the loop -bool OmpAttributeVisitor::Pre(const parser::DoConstruct &x) { - if (!dirContext_.empty() && GetContext().withinConstruct) { - if (const auto &iv{GetLoopIndex(x)}; iv.symbol) { - if (!iv.symbol->test(Symbol::Flag::OmpPreDetermined)) { - ResolveSeqLoopIndexInParallelOrTaskConstruct(iv); - } else { - // TODO: conflict checks with explicitly determined DSA - } - } - } - return true; -} - -std::int64_t OmpAttributeVisitor::GetAssociatedLoopLevelFromClauses( - const parser::OmpClauseList &x) { - std::int64_t orderedLevel{0}; - std::int64_t collapseLevel{0}; - for (const auto &clause : x.v) { - if (const auto *orderedClause{ - std::get_if(&clause.u)}) { - if (const auto v{ - evaluate::ToInt64(resolver_.EvaluateIntExpr(orderedClause->v))}) { - orderedLevel = *v; - } - } - if (const auto *collapseClause{ - std::get_if(&clause.u)}) { - if (const auto v{evaluate::ToInt64( - resolver_.EvaluateIntExpr(collapseClause->v))}) { - collapseLevel = *v; - } - } - } - - if (orderedLevel && (!collapseLevel || orderedLevel >= collapseLevel)) { - return orderedLevel; - } else if (!orderedLevel && collapseLevel) { - return collapseLevel; - } // orderedLevel < collapseLevel is an error handled in structural checks - return 1; // default is outermost loop -} - -// 2.15.1.1 Data-sharing Attribute Rules - Predetermined -// - The loop iteration variable(s) in the associated do-loop(s) of a do, -// parallel do, taskloop, or distribute construct is (are) private. -// - The loop iteration variable in the associated do-loop of a simd construct -// with just one associated do-loop is linear with a linear-step that is the -// increment of the associated do-loop. -// - The loop iteration variables in the associated do-loops of a simd -// construct with multiple associated do-loops are lastprivate. -// -// TODO: revisit after semantics checks are completed for do-loop association of -// collapse and ordered -void OmpAttributeVisitor::PrivatizeAssociatedLoopIndex( - const parser::OpenMPLoopConstruct &x) { - std::int64_t level{GetContext().associatedLoopLevel}; - if (level <= 0) - return; - Symbol::Flag ivDSA{Symbol::Flag::OmpPrivate}; - if (llvm::omp::simdSet.test(GetContext().directive)) { - if (level == 1) { - ivDSA = Symbol::Flag::OmpLinear; - } else { - ivDSA = Symbol::Flag::OmpLastPrivate; - } - } - - auto &outer{std::get>(x.t)}; - for (const parser::DoConstruct *loop{&*outer}; loop && level > 0; --level) { - // go through all the nested do-loops and resolve index variables - const parser::Name &iv{GetLoopIndex(*loop)}; - if (auto *symbol{ResolveOmp(iv, ivDSA, currScope())}) { - symbol->set(Symbol::Flag::OmpPreDetermined); - iv.symbol = symbol; // adjust the symbol within region - AddToContextObjectWithDSA(*symbol, ivDSA); - } - - const auto &block{std::get(loop->t)}; - const auto it{block.begin()}; - loop = it != block.end() ? GetDoConstructIf(*it) : nullptr; - } - CHECK(level == 0); -} - -bool OmpAttributeVisitor::Pre(const parser::OpenMPSectionsConstruct &x) { - const auto &beginSectionsDir{ - std::get(x.t)}; - const auto &beginDir{ - std::get(beginSectionsDir.t)}; - switch (beginDir.v) { - case llvm::omp::Directive::OMPD_parallel_sections: - case llvm::omp::Directive::OMPD_sections: - PushContext(beginDir.source, beginDir.v); - break; - default: - break; - } - ClearDataSharingAttributeObjects(); - return true; -} - -bool OmpAttributeVisitor::Pre(const parser::OpenMPThreadprivate &x) { - PushContext(x.source, llvm::omp::Directive::OMPD_threadprivate); - const auto &list{std::get(x.t)}; - ResolveOmpObjectList(list, Symbol::Flag::OmpThreadprivate); - return false; -} - -void OmpAttributeVisitor::Post(const parser::OmpDefaultClause &x) { - if (!dirContext_.empty()) { - switch (x.v) { - case parser::OmpDefaultClause::Type::Private: - SetContextDefaultDSA(Symbol::Flag::OmpPrivate); - break; - case parser::OmpDefaultClause::Type::Firstprivate: - SetContextDefaultDSA(Symbol::Flag::OmpFirstPrivate); - break; - case parser::OmpDefaultClause::Type::Shared: - SetContextDefaultDSA(Symbol::Flag::OmpShared); - break; - case parser::OmpDefaultClause::Type::None: - SetContextDefaultDSA(Symbol::Flag::OmpNone); - break; - } - } -} - -// For OpenMP constructs, check all the data-refs within the constructs -// and adjust the symbol for each Name if necessary -void OmpAttributeVisitor::Post(const parser::Name &name) { - auto *symbol{name.symbol}; - if (symbol && !dirContext_.empty() && GetContext().withinConstruct) { - if (!symbol->owner().IsDerivedType() && !symbol->has() && - !IsObjectWithDSA(*symbol)) { - // TODO: create a separate function to go through the rules for - // predetermined, explicitly determined, and implicitly - // determined data-sharing attributes (2.15.1.1). - if (Symbol * found{currScope().FindSymbol(name.source)}) { - if (symbol != found) { - name.symbol = found; // adjust the symbol within region - } else if (GetContext().defaultDSA == Symbol::Flag::OmpNone) { - context_.Say(name.source, - "The DEFAULT(NONE) clause requires that '%s' must be listed in " - "a data-sharing attribute clause"_err_en_US, - symbol->name()); - } - } - } - } // within OpenMP construct -} - -Symbol *OmpAttributeVisitor::ResolveOmpCommonBlockName( - const parser::Name *name) { - if (auto *prev{name - ? GetContext().scope.parent().FindCommonBlock(name->source) - : nullptr}) { - name->symbol = prev; - return prev; - } else { - return nullptr; - } -} - -void OmpAttributeVisitor::ResolveOmpObjectList( - const parser::OmpObjectList &ompObjectList, Symbol::Flag ompFlag) { - for (const auto &ompObject : ompObjectList.v) { - ResolveOmpObject(ompObject, ompFlag); - } -} - -void OmpAttributeVisitor::ResolveOmpObject( - const parser::OmpObject &ompObject, Symbol::Flag ompFlag) { - std::visit( - common::visitors{ - [&](const parser::Designator &designator) { - if (const auto *name{GetDesignatorNameIfDataRef(designator)}) { - if (auto *symbol{ResolveOmp(*name, ompFlag, currScope())}) { - AddToContextObjectWithDSA(*symbol, ompFlag); - if (dataSharingAttributeFlags.test(ompFlag)) { - CheckMultipleAppearances(*name, *symbol, ompFlag); - } - } - } else if (const auto *designatorName{ - resolver_.ResolveDesignator(designator)}; - designatorName->symbol) { - // Array sections to be changed to substrings as needed - if (AnalyzeExpr(context_, designator)) { - if (std::holds_alternative(designator.u)) { - context_.Say(designator.source, - "Substrings are not allowed on OpenMP " - "directives or clauses"_err_en_US); - } - } - // other checks, more TBD - if (const auto *details{designatorName->symbol - ->detailsIf()}) { - if (details->IsArray()) { - // TODO: check Array Sections - } else if (designatorName->symbol->owner().IsDerivedType()) { - // TODO: check Structure Component - } - } - } - }, - [&](const parser::Name &name) { // common block - if (auto *symbol{ResolveOmpCommonBlockName(&name)}) { - CheckMultipleAppearances( - name, *symbol, Symbol::Flag::OmpCommonBlock); - // 2.15.3 When a named common block appears in a list, it has the - // same meaning as if every explicit member of the common block - // appeared in the list - for (auto &object : symbol->get().objects()) { - if (auto *resolvedObject{ - ResolveOmp(*object, ompFlag, currScope())}) { - AddToContextObjectWithDSA(*resolvedObject, ompFlag); - } - } - } else { - context_.Say(name.source, // 2.15.3 - "COMMON block must be declared in the same scoping unit " - "in which the OpenMP directive or clause appears"_err_en_US); - } - }, - }, - ompObject.u); -} - -Symbol *OmpAttributeVisitor::ResolveOmp( - const parser::Name &name, Symbol::Flag ompFlag, Scope &scope) { - if (ompFlagsRequireNewSymbol.test(ompFlag)) { - return DeclarePrivateAccessEntity(name, ompFlag, scope); - } else { - return DeclareOrMarkOtherAccessEntity(name, ompFlag); - } -} - -Symbol *OmpAttributeVisitor::ResolveOmp( - Symbol &symbol, Symbol::Flag ompFlag, Scope &scope) { - if (ompFlagsRequireNewSymbol.test(ompFlag)) { - return DeclarePrivateAccessEntity(symbol, ompFlag, scope); - } else { - return DeclareOrMarkOtherAccessEntity(symbol, ompFlag); - } -} - -Symbol *OmpAttributeVisitor::DeclareOrMarkOtherAccessEntity( - const parser::Name &name, Symbol::Flag ompFlag) { - Symbol *prev{currScope().FindSymbol(name.source)}; - if (!name.symbol || !prev) { - return nullptr; - } else if (prev != name.symbol) { - name.symbol = prev; - } - return DeclareOrMarkOtherAccessEntity(*prev, ompFlag); -} - -Symbol *OmpAttributeVisitor::DeclareOrMarkOtherAccessEntity( - Symbol &object, Symbol::Flag ompFlag) { - if (ompFlagsRequireMark.test(ompFlag)) { - object.set(ompFlag); - } - return &object; -} - -static bool WithMultipleAppearancesOmpException( - const Symbol &symbol, Symbol::Flag flag) { - return (flag == Symbol::Flag::OmpFirstPrivate && - symbol.test(Symbol::Flag::OmpLastPrivate)) || - (flag == Symbol::Flag::OmpLastPrivate && - symbol.test(Symbol::Flag::OmpFirstPrivate)); -} - -void OmpAttributeVisitor::CheckMultipleAppearances( - const parser::Name &name, const Symbol &symbol, Symbol::Flag ompFlag) { - const auto *target{&symbol}; - if (ompFlagsRequireNewSymbol.test(ompFlag)) { - if (const auto *details{symbol.detailsIf()}) { - target = &details->symbol(); - } - } - if (HasDataSharingAttributeObject(*target) && - !WithMultipleAppearancesOmpException(symbol, ompFlag)) { - context_.Say(name.source, - "'%s' appears in more than one data-sharing clause " - "on the same OpenMP directive"_err_en_US, - name.ToString()); - } else { - AddDataSharingAttributeObject(*target); - } -} - // Perform checks and completions that need to happen after all of // the specification parts but before any of the execution parts. void ResolveNamesVisitor::FinishSpecificationParts(const ProgramTree &node) { @@ -7427,22 +6406,6 @@ } } -void ResolveNamesVisitor::ResolveAccParts(const parser::ProgramUnit &node) { - AccAttributeVisitor{context(), *this}.Walk(node); -} - -void ResolveNamesVisitor::ResolveOmpParts(const parser::ProgramUnit &node) { - OmpAttributeVisitor{context(), *this}.Walk(node); - if (!context().AnyFatalError()) { - // The data-sharing attribute of the loop iteration variable for a - // sequential loop (2.15.1.1) can only be determined when visiting - // the corresponding DoConstruct, a second walk is to adjust the - // symbols for all the data-refs of that loop iteration variable - // prior to the DoConstruct. - OmpAttributeVisitor{context(), *this}.Walk(node); - } -} - void ResolveNamesVisitor::Post(const parser::Program &) { // ensure that all temps were deallocated CHECK(!attrs_); @@ -7474,4 +6437,5 @@ visitor.ResolveSpecificationParts(node); context.set_location(std::move(originalLocation)); } + } // namespace Fortran::semantics diff --git a/flang/test/Semantics/acc-resolve01.f90 b/flang/test/Semantics/acc-resolve01.f90 --- a/flang/test/Semantics/acc-resolve01.f90 +++ b/flang/test/Semantics/acc-resolve01.f90 @@ -19,4 +19,4 @@ program mm call default_none() -end \ No newline at end of file +end