@@ -285,9 +285,12 @@ void OmpStructureChecker::Enter(const parser::OpenMPConstruct &x) {
285
285
// called individually for each construct. Therefore a
286
286
// dirContext_ size `1` means the current construct is nested
287
287
if (dirContext_.size () >= 1 ) {
288
- if (GetSIMDNest ( ) > 0 ) {
288
+ if (GetDirectiveNest (SIMDNest ) > 0 ) {
289
289
CheckSIMDNest (x);
290
290
}
291
+ if (GetDirectiveNest (TargetNest) > 0 ) {
292
+ CheckTargetNest (x);
293
+ }
291
294
}
292
295
}
293
296
@@ -306,7 +309,7 @@ void OmpStructureChecker::Enter(const parser::OpenMPLoopConstruct &x) {
306
309
307
310
PushContextAndClauseSets (beginDir.source , beginDir.v );
308
311
if (llvm::omp::simdSet.test (GetContext ().directive )) {
309
- EnterSIMDNest ( );
312
+ EnterDirectiveNest (SIMDNest );
310
313
}
311
314
312
315
if (beginDir.v == llvm::omp::Directive::OMPD_do) {
@@ -473,6 +476,53 @@ void OmpStructureChecker::CheckSIMDNest(const parser::OpenMPConstruct &c) {
473
476
}
474
477
}
475
478
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
+
476
526
std::int64_t OmpStructureChecker::GetOrdCollapseLevel (
477
527
const parser::OpenMPLoopConstruct &x) {
478
528
const auto &beginLoopDir{std::get<parser::OmpBeginLoopDirective>(x.t )};
@@ -585,7 +635,7 @@ void OmpStructureChecker::CheckDistLinear(
585
635
586
636
void OmpStructureChecker::Leave (const parser::OpenMPLoopConstruct &) {
587
637
if (llvm::omp::simdSet.test (GetContext ().directive )) {
588
- ExitSIMDNest ( );
638
+ ExitDirectiveNest (SIMDNest );
589
639
}
590
640
dirContext_.pop_back ();
591
641
}
@@ -616,6 +666,9 @@ void OmpStructureChecker::Enter(const parser::OpenMPBlockConstruct &x) {
616
666
CheckMatching<parser::OmpBlockDirective>(beginDir, endDir);
617
667
618
668
PushContextAndClauseSets (beginDir.source , beginDir.v );
669
+ if (GetContext ().directive == llvm::omp::Directive::OMPD_target) {
670
+ EnterDirectiveNest (TargetNest);
671
+ }
619
672
620
673
if (CurrentDirectiveIsNested ()) {
621
674
CheckIfDoOrderedClause (beginDir);
@@ -625,11 +678,35 @@ void OmpStructureChecker::Enter(const parser::OpenMPBlockConstruct &x) {
625
678
if (GetContext ().directive == llvm::omp::Directive::OMPD_master) {
626
679
CheckMasterNesting (x);
627
680
}
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
+ }
628
700
}
629
701
630
702
CheckNoBranching (block, beginDir.v , beginDir.source );
631
703
632
704
switch (beginDir.v ) {
705
+ case llvm::omp::Directive::OMPD_target:
706
+ if (CheckTargetBlockOnlyTeams (block)) {
707
+ EnterDirectiveNest (TargetBlockOnlyTeams);
708
+ }
709
+ break ;
633
710
case llvm::omp::OMPD_workshare:
634
711
case llvm::omp::OMPD_parallel_workshare:
635
712
CheckWorkshareBlockStmts (block, beginDir.source );
@@ -683,6 +760,12 @@ void OmpStructureChecker::CheckIfDoOrderedClause(
683
760
}
684
761
685
762
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
+ }
686
769
dirContext_.pop_back ();
687
770
}
688
771
@@ -846,7 +929,9 @@ void OmpStructureChecker::Leave(const parser::OpenMPFlushConstruct &x) {
846
929
847
930
void OmpStructureChecker::Enter (const parser::OpenMPCancelConstruct &x) {
848
931
const auto &dir{std::get<parser::Verbatim>(x.t )};
932
+ const auto &type{std::get<parser::OmpCancelType>(x.t )};
849
933
PushContextAndClauseSets (dir.source , llvm::omp::Directive::OMPD_cancel);
934
+ CheckCancellationNest (dir.source , type.v );
850
935
}
851
936
852
937
void OmpStructureChecker::Leave (const parser::OpenMPCancelConstruct &) {
@@ -867,15 +952,140 @@ void OmpStructureChecker::Leave(const parser::OpenMPCriticalConstruct &) {
867
952
void OmpStructureChecker::Enter (
868
953
const parser::OpenMPCancellationPointConstruct &x) {
869
954
const auto &dir{std::get<parser::Verbatim>(x.t )};
955
+ const auto &type{std::get<parser::OmpCancelType>(x.t )};
870
956
PushContextAndClauseSets (
871
957
dir.source , llvm::omp::Directive::OMPD_cancellation_point);
958
+ CheckCancellationNest (dir.source , type.v );
872
959
}
873
960
874
961
void OmpStructureChecker::Leave (
875
962
const parser::OpenMPCancellationPointConstruct &) {
876
963
dirContext_.pop_back ();
877
964
}
878
965
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
+
879
1089
void OmpStructureChecker::Enter (const parser::OmpEndBlockDirective &x) {
880
1090
const auto &dir{std::get<parser::OmpBlockDirective>(x.t )};
881
1091
ResetPartialContext (dir.source );
@@ -1788,6 +1998,30 @@ void OmpStructureChecker::CheckPrivateSymbolsInOuterCxt(
1788
1998
}
1789
1999
}
1790
2000
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
+
1791
2025
void OmpStructureChecker::CheckWorkshareBlockStmts (
1792
2026
const parser::Block &block, parser::CharBlock source) {
1793
2027
OmpWorkshareBlockChecker ompWorkshareBlockChecker{context_, source};
0 commit comments