[llvm-branch-commits] [flang] [flang][openmp] Add parser/semantic support for workdistribute (PR #154377)
via llvm-branch-commits
llvm-branch-commits at lists.llvm.org
Wed Aug 20 02:26:14 PDT 2025
llvmbot wrote:
<!--LLVM PR SUMMARY COMMENT-->
@llvm/pr-subscribers-flang-semantics
Author: Chaitanya (skc7)
<details>
<summary>Changes</summary>
This PR adds workdistribute parser and semantic support in flang.
This is part 2 of workdistribute frontend support.
Part1 : #<!-- -->154376 [OpenMP] Add workdistribute construct in openMP dialect and in llvm frontend
Part2 : #<!-- -->154377 [flang][openmp] Add parser/semantic support for workdistribute
Part3 : #<!-- -->154378 [flang][openmp] Add Lowering to omp mlir for workdistribute construct
---
Full diff: https://github.com/llvm/llvm-project/pull/154377.diff
9 Files Affected:
- (modified) flang/include/flang/Semantics/openmp-directive-sets.h (+7)
- (modified) flang/lib/Parser/openmp-parsers.cpp (+5-1)
- (modified) flang/lib/Semantics/check-omp-structure.cpp (+95)
- (modified) flang/lib/Semantics/check-omp-structure.h (+1)
- (modified) flang/lib/Semantics/resolve-directives.cpp (+7-1)
- (added) flang/test/Parser/OpenMP/workdistribute.f90 (+27)
- (added) flang/test/Semantics/OpenMP/workdistribute01.f90 (+16)
- (added) flang/test/Semantics/OpenMP/workdistribute02.f90 (+34)
- (added) flang/test/Semantics/OpenMP/workdistribute03.f90 (+34)
``````````diff
diff --git a/flang/include/flang/Semantics/openmp-directive-sets.h b/flang/include/flang/Semantics/openmp-directive-sets.h
index cc66cc833e8b7..01e8481e05721 100644
--- a/flang/include/flang/Semantics/openmp-directive-sets.h
+++ b/flang/include/flang/Semantics/openmp-directive-sets.h
@@ -143,6 +143,7 @@ static const OmpDirectiveSet topTargetSet{
Directive::OMPD_target_teams_distribute_parallel_do_simd,
Directive::OMPD_target_teams_distribute_simd,
Directive::OMPD_target_teams_loop,
+ Directive::OMPD_target_teams_workdistribute,
};
static const OmpDirectiveSet allTargetSet{topTargetSet};
@@ -172,6 +173,7 @@ static const OmpDirectiveSet topTeamsSet{
Directive::OMPD_teams_distribute_parallel_do_simd,
Directive::OMPD_teams_distribute_simd,
Directive::OMPD_teams_loop,
+ Directive::OMPD_teams_workdistribute,
};
static const OmpDirectiveSet bottomTeamsSet{
@@ -187,6 +189,7 @@ static const OmpDirectiveSet allTeamsSet{
Directive::OMPD_target_teams_distribute_parallel_do_simd,
Directive::OMPD_target_teams_distribute_simd,
Directive::OMPD_target_teams_loop,
+ Directive::OMPD_target_teams_workdistribute,
} | topTeamsSet,
};
@@ -230,6 +233,9 @@ static const OmpDirectiveSet blockConstructSet{
Directive::OMPD_taskgroup,
Directive::OMPD_teams,
Directive::OMPD_workshare,
+ Directive::OMPD_target_teams_workdistribute,
+ Directive::OMPD_teams_workdistribute,
+ Directive::OMPD_workdistribute,
};
static const OmpDirectiveSet loopConstructSet{
@@ -376,6 +382,7 @@ static const OmpDirectiveSet nestedReduceWorkshareAllowedSet{
};
static const OmpDirectiveSet nestedTeamsAllowedSet{
+ Directive::OMPD_workdistribute,
Directive::OMPD_distribute,
Directive::OMPD_distribute_parallel_do,
Directive::OMPD_distribute_parallel_do_simd,
diff --git a/flang/lib/Parser/openmp-parsers.cpp b/flang/lib/Parser/openmp-parsers.cpp
index 56cee4ab38e9b..51b49a591b02f 100644
--- a/flang/lib/Parser/openmp-parsers.cpp
+++ b/flang/lib/Parser/openmp-parsers.cpp
@@ -1870,11 +1870,15 @@ TYPE_PARSER( //
MakeBlockConstruct(llvm::omp::Directive::OMPD_target_data) ||
MakeBlockConstruct(llvm::omp::Directive::OMPD_target_parallel) ||
MakeBlockConstruct(llvm::omp::Directive::OMPD_target_teams) ||
+ MakeBlockConstruct(
+ llvm::omp::Directive::OMPD_target_teams_workdistribute) ||
MakeBlockConstruct(llvm::omp::Directive::OMPD_target) ||
MakeBlockConstruct(llvm::omp::Directive::OMPD_task) ||
MakeBlockConstruct(llvm::omp::Directive::OMPD_taskgroup) ||
MakeBlockConstruct(llvm::omp::Directive::OMPD_teams) ||
- MakeBlockConstruct(llvm::omp::Directive::OMPD_workshare))
+ MakeBlockConstruct(llvm::omp::Directive::OMPD_teams_workdistribute) ||
+ MakeBlockConstruct(llvm::omp::Directive::OMPD_workshare) ||
+ MakeBlockConstruct(llvm::omp::Directive::OMPD_workdistribute))
#undef MakeBlockConstruct
// OMP SECTIONS Directive
diff --git a/flang/lib/Semantics/check-omp-structure.cpp b/flang/lib/Semantics/check-omp-structure.cpp
index 2b36b085ae08d..4c4e17c39c03a 100644
--- a/flang/lib/Semantics/check-omp-structure.cpp
+++ b/flang/lib/Semantics/check-omp-structure.cpp
@@ -141,6 +141,67 @@ class OmpWorkshareBlockChecker {
parser::CharBlock source_;
};
+// 'OmpWorkdistributeBlockChecker' is used to check the validity of the
+// assignment statements and the expressions enclosed in an OpenMP
+// workdistribute construct
+class OmpWorkdistributeBlockChecker {
+public:
+ OmpWorkdistributeBlockChecker(
+ SemanticsContext &context, parser::CharBlock source)
+ : context_{context}, source_{source} {}
+
+ template <typename T> bool Pre(const T &) { return true; }
+ template <typename T> void Post(const T &) {}
+
+ bool Pre(const parser::AssignmentStmt &assignment) {
+ const auto &var{std::get<parser::Variable>(assignment.t)};
+ const auto &expr{std::get<parser::Expr>(assignment.t)};
+ const auto *lhs{GetExpr(context_, var)};
+ const auto *rhs{GetExpr(context_, expr)};
+ if (lhs && rhs) {
+ Tristate isDefined{semantics::IsDefinedAssignment(
+ lhs->GetType(), lhs->Rank(), rhs->GetType(), rhs->Rank())};
+ if (isDefined == Tristate::Yes) {
+ context_.Say(expr.source,
+ "Defined assignment statement is not "
+ "allowed in a WORKDISTRIBUTE construct"_err_en_US);
+ }
+ }
+ return true;
+ }
+
+ bool Pre(const parser::Expr &expr) {
+ if (const auto *e{GetExpr(context_, expr)}) {
+ for (const Symbol &symbol : evaluate::CollectSymbols(*e)) {
+ const Symbol &root{GetAssociationRoot(symbol)};
+ if (IsFunction(root)) {
+ std::string attrs{""};
+ if (!IsElementalProcedure(root)) {
+ attrs = " non-ELEMENTAL";
+ }
+ if (root.attrs().test(Attr::IMPURE)) {
+ if (attrs != "") {
+ attrs = "," + attrs;
+ }
+ attrs = " IMPURE" + attrs;
+ }
+ if (attrs != "") {
+ context_.Say(expr.source,
+ "User defined%s function '%s' is not allowed in a "
+ "WORKDISTRIBUTE construct"_err_en_US,
+ attrs, root.name());
+ }
+ }
+ }
+ }
+ return false;
+ }
+
+private:
+ SemanticsContext &context_;
+ parser::CharBlock source_;
+};
+
// `OmpUnitedTaskDesignatorChecker` is used to check if the designator
// can appear within the TASK construct
class OmpUnitedTaskDesignatorChecker {
@@ -813,6 +874,13 @@ void OmpStructureChecker::Enter(const parser::OpenMPBlockConstruct &x) {
"TARGET construct with nested TEAMS region contains statements or "
"directives outside of the TEAMS construct"_err_en_US);
}
+ if (GetContext().directive == llvm::omp::Directive::OMPD_workdistribute &&
+ GetContextParent().directive != llvm::omp::Directive::OMPD_teams) {
+ context_.Say(x.BeginDir().DirName().source,
+ "%s region can only be strictly nested within the "
+ "teams region"_err_en_US,
+ ContextDirectiveAsFortran());
+ }
}
CheckNoBranching(block, beginSpec.DirId(), beginSpec.source);
@@ -896,6 +964,17 @@ void OmpStructureChecker::Enter(const parser::OpenMPBlockConstruct &x) {
HasInvalidWorksharingNesting(
beginSpec.source, llvm::omp::nestedWorkshareErrSet);
break;
+ case llvm::omp::OMPD_workdistribute:
+ if (!CurrentDirectiveIsNested()) {
+ context_.Say(beginSpec.source,
+ "A workdistribute region must be nested inside teams region only."_err_en_US);
+ }
+ CheckWorkdistributeBlockStmts(block, beginSpec.source);
+ break;
+ case llvm::omp::OMPD_teams_workdistribute:
+ case llvm::omp::OMPD_target_teams_workdistribute:
+ CheckWorkdistributeBlockStmts(block, beginSpec.source);
+ break;
case llvm::omp::Directive::OMPD_scope:
case llvm::omp::Directive::OMPD_single:
// TODO: This check needs to be extended while implementing nesting of
@@ -4497,6 +4576,22 @@ void OmpStructureChecker::CheckWorkshareBlockStmts(
}
}
+void OmpStructureChecker::CheckWorkdistributeBlockStmts(
+ const parser::Block &block, parser::CharBlock source) {
+ OmpWorkdistributeBlockChecker ompWorkdistributeBlockChecker{context_, source};
+
+ for (auto it{block.begin()}; it != block.end(); ++it) {
+ if (parser::Unwrap<parser::AssignmentStmt>(*it)) {
+ parser::Walk(*it, ompWorkdistributeBlockChecker);
+ } else {
+ context_.Say(source,
+ "The structured block in a WORKDISTRIBUTE construct may consist of "
+ "only "
+ "SCALAR or ARRAY assignments"_err_en_US);
+ }
+ }
+}
+
void OmpStructureChecker::CheckIfContiguous(const parser::OmpObject &object) {
if (auto contig{IsContiguous(context_, object)}; contig && !*contig) {
const parser::Name *name{GetObjectName(object)};
diff --git a/flang/lib/Semantics/check-omp-structure.h b/flang/lib/Semantics/check-omp-structure.h
index b548a455ee077..08519c4836ed8 100644
--- a/flang/lib/Semantics/check-omp-structure.h
+++ b/flang/lib/Semantics/check-omp-structure.h
@@ -245,6 +245,7 @@ class OmpStructureChecker
llvmOmpClause clause, const parser::OmpObjectList &ompObjectList);
bool CheckTargetBlockOnlyTeams(const parser::Block &);
void CheckWorkshareBlockStmts(const parser::Block &, parser::CharBlock);
+ void CheckWorkdistributeBlockStmts(const parser::Block &, parser::CharBlock);
void CheckIteratorRange(const parser::OmpIteratorSpecifier &x);
void CheckIteratorModifier(const parser::OmpIterator &x);
diff --git a/flang/lib/Semantics/resolve-directives.cpp b/flang/lib/Semantics/resolve-directives.cpp
index 6a4660c9882ab..069d78554e9c2 100644
--- a/flang/lib/Semantics/resolve-directives.cpp
+++ b/flang/lib/Semantics/resolve-directives.cpp
@@ -1736,10 +1736,13 @@ bool OmpAttributeVisitor::Pre(const parser::OpenMPBlockConstruct &x) {
case llvm::omp::Directive::OMPD_task:
case llvm::omp::Directive::OMPD_taskgroup:
case llvm::omp::Directive::OMPD_teams:
+ case llvm::omp::Directive::OMPD_workdistribute:
case llvm::omp::Directive::OMPD_workshare:
case llvm::omp::Directive::OMPD_parallel_workshare:
case llvm::omp::Directive::OMPD_target_teams:
+ case llvm::omp::Directive::OMPD_target_teams_workdistribute:
case llvm::omp::Directive::OMPD_target_parallel:
+ case llvm::omp::Directive::OMPD_teams_workdistribute:
PushContext(dirSpec.source, dirId);
break;
default:
@@ -1769,9 +1772,12 @@ void OmpAttributeVisitor::Post(const parser::OpenMPBlockConstruct &x) {
case llvm::omp::Directive::OMPD_target:
case llvm::omp::Directive::OMPD_task:
case llvm::omp::Directive::OMPD_teams:
+ case llvm::omp::Directive::OMPD_workdistribute:
case llvm::omp::Directive::OMPD_parallel_workshare:
case llvm::omp::Directive::OMPD_target_teams:
- case llvm::omp::Directive::OMPD_target_parallel: {
+ case llvm::omp::Directive::OMPD_target_parallel:
+ case llvm::omp::Directive::OMPD_target_teams_workdistribute:
+ case llvm::omp::Directive::OMPD_teams_workdistribute: {
bool hasPrivate;
for (const auto *allocName : allocateNames_) {
hasPrivate = false;
diff --git a/flang/test/Parser/OpenMP/workdistribute.f90 b/flang/test/Parser/OpenMP/workdistribute.f90
new file mode 100644
index 0000000000000..874538d5f0296
--- /dev/null
+++ b/flang/test/Parser/OpenMP/workdistribute.f90
@@ -0,0 +1,27 @@
+!RUN: %flang_fc1 -fdebug-unparse -fopenmp %s | FileCheck --ignore-case --check-prefix="UNPARSE" %s
+!RUN: %flang_fc1 -fdebug-dump-parse-tree -fopenmp -fopenmp-version=61 %s | FileCheck --check-prefix="PARSE-TREE" %s
+
+!UNPARSE: SUBROUTINE teams_workdistribute
+!UNPARSE: USE :: iso_fortran_env
+!UNPARSE: REAL(KIND=4_4) a
+!UNPARSE: REAL(KIND=4_4), DIMENSION(10_4) :: x
+!UNPARSE: REAL(KIND=4_4), DIMENSION(10_4) :: y
+!UNPARSE: !$OMP TEAMS WORKDISTRIBUTE
+!UNPARSE: y=a*x+y
+!UNPARSE: !$OMP END TEAMS WORKDISTRIBUTE
+!UNPARSE: END SUBROUTINE teams_workdistribute
+
+!PARSE-TREE: | | | OmpBeginDirective
+!PARSE-TREE: | | | | OmpDirectiveName -> llvm::omp::Directive = teams workdistribute
+!PARSE-TREE: | | | OmpEndDirective
+!PARSE-TREE: | | | | OmpDirectiveName -> llvm::omp::Directive = teams workdistribute
+
+subroutine teams_workdistribute()
+ use iso_fortran_env
+ real(kind=real32) :: a
+ real(kind=real32), dimension(10) :: x
+ real(kind=real32), dimension(10) :: y
+ !$omp teams workdistribute
+ y = a * x + y
+ !$omp end teams workdistribute
+end subroutine teams_workdistribute
diff --git a/flang/test/Semantics/OpenMP/workdistribute01.f90 b/flang/test/Semantics/OpenMP/workdistribute01.f90
new file mode 100644
index 0000000000000..76ddbc74ea4ec
--- /dev/null
+++ b/flang/test/Semantics/OpenMP/workdistribute01.f90
@@ -0,0 +1,16 @@
+! RUN: %python %S/../test_errors.py %s %flang -fopenmp
+! OpenMP Version 6.0
+! workdistribute Construct
+! Invalid do construct inside !$omp workdistribute
+
+subroutine workdistribute()
+ integer n, i
+ !ERROR: A workdistribute region must be nested inside teams region only.
+ !ERROR: The structured block in a WORKDISTRIBUTE construct may consist of only SCALAR or ARRAY assignments
+ !$omp workdistribute
+ do i = 1, n
+ print *, "omp workdistribute"
+ end do
+ !$omp end workdistribute
+
+end subroutine workdistribute
diff --git a/flang/test/Semantics/OpenMP/workdistribute02.f90 b/flang/test/Semantics/OpenMP/workdistribute02.f90
new file mode 100644
index 0000000000000..ad2cde2c3daf0
--- /dev/null
+++ b/flang/test/Semantics/OpenMP/workdistribute02.f90
@@ -0,0 +1,34 @@
+! RUN: %python %S/../test_errors.py %s %flang -fopenmp
+! OpenMP Version 6.0
+! workdistribute Construct
+! The !omp workdistribute construct must not contain any user defined
+! function calls unless the function is ELEMENTAL.
+
+module my_mod
+ contains
+ integer function my_func()
+ my_func = 10
+ end function my_func
+
+ impure integer function impure_my_func()
+ impure_my_func = 20
+ end function impure_my_func
+
+ impure elemental integer function impure_ele_my_func()
+ impure_ele_my_func = 20
+ end function impure_ele_my_func
+end module my_mod
+
+subroutine workdistribute(aa, bb, cc, n)
+ use my_mod
+ integer n
+ real aa(n), bb(n), cc(n)
+ !$omp teams
+ !$omp workdistribute
+ !ERROR: User defined non-ELEMENTAL function 'my_func' is not allowed in a WORKDISTRIBUTE construct
+ aa = my_func()
+ aa = bb * cc
+ !$omp end workdistribute
+ !$omp end teams
+
+end subroutine workdistribute
diff --git a/flang/test/Semantics/OpenMP/workdistribute03.f90 b/flang/test/Semantics/OpenMP/workdistribute03.f90
new file mode 100644
index 0000000000000..eac28cb39c47f
--- /dev/null
+++ b/flang/test/Semantics/OpenMP/workdistribute03.f90
@@ -0,0 +1,34 @@
+! RUN: %python %S/../test_errors.py %s %flang -fopenmp
+! OpenMP Version 6.0
+! workdistribute Construct
+! All array assignments, scalar assignments, and masked array assignments
+! must be intrinsic assignments.
+
+module defined_assign
+ interface assignment(=)
+ module procedure work_assign
+ end interface
+
+ contains
+ subroutine work_assign(a,b)
+ integer, intent(out) :: a
+ logical, intent(in) :: b(:)
+ end subroutine work_assign
+end module defined_assign
+
+program omp_workdistribute
+ use defined_assign
+
+ integer :: a, aa(10), bb(10)
+ logical :: l(10)
+ l = .TRUE.
+
+ !$omp teams
+ !$omp workdistribute
+ !ERROR: Defined assignment statement is not allowed in a WORKDISTRIBUTE construct
+ a = l
+ aa = bb
+ !$omp end workdistribute
+ !$omp end teams
+
+end program omp_workdistribute
``````````
</details>
https://github.com/llvm/llvm-project/pull/154377
More information about the llvm-branch-commits
mailing list