[flang][openmp] Add parser/semantic support for workdistribute

This commit is contained in:
skc7 2025-08-19 21:43:06 +05:30
parent f53da4fa4f
commit 4442fced82
9 changed files with 226 additions and 2 deletions

View File

@ -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,

View File

@ -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

View File

@ -141,6 +141,67 @@ private:
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)};

View File

@ -245,6 +245,7 @@ private:
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);

View File

@ -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;

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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