[flang-commits] [flang] [llvm] [Flang][OpenMP] Support certain directives in PURE procedures (PR #212676)
via flang-commits
flang-commits at lists.llvm.org
Thu Aug 6 09:34:43 PDT 2026
https://github.com/ShashwathiNavada updated https://github.com/llvm/llvm-project/pull/212676
>From 1c3c1b7d9e3047650abfb7a80e1ed346c18b97ee Mon Sep 17 00:00:00 2001
From: ShashwathiNavada <shashwathinavada at gmail.com>
Date: Tue, 28 Jul 2026 22:52:45 -0500
Subject: [PATCH 1/5] [FLANG][OpenMP] support certain directives in PURE
procedures
---
flang/lib/Semantics/check-omp-structure.cpp | 18 +++++
flang/lib/Semantics/check-omp-structure.h | 2 +
.../test/Semantics/OpenMP/pure-procedure.f90 | 76 +++++++++++++++++++
.../llvm/Frontend/Directive/DirectiveBase.td | 5 ++
llvm/include/llvm/Frontend/OpenMP/OMP.td | 12 +++
llvm/include/llvm/TableGen/DirectiveEmitter.h | 2 +
.../utils/TableGen/Basic/DirectiveEmitter.cpp | 22 ++++++
7 files changed, 137 insertions(+)
create mode 100644 flang/test/Semantics/OpenMP/pure-procedure.f90
diff --git a/flang/lib/Semantics/check-omp-structure.cpp b/flang/lib/Semantics/check-omp-structure.cpp
index f77382c9045fc..76d0b0266838d 100644
--- a/flang/lib/Semantics/check-omp-structure.cpp
+++ b/flang/lib/Semantics/check-omp-structure.cpp
@@ -945,6 +945,22 @@ 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) {
+ // A directive that does not have the "pure" property is not permitted to
+ // appear in a Fortran PURE procedure, since it may introduce side effects.
+ if (llvm::omp::isDirectivePure(id)) {
+ return;
+ }
+ const Scope &scope{context_.FindScope(source)};
+ if (FindPureProcedureContaining(scope)) {
+ unsigned version{context_.langOptions().OpenMPVersion};
+ context_.Say(source,
+ "The OpenMP directive '%s' is not allowed in a PURE procedure"_err_en_US,
+ parser::omp::GetUpperName(id, version));
+ }
+}
+
std::pair<const parser::OmpClause *, const parser::OmpClause *>
OmpStructureChecker::FindMutuallyExclusiveClauses(
llvm::omp::ClauseSet exclusive,
@@ -1293,6 +1309,7 @@ void OmpStructureChecker::Enter(const parser::OpenMPConstruct &x) {
PushContextAndClauseSets(dirName.source, dirName.v);
dirStack_.push_back(&GetOmpDirectiveSpecification(x));
CheckDirectiveDeprecation(x);
+ CheckDirectiveInPureProcedure(dirName.source, dirName.v);
// Verify clauses
common::visit(
@@ -1349,6 +1366,7 @@ void OmpStructureChecker::Enter(const parser::OpenMPDeclarativeConstruct &x) {
CheckClauses(dirName, llvm::iterator_range(dirStack_.back()->Clauses().v),
llvm::iterator_range(std::list<parser::OmpClause>{}));
+ CheckDirectiveInPureProcedure(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 f8aa47043a614..f275b2a6bd1e5 100644
--- a/flang/lib/Semantics/check-omp-structure.h
+++ b/flang/lib/Semantics/check-omp-structure.h
@@ -346,6 +346,8 @@ class OmpStructureChecker : public OmpStructureCheckerBase {
void CheckDirectiveSpelling(
parser::CharBlock spelling, llvm::omp::Directive id);
void CheckDirectiveDeprecation(const parser::OpenMPConstruct &x);
+ void CheckDirectiveInPureProcedure(
+ 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/pure-procedure.f90 b/flang/test/Semantics/OpenMP/pure-procedure.f90
new file mode 100644
index 0000000000000..0270c1d5e0b79
--- /dev/null
+++ b/flang/test/Semantics/OpenMP/pure-procedure.f90
@@ -0,0 +1,76 @@
+! RUN: %python %S/../test_errors.py %s %flang_fc1 -fopenmp -fopenmp-version=60
+
+! Only directives that have the "pure" property are permitted to appear in a
+! Fortran PURE procedure. These are: metadirective, assume, assumes, nothing,
+! error, and the loop-transforming directives (tile, unroll, reverse,
+! interchange, fuse, split, stripe).
+! (The SPLIT directive is omitted below only because its required COUNTS clause
+! is not yet parsed; it is still marked pure.)
+
+module m
+contains
+ pure subroutine pure_ok(a, n)
+ !$omp assumes no_openmp
+ integer, intent(in) :: n
+ integer, intent(inout) :: a(n)
+ integer :: i, j
+ !$omp nothing
+ !$omp assume no_openmp
+ !$omp end assume
+ !$omp metadirective when(user={condition(.true.)}: nothing)
+ !$omp error at(execution) severity(warning) message("ok")
+ !$omp tile sizes(4)
+ do i = 1, n
+ a(i) = a(i) + 1
+ end do
+ !$omp unroll partial(2)
+ do i = 1, n
+ a(i) = a(i) * 2
+ end do
+ !$omp reverse
+ do i = 1, n
+ a(i) = a(i) - 1
+ end do
+ !$omp interchange
+ do i = 1, n
+ do j = 1, n
+ a(i) = a(i) + j
+ end do
+ end do
+ !$omp stripe sizes(4)
+ do i = 1, n
+ a(i) = a(i) + 1
+ end do
+ !$omp fuse
+ do i = 1, n
+ a(i) = a(i) + 1
+ end do
+ do i = 1, n
+ a(i) = a(i) + 2
+ end do
+ end subroutine
+
+ pure subroutine pure_bad(a, n, r)
+ integer, intent(in) :: n
+ integer, intent(inout) :: a(n)
+ integer, intent(out) :: r
+ integer :: i
+ !ERROR: The OpenMP directive 'PARALLEL' is not allowed in a PURE procedure
+ !$omp parallel
+ !$omp end parallel
+ !ERROR: The OpenMP directive 'BARRIER' is not allowed in a PURE procedure
+ !$omp barrier
+ !ERROR: The OpenMP directive 'ATOMIC' is not allowed in a PURE procedure
+ !$omp atomic
+ r = r + 1
+ !ERROR: The OpenMP directive 'PARALLEL DO' is not allowed in a PURE procedure
+ !$omp parallel do
+ do i = 1, n
+ a(i) = a(i) + 1
+ end do
+ !$omp end parallel do
+ !ERROR: The OpenMP directive 'TASK' is not allowed in a PURE procedure
+ !$omp task
+ !$omp end task
+ end subroutine
+end module
diff --git a/llvm/include/llvm/Frontend/Directive/DirectiveBase.td b/llvm/include/llvm/Frontend/Directive/DirectiveBase.td
index 8c4fb1325400b..d41741bdd03c2 100644
--- a/llvm/include/llvm/Frontend/Directive/DirectiveBase.td
+++ b/llvm/include/llvm/Frontend/Directive/DirectiveBase.td
@@ -267,4 +267,9 @@ class Directive<list<Spelling> ss> {
// The languages that allow this directive. Default: all languages.
list<SourceLanguage> languages = [L_C, L_Fortran];
+
+ // Whether the directive has the "pure" property, i.e. it is permitted to
+ // appear in a context that must be free of side effects, such as a Fortran
+ // PURE procedure. Defaults to false.
+ bit isPure = false;
}
diff --git a/llvm/include/llvm/Frontend/OpenMP/OMP.td b/llvm/include/llvm/Frontend/OpenMP/OMP.td
index dc2af1e62cfa4..4393ea37a9121 100644
--- a/llvm/include/llvm/Frontend/OpenMP/OMP.td
+++ b/llvm/include/llvm/Frontend/OpenMP/OMP.td
@@ -680,6 +680,7 @@ def OMP_Assumes : Directive<[Spelling<"assumes">]> {
VersionedClause<OMPC_NoOpenMPRoutines, 51>,
VersionedClause<OMPC_NoParallelism, 51>,
];
+ let isPure = true;
}
def OMP_Assume : Directive<[Spelling<"assume">]> {
let association = AS_Block;
@@ -693,6 +694,7 @@ def OMP_Assume : Directive<[Spelling<"assume">]> {
VersionedClause<OMPC_NoOpenMPRoutines, 51>,
VersionedClause<OMPC_NoParallelism, 51>,
];
+ let isPure = true;
}
def OMP_Atomic : Directive<[Spelling<"atomic">]> {
let allowedOnceClauses = [
@@ -936,6 +938,7 @@ def OMP_Error : Directive<[Spelling<"error">]> {
];
let association = AS_None;
let category = CA_Utility;
+ let isPure = true;
}
def OMP_Flatten : Directive<[Spelling<"flatten">]> {
let allowedClauses = [
@@ -1004,6 +1007,7 @@ def OMP_Fuse : Directive<[Spelling<"fuse">]> {
let allowedLoopModifiers = [
LM_Fused,
];
+ let isPure = true;
}
def OMP_Interchange : Directive<[Spelling<"interchange">]> {
let allowedClauses = [
@@ -1017,6 +1021,7 @@ def OMP_Interchange : Directive<[Spelling<"interchange">]> {
let allowedLoopModifiers = [
LM_Interchanged,
];
+ let isPure = true;
}
def OMP_interop : Directive<[Spelling<"interop">]> {
let allowedClauses = [
@@ -1067,6 +1072,7 @@ def OMP_Metadirective : Directive<[Spelling<"metadirective">]> {
];
let association = AS_None;
let category = CA_Meta;
+ let isPure = true;
}
def OMP_Nothing : Directive<[Spelling<"nothing">]> {
let allowedClauses = [
@@ -1077,6 +1083,7 @@ def OMP_Nothing : Directive<[Spelling<"nothing">]> {
let allowedLoopModifiers = [
LM_Identity,
];
+ let isPure = true;
}
def OMP_Ordered : Directive<[Spelling<"ordered">]> {
let allowedClauses = [
@@ -1134,6 +1141,7 @@ def OMP_Reverse : Directive<[Spelling<"reverse">]> {
let allowedLoopModifiers = [
LM_Reversed,
];
+ let isPure = true;
}
def OMP_Scan : Directive<[Spelling<"scan">]> {
let allowedOnceClauses = [
@@ -1432,6 +1440,7 @@ def OMP_Tile : Directive<[Spelling<"tile">]> {
LM_Grid,
LM_Intratile,
];
+ let isPure = true;
}
def OMP_Stripe : Directive<[Spelling<"stripe">]> {
let allowedClauses = [
@@ -1446,6 +1455,7 @@ def OMP_Stripe : Directive<[Spelling<"stripe">]> {
LM_Grid,
LM_Offsets,
];
+ let isPure = true;
}
def OMP_Split : Directive<[Spelling<"split">]> {
let allowedClauses = [
@@ -1462,6 +1472,7 @@ def OMP_Split : Directive<[Spelling<"split">]> {
let allowedLoopModifiers = [
LM_Split,
];
+ let isPure = true;
}
def OMP_Unknown : Directive<[Spelling<"unknown">]> {
let isDefault = true;
@@ -1481,6 +1492,7 @@ def OMP_Unroll : Directive<[Spelling<"unroll">]> {
let allowedLoopModifiers = [
LM_Unrolled,
];
+ let isPure = true;
}
def OMP_Workshare : Directive<[Spelling<"workshare">]> {
let allowedOnceClauses = [
diff --git a/llvm/include/llvm/TableGen/DirectiveEmitter.h b/llvm/include/llvm/TableGen/DirectiveEmitter.h
index 00b2cec453aef..9f851ffd2ffea 100644
--- a/llvm/include/llvm/TableGen/DirectiveEmitter.h
+++ b/llvm/include/llvm/TableGen/DirectiveEmitter.h
@@ -274,6 +274,8 @@ class Directive : public BaseRecord {
return Def->getValueAsListOfDefs("allowedLoopModifiers");
}
+ bool isPure() const { return Def->getValueAsBit("isPure"); }
+
// Clang uses a different format for names of its directives enum.
std::string getClangAccSpelling() const {
StringRef Name = getSpellingForIdentifier();
diff --git a/llvm/utils/TableGen/Basic/DirectiveEmitter.cpp b/llvm/utils/TableGen/Basic/DirectiveEmitter.cpp
index 390cb37cb3c94..833bd15ea4548 100644
--- a/llvm/utils/TableGen/Basic/DirectiveEmitter.cpp
+++ b/llvm/utils/TableGen/Basic/DirectiveEmitter.cpp
@@ -899,6 +899,26 @@ static void generateGetDirectiveCategory(const DirectiveLanguage &DirLang,
OS << "}\n";
}
+static void generateIsDirectivePure(const DirectiveLanguage &DirLang,
+ raw_ostream &OS) {
+ OS << "constexpr bool isDirectivePure(Directive Dir) {\n";
+ OS << " switch (Dir) {\n";
+
+ StringRef Prefix = DirLang.getDirectivePrefix();
+
+ for (const Record *R : DirLang.getDirectives()) {
+ Directive D(R);
+ if (!D.isPure())
+ continue;
+ OS << " case " << getIdentifierName(R, Prefix) << ":\n";
+ }
+ OS << " return true;\n";
+ OS << " default:\n";
+ OS << " return false;\n";
+ OS << " } // switch (Dir)\n";
+ OS << "}\n";
+}
+
static void generateGetDirectiveLanguages(const DirectiveLanguage &DirLang,
raw_ostream &OS) {
OS << "constexpr SourceLanguage getDirectiveLanguages(Directive D) {\n";
@@ -1383,6 +1403,8 @@ static void emitDirectivesConstexprImpl(const DirectiveLanguage &DirLang,
OS << "\n";
generateGetDirectiveCategory(DirLang, OS);
OS << "\n";
+ generateIsDirectivePure(DirLang, OS);
+ OS << "\n";
generateGetDirectiveLanguages(DirLang, OS);
}
>From 6838e401fdd247502eeb4bc88aa010a0efef47f4 Mon Sep 17 00:00:00 2001
From: ShashwathiNavada <shashwathinavada at gmail.com>
Date: Tue, 28 Jul 2026 23:53:45 -0500
Subject: [PATCH 2/5] Added the property to the directives that was missed
earlier
---
llvm/include/llvm/Frontend/OpenMP/OMP.td | 4 ++++
1 file changed, 4 insertions(+)
diff --git a/llvm/include/llvm/Frontend/OpenMP/OMP.td b/llvm/include/llvm/Frontend/OpenMP/OMP.td
index 4393ea37a9121..7cf57c3fda68a 100644
--- a/llvm/include/llvm/Frontend/OpenMP/OMP.td
+++ b/llvm/include/llvm/Frontend/OpenMP/OMP.td
@@ -819,6 +819,7 @@ def OMP_DeclareReduction : Directive<[Spelling<"declare reduction", 1, 52>,
];
let association = AS_None;
let category = CA_Declarative;
+ let isPure = true;
}
def OMP_DeclareSimd : Directive<[Spelling<"declare simd", 1, 52>,
Spelling<"declare_simd", 60>]> {
@@ -836,6 +837,7 @@ def OMP_DeclareSimd : Directive<[Spelling<"declare simd", 1, 52>,
];
let association = AS_Declaration;
let category = CA_Declarative;
+ let isPure = true;
}
def OMP_DeclareTarget : Directive<[Spelling<"declare target", 1, 52>,
Spelling<"declare_target", 60>]> {
@@ -851,6 +853,7 @@ def OMP_DeclareTarget : Directive<[Spelling<"declare target", 1, 52>,
];
let association = AS_None;
let category = CA_Declarative;
+ let isPure = true;
}
def OMP_DeclareVariant : Directive<[Spelling<"declare variant", 1, 52>,
Spelling<"declare_variant", 60>]> {
@@ -864,6 +867,7 @@ def OMP_DeclareVariant : Directive<[Spelling<"declare variant", 1, 52>,
let association = AS_Declaration;
let category = CA_Declarative;
let languages = [L_C];
+ let isPure = true;
}
def OMP_Depobj : Directive<[Spelling<"depobj">]> {
let allowedClauses = [
>From 2a522a22ed16625c561b3e7d0ba0029c25afcc7e Mon Sep 17 00:00:00 2001
From: ShashwathiNavada <shashwathinavada at gmail.com>
Date: Tue, 28 Jul 2026 23:53:45 -0500
Subject: [PATCH 3/5] Added the property to the directives that was missed
earlier
---
llvm/include/llvm/Frontend/OpenMP/OMP.td | 4 ++++
llvm/test/TableGen/directive1.td | 8 ++++++++
llvm/test/TableGen/directive2.td | 8 ++++++++
3 files changed, 20 insertions(+)
diff --git a/llvm/include/llvm/Frontend/OpenMP/OMP.td b/llvm/include/llvm/Frontend/OpenMP/OMP.td
index 4393ea37a9121..7cf57c3fda68a 100644
--- a/llvm/include/llvm/Frontend/OpenMP/OMP.td
+++ b/llvm/include/llvm/Frontend/OpenMP/OMP.td
@@ -819,6 +819,7 @@ def OMP_DeclareReduction : Directive<[Spelling<"declare reduction", 1, 52>,
];
let association = AS_None;
let category = CA_Declarative;
+ let isPure = true;
}
def OMP_DeclareSimd : Directive<[Spelling<"declare simd", 1, 52>,
Spelling<"declare_simd", 60>]> {
@@ -836,6 +837,7 @@ def OMP_DeclareSimd : Directive<[Spelling<"declare simd", 1, 52>,
];
let association = AS_Declaration;
let category = CA_Declarative;
+ let isPure = true;
}
def OMP_DeclareTarget : Directive<[Spelling<"declare target", 1, 52>,
Spelling<"declare_target", 60>]> {
@@ -851,6 +853,7 @@ def OMP_DeclareTarget : Directive<[Spelling<"declare target", 1, 52>,
];
let association = AS_None;
let category = CA_Declarative;
+ let isPure = true;
}
def OMP_DeclareVariant : Directive<[Spelling<"declare variant", 1, 52>,
Spelling<"declare_variant", 60>]> {
@@ -864,6 +867,7 @@ def OMP_DeclareVariant : Directive<[Spelling<"declare variant", 1, 52>,
let association = AS_Declaration;
let category = CA_Declarative;
let languages = [L_C];
+ let isPure = true;
}
def OMP_Depobj : Directive<[Spelling<"depobj">]> {
let allowedClauses = [
diff --git a/llvm/test/TableGen/directive1.td b/llvm/test/TableGen/directive1.td
index a82ca6e2451ce..40c5e96d080ba 100644
--- a/llvm/test/TableGen/directive1.td
+++ b/llvm/test/TableGen/directive1.td
@@ -190,6 +190,14 @@ def TDL_DirA : Directive<[Spelling<"dira">]> {
// CHECK-NEXT: #endif
// CHECK-NEXT: }
// CHECK-EMPTY:
+// CHECK-NEXT: constexpr bool isDirectivePure(Directive Dir) {
+// CHECK-NEXT: switch (Dir) {
+// CHECK-NEXT: return true;
+// CHECK-NEXT: default:
+// CHECK-NEXT: return false;
+// CHECK-NEXT: } // switch (Dir)
+// CHECK-NEXT: }
+// CHECK-EMPTY:
// CHECK-NEXT: constexpr SourceLanguage getDirectiveLanguages(Directive D) {
// CHECK-NEXT: switch (D) {
// CHECK-NEXT: case TDLD_dira:
diff --git a/llvm/test/TableGen/directive2.td b/llvm/test/TableGen/directive2.td
index 76252e5f92303..2943a8eb3f1a6 100644
--- a/llvm/test/TableGen/directive2.td
+++ b/llvm/test/TableGen/directive2.td
@@ -155,6 +155,14 @@ def TDL_DirA : Directive<[Spelling<"dira">]> {
// CHECK-NEXT: #endif
// CHECK-NEXT: }
// CHECK-EMPTY:
+// CHECK-NEXT: constexpr bool isDirectivePure(Directive Dir) {
+// CHECK-NEXT: switch (Dir) {
+// CHECK-NEXT: return true;
+// CHECK-NEXT: default:
+// CHECK-NEXT: return false;
+// CHECK-NEXT: } // switch (Dir)
+// CHECK-NEXT: }
+// CHECK-EMPTY:
// CHECK-NEXT: constexpr SourceLanguage getDirectiveLanguages(Directive D) {
// CHECK-NEXT: switch (D) {
// CHECK-NEXT: case TDLD_dira:
>From 3510a97622c3fa1dff369c444eaab3b06f7520cf Mon Sep 17 00:00:00 2001
From: ShashwathiNavada <shashwathinavada at gmail.com>
Date: Thu, 30 Jul 2026 07:51:04 -0500
Subject: [PATCH 4/5] Added version guard
---
flang/lib/Semantics/check-omp-structure.cpp | 23 ++++++---
.../OpenMP/pure-procedure-version.f90 | 49 +++++++++++++++++++
2 files changed, 66 insertions(+), 6 deletions(-)
create mode 100644 flang/test/Semantics/OpenMP/pure-procedure-version.f90
diff --git a/flang/lib/Semantics/check-omp-structure.cpp b/flang/lib/Semantics/check-omp-structure.cpp
index 76d0b0266838d..451560b7d812b 100644
--- a/flang/lib/Semantics/check-omp-structure.cpp
+++ b/flang/lib/Semantics/check-omp-structure.cpp
@@ -947,14 +947,25 @@ void OmpStructureChecker::CheckDirectiveDeprecation(
void OmpStructureChecker::CheckDirectiveInPureProcedure(
parser::CharBlock source, llvm::omp::Directive id) {
- // A directive that does not have the "pure" property is not permitted to
- // appear in a Fortran PURE procedure, since it may introduce side effects.
- if (llvm::omp::isDirectivePure(id)) {
+ const Scope &scope{context_.FindScope(source)};
+ if (!FindPureProcedureContaining(scope)) {
return;
}
- const Scope &scope{context_.FindScope(source)};
- if (FindPureProcedureContaining(scope)) {
- unsigned version{context_.langOptions().OpenMPVersion};
+ unsigned version{context_.langOptions().OpenMPVersion};
+ // OpenMP 5.1 permits only SIMD and declarative directives in a PURE
+ // procedure; OpenMP 5.2 additionally permits metadirective, assume(s),
+ // nothing, error, and the loop-transforming constructs).
+ bool alwaysAllowed{id == llvm::omp::Directive::OMPD_simd ||
+ llvm::omp::getDirectiveCategory(id) == llvm::omp::Category::Declarative};
+ if (alwaysAllowed || (version >= 52 && llvm::omp::isDirectivePure(id))) {
+ return;
+ }
+ if (llvm::omp::isDirectivePure(id)) {
+ 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),
+ TryVersion(52));
+ } else {
context_.Say(source,
"The OpenMP directive '%s' is not allowed in a PURE procedure"_err_en_US,
parser::omp::GetUpperName(id, version));
diff --git a/flang/test/Semantics/OpenMP/pure-procedure-version.f90 b/flang/test/Semantics/OpenMP/pure-procedure-version.f90
new file mode 100644
index 0000000000000..bda2ad18eef82
--- /dev/null
+++ b/flang/test/Semantics/OpenMP/pure-procedure-version.f90
@@ -0,0 +1,49 @@
+! RUN: %python %S/../test_errors.py %s %flang_fc1 -fopenmp -fopenmp-version=51
+
+! In OpenMP 5.1, only SIMD and declarative directives are permitted in a PURE
+! procedure. The metadirective, assumption, nothing, error, and
+! loop-transforming directives were not added to that list until OpenMP 5.2, so
+! they are diagnosed here with a version-specific message. Directives that never
+! have the "pure" property (e.g. parallel, task) are rejected outright.
+
+module m
+contains
+ pure subroutine pure_ok(a, n)
+ integer, intent(in) :: n
+ integer, intent(inout) :: a(n)
+ integer :: i
+ ! SIMD and declarative directives are allowed in every version.
+ !$omp declare reduction(myadd : integer : omp_out = omp_out + omp_in) &
+ !$omp& initializer(omp_priv = 0)
+ !$omp simd
+ do i = 1, n
+ a(i) = a(i) + 1
+ end do
+ end subroutine
+
+ pure subroutine pure_bad(a, n)
+ integer, intent(in) :: n
+ integer, intent(inout) :: a(n)
+ integer :: i
+ !ERROR: The OpenMP directive 'NOTHING' is not allowed in a PURE procedure in OpenMP v5.1, try -fopenmp-version=52
+ !$omp nothing
+ !ERROR: The OpenMP directive 'METADIRECTIVE' is not allowed in a PURE procedure in OpenMP v5.1, try -fopenmp-version=52
+ !$omp metadirective when(user={condition(.true.)}: nothing)
+ !ERROR: The OpenMP directive 'TILE' is not allowed in a PURE procedure in OpenMP v5.1, try -fopenmp-version=52
+ !$omp tile sizes(4)
+ do i = 1, n
+ a(i) = a(i) + 1
+ end do
+ !ERROR: The OpenMP directive 'UNROLL' is not allowed in a PURE procedure in OpenMP v5.1, try -fopenmp-version=52
+ !$omp unroll partial(2)
+ do i = 1, n
+ a(i) = a(i) * 2
+ end do
+ !ERROR: The OpenMP directive 'PARALLEL' is not allowed in a PURE procedure
+ !$omp parallel
+ !$omp end parallel
+ !ERROR: The OpenMP directive 'TASK' is not allowed in a PURE procedure
+ !$omp task
+ !$omp end task
+ end subroutine
+end module
>From 485b0e57fb0f65a3a18cd2539b2e82b602f50458 Mon Sep 17 00:00:00 2001
From: Shashwathi N <nshashwa at pe34genoa.hpc.amslabs.hpecorp.net>
Date: Thu, 6 Aug 2026 10:49:04 -0500
Subject: [PATCH 5/5] Updated
---
flang/lib/Semantics/check-omp-structure.cpp | 9 ++++--
.../test/Semantics/OpenMP/pure-procedure.f90 | 29 +++++++++++++++++--
.../llvm/Frontend/Directive/DirectiveBase.td | 6 ++--
llvm/include/llvm/Frontend/OpenMP/OMP.td | 29 +++++++++----------
llvm/include/llvm/TableGen/DirectiveEmitter.h | 2 +-
llvm/test/TableGen/directive1.td | 5 ++--
llvm/test/TableGen/directive2.td | 5 ++--
.../utils/TableGen/Basic/DirectiveEmitter.cpp | 18 +++++++-----
8 files changed, 63 insertions(+), 40 deletions(-)
diff --git a/flang/lib/Semantics/check-omp-structure.cpp b/flang/lib/Semantics/check-omp-structure.cpp
index 4fca4314aa585..41d1087a20a53 100644
--- a/flang/lib/Semantics/check-omp-structure.cpp
+++ b/flang/lib/Semantics/check-omp-structure.cpp
@@ -957,14 +957,17 @@ void OmpStructureChecker::CheckDirectiveInPureProcedure(
// nothing, error, and the loop-transforming constructs).
bool alwaysAllowed{id == llvm::omp::Directive::OMPD_simd ||
llvm::omp::getDirectiveCategory(id) == llvm::omp::Category::Declarative};
- if (alwaysAllowed || (version >= 52 && llvm::omp::isDirectivePure(id))) {
+ // A directive's "pure" property is version-specific: pureSince is the
+ // OpenMP version at which the directive gained that property.
+ unsigned pureSince{llvm::omp::getDirectivePureSince(id)};
+ if (alwaysAllowed || version >= pureSince) {
return;
}
- if (llvm::omp::isDirectivePure(id)) {
+ 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),
- TryVersion(52));
+ TryVersion(pureSince));
} else {
context_.Say(source,
"The OpenMP directive '%s' is not allowed in a PURE procedure"_err_en_US,
diff --git a/flang/test/Semantics/OpenMP/pure-procedure.f90 b/flang/test/Semantics/OpenMP/pure-procedure.f90
index 0270c1d5e0b79..d942d1b193b8e 100644
--- a/flang/test/Semantics/OpenMP/pure-procedure.f90
+++ b/flang/test/Semantics/OpenMP/pure-procedure.f90
@@ -1,9 +1,9 @@
! RUN: %python %S/../test_errors.py %s %flang_fc1 -fopenmp -fopenmp-version=60
! Only directives that have the "pure" property are permitted to appear in a
-! Fortran PURE procedure. These are: metadirective, assume, assumes, nothing,
-! error, and the loop-transforming directives (tile, unroll, reverse,
-! interchange, fuse, split, stripe).
+! Fortran PURE procedure. These are: simd, scan, metadirective, assume,
+! assumes, nothing, error, and the loop-transforming directives (tile, unroll,
+! reverse, interchange, fuse, split, stripe).
! (The SPLIT directive is omitted below only because its required COUNTS clause
! is not yet parsed; it is still marked pure.)
@@ -50,6 +50,29 @@ pure subroutine pure_ok(a, n)
end do
end subroutine
+ pure subroutine pure_scan(a, n)
+ integer, intent(in) :: n
+ integer, intent(inout) :: a(n)
+ integer :: i, x
+ x = 0
+ !$omp simd reduction(inscan, +: x)
+ do i = 1, n
+ x = x + a(i)
+ !$omp scan inclusive(x)
+ a(i) = x
+ end do
+ end subroutine
+
+ pure subroutine pure_simd(a, n)
+ integer, intent(in) :: n
+ integer, intent(inout) :: a(n)
+ integer :: i
+ !$omp simd
+ do i = 1, n
+ a(i) = a(i) + 1
+ end do
+ end subroutine
+
pure subroutine pure_bad(a, n, r)
integer, intent(in) :: n
integer, intent(inout) :: a(n)
diff --git a/llvm/include/llvm/Frontend/Directive/DirectiveBase.td b/llvm/include/llvm/Frontend/Directive/DirectiveBase.td
index d41741bdd03c2..c7653dd228f7c 100644
--- a/llvm/include/llvm/Frontend/Directive/DirectiveBase.td
+++ b/llvm/include/llvm/Frontend/Directive/DirectiveBase.td
@@ -268,8 +268,6 @@ class Directive<list<Spelling> ss> {
// The languages that allow this directive. Default: all languages.
list<SourceLanguage> languages = [L_C, L_Fortran];
- // Whether the directive has the "pure" property, i.e. it is permitted to
- // appear in a context that must be free of side effects, such as a Fortran
- // PURE procedure. Defaults to false.
- bit isPure = false;
+ // The OpenMP version at which the directive gained the "pure" property.
+ int pureSince = 0x7FFFFFFF;
}
diff --git a/llvm/include/llvm/Frontend/OpenMP/OMP.td b/llvm/include/llvm/Frontend/OpenMP/OMP.td
index 7cf57c3fda68a..c0838407d1af5 100644
--- a/llvm/include/llvm/Frontend/OpenMP/OMP.td
+++ b/llvm/include/llvm/Frontend/OpenMP/OMP.td
@@ -680,7 +680,7 @@ def OMP_Assumes : Directive<[Spelling<"assumes">]> {
VersionedClause<OMPC_NoOpenMPRoutines, 51>,
VersionedClause<OMPC_NoParallelism, 51>,
];
- let isPure = true;
+ let pureSince = 52;
}
def OMP_Assume : Directive<[Spelling<"assume">]> {
let association = AS_Block;
@@ -694,7 +694,7 @@ def OMP_Assume : Directive<[Spelling<"assume">]> {
VersionedClause<OMPC_NoOpenMPRoutines, 51>,
VersionedClause<OMPC_NoParallelism, 51>,
];
- let isPure = true;
+ let pureSince = 52;
}
def OMP_Atomic : Directive<[Spelling<"atomic">]> {
let allowedOnceClauses = [
@@ -819,7 +819,6 @@ def OMP_DeclareReduction : Directive<[Spelling<"declare reduction", 1, 52>,
];
let association = AS_None;
let category = CA_Declarative;
- let isPure = true;
}
def OMP_DeclareSimd : Directive<[Spelling<"declare simd", 1, 52>,
Spelling<"declare_simd", 60>]> {
@@ -837,7 +836,6 @@ def OMP_DeclareSimd : Directive<[Spelling<"declare simd", 1, 52>,
];
let association = AS_Declaration;
let category = CA_Declarative;
- let isPure = true;
}
def OMP_DeclareTarget : Directive<[Spelling<"declare target", 1, 52>,
Spelling<"declare_target", 60>]> {
@@ -853,7 +851,6 @@ def OMP_DeclareTarget : Directive<[Spelling<"declare target", 1, 52>,
];
let association = AS_None;
let category = CA_Declarative;
- let isPure = true;
}
def OMP_DeclareVariant : Directive<[Spelling<"declare variant", 1, 52>,
Spelling<"declare_variant", 60>]> {
@@ -867,7 +864,6 @@ def OMP_DeclareVariant : Directive<[Spelling<"declare variant", 1, 52>,
let association = AS_Declaration;
let category = CA_Declarative;
let languages = [L_C];
- let isPure = true;
}
def OMP_Depobj : Directive<[Spelling<"depobj">]> {
let allowedClauses = [
@@ -942,7 +938,7 @@ def OMP_Error : Directive<[Spelling<"error">]> {
];
let association = AS_None;
let category = CA_Utility;
- let isPure = true;
+ let pureSince = 52;
}
def OMP_Flatten : Directive<[Spelling<"flatten">]> {
let allowedClauses = [
@@ -1011,7 +1007,7 @@ def OMP_Fuse : Directive<[Spelling<"fuse">]> {
let allowedLoopModifiers = [
LM_Fused,
];
- let isPure = true;
+ let pureSince = 52;
}
def OMP_Interchange : Directive<[Spelling<"interchange">]> {
let allowedClauses = [
@@ -1025,7 +1021,7 @@ def OMP_Interchange : Directive<[Spelling<"interchange">]> {
let allowedLoopModifiers = [
LM_Interchanged,
];
- let isPure = true;
+ let pureSince = 52;
}
def OMP_interop : Directive<[Spelling<"interop">]> {
let allowedClauses = [
@@ -1076,7 +1072,7 @@ def OMP_Metadirective : Directive<[Spelling<"metadirective">]> {
];
let association = AS_None;
let category = CA_Meta;
- let isPure = true;
+ let pureSince = 52;
}
def OMP_Nothing : Directive<[Spelling<"nothing">]> {
let allowedClauses = [
@@ -1087,7 +1083,7 @@ def OMP_Nothing : Directive<[Spelling<"nothing">]> {
let allowedLoopModifiers = [
LM_Identity,
];
- let isPure = true;
+ let pureSince = 52;
}
def OMP_Ordered : Directive<[Spelling<"ordered">]> {
let allowedClauses = [
@@ -1145,7 +1141,7 @@ def OMP_Reverse : Directive<[Spelling<"reverse">]> {
let allowedLoopModifiers = [
LM_Reversed,
];
- let isPure = true;
+ let pureSince = 52;
}
def OMP_Scan : Directive<[Spelling<"scan">]> {
let allowedOnceClauses = [
@@ -1154,6 +1150,7 @@ def OMP_Scan : Directive<[Spelling<"scan">]> {
];
let association = AS_Separating;
let category = CA_Subsidiary;
+ let pureSince = 60;
}
def OMP_Scope : Directive<[Spelling<"scope">]> {
let allowedClauses = [
@@ -1444,7 +1441,7 @@ def OMP_Tile : Directive<[Spelling<"tile">]> {
LM_Grid,
LM_Intratile,
];
- let isPure = true;
+ let pureSince = 52;
}
def OMP_Stripe : Directive<[Spelling<"stripe">]> {
let allowedClauses = [
@@ -1459,7 +1456,7 @@ def OMP_Stripe : Directive<[Spelling<"stripe">]> {
LM_Grid,
LM_Offsets,
];
- let isPure = true;
+ let pureSince = 52;
}
def OMP_Split : Directive<[Spelling<"split">]> {
let allowedClauses = [
@@ -1476,7 +1473,7 @@ def OMP_Split : Directive<[Spelling<"split">]> {
let allowedLoopModifiers = [
LM_Split,
];
- let isPure = true;
+ let pureSince = 52;
}
def OMP_Unknown : Directive<[Spelling<"unknown">]> {
let isDefault = true;
@@ -1496,7 +1493,7 @@ def OMP_Unroll : Directive<[Spelling<"unroll">]> {
let allowedLoopModifiers = [
LM_Unrolled,
];
- let isPure = true;
+ let pureSince = 52;
}
def OMP_Workshare : Directive<[Spelling<"workshare">]> {
let allowedOnceClauses = [
diff --git a/llvm/include/llvm/TableGen/DirectiveEmitter.h b/llvm/include/llvm/TableGen/DirectiveEmitter.h
index 9f851ffd2ffea..8e7854da50252 100644
--- a/llvm/include/llvm/TableGen/DirectiveEmitter.h
+++ b/llvm/include/llvm/TableGen/DirectiveEmitter.h
@@ -274,7 +274,7 @@ class Directive : public BaseRecord {
return Def->getValueAsListOfDefs("allowedLoopModifiers");
}
- bool isPure() const { return Def->getValueAsBit("isPure"); }
+ int getPureSince() const { return Def->getValueAsInt("pureSince"); }
// Clang uses a different format for names of its directives enum.
std::string getClangAccSpelling() const {
diff --git a/llvm/test/TableGen/directive1.td b/llvm/test/TableGen/directive1.td
index 40c5e96d080ba..2b3a3a14828e5 100644
--- a/llvm/test/TableGen/directive1.td
+++ b/llvm/test/TableGen/directive1.td
@@ -190,11 +190,10 @@ def TDL_DirA : Directive<[Spelling<"dira">]> {
// CHECK-NEXT: #endif
// CHECK-NEXT: }
// CHECK-EMPTY:
-// CHECK-NEXT: constexpr bool isDirectivePure(Directive Dir) {
+// CHECK-NEXT: constexpr unsigned getDirectivePureSince(Directive Dir) {
// CHECK-NEXT: switch (Dir) {
-// CHECK-NEXT: return true;
// CHECK-NEXT: default:
-// CHECK-NEXT: return false;
+// CHECK-NEXT: return 0x7FFFFFFF;
// CHECK-NEXT: } // switch (Dir)
// CHECK-NEXT: }
// CHECK-EMPTY:
diff --git a/llvm/test/TableGen/directive2.td b/llvm/test/TableGen/directive2.td
index 2943a8eb3f1a6..367756206c6ac 100644
--- a/llvm/test/TableGen/directive2.td
+++ b/llvm/test/TableGen/directive2.td
@@ -155,11 +155,10 @@ def TDL_DirA : Directive<[Spelling<"dira">]> {
// CHECK-NEXT: #endif
// CHECK-NEXT: }
// CHECK-EMPTY:
-// CHECK-NEXT: constexpr bool isDirectivePure(Directive Dir) {
+// CHECK-NEXT: constexpr unsigned getDirectivePureSince(Directive Dir) {
// CHECK-NEXT: switch (Dir) {
-// CHECK-NEXT: return true;
// CHECK-NEXT: default:
-// CHECK-NEXT: return false;
+// CHECK-NEXT: return 0x7FFFFFFF;
// CHECK-NEXT: } // switch (Dir)
// CHECK-NEXT: }
// CHECK-EMPTY:
diff --git a/llvm/utils/TableGen/Basic/DirectiveEmitter.cpp b/llvm/utils/TableGen/Basic/DirectiveEmitter.cpp
index 33a725f31b11b..c60f362eb2d85 100644
--- a/llvm/utils/TableGen/Basic/DirectiveEmitter.cpp
+++ b/llvm/utils/TableGen/Basic/DirectiveEmitter.cpp
@@ -899,22 +899,26 @@ static void generateGetDirectiveCategory(const DirectiveLanguage &DirLang,
OS << "}\n";
}
-static void generateIsDirectivePure(const DirectiveLanguage &DirLang,
- raw_ostream &OS) {
- OS << "constexpr bool isDirectivePure(Directive Dir) {\n";
+static void generateGetDirectivePureSince(const DirectiveLanguage &DirLang,
+ raw_ostream &OS) {
+ // Must match the sentinel in DirectiveBase.td and in
+ // OmpStructureChecker::CheckDirectiveInPureProcedure.
+ constexpr int NeverPure = 0x7FFFFFFF;
+ OS << "constexpr unsigned getDirectivePureSince(Directive Dir) {\n";
OS << " switch (Dir) {\n";
StringRef Prefix = DirLang.getDirectivePrefix();
for (const Record *R : DirLang.getDirectives()) {
Directive D(R);
- if (!D.isPure())
+ int PureSince = D.getPureSince();
+ if (PureSince == NeverPure)
continue;
OS << " case " << getIdentifierName(R, Prefix) << ":\n";
+ OS << " return " << PureSince << ";\n";
}
- OS << " return true;\n";
OS << " default:\n";
- OS << " return false;\n";
+ OS << " return " << NeverPure << ";\n";
OS << " } // switch (Dir)\n";
OS << "}\n";
}
@@ -1403,7 +1407,7 @@ static void emitDirectivesConstexprImpl(const DirectiveLanguage &DirLang,
OS << "\n";
generateGetDirectiveCategory(DirLang, OS);
OS << "\n";
- generateIsDirectivePure(DirLang, OS);
+ generateGetDirectivePureSince(DirLang, OS);
OS << "\n";
generateGetDirectiveLanguages(DirLang, OS);
}
More information about the flang-commits
mailing list