[flang-commits] [flang] [flang][OpenMP] Identify DO loops affected by loop-associated construct (PR #191719)
Krzysztof Parzyszek via flang-commits
flang-commits at lists.llvm.org
Wed Apr 15 07:02:23 PDT 2026
https://github.com/kparzysz updated https://github.com/llvm/llvm-project/pull/191719
>From 124455574867b7c26c13c8c60abf5af62482a2b3 Mon Sep 17 00:00:00 2001
From: Krzysztof Parzyszek <Krzysztof.Parzyszek at amd.com>
Date: Sun, 12 Apr 2026 09:02:28 -0500
Subject: [PATCH 1/8] [flang][OpenMP] Implement GetGeneratedNestDepthWithReason
For a loop-nest-generating construct this function returns the number of
loops in the generated loop nest.
A loop-nest-transformation construct can be thought as replacing N nested
loops with K nested loops, where
N = GetAffectedNestDepthWithReason
K = GetGeneratedNestDepthWithReason
---
flang/include/flang/Semantics/openmp-utils.h | 5 ++
flang/lib/Semantics/openmp-utils.cpp | 91 ++++++++++++++------
2 files changed, 72 insertions(+), 24 deletions(-)
diff --git a/flang/include/flang/Semantics/openmp-utils.h b/flang/include/flang/Semantics/openmp-utils.h
index b449988abe9c8..252c88c967750 100644
--- a/flang/include/flang/Semantics/openmp-utils.h
+++ b/flang/include/flang/Semantics/openmp-utils.h
@@ -218,6 +218,11 @@ WithReason<int64_t> GetHeightWithReason(
std::pair<WithReason<int64_t>, bool> GetAffectedNestDepthWithReason(
const parser::OmpDirectiveSpecification &spec, unsigned version,
SemanticsContext *semaCtx = nullptr);
+/// Return the depth of the generated nest(s):
+/// {generated-depth, is-perfect-nest}
+std::pair<WithReason<int64_t>, bool> GetGeneratedNestDepthWithReason(
+ const parser::OmpDirectiveSpecification &spec, unsigned version,
+ SemanticsContext *semaCtx = nullptr);
/// Return the range of the affected nests in the sequence:
/// {first, count}.
/// If the range is "the whole sequence", the return value will be {1, -1}.
diff --git a/flang/lib/Semantics/openmp-utils.cpp b/flang/lib/Semantics/openmp-utils.cpp
index c901d00695093..7a5b31b1c805e 100644
--- a/flang/lib/Semantics/openmp-utils.cpp
+++ b/flang/lib/Semantics/openmp-utils.cpp
@@ -767,37 +767,27 @@ WithReason<int64_t> GetHeightWithReason(
"This construct is not a DO-loop or a loop-transformation construct"_because_en_US);
return {0, reason};
}
- switch (spec.DirName().v) {
- case llvm::omp::Directive::OMPD_flatten:
- if (auto &&value{GetArgumentValueWithReason(
- spec, llvm::omp::Clause::OMPC_depth, version, semaCtx)}) {
- // FLATTEN DEPTH(n) replaces n loops with 1.
- return {int64_t(1) - *value.value, std::move(value.reason)};
- } else {
- Reason reason;
- reason.Say(spec.DirName().source, MsgClauseAbsentAssume,
- GetUpperName(llvm::omp::Clause::OMPC_depth, version), "a depth of 2");
- return {-1, std::move(reason)};
- }
+
+ switch (spec.DirId()) {
+ // These generate loop sequences.
case llvm::omp::Directive::OMPD_fuse:
case llvm::omp::Directive::OMPD_split:
return {0, Reason()};
+ case llvm::omp::Directive::OMPD_flatten:
case llvm::omp::Directive::OMPD_interchange:
case llvm::omp::Directive::OMPD_nothing:
case llvm::omp::Directive::OMPD_reverse:
- return {0, Reason()};
case llvm::omp::Directive::OMPD_stripe:
case llvm::omp::Directive::OMPD_tile:
- return GetNumArgumentsWithReason(
- spec, llvm::omp::Clause::OMPC_sizes, version, semaCtx);
- case llvm::omp::Directive::OMPD_unroll:
- if (isFullUnroll) {
- Reason reason;
- reason.Say(spec.DirName().source, MsgConstructDoesNotResult,
- "Fully unrolled loop", "a loop nest");
- return {-1, std::move(reason)};
+ case llvm::omp::Directive::OMPD_unroll: {
+ auto [cons, _1]{GetAffectedNestDepthWithReason(spec, version, semaCtx)};
+ auto [prod, _2]{GetGeneratedNestDepthWithReason(spec, version, semaCtx)};
+ if (cons && prod) {
+ return WithReason<int64_t>{*prod.value - *cons.value,
+ Reason().Append(cons.reason).Append(prod.reason)};
}
- return {0, Reason()};
+ return {};
+ }
default:
llvm_unreachable("Expecting loop-transforming construct");
}
@@ -1000,6 +990,18 @@ std::pair<WithReason<int64_t>, bool> GetAffectedNestDepthWithReason(
if (IsLoopTransforming(dir)) {
switch (dir) {
+ case llvm::omp::Directive::OMPD_flatten:
+ if (auto &&value{GetArgumentValueWithReason(
+ spec, llvm::omp::Clause::OMPC_depth, version, semaCtx)}) {
+ // FLATTEN DEPTH(n) replaces n loops with 1.
+ return {std::move(value), true};
+ } else {
+ Reason reason;
+ reason.Say(spec.DirName().source, MsgClauseAbsentAssume,
+ GetUpperName(llvm::omp::Clause::OMPC_depth, version), "a depth of 2");
+ return {{2, std::move(reason)}, true};
+ }
+ break;
case llvm::omp::Directive::OMPD_interchange: {
// Get the length of the argument list to PERMUTATION.
if (parser::omp::FindClause(spec, llvm::omp::Clause::OMPC_permutation)) {
@@ -1015,6 +1017,8 @@ std::pair<WithReason<int64_t>, bool> GetAffectedNestDepthWithReason(
spec.source, MsgClauseAbsentAssume, name, "a permutation (2, 1)");
return {{2, std::move(reason)}, true};
}
+ case llvm::omp::Directive::OMPD_nothing:
+ return {WithReason<int64_t>(0), false};
case llvm::omp::Directive::OMPD_stripe:
case llvm::omp::Directive::OMPD_tile: {
// Get the length of the argument list to SIZES.
@@ -1035,10 +1039,9 @@ std::pair<WithReason<int64_t>, bool> GetAffectedNestDepthWithReason(
return {{1, std::move(reason)}, true};
}
case llvm::omp::Directive::OMPD_reverse:
+ case llvm::omp::Directive::OMPD_split:
case llvm::omp::Directive::OMPD_unroll:
return {WithReason<int64_t>(1), false};
- // TODO: case llvm::omp::Directive::OMPD_flatten:
- // TODO: case llvm::omp::Directive::OMPD_split:
default:
break;
}
@@ -1047,6 +1050,46 @@ std::pair<WithReason<int64_t>, bool> GetAffectedNestDepthWithReason(
return {{}, false};
}
+/// Return the depth of the generated nest(s)
+/// {generated-depth, is-perfect-nest}
+std::pair<WithReason<int64_t>, bool> GetGeneratedNestDepthWithReason(
+ const parser::OmpDirectiveSpecification &spec, unsigned version,
+ SemanticsContext *semaCtx) {
+ llvm::omp::Directive dir{spec.DirId()};
+ if (!IsLoopTransforming(dir)) {
+ return {{}, false};
+ }
+
+ auto [depth, _]{GetAffectedNestDepthWithReason(spec, version, semaCtx)};
+
+ switch (dir) {
+ case llvm::omp::Directive::OMPD_flatten:
+ return {WithReason<int64_t>(1), true};
+ case llvm::omp::Directive::OMPD_fuse:
+ case llvm::omp::Directive::OMPD_split:
+ // These result in loop sequences.
+ return {{}, false};
+ case llvm::omp::Directive::OMPD_interchange:
+ case llvm::omp::Directive::OMPD_nothing:
+ case llvm::omp::Directive::OMPD_reverse:
+ return {depth, true};
+ case llvm::omp::Directive::OMPD_stripe:
+ case llvm::omp::Directive::OMPD_tile:
+ if (depth) {
+ return {
+ WithReason<int64_t>(2 * *depth.value, std::move(depth.reason)), true};
+ }
+ return {{}, true};
+ case llvm::omp::Directive::OMPD_unroll:
+ if (IsFullUnroll(spec)) {
+ return {WithReason<int64_t>(0), false};
+ }
+ return {WithReason<int64_t>(1), true};
+ default:
+ return {{}, false};
+ }
+}
+
/// Return the range of the affected nests in the sequence:
/// {first, count}
WithReason<std::pair<int64_t, int64_t>> GetAffectedLoopRangeWithReason(
>From bc23293b1f78161af44d0e4b04b72df6b30ac8b7 Mon Sep 17 00:00:00 2001
From: Krzysztof Parzyszek <Krzysztof.Parzyszek at amd.com>
Date: Thu, 9 Apr 2026 14:24:16 -0500
Subject: [PATCH 2/8] [flang][OpenMP] Identify DO loops affected by
loop-associated construct
This is to identify iteration variables of DO loops affected by an OpenMP
loop construct. These variables are privatized as per data-sharing rules.
---
flang/include/flang/Semantics/openmp-utils.h | 7 ++
flang/lib/Semantics/openmp-utils.cpp | 91 +++++++++++++++++++
flang/lib/Semantics/resolve-directives.cpp | 69 +++++++-------
.../test/Semantics/OpenMP/affected-loops.f90 | 27 ++++++
4 files changed, 158 insertions(+), 36 deletions(-)
create mode 100644 flang/test/Semantics/OpenMP/affected-loops.f90
diff --git a/flang/include/flang/Semantics/openmp-utils.h b/flang/include/flang/Semantics/openmp-utils.h
index 252c88c967750..1924fbe9b093d 100644
--- a/flang/include/flang/Semantics/openmp-utils.h
+++ b/flang/include/flang/Semantics/openmp-utils.h
@@ -242,6 +242,13 @@ std::optional<int64_t> GetMinimumSequenceCount(
std::optional<int64_t> GetMinimumSequenceCount(
std::optional<std::pair<int64_t, int64_t>> range);
+/// Collect the set of DO loops present in the source code that are directly
+/// affected by the given loop construct. This does not include any DO loops
+/// that are affected by any construct nested in `x`.
+std::optional<std::vector<const parser::DoConstruct *>> CollectAffectedDoLoops(
+ const parser::OpenMPLoopConstruct &x, unsigned version,
+ SemanticsContext *semaCtx = nullptr);
+
struct LoopSequence {
LoopSequence(const parser::ExecutionPartConstruct &root, unsigned version,
bool allowAllLoops = false, SemanticsContext *semaCtx = nullptr);
diff --git a/flang/lib/Semantics/openmp-utils.cpp b/flang/lib/Semantics/openmp-utils.cpp
index 7a5b31b1c805e..b09d4a2e207b7 100644
--- a/flang/lib/Semantics/openmp-utils.cpp
+++ b/flang/lib/Semantics/openmp-utils.cpp
@@ -1202,6 +1202,97 @@ std::optional<int64_t> GetMinimumSequenceCount(
return GetMinimumSequenceCount(std::nullopt, std::nullopt);
}
+/// Collect the DO loops that are affected directly by the given loop
+/// transformation. Not all DO loops nested in the associated nest are
+/// affected by the top-level loop transformation, e.g.
+///
+/// !$omp do collapse(5) | [2]
+/// !$omp tile sizes(2, 2) | [1] | <- nest of 4 loops
+/// do i = 1, 10 | <- affected by TILE | generated by TILE
+/// do j = 1, 10 | <- |
+/// do k = 1, 10 | <- affected by DO
+/// end do
+/// end do
+/// end do
+///
+/// The two DO loops (i and j) in [1] are affected by the TILE construct.
+/// The k DO loop is affected by the DO construct [2].
+/// For the top-level DO COLLAPSE(5) construct, the k loop is the only
+/// directly affected loop.
+std::optional<std::vector<const parser::DoConstruct *>> CollectAffectedDoLoops(
+ const parser::OpenMPLoopConstruct &x, unsigned version,
+ SemanticsContext *semaCtx) {
+ std::vector<const parser::DoConstruct *> result;
+ const parser::OmpDirectiveSpecification &spec{x.BeginDir()};
+
+ auto [depth, _]{GetAffectedNestDepthWithReason(spec, version, semaCtx)};
+
+ // If the depth is absent, then there is some issue. Leave it alone here,
+ // and let the semantic checks diagnose the problem.
+ if (!depth) {
+ return std::nullopt;
+ }
+ if (*depth.value <= 0) {
+ return result;
+ }
+
+ // The algorithm is to descend down the nest and keep track of intervening
+ // constructs and how many loops they consume and produce. This is similar
+ // to traversing an expression tree to identify the operands to the top-
+ // level operation:
+ //
+ // ... + + x y z w ...
+ // ^ ^ ^
+ // | | |
+ // | | +-- produces 1 value consumed by the first +
+ // | +-- produces 1 value, but first consumes 2
+ // +-- consumes 2 operands
+ //
+ // The analogous result here would be "z" as the operand to the first +.
+
+ int64_t produced{0};
+ int64_t consuming{0};
+ int64_t level{*depth.value};
+
+ auto visit{[&](const LoopSequence &nest, auto &&self) -> void {
+ const parser::ExecutionPartConstruct *owner{nest.owner()};
+
+ if (auto *doLoop{parser::Unwrap<parser::DoConstruct>(owner)}) {
+ if (consuming == 0) {
+ result.push_back(doLoop);
+ ++produced;
+ } else {
+ --consuming;
+ }
+ } else if (auto *omp{parser::Unwrap<parser::OpenMPLoopConstruct>(owner)}) {
+ const parser::OmpDirectiveSpecification &spec{omp->BeginDir()};
+ auto [cons, _1]{GetAffectedNestDepthWithReason(spec, version, semaCtx)};
+ auto [prod, _2]{GetGeneratedNestDepthWithReason(spec, version, semaCtx)};
+ if (!cons || !prod) {
+ return;
+ }
+ if (*prod.value <= consuming) {
+ consuming -= *prod.value;
+ } else {
+ produced += (*prod.value - consuming);
+ consuming = 0;
+ }
+ consuming += *cons.value;
+ }
+
+ if (produced < level) {
+ for (const LoopSequence &child : nest.children()) {
+ self(child, self);
+ }
+ }
+ }};
+
+ LoopSequence sequence(std::get<parser::Block>(x.t), version, true, semaCtx);
+ visit(sequence, visit);
+
+ return result;
+}
+
#ifdef EXPENSIVE_CHECKS
namespace {
/// Check that for every value x of type T, there will be a "source" member
diff --git a/flang/lib/Semantics/resolve-directives.cpp b/flang/lib/Semantics/resolve-directives.cpp
index c50c76647962c..6b42a7290e260 100644
--- a/flang/lib/Semantics/resolve-directives.cpp
+++ b/flang/lib/Semantics/resolve-directives.cpp
@@ -1114,8 +1114,7 @@ class OmpAttributeVisitor : DirectiveAttributeVisitor<llvm::omp::Directive> {
}
// Predetermined DSA rules
- void PrivatizeAssociatedLoopIndexAndCheckLoopLevel(
- const parser::OpenMPLoopConstruct &);
+ void PrivatizeAssociatedLoopIndex(const parser::OpenMPLoopConstruct &);
void ResolveSeqLoopIndexInParallelOrTaskConstruct(const parser::Name &);
bool IsNestedInDirective(llvm::omp::Directive directive);
@@ -2068,7 +2067,7 @@ bool OmpAttributeVisitor::Pre(const parser::OpenMPLoopConstruct &x) {
}
}
- PrivatizeAssociatedLoopIndexAndCheckLoopLevel(x);
+ PrivatizeAssociatedLoopIndex(x);
return true;
}
@@ -2162,13 +2161,16 @@ bool OmpAttributeVisitor::Pre(const parser::DoConstruct &x) {
// increment of the associated do-loop (only for OpenMP versions <= 4.5)
// - The loop iteration variables in the associated do-loops of a simd
// construct with multiple associated do-loops are lastprivate.
-void OmpAttributeVisitor::PrivatizeAssociatedLoopIndexAndCheckLoopLevel(
+void OmpAttributeVisitor::PrivatizeAssociatedLoopIndex(
const parser::OpenMPLoopConstruct &x) {
+ const parser::OmpDirectiveSpecification &spec{x.BeginDir()};
unsigned version{context_.langOptions().OpenMPVersion};
+
auto [depth, _]{
- omp::GetAffectedNestDepthWithReason(x.BeginDir(), version, &context_)};
- // If there was a problem obtaining the depth, it will be diagnosed in
- // the semantic checks.
+ omp::GetAffectedNestDepthWithReason(spec, version, &context_)};
+
+ // If depth is absent, then there is some issue. Leave it alone here,
+ // and let the semantic checks diagnose the problem.
if (!depth || *depth.value <= 0) {
return;
}
@@ -2183,35 +2185,30 @@ void OmpAttributeVisitor::PrivatizeAssociatedLoopIndexAndCheckLoopLevel(
ivDSA = Symbol::Flag::OmpLastPrivate;
}
- for (auto &construct : std::get<parser::Block>(x.t)) {
- if (const auto *innermostConstruct{parser::omp::GetOmpLoop(construct)}) {
- PrivatizeAssociatedLoopIndexAndCheckLoopLevel(*innermostConstruct);
- } else if (const auto *doConstruct{
- parser::omp::GetDoConstruct(construct)}) {
- for (const parser::DoConstruct *loop{&*doConstruct}; loop && level > 0;
- --level) {
- // go through all the nested do-loops and resolve index variables
- if (const parser::Name *iv{GetLoopIndex(*loop)}) {
- if (!iv->symbol || !IsLocalInsideScope(*iv->symbol, currScope())) {
- if (auto *symbol{ResolveOmp(*iv, ivDSA, currScope())}) {
- 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());
- }
- }
- SetSymbolDSA(*symbol, {Symbol::Flag::OmpPreDetermined, ivDSA});
- 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;
- }
+ auto checkThreadprivate{[&](const parser::Name &iv) {
+ if (const auto *details{iv.symbol->detailsIf<HostAssocDetails>()}) {
+ if (details->symbol().test(Symbol::Flag::OmpThreadprivate)) {
+ context_.Say(iv.source,
+ "Loop iteration variable %s is not allowed in THREADPRIVATE."_err_en_US,
+ iv.ToString());
+ }
+ }
+ }};
+
+ Scope &scope{currScope()};
+
+ if (auto doLoops{omp::CollectAffectedDoLoops(x, version, &context_)}) {
+ for (const parser::DoConstruct *loop : *doLoops) {
+ const parser::Name *iv{GetLoopIndex(*loop)};
+ if (!iv || (iv->symbol && IsLocalInsideScope(*iv->symbol, scope))) {
+ continue;
+ }
+
+ if (auto *symbol{ResolveOmp(*iv, ivDSA, scope)}) {
+ checkThreadprivate(*iv);
+ SetSymbolDSA(*symbol, {Symbol::Flag::OmpPreDetermined, ivDSA});
+ iv->symbol = symbol; // adjust the symbol within region
+ AddToContextObjectWithDSA(*symbol, ivDSA);
}
}
}
diff --git a/flang/test/Semantics/OpenMP/affected-loops.f90 b/flang/test/Semantics/OpenMP/affected-loops.f90
new file mode 100644
index 0000000000000..01273ec4725ec
--- /dev/null
+++ b/flang/test/Semantics/OpenMP/affected-loops.f90
@@ -0,0 +1,27 @@
+!RUN: %flang_fc1 -fdebug-dump-symbols -fopenmp -fopenmp-version=60 %s | FileCheck %s
+
+subroutine f
+ integer :: i, j, k
+ !$omp do collapse(5)
+ !$omp tile sizes(2, 2)
+ do i = 1, 10
+ do j = 1, 10
+ do k = 1, 10
+ end do
+ end do
+ end do
+end
+
+! Check that k is privatized in the scope of DO, and that i, j are privatized
+! in the scope of TILE.
+
+!CHECK: Subprogram scope: f size=12 alignment=4 sourceRange=139 bytes
+!CHECK: f (Subroutine): HostAssoc => f (Subroutine): Subprogram ()
+!CHECK: i size=4 offset=0: ObjectEntity type: INTEGER(4)
+!CHECK: j size=4 offset=4: ObjectEntity type: INTEGER(4)
+!CHECK: k size=4 offset=8: ObjectEntity type: INTEGER(4)
+!CHECK: OtherConstruct scope: size=0 alignment=1 sourceRange=98 bytes
+!CHECK: k (OmpPrivate, OmpPreDetermined): HostAssoc => k size=4 offset=8: ObjectEntity type: INTEGER(4)
+!CHECK: OtherConstruct scope: size=0 alignment=1 sourceRange=77 bytes
+!CHECK: i (OmpPrivate, OmpPreDetermined): HostAssoc => i size=4 offset=0: ObjectEntity type: INTEGER(4)
+!CHECK: j (OmpPrivate, OmpPreDetermined): HostAssoc => j size=4 offset=4: ObjectEntity type: INTEGER(4)
>From 5c5b8ea7c1ef85da9c2d533b1531de959337935a Mon Sep 17 00:00:00 2001
From: Krzysztof Parzyszek <Krzysztof.Parzyszek at amd.com>
Date: Sun, 12 Apr 2026 10:17:22 -0500
Subject: [PATCH 3/8] format
---
flang/lib/Semantics/openmp-utils.cpp | 3 ++-
1 file changed, 2 insertions(+), 1 deletion(-)
diff --git a/flang/lib/Semantics/openmp-utils.cpp b/flang/lib/Semantics/openmp-utils.cpp
index 7a5b31b1c805e..436ee35664f2c 100644
--- a/flang/lib/Semantics/openmp-utils.cpp
+++ b/flang/lib/Semantics/openmp-utils.cpp
@@ -998,7 +998,8 @@ std::pair<WithReason<int64_t>, bool> GetAffectedNestDepthWithReason(
} else {
Reason reason;
reason.Say(spec.DirName().source, MsgClauseAbsentAssume,
- GetUpperName(llvm::omp::Clause::OMPC_depth, version), "a depth of 2");
+ GetUpperName(llvm::omp::Clause::OMPC_depth, version),
+ "a depth of 2");
return {{2, std::move(reason)}, true};
}
break;
>From 3cfc65a2112d00a9df1ff0df802bf129a5dd0766 Mon Sep 17 00:00:00 2001
From: Krzysztof Parzyszek <Krzysztof.Parzyszek at amd.com>
Date: Mon, 13 Apr 2026 16:56:03 -0500
Subject: [PATCH 4/8] Rename local variable
---
flang/lib/Semantics/openmp-utils.cpp | 6 +++---
1 file changed, 3 insertions(+), 3 deletions(-)
diff --git a/flang/lib/Semantics/openmp-utils.cpp b/flang/lib/Semantics/openmp-utils.cpp
index 10e825a3a136c..4ff29ed9e25c8 100644
--- a/flang/lib/Semantics/openmp-utils.cpp
+++ b/flang/lib/Semantics/openmp-utils.cpp
@@ -1266,9 +1266,9 @@ std::optional<std::vector<const parser::DoConstruct *>> CollectAffectedDoLoops(
--consuming;
}
} else if (auto *omp{parser::Unwrap<parser::OpenMPLoopConstruct>(owner)}) {
- const parser::OmpDirectiveSpecification &spec{omp->BeginDir()};
- auto [cons, _1]{GetAffectedNestDepthWithReason(spec, version, semaCtx)};
- auto [prod, _2]{GetGeneratedNestDepthWithReason(spec, version, semaCtx)};
+ const parser::OmpDirectiveSpecification &ods{omp->BeginDir()};
+ auto [cons, _1]{GetAffectedNestDepthWithReason(ods, version, semaCtx)};
+ auto [prod, _2]{GetGeneratedNestDepthWithReason(ods, version, semaCtx)};
if (!cons || !prod) {
return;
}
>From 387ff9c4791b954ac534c7bd622e2998e7f5d902 Mon Sep 17 00:00:00 2001
From: Krzysztof Parzyszek <Krzysztof.Parzyszek at amd.com>
Date: Wed, 15 Apr 2026 08:36:55 -0500
Subject: [PATCH 5/8] Detect issues getting cons/prod in the middle of nest
---
flang/lib/Semantics/openmp-utils.cpp | 16 ++++++++++------
1 file changed, 10 insertions(+), 6 deletions(-)
diff --git a/flang/lib/Semantics/openmp-utils.cpp b/flang/lib/Semantics/openmp-utils.cpp
index 4ff29ed9e25c8..59d80474727f9 100644
--- a/flang/lib/Semantics/openmp-utils.cpp
+++ b/flang/lib/Semantics/openmp-utils.cpp
@@ -1255,7 +1255,7 @@ std::optional<std::vector<const parser::DoConstruct *>> CollectAffectedDoLoops(
int64_t consuming{0};
int64_t level{*depth.value};
- auto visit{[&](const LoopSequence &nest, auto &&self) -> void {
+ auto visit{[&](const LoopSequence &nest, auto &&self) -> bool {
const parser::ExecutionPartConstruct *owner{nest.owner()};
if (auto *doLoop{parser::Unwrap<parser::DoConstruct>(owner)}) {
@@ -1270,7 +1270,7 @@ std::optional<std::vector<const parser::DoConstruct *>> CollectAffectedDoLoops(
auto [cons, _1]{GetAffectedNestDepthWithReason(ods, version, semaCtx)};
auto [prod, _2]{GetGeneratedNestDepthWithReason(ods, version, semaCtx)};
if (!cons || !prod) {
- return;
+ return false;
}
if (*prod.value <= consuming) {
consuming -= *prod.value;
@@ -1281,17 +1281,21 @@ std::optional<std::vector<const parser::DoConstruct *>> CollectAffectedDoLoops(
consuming += *cons.value;
}
+ bool success{true};
if (produced < level) {
for (const LoopSequence &child : nest.children()) {
- self(child, self);
+ success = success && self(child, self);
}
}
+
+ return success;
}};
LoopSequence sequence(std::get<parser::Block>(x.t), version, true, semaCtx);
- visit(sequence, visit);
-
- return result;
+ if (visit(sequence, visit)) {
+ return result;
+ }
+ return std::nullopt;
}
#ifdef EXPENSIVE_CHECKS
>From 2be9a7fd3a66fe6179968ac96af1e465c33857da Mon Sep 17 00:00:00 2001
From: Krzysztof Parzyszek <Krzysztof.Parzyszek at amd.com>
Date: Wed, 15 Apr 2026 08:42:11 -0500
Subject: [PATCH 6/8] Depth should not be negative
---
flang/lib/Semantics/openmp-utils.cpp | 3 ++-
1 file changed, 2 insertions(+), 1 deletion(-)
diff --git a/flang/lib/Semantics/openmp-utils.cpp b/flang/lib/Semantics/openmp-utils.cpp
index 59d80474727f9..4fd61fffb4c01 100644
--- a/flang/lib/Semantics/openmp-utils.cpp
+++ b/flang/lib/Semantics/openmp-utils.cpp
@@ -1233,9 +1233,10 @@ std::optional<std::vector<const parser::DoConstruct *>> CollectAffectedDoLoops(
if (!depth) {
return std::nullopt;
}
- if (*depth.value <= 0) {
+ if (*depth.value == 0) {
return result;
}
+ assert(*depth.value > 0 && "Expecting positive depth");
// The algorithm is to descend down the nest and keep track of intervening
// constructs and how many loops they consume and produce. This is similar
>From 25264b6f414cc48f2c9ec3f8657fac53e71d3432 Mon Sep 17 00:00:00 2001
From: Krzysztof Parzyszek <Krzysztof.Parzyszek at amd.com>
Date: Wed, 15 Apr 2026 08:52:57 -0500
Subject: [PATCH 7/8] Check if enough loops were present
---
flang/lib/Semantics/openmp-utils.cpp | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/flang/lib/Semantics/openmp-utils.cpp b/flang/lib/Semantics/openmp-utils.cpp
index 4fd61fffb4c01..58aefe2e1fc52 100644
--- a/flang/lib/Semantics/openmp-utils.cpp
+++ b/flang/lib/Semantics/openmp-utils.cpp
@@ -1289,7 +1289,7 @@ std::optional<std::vector<const parser::DoConstruct *>> CollectAffectedDoLoops(
}
}
- return success;
+ return success && produced >= level;
}};
LoopSequence sequence(std::get<parser::Block>(x.t), version, true, semaCtx);
>From f22071a2ecd8516232e4709b377ec545606c1792 Mon Sep 17 00:00:00 2001
From: Krzysztof Parzyszek <Krzysztof.Parzyszek at amd.com>
Date: Wed, 15 Apr 2026 08:57:58 -0500
Subject: [PATCH 8/8] Update comment about returning std::nullopt
---
flang/include/flang/Semantics/openmp-utils.h | 2 ++
1 file changed, 2 insertions(+)
diff --git a/flang/include/flang/Semantics/openmp-utils.h b/flang/include/flang/Semantics/openmp-utils.h
index 1924fbe9b093d..665d5f221b43a 100644
--- a/flang/include/flang/Semantics/openmp-utils.h
+++ b/flang/include/flang/Semantics/openmp-utils.h
@@ -245,6 +245,8 @@ std::optional<int64_t> GetMinimumSequenceCount(
/// Collect the set of DO loops present in the source code that are directly
/// affected by the given loop construct. This does not include any DO loops
/// that are affected by any construct nested in `x`.
+/// Returns std::nullopt if `x` or code nested in `x` was malformed in a
+/// way that prevented the function from returning an accurate result.
std::optional<std::vector<const parser::DoConstruct *>> CollectAffectedDoLoops(
const parser::OpenMPLoopConstruct &x, unsigned version,
SemanticsContext *semaCtx = nullptr);
More information about the flang-commits
mailing list