[flang-commits] [flang] [flang][OpenMP] Generalize checks of loop construct structure (PR #170735)
Krzysztof Parzyszek via flang-commits
flang-commits at lists.llvm.org
Thu Dec 11 09:38:28 PST 2025
https://github.com/kparzysz updated https://github.com/llvm/llvm-project/pull/170735
>From ca0b4a3ae509f29aaaaa9a67140533e3ceca544b Mon Sep 17 00:00:00 2001
From: Krzysztof Parzyszek <Krzysztof.Parzyszek at amd.com>
Date: Tue, 2 Dec 2025 14:34:45 -0600
Subject: [PATCH 1/6] [flang][OpenMP] Implement loop construct iterator range
Since we're trying to preserve compiler directives in loop constructs,
not every element of the associated parser::Block needs to be a loop
or an OpenMP loop construct. Implement a helper class `LoopRange` to
make it easy to iterate over elements of parser::Block that are loops
or loop constructs.
---
flang/include/flang/Parser/openmp-utils.h | 74 +++++++++++++++++++++++
flang/lib/Parser/openmp-utils.cpp | 37 ++++++++++++
2 files changed, 111 insertions(+)
diff --git a/flang/include/flang/Parser/openmp-utils.h b/flang/include/flang/Parser/openmp-utils.h
index e164f63aa189b..27e4aa6db0f0b 100644
--- a/flang/include/flang/Parser/openmp-utils.h
+++ b/flang/include/flang/Parser/openmp-utils.h
@@ -237,6 +237,80 @@ struct OmpAllocateInfo {
OmpAllocateInfo SplitOmpAllocate(const OmpAllocateDirective &x);
+namespace detail {
+template <bool IsConst, typename T>
+struct ConstIf {
+ using type = std::conditional_t<IsConst, std::add_const_t<T>, T>;
+};
+
+template <bool IsConst, typename T>
+using ConstIfT = typename ConstIf<IsConst, T>::type;
+}
+
+template <bool IsConst> struct LoopRange {
+ using QualBlock = detail::ConstIfT<IsConst, Block>;
+ using QualReference = decltype(std::declval<QualBlock>().front());
+ using QualPointer = std::remove_reference_t<QualReference> *;
+
+ LoopRange(QualBlock &x) { Initialize(x); }
+ LoopRange(QualReference x);
+
+ LoopRange(detail::ConstIfT<IsConst, OpenMPLoopConstruct> &x)
+ : LoopRange(std::get<Block>(x.t)) {}
+ LoopRange(detail::ConstIfT<IsConst, DoConstruct> &x)
+ : LoopRange(std::get<Block>(x.t)) {}
+
+ size_t size() const { return items.size(); }
+
+ struct iterator;
+
+ iterator begin();
+ iterator end();
+
+private:
+ void Initialize(QualBlock &body);
+
+ std::vector<QualPointer> items;
+};
+
+template <typename T> LoopRange(T &x) -> LoopRange<std::is_const_v<T>>;
+
+template <bool IsConst> struct LoopRange<IsConst>::iterator {
+ QualReference operator*() { return **at; }
+
+ bool operator==(const iterator &other) const { return at == other.at; }
+ bool operator!=(const iterator &other) const { return at != other.at; }
+
+ iterator &operator++() {
+ ++at;
+ return *this;
+ }
+ iterator &operator--() {
+ --at;
+ return *this;
+ }
+ iterator &operator++(int);
+ iterator &operator--(int);
+
+private:
+ friend struct LoopRange;
+ typename decltype(LoopRange::items)::iterator at;
+};
+
+template <bool IsConst> inline auto LoopRange<IsConst>::begin() -> iterator {
+ iterator x;
+ x.at = items.begin();
+ return x;
+}
+
+template <bool IsConst> inline auto LoopRange<IsConst>::end() -> iterator {
+ iterator x;
+ x.at = items.end();
+ return x;
+}
+
+using ConstLoopRange = LoopRange<true>;
+
} // namespace Fortran::parser::omp
#endif // FORTRAN_PARSER_OPENMP_UTILS_H
diff --git a/flang/lib/Parser/openmp-utils.cpp b/flang/lib/Parser/openmp-utils.cpp
index 4c38917e87d29..099c7faf328c3 100644
--- a/flang/lib/Parser/openmp-utils.cpp
+++ b/flang/lib/Parser/openmp-utils.cpp
@@ -205,4 +205,41 @@ OmpAllocateInfo SplitOmpAllocate(const OmpAllocateDirective &x) {
return info;
}
+template <bool IsConst>
+LoopRange<IsConst>::LoopRange(QualReference x) {
+ if (auto *doLoop{Unwrap<DoConstruct>(x)}) {
+ Initialize(std::get<Block>(doLoop->t));
+ } else if (auto *omp{Unwrap<OpenMPLoopConstruct>(x)}) {
+ Initialize(std::get<Block>(omp->t));
+ }
+}
+
+template <bool IsConst>
+void LoopRange<IsConst>::Initialize(QualBlock &body) {
+ using QualIterator = decltype(std::declval<QualBlock>().begin());
+ auto makeRange{[](auto &container) {
+ return llvm::make_range(container.begin(), container.end());
+ }};
+
+ std::vector<llvm::iterator_range<QualIterator>> nest{makeRange(body)};
+ do {
+ auto at{nest.back().begin()};
+ auto end{nest.back().end()};
+ nest.pop_back();
+ while (at != end) {
+ if (auto *block{Unwrap<BlockConstruct>(*at)}) {
+ nest.push_back(llvm::make_range(std::next(at), end));
+ nest.push_back(makeRange(std::get<Block>(block->t)));
+ break;
+ } else if (Unwrap<DoConstruct>(*at) || Unwrap<OpenMPLoopConstruct>(*at)) {
+ items.push_back(&*at);
+ }
+ ++at;
+ }
+ } while (!nest.empty());
+}
+
+template struct LoopRange<false>;
+template struct LoopRange<true>;
+
} // namespace Fortran::parser::omp
>From 9a2d3dca08ab237e7e949fd5642c96cf0fba89b8 Mon Sep 17 00:00:00 2001
From: Krzysztof Parzyszek <Krzysztof.Parzyszek at amd.com>
Date: Tue, 2 Dec 2025 14:59:34 -0600
Subject: [PATCH 2/6] [flang][OpenMP] Generalize checks of loop construct
structure
For an OpenMP loop construct, count how many loops will effectively be
contained in its associated block. For constructs that are loop-nest
associated this number should be 1. Report cases where this number is
different.
Take into account that the block associated with a loop construct can
contain compiler directives.
---
flang/lib/Semantics/check-omp-loop.cpp | 201 +++++++++++-------
flang/lib/Semantics/check-omp-structure.h | 3 +-
flang/test/Parser/OpenMP/tile-fail.f90 | 8 +-
flang/test/Semantics/OpenMP/do21.f90 | 10 +-
.../Semantics/OpenMP/loop-association.f90 | 6 +-
.../OpenMP/loop-transformation-clauses01.f90 | 16 +-
.../loop-transformation-construct01.f90 | 4 +-
.../loop-transformation-construct02.f90 | 8 +-
.../loop-transformation-construct04.f90 | 4 +-
9 files changed, 156 insertions(+), 104 deletions(-)
diff --git a/flang/lib/Semantics/check-omp-loop.cpp b/flang/lib/Semantics/check-omp-loop.cpp
index fc4b9222d91b3..6414f0028e008 100644
--- a/flang/lib/Semantics/check-omp-loop.cpp
+++ b/flang/lib/Semantics/check-omp-loop.cpp
@@ -37,6 +37,14 @@
#include <tuple>
#include <variant>
+namespace Fortran::semantics {
+static bool IsLoopTransforming(llvm::omp::Directive dir);
+static bool IsFullUnroll(const parser::OpenMPLoopConstruct &x);
+static std::optional<size_t> CountGeneratedLoops(
+ const parser::ExecutionPartConstruct &epc);
+static std::optional<size_t> CountGeneratedLoops(const parser::Block &block);
+} // namespace Fortran::semantics
+
namespace {
using namespace Fortran;
@@ -263,22 +271,19 @@ static bool IsLoopTransforming(llvm::omp::Directive dir) {
}
void OmpStructureChecker::CheckNestedBlock(const parser::OpenMPLoopConstruct &x,
- const parser::Block &body, size_t &nestedCount) {
+ const parser::Block &body) {
for (auto &stmt : body) {
if (auto *dir{parser::Unwrap<parser::CompilerDirective>(stmt)}) {
context_.Say(dir->source,
"Compiler directives are not allowed inside OpenMP loop constructs"_warn_en_US);
- } else if (parser::Unwrap<parser::DoConstruct>(stmt)) {
- ++nestedCount;
} else if (auto *omp{parser::Unwrap<parser::OpenMPLoopConstruct>(stmt)}) {
if (!IsLoopTransforming(omp->BeginDir().DirName().v)) {
context_.Say(omp->source,
"Only loop-transforming OpenMP constructs are allowed inside OpenMP loop constructs"_err_en_US);
}
- ++nestedCount;
} else if (auto *block{parser::Unwrap<parser::BlockConstruct>(stmt)}) {
- CheckNestedBlock(x, std::get<parser::Block>(block->t), nestedCount);
- } else {
+ CheckNestedBlock(x, std::get<parser::Block>(block->t));
+ } else if (!parser::Unwrap<parser::DoConstruct>(stmt)) {
parser::CharBlock source{parser::GetSource(stmt).value_or(x.source)};
context_.Say(source,
"OpenMP loop construct can only contain DO loops or loop-nest-generating OpenMP constructs"_err_en_US);
@@ -286,16 +291,96 @@ void OmpStructureChecker::CheckNestedBlock(const parser::OpenMPLoopConstruct &x,
}
}
+static bool IsFullUnroll(const parser::OpenMPLoopConstruct &x) {
+ const parser::OmpDirectiveSpecification &beginSpec{x.BeginDir()};
+
+ if (beginSpec.DirName().v == llvm::omp::Directive::OMPD_unroll) {
+ return llvm::none_of(beginSpec.Clauses().v, [](const parser::OmpClause &c) {
+ return c.Id() == llvm::omp::Clause::OMPC_partial;
+ });
+ }
+ return false;
+}
+
+static std::optional<size_t> CountGeneratedLoops(
+ 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};
+
+ // TODO: Handle split, apply.
+ if (IsFullUnroll(omp)) {
+ return std::nullopt;
+ }
+ if (dir == llvm::omp::Directive::OMPD_fuse) {
+ auto rangeAt{
+ llvm::find_if(beginSpec.Clauses().v, [](const parser::OmpClause &c) {
+ return c.Id() == llvm::omp::Clause::OMPC_looprange;
+ })};
+ if (rangeAt == beginSpec.Clauses().v.end()) {
+ return std::nullopt;
+ }
+
+ auto *loopRange{parser::Unwrap<parser::OmpLooprangeClause>(*rangeAt)};
+ std::optional<int64_t> count{GetIntValue(std::get<1>(loopRange->t))};
+ if (!count || *count <= 0) {
+ return std::nullopt;
+ }
+ if (auto nestedCount{CountGeneratedLoops(std::get<parser::Block>(omp.t))}) {
+ return 1 + *nestedCount - static_cast<size_t>(*count);
+ } else {
+ return std::nullopt;
+ }
+ }
+
+ // For every other loop construct return 1.
+ return 1;
+}
+
+static std::optional<size_t> CountGeneratedLoops(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.
+ // These issues will be diagnosed elsewhere, and we should not emit any
+ // 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<size_t> numLoops{0};
+ for (auto &epc : parser::omp::LoopRange(block)) {
+ if (auto genCount{CountGeneratedLoops(epc)}) {
+ *numLoops += *genCount;
+ } else {
+ numLoops = std::nullopt;
+ break;
+ }
+ }
+ return numLoops;
+}
+
void OmpStructureChecker::CheckNestedConstruct(
const parser::OpenMPLoopConstruct &x) {
- size_t nestedCount{0};
-
+ const parser::OmpDirectiveSpecification &beginSpec{x.BeginDir()};
auto &body{std::get<parser::Block>(x.t)};
- if (body.empty()) {
- context_.Say(x.source,
- "OpenMP loop construct should contain a DO-loop or a loop-nest-generating OpenMP construct"_err_en_US);
- } else {
- CheckNestedBlock(x, body, nestedCount);
+
+ CheckNestedBlock(x, body);
+
+ // Check if a loop-nest-associated construct has only one top-level loop
+ // in it.
+ if (std::optional<size_t> numLoops{CountGeneratedLoops(body)}) {
+ if (*numLoops == 0) {
+ context_.Say(beginSpec.DirName().source,
+ "This construct should contain a DO-loop or a loop-nest-generating OpenMP construct"_err_en_US);
+ } else {
+ auto assoc{llvm::omp::getDirectiveAssociation(beginSpec.DirName().v)};
+ if (*numLoops > 1 && assoc == llvm::omp::Association::LoopNest) {
+ context_.Say(beginSpec.DirName().source,
+ "This construct applies to a loop nest, but has a loop sequence of length %zu"_err_en_US,
+ *numLoops);
+ }
+ }
}
}
@@ -304,16 +389,9 @@ void OmpStructureChecker::CheckFullUnroll(
// If the nested construct is a full unroll, then this construct is invalid
// since it won't contain a loop.
if (const parser::OpenMPLoopConstruct *nested{x.GetNestedConstruct()}) {
- auto &nestedSpec{nested->BeginDir()};
- if (nestedSpec.DirName().v == llvm::omp::Directive::OMPD_unroll) {
- bool isPartial{
- llvm::any_of(nestedSpec.Clauses().v, [](const parser::OmpClause &c) {
- return c.Id() == llvm::omp::Clause::OMPC_partial;
- })};
- if (!isPartial) {
- context_.Say(x.source,
- "OpenMP loop construct cannot apply to a fully unrolled loop"_err_en_US);
- }
+ if (IsFullUnroll(*nested)) {
+ context_.Say(x.source,
+ "OpenMP loop construct cannot apply to a fully unrolled loop"_err_en_US);
}
}
}
@@ -387,11 +465,6 @@ void OmpStructureChecker::Enter(const parser::OpenMPLoopConstruct &x) {
beginName.v == llvm::omp::Directive::OMPD_distribute_simd) {
CheckDistLinear(x);
}
- if (beginName.v == llvm::omp::Directive::OMPD_fuse) {
- CheckLooprangeBounds(x);
- } else {
- CheckNestedFuse(x);
- }
}
const parser::Name OmpStructureChecker::GetLoopIndex(
@@ -531,57 +604,20 @@ void OmpStructureChecker::CheckDistLinear(
void OmpStructureChecker::CheckLooprangeBounds(
const parser::OpenMPLoopConstruct &x) {
- const parser::OmpClauseList &clauseList{x.BeginDir().Clauses()};
- if (clauseList.v.empty()) {
- return;
- }
- for (auto &clause : clauseList.v) {
- if (const auto *lrClause{
- std::get_if<parser::OmpClause::Looprange>(&clause.u)}) {
- auto first{GetIntValue(std::get<0>((lrClause->v).t))};
- auto count{GetIntValue(std::get<1>((lrClause->v).t))};
- if (!first || !count) {
- return;
- }
- auto &loopConsList{std::get<parser::Block>(x.t)};
- if (*first > 0 && *count > 0 &&
- loopConsList.size() < (unsigned)(*first + *count - 1)) {
- context_.Say(clause.source,
- "The loop range indicated in the %s clause must not be out of the bounds of the Loop Sequence following the construct."_err_en_US,
- parser::ToUpperCaseLetters(clause.source.ToString()));
- }
- return;
- }
- }
-}
-
-void OmpStructureChecker::CheckNestedFuse(
- const parser::OpenMPLoopConstruct &x) {
- auto &loopConsList{std::get<parser::Block>(x.t)};
- if (loopConsList.empty()) {
- return;
- }
- const auto *ompConstruct{parser::omp::GetOmpLoop(loopConsList.front())};
- if (!ompConstruct) {
- return;
- }
- const parser::OmpClauseList &clauseList{ompConstruct->BeginDir().Clauses()};
- if (clauseList.v.empty()) {
- return;
- }
- for (auto &clause : clauseList.v) {
- if (const auto *lrClause{
- std::get_if<parser::OmpClause::Looprange>(&clause.u)}) {
- auto count{GetIntValue(std::get<1>((lrClause->v).t))};
- if (!count) {
+ for (const parser::OmpClause &clause : x.BeginDir().Clauses().v) {
+ if (auto *lrClause{parser::Unwrap<parser::OmpLooprangeClause>(clause)}) {
+ auto first{GetIntValue(std::get<0>(lrClause->t))};
+ auto count{GetIntValue(std::get<1>(lrClause->t))};
+ if (!first || !count || *first <= 0 || *count <= 0) {
return;
}
- auto &nestedLoopConsList{std::get<parser::Block>(ompConstruct->t)};
- if (nestedLoopConsList.size() > (unsigned)(*count)) {
- context_.Say(x.BeginDir().DirName().source,
- "The loop sequence following the %s construct must be fully fused first."_err_en_US,
- parser::ToUpperCaseLetters(
- x.BeginDir().DirName().source.ToString()));
+ auto requiredCount{static_cast<size_t>(*first + *count - 1)};
+ if (auto loopCount{CountGeneratedLoops(std::get<parser::Block>(x.t))}) {
+ if (*loopCount < requiredCount) {
+ context_.Say(clause.source,
+ "The specified loop range requires %zu loops, but the loop sequence has a length of %zu"_err_en_US,
+ requiredCount, *loopCount);
+ }
}
return;
}
@@ -625,18 +661,21 @@ void OmpStructureChecker::CheckScanModifier(
}
void OmpStructureChecker::Leave(const parser::OpenMPLoopConstruct &x) {
- const parser::OmpClauseList &clauseList{x.BeginDir().Clauses()};
+ const parser::OmpDirectiveSpecification &beginSpec{x.BeginDir()};
// A few semantic checks for InScan reduction are performed below as SCAN
// constructs inside LOOP may add the relevant information. Scan reduction is
// supported only in loop constructs, so same checks are not applicable to
// other directives.
- for (const auto &clause : clauseList.v) {
+ for (const auto &clause : beginSpec.Clauses().v) {
if (auto *reduction{std::get_if<parser::OmpClause::Reduction>(&clause.u)}) {
CheckScanModifier(*reduction);
}
}
- if (llvm::omp::allSimdSet.test(GetContext().directive)) {
+ if (beginSpec.DirName().v == llvm::omp::Directive::OMPD_fuse) {
+ CheckLooprangeBounds(x);
+ }
+ if (llvm::omp::allSimdSet.test(beginSpec.DirName().v)) {
ExitDirectiveNest(SIMDNest);
}
dirContext_.pop_back();
@@ -782,8 +821,8 @@ void OmpStructureChecker::Enter(const parser::OmpClause::Sizes &c) {
void OmpStructureChecker::Enter(const parser::OmpClause::Looprange &x) {
CheckAllowedClause(llvm::omp::Clause::OMPC_looprange);
auto &[first, count]{x.v.t};
- RequiresConstantPositiveParameter(llvm::omp::Clause::OMPC_looprange, count);
RequiresConstantPositiveParameter(llvm::omp::Clause::OMPC_looprange, first);
+ RequiresConstantPositiveParameter(llvm::omp::Clause::OMPC_looprange, count);
}
void OmpStructureChecker::Enter(const parser::DoConstruct &x) {
diff --git a/flang/lib/Semantics/check-omp-structure.h b/flang/lib/Semantics/check-omp-structure.h
index 5bd5ae050be64..267362b6325f1 100644
--- a/flang/lib/Semantics/check-omp-structure.h
+++ b/flang/lib/Semantics/check-omp-structure.h
@@ -323,11 +323,10 @@ class OmpStructureChecker : public OmpStructureCheckerBase {
void CheckScanModifier(const parser::OmpClause::Reduction &x);
void CheckLooprangeBounds(const parser::OpenMPLoopConstruct &x);
- void CheckNestedFuse(const parser::OpenMPLoopConstruct &x);
void CheckDistLinear(const parser::OpenMPLoopConstruct &x);
void CheckSIMDNest(const parser::OpenMPConstruct &x);
void CheckNestedBlock(const parser::OpenMPLoopConstruct &x,
- const parser::Block &body, size_t &nestedCount);
+ const parser::Block &body);
void CheckNestedConstruct(const parser::OpenMPLoopConstruct &x);
void CheckFullUnroll(const parser::OpenMPLoopConstruct &x);
void CheckTargetNest(const parser::OpenMPConstruct &x);
diff --git a/flang/test/Parser/OpenMP/tile-fail.f90 b/flang/test/Parser/OpenMP/tile-fail.f90
index a69261a927961..d5ff39cd1037c 100644
--- a/flang/test/Parser/OpenMP/tile-fail.f90
+++ b/flang/test/Parser/OpenMP/tile-fail.f90
@@ -1,7 +1,7 @@
! RUN: split-file %s %t
-! RUN: not %flang_fc1 -fsyntax-only -fopenmp %t/stray_end1.f90 2>&1 | FileCheck %t/stray_end1.f90
-! RUN: not %flang_fc1 -fsyntax-only -fopenmp %t/stray_end2.f90 2>&1 | FileCheck %t/stray_end2.f90
-! RUN: not %flang_fc1 -fsyntax-only -fopenmp %t/stray_begin.f90 2>&1 | FileCheck %t/stray_begin.f90
+! RUN: not %flang_fc1 -fsyntax-only -fopenmp -fopenmp-version=60 %t/stray_end1.f90 2>&1 | FileCheck %t/stray_end1.f90
+! RUN: not %flang_fc1 -fsyntax-only -fopenmp -fopenmp-version=60 %t/stray_end2.f90 2>&1 | FileCheck %t/stray_end2.f90
+! RUN: not %flang_fc1 -fsyntax-only -fopenmp -fopenmp-version=60 %t/stray_begin.f90 2>&1 | FileCheck %t/stray_begin.f90
!--- stray_end1.f90
@@ -25,7 +25,7 @@ subroutine stray_end2
!--- stray_begin.f90
subroutine stray_begin
- !CHECK: error: OpenMP loop construct should contain a DO-loop or a loop-nest-generating OpenMP construct
+ !CHECK: error: This construct should contain a DO-loop or a loop-nest-generating OpenMP construct
!$omp tile sizes(2)
end subroutine
diff --git a/flang/test/Semantics/OpenMP/do21.f90 b/flang/test/Semantics/OpenMP/do21.f90
index e6fe7dd39dd3e..683118a5b2182 100644
--- a/flang/test/Semantics/OpenMP/do21.f90
+++ b/flang/test/Semantics/OpenMP/do21.f90
@@ -2,26 +2,26 @@
! Check for existence of loop following a DO directive
subroutine do1
- !ERROR: OpenMP loop construct should contain a DO-loop or a loop-nest-generating OpenMP construct
+ !ERROR: This construct should contain a DO-loop or a loop-nest-generating OpenMP construct
!$omp do
end subroutine
subroutine do2
- !ERROR: OpenMP loop construct should contain a DO-loop or a loop-nest-generating OpenMP construct
+ !ERROR: This construct should contain a DO-loop or a loop-nest-generating OpenMP construct
!$omp parallel do
end subroutine
subroutine do3
- !ERROR: OpenMP loop construct should contain a DO-loop or a loop-nest-generating OpenMP construct
+ !ERROR: This construct should contain a DO-loop or a loop-nest-generating OpenMP construct
!$omp simd
end subroutine
subroutine do4
- !ERROR: OpenMP loop construct should contain a DO-loop or a loop-nest-generating OpenMP construct
+ !ERROR: This construct should contain a DO-loop or a loop-nest-generating OpenMP construct
!$omp do simd
end subroutine
subroutine do5
- !ERROR: OpenMP loop construct should contain a DO-loop or a loop-nest-generating OpenMP construct
+ !ERROR: This construct should contain a DO-loop or a loop-nest-generating OpenMP construct
!$omp loop
end subroutine
diff --git a/flang/test/Semantics/OpenMP/loop-association.f90 b/flang/test/Semantics/OpenMP/loop-association.f90
index 4e63cafb3fda1..4e6cf4ce13486 100644
--- a/flang/test/Semantics/OpenMP/loop-association.f90
+++ b/flang/test/Semantics/OpenMP/loop-association.f90
@@ -103,7 +103,7 @@
!$omp parallel do private(c)
do i = 1, N
do j = 1, N
- !ERROR: OpenMP loop construct should contain a DO-loop or a loop-nest-generating OpenMP construct
+ !ERROR: This construct should contain a DO-loop or a loop-nest-generating OpenMP construct
!$omp parallel do shared(b)
a = 3.14
enddo
@@ -123,7 +123,7 @@
!ERROR: Misplaced OpenMP end-directive
!$omp end parallel do
- !ERROR: OpenMP loop construct should contain a DO-loop or a loop-nest-generating OpenMP construct
+ !ERROR: This construct should contain a DO-loop or a loop-nest-generating OpenMP construct
!$omp parallel do private(c)
5 FORMAT (1PE12.4, I10)
do i=1, N
@@ -140,7 +140,7 @@
!ERROR: Misplaced OpenMP end-directive
!$omp end parallel do simd
- !ERROR: OpenMP loop construct should contain a DO-loop or a loop-nest-generating OpenMP construct
+ !ERROR: This construct should contain a DO-loop or a loop-nest-generating OpenMP construct
!$omp simd
a = i + 1
!ERROR: Misplaced OpenMP end-directive
diff --git a/flang/test/Semantics/OpenMP/loop-transformation-clauses01.f90 b/flang/test/Semantics/OpenMP/loop-transformation-clauses01.f90
index 9ca0e8cfc9af1..5e3d32d7c6eff 100644
--- a/flang/test/Semantics/OpenMP/loop-transformation-clauses01.f90
+++ b/flang/test/Semantics/OpenMP/loop-transformation-clauses01.f90
@@ -20,7 +20,7 @@ subroutine loop_transformation_construct1
end do
!$omp end fuse
- !ERROR: The loop range indicated in the LOOPRANGE(5,2) clause must not be out of the bounds of the Loop Sequence following the construct.
+ !ERROR: The specified loop range requires 6 loops, but the loop sequence has a length of 2
!$omp fuse looprange(5,2)
do x = 1, i
v(x) = x * 2
@@ -63,4 +63,18 @@ subroutine loop_transformation_construct1
v(x) = x * 2
end do
!$omp end fuse
+
+ ! This is ok aside from the warnings about compiler directives
+ !$omp fuse looprange(1,3)
+ do x = 1, 10; end do ! 1 loop
+ !WARNING: Compiler directives are not allowed inside OpenMP loop constructs
+ !dir$ novector
+ !$omp fuse looprange(1,2) ! 2 loops
+ do x = 1, 10; end do
+ !WARNING: Compiler directives are not allowed inside OpenMP loop constructs
+ !dir$ nounroll
+ do x = 1, 10; end do
+ do x = 1, 10; end do
+ !$omp end fuse
+ !$omp end fuse
end subroutine
diff --git a/flang/test/Semantics/OpenMP/loop-transformation-construct01.f90 b/flang/test/Semantics/OpenMP/loop-transformation-construct01.f90
index caa8f3f216fec..4eeb7330ea589 100644
--- a/flang/test/Semantics/OpenMP/loop-transformation-construct01.f90
+++ b/flang/test/Semantics/OpenMP/loop-transformation-construct01.f90
@@ -7,7 +7,7 @@ subroutine loop_transformation_construct1
!ERROR: OpenMP loop construct cannot apply to a fully unrolled loop
!$omp do
- !ERROR: OpenMP loop construct should contain a DO-loop or a loop-nest-generating OpenMP construct
+ !ERROR: This construct should contain a DO-loop or a loop-nest-generating OpenMP construct
!$omp unroll
end subroutine
@@ -51,7 +51,7 @@ subroutine loop_transformation_construct4
do x = 1, i
v(x) = v(x) * 2
end do
- !ERROR: OpenMP loop construct should contain a DO-loop or a loop-nest-generating OpenMP construct
+ !ERROR: This construct should contain a DO-loop or a loop-nest-generating OpenMP construct
!ERROR: At least one of SIZES clause must appear on the TILE directive
!$omp tile
end subroutine
diff --git a/flang/test/Semantics/OpenMP/loop-transformation-construct02.f90 b/flang/test/Semantics/OpenMP/loop-transformation-construct02.f90
index 1b15c938915cd..25247c3896cae 100644
--- a/flang/test/Semantics/OpenMP/loop-transformation-construct02.f90
+++ b/flang/test/Semantics/OpenMP/loop-transformation-construct02.f90
@@ -7,7 +7,7 @@ subroutine loop_transformation_construct1
implicit none
!$omp do
- !ERROR: OpenMP loop construct should contain a DO-loop or a loop-nest-generating OpenMP construct
+ !ERROR: This construct should contain a DO-loop or a loop-nest-generating OpenMP construct
!$omp fuse
end subroutine
@@ -15,7 +15,7 @@ subroutine loop_transformation_construct2
implicit none
!$omp do
- !ERROR: OpenMP loop construct should contain a DO-loop or a loop-nest-generating OpenMP construct
+ !ERROR: This construct should contain a DO-loop or a loop-nest-generating OpenMP construct
!$omp fuse
!$omp end fuse
end subroutine
@@ -50,7 +50,7 @@ subroutine loop_transformation_construct4
do x = 1, i
v(x) = v(x) * 2
end do
- !ERROR: OpenMP loop construct should contain a DO-loop or a loop-nest-generating OpenMP construct
+ !ERROR: This construct should contain a DO-loop or a loop-nest-generating OpenMP construct
!$omp fuse
!$omp end fuse
end subroutine
@@ -80,7 +80,7 @@ subroutine loop_transformation_construct6
integer :: x
integer :: v(i)
- !ERROR: The loop sequence following the DO construct must be fully fused first.
+ !ERROR: This construct applies to a loop nest, but has a loop sequence of length 2
!$omp do
!$omp fuse looprange(1,1)
!$omp unroll partial(2)
diff --git a/flang/test/Semantics/OpenMP/loop-transformation-construct04.f90 b/flang/test/Semantics/OpenMP/loop-transformation-construct04.f90
index 2856247329f3b..158b030906e07 100644
--- a/flang/test/Semantics/OpenMP/loop-transformation-construct04.f90
+++ b/flang/test/Semantics/OpenMP/loop-transformation-construct04.f90
@@ -8,7 +8,7 @@ subroutine loop_transformation_construct3
integer :: x
integer :: v(i)
- !ERROR: The loop sequence following the DO construct must be fully fused first.
+ !ERROR: This construct applies to a loop nest, but has a loop sequence of length 2
!$omp do
!$omp fuse looprange(1,2)
do x = 1, i
@@ -30,7 +30,7 @@ subroutine loop_transformation_construct4
integer :: x
integer :: v(i)
- !ERROR: The loop sequence following the TILE construct must be fully fused first.
+ !ERROR: This construct applies to a loop nest, but has a loop sequence of length 2
!$omp tile sizes(2)
!$omp fuse looprange(1,2)
do x = 1, i
>From 713e9446c145e323651a8908c617f83b7389a757 Mon Sep 17 00:00:00 2001
From: Krzysztof Parzyszek <Krzysztof.Parzyszek at amd.com>
Date: Thu, 4 Dec 2025 14:20:46 -0600
Subject: [PATCH 3/6] format
---
flang/include/flang/Parser/openmp-utils.h | 5 ++---
flang/lib/Parser/openmp-utils.cpp | 6 ++----
2 files changed, 4 insertions(+), 7 deletions(-)
diff --git a/flang/include/flang/Parser/openmp-utils.h b/flang/include/flang/Parser/openmp-utils.h
index 27e4aa6db0f0b..c47cdb295da4a 100644
--- a/flang/include/flang/Parser/openmp-utils.h
+++ b/flang/include/flang/Parser/openmp-utils.h
@@ -238,14 +238,13 @@ struct OmpAllocateInfo {
OmpAllocateInfo SplitOmpAllocate(const OmpAllocateDirective &x);
namespace detail {
-template <bool IsConst, typename T>
-struct ConstIf {
+template <bool IsConst, typename T> struct ConstIf {
using type = std::conditional_t<IsConst, std::add_const_t<T>, T>;
};
template <bool IsConst, typename T>
using ConstIfT = typename ConstIf<IsConst, T>::type;
-}
+} // namespace detail
template <bool IsConst> struct LoopRange {
using QualBlock = detail::ConstIfT<IsConst, Block>;
diff --git a/flang/lib/Parser/openmp-utils.cpp b/flang/lib/Parser/openmp-utils.cpp
index 099c7faf328c3..1593b19d6b372 100644
--- a/flang/lib/Parser/openmp-utils.cpp
+++ b/flang/lib/Parser/openmp-utils.cpp
@@ -205,8 +205,7 @@ OmpAllocateInfo SplitOmpAllocate(const OmpAllocateDirective &x) {
return info;
}
-template <bool IsConst>
-LoopRange<IsConst>::LoopRange(QualReference x) {
+template <bool IsConst> LoopRange<IsConst>::LoopRange(QualReference x) {
if (auto *doLoop{Unwrap<DoConstruct>(x)}) {
Initialize(std::get<Block>(doLoop->t));
} else if (auto *omp{Unwrap<OpenMPLoopConstruct>(x)}) {
@@ -214,8 +213,7 @@ LoopRange<IsConst>::LoopRange(QualReference x) {
}
}
-template <bool IsConst>
-void LoopRange<IsConst>::Initialize(QualBlock &body) {
+template <bool IsConst> void LoopRange<IsConst>::Initialize(QualBlock &body) {
using QualIterator = decltype(std::declval<QualBlock>().begin());
auto makeRange{[](auto &container) {
return llvm::make_range(container.begin(), container.end());
>From 06c45b22341bb4792ce89f592c2f299de4030104 Mon Sep 17 00:00:00 2001
From: Krzysztof Parzyszek <Krzysztof.Parzyszek at amd.com>
Date: Thu, 4 Dec 2025 14:23:11 -0600
Subject: [PATCH 4/6] format
---
flang/lib/Semantics/check-omp-loop.cpp | 4 ++--
flang/lib/Semantics/check-omp-structure.h | 4 ++--
2 files changed, 4 insertions(+), 4 deletions(-)
diff --git a/flang/lib/Semantics/check-omp-loop.cpp b/flang/lib/Semantics/check-omp-loop.cpp
index 6414f0028e008..917b2c5015744 100644
--- a/flang/lib/Semantics/check-omp-loop.cpp
+++ b/flang/lib/Semantics/check-omp-loop.cpp
@@ -270,8 +270,8 @@ static bool IsLoopTransforming(llvm::omp::Directive dir) {
}
}
-void OmpStructureChecker::CheckNestedBlock(const parser::OpenMPLoopConstruct &x,
- const parser::Block &body) {
+void OmpStructureChecker::CheckNestedBlock(
+ const parser::OpenMPLoopConstruct &x, const parser::Block &body) {
for (auto &stmt : body) {
if (auto *dir{parser::Unwrap<parser::CompilerDirective>(stmt)}) {
context_.Say(dir->source,
diff --git a/flang/lib/Semantics/check-omp-structure.h b/flang/lib/Semantics/check-omp-structure.h
index 267362b6325f1..12f458b624791 100644
--- a/flang/lib/Semantics/check-omp-structure.h
+++ b/flang/lib/Semantics/check-omp-structure.h
@@ -325,8 +325,8 @@ class OmpStructureChecker : public OmpStructureCheckerBase {
void CheckLooprangeBounds(const parser::OpenMPLoopConstruct &x);
void CheckDistLinear(const parser::OpenMPLoopConstruct &x);
void CheckSIMDNest(const parser::OpenMPConstruct &x);
- void CheckNestedBlock(const parser::OpenMPLoopConstruct &x,
- const parser::Block &body);
+ void CheckNestedBlock(
+ const parser::OpenMPLoopConstruct &x, const parser::Block &body);
void CheckNestedConstruct(const parser::OpenMPLoopConstruct &x);
void CheckFullUnroll(const parser::OpenMPLoopConstruct &x);
void CheckTargetNest(const parser::OpenMPConstruct &x);
>From c7022e36fe3506aaa18a4a9f8595080fd12f3c88 Mon Sep 17 00:00:00 2001
From: Krzysztof Parzyszek <Krzysztof.Parzyszek at amd.com>
Date: Mon, 8 Dec 2025 10:52:02 -0600
Subject: [PATCH 5/6] Add empty
---
flang/include/flang/Parser/openmp-utils.h | 1 +
1 file changed, 1 insertion(+)
diff --git a/flang/include/flang/Parser/openmp-utils.h b/flang/include/flang/Parser/openmp-utils.h
index c47cdb295da4a..6dac64ecbaf08 100644
--- a/flang/include/flang/Parser/openmp-utils.h
+++ b/flang/include/flang/Parser/openmp-utils.h
@@ -260,6 +260,7 @@ template <bool IsConst> struct LoopRange {
: LoopRange(std::get<Block>(x.t)) {}
size_t size() const { return items.size(); }
+ bool empty() const { return items.size() == 0; }
struct iterator;
>From 11e3f22e0b997a5d9d403b0fd376a0d82d2d5c20 Mon Sep 17 00:00:00 2001
From: Krzysztof Parzyszek <Krzysztof.Parzyszek at amd.com>
Date: Mon, 8 Dec 2025 11:30:45 -0600
Subject: [PATCH 6/6] Fix merge error
---
flang/lib/Semantics/check-omp-loop.cpp | 1 -
1 file changed, 1 deletion(-)
diff --git a/flang/lib/Semantics/check-omp-loop.cpp b/flang/lib/Semantics/check-omp-loop.cpp
index fc269e54abebc..77cc6c9e38c9c 100644
--- a/flang/lib/Semantics/check-omp-loop.cpp
+++ b/flang/lib/Semantics/check-omp-loop.cpp
@@ -370,7 +370,6 @@ void OmpStructureChecker::CheckNestedConstruct(
// do 100 j = ...
// 100 continue
// !$omp end do ! error
- const parser::OmpDirectiveSpecification &beginSpec{x.BeginDir()};
auto &flags{std::get<parser::OmpDirectiveSpecification::Flags>(beginSpec.t)};
if (flags.test(parser::OmpDirectiveSpecification::Flag::CrossesLabelDo)) {
if (auto &endSpec{x.EndDir()}) {
More information about the flang-commits
mailing list