[llvm-branch-commits] [flang] [flang][OpenMP] Implement LoopSequence class, calculate sequence length (PR #185296)
via llvm-branch-commits
llvm-branch-commits at lists.llvm.org
Sun Mar 8 08:42:16 PDT 2026
llvmbot wrote:
<!--LLVM PR SUMMARY COMMENT-->
@llvm/pr-subscribers-flang-semantics
Author: Krzysztof Parzyszek (kparzysz)
<details>
<summary>Changes</summary>
This is a tree-like structure that can represent nesting of loop sequences, or, if the sequences are of length 1, a loop nest.
---
Full diff: https://github.com/llvm/llvm-project/pull/185296.diff
4 Files Affected:
- (modified) flang/include/flang/Semantics/openmp-utils.h (+49-8)
- (modified) flang/lib/Semantics/check-omp-loop.cpp (+1-37)
- (modified) flang/lib/Semantics/openmp-utils.cpp (+83)
- (modified) flang/test/Semantics/OpenMP/loop-transformation-construct01.f90 (+1)
``````````diff
diff --git a/flang/include/flang/Semantics/openmp-utils.h b/flang/include/flang/Semantics/openmp-utils.h
index 7f6c2824a986a..ac2743445a34e 100644
--- a/flang/include/flang/Semantics/openmp-utils.h
+++ b/flang/include/flang/Semantics/openmp-utils.h
@@ -16,6 +16,7 @@
#include "flang/Common/indirection.h"
#include "flang/Evaluate/type.h"
#include "flang/Parser/char-block.h"
+#include "flang/Parser/openmp-utils.h"
#include "flang/Parser/parse-tree.h"
#include "flang/Parser/tools.h"
#include "flang/Semantics/tools.h"
@@ -27,14 +28,6 @@
#include <type_traits>
#include <utility>
-namespace Fortran::parser::omp {
-struct ExecutionPartIterator;
-struct LoopNestIterator;
-template <typename T> struct ExecutionPartRange;
-using BlockRange = ExecutionPartRange<ExecutionPartIterator>;
-using LoopRange = ExecutionPartRange<LoopNestIterator>;
-} // namespace Fortran::parser::omp
-
namespace Fortran::semantics {
class Scope;
class SemanticsContext;
@@ -46,6 +39,7 @@ using Fortran::parser::omp::ExecutionPartIterator;
using Fortran::parser::omp::LoopNestIterator;
using Fortran::parser::omp::BlockRange;
using Fortran::parser::omp::LoopRange;
+using Fortran::parser::omp::is_range_v;
template <typename T, typename U = std::remove_const_t<T>> U AsRvalue(T &t) {
return U(t);
@@ -121,6 +115,53 @@ bool IsFullUnroll(const parser::OpenMPLoopConstruct &x);
std::optional<int64_t> GetNumGeneratedNestsFrom(
const parser::ExecutionPartConstruct &epc,
std::optional<int64_t> nestedCount);
+
+struct LoopSequence {
+ LoopSequence(
+ const parser::ExecutionPartConstruct &root, bool allowAllLoops = false);
+
+ template <typename R, typename = std::enable_if_t<is_range_v<R>>>
+ LoopSequence(const R &range, bool allowAllLoops = false)
+ : allowAllLoops_(allowAllLoops) {
+ entry_ = std::make_unique<Construct>(range, nullptr);
+ createChildrenFromRange(entry_->location);
+ length_ = calculateLength();
+ }
+
+ bool isNest() const { return length_ && *length_ == 1; }
+ std::optional<int64_t> length() const { return length_; }
+ const std::vector<LoopSequence> &children() const { return children_; }
+
+private:
+ using Construct = ExecutionPartIterator::Construct;
+
+ LoopSequence(std::unique_ptr<Construct> entry, bool allowAllLoops);
+
+ template <typename R, typename = std::enable_if_t<is_range_v<R>>>
+ void createChildrenFromRange(const R &range) {
+ createChildrenFromRange(range.begin(), range.end());
+ }
+
+ std::unique_ptr<Construct> createConstructEntry(
+ const parser::ExecutionPartConstruct &code);
+
+ void createChildrenFromRange( //
+ ExecutionPartIterator::IteratorType begin,
+ ExecutionPartIterator::IteratorType end);
+
+ std::optional<int64_t> calculateLength() const;
+ std::optional<int64_t> sumOfChildrenLengths() const;
+
+ // Precalculated length of the sequence. Note that this is different from
+ // the number of children because a child may result in a sequence, for
+ // example a fuse with a reduced loop range. The length of that sequence
+ // adds to the length of the owning LoopSequence.
+ std::optional<int64_t> length_;
+ // The core structure of the class:
+ bool allowAllLoops_;
+ std::unique_ptr<Construct> entry_;
+ std::vector<LoopSequence> children_;
+};
} // namespace omp
} // namespace Fortran::semantics
diff --git a/flang/lib/Semantics/check-omp-loop.cpp b/flang/lib/Semantics/check-omp-loop.cpp
index 45f4798d0c3c6..6c0d6afed8696 100644
--- a/flang/lib/Semantics/check-omp-loop.cpp
+++ b/flang/lib/Semantics/check-omp-loop.cpp
@@ -38,8 +38,6 @@
#include <variant>
namespace Fortran::semantics {
-static std::optional<int64_t> GetNumGeneratedNests(
- const parser::ExecutionPartConstruct &epc);
static std::optional<int64_t> GetNumGeneratedNests(const parser::Block &block);
} // namespace Fortran::semantics
@@ -246,31 +244,6 @@ void OmpStructureChecker::CheckSIMDNest(const parser::OpenMPConstruct &c) {
}
}
-// Count the number of loop nests generated by `epc`. This is just a helper
-// function for counting the number of loop nests in a parser::Block.
-static std::optional<int64_t> GetNumGeneratedNests(
- const parser::ExecutionPartConstruct &epc) {
- if (parser::Unwrap<parser::DoConstruct>(epc)) {
- return 1;
- }
-
- auto &omp{DEREF(parser::Unwrap<parser::OpenMPLoopConstruct>(epc))};
- const parser::OmpDirectiveSpecification &beginSpec{omp.BeginDir()};
- llvm::omp::Directive dir{beginSpec.DirName().v};
-
- switch (dir) {
- case llvm::omp::Directive::OMPD_fuse:
- case llvm::omp::Directive::OMPD_nothing:
- return GetNumGeneratedNestsFrom(
- epc, GetNumGeneratedNests(std::get<parser::Block>(omp.t)));
- default:
- break;
- }
-
- // For every other loop construct return 1.
- return 1;
-}
-
static std::optional<int64_t> GetNumGeneratedNests(const parser::Block &block) {
// Count the number of loops in the associated block. If there are any
// malformed construct in there, getting the number may be meaningless.
@@ -278,16 +251,7 @@ static std::optional<int64_t> GetNumGeneratedNests(const parser::Block &block) {
// messages about a potentially incorrect loop count.
// In such cases reset the count to nullopt. Once it becomes nullopt,
// keep it that way.
- std::optional<int64_t> numLoops{0};
- for (auto &epc : LoopRange(block, LoopRange::Step::Over)) {
- if (auto genCount{GetNumGeneratedNests(epc)}) {
- *numLoops += *genCount;
- } else {
- numLoops = std::nullopt;
- break;
- }
- }
- return numLoops;
+ return LoopSequence(block, true).length();
}
void OmpStructureChecker::CheckNestedConstruct(
diff --git a/flang/lib/Semantics/openmp-utils.cpp b/flang/lib/Semantics/openmp-utils.cpp
index 0b50160053012..1757dafdd3b3d 100644
--- a/flang/lib/Semantics/openmp-utils.cpp
+++ b/flang/lib/Semantics/openmp-utils.cpp
@@ -694,6 +694,24 @@ bool IsTransparentInterveningCode(const parser::ExecutionPartConstruct &x) {
parser::Unwrap<parser::ContinueStmt>(x);
}
+bool IsTransformableLoop(const parser::DoConstruct &loop) {
+ return loop.IsDoNormal();
+}
+
+bool IsTransformableLoop(const parser::OpenMPLoopConstruct &omp) {
+ return IsLoopTransforming(omp.BeginDir().DirId());
+}
+
+bool IsTransformableLoop(const parser::ExecutionPartConstruct &epc) {
+ if (auto *loop{parser::Unwrap<parser::DoConstruct>(epc)}) {
+ return IsTransformableLoop(*loop);
+ }
+ if (auto *omp{parser::Unwrap<parser::OpenMPLoopConstruct>(epc)}) {
+ return IsTransformableLoop(*omp);
+ }
+ return false;
+}
+
std::optional<int64_t> GetNumGeneratedNestsFrom(
const parser::ExecutionPartConstruct &epc,
std::optional<int64_t> nestedCount) {
@@ -751,4 +769,69 @@ std::optional<int64_t> GetNumGeneratedNestsFrom(
// For every other loop construct return 1.
return 1;
}
+
+LoopSequence::LoopSequence(
+ const parser::ExecutionPartConstruct &root, bool allowAllLoops)
+ : allowAllLoops_(allowAllLoops) {
+ entry_ = createConstructEntry(root);
+ assert(entry_ && "Expecting loop like code");
+
+ createChildrenFromRange(entry_->location);
+ length_ = calculateLength();
+}
+
+LoopSequence::LoopSequence(std::unique_ptr<Construct> entry, bool allowAllLoops)
+ : allowAllLoops_(allowAllLoops), entry_(std::move(entry)) {
+ createChildrenFromRange(entry_->location);
+ length_ = calculateLength();
+}
+
+std::unique_ptr<LoopSequence::Construct> LoopSequence::createConstructEntry(
+ const parser::ExecutionPartConstruct &code) {
+ if (auto *loop{parser::Unwrap<parser::DoConstruct>(code)}) {
+ if (allowAllLoops_ || IsTransformableLoop(*loop)) {
+ auto &body{std::get<parser::Block>(loop->t)};
+ return std::make_unique<Construct>(body, &code);
+ }
+ } else if (auto *omp{parser::Unwrap<parser::OpenMPLoopConstruct>(code)}) {
+ if (IsTransformableLoop(*omp)) {
+ auto &body{std::get<parser::Block>(omp->t)};
+ return std::make_unique<Construct>(body, &code);
+ }
+ }
+
+ return nullptr;
+}
+
+void LoopSequence::createChildrenFromRange(
+ ExecutionPartIterator::IteratorType begin,
+ ExecutionPartIterator::IteratorType end) {
+ for (auto &code : BlockRange(begin, end, BlockRange::Step::Over)) {
+ if (auto entry{createConstructEntry(code)}) {
+ children_.push_back(LoopSequence(std::move(entry), allowAllLoops_));
+ }
+ }
+}
+
+std::optional<int64_t> LoopSequence::calculateLength() const {
+ if (!entry_->owner) {
+ return sumOfChildrenLengths();
+ }
+ if (parser::Unwrap<parser::DoConstruct>(entry_->owner)) {
+ return 1;
+ }
+ return GetNumGeneratedNestsFrom(*entry_->owner, sumOfChildrenLengths());
+}
+
+std::optional<int64_t> LoopSequence::sumOfChildrenLengths() const {
+ int64_t sum{0};
+ for (auto &seq : children_) {
+ if (auto len{seq.length()}) {
+ sum += *len;
+ } else {
+ return std::nullopt;
+ }
+ }
+ return sum;
+}
} // namespace Fortran::semantics::omp
diff --git a/flang/test/Semantics/OpenMP/loop-transformation-construct01.f90 b/flang/test/Semantics/OpenMP/loop-transformation-construct01.f90
index 4eeb7330ea589..2d27d9ab85c00 100644
--- a/flang/test/Semantics/OpenMP/loop-transformation-construct01.f90
+++ b/flang/test/Semantics/OpenMP/loop-transformation-construct01.f90
@@ -33,6 +33,7 @@ subroutine loop_transformation_construct3
integer :: x
integer :: v(i)
+ !ERROR: This construct should contain a DO-loop or a loop-nest-generating OpenMP construct
!$omp do
!ERROR: Only loop-transforming OpenMP constructs are allowed inside OpenMP loop constructs
!$omp parallel do
``````````
</details>
https://github.com/llvm/llvm-project/pull/185296
More information about the llvm-branch-commits
mailing list