Skip to content

Commit a0f8aca

Browse files
committed
Add parser and semantic support for workdistribute (llvm#154377)
This PR adds workdistribute parser and semantic support in flang. The work in this PR is c-p and updated from @ivanradanov commits from coexecute implementation: flang_workdistribute_iwomp_2024
1 parent b74fde8 commit a0f8aca

File tree

11 files changed

+254
-1
lines changed

11 files changed

+254
-1
lines changed

flang/include/flang/Semantics/openmp-directive-sets.h

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -143,6 +143,7 @@ static const OmpDirectiveSet topTargetSet{
143143
Directive::OMPD_target_teams_distribute_parallel_do_simd,
144144
Directive::OMPD_target_teams_distribute_simd,
145145
Directive::OMPD_target_teams_loop,
146+
Directive::OMPD_target_teams_workdistribute,
146147
};
147148

148149
static const OmpDirectiveSet allTargetSet{topTargetSet};
@@ -172,6 +173,7 @@ static const OmpDirectiveSet topTeamsSet{
172173
Directive::OMPD_teams_distribute_parallel_do_simd,
173174
Directive::OMPD_teams_distribute_simd,
174175
Directive::OMPD_teams_loop,
176+
Directive::OMPD_teams_workdistribute,
175177
};
176178

177179
static const OmpDirectiveSet bottomTeamsSet{
@@ -187,6 +189,7 @@ static const OmpDirectiveSet allTeamsSet{
187189
Directive::OMPD_target_teams_distribute_parallel_do_simd,
188190
Directive::OMPD_target_teams_distribute_simd,
189191
Directive::OMPD_target_teams_loop,
192+
Directive::OMPD_target_teams_workdistribute,
190193
} | topTeamsSet,
191194
};
192195

@@ -230,6 +233,9 @@ static const OmpDirectiveSet blockConstructSet{
230233
Directive::OMPD_taskgroup,
231234
Directive::OMPD_teams,
232235
Directive::OMPD_workshare,
236+
Directive::OMPD_target_teams_workdistribute,
237+
Directive::OMPD_teams_workdistribute,
238+
Directive::OMPD_workdistribute,
233239
};
234240

235241
static const OmpDirectiveSet loopConstructSet{
@@ -376,6 +382,7 @@ static const OmpDirectiveSet nestedReduceWorkshareAllowedSet{
376382
};
377383

378384
static const OmpDirectiveSet nestedTeamsAllowedSet{
385+
Directive::OMPD_workdistribute,
379386
Directive::OMPD_distribute,
380387
Directive::OMPD_distribute_parallel_do,
381388
Directive::OMPD_distribute_parallel_do_simd,

flang/lib/Parser/openmp-parsers.cpp

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1567,11 +1567,16 @@ TYPE_PARSER(
15671567
"TARGET DATA" >> pure(llvm::omp::Directive::OMPD_target_data),
15681568
"TARGET_DATA" >> pure(llvm::omp::Directive::OMPD_target_data),
15691569
"TARGET PARALLEL" >> pure(llvm::omp::Directive::OMPD_target_parallel),
1570+
"TARGET TEAMS WORKDISTRIBUTE" >>
1571+
pure(llvm::omp::Directive::OMPD_target_teams_workdistribute),
15701572
"TARGET TEAMS" >> pure(llvm::omp::Directive::OMPD_target_teams),
15711573
"TARGET" >> pure(llvm::omp::Directive::OMPD_target),
15721574
"TASK"_id >> pure(llvm::omp::Directive::OMPD_task),
15731575
"TASKGROUP" >> pure(llvm::omp::Directive::OMPD_taskgroup),
1576+
"TEAMS WORKDISTRIBUTE" >>
1577+
pure(llvm::omp::Directive::OMPD_teams_workdistribute),
15741578
"TEAMS" >> pure(llvm::omp::Directive::OMPD_teams),
1579+
"WORKDISTRIBUTE" >> pure(llvm::omp::Directive::OMPD_workdistribute),
15751580
"WORKSHARE" >> pure(llvm::omp::Directive::OMPD_workshare))))
15761581

15771582
TYPE_PARSER(sourced(construct<OmpBeginBlockDirective>(
@@ -1729,6 +1734,8 @@ TYPE_PARSER(sourced(
17291734
TYPE_PARSER(construct<OpenMPBlockConstruct>(
17301735
Parser<OmpBeginBlockDirective>{} / endOmpLine, block,
17311736
Parser<OmpEndBlockDirective>{} / endOmpLine))
1737+
#define MakeBlockConstruct(dir) \
1738+
construct<OpenMPBlockConstruct>(OmpBlockConstructParser{dir})
17321739

17331740
// OMP SECTIONS Directive
17341741
TYPE_PARSER(construct<OmpSectionsDirective>(first(

flang/lib/Parser/unparse.cpp

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2561,6 +2561,15 @@ class UnparseVisitor {
25612561
case llvm::omp::Directive::OMPD_workshare:
25622562
Word("WORKSHARE ");
25632563
break;
2564+
case llvm::omp::Directive::OMPD_workdistribute:
2565+
Word("WORKDISTRIBUTE ");
2566+
break;
2567+
case llvm::omp::Directive::OMPD_teams_workdistribute:
2568+
Word("TEAMS WORKDISTRIBUTE ");
2569+
break;
2570+
case llvm::omp::Directive::OMPD_target_teams_workdistribute:
2571+
Word("TARGET TEAMS WORKDISTRIBUTE ");
2572+
break;
25642573
default:
25652574
// Nothing to be done
25662575
break;

flang/lib/Semantics/check-omp-structure.cpp

Lines changed: 97 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -37,6 +37,7 @@
3737

3838
#include "llvm/ADT/ArrayRef.h"
3939
#include "llvm/ADT/STLExtras.h"
40+
#include "llvm/ADT/StringExtras.h"
4041
#include "llvm/ADT/StringRef.h"
4142
#include "llvm/Frontend/OpenMP/OMP.h"
4243

@@ -141,6 +142,64 @@ class OmpWorkshareBlockChecker {
141142
parser::CharBlock source_;
142143
};
143144

145+
// 'OmpWorkdistributeBlockChecker' is used to check the validity of the
146+
// assignment statements and the expressions enclosed in an OpenMP
147+
// WORKDISTRIBUTE construct
148+
class OmpWorkdistributeBlockChecker {
149+
public:
150+
OmpWorkdistributeBlockChecker(
151+
SemanticsContext &context, parser::CharBlock source)
152+
: context_{context}, source_{source} {}
153+
154+
template <typename T> bool Pre(const T &) { return true; }
155+
template <typename T> void Post(const T &) {}
156+
157+
bool Pre(const parser::AssignmentStmt &assignment) {
158+
const auto &var{std::get<parser::Variable>(assignment.t)};
159+
const auto &expr{std::get<parser::Expr>(assignment.t)};
160+
const auto *lhs{GetExpr(context_, var)};
161+
const auto *rhs{GetExpr(context_, expr)};
162+
if (lhs && rhs) {
163+
Tristate isDefined{semantics::IsDefinedAssignment(
164+
lhs->GetType(), lhs->Rank(), rhs->GetType(), rhs->Rank())};
165+
if (isDefined == Tristate::Yes) {
166+
context_.Say(expr.source,
167+
"Defined assignment statement is not allowed in a WORKDISTRIBUTE construct"_err_en_US);
168+
}
169+
}
170+
return true;
171+
}
172+
173+
bool Pre(const parser::Expr &expr) {
174+
if (const auto *e{GetExpr(context_, expr)}) {
175+
if (!e)
176+
return false;
177+
for (const Symbol &symbol : evaluate::CollectSymbols(*e)) {
178+
const Symbol &root{GetAssociationRoot(symbol)};
179+
if (IsFunction(root)) {
180+
std::vector<std::string> attrs;
181+
if (!IsElementalProcedure(root)) {
182+
attrs.push_back("non-ELEMENTAL");
183+
}
184+
if (root.attrs().test(Attr::IMPURE)) {
185+
attrs.push_back("IMPURE");
186+
}
187+
std::string attrsStr =
188+
attrs.empty() ? "" : " " + llvm::join(attrs, ", ");
189+
context_.Say(expr.source,
190+
"User defined%s function '%s' is not allowed in a WORKDISTRIBUTE construct"_err_en_US,
191+
attrsStr, root.name());
192+
}
193+
}
194+
}
195+
return false;
196+
}
197+
198+
private:
199+
SemanticsContext &context_;
200+
parser::CharBlock source_;
201+
};
202+
144203
// `OmpUnitedTaskDesignatorChecker` is used to check if the designator
145204
// can appear within the TASK construct
146205
class OmpUnitedTaskDesignatorChecker {
@@ -819,6 +878,12 @@ void OmpStructureChecker::Enter(const parser::OpenMPBlockConstruct &x) {
819878
"TARGET construct with nested TEAMS region contains statements or "
820879
"directives outside of the TEAMS construct"_err_en_US);
821880
}
881+
if (GetContext().directive == llvm::omp::Directive::OMPD_workdistribute &&
882+
GetContextParent().directive != llvm::omp::Directive::OMPD_teams) {
883+
context_.Say(parser::FindSourceLocation(x),
884+
"%s region can only be strictly nested within TEAMS region"_err_en_US,
885+
ContextDirectiveAsFortran());
886+
}
822887
}
823888

824889
CheckNoBranching(block, beginDir.v, beginDir.source);
@@ -900,6 +965,17 @@ void OmpStructureChecker::Enter(const parser::OpenMPBlockConstruct &x) {
900965
HasInvalidWorksharingNesting(
901966
beginDir.source, llvm::omp::nestedWorkshareErrSet);
902967
break;
968+
case llvm::omp::OMPD_workdistribute:
969+
if (!CurrentDirectiveIsNested()) {
970+
context_.Say(beginDir.source,
971+
"A WORKDISTRIBUTE region must be nested inside TEAMS region only."_err_en_US);
972+
}
973+
CheckWorkdistributeBlockStmts(block, beginDir.source);
974+
break;
975+
case llvm::omp::OMPD_teams_workdistribute:
976+
case llvm::omp::OMPD_target_teams_workdistribute:
977+
CheckWorkdistributeBlockStmts(block, beginDir.source);
978+
break;
903979
case llvm::omp::Directive::OMPD_scope:
904980
case llvm::omp::Directive::OMPD_single:
905981
// TODO: This check needs to be extended while implementing nesting of
@@ -4385,6 +4461,27 @@ void OmpStructureChecker::CheckWorkshareBlockStmts(
43854461
}
43864462
}
43874463

4464+
void OmpStructureChecker::CheckWorkdistributeBlockStmts(
4465+
const parser::Block &block, parser::CharBlock source) {
4466+
unsigned version{context_.langOptions().OpenMPVersion};
4467+
unsigned since{60};
4468+
if (version < since)
4469+
context_.Say(source,
4470+
"WORKDISTRIBUTE construct is not allowed in %s, %s"_err_en_US,
4471+
ThisVersion(version), TryVersion(since));
4472+
4473+
OmpWorkdistributeBlockChecker ompWorkdistributeBlockChecker{context_, source};
4474+
4475+
for (auto it{block.begin()}; it != block.end(); ++it) {
4476+
if (parser::Unwrap<parser::AssignmentStmt>(*it)) {
4477+
parser::Walk(*it, ompWorkdistributeBlockChecker);
4478+
} else {
4479+
context_.Say(source,
4480+
"The structured block in a WORKDISTRIBUTE construct may consist of only SCALAR or ARRAY assignments"_err_en_US);
4481+
}
4482+
}
4483+
}
4484+
43884485
void OmpStructureChecker::CheckIfContiguous(const parser::OmpObject &object) {
43894486
if (auto contig{IsContiguous(context_, object)}; contig && !*contig) {
43904487
const parser::Name *name{GetObjectName(object)};

flang/lib/Semantics/check-omp-structure.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -242,6 +242,7 @@ class OmpStructureChecker
242242
llvmOmpClause clause, const parser::OmpObjectList &ompObjectList);
243243
bool CheckTargetBlockOnlyTeams(const parser::Block &);
244244
void CheckWorkshareBlockStmts(const parser::Block &, parser::CharBlock);
245+
void CheckWorkdistributeBlockStmts(const parser::Block &, parser::CharBlock);
245246

246247
void CheckIteratorRange(const parser::OmpIteratorSpecifier &x);
247248
void CheckIteratorModifier(const parser::OmpIterator &x);

flang/lib/Semantics/resolve-directives.cpp

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1680,10 +1680,13 @@ bool OmpAttributeVisitor::Pre(const parser::OpenMPBlockConstruct &x) {
16801680
case llvm::omp::Directive::OMPD_task:
16811681
case llvm::omp::Directive::OMPD_taskgroup:
16821682
case llvm::omp::Directive::OMPD_teams:
1683+
case llvm::omp::Directive::OMPD_workdistribute:
16831684
case llvm::omp::Directive::OMPD_workshare:
16841685
case llvm::omp::Directive::OMPD_parallel_workshare:
16851686
case llvm::omp::Directive::OMPD_target_teams:
1687+
case llvm::omp::Directive::OMPD_target_teams_workdistribute:
16861688
case llvm::omp::Directive::OMPD_target_parallel:
1689+
case llvm::omp::Directive::OMPD_teams_workdistribute:
16871690
PushContext(beginDir.source, beginDir.v);
16881691
break;
16891692
default:
@@ -1713,9 +1716,12 @@ void OmpAttributeVisitor::Post(const parser::OpenMPBlockConstruct &x) {
17131716
case llvm::omp::Directive::OMPD_target:
17141717
case llvm::omp::Directive::OMPD_task:
17151718
case llvm::omp::Directive::OMPD_teams:
1719+
case llvm::omp::Directive::OMPD_workdistribute:
17161720
case llvm::omp::Directive::OMPD_parallel_workshare:
17171721
case llvm::omp::Directive::OMPD_target_teams:
1718-
case llvm::omp::Directive::OMPD_target_parallel: {
1722+
case llvm::omp::Directive::OMPD_target_parallel:
1723+
case llvm::omp::Directive::OMPD_target_teams_workdistribute:
1724+
case llvm::omp::Directive::OMPD_teams_workdistribute: {
17191725
bool hasPrivate;
17201726
for (const auto *allocName : allocateNames_) {
17211727
hasPrivate = false;
Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
1+
!RUN: %flang_fc1 -fdebug-unparse -fopenmp -fopenmp-version=60 %s | FileCheck --ignore-case --check-prefix="UNPARSE" %s
2+
!RUN: %flang_fc1 -fdebug-dump-parse-tree -fopenmp -fopenmp-version=60 %s | FileCheck --check-prefix="PARSE-TREE" %s
3+
4+
!UNPARSE: SUBROUTINE teams_workdistribute
5+
!UNPARSE: USE :: iso_fortran_env
6+
!UNPARSE: REAL(KIND=4_4) a
7+
!UNPARSE: REAL(KIND=4_4), DIMENSION(10_4) :: x
8+
!UNPARSE: REAL(KIND=4_4), DIMENSION(10_4) :: y
9+
!UNPARSE: !$OMP TEAMS WORKDISTRIBUTE
10+
!UNPARSE: y=a*x+y
11+
!UNPARSE: !$OMP END TEAMS WORKDISTRIBUTE
12+
!UNPARSE: END SUBROUTINE teams_workdistribute
13+
14+
!PARSE-TREE: | | | OmpBeginBlockDirective
15+
!PARSE-TREE: | | | | OmpBlockDirective -> llvm::omp::Directive = teams workdistribute
16+
!PARSE-TREE: | | | OmpEndBlockDirective
17+
!PARSE-TREE: | | | | OmpBlockDirective -> llvm::omp::Directive = teams workdistribute
18+
19+
subroutine teams_workdistribute()
20+
use iso_fortran_env
21+
real(kind=real32) :: a
22+
real(kind=real32), dimension(10) :: x
23+
real(kind=real32), dimension(10) :: y
24+
!$omp teams workdistribute
25+
y = a * x + y
26+
!$omp end teams workdistribute
27+
end subroutine teams_workdistribute
Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
! RUN: %python %S/../test_errors.py %s %flang -fopenmp -fopenmp-version=60
2+
! OpenMP Version 6.0
3+
! workdistribute Construct
4+
! Invalid do construct inside !$omp workdistribute
5+
6+
subroutine workdistribute()
7+
integer n, i
8+
!ERROR: A WORKDISTRIBUTE region must be nested inside TEAMS region only.
9+
!ERROR: The structured block in a WORKDISTRIBUTE construct may consist of only SCALAR or ARRAY assignments
10+
!$omp workdistribute
11+
do i = 1, n
12+
print *, "omp workdistribute"
13+
end do
14+
!$omp end workdistribute
15+
16+
end subroutine workdistribute
Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,34 @@
1+
! RUN: %python %S/../test_errors.py %s %flang -fopenmp -fopenmp-version=60
2+
! OpenMP Version 6.0
3+
! workdistribute Construct
4+
! The !omp workdistribute construct must not contain any user defined
5+
! function calls unless the function is ELEMENTAL.
6+
7+
module my_mod
8+
contains
9+
integer function my_func()
10+
my_func = 10
11+
end function my_func
12+
13+
impure integer function impure_my_func()
14+
impure_my_func = 20
15+
end function impure_my_func
16+
17+
impure elemental integer function impure_ele_my_func()
18+
impure_ele_my_func = 20
19+
end function impure_ele_my_func
20+
end module my_mod
21+
22+
subroutine workdistribute(aa, bb, cc, n)
23+
use my_mod
24+
integer n
25+
real aa(n), bb(n), cc(n)
26+
!$omp teams
27+
!$omp workdistribute
28+
!ERROR: User defined non-ELEMENTAL function 'my_func' is not allowed in a WORKDISTRIBUTE construct
29+
aa = my_func()
30+
aa = bb * cc
31+
!$omp end workdistribute
32+
!$omp end teams
33+
34+
end subroutine workdistribute
Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,34 @@
1+
! RUN: %python %S/../test_errors.py %s %flang -fopenmp -fopenmp-version=60
2+
! OpenMP Version 6.0
3+
! workdistribute Construct
4+
! All array assignments, scalar assignments, and masked array assignments
5+
! must be intrinsic assignments.
6+
7+
module defined_assign
8+
interface assignment(=)
9+
module procedure work_assign
10+
end interface
11+
12+
contains
13+
subroutine work_assign(a,b)
14+
integer, intent(out) :: a
15+
logical, intent(in) :: b(:)
16+
end subroutine work_assign
17+
end module defined_assign
18+
19+
program omp_workdistribute
20+
use defined_assign
21+
22+
integer :: a, aa(10), bb(10)
23+
logical :: l(10)
24+
l = .TRUE.
25+
26+
!$omp teams
27+
!$omp workdistribute
28+
!ERROR: Defined assignment statement is not allowed in a WORKDISTRIBUTE construct
29+
a = l
30+
aa = bb
31+
!$omp end workdistribute
32+
!$omp end teams
33+
34+
end program omp_workdistribute

0 commit comments

Comments
 (0)