forked from OSchip/llvm-project
1874 lines
69 KiB
C++
1874 lines
69 KiB
C++
//===----------------------------------------------------------------------===//
|
|
//
|
|
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
|
|
// See https://llvm.org/LICENSE.txt for license information.
|
|
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
|
|
//
|
|
//===----------------------------------------------------------------------===//
|
|
|
|
#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/Evaluate/type.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 <list>
|
|
#include <map>
|
|
|
|
namespace Fortran::semantics {
|
|
|
|
template <typename T> class DirectiveAttributeVisitor {
|
|
public:
|
|
explicit DirectiveAttributeVisitor(SemanticsContext &context)
|
|
: context_{context} {}
|
|
|
|
template <typename A> bool Pre(const A &) { return true; }
|
|
template <typename A> 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<const Symbol *, Symbol::Flag> objectWithDSA;
|
|
bool withinConstruct{false};
|
|
std::int64_t associatedLoopLevel{0};
|
|
};
|
|
|
|
DirContext &GetContext() {
|
|
CHECK(!dirContext_.empty());
|
|
return dirContext_.back();
|
|
}
|
|
std::optional<DirContext> GetContextIf() {
|
|
return dirContext_.empty()
|
|
? std::nullopt
|
|
: std::make_optional<DirContext>(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<parser::DataRef>(&designator.u)};
|
|
return dataRef ? std::get_if<parser::Name>(&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);
|
|
|
|
UnorderedSymbolSet dataSharingAttributeObjects_; // on one directive
|
|
SemanticsContext &context_;
|
|
std::vector<DirContext> dirContext_; // used as a stack
|
|
};
|
|
|
|
class AccAttributeVisitor : DirectiveAttributeVisitor<llvm::acc::Directive> {
|
|
public:
|
|
explicit AccAttributeVisitor(SemanticsContext &context)
|
|
: DirectiveAttributeVisitor(context) {}
|
|
|
|
template <typename A> void Walk(const A &x) { parser::Walk(x, *this); }
|
|
template <typename A> bool Pre(const A &) { return true; }
|
|
template <typename A> void Post(const A &) {}
|
|
|
|
bool Pre(const parser::OpenACCBlockConstruct &);
|
|
void Post(const parser::OpenACCBlockConstruct &) { PopContext(); }
|
|
bool Pre(const parser::OpenACCCombinedConstruct &);
|
|
void Post(const parser::OpenACCCombinedConstruct &) { PopContext(); }
|
|
|
|
bool Pre(const parser::OpenACCDeclarativeConstruct &);
|
|
void Post(const parser::OpenACCDeclarativeConstruct &) { PopContext(); }
|
|
|
|
bool Pre(const parser::OpenACCRoutineConstruct &);
|
|
bool Pre(const parser::AccBindClause &);
|
|
void Post(const parser::OpenACCStandaloneDeclarativeConstruct &);
|
|
|
|
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;
|
|
}
|
|
|
|
bool Pre(const parser::OpenACCCacheConstruct &);
|
|
void Post(const parser::OpenACCCacheConstruct &) { PopContext(); }
|
|
|
|
void Post(const parser::AccDefaultClause &);
|
|
|
|
bool Pre(const parser::AccClause::Attach &);
|
|
bool Pre(const parser::AccClause::Detach &);
|
|
|
|
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<parser::AccObjectList>(x.v.t)};
|
|
ResolveAccObjectList(objectList, Symbol::Flag::AccCreate);
|
|
return false;
|
|
}
|
|
|
|
bool Pre(const parser::AccClause::Copyin &x) {
|
|
const auto &objectList{std::get<parser::AccObjectList>(x.v.t)};
|
|
ResolveAccObjectList(objectList, Symbol::Flag::AccCopyIn);
|
|
return false;
|
|
}
|
|
|
|
bool Pre(const parser::AccClause::Copyout &x) {
|
|
const auto &objectList{std::get<parser::AccObjectList>(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 *ResolveName(const parser::Name &);
|
|
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);
|
|
void AllowOnlyArrayAndSubArray(const parser::AccObjectList &objectList);
|
|
void DoNotAllowAssumedSizedArray(const parser::AccObjectList &objectList);
|
|
void EnsureAllocatableOrPointer(
|
|
const llvm::acc::Clause clause, const parser::AccObjectList &objectList);
|
|
};
|
|
|
|
// Data-sharing and Data-mapping attributes for data-refs in OpenMP construct
|
|
class OmpAttributeVisitor : DirectiveAttributeVisitor<llvm::omp::Directive> {
|
|
public:
|
|
explicit OmpAttributeVisitor(SemanticsContext &context)
|
|
: DirectiveAttributeVisitor(context) {}
|
|
|
|
template <typename A> void Walk(const A &x) { parser::Walk(x, *this); }
|
|
template <typename A> bool Pre(const A &) { return true; }
|
|
template <typename A> void Post(const A &) {}
|
|
|
|
template <typename A> bool Pre(const parser::Statement<A> &statement) {
|
|
currentStatementSource_ = statement.source;
|
|
// Keep track of the labels in all the labelled statements
|
|
if (statement.label) {
|
|
auto label{statement.label.value()};
|
|
// Get the context to check if the labelled statement is in an
|
|
// enclosing OpenMP construct
|
|
std::optional<DirContext> thisContext{GetContextIf()};
|
|
targetLabels_.emplace(
|
|
label, std::make_pair(currentStatementSource_, thisContext));
|
|
// Check if a statement that causes a jump to the 'label'
|
|
// has already been encountered
|
|
auto range{sourceLabels_.equal_range(label)};
|
|
for (auto it{range.first}; it != range.second; ++it) {
|
|
// Check if both the statement with 'label' and the statement that
|
|
// causes a jump to the 'label' are in the same scope
|
|
CheckLabelContext(it->second.first, currentStatementSource_,
|
|
it->second.second, thisContext);
|
|
}
|
|
}
|
|
return true;
|
|
}
|
|
|
|
bool Pre(const parser::InternalSubprogram &) {
|
|
// Clear the labels being tracked in the previous scope
|
|
ClearLabels();
|
|
return true;
|
|
}
|
|
|
|
bool Pre(const parser::ModuleSubprogram &) {
|
|
// Clear the labels being tracked in the previous scope
|
|
ClearLabels();
|
|
return true;
|
|
}
|
|
|
|
bool Pre(const parser::SpecificationPart &x) {
|
|
Walk(std::get<std::list<parser::OpenMPDeclarativeConstruct>>(x.t));
|
|
return true;
|
|
}
|
|
|
|
bool Pre(const parser::StmtFunctionStmt &x) {
|
|
const auto &parsedExpr{std::get<parser::Scalar<parser::Expr>>(x.t)};
|
|
if (const auto *expr{GetExpr(parsedExpr)}) {
|
|
for (const Symbol &symbol : evaluate::CollectSymbols(*expr)) {
|
|
if (!IsStmtFunctionDummy(symbol)) {
|
|
stmtFunctionExprSymbols_.insert(symbol.GetUltimate());
|
|
}
|
|
}
|
|
}
|
|
return true;
|
|
}
|
|
|
|
bool Pre(const parser::OpenMPBlockConstruct &);
|
|
void Post(const parser::OpenMPBlockConstruct &);
|
|
bool Pre(const parser::OmpCriticalDirective &x);
|
|
bool Pre(const parser::OmpEndCriticalDirective &x);
|
|
|
|
void Post(const parser::OmpBeginBlockDirective &) {
|
|
GetContext().withinConstruct = true;
|
|
}
|
|
|
|
bool Pre(const parser::OpenMPSimpleStandaloneConstruct &);
|
|
void Post(const parser::OpenMPSimpleStandaloneConstruct &) { PopContext(); }
|
|
|
|
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::OpenMPCriticalConstruct &);
|
|
void Post(const parser::OpenMPCriticalConstruct &) { PopContext(); }
|
|
|
|
bool Pre(const parser::OpenMPDeclareSimdConstruct &x) {
|
|
PushContext(x.source, llvm::omp::Directive::OMPD_declare_simd);
|
|
const auto &name{std::get<std::optional<parser::Name>>(x.t)};
|
|
if (name) {
|
|
ResolveOmpName(*name, Symbol::Flag::OmpDeclareSimd);
|
|
}
|
|
return true;
|
|
}
|
|
void Post(const parser::OpenMPDeclareSimdConstruct &) { PopContext(); }
|
|
bool Pre(const parser::OpenMPThreadprivate &);
|
|
void Post(const parser::OpenMPThreadprivate &) { PopContext(); }
|
|
|
|
bool Pre(const parser::OpenMPDeclarativeAllocate &);
|
|
void Post(const parser::OpenMPDeclarativeAllocate &) { PopContext(); }
|
|
|
|
bool Pre(const parser::OpenMPExecutableAllocate &);
|
|
void Post(const parser::OpenMPExecutableAllocate &);
|
|
|
|
// 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::OmpAllocateClause &x) {
|
|
const auto &objectList{std::get<parser::OmpObjectList>(x.t)};
|
|
ResolveOmpObjectList(objectList, Symbol::Flag::OmpAllocate);
|
|
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;
|
|
}
|
|
bool Pre(const parser::OmpClause::Copyin &x) {
|
|
ResolveOmpObjectList(x.v, Symbol::Flag::OmpCopyIn);
|
|
return false;
|
|
}
|
|
bool Pre(const parser::OmpClause::Copyprivate &x) {
|
|
ResolveOmpObjectList(x.v, Symbol::Flag::OmpCopyPrivate);
|
|
return false;
|
|
}
|
|
bool Pre(const parser::OmpLinearClause &x) {
|
|
std::visit(common::visitors{
|
|
[&](const parser::OmpLinearClause::WithoutModifier
|
|
&linearWithoutModifier) {
|
|
ResolveOmpNameList(
|
|
linearWithoutModifier.names, Symbol::Flag::OmpLinear);
|
|
},
|
|
[&](const parser::OmpLinearClause::WithModifier
|
|
&linearWithModifier) {
|
|
ResolveOmpNameList(
|
|
linearWithModifier.names, Symbol::Flag::OmpLinear);
|
|
},
|
|
},
|
|
x.u);
|
|
return false;
|
|
}
|
|
|
|
bool Pre(const parser::OmpClause::Reduction &x) {
|
|
const parser::OmpReductionOperator &opr{
|
|
std::get<parser::OmpReductionOperator>(x.v.t)};
|
|
if (const auto *procD{parser::Unwrap<parser::ProcedureDesignator>(opr.u)}) {
|
|
if (const auto *name{parser::Unwrap<parser::Name>(procD->u)}) {
|
|
if (!name->symbol) {
|
|
const auto namePair{currScope().try_emplace(
|
|
name->source, Attrs{}, ProcEntityDetails{})};
|
|
auto &symbol{*namePair.first->second};
|
|
name->symbol = &symbol;
|
|
name->symbol->set(Symbol::Flag::OmpReduction);
|
|
AddToContextObjectWithDSA(*name->symbol, Symbol::Flag::OmpReduction);
|
|
}
|
|
}
|
|
if (const auto *procRef{
|
|
parser::Unwrap<parser::ProcComponentRef>(procD->u)}) {
|
|
ResolveOmp(*procRef->v.thing.component.symbol,
|
|
Symbol::Flag::OmpReduction, currScope());
|
|
}
|
|
}
|
|
const auto &objList{std::get<parser::OmpObjectList>(x.v.t)};
|
|
ResolveOmpObjectList(objList, Symbol::Flag::OmpReduction);
|
|
return false;
|
|
}
|
|
|
|
bool Pre(const parser::OmpAlignedClause &x) {
|
|
const auto &alignedNameList{std::get<std::list<parser::Name>>(x.t)};
|
|
ResolveOmpNameList(alignedNameList, Symbol::Flag::OmpAligned);
|
|
return false;
|
|
}
|
|
|
|
bool Pre(const parser::OmpClause::Nontemporal &x) {
|
|
const auto &nontemporalNameList{x.v};
|
|
ResolveOmpNameList(nontemporalNameList, Symbol::Flag::OmpNontemporal);
|
|
return false;
|
|
}
|
|
|
|
bool Pre(const parser::OmpDependClause &x) {
|
|
if (const auto *dependSink{
|
|
std::get_if<parser::OmpDependClause::Sink>(&x.u)}) {
|
|
const auto &dependSinkVec{dependSink->v};
|
|
for (const auto &dependSinkElement : dependSinkVec) {
|
|
const auto &name{std::get<parser::Name>(dependSinkElement.t)};
|
|
ResolveName(&name);
|
|
}
|
|
}
|
|
return false;
|
|
}
|
|
|
|
void Post(const parser::Name &);
|
|
|
|
// Keep track of labels in the statements that causes jumps to target labels
|
|
void Post(const parser::GotoStmt &gotoStmt) { CheckSourceLabel(gotoStmt.v); }
|
|
void Post(const parser::ComputedGotoStmt &computedGotoStmt) {
|
|
for (auto &label : std::get<std::list<parser::Label>>(computedGotoStmt.t)) {
|
|
CheckSourceLabel(label);
|
|
}
|
|
}
|
|
void Post(const parser::ArithmeticIfStmt &arithmeticIfStmt) {
|
|
CheckSourceLabel(std::get<1>(arithmeticIfStmt.t));
|
|
CheckSourceLabel(std::get<2>(arithmeticIfStmt.t));
|
|
CheckSourceLabel(std::get<3>(arithmeticIfStmt.t));
|
|
}
|
|
void Post(const parser::AssignedGotoStmt &assignedGotoStmt) {
|
|
for (auto &label : std::get<std::list<parser::Label>>(assignedGotoStmt.t)) {
|
|
CheckSourceLabel(label);
|
|
}
|
|
}
|
|
void Post(const parser::AltReturnSpec &altReturnSpec) {
|
|
CheckSourceLabel(altReturnSpec.v);
|
|
}
|
|
void Post(const parser::ErrLabel &errLabel) { CheckSourceLabel(errLabel.v); }
|
|
void Post(const parser::EndLabel &endLabel) { CheckSourceLabel(endLabel.v); }
|
|
void Post(const parser::EorLabel &eorLabel) { CheckSourceLabel(eorLabel.v); }
|
|
|
|
const parser::OmpClause *associatedClause{nullptr};
|
|
void SetAssociatedClause(const parser::OmpClause &c) {
|
|
associatedClause = &c;
|
|
}
|
|
const parser::OmpClause *GetAssociatedClause() { return associatedClause; }
|
|
|
|
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 privateDataSharingAttributeFlags{
|
|
Symbol::Flag::OmpPrivate, Symbol::Flag::OmpFirstPrivate,
|
|
Symbol::Flag::OmpLastPrivate};
|
|
|
|
static constexpr Symbol::Flags ompFlagsRequireNewSymbol{
|
|
Symbol::Flag::OmpPrivate, Symbol::Flag::OmpLinear,
|
|
Symbol::Flag::OmpFirstPrivate, Symbol::Flag::OmpLastPrivate,
|
|
Symbol::Flag::OmpReduction, Symbol::Flag::OmpCriticalLock};
|
|
|
|
static constexpr Symbol::Flags ompFlagsRequireMark{
|
|
Symbol::Flag::OmpThreadprivate};
|
|
|
|
static constexpr Symbol::Flags dataCopyingAttributeFlags{
|
|
Symbol::Flag::OmpCopyIn, Symbol::Flag::OmpCopyPrivate};
|
|
|
|
std::vector<const parser::Name *> allocateNames_; // on one directive
|
|
UnorderedSymbolSet privateDataSharingAttributeObjects_; // on one directive
|
|
UnorderedSymbolSet stmtFunctionExprSymbols_;
|
|
std::multimap<const parser::Label,
|
|
std::pair<parser::CharBlock, std::optional<DirContext>>>
|
|
sourceLabels_;
|
|
std::map<const parser::Label,
|
|
std::pair<parser::CharBlock, std::optional<DirContext>>>
|
|
targetLabels_;
|
|
parser::CharBlock currentStatementSource_;
|
|
|
|
void AddAllocateName(const parser::Name *&object) {
|
|
allocateNames_.push_back(object);
|
|
}
|
|
void ClearAllocateNames() { allocateNames_.clear(); }
|
|
|
|
void AddPrivateDataSharingAttributeObjects(SymbolRef object) {
|
|
privateDataSharingAttributeObjects_.insert(object);
|
|
}
|
|
void ClearPrivateDataSharingAttributeObjects() {
|
|
privateDataSharingAttributeObjects_.clear();
|
|
}
|
|
|
|
// Predetermined DSA rules
|
|
void PrivatizeAssociatedLoopIndexAndCheckLoopLevel(
|
|
const parser::OpenMPLoopConstruct &);
|
|
void ResolveSeqLoopIndexInParallelOrTaskConstruct(const parser::Name &);
|
|
|
|
bool IsNestedInDirective(llvm::omp::Directive directive);
|
|
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 *);
|
|
void ResolveOmpNameList(const std::list<parser::Name> &, Symbol::Flag);
|
|
void ResolveOmpName(const parser::Name &, Symbol::Flag);
|
|
Symbol *ResolveName(const parser::Name *);
|
|
Symbol *ResolveOmpObjectScope(const parser::Name *);
|
|
Symbol *DeclareOrMarkOtherAccessEntity(const parser::Name &, Symbol::Flag);
|
|
Symbol *DeclareOrMarkOtherAccessEntity(Symbol &, Symbol::Flag);
|
|
void CheckMultipleAppearances(
|
|
const parser::Name &, const Symbol &, Symbol::Flag);
|
|
|
|
void CheckDataCopyingClause(
|
|
const parser::Name &, const Symbol &, Symbol::Flag);
|
|
void CheckAssocLoopLevel(std::int64_t level, const parser::OmpClause *clause);
|
|
void CheckPrivateDSAObject(
|
|
const parser::Name &, const Symbol &, Symbol::Flag);
|
|
void CheckSourceLabel(const parser::Label &);
|
|
void CheckLabelContext(const parser::CharBlock, const parser::CharBlock,
|
|
std::optional<DirContext>, std::optional<DirContext>);
|
|
void ClearLabels() {
|
|
sourceLabels_.clear();
|
|
targetLabels_.clear();
|
|
};
|
|
|
|
bool HasSymbolInEnclosingScope(const Symbol &, Scope &);
|
|
std::int64_t ordCollapseLevel{0};
|
|
};
|
|
|
|
template <typename T>
|
|
bool DirectiveAttributeVisitor<T>::HasDataSharingAttributeObject(
|
|
const Symbol &object) {
|
|
auto it{dataSharingAttributeObjects_.find(object)};
|
|
return it != dataSharingAttributeObjects_.end();
|
|
}
|
|
|
|
template <typename T>
|
|
const parser::Name &DirectiveAttributeVisitor<T>::GetLoopIndex(
|
|
const parser::DoConstruct &x) {
|
|
using Bounds = parser::LoopControl::Bounds;
|
|
return std::get<Bounds>(x.GetLoopControl()->u).name.thing;
|
|
}
|
|
|
|
template <typename T>
|
|
const parser::DoConstruct *DirectiveAttributeVisitor<T>::GetDoConstructIf(
|
|
const parser::ExecutionPartConstruct &x) {
|
|
return parser::Unwrap<parser::DoConstruct>(x);
|
|
}
|
|
|
|
template <typename T>
|
|
Symbol *DirectiveAttributeVisitor<T>::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 <typename T>
|
|
Symbol *DirectiveAttributeVisitor<T>::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<parser::AccBeginBlockDirective>(x.t)};
|
|
const auto &blockDir{std::get<parser::AccBlockDirective>(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::OpenACCDeclarativeConstruct &x) {
|
|
if (const auto *declConstruct{
|
|
std::get_if<parser::OpenACCStandaloneDeclarativeConstruct>(&x.u)}) {
|
|
const auto &declDir{
|
|
std::get<parser::AccDeclarativeDirective>(declConstruct->t)};
|
|
PushContext(declDir.source, llvm::acc::Directive::ACCD_declare);
|
|
} else if (const auto *routineConstruct{
|
|
std::get_if<parser::OpenACCRoutineConstruct>(&x.u)}) {
|
|
const auto &verbatim{std::get<parser::Verbatim>(routineConstruct->t)};
|
|
PushContext(verbatim.source, llvm::acc::Directive::ACCD_routine);
|
|
}
|
|
ClearDataSharingAttributeObjects();
|
|
return true;
|
|
}
|
|
|
|
static const parser::AccObjectList &GetAccObjectList(
|
|
const parser::AccClause &clause) {
|
|
if (const auto *copyClause =
|
|
std::get_if<Fortran::parser::AccClause::Copy>(&clause.u)) {
|
|
return copyClause->v;
|
|
} else if (const auto *createClause =
|
|
std::get_if<Fortran::parser::AccClause::Create>(&clause.u)) {
|
|
const Fortran::parser::AccObjectListWithModifier &listWithModifier =
|
|
createClause->v;
|
|
const Fortran::parser::AccObjectList &accObjectList =
|
|
std::get<Fortran::parser::AccObjectList>(listWithModifier.t);
|
|
return accObjectList;
|
|
} else if (const auto *copyinClause =
|
|
std::get_if<Fortran::parser::AccClause::Copyin>(&clause.u)) {
|
|
const Fortran::parser::AccObjectListWithModifier &listWithModifier =
|
|
copyinClause->v;
|
|
const Fortran::parser::AccObjectList &accObjectList =
|
|
std::get<Fortran::parser::AccObjectList>(listWithModifier.t);
|
|
return accObjectList;
|
|
} else if (const auto *copyoutClause =
|
|
std::get_if<Fortran::parser::AccClause::Copyout>(&clause.u)) {
|
|
const Fortran::parser::AccObjectListWithModifier &listWithModifier =
|
|
copyoutClause->v;
|
|
const Fortran::parser::AccObjectList &accObjectList =
|
|
std::get<Fortran::parser::AccObjectList>(listWithModifier.t);
|
|
return accObjectList;
|
|
} else if (const auto *presentClause =
|
|
std::get_if<Fortran::parser::AccClause::Present>(&clause.u)) {
|
|
return presentClause->v;
|
|
} else if (const auto *deviceptrClause =
|
|
std::get_if<Fortran::parser::AccClause::Deviceptr>(
|
|
&clause.u)) {
|
|
return deviceptrClause->v;
|
|
} else if (const auto *deviceResidentClause =
|
|
std::get_if<Fortran::parser::AccClause::DeviceResident>(
|
|
&clause.u)) {
|
|
return deviceResidentClause->v;
|
|
} else if (const auto *linkClause =
|
|
std::get_if<Fortran::parser::AccClause::Link>(&clause.u)) {
|
|
return linkClause->v;
|
|
} else {
|
|
llvm_unreachable("Clause without object list!");
|
|
}
|
|
}
|
|
|
|
void AccAttributeVisitor::Post(
|
|
const parser::OpenACCStandaloneDeclarativeConstruct &x) {
|
|
const auto &clauseList = std::get<parser::AccClauseList>(x.t);
|
|
for (const auto &clause : clauseList.v) {
|
|
// Restriction - line 2414
|
|
DoNotAllowAssumedSizedArray(GetAccObjectList(clause));
|
|
}
|
|
}
|
|
|
|
bool AccAttributeVisitor::Pre(const parser::OpenACCLoopConstruct &x) {
|
|
const auto &beginDir{std::get<parser::AccBeginLoopDirective>(x.t)};
|
|
const auto &loopDir{std::get<parser::AccLoopDirective>(beginDir.t)};
|
|
const auto &clauseList{std::get<parser::AccClauseList>(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<parser::AccStandaloneDirective>(x.t)};
|
|
switch (standaloneDir.v) {
|
|
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;
|
|
}
|
|
|
|
Symbol *AccAttributeVisitor::ResolveName(const parser::Name &name) {
|
|
Symbol *prev{currScope().FindSymbol(name.source)};
|
|
if (prev != name.symbol) {
|
|
name.symbol = prev;
|
|
}
|
|
return prev;
|
|
}
|
|
|
|
bool AccAttributeVisitor::Pre(const parser::OpenACCRoutineConstruct &x) {
|
|
const auto &optName{std::get<std::optional<parser::Name>>(x.t)};
|
|
if (optName) {
|
|
if (!ResolveName(*optName)) {
|
|
context_.Say((*optName).source,
|
|
"No function or subroutine declared for '%s'"_err_en_US,
|
|
(*optName).source);
|
|
}
|
|
}
|
|
return true;
|
|
}
|
|
|
|
bool AccAttributeVisitor::Pre(const parser::AccBindClause &x) {
|
|
if (const auto *name{std::get_if<parser::Name>(&x.u)}) {
|
|
if (!ResolveName(*name)) {
|
|
context_.Say(name->source,
|
|
"No function or subroutine declared for '%s'"_err_en_US,
|
|
name->source);
|
|
}
|
|
}
|
|
return true;
|
|
}
|
|
|
|
bool AccAttributeVisitor::Pre(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:
|
|
PushContext(combinedDir.source, combinedDir.v);
|
|
break;
|
|
default:
|
|
break;
|
|
}
|
|
ClearDataSharingAttributeObjects();
|
|
return true;
|
|
}
|
|
|
|
static bool IsLastNameArray(const parser::Designator &designator) {
|
|
const auto &name{GetLastName(designator)};
|
|
const evaluate::DataRef dataRef{*(name.symbol)};
|
|
return std::visit(
|
|
common::visitors{
|
|
[](const evaluate::SymbolRef &ref) { return ref->Rank() > 0; },
|
|
[](const evaluate::ArrayRef &aref) {
|
|
return aref.base().IsSymbol() ||
|
|
aref.base().GetComponent().base().Rank() == 0;
|
|
},
|
|
[](const auto &) { return false; },
|
|
},
|
|
dataRef.u);
|
|
}
|
|
|
|
void AccAttributeVisitor::AllowOnlyArrayAndSubArray(
|
|
const parser::AccObjectList &objectList) {
|
|
for (const auto &accObject : objectList.v) {
|
|
std::visit(
|
|
common::visitors{
|
|
[&](const parser::Designator &designator) {
|
|
if (!IsLastNameArray(designator)) {
|
|
context_.Say(designator.source,
|
|
"Only array element or subarray are allowed in %s directive"_err_en_US,
|
|
parser::ToUpperCaseLetters(
|
|
llvm::acc::getOpenACCDirectiveName(
|
|
GetContext().directive)
|
|
.str()));
|
|
}
|
|
},
|
|
[&](const auto &name) {
|
|
context_.Say(name.source,
|
|
"Only array element or subarray are allowed in %s directive"_err_en_US,
|
|
parser::ToUpperCaseLetters(
|
|
llvm::acc::getOpenACCDirectiveName(GetContext().directive)
|
|
.str()));
|
|
},
|
|
},
|
|
accObject.u);
|
|
}
|
|
}
|
|
|
|
void AccAttributeVisitor::DoNotAllowAssumedSizedArray(
|
|
const parser::AccObjectList &objectList) {
|
|
for (const auto &accObject : objectList.v) {
|
|
std::visit(
|
|
common::visitors{
|
|
[&](const parser::Designator &designator) {
|
|
const auto &name{GetLastName(designator)};
|
|
if (name.symbol && semantics::IsAssumedSizeArray(*name.symbol)) {
|
|
context_.Say(designator.source,
|
|
"Assumed-size dummy arrays may not appear on the %s "
|
|
"directive"_err_en_US,
|
|
parser::ToUpperCaseLetters(
|
|
llvm::acc::getOpenACCDirectiveName(
|
|
GetContext().directive)
|
|
.str()));
|
|
}
|
|
},
|
|
[&](const auto &name) {
|
|
|
|
},
|
|
},
|
|
accObject.u);
|
|
}
|
|
}
|
|
|
|
bool AccAttributeVisitor::Pre(const parser::OpenACCCacheConstruct &x) {
|
|
const auto &verbatim{std::get<parser::Verbatim>(x.t)};
|
|
PushContext(verbatim.source, llvm::acc::Directive::ACCD_cache);
|
|
ClearDataSharingAttributeObjects();
|
|
|
|
const auto &objectListWithModifier =
|
|
std::get<parser::AccObjectListWithModifier>(x.t);
|
|
const auto &objectList =
|
|
std::get<Fortran::parser::AccObjectList>(objectListWithModifier.t);
|
|
|
|
// 2.10 Cache directive restriction: A var in a cache directive must be a
|
|
// single array element or a simple subarray.
|
|
AllowOnlyArrayAndSubArray(objectList);
|
|
|
|
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<parser::AccClause::Collapse>(&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<std::optional<parser::DoConstruct>>(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<parser::Block>(loop->t)};
|
|
const auto it{block.begin()};
|
|
loop = it != block.end() ? GetDoConstructIf(*it) : nullptr;
|
|
}
|
|
CHECK(level == 0);
|
|
}
|
|
|
|
void AccAttributeVisitor::EnsureAllocatableOrPointer(
|
|
const llvm::acc::Clause clause, const parser::AccObjectList &objectList) {
|
|
for (const auto &accObject : objectList.v) {
|
|
std::visit(
|
|
common::visitors{
|
|
[&](const parser::Designator &designator) {
|
|
const auto &lastName{GetLastName(designator)};
|
|
if (!IsAllocatableOrPointer(*lastName.symbol)) {
|
|
context_.Say(designator.source,
|
|
"Argument `%s` on the %s clause must be a variable or "
|
|
"array with the POINTER or ALLOCATABLE attribute"_err_en_US,
|
|
lastName.symbol->name(),
|
|
parser::ToUpperCaseLetters(
|
|
llvm::acc::getOpenACCClauseName(clause).str()));
|
|
}
|
|
},
|
|
[&](const auto &name) {
|
|
context_.Say(name.source,
|
|
"Argument on the %s clause must be a variable or "
|
|
"array with the POINTER or ALLOCATABLE attribute"_err_en_US,
|
|
parser::ToUpperCaseLetters(
|
|
llvm::acc::getOpenACCClauseName(clause).str()));
|
|
},
|
|
},
|
|
accObject.u);
|
|
}
|
|
}
|
|
|
|
bool AccAttributeVisitor::Pre(const parser::AccClause::Attach &x) {
|
|
// Restriction - line 1708-1709
|
|
EnsureAllocatableOrPointer(llvm::acc::Clause::ACCC_attach, x.v);
|
|
return true;
|
|
}
|
|
|
|
bool AccAttributeVisitor::Pre(const parser::AccClause::Detach &x) {
|
|
// Restriction - line 1715-1717
|
|
EnsureAllocatableOrPointer(llvm::acc::Clause::ACCC_detach, x.v);
|
|
return true;
|
|
}
|
|
|
|
void AccAttributeVisitor::Post(const parser::AccDefaultClause &x) {
|
|
if (!dirContext_.empty()) {
|
|
switch (x.v) {
|
|
case llvm::acc::DefaultValue::ACC_Default_present:
|
|
SetContextDefaultDSA(Symbol::Flag::AccPresent);
|
|
break;
|
|
case llvm::acc::DefaultValue::ACC_Default_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<ProcEntityDetails>() &&
|
|
!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<parser::Substring>(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<CommonBlockDetails>().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<HostAssocDetails>()}) {
|
|
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<parser::OmpBeginBlockDirective>(x.t)};
|
|
const auto &beginDir{std::get<parser::OmpBlockDirective>(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_taskgroup:
|
|
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();
|
|
ClearPrivateDataSharingAttributeObjects();
|
|
ClearAllocateNames();
|
|
return true;
|
|
}
|
|
|
|
void OmpAttributeVisitor::Post(const parser::OpenMPBlockConstruct &x) {
|
|
const auto &beginBlockDir{std::get<parser::OmpBeginBlockDirective>(x.t)};
|
|
const auto &beginDir{std::get<parser::OmpBlockDirective>(beginBlockDir.t)};
|
|
switch (beginDir.v) {
|
|
case llvm::omp::Directive::OMPD_parallel:
|
|
case llvm::omp::Directive::OMPD_single:
|
|
case llvm::omp::Directive::OMPD_target:
|
|
case llvm::omp::Directive::OMPD_task:
|
|
case llvm::omp::Directive::OMPD_teams:
|
|
case llvm::omp::Directive::OMPD_parallel_workshare:
|
|
case llvm::omp::Directive::OMPD_target_teams:
|
|
case llvm::omp::Directive::OMPD_target_parallel: {
|
|
bool hasPrivate;
|
|
for (const auto *allocName : allocateNames_) {
|
|
hasPrivate = false;
|
|
for (auto privateObj : privateDataSharingAttributeObjects_) {
|
|
const Symbol &symbolPrivate{*privateObj};
|
|
if (allocName->source == symbolPrivate.name()) {
|
|
hasPrivate = true;
|
|
break;
|
|
}
|
|
}
|
|
if (!hasPrivate) {
|
|
context_.Say(allocName->source,
|
|
"The ALLOCATE clause requires that '%s' must be listed in a "
|
|
"private "
|
|
"data-sharing attribute clause on the same directive"_err_en_US,
|
|
allocName->ToString());
|
|
}
|
|
}
|
|
break;
|
|
}
|
|
default:
|
|
break;
|
|
}
|
|
PopContext();
|
|
}
|
|
|
|
bool OmpAttributeVisitor::Pre(
|
|
const parser::OpenMPSimpleStandaloneConstruct &x) {
|
|
const auto &standaloneDir{
|
|
std::get<parser::OmpSimpleStandaloneDirective>(x.t)};
|
|
switch (standaloneDir.v) {
|
|
case llvm::omp::Directive::OMPD_barrier:
|
|
case llvm::omp::Directive::OMPD_ordered:
|
|
case llvm::omp::Directive::OMPD_target_enter_data:
|
|
case llvm::omp::Directive::OMPD_target_exit_data:
|
|
case llvm::omp::Directive::OMPD_target_update:
|
|
case llvm::omp::Directive::OMPD_taskwait:
|
|
case llvm::omp::Directive::OMPD_taskyield:
|
|
PushContext(standaloneDir.source, standaloneDir.v);
|
|
break;
|
|
default:
|
|
break;
|
|
}
|
|
ClearDataSharingAttributeObjects();
|
|
return true;
|
|
}
|
|
|
|
bool OmpAttributeVisitor::Pre(const parser::OpenMPLoopConstruct &x) {
|
|
const auto &beginLoopDir{std::get<parser::OmpBeginLoopDirective>(x.t)};
|
|
const auto &beginDir{std::get<parser::OmpLoopDirective>(beginLoopDir.t)};
|
|
const auto &clauseList{std::get<parser::OmpClauseList>(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));
|
|
|
|
if (beginDir.v == llvm::omp::Directive::OMPD_do) {
|
|
if (const auto &doConstruct{
|
|
std::get<std::optional<parser::DoConstruct>>(x.t)}) {
|
|
if (doConstruct.value().IsDoWhile()) {
|
|
return true;
|
|
}
|
|
}
|
|
}
|
|
PrivatizeAssociatedLoopIndexAndCheckLoopLevel(x);
|
|
ordCollapseLevel = GetAssociatedLoopLevelFromClauses(clauseList) + 1;
|
|
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);
|
|
}
|
|
}
|
|
}
|
|
|
|
// [OMP-4.5]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
|
|
// Loop iteration variables are not well defined for DO WHILE loop.
|
|
// Use of DO CONCURRENT inside OpenMP construct is unspecified behavior
|
|
// till OpenMP-5.0 standard.
|
|
// In above both cases we skip the privatization of iteration variables.
|
|
bool OmpAttributeVisitor::Pre(const parser::DoConstruct &x) {
|
|
// TODO:[OpenMP 5.1] DO CONCURRENT indices are private
|
|
if (x.IsDoNormal()) {
|
|
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
|
|
}
|
|
ordCollapseLevel--;
|
|
if (ordCollapseLevel) {
|
|
if (const auto *details{iv.symbol->detailsIf<HostAssocDetails>()}) {
|
|
const Symbol *tpSymbol = &details->symbol();
|
|
if (tpSymbol->test(Symbol::Flag::OmpThreadprivate)) {
|
|
context_.Say(iv.source,
|
|
"Loop iteration variable %s is not allowed in THREADPRIVATE."_err_en_US,
|
|
iv.ToString());
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
return true;
|
|
}
|
|
|
|
std::int64_t OmpAttributeVisitor::GetAssociatedLoopLevelFromClauses(
|
|
const parser::OmpClauseList &x) {
|
|
std::int64_t orderedLevel{0};
|
|
std::int64_t collapseLevel{0};
|
|
|
|
const parser::OmpClause *ordClause{nullptr};
|
|
const parser::OmpClause *collClause{nullptr};
|
|
|
|
for (const auto &clause : x.v) {
|
|
if (const auto *orderedClause{
|
|
std::get_if<parser::OmpClause::Ordered>(&clause.u)}) {
|
|
if (const auto v{EvaluateInt64(context_, orderedClause->v)}) {
|
|
orderedLevel = *v;
|
|
}
|
|
ordClause = &clause;
|
|
}
|
|
if (const auto *collapseClause{
|
|
std::get_if<parser::OmpClause::Collapse>(&clause.u)}) {
|
|
if (const auto v{EvaluateInt64(context_, collapseClause->v)}) {
|
|
collapseLevel = *v;
|
|
}
|
|
collClause = &clause;
|
|
}
|
|
}
|
|
|
|
if (orderedLevel && (!collapseLevel || orderedLevel >= collapseLevel)) {
|
|
SetAssociatedClause(*ordClause);
|
|
return orderedLevel;
|
|
} else if (!orderedLevel && collapseLevel) {
|
|
SetAssociatedClause(*collClause);
|
|
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.
|
|
void OmpAttributeVisitor::PrivatizeAssociatedLoopIndexAndCheckLoopLevel(
|
|
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<std::optional<parser::DoConstruct>>(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<parser::Block>(loop->t)};
|
|
const auto it{block.begin()};
|
|
loop = it != block.end() ? GetDoConstructIf(*it) : nullptr;
|
|
}
|
|
CheckAssocLoopLevel(level, GetAssociatedClause());
|
|
}
|
|
void OmpAttributeVisitor::CheckAssocLoopLevel(
|
|
std::int64_t level, const parser::OmpClause *clause) {
|
|
if (clause && level != 0) {
|
|
context_.Say(clause->source,
|
|
"The value of the parameter in the COLLAPSE or ORDERED clause must"
|
|
" not be larger than the number of nested loops"
|
|
" following the construct."_err_en_US);
|
|
}
|
|
}
|
|
|
|
bool OmpAttributeVisitor::Pre(const parser::OpenMPSectionsConstruct &x) {
|
|
const auto &beginSectionsDir{
|
|
std::get<parser::OmpBeginSectionsDirective>(x.t)};
|
|
const auto &beginDir{
|
|
std::get<parser::OmpSectionsDirective>(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::OmpCriticalDirective &x) {
|
|
const auto &name{std::get<std::optional<parser::Name>>(x.t)};
|
|
if (name) {
|
|
ResolveOmpName(*name, Symbol::Flag::OmpCriticalLock);
|
|
}
|
|
return true;
|
|
}
|
|
|
|
bool OmpAttributeVisitor::Pre(const parser::OmpEndCriticalDirective &x) {
|
|
const auto &name{std::get<std::optional<parser::Name>>(x.t)};
|
|
if (name) {
|
|
ResolveOmpName(*name, Symbol::Flag::OmpCriticalLock);
|
|
}
|
|
return true;
|
|
}
|
|
|
|
bool OmpAttributeVisitor::Pre(const parser::OpenMPCriticalConstruct &x) {
|
|
const auto &criticalDir{std::get<parser::OmpCriticalDirective>(x.t)};
|
|
PushContext(criticalDir.source, llvm::omp::Directive::OMPD_critical);
|
|
return true;
|
|
}
|
|
|
|
bool OmpAttributeVisitor::Pre(const parser::OpenMPThreadprivate &x) {
|
|
PushContext(x.source, llvm::omp::Directive::OMPD_threadprivate);
|
|
const auto &list{std::get<parser::OmpObjectList>(x.t)};
|
|
ResolveOmpObjectList(list, Symbol::Flag::OmpThreadprivate);
|
|
return true;
|
|
}
|
|
|
|
bool OmpAttributeVisitor::Pre(const parser::OpenMPDeclarativeAllocate &x) {
|
|
PushContext(x.source, llvm::omp::Directive::OMPD_allocate);
|
|
const auto &list{std::get<parser::OmpObjectList>(x.t)};
|
|
ResolveOmpObjectList(list, Symbol::Flag::OmpDeclarativeAllocateDirective);
|
|
return false;
|
|
}
|
|
|
|
bool OmpAttributeVisitor::Pre(const parser::OpenMPExecutableAllocate &x) {
|
|
PushContext(x.source, llvm::omp::Directive::OMPD_allocate);
|
|
const auto &list{std::get<std::optional<parser::OmpObjectList>>(x.t)};
|
|
if (list) {
|
|
ResolveOmpObjectList(*list, Symbol::Flag::OmpExecutableAllocateDirective);
|
|
}
|
|
return true;
|
|
}
|
|
|
|
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;
|
|
}
|
|
}
|
|
}
|
|
|
|
bool OmpAttributeVisitor::IsNestedInDirective(llvm::omp::Directive directive) {
|
|
if (dirContext_.size() >= 1) {
|
|
for (std::size_t i = dirContext_.size() - 1; i > 0; --i) {
|
|
if (dirContext_[i - 1].directive == directive) {
|
|
return true;
|
|
}
|
|
}
|
|
}
|
|
return false;
|
|
}
|
|
|
|
void OmpAttributeVisitor::Post(const parser::OpenMPExecutableAllocate &x) {
|
|
bool hasAllocator = false;
|
|
// TODO: Investigate whether searching the clause list can be done with
|
|
// parser::Unwrap instead of the following loop
|
|
const auto &clauseList{std::get<parser::OmpClauseList>(x.t)};
|
|
for (const auto &clause : clauseList.v) {
|
|
if (std::get_if<parser::OmpClause::Allocator>(&clause.u)) {
|
|
hasAllocator = true;
|
|
}
|
|
}
|
|
|
|
if (IsNestedInDirective(llvm::omp::Directive::OMPD_target) && !hasAllocator) {
|
|
// TODO: expand this check to exclude the case when a requires
|
|
// directive with the dynamic_allocators clause is present
|
|
// in the same compilation unit (OMP5.0 2.11.3).
|
|
context_.Say(x.source,
|
|
"ALLOCATE directives that appear in a TARGET region "
|
|
"must specify an allocator clause"_err_en_US);
|
|
}
|
|
PopContext();
|
|
}
|
|
|
|
// 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<ProcEntityDetails>() &&
|
|
!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::ResolveName(const parser::Name *name) {
|
|
if (auto *resolvedSymbol{
|
|
name ? GetContext().scope.FindSymbol(name->source) : nullptr}) {
|
|
name->symbol = resolvedSymbol;
|
|
return resolvedSymbol;
|
|
} else {
|
|
return nullptr;
|
|
}
|
|
}
|
|
|
|
void OmpAttributeVisitor::ResolveOmpName(
|
|
const parser::Name &name, Symbol::Flag ompFlag) {
|
|
if (ResolveName(&name)) {
|
|
if (auto *resolvedSymbol{ResolveOmp(name, ompFlag, currScope())}) {
|
|
if (dataSharingAttributeFlags.test(ompFlag)) {
|
|
AddToContextObjectWithDSA(*resolvedSymbol, ompFlag);
|
|
}
|
|
}
|
|
} else if (ompFlagsRequireNewSymbol.test(ompFlag)) {
|
|
const auto pair{GetContext().scope.try_emplace(
|
|
name.source, Attrs{}, ObjectEntityDetails{})};
|
|
CHECK(pair.second);
|
|
name.symbol = &pair.first->second.get();
|
|
} else {
|
|
DIE("OpenMP Name resolution failed");
|
|
}
|
|
}
|
|
|
|
void OmpAttributeVisitor::ResolveOmpNameList(
|
|
const std::list<parser::Name> &nameList, Symbol::Flag ompFlag) {
|
|
for (const auto &name : nameList) {
|
|
ResolveOmpName(name, ompFlag);
|
|
}
|
|
}
|
|
|
|
Symbol *OmpAttributeVisitor::ResolveOmpCommonBlockName(
|
|
const parser::Name *name) {
|
|
if (auto *prev{name
|
|
? GetContext().scope.parent().FindCommonBlock(name->source)
|
|
: nullptr}) {
|
|
name->symbol = prev;
|
|
return prev;
|
|
}
|
|
// Check if the Common Block is declared in the current scope
|
|
if (auto *commonBlockSymbol{
|
|
name ? GetContext().scope.FindCommonBlock(name->source) : nullptr}) {
|
|
name->symbol = commonBlockSymbol;
|
|
return commonBlockSymbol;
|
|
}
|
|
return nullptr;
|
|
}
|
|
|
|
// Use this function over ResolveOmpName when an omp object's scope needs
|
|
// resolving, it's symbol flag isn't important and a simple check for resolution
|
|
// failure is desired. Using ResolveOmpName means needing to work with the
|
|
// context to check for failure, whereas here a pointer comparison is all that's
|
|
// needed.
|
|
Symbol *OmpAttributeVisitor::ResolveOmpObjectScope(const parser::Name *name) {
|
|
|
|
// TODO: Investigate whether the following block can be replaced by, or
|
|
// included in, the ResolveOmpName function
|
|
if (auto *prev{name ? GetContext().scope.parent().FindSymbol(name->source)
|
|
: nullptr}) {
|
|
name->symbol = prev;
|
|
return nullptr;
|
|
}
|
|
|
|
// TODO: Investigate whether the following block can be replaced by, or
|
|
// included in, the ResolveOmpName function
|
|
if (auto *ompSymbol{
|
|
name ? GetContext().scope.FindSymbol(name->source) : nullptr}) {
|
|
name->symbol = ompSymbol;
|
|
return ompSymbol;
|
|
}
|
|
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())}) {
|
|
if (dataCopyingAttributeFlags.test(ompFlag)) {
|
|
CheckDataCopyingClause(*name, *symbol, ompFlag);
|
|
} else {
|
|
AddToContextObjectWithDSA(*symbol, ompFlag);
|
|
if (dataSharingAttributeFlags.test(ompFlag)) {
|
|
CheckMultipleAppearances(*name, *symbol, ompFlag);
|
|
}
|
|
if (privateDataSharingAttributeFlags.test(ompFlag)) {
|
|
CheckPrivateDSAObject(*name, *symbol, ompFlag);
|
|
}
|
|
|
|
if (ompFlag == Symbol::Flag::OmpAllocate) {
|
|
AddAllocateName(name);
|
|
}
|
|
}
|
|
if (ompFlag == Symbol::Flag::OmpDeclarativeAllocateDirective &&
|
|
IsAllocatable(*symbol)) {
|
|
context_.Say(designator.source,
|
|
"List items specified in the ALLOCATE directive must not "
|
|
"have the ALLOCATABLE attribute unless the directive is "
|
|
"associated with an ALLOCATE statement"_err_en_US);
|
|
}
|
|
if ((ompFlag == Symbol::Flag::OmpDeclarativeAllocateDirective ||
|
|
ompFlag ==
|
|
Symbol::Flag::OmpExecutableAllocateDirective) &&
|
|
ResolveOmpObjectScope(name) == nullptr) {
|
|
context_.Say(designator.source, // 2.15.3
|
|
"List items must be declared in the same scoping unit "
|
|
"in which the ALLOCATE directive appears"_err_en_US);
|
|
}
|
|
}
|
|
} else {
|
|
// Array sections to be changed to substrings as needed
|
|
if (AnalyzeExpr(context_, designator)) {
|
|
if (std::holds_alternative<parser::Substring>(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)}) {
|
|
if (!dataCopyingAttributeFlags.test(ompFlag)) {
|
|
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<CommonBlockDetails>().objects()) {
|
|
if (auto *resolvedObject{
|
|
ResolveOmp(*object, ompFlag, currScope())}) {
|
|
if (dataCopyingAttributeFlags.test(ompFlag)) {
|
|
CheckDataCopyingClause(name, *resolvedObject, ompFlag);
|
|
} else {
|
|
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<HostAssocDetails>()}) {
|
|
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);
|
|
if (privateDataSharingAttributeFlags.test(ompFlag)) {
|
|
AddPrivateDataSharingAttributeObjects(*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);
|
|
}
|
|
}
|
|
}
|
|
|
|
void OmpAttributeVisitor::CheckDataCopyingClause(
|
|
const parser::Name &name, const Symbol &symbol, Symbol::Flag ompFlag) {
|
|
const auto *checkSymbol{&symbol};
|
|
if (const auto *details{symbol.detailsIf<HostAssocDetails>()}) {
|
|
checkSymbol = &details->symbol();
|
|
}
|
|
|
|
if (ompFlag == Symbol::Flag::OmpCopyIn) {
|
|
// List of items/objects that can appear in a 'copyin' clause must be
|
|
// 'threadprivate'
|
|
if (!checkSymbol->test(Symbol::Flag::OmpThreadprivate)) {
|
|
context_.Say(name.source,
|
|
"Non-THREADPRIVATE object '%s' in COPYIN clause"_err_en_US,
|
|
checkSymbol->name());
|
|
}
|
|
} else if (ompFlag == Symbol::Flag::OmpCopyPrivate &&
|
|
GetContext().directive == llvm::omp::Directive::OMPD_single) {
|
|
// A list item that appears in a 'copyprivate' clause may not appear on a
|
|
// 'private' or 'firstprivate' clause on a single construct
|
|
if (IsObjectWithDSA(symbol) &&
|
|
(symbol.test(Symbol::Flag::OmpPrivate) ||
|
|
symbol.test(Symbol::Flag::OmpFirstPrivate))) {
|
|
context_.Say(name.source,
|
|
"COPYPRIVATE variable '%s' may not appear on a PRIVATE or "
|
|
"FIRSTPRIVATE clause on a SINGLE construct"_err_en_US,
|
|
symbol.name());
|
|
} else {
|
|
// List of items/objects that can appear in a 'copyprivate' clause must be
|
|
// either 'private' or 'threadprivate' in enclosing context.
|
|
if (!checkSymbol->test(Symbol::Flag::OmpThreadprivate) &&
|
|
!(HasSymbolInEnclosingScope(symbol, currScope()) &&
|
|
symbol.test(Symbol::Flag::OmpPrivate))) {
|
|
context_.Say(name.source,
|
|
"COPYPRIVATE variable '%s' is not PRIVATE or THREADPRIVATE in "
|
|
"outer context"_err_en_US,
|
|
symbol.name());
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
void OmpAttributeVisitor::CheckPrivateDSAObject(
|
|
const parser::Name &name, const Symbol &symbol, Symbol::Flag ompFlag) {
|
|
const auto &ultimateSymbol{symbol.GetUltimate()};
|
|
llvm::StringRef clauseName{"PRIVATE"};
|
|
if (ompFlag == Symbol::Flag::OmpFirstPrivate) {
|
|
clauseName = "FIRSTPRIVATE";
|
|
} else if (ompFlag == Symbol::Flag::OmpLastPrivate) {
|
|
clauseName = "LASTPRIVATE";
|
|
}
|
|
|
|
if (ultimateSymbol.test(Symbol::Flag::InNamelist)) {
|
|
context_.Say(name.source,
|
|
"Variable '%s' in NAMELIST cannot be in a %s clause"_err_en_US,
|
|
name.ToString(), clauseName.str());
|
|
}
|
|
|
|
if (stmtFunctionExprSymbols_.find(ultimateSymbol) !=
|
|
stmtFunctionExprSymbols_.end()) {
|
|
context_.Say(name.source,
|
|
"Variable '%s' in STATEMENT FUNCTION expression cannot be in a "
|
|
"%s clause"_err_en_US,
|
|
name.ToString(), clauseName.str());
|
|
}
|
|
}
|
|
|
|
void OmpAttributeVisitor::CheckSourceLabel(const parser::Label &label) {
|
|
// Get the context to check if the statement causing a jump to the 'label' is
|
|
// in an enclosing OpenMP construct
|
|
std::optional<DirContext> thisContext{GetContextIf()};
|
|
sourceLabels_.emplace(
|
|
label, std::make_pair(currentStatementSource_, thisContext));
|
|
// Check if the statement with 'label' to which a jump is being introduced
|
|
// has already been encountered
|
|
auto it{targetLabels_.find(label)};
|
|
if (it != targetLabels_.end()) {
|
|
// Check if both the statement with 'label' and the statement that causes a
|
|
// jump to the 'label' are in the same scope
|
|
CheckLabelContext(currentStatementSource_, it->second.first, thisContext,
|
|
it->second.second);
|
|
}
|
|
}
|
|
|
|
// Check for invalid branch into or out of OpenMP structured blocks
|
|
void OmpAttributeVisitor::CheckLabelContext(const parser::CharBlock source,
|
|
const parser::CharBlock target, std::optional<DirContext> sourceContext,
|
|
std::optional<DirContext> targetContext) {
|
|
if (targetContext &&
|
|
(!sourceContext ||
|
|
(sourceContext->scope != targetContext->scope &&
|
|
!DoesScopeContain(
|
|
&targetContext->scope, sourceContext->scope)))) {
|
|
context_
|
|
.Say(source, "invalid branch into an OpenMP structured block"_err_en_US)
|
|
.Attach(target, "In the enclosing %s directive branched into"_en_US,
|
|
parser::ToUpperCaseLetters(
|
|
llvm::omp::getOpenMPDirectiveName(targetContext->directive)
|
|
.str()));
|
|
}
|
|
if (sourceContext &&
|
|
(!targetContext ||
|
|
(sourceContext->scope != targetContext->scope &&
|
|
!DoesScopeContain(
|
|
&sourceContext->scope, targetContext->scope)))) {
|
|
context_
|
|
.Say(source,
|
|
"invalid branch leaving an OpenMP structured block"_err_en_US)
|
|
.Attach(target, "Outside the enclosing %s directive"_en_US,
|
|
parser::ToUpperCaseLetters(
|
|
llvm::omp::getOpenMPDirectiveName(sourceContext->directive)
|
|
.str()));
|
|
}
|
|
}
|
|
|
|
bool OmpAttributeVisitor::HasSymbolInEnclosingScope(
|
|
const Symbol &symbol, Scope &scope) {
|
|
const auto symbols{scope.parent().GetSymbols()};
|
|
auto it{std::find(symbols.begin(), symbols.end(), symbol)};
|
|
return it != symbols.end();
|
|
}
|
|
|
|
} // namespace Fortran::semantics
|