Skip to content

Commit 6c1b99c

Browse files
authored
Merge pull request #1013 from flang-compiler/jpr-cherry-pick
Cherry pick all flang commits from LLVM since last cherry-picking
2 parents bf166d3 + a4814f7 commit 6c1b99c

22 files changed

+1079
-111
lines changed

flang/docs/Extensions.md

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -58,6 +58,11 @@ write(buffer,*,delim="QUOTE") quotes
5858
print "('>',a10,'<')", buffer
5959
end
6060
```
61+
* The name of the control variable in an implied DO loop in an array
62+
constructor or DATA statement has a scope over the value-list only,
63+
not the bounds of the implied DO loop. It is not advisable to use
64+
an object of the same name as the index variable in a bounds
65+
expression, but it will work, instead of being needlessly undefined.
6166

6267
## Extensions, deletions, and legacy features supported by default
6368

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

Lines changed: 0 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -203,12 +203,6 @@ class DirectiveStructureChecker : public virtual BaseChecker {
203203
GetContext().actualClauses.push_back(type);
204204
}
205205

206-
void EnterSIMDNest() { simdNest_++; }
207-
208-
void ExitSIMDNest() { simdNest_--; }
209-
210-
int GetSIMDNest() { return simdNest_; }
211-
212206
// Check if the given clause is present in the current context
213207
const PC *FindClause(C type) {
214208
auto it{GetContext().clauseInfo.find(type)};
@@ -320,7 +314,6 @@ class DirectiveStructureChecker : public virtual BaseChecker {
320314
directiveClausesMap_;
321315

322316
std::string ClauseSetToString(const common::EnumSet<C, ClauseEnumSize> set);
323-
int simdNest_{0};
324317
};
325318

326319
template <typename D, typename C, typename PC, std::size_t ClauseEnumSize>

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

Lines changed: 237 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -285,9 +285,12 @@ void OmpStructureChecker::Enter(const parser::OpenMPConstruct &x) {
285285
// called individually for each construct. Therefore a
286286
// dirContext_ size `1` means the current construct is nested
287287
if (dirContext_.size() >= 1) {
288-
if (GetSIMDNest() > 0) {
288+
if (GetDirectiveNest(SIMDNest) > 0) {
289289
CheckSIMDNest(x);
290290
}
291+
if (GetDirectiveNest(TargetNest) > 0) {
292+
CheckTargetNest(x);
293+
}
291294
}
292295
}
293296

@@ -306,7 +309,7 @@ void OmpStructureChecker::Enter(const parser::OpenMPLoopConstruct &x) {
306309

307310
PushContextAndClauseSets(beginDir.source, beginDir.v);
308311
if (llvm::omp::simdSet.test(GetContext().directive)) {
309-
EnterSIMDNest();
312+
EnterDirectiveNest(SIMDNest);
310313
}
311314

312315
if (beginDir.v == llvm::omp::Directive::OMPD_do) {
@@ -473,6 +476,53 @@ void OmpStructureChecker::CheckSIMDNest(const parser::OpenMPConstruct &c) {
473476
}
474477
}
475478

479+
void OmpStructureChecker::CheckTargetNest(const parser::OpenMPConstruct &c) {
480+
// 2.12.5 Target Construct Restriction
481+
bool eligibleTarget{true};
482+
llvm::omp::Directive ineligibleTargetDir;
483+
std::visit(
484+
common::visitors{
485+
[&](const parser::OpenMPBlockConstruct &c) {
486+
const auto &beginBlockDir{
487+
std::get<parser::OmpBeginBlockDirective>(c.t)};
488+
const auto &beginDir{
489+
std::get<parser::OmpBlockDirective>(beginBlockDir.t)};
490+
if (beginDir.v == llvm::omp::Directive::OMPD_target_data) {
491+
eligibleTarget = false;
492+
ineligibleTargetDir = beginDir.v;
493+
}
494+
},
495+
[&](const parser::OpenMPStandaloneConstruct &c) {
496+
std::visit(
497+
common::visitors{
498+
[&](const parser::OpenMPSimpleStandaloneConstruct &c) {
499+
const auto &dir{
500+
std::get<parser::OmpSimpleStandaloneDirective>(c.t)};
501+
if (dir.v == llvm::omp::Directive::OMPD_target_update ||
502+
dir.v ==
503+
llvm::omp::Directive::OMPD_target_enter_data ||
504+
dir.v ==
505+
llvm::omp::Directive::OMPD_target_exit_data) {
506+
eligibleTarget = false;
507+
ineligibleTargetDir = dir.v;
508+
}
509+
},
510+
[&](const auto &c) {},
511+
},
512+
c.u);
513+
},
514+
[&](const auto &c) {},
515+
},
516+
c.u);
517+
if (!eligibleTarget) {
518+
context_.Say(parser::FindSourceLocation(c),
519+
"If %s directive is nested inside TARGET region, the behaviour "
520+
"is unspecified"_en_US,
521+
parser::ToUpperCaseLetters(
522+
getDirectiveName(ineligibleTargetDir).str()));
523+
}
524+
}
525+
476526
std::int64_t OmpStructureChecker::GetOrdCollapseLevel(
477527
const parser::OpenMPLoopConstruct &x) {
478528
const auto &beginLoopDir{std::get<parser::OmpBeginLoopDirective>(x.t)};
@@ -585,7 +635,7 @@ void OmpStructureChecker::CheckDistLinear(
585635

586636
void OmpStructureChecker::Leave(const parser::OpenMPLoopConstruct &) {
587637
if (llvm::omp::simdSet.test(GetContext().directive)) {
588-
ExitSIMDNest();
638+
ExitDirectiveNest(SIMDNest);
589639
}
590640
dirContext_.pop_back();
591641
}
@@ -616,6 +666,9 @@ void OmpStructureChecker::Enter(const parser::OpenMPBlockConstruct &x) {
616666
CheckMatching<parser::OmpBlockDirective>(beginDir, endDir);
617667

618668
PushContextAndClauseSets(beginDir.source, beginDir.v);
669+
if (GetContext().directive == llvm::omp::Directive::OMPD_target) {
670+
EnterDirectiveNest(TargetNest);
671+
}
619672

620673
if (CurrentDirectiveIsNested()) {
621674
CheckIfDoOrderedClause(beginDir);
@@ -625,11 +678,35 @@ void OmpStructureChecker::Enter(const parser::OpenMPBlockConstruct &x) {
625678
if (GetContext().directive == llvm::omp::Directive::OMPD_master) {
626679
CheckMasterNesting(x);
627680
}
681+
// A teams region can only be strictly nested within the implicit parallel
682+
// region or a target region.
683+
if (GetContext().directive == llvm::omp::Directive::OMPD_teams &&
684+
GetContextParent().directive != llvm::omp::Directive::OMPD_target) {
685+
context_.Say(parser::FindSourceLocation(x),
686+
"%s region can only be strictly nested within the implicit parallel "
687+
"region or TARGET region"_err_en_US,
688+
ContextDirectiveAsFortran());
689+
}
690+
// If a teams construct is nested within a target construct, that target
691+
// construct must contain no statements, declarations or directives outside
692+
// of the teams construct.
693+
if (GetContext().directive == llvm::omp::Directive::OMPD_teams &&
694+
GetContextParent().directive == llvm::omp::Directive::OMPD_target &&
695+
!GetDirectiveNest(TargetBlockOnlyTeams)) {
696+
context_.Say(GetContextParent().directiveSource,
697+
"TARGET construct with nested TEAMS region contains statements or "
698+
"directives outside of the TEAMS construct"_err_en_US);
699+
}
628700
}
629701

630702
CheckNoBranching(block, beginDir.v, beginDir.source);
631703

632704
switch (beginDir.v) {
705+
case llvm::omp::Directive::OMPD_target:
706+
if (CheckTargetBlockOnlyTeams(block)) {
707+
EnterDirectiveNest(TargetBlockOnlyTeams);
708+
}
709+
break;
633710
case llvm::omp::OMPD_workshare:
634711
case llvm::omp::OMPD_parallel_workshare:
635712
CheckWorkshareBlockStmts(block, beginDir.source);
@@ -683,6 +760,12 @@ void OmpStructureChecker::CheckIfDoOrderedClause(
683760
}
684761

685762
void OmpStructureChecker::Leave(const parser::OpenMPBlockConstruct &) {
763+
if (GetDirectiveNest(TargetBlockOnlyTeams)) {
764+
ExitDirectiveNest(TargetBlockOnlyTeams);
765+
}
766+
if (GetContext().directive == llvm::omp::Directive::OMPD_target) {
767+
ExitDirectiveNest(TargetNest);
768+
}
686769
dirContext_.pop_back();
687770
}
688771

@@ -846,7 +929,9 @@ void OmpStructureChecker::Leave(const parser::OpenMPFlushConstruct &x) {
846929

847930
void OmpStructureChecker::Enter(const parser::OpenMPCancelConstruct &x) {
848931
const auto &dir{std::get<parser::Verbatim>(x.t)};
932+
const auto &type{std::get<parser::OmpCancelType>(x.t)};
849933
PushContextAndClauseSets(dir.source, llvm::omp::Directive::OMPD_cancel);
934+
CheckCancellationNest(dir.source, type.v);
850935
}
851936

852937
void OmpStructureChecker::Leave(const parser::OpenMPCancelConstruct &) {
@@ -867,15 +952,140 @@ void OmpStructureChecker::Leave(const parser::OpenMPCriticalConstruct &) {
867952
void OmpStructureChecker::Enter(
868953
const parser::OpenMPCancellationPointConstruct &x) {
869954
const auto &dir{std::get<parser::Verbatim>(x.t)};
955+
const auto &type{std::get<parser::OmpCancelType>(x.t)};
870956
PushContextAndClauseSets(
871957
dir.source, llvm::omp::Directive::OMPD_cancellation_point);
958+
CheckCancellationNest(dir.source, type.v);
872959
}
873960

874961
void OmpStructureChecker::Leave(
875962
const parser::OpenMPCancellationPointConstruct &) {
876963
dirContext_.pop_back();
877964
}
878965

966+
void OmpStructureChecker::CheckCancellationNest(
967+
const parser::CharBlock &source, const parser::OmpCancelType::Type &type) {
968+
if (CurrentDirectiveIsNested()) {
969+
// If construct-type-clause is taskgroup, the cancellation construct must be
970+
// closely nested inside a task or a taskloop construct and the cancellation
971+
// region must be closely nested inside a taskgroup region. If
972+
// construct-type-clause is sections, the cancellation construct must be
973+
// closely nested inside a sections or section construct. Otherwise, the
974+
// cancellation construct must be closely nested inside an OpenMP construct
975+
// that matches the type specified in construct-type-clause of the
976+
// cancellation construct.
977+
978+
OmpDirectiveSet allowedTaskgroupSet{
979+
llvm::omp::Directive::OMPD_task, llvm::omp::Directive::OMPD_taskloop};
980+
OmpDirectiveSet allowedSectionsSet{llvm::omp::Directive::OMPD_sections,
981+
llvm::omp::Directive::OMPD_parallel_sections};
982+
OmpDirectiveSet allowedDoSet{llvm::omp::Directive::OMPD_do,
983+
llvm::omp::Directive::OMPD_distribute_parallel_do,
984+
llvm::omp::Directive::OMPD_parallel_do,
985+
llvm::omp::Directive::OMPD_target_parallel_do,
986+
llvm::omp::Directive::OMPD_target_teams_distribute_parallel_do,
987+
llvm::omp::Directive::OMPD_teams_distribute_parallel_do};
988+
OmpDirectiveSet allowedParallelSet{llvm::omp::Directive::OMPD_parallel,
989+
llvm::omp::Directive::OMPD_target_parallel};
990+
991+
bool eligibleCancellation{false};
992+
switch (type) {
993+
case parser::OmpCancelType::Type::Taskgroup:
994+
if (allowedTaskgroupSet.test(GetContextParent().directive)) {
995+
eligibleCancellation = true;
996+
if (dirContext_.size() >= 3) {
997+
// Check if the cancellation region is closely nested inside a
998+
// taskgroup region when there are more than two levels of directives
999+
// in the directive context stack.
1000+
if (GetContextParent().directive == llvm::omp::Directive::OMPD_task ||
1001+
FindClauseParent(llvm::omp::Clause::OMPC_nogroup)) {
1002+
for (int i = dirContext_.size() - 3; i >= 0; i--) {
1003+
if (dirContext_[i].directive ==
1004+
llvm::omp::Directive::OMPD_taskgroup) {
1005+
break;
1006+
}
1007+
if (allowedParallelSet.test(dirContext_[i].directive)) {
1008+
eligibleCancellation = false;
1009+
break;
1010+
}
1011+
}
1012+
}
1013+
}
1014+
}
1015+
if (!eligibleCancellation) {
1016+
context_.Say(source,
1017+
"With %s clause, %s construct must be closely nested inside TASK "
1018+
"or TASKLOOP construct and %s region must be closely nested inside "
1019+
"TASKGROUP region"_err_en_US,
1020+
parser::ToUpperCaseLetters(
1021+
parser::OmpCancelType::EnumToString(type)),
1022+
ContextDirectiveAsFortran(), ContextDirectiveAsFortran());
1023+
}
1024+
return;
1025+
case parser::OmpCancelType::Type::Sections:
1026+
if (allowedSectionsSet.test(GetContextParent().directive)) {
1027+
eligibleCancellation = true;
1028+
}
1029+
break;
1030+
case Fortran::parser::OmpCancelType::Type::Do:
1031+
if (allowedDoSet.test(GetContextParent().directive)) {
1032+
eligibleCancellation = true;
1033+
}
1034+
break;
1035+
case parser::OmpCancelType::Type::Parallel:
1036+
if (allowedParallelSet.test(GetContextParent().directive)) {
1037+
eligibleCancellation = true;
1038+
}
1039+
break;
1040+
}
1041+
if (!eligibleCancellation) {
1042+
context_.Say(source,
1043+
"With %s clause, %s construct cannot be closely nested inside %s "
1044+
"construct"_err_en_US,
1045+
parser::ToUpperCaseLetters(parser::OmpCancelType::EnumToString(type)),
1046+
ContextDirectiveAsFortran(),
1047+
parser::ToUpperCaseLetters(
1048+
getDirectiveName(GetContextParent().directive).str()));
1049+
}
1050+
} else {
1051+
// The cancellation directive cannot be orphaned.
1052+
switch (type) {
1053+
case parser::OmpCancelType::Type::Taskgroup:
1054+
context_.Say(source,
1055+
"%s %s directive is not closely nested inside "
1056+
"TASK or TASKLOOP"_err_en_US,
1057+
ContextDirectiveAsFortran(),
1058+
parser::ToUpperCaseLetters(
1059+
parser::OmpCancelType::EnumToString(type)));
1060+
break;
1061+
case parser::OmpCancelType::Type::Sections:
1062+
context_.Say(source,
1063+
"%s %s directive is not closely nested inside "
1064+
"SECTION or SECTIONS"_err_en_US,
1065+
ContextDirectiveAsFortran(),
1066+
parser::ToUpperCaseLetters(
1067+
parser::OmpCancelType::EnumToString(type)));
1068+
break;
1069+
case Fortran::parser::OmpCancelType::Type::Do:
1070+
context_.Say(source,
1071+
"%s %s directive is not closely nested inside "
1072+
"the construct that matches the DO clause type"_err_en_US,
1073+
ContextDirectiveAsFortran(),
1074+
parser::ToUpperCaseLetters(
1075+
parser::OmpCancelType::EnumToString(type)));
1076+
break;
1077+
case parser::OmpCancelType::Type::Parallel:
1078+
context_.Say(source,
1079+
"%s %s directive is not closely nested inside "
1080+
"the construct that matches the PARALLEL clause type"_err_en_US,
1081+
ContextDirectiveAsFortran(),
1082+
parser::ToUpperCaseLetters(
1083+
parser::OmpCancelType::EnumToString(type)));
1084+
break;
1085+
}
1086+
}
1087+
}
1088+
8791089
void OmpStructureChecker::Enter(const parser::OmpEndBlockDirective &x) {
8801090
const auto &dir{std::get<parser::OmpBlockDirective>(x.t)};
8811091
ResetPartialContext(dir.source);
@@ -1788,6 +1998,30 @@ void OmpStructureChecker::CheckPrivateSymbolsInOuterCxt(
17881998
}
17891999
}
17902000

2001+
bool OmpStructureChecker::CheckTargetBlockOnlyTeams(
2002+
const parser::Block &block) {
2003+
bool nestedTeams{false};
2004+
auto it{block.begin()};
2005+
2006+
if (const auto *ompConstruct{parser::Unwrap<parser::OpenMPConstruct>(*it)}) {
2007+
if (const auto *ompBlockConstruct{
2008+
std::get_if<parser::OpenMPBlockConstruct>(&ompConstruct->u)}) {
2009+
const auto &beginBlockDir{
2010+
std::get<parser::OmpBeginBlockDirective>(ompBlockConstruct->t)};
2011+
const auto &beginDir{
2012+
std::get<parser::OmpBlockDirective>(beginBlockDir.t)};
2013+
if (beginDir.v == llvm::omp::Directive::OMPD_teams) {
2014+
nestedTeams = true;
2015+
}
2016+
}
2017+
}
2018+
2019+
if (nestedTeams && ++it == block.end()) {
2020+
return true;
2021+
}
2022+
return false;
2023+
}
2024+
17912025
void OmpStructureChecker::CheckWorkshareBlockStmts(
17922026
const parser::Block &block, parser::CharBlock source) {
17932027
OmpWorkshareBlockChecker ompWorkshareBlockChecker{context_, source};

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

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -218,13 +218,17 @@ class OmpStructureChecker
218218
void SetLoopInfo(const parser::OpenMPLoopConstruct &x);
219219
void CheckIsLoopIvPartOfClause(
220220
llvmOmpClause clause, const parser::OmpObjectList &ompObjectList);
221+
bool CheckTargetBlockOnlyTeams(const parser::Block &);
221222
void CheckWorkshareBlockStmts(const parser::Block &, parser::CharBlock);
222223

223224
void CheckLoopItrVariableIsInt(const parser::OpenMPLoopConstruct &x);
224225
void CheckDoWhile(const parser::OpenMPLoopConstruct &x);
225226
void CheckCycleConstraints(const parser::OpenMPLoopConstruct &x);
226227
void CheckDistLinear(const parser::OpenMPLoopConstruct &x);
227228
void CheckSIMDNest(const parser::OpenMPConstruct &x);
229+
void CheckTargetNest(const parser::OpenMPConstruct &x);
230+
void CheckCancellationNest(
231+
const parser::CharBlock &source, const parser::OmpCancelType::Type &type);
228232
std::int64_t GetOrdCollapseLevel(const parser::OpenMPLoopConstruct &x);
229233
void CheckIfDoOrderedClause(const parser::OmpBlockDirective &blkDirectiv);
230234
bool CheckReductionOperators(const parser::OmpClause::Reduction &);
@@ -246,6 +250,17 @@ class OmpStructureChecker
246250
void CheckPredefinedAllocatorRestriction(
247251
const parser::CharBlock &source, const parser::Name &name);
248252
bool isPredefinedAllocator{false};
253+
void EnterDirectiveNest(const int index) { directiveNest_[index]++; }
254+
void ExitDirectiveNest(const int index) { directiveNest_[index]--; }
255+
int GetDirectiveNest(const int index) { return directiveNest_[index]; }
256+
257+
enum directiveNestType {
258+
SIMDNest,
259+
TargetBlockOnlyTeams,
260+
TargetNest,
261+
LastType
262+
};
263+
int directiveNest_[LastType + 1] = {0};
249264
};
250265
} // namespace Fortran::semantics
251266
#endif // FORTRAN_SEMANTICS_CHECK_OMP_STRUCTURE_H_

0 commit comments

Comments
 (0)