aboutsummaryrefslogtreecommitdiff
path: root/flang/lib/Semantics/resolve-directives.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'flang/lib/Semantics/resolve-directives.cpp')
-rw-r--r--flang/lib/Semantics/resolve-directives.cpp275
1 files changed, 151 insertions, 124 deletions
diff --git a/flang/lib/Semantics/resolve-directives.cpp b/flang/lib/Semantics/resolve-directives.cpp
index 270642a..bd7b8ac 100644
--- a/flang/lib/Semantics/resolve-directives.cpp
+++ b/flang/lib/Semantics/resolve-directives.cpp
@@ -149,7 +149,24 @@ protected:
dataSharingAttributeObjects_.clear();
}
bool HasDataSharingAttributeObject(const Symbol &);
+
+ /// Extract the iv and bounds of a DO loop:
+ /// 1. The loop index/induction variable
+ /// 2. The lower bound
+ /// 3. The upper bound
+ /// 4. The step/increment (or nullptr if not present)
+ ///
+ /// Each returned tuple value can be nullptr if not present. Diagnoses an
+ /// error if the the DO loop is a DO WHILE or DO CONCURRENT loop.
+ std::tuple<const parser::Name *, const parser::ScalarExpr *,
+ const parser::ScalarExpr *, const parser::ScalarExpr *>
+ GetLoopBounds(const parser::DoConstruct &);
+
+ /// Extract the loop index/induction variable from a DO loop. Diagnoses an
+ /// error if the the DO loop is a DO WHILE or DO CONCURRENT loop and returns
+ /// nullptr.
const parser::Name *GetLoopIndex(const parser::DoConstruct &);
+
const parser::DoConstruct *GetDoConstructIf(
const parser::ExecutionPartConstruct &);
Symbol *DeclareNewAccessEntity(const Symbol &, Symbol::Flag, Scope &);
@@ -953,6 +970,13 @@ private:
privateDataSharingAttributeObjects_.clear();
}
+ /// Check that loops in the loop nest are perfectly nested, as well that lower
+ /// bound, upper bound, and step expressions do not use the iv
+ /// of a surrounding loop of the associated loops nest.
+ /// We do not support non-perfectly nested loops not non-rectangular loops yet
+ /// (both introduced in OpenMP 5.0)
+ void CheckPerfectNestAndRectangularLoop(const parser::OpenMPLoopConstruct &x);
+
// Predetermined DSA rules
void PrivatizeAssociatedLoopIndexAndCheckLoopLevel(
const parser::OpenMPLoopConstruct &);
@@ -987,11 +1011,6 @@ private:
sourceLabels_.clear();
targetLabels_.clear();
};
- void CheckAllNamesInAllocateStmt(const parser::CharBlock &source,
- const parser::OmpObjectList &ompObjectList,
- const parser::AllocateStmt &allocate);
- void CheckNameInAllocateStmt(const parser::CharBlock &source,
- const parser::Name &ompObject, const parser::AllocateStmt &allocate);
std::int64_t ordCollapseLevel{0};
@@ -1028,14 +1047,15 @@ bool DirectiveAttributeVisitor<T>::HasDataSharingAttributeObject(
}
template <typename T>
-const parser::Name *DirectiveAttributeVisitor<T>::GetLoopIndex(
- const parser::DoConstruct &x) {
+std::tuple<const parser::Name *, const parser::ScalarExpr *,
+ const parser::ScalarExpr *, const parser::ScalarExpr *>
+DirectiveAttributeVisitor<T>::GetLoopBounds(const parser::DoConstruct &x) {
using Bounds = parser::LoopControl::Bounds;
if (x.GetLoopControl()) {
if (const Bounds * b{std::get_if<Bounds>(&x.GetLoopControl()->u)}) {
- return &b->name.thing;
- } else {
- return nullptr;
+ auto &step = b->step;
+ return {&b->name.thing, &b->lower, &b->upper,
+ step.has_value() ? &step.value() : nullptr};
}
} else {
context_
@@ -1043,8 +1063,14 @@ const parser::Name *DirectiveAttributeVisitor<T>::GetLoopIndex(
"Loop control is not present in the DO LOOP"_err_en_US)
.Attach(GetContext().directiveSource,
"associated with the enclosing LOOP construct"_en_US);
- return nullptr;
}
+ return {nullptr, nullptr, nullptr, nullptr};
+}
+
+template <typename T>
+const parser::Name *DirectiveAttributeVisitor<T>::GetLoopIndex(
+ const parser::DoConstruct &x) {
+ return std::get<const parser::Name *>(GetLoopBounds(x));
}
template <typename T>
@@ -1990,6 +2016,10 @@ bool OmpAttributeVisitor::Pre(const parser::OpenMPLoopConstruct &x) {
}
}
}
+
+ // Must be done before iv privatization
+ CheckPerfectNestAndRectangularLoop(x);
+
PrivatizeAssociatedLoopIndexAndCheckLoopLevel(x);
ordCollapseLevel = GetNumAffectedLoopsFromLoopConstruct(x) + 1;
return true;
@@ -2185,6 +2215,116 @@ void OmpAttributeVisitor::CollectNumAffectedLoopsFromClauses(
}
}
+void OmpAttributeVisitor::CheckPerfectNestAndRectangularLoop(
+ const parser::OpenMPLoopConstruct &x) {
+ auto &dirContext{GetContext()};
+ std::int64_t dirDepth{dirContext.associatedLoopLevel};
+ if (dirDepth <= 0)
+ return;
+
+ auto checkExprHasSymbols = [&](llvm::SmallVector<Symbol *> &ivs,
+ const parser::ScalarExpr *bound) {
+ if (ivs.empty())
+ return;
+ auto boundExpr{semantics::AnalyzeExpr(context_, *bound)};
+ if (!boundExpr)
+ return;
+ semantics::UnorderedSymbolSet boundSyms{
+ evaluate::CollectSymbols(*boundExpr)};
+ if (boundSyms.empty())
+ return;
+ for (Symbol *iv : ivs) {
+ if (boundSyms.count(*iv) != 0) {
+ // TODO: Point to occurence of iv in boundExpr, directiveSource as a
+ // note
+ context_.Say(dirContext.directiveSource,
+ "Trip count must be computable and invariant"_err_en_US);
+ }
+ }
+ };
+
+ // Find the associated region by skipping nested loop-associated constructs
+ // such as loop transformations
+ const parser::NestedConstruct *innermostAssocRegion{nullptr};
+ const parser::OpenMPLoopConstruct *innermostConstruct{&x};
+ while (const auto &innerAssocStmt{
+ std::get<std::optional<parser::NestedConstruct>>(
+ innermostConstruct->t)}) {
+ innermostAssocRegion = &(innerAssocStmt.value());
+ if (const auto *innerConstruct{
+ std::get_if<common::Indirection<parser::OpenMPLoopConstruct>>(
+ innermostAssocRegion)}) {
+ innermostConstruct = &innerConstruct->value();
+ } else {
+ break;
+ }
+ }
+
+ if (!innermostAssocRegion)
+ return;
+ const auto &outer{std::get_if<parser::DoConstruct>(innermostAssocRegion)};
+ if (!outer)
+ return;
+
+ llvm::SmallVector<Symbol *> ivs;
+ int curLevel{0};
+ const parser::DoConstruct *loop{outer};
+ while (true) {
+ auto [iv, lb, ub, step] = GetLoopBounds(*loop);
+
+ if (lb)
+ checkExprHasSymbols(ivs, lb);
+ if (ub)
+ checkExprHasSymbols(ivs, ub);
+ if (step)
+ checkExprHasSymbols(ivs, step);
+ if (iv) {
+ if (auto *symbol{currScope().FindSymbol(iv->source)})
+ ivs.push_back(symbol);
+ }
+
+ // Stop after processing all affected loops
+ if (curLevel + 1 >= dirDepth)
+ break;
+
+ // Recurse into nested loop
+ const auto &block{std::get<parser::Block>(loop->t)};
+ if (block.empty()) {
+ // Insufficient number of nested loops already reported by
+ // CheckAssocLoopLevel()
+ break;
+ }
+
+ loop = GetDoConstructIf(block.front());
+ if (!loop) {
+ // Insufficient number of nested loops already reported by
+ // CheckAssocLoopLevel()
+ break;
+ }
+
+ auto checkPerfectNest = [&, this]() {
+ auto blockSize = block.size();
+ if (blockSize <= 1)
+ return;
+
+ if (parser::Unwrap<parser::ContinueStmt>(x))
+ blockSize -= 1;
+
+ if (blockSize <= 1)
+ return;
+
+ // Non-perfectly nested loop
+ // TODO: Point to non-DO statement, directiveSource as a note
+ context_.Say(dirContext.directiveSource,
+ "Canonical loop nest must be perfectly nested."_err_en_US);
+ };
+
+ checkPerfectNest();
+
+ ++curLevel;
+ }
+}
+
// 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.
@@ -2405,8 +2545,6 @@ bool OmpAttributeVisitor::Pre(const parser::OpenMPDispatchConstruct &x) {
}
bool OmpAttributeVisitor::Pre(const parser::OpenMPExecutableAllocate &x) {
- IssueNonConformanceWarning(llvm::omp::Directive::OMPD_allocate, x.source, 52);
-
PushContext(x.source, llvm::omp::Directive::OMPD_allocate);
const auto &list{std::get<std::optional<parser::OmpObjectList>>(x.t)};
if (list) {
@@ -2487,83 +2625,10 @@ bool OmpAttributeVisitor::IsNestedInDirective(llvm::omp::Directive directive) {
}
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);
- }
-
- const auto &allocateStmt =
- std::get<parser::Statement<parser::AllocateStmt>>(x.t).statement;
- if (const auto &list{std::get<std::optional<parser::OmpObjectList>>(x.t)}) {
- CheckAllNamesInAllocateStmt(
- std::get<parser::Verbatim>(x.t).source, *list, allocateStmt);
- }
- if (const auto &subDirs{
- std::get<std::optional<std::list<parser::OpenMPDeclarativeAllocate>>>(
- x.t)}) {
- for (const auto &dalloc : *subDirs) {
- CheckAllNamesInAllocateStmt(std::get<parser::Verbatim>(dalloc.t).source,
- std::get<parser::OmpObjectList>(dalloc.t), allocateStmt);
- }
- }
PopContext();
}
void OmpAttributeVisitor::Post(const parser::OpenMPAllocatorsConstruct &x) {
- const parser::OmpDirectiveSpecification &dirSpec{x.BeginDir()};
- auto &block{std::get<parser::Block>(x.t)};
-
- omp::SourcedActionStmt action{omp::GetActionStmt(block)};
- const parser::AllocateStmt *allocate{[&]() {
- if (action) {
- if (auto *alloc{std::get_if<common::Indirection<parser::AllocateStmt>>(
- &action.stmt->u)}) {
- return &alloc->value();
- }
- }
- return static_cast<const parser::AllocateStmt *>(nullptr);
- }()};
-
- if (allocate) {
- for (const auto &clause : dirSpec.Clauses().v) {
- if (auto *alloc{std::get_if<parser::OmpClause::Allocate>(&clause.u)}) {
- CheckAllNamesInAllocateStmt(
- x.source, std::get<parser::OmpObjectList>(alloc->v.t), *allocate);
-
- using OmpAllocatorSimpleModifier = parser::OmpAllocatorSimpleModifier;
- using OmpAllocatorComplexModifier = parser::OmpAllocatorComplexModifier;
-
- auto &modifiers{OmpGetModifiers(alloc->v)};
- bool hasAllocator{
- OmpGetUniqueModifier<OmpAllocatorSimpleModifier>(modifiers) ||
- OmpGetUniqueModifier<OmpAllocatorComplexModifier>(modifiers)};
-
- // TODO: As with allocate directive, exclude the case when a requires
- // directive with the dynamic_allocators clause is present in
- // the same compilation unit (OMP5.0 2.11.3).
- if (IsNestedInDirective(llvm::omp::Directive::OMPD_target) &&
- !hasAllocator) {
- context_.Say(x.source,
- "ALLOCATORS directives that appear in a TARGET region "
- "must specify an allocator"_err_en_US);
- }
- }
- }
- }
PopContext();
}
@@ -3483,44 +3548,6 @@ void OmpAttributeVisitor::CheckLabelContext(const parser::CharBlock source,
}
}
-// Goes through the names in an OmpObjectList and checks if each name appears
-// in the given allocate statement
-void OmpAttributeVisitor::CheckAllNamesInAllocateStmt(
- const parser::CharBlock &source, const parser::OmpObjectList &ompObjectList,
- const parser::AllocateStmt &allocate) {
- for (const auto &obj : ompObjectList.v) {
- if (const auto *d{std::get_if<parser::Designator>(&obj.u)}) {
- if (const auto *ref{std::get_if<parser::DataRef>(&d->u)}) {
- if (const auto *n{std::get_if<parser::Name>(&ref->u)}) {
- CheckNameInAllocateStmt(source, *n, allocate);
- }
- }
- }
- }
-}
-
-void OmpAttributeVisitor::CheckNameInAllocateStmt(
- const parser::CharBlock &source, const parser::Name &name,
- const parser::AllocateStmt &allocate) {
- for (const auto &allocation :
- std::get<std::list<parser::Allocation>>(allocate.t)) {
- const auto &allocObj = std::get<parser::AllocateObject>(allocation.t);
- if (const auto *n{std::get_if<parser::Name>(&allocObj.u)}) {
- if (n->source == name.source) {
- return;
- }
- }
- }
- unsigned version{context_.langOptions().OpenMPVersion};
- context_.Say(source,
- "Object '%s' in %s directive not "
- "found in corresponding ALLOCATE statement"_err_en_US,
- name.ToString(),
- parser::ToUpperCaseLetters(
- llvm::omp::getOpenMPDirectiveName(GetContext().directive, version)
- .str()));
-}
-
void OmpAttributeVisitor::AddOmpRequiresToScope(Scope &scope,
WithOmpDeclarative::RequiresFlags flags,
std::optional<common::OmpMemoryOrderType> memOrder) {