-
Notifications
You must be signed in to change notification settings - Fork 15.1k
[Flang] Add perfect-nest and rectangular-loop semantic tests #160283
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: main
Are you sure you want to change the base?
Conversation
@llvm/pr-subscribers-flang-semantics @llvm/pr-subscribers-flang-openmp Author: Michael Kruse (Meinersbur) ChangesAdd semantic tests of currently unsupported OpenMP canonical loops:
The message "Trip count must be computable and invariant" is the same that OpenACC emits for non-rectangular loops in Full diff: https://github.com/llvm/llvm-project/pull/160283.diff 4 Files Affected:
diff --git a/flang/lib/Semantics/resolve-directives.cpp b/flang/lib/Semantics/resolve-directives.cpp
index 2d1bec9968593..a0109324e546c 100644
--- a/flang/lib/Semantics/resolve-directives.cpp
+++ b/flang/lib/Semantics/resolve-directives.cpp
@@ -149,6 +149,9 @@ template <typename T> class DirectiveAttributeVisitor {
dataSharingAttributeObjects_.clear();
}
bool HasDataSharingAttributeObject(const Symbol &);
+ std::tuple<const parser::Name *, const parser::ScalarExpr *,
+ const parser::ScalarExpr *, const parser::ScalarExpr *>
+ GetLoopBounds(const parser::DoConstruct &);
const parser::Name *GetLoopIndex(const parser::DoConstruct &);
const parser::DoConstruct *GetDoConstructIf(
const parser::ExecutionPartConstruct &);
@@ -933,6 +936,13 @@ class OmpAttributeVisitor : DirectiveAttributeVisitor<llvm::omp::Directive> {
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 &);
@@ -1009,14 +1019,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_
@@ -1024,8 +1035,15 @@ 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) {
+ auto &&[iv, lb, ub, step] = GetLoopBounds(x);
+ return iv;
}
template <typename T>
@@ -1957,6 +1975,10 @@ bool OmpAttributeVisitor::Pre(const parser::OpenMPLoopConstruct &x) {
}
}
}
+
+ // Must be done before iv privatization
+ CheckPerfectNestAndRectangularLoop(x);
+
PrivatizeAssociatedLoopIndexAndCheckLoopLevel(x);
ordCollapseLevel = GetNumAffectedLoopsFromLoopConstruct(x) + 1;
return true;
@@ -2152,6 +2174,114 @@ 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);
+ }
+ }
+ };
+
+ // Skip over loop transformation directives
+ const parser::OpenMPLoopConstruct *innerMostLoop = &x;
+ const parser::NestedConstruct *innerMostNest = nullptr;
+ while (auto &optLoopCons{
+ std::get<std::optional<parser::NestedConstruct>>(innerMostLoop->t)}) {
+ innerMostNest = &(optLoopCons.value());
+ if (const auto *innerLoop{
+ std::get_if<common::Indirection<parser::OpenMPLoopConstruct>>(
+ innerMostNest)}) {
+ innerMostLoop = &(innerLoop->value());
+ } else {
+ break;
+ }
+ }
+
+ if (!innerMostNest)
+ return;
+ const auto &outer{std::get_if<parser::DoConstruct>(innerMostNest)};
+ 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.
diff --git a/flang/test/Semantics/OpenMP/do08.f90 b/flang/test/Semantics/OpenMP/do08.f90
index 5143dff0dd315..bb3c1d0cd3855 100644
--- a/flang/test/Semantics/OpenMP/do08.f90
+++ b/flang/test/Semantics/OpenMP/do08.f90
@@ -61,6 +61,7 @@ program omp
!$omp end do
+ !ERROR: Canonical loop nest must be perfectly nested.
!ERROR: The value of the parameter in the COLLAPSE or ORDERED clause must not be larger than the number of nested loops following the construct.
!$omp do collapse(3)
do 60 i=2,200,2
diff --git a/flang/test/Semantics/OpenMP/do13.f90 b/flang/test/Semantics/OpenMP/do13.f90
index 6e9d1dddade4c..8f7844f4136f9 100644
--- a/flang/test/Semantics/OpenMP/do13.f90
+++ b/flang/test/Semantics/OpenMP/do13.f90
@@ -59,6 +59,7 @@ program omp
!$omp end do
+ !ERROR: Canonical loop nest must be perfectly nested.
!ERROR: The value of the parameter in the COLLAPSE or ORDERED clause must not be larger than the number of nested loops following the construct.
!$omp do collapse(3)
do 60 i=1,10
diff --git a/flang/test/Semantics/OpenMP/do22.f90 b/flang/test/Semantics/OpenMP/do22.f90
new file mode 100644
index 0000000000000..9d96d3af54e5c
--- /dev/null
+++ b/flang/test/Semantics/OpenMP/do22.f90
@@ -0,0 +1,73 @@
+! RUN: %python %S/../test_errors.py %s %flang -fopenmp
+! Check for existence of loop following a DO directive
+
+subroutine do_imperfectly_nested_before
+ integer i, j
+
+ !ERROR: The value of the parameter in the COLLAPSE or ORDERED clause must not be larger than the number of nested loops following the construct.
+ !$omp do collapse(2)
+ do i = 1, 10
+ print *, i
+ do j = 1, 10
+ print *, i, j
+ end do
+ end do
+ !$omp end do
+end subroutine
+
+
+subroutine do_imperfectly_nested_behind
+ integer i, j
+
+ !ERROR: Canonical loop nest must be perfectly nested.
+ !$omp do collapse(2)
+ do i = 1, 10
+ do j = 1, 10
+ print *, i, j
+ end do
+ print *, i
+ end do
+ !$omp end do
+end subroutine
+
+
+subroutine do_nonrectangular_lb
+ integer i, j
+
+ !ERROR: Trip count must be computable and invariant
+ !$omp do collapse(2)
+ do i = 1, 10
+ do j = i, 10
+ print *, i, j
+ end do
+ end do
+ !$omp end do
+end subroutine
+
+
+subroutine do_nonrectangular_ub
+ integer i, j
+
+ !ERROR: Trip count must be computable and invariant
+ !$omp do collapse(2)
+ do i = 1, 10
+ do j = 0, i
+ print *, i, j
+ end do
+ end do
+ !$omp end do
+end subroutine
+
+
+subroutine do_nonrectangular_step
+ integer i, j
+
+ !ERROR: Trip count must be computable and invariant
+ !$omp do collapse(2)
+ do i = 1, 10
+ do j = 1, 10, i
+ print *, i, j
+ end do
+ end do
+ !$omp end do
+end subroutine
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
LGTM with some nits
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Thanks for the update. Just one more trivial change.
const parser::OpenMPLoopConstruct *innerMostLoop = &x; | ||
const parser::NestedConstruct *innerMostNest = nullptr; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
braced init
Add semantic tests of currently unsupported OpenMP canonical loops:
The message "Trip count must be computable and invariant" is the same that OpenACC emits for non-rectangular loops in
AccAttributeVisitor::CheckAssociatedLoop
. I considered reusing the code, but calls OpenACC-only methods and has different behavior (e.g. symbol resolution and does not check the step operand)