[flang-commits] [flang] 8593524 - [flang][semantics][openacc] Allow collapse clauses on do concurrent (#192488)
via flang-commits
flang-commits at lists.llvm.org
Mon Apr 27 07:42:33 PDT 2026
Author: Andre Kuhlenschmidt
Date: 2026-04-27T07:42:28-07:00
New Revision: 85935241b74f332a2d8c616510b7ef74ebe6a1ac
URL: https://github.com/llvm/llvm-project/commit/85935241b74f332a2d8c616510b7ef74ebe6a1ac
DIFF: https://github.com/llvm/llvm-project/commit/85935241b74f332a2d8c616510b7ef74ebe6a1ac.diff
LOG: [flang][semantics][openacc] Allow collapse clauses on do concurrent (#192488)
This PR generalizes the semantic checking for collapse clauses to work
on `do concurrent` and fixes two bugs exposed along the way:
- The first was that `collapse (n)` where n < the number of nested loops
was giving an assertion violation.
- The second was do concurrent index variables were causing an assertion
violation because they hadn't been declared before looking them up.
The lowering is implemented as a TODO which will happen in a following
diff.
Added:
Modified:
flang/lib/Lower/OpenACC.cpp
flang/lib/Semantics/canonicalize-acc.cpp
flang/lib/Semantics/resolve-directives.cpp
flang/test/Lower/OpenACC/Todo/do-loops-to-acc-loops-todo.f90
flang/test/Semantics/OpenACC/acc-canonicalization-validity.f90
flang/test/Semantics/OpenACC/acc-loop.f90
Removed:
################################################################################
diff --git a/flang/lib/Lower/OpenACC.cpp b/flang/lib/Lower/OpenACC.cpp
index 48d59bd70a853..bcb77fdfd52b1 100644
--- a/flang/lib/Lower/OpenACC.cpp
+++ b/flang/lib/Lower/OpenACC.cpp
@@ -1538,9 +1538,11 @@ static void visitLoopControl(
break; // No deeper loop; stop collecting collapsed bounds.
Fortran::lower::markDoConstructAsCollapsed(*innerDo);
- loopControl = &*innerDo->GetLoopControl();
mlir::Location loc =
converter.genLocation(Fortran::parser::FindSourceLocation(*innerDo));
+ if (innerDo->IsDoConcurrent())
+ TODO(loc, "OpenACC LOOP with nested DO CONCURRENT");
+ loopControl = &*innerDo->GetLoopControl();
callback(std::get<Fortran::parser::LoopControl::Bounds>(loopControl->u),
loc);
}
@@ -1917,7 +1919,7 @@ static void privatizeInductionVariables(
llvm::SmallVector<mlir::Type> ivTypes;
llvm::SmallVector<mlir::Location> ivLocs;
assert(!outerDoConstruct.IsDoConcurrent() &&
- "do concurrent loops are not expected to contained earlty exits");
+ "do concurrent loops are not expected to contained early exits");
visitLoopControl(converter, outerDoConstruct, loopsToProcess, eval,
[&](const Fortran::parser::LoopControl::Bounds &bounds,
mlir::Location loc) {
@@ -2237,6 +2239,11 @@ static mlir::acc::LoopOp createLoopOp(
uint64_t loopsToProcess =
Fortran::lower::getLoopCountForCollapseAndTile(accClauseList);
+
+ if (outerDoConstruct.IsDoConcurrent() &&
+ Fortran::lower::getCollapseSizeAndForce(accClauseList).first > 1)
+ TODO(currentLocation, "OpenACC LOOP COLLAPSE with DO CONCURRENT");
+
auto loopOp = buildACCLoopOp(
converter, currentLocation, semanticsContext, stmtCtx, outerDoConstruct,
eval, privateOperands, dataMap, gangOperands, workerNumOperands,
diff --git a/flang/lib/Semantics/canonicalize-acc.cpp b/flang/lib/Semantics/canonicalize-acc.cpp
index 9d2d2ce3a82fb..b878b500963fa 100644
--- a/flang/lib/Semantics/canonicalize-acc.cpp
+++ b/flang/lib/Semantics/canonicalize-acc.cpp
@@ -108,11 +108,6 @@ class CanonicalizationOfAcc {
"TILE clause may not appear on loop construct "
"associated with DO CONCURRENT"_err_en_US);
}
- if (std::holds_alternative<parser::AccClause::Collapse>(clause.u)) {
- messages_.Say(beginLoopDirective.source,
- "COLLAPSE clause may not appear on loop construct "
- "associated with DO CONCURRENT"_err_en_US);
- }
}
}
diff --git a/flang/lib/Semantics/resolve-directives.cpp b/flang/lib/Semantics/resolve-directives.cpp
index fa6a957ee3831..f54a73322ab96 100644
--- a/flang/lib/Semantics/resolve-directives.cpp
+++ b/flang/lib/Semantics/resolve-directives.cpp
@@ -1677,34 +1677,68 @@ void AccAttributeVisitor::CheckAssociatedLoop(
Symbol::Flag flag = Symbol::Flag::AccPrivate;
llvm::SmallVector<Symbol *> ivs;
- using Bounds = parser::LoopControl::Bounds;
+
+ // Iterate the index variables of one DoConstruct, calling fn(name, lower,
+ // upper) for each: once for a regular do loop, once per control variable for
+ // a do concurrent loop. Null pointers signal a loop without valid bounds
+ // (e.g. do while); the level must still be consumed.
+ auto forEachIndex = [this](const parser::DoConstruct &loop, auto &&fn) {
+ if (loop.IsDoConcurrent()) {
+ const auto &loopControl{*loop.GetLoopControl()};
+ const auto &concurrent{
+ std::get<parser::LoopControl::Concurrent>(loopControl.u)};
+ const auto &header{std::get<parser::ConcurrentHeader>(concurrent.t)};
+ for (const auto &control :
+ std::get<std::list<parser::ConcurrentControl>>(header.t)) {
+ fn(&std::get<parser::Name>(control.t),
+ &parser::UnwrapRef<parser::Expr>(std::get<1>(control.t)),
+ &parser::UnwrapRef<parser::Expr>(std::get<2>(control.t)));
+ }
+ } else {
+ auto bounds{GetLoopBounds(loop)};
+ const parser::ScalarExpr *lower{std::get<1>(bounds)};
+ const parser::ScalarExpr *upper{std::get<2>(bounds)};
+ fn(std::get<0>(bounds),
+ lower ? &parser::UnwrapRef<parser::Expr>(*lower) : nullptr,
+ upper ? &parser::UnwrapRef<parser::Expr>(*upper) : nullptr);
+ }
+ };
+
for (const parser::DoConstruct *loop{&outerDoConstruct}; loop && level > 0;) {
- // Go through all nested loops to ensure index variable exists.
- if (const parser::Name *ivName{GetLoopIndex(*loop)}) {
- if (auto *symbol{ResolveAcc(*ivName, flag, currScope())}) {
- if (auto &control{loop->GetLoopControl()}) {
- if (const Bounds *b{std::get_if<Bounds>(&control->u)}) {
- if (auto lowerExpr{semantics::AnalyzeExpr(context_, b->Lower())}) {
- semantics::UnorderedSymbolSet lowerSyms =
- evaluate::CollectSymbols(*lowerExpr);
- checkExprHasSymbols(ivs, lowerSyms);
- }
- if (auto upperExpr{semantics::AnalyzeExpr(context_, b->Upper())}) {
- semantics::UnorderedSymbolSet upperSyms =
- evaluate::CollectSymbols(*upperExpr);
- checkExprHasSymbols(ivs, upperSyms);
+ forEachIndex(*loop,
+ [&](const parser::Name *ivName, const parser::Expr *lower,
+ const parser::Expr *upper) {
+ if (level <= 0)
+ return;
+ if (ivName && lower && upper) {
+ if (auto *symbol{ResolveAcc(*ivName, flag, currScope())}) {
+ if (auto lowerExpr{semantics::AnalyzeExpr(context_, *lower)}) {
+ semantics::UnorderedSymbolSet lowerSyms =
+ evaluate::CollectSymbols(*lowerExpr);
+ checkExprHasSymbols(ivs, lowerSyms);
+ }
+ if (auto upperExpr{semantics::AnalyzeExpr(context_, *upper)}) {
+ semantics::UnorderedSymbolSet upperSyms =
+ evaluate::CollectSymbols(*upperExpr);
+ checkExprHasSymbols(ivs, upperSyms);
+ }
+ ivs.push_back(symbol);
}
}
- }
- ivs.push_back(symbol);
- }
- }
+ --level;
+ });
const auto &block{std::get<parser::Block>(loop->t)};
- --level;
loop = getNextDoConstruct(block, level);
}
- CHECK(level == 0);
+
+ if (level != 0) {
+ context_.Say(GetContext().directiveSource,
+ "Not enough %s for COLLAPSE(%jd) clause, found %jd, expected %jd more"_err_en_US,
+ forceCollapsed ? "nested loops" : "perfectly nested loops",
+ GetContext().associatedLoopLevel,
+ GetContext().associatedLoopLevel - level, level);
+ }
}
void AccAttributeVisitor::EnsureAllocatableOrPointer(
diff --git a/flang/test/Lower/OpenACC/Todo/do-loops-to-acc-loops-todo.f90 b/flang/test/Lower/OpenACC/Todo/do-loops-to-acc-loops-todo.f90
index 3f2b77a9a1484..aa293642b0865 100644
--- a/flang/test/Lower/OpenACC/Todo/do-loops-to-acc-loops-todo.f90
+++ b/flang/test/Lower/OpenACC/Todo/do-loops-to-acc-loops-todo.f90
@@ -3,6 +3,8 @@
! RUN: %not_todo_cmd bbc -fopenacc -emit-hlfir %t/do_loop_with_cycle_goto.f90 -o - 2>&1 | FileCheck %s --check-prefix=CHECK2
! RUN: %not_todo_cmd bbc -fopenacc -emit-hlfir %t/nested_goto_loop.f90 -o - 2>&1 | FileCheck %s --check-prefix=CHECK3
! RUN: %not_todo_cmd bbc -fopenacc -emit-hlfir %t/nested_loop_with_inner_goto.f90 -o - 2>&1 | FileCheck %s --check-prefix=CHECK4
+! RUN: %not_todo_cmd bbc -fopenacc -emit-hlfir %t/collapse.f90 -o - 2>&1 | FileCheck %s --check-prefix=CHECK5
+! RUN: %not_todo_cmd bbc -fopenacc -emit-hlfir %t/collapse_nested.f90 -o - 2>&1 | FileCheck %s --check-prefix=CHECK6
//--- do_loop_with_stop.f90
@@ -89,3 +91,34 @@ subroutine nested_loop_with_inner_goto()
! CHECK4: not yet implemented: unstructured do loop in acc kernels
end subroutine
+
+//--- collapse.f90
+
+! !$acc parallel loop collapse(N) over a do concurrent.
+subroutine combined(i, j, k)
+ integer :: i, j, k
+ integer :: a(i,j,k)
+ !$acc parallel loop collapse(3)
+ do concurrent (i=1:10, j=1:100, k=1:200)
+ a(i,j,k) = a(i,j,k) + 1
+ end do
+ ! CHECK5: not yet implemented: OpenACC LOOP COLLAPSE with DO CONCURRENT
+end subroutine
+
+
+//--- collapse_nested.f90
+
+! !$acc parallel loop collapse(N) over a nested do concurrent.
+subroutine combined(i, j, k)
+ integer :: i, j, k
+ integer :: a(i,j,k)
+ !$acc parallel loop collapse(3)
+ do i = 1, 10
+ do concurrent (j=1:100, k=1:200)
+ a(i,j,k) = a(i,j,k) + 1
+ end do
+ end do
+ ! CHECK6: not yet implemented: OpenACC LOOP with nested DO CONCURRENT
+
+end subroutine
+
diff --git a/flang/test/Semantics/OpenACC/acc-canonicalization-validity.f90 b/flang/test/Semantics/OpenACC/acc-canonicalization-validity.f90
index a92be44c60b74..3151d726380f1 100644
--- a/flang/test/Semantics/OpenACC/acc-canonicalization-validity.f90
+++ b/flang/test/Semantics/OpenACC/acc-canonicalization-validity.f90
@@ -85,7 +85,6 @@ program openacc_clause_validity
end do
!$acc parallel
- !ERROR: COLLAPSE clause may not appear on loop construct associated with DO CONCURRENT
!$acc loop collapse(2)
do concurrent (i = 1:N, j = 1:N)
aa(i, j) = 3.14
@@ -102,7 +101,6 @@ program openacc_clause_validity
!$acc parallel
!ERROR: TILE clause may not appear on loop construct associated with DO CONCURRENT
- !ERROR: COLLAPSE clause may not appear on loop construct associated with DO CONCURRENT
!$acc loop tile(2, 2) collapse(2)
do concurrent (i = 1:N, j = 1:N)
aa(i, j) = 3.14
diff --git a/flang/test/Semantics/OpenACC/acc-loop.f90 b/flang/test/Semantics/OpenACC/acc-loop.f90
index 635dbb04cd666..0358b2fa4e1c6 100644
--- a/flang/test/Semantics/OpenACC/acc-loop.f90
+++ b/flang/test/Semantics/OpenACC/acc-loop.f90
@@ -447,6 +447,97 @@ program openacc_loop_validity
END DO
END DO
+ ! do concurrent: each index variable counts as one collapse level.
+
+ ! Valid: collapse(2) covers both indices of a 2-index do concurrent.
+ !$acc loop collapse(2)
+ DO CONCURRENT (i = 1:n, j = 1:n)
+ aa(i, j) = 3.14d0
+ END DO
+
+ ! Valid: collapse(3) covers both concurrent indices then one nested do.
+ !$acc loop collapse(3)
+ DO CONCURRENT (i = 1:n, j = 1:n)
+ DO k = 1, n
+ aa(i, j) = aa(i, j) + a(k)
+ END DO
+ END DO
+
+ ! Valid: collapse(2) with single-index do concurrent followed by a nested do.
+ !$acc loop collapse(2)
+ DO CONCURRENT (i = 1:n)
+ DO j = 1, n
+ aa(i, j) = 3.14d0
+ END DO
+ END DO
+
+ ! Valid: combined directive, collapse(2) with do concurrent.
+ !$acc parallel loop collapse(2)
+ DO CONCURRENT (i = 1:n, j = 1:n)
+ aa(i, j) = 3.14d0
+ END DO
+
+ ! Valid: outer regular do followed by inner do concurrent covering the
+ ! remaining collapse levels.
+ !$acc loop collapse(3)
+ DO i = 1, n
+ DO CONCURRENT (j = 1:n, k = 1:n)
+ aa(i, j) = aa(i, j) + a(k)
+ END DO
+ END DO
+
+ ! Valid (more concurrent indices than collapse levels): collapse(2) consumes
+ ! only the first two indices of a 3-index do concurrent; the third is outside
+ ! the collapsed nest.
+ !$acc loop collapse(2)
+ DO CONCURRENT (i = 1:n, j = 1:n, k = 1:n)
+ aa(i, j) = aa(i, j) + a(k)
+ END DO
+
+ ! Valid (more loops than collapse levels): collapse(1) consumes only the
+ ! first index of a 2-index do concurrent; the second index is outside the
+ ! collapsed nest.
+ !$acc loop collapse(1)
+ DO CONCURRENT (i = 1:n, j = 1:n)
+ aa(i, j) = 3.14d0
+ END DO
+
+ ! Invalid: nested do's upper bound depends on a collapsed concurrent index.
+ !ERROR: Trip count must be computable and invariant
+ !$acc loop collapse(3)
+ DO CONCURRENT (i = 1:n, j = 1:n)
+ DO k = 1, i
+ aa(i, j) = aa(i, j) + a(k)
+ END DO
+ END DO
+
+ ! Invalid: nested do's upper bound depends on a collapsed concurrent index.
+ !ERROR: Trip count must be computable and invariant
+ !$acc loop collapse(2)
+ DO CONCURRENT (i = 1:n)
+ DO j = 1, i
+ aa(i, j) = 3.14d0
+ END DO
+ END DO
+
+ ! Invalid: inner concurrent index bound depends on the outer collapsed regular
+ ! do index.
+ !ERROR: Trip count must be computable and invariant
+ !$acc loop collapse(3)
+ DO i = 1, n
+ DO CONCURRENT (j = 1:n, k = 1:i)
+ aa(i, j) = aa(i, j) + a(k)
+ END DO
+ END DO
+
+ ! Fewer loops than collapse(n): collapse(3) but only 2 levels exist.
+ ! This exercises the loop-nest depth check.
+ !ERROR: Not enough perfectly nested loops for COLLAPSE(3) clause, found 2, expected 1 more
+ !$acc loop collapse(3)
+ DO CONCURRENT (i = 1:n, j = 1:n)
+ aa(i, j) = 3.14d0
+ END DO
+
contains
subroutine sub1()
More information about the flang-commits
mailing list