[flang-commits] [flang] [FLANG][OpenMP] Handling pure directives in DO CONCURRENT (PR #216642)
via flang-commits
flang-commits at lists.llvm.org
Sun Aug 16 23:06:15 PDT 2026
https://github.com/ShashwathiNavada updated https://github.com/llvm/llvm-project/pull/216642
>From 4b13bf0d1fb507723914c1fbfcc0aff5283924c0 Mon Sep 17 00:00:00 2001
From: Shashwathi N <nshashwa at pe34genoa.hpc.amslabs.hpecorp.net>
Date: Mon, 17 Aug 2026 00:57:19 -0500
Subject: [PATCH 1/2] [FLANG][OpenMP] Handling pure directives in DO CONCURRENt
---
flang/docs/OpenMPSupport.md | 2 +-
flang/lib/Semantics/check-omp-structure.cpp | 52 +++++++++++++++----
flang/lib/Semantics/check-omp-structure.h | 4 ++
.../OpenMP/do-concurrent-pure-version.f90 | 22 ++++++++
.../Semantics/OpenMP/do-concurrent-pure.f90 | 48 +++++++++++++++++
5 files changed, 117 insertions(+), 11 deletions(-)
create mode 100644 flang/test/Semantics/OpenMP/do-concurrent-pure-version.f90
create mode 100644 flang/test/Semantics/OpenMP/do-concurrent-pure.f90
diff --git a/flang/docs/OpenMPSupport.md b/flang/docs/OpenMPSupport.md
index 63c210ca6d966..1a9d92c9424a1 100644
--- a/flang/docs/OpenMPSupport.md
+++ b/flang/docs/OpenMPSupport.md
@@ -234,7 +234,7 @@ Parser/Semantics, MLIR, Lowering, or the OpenMPIRBuilder.
| loop construct with DO CONCURRENT | <span class="progress">in progress</span> | | Experimental support is available and under active development. | [llvm/llvm-project#178138](https://github.com/llvm/llvm-project/pull/178138), [llvm/llvm-project#190990](https://github.com/llvm/llvm-project/pull/190990) |
| optional argument for all clauses | <span class="part">partial</span> | | Semantics coverage exists across clause tests (for example `flang/test/Semantics/OpenMP/if-clause-50.f90`). | |
| canonical loop sequences | <span class="part">partial</span> | | Related loop/transform coverage exists (`flang/test/Semantics/OpenMP/loop-transformation-construct01.f90`, `flang/test/Lower/OpenMP/loop-directive.f90`). | [llvm/llvm-project#161213](https://github.com/llvm/llvm-project/pull/161213), [llvm/llvm-project#168884](https://github.com/llvm/llvm-project/pull/168884), [llvm/llvm-project#170734](https://github.com/llvm/llvm-project/pull/170734), [llvm/llvm-project#170735](https://github.com/llvm/llvm-project/pull/170735) |
-| pure directives in DO CONCURRENT | <span class="none">unclaimed</span> | | Define exact PURE+DO CONCURRENT directive legality in semantics and add lowering tests proving accepted forms remain side-effect safe. | |
+| pure directives in DO CONCURRENT | <span class="part">partial</span> | | Semantics enforce that OpenMP directives inside a DO CONCURRENT body must have the "pure" property. | |
| pure procedure support extended to more directives | <span class="good">done</span> | | Semantics extend the OpenMP 5.2 pure-procedure allow list with the newly added loop-transforming constructs and the scan directive. | [llvm/llvm-project#212676](https://github.com/llvm/llvm-project/pull/212676) |
| extensions to depobj construct | <span class="none">unclaimed</span> | | Semantics and deprecation diagnostics exist for several depobj forms, but extension support remains incomplete in lowering (for example `flang/test/Lower/OpenMP/Todo/depobj-construct.f90`). Implement lowering for extension operands/modifiers and add non-TODO lowering tests. | |
| extensions to atomic construct | <span class="part">partial</span> | | Atomic compare lowering is now available (`flang/test/Lower/OpenMP/atomic-compare.f90`), but fail/capture-related extension paths remain incomplete (`flang/test/Lower/OpenMP/Todo/atomic-compare-fail.f90`). | [llvm/llvm-project#184761](https://github.com/llvm/llvm-project/pull/184761) |
diff --git a/flang/lib/Semantics/check-omp-structure.cpp b/flang/lib/Semantics/check-omp-structure.cpp
index 274efa64d03d0..204c2c49ef401 100644
--- a/flang/lib/Semantics/check-omp-structure.cpp
+++ b/flang/lib/Semantics/check-omp-structure.cpp
@@ -957,12 +957,8 @@ void OmpStructureChecker::CheckDirectiveDeprecation(
// one another, but only the top-level directive should cause a warning.
}
-void OmpStructureChecker::CheckDirectiveInPureProcedure(
- parser::CharBlock source, llvm::omp::Directive id) {
- const Scope &scope{context_.FindScope(source)};
- if (!FindPureProcedureContaining(scope)) {
- return;
- }
+void OmpStructureChecker::CheckDirectivePureSince(
+ parser::CharBlock source, llvm::omp::Directive id, const char *where) {
unsigned version{context_.langOptions().OpenMPVersion};
// A directive's "pure" property is version-specific: pureSince is the
// OpenMP version at which the directive gained that property.
@@ -972,13 +968,47 @@ void OmpStructureChecker::CheckDirectiveInPureProcedure(
}
if (pureSince != 0x7FFFFFFF) {
context_.Say(source,
- "The OpenMP directive '%s' is not allowed in a PURE procedure in %s, %s"_err_en_US,
- parser::omp::GetUpperName(id, version), ThisVersion(version),
+ "The OpenMP directive '%s' is not allowed in %s in %s, %s"_err_en_US,
+ parser::omp::GetUpperName(id, version), where, ThisVersion(version),
TryVersion(pureSince));
} else {
context_.Say(source,
- "The OpenMP directive '%s' is not allowed in a PURE procedure"_err_en_US,
- parser::omp::GetUpperName(id, version));
+ "The OpenMP directive '%s' is not allowed in %s"_err_en_US,
+ parser::omp::GetUpperName(id, version), where);
+ }
+}
+
+void OmpStructureChecker::CheckDirectiveInPureProcedure(
+ parser::CharBlock source, llvm::omp::Directive id) {
+ const Scope &scope{context_.FindScope(source)};
+ if (!FindPureProcedureContaining(scope)) {
+ return;
+ }
+ CheckDirectivePureSince(source, id, "a PURE procedure");
+}
+
+void OmpStructureChecker::CheckDirectiveInDoConcurrent(
+ parser::CharBlock source, llvm::omp::Directive id) {
+ // Look for any enclosing DO CONCURRENT, not just the nearest DO, since a
+ // plain DO nested inside DO CONCURRENT is still part of its body.
+ for (const LoopOrConstruct &c : llvm::reverse(constructStack_)) {
+ auto *doConstruct{std::get_if<const parser::DoConstruct *>(&c)};
+ if (!doConstruct || !(*doConstruct)->IsDoConcurrent()) {
+ continue;
+ }
+ unsigned version{context_.langOptions().OpenMPVersion};
+ if (!IsDoConcurrentLegal(version)) {
+ // Prior to OpenMP 6.0, no OpenMP directive, regardless of its "pure"
+ // property, was allowed inside a DO CONCURRENT construct.
+ context_.Say(source,
+ "The OpenMP directive '%s' is not allowed inside a DO CONCURRENT construct"_err_en_US,
+ parser::omp::GetUpperName(id, version));
+ } else {
+ // Starting with OpenMP 6.0, directives that have the "pure" property
+ // are permitted inside a DO CONCURRENT construct.
+ CheckDirectivePureSince(source, id, "a DO CONCURRENT construct");
+ }
+ return;
}
}
@@ -1331,6 +1361,7 @@ void OmpStructureChecker::Enter(const parser::OpenMPConstruct &x) {
dirStack_.push_back(&GetOmpDirectiveSpecification(x));
CheckDirectiveDeprecation(x);
CheckDirectiveInPureProcedure(dirName.source, dirName.v);
+ CheckDirectiveInDoConcurrent(dirName.source, dirName.v);
// Verify clauses
common::visit(
@@ -1388,6 +1419,7 @@ void OmpStructureChecker::Enter(const parser::OpenMPDeclarativeConstruct &x) {
llvm::iterator_range(std::list<parser::OmpClause>{}));
CheckDirectiveInPureProcedure(dirName.source, dirName.v);
+ CheckDirectiveInDoConcurrent(dirName.source, dirName.v);
EnterDirectiveNest(DeclarativeNest);
}
diff --git a/flang/lib/Semantics/check-omp-structure.h b/flang/lib/Semantics/check-omp-structure.h
index 5eb33133e1efd..000ef81786e81 100644
--- a/flang/lib/Semantics/check-omp-structure.h
+++ b/flang/lib/Semantics/check-omp-structure.h
@@ -348,8 +348,12 @@ class OmpStructureChecker : public OmpStructureCheckerBase {
void CheckDirectiveSpelling(
parser::CharBlock spelling, llvm::omp::Directive id);
void CheckDirectiveDeprecation(const parser::OpenMPConstruct &x);
+ void CheckDirectivePureSince(parser::CharBlock source,
+ llvm::omp::Directive id, const char *where);
void CheckDirectiveInPureProcedure(
parser::CharBlock source, llvm::omp::Directive id);
+ void CheckDirectiveInDoConcurrent(
+ parser::CharBlock source, llvm::omp::Directive id);
void CheckClauses(parser::OmpDirectiveName dirName,
llvm::iterator_range<ClauseIterator> beginClauses,
llvm::iterator_range<ClauseIterator> endClauses);
diff --git a/flang/test/Semantics/OpenMP/do-concurrent-pure-version.f90 b/flang/test/Semantics/OpenMP/do-concurrent-pure-version.f90
new file mode 100644
index 0000000000000..e23bfac8b8303
--- /dev/null
+++ b/flang/test/Semantics/OpenMP/do-concurrent-pure-version.f90
@@ -0,0 +1,22 @@
+! RUN: %python %S/../test_errors.py %s %flang_fc1 -fopenmp -fopenmp-version=52
+
+! Prior to OpenMP 6.0, no OpenMP directive is allowed inside a DO CONCURRENT
+! construct, regardless of whether it would otherwise have the "pure"
+! property (e.g. SIMD, which has been "pure" since OpenMP 4.5).
+
+module m
+contains
+ subroutine do_concurrent_bad(a, n)
+ integer, intent(in) :: n
+ integer, intent(inout) :: a(n)
+ integer :: i, j
+ do concurrent (i = 1:n)
+ !ERROR: The OpenMP directive 'SIMD' is not allowed inside a DO CONCURRENT construct
+ !$omp simd
+ do j = 1, n
+ end do
+ !ERROR: The OpenMP directive 'BARRIER' is not allowed inside a DO CONCURRENT construct
+ !$omp barrier
+ end do
+ end subroutine
+end module
diff --git a/flang/test/Semantics/OpenMP/do-concurrent-pure.f90 b/flang/test/Semantics/OpenMP/do-concurrent-pure.f90
new file mode 100644
index 0000000000000..cefc68190d61c
--- /dev/null
+++ b/flang/test/Semantics/OpenMP/do-concurrent-pure.f90
@@ -0,0 +1,48 @@
+! RUN: %python %S/../test_errors.py %s %flang_fc1 -fopenmp -fopenmp-version=60
+
+! Starting with OpenMP 6.0, directives with the "pure" property are allowed
+! in a DO CONCURRENT construct; other directives are still rejected.
+
+module m
+contains
+ subroutine do_concurrent_valid(a, n)
+ integer, intent(in) :: n
+ integer, intent(inout) :: a(n)
+ integer :: i
+ do concurrent (i = 1:n)
+ !$omp nothing
+ a(i) = a(i) + 1
+ end do
+ end subroutine
+
+ subroutine do_concurrent_bad(a, n)
+ integer, intent(in) :: n
+ integer, intent(inout) :: a(n)
+ integer :: i
+ do concurrent (i = 1:n)
+ !ERROR: The OpenMP directive 'BARRIER' is not allowed in a DO CONCURRENT construct
+ !$omp barrier
+ !ERROR: The OpenMP directive 'PARALLEL' is not allowed in a DO CONCURRENT construct
+ !$omp parallel
+ !$omp end parallel
+ !ERROR: The OpenMP directive 'ATOMIC' is not allowed in a DO CONCURRENT construct
+ !$omp atomic
+ a(i) = a(i) + 1
+ end do
+ end subroutine
+
+ ! A plain DO nested in DO CONCURRENT is still part of its body.
+ subroutine do_concurrent_nested_do(a, n)
+ integer, intent(in) :: n
+ integer, intent(inout) :: a(n)
+ integer :: i, j
+ do concurrent (i = 1:n)
+ do j = 1, n
+ !ERROR: The OpenMP directive 'TASK' is not allowed in a DO CONCURRENT construct
+ !$omp task
+ !$omp end task
+ end do
+ a(i) = a(i) + 1
+ end do
+ end subroutine
+end module
>From 29dcb5aba53d1154a0cfd43c43da62a69ea88f4a Mon Sep 17 00:00:00 2001
From: Shashwathi N <nshashwa at pe34genoa.hpc.amslabs.hpecorp.net>
Date: Mon, 17 Aug 2026 01:06:01 -0500
Subject: [PATCH 2/2] Updated Doc
---
flang/docs/OpenMPSupport.md | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/flang/docs/OpenMPSupport.md b/flang/docs/OpenMPSupport.md
index 1a9d92c9424a1..0eed6e1265476 100644
--- a/flang/docs/OpenMPSupport.md
+++ b/flang/docs/OpenMPSupport.md
@@ -234,7 +234,7 @@ Parser/Semantics, MLIR, Lowering, or the OpenMPIRBuilder.
| loop construct with DO CONCURRENT | <span class="progress">in progress</span> | | Experimental support is available and under active development. | [llvm/llvm-project#178138](https://github.com/llvm/llvm-project/pull/178138), [llvm/llvm-project#190990](https://github.com/llvm/llvm-project/pull/190990) |
| optional argument for all clauses | <span class="part">partial</span> | | Semantics coverage exists across clause tests (for example `flang/test/Semantics/OpenMP/if-clause-50.f90`). | |
| canonical loop sequences | <span class="part">partial</span> | | Related loop/transform coverage exists (`flang/test/Semantics/OpenMP/loop-transformation-construct01.f90`, `flang/test/Lower/OpenMP/loop-directive.f90`). | [llvm/llvm-project#161213](https://github.com/llvm/llvm-project/pull/161213), [llvm/llvm-project#168884](https://github.com/llvm/llvm-project/pull/168884), [llvm/llvm-project#170734](https://github.com/llvm/llvm-project/pull/170734), [llvm/llvm-project#170735](https://github.com/llvm/llvm-project/pull/170735) |
-| pure directives in DO CONCURRENT | <span class="part">partial</span> | | Semantics enforce that OpenMP directives inside a DO CONCURRENT body must have the "pure" property. | |
+| pure directives in DO CONCURRENT | <span class="good">done</span> | | Semantics enforce that OpenMP directives inside a DO CONCURRENT body must have the "pure" property. | [llvm/llvm-project#216642](https://github.com/llvm/llvm-project/pull/216642) |
| pure procedure support extended to more directives | <span class="good">done</span> | | Semantics extend the OpenMP 5.2 pure-procedure allow list with the newly added loop-transforming constructs and the scan directive. | [llvm/llvm-project#212676](https://github.com/llvm/llvm-project/pull/212676) |
| extensions to depobj construct | <span class="none">unclaimed</span> | | Semantics and deprecation diagnostics exist for several depobj forms, but extension support remains incomplete in lowering (for example `flang/test/Lower/OpenMP/Todo/depobj-construct.f90`). Implement lowering for extension operands/modifiers and add non-TODO lowering tests. | |
| extensions to atomic construct | <span class="part">partial</span> | | Atomic compare lowering is now available (`flang/test/Lower/OpenMP/atomic-compare.f90`), but fail/capture-related extension paths remain incomplete (`flang/test/Lower/OpenMP/Todo/atomic-compare-fail.f90`). | [llvm/llvm-project#184761](https://github.com/llvm/llvm-project/pull/184761) |
More information about the flang-commits
mailing list