@@ -575,6 +575,7 @@ void OmpStructureChecker::Leave(const parser::OpenMPConstruct &) {
575
575
}
576
576
577
577
void OmpStructureChecker::Enter (const parser::OpenMPLoopConstruct &x) {
578
+ loopStack_.push_back (&x);
578
579
const auto &beginLoopDir{std::get<parser::OmpBeginLoopDirective>(x.t )};
579
580
const auto &beginDir{std::get<parser::OmpLoopDirective>(beginLoopDir.t )};
580
581
@@ -968,11 +969,19 @@ void OmpStructureChecker::CheckDistLinear(
968
969
}
969
970
}
970
971
971
- void OmpStructureChecker::Leave (const parser::OpenMPLoopConstruct &) {
972
+ void OmpStructureChecker::Leave (const parser::OpenMPLoopConstruct &x ) {
972
973
if (llvm::omp::allSimdSet.test (GetContext ().directive )) {
973
974
ExitDirectiveNest (SIMDNest);
974
975
}
975
976
dirContext_.pop_back ();
977
+
978
+ assert (!loopStack_.empty () && " Expecting non-empty loop stack" );
979
+ const LoopConstruct &top{loopStack_.back ()};
980
+ #ifndef NDEBUG
981
+ auto *loopc{std::get_if<const parser::OpenMPLoopConstruct *>(&top)};
982
+ assert (loopc != nullptr && *loopc == &x && " Mismatched loop constructs" );
983
+ #endif
984
+ loopStack_.pop_back ();
976
985
}
977
986
978
987
void OmpStructureChecker::Enter (const parser::OmpEndLoopDirective &x) {
@@ -1103,8 +1112,7 @@ void OmpStructureChecker::Leave(const parser::OpenMPBlockConstruct &) {
1103
1112
void OmpStructureChecker::ChecksOnOrderedAsBlock () {
1104
1113
if (FindClause (llvm::omp::Clause::OMPC_depend)) {
1105
1114
context_.Say (GetContext ().clauseSource ,
1106
- " DEPEND(*) clauses are not allowed when ORDERED construct is a block"
1107
- " construct with an ORDERED region" _err_en_US);
1115
+ " DEPEND clauses are not allowed when ORDERED construct is a block construct with an ORDERED region" _err_en_US);
1108
1116
return ;
1109
1117
}
1110
1118
@@ -1654,15 +1662,14 @@ void OmpStructureChecker::ChecksOnOrderedAsStandalone() {
1654
1662
if (FindClause (llvm::omp::Clause::OMPC_threads) ||
1655
1663
FindClause (llvm::omp::Clause::OMPC_simd)) {
1656
1664
context_.Say (GetContext ().clauseSource ,
1657
- " THREADS, SIMD clauses are not allowed when ORDERED construct is a "
1658
- " standalone construct with no ORDERED region" _err_en_US);
1665
+ " THREADS and SIMD clauses are not allowed when ORDERED construct is a standalone construct with no ORDERED region" _err_en_US);
1659
1666
}
1660
1667
1661
1668
int dependSinkCount{0 }, dependSourceCount{0 };
1662
1669
bool exclusiveShown{false }, duplicateSourceShown{false };
1663
1670
1664
- auto visitDoacross = [&](const parser::OmpDoacross &doa,
1665
- const parser::CharBlock &src) {
1671
+ auto visitDoacross{ [&](const parser::OmpDoacross &doa,
1672
+ const parser::CharBlock &src) {
1666
1673
common::visit (
1667
1674
common::visitors{
1668
1675
[&](const parser::OmpDoacross::Source &) { dependSourceCount++; },
@@ -1678,10 +1685,11 @@ void OmpStructureChecker::ChecksOnOrderedAsStandalone() {
1678
1685
context_.Say (src,
1679
1686
" At most one SOURCE dependence type can appear on the ORDERED directive" _err_en_US);
1680
1687
}
1681
- };
1688
+ }} ;
1682
1689
1683
- auto clauseAll = FindClauses (llvm::omp::Clause::OMPC_depend);
1684
- for (auto itr = clauseAll.first ; itr != clauseAll.second ; ++itr) {
1690
+ // Visit the DEPEND and DOACROSS clauses.
1691
+ auto depClauses{FindClauses (llvm::omp::Clause::OMPC_depend)};
1692
+ for (auto itr{depClauses.first }; itr != depClauses.second ; ++itr) {
1685
1693
const auto &dependClause{
1686
1694
std::get<parser::OmpClause::Depend>(itr->second ->u )};
1687
1695
if (auto *doAcross{std::get_if<parser::OmpDoacross>(&dependClause.v .u )}) {
@@ -1691,6 +1699,11 @@ void OmpStructureChecker::ChecksOnOrderedAsStandalone() {
1691
1699
" Only SINK or SOURCE dependence types are allowed when ORDERED construct is a standalone construct with no ORDERED region" _err_en_US);
1692
1700
}
1693
1701
}
1702
+ auto doaClauses{FindClauses (llvm::omp::Clause::OMPC_doacross)};
1703
+ for (auto itr{doaClauses.first }; itr != doaClauses.second ; ++itr) {
1704
+ auto &doaClause{std::get<parser::OmpClause::Doacross>(itr->second ->u )};
1705
+ visitDoacross (doaClause.v .v , itr->second ->source );
1706
+ }
1694
1707
1695
1708
bool isNestedInDoOrderedWithPara{false };
1696
1709
if (CurrentDirectiveIsNested () &&
@@ -1718,23 +1731,28 @@ void OmpStructureChecker::ChecksOnOrderedAsStandalone() {
1718
1731
1719
1732
void OmpStructureChecker::CheckOrderedDependClause (
1720
1733
std::optional<int64_t > orderedValue) {
1721
- auto visitDoacross = [&](const parser::OmpDoacross &doa,
1722
- const parser::CharBlock &src) {
1734
+ auto visitDoacross{ [&](const parser::OmpDoacross &doa,
1735
+ const parser::CharBlock &src) {
1723
1736
if (auto *sinkVector{std::get_if<parser::OmpDoacross::Sink>(&doa.u )}) {
1724
1737
int64_t numVar = sinkVector->v .v .size ();
1725
1738
if (orderedValue != numVar) {
1726
1739
context_.Say (src,
1727
1740
" The number of variables in the SINK iteration vector does not match the parameter specified in ORDERED clause" _err_en_US);
1728
1741
}
1729
1742
}
1730
- };
1731
- auto clauseAll {FindClauses (llvm::omp::Clause::OMPC_depend)};
1732
- for (auto itr = clauseAll .first ; itr != clauseAll .second ; ++itr) {
1743
+ }} ;
1744
+ auto depClauses {FindClauses (llvm::omp::Clause::OMPC_depend)};
1745
+ for (auto itr{depClauses .first } ; itr != depClauses .second ; ++itr) {
1733
1746
auto &dependClause{std::get<parser::OmpClause::Depend>(itr->second ->u )};
1734
1747
if (auto *doAcross{std::get_if<parser::OmpDoacross>(&dependClause.v .u )}) {
1735
1748
visitDoacross (*doAcross, itr->second ->source );
1736
1749
}
1737
1750
}
1751
+ auto doaClauses = FindClauses (llvm::omp::Clause::OMPC_doacross);
1752
+ for (auto itr{doaClauses.first }; itr != doaClauses.second ; ++itr) {
1753
+ auto &doaClause{std::get<parser::OmpClause::Doacross>(itr->second ->u )};
1754
+ visitDoacross (doaClause.v .v , itr->second ->source );
1755
+ }
1738
1756
}
1739
1757
1740
1758
void OmpStructureChecker::CheckTargetUpdate () {
@@ -2712,7 +2730,6 @@ CHECK_SIMPLE_CLAUSE(Bind, OMPC_bind)
2712
2730
CHECK_SIMPLE_CLAUSE (Align, OMPC_align)
2713
2731
CHECK_SIMPLE_CLAUSE (Compare, OMPC_compare)
2714
2732
CHECK_SIMPLE_CLAUSE (CancellationConstructType, OMPC_cancellation_construct_type)
2715
- CHECK_SIMPLE_CLAUSE (Doacross, OMPC_doacross)
2716
2733
CHECK_SIMPLE_CLAUSE (OmpxAttribute, OMPC_ompx_attribute)
2717
2734
CHECK_SIMPLE_CLAUSE (OmpxBare, OMPC_ompx_bare)
2718
2735
CHECK_SIMPLE_CLAUSE (Fail, OMPC_fail)
@@ -3493,6 +3510,7 @@ void OmpStructureChecker::Enter(const parser::OmpClause::Depend &x) {
3493
3510
" Unexpected alternative in update clause" );
3494
3511
3495
3512
if (doaDep) {
3513
+ CheckDoacross (*doaDep);
3496
3514
CheckDependenceType (doaDep->GetDepType ());
3497
3515
} else {
3498
3516
CheckTaskDependenceType (taskDep->GetTaskDepType ());
@@ -3572,6 +3590,93 @@ void OmpStructureChecker::Enter(const parser::OmpClause::Depend &x) {
3572
3590
}
3573
3591
}
3574
3592
3593
+ void OmpStructureChecker::Enter (const parser::OmpClause::Doacross &x) {
3594
+ CheckAllowedClause (llvm::omp::Clause::OMPC_doacross);
3595
+ CheckDoacross (x.v .v );
3596
+ }
3597
+
3598
+ void OmpStructureChecker::CheckDoacross (const parser::OmpDoacross &doa) {
3599
+ if (std::holds_alternative<parser::OmpDoacross::Source>(doa.u )) {
3600
+ // Nothing to check here.
3601
+ return ;
3602
+ }
3603
+
3604
+ // Process SINK dependence type. SINK may only appear in an ORDER construct,
3605
+ // which references a prior ORDERED(n) clause on a DO or SIMD construct
3606
+ // that marks the top of the loop nest.
3607
+
3608
+ auto &sink{std::get<parser::OmpDoacross::Sink>(doa.u )};
3609
+ const std::list<parser::OmpIteration> &vec{sink.v .v };
3610
+
3611
+ // Check if the variables in the iteration vector are unique.
3612
+ struct Less {
3613
+ bool operator ()(
3614
+ const parser::OmpIteration *a, const parser::OmpIteration *b) const {
3615
+ auto namea{std::get<parser::Name>(a->t )};
3616
+ auto nameb{std::get<parser::Name>(b->t )};
3617
+ assert (namea.symbol && nameb.symbol && " Unresolved symbols" );
3618
+ // The non-determinism of the "<" doesn't matter, we only care about
3619
+ // equality, i.e. a == b <=> !(a < b) && !(b < a)
3620
+ return reinterpret_cast <uintptr_t >(namea.symbol ) <
3621
+ reinterpret_cast <uintptr_t >(nameb.symbol );
3622
+ }
3623
+ };
3624
+ if (auto *duplicate{FindDuplicateEntry<parser::OmpIteration, Less>(vec)}) {
3625
+ auto name{std::get<parser::Name>(duplicate->t )};
3626
+ context_.Say (name.source ,
3627
+ " Duplicate variable '%s' in the iteration vector" _err_en_US,
3628
+ name.ToString ());
3629
+ }
3630
+
3631
+ // Check if the variables in the iteration vector are induction variables.
3632
+ // Ignore any mismatch between the size of the iteration vector and the
3633
+ // number of DO constructs on the stack. This is checked elsewhere.
3634
+
3635
+ auto GetLoopDirective{[](const parser::OpenMPLoopConstruct &x) {
3636
+ auto &begin{std::get<parser::OmpBeginLoopDirective>(x.t )};
3637
+ return std::get<parser::OmpLoopDirective>(begin.t ).v ;
3638
+ }};
3639
+ auto GetLoopClauses{[](const parser::OpenMPLoopConstruct &x)
3640
+ -> const std::list<parser::OmpClause> & {
3641
+ auto &begin{std::get<parser::OmpBeginLoopDirective>(x.t )};
3642
+ return std::get<parser::OmpClauseList>(begin.t ).v ;
3643
+ }};
3644
+
3645
+ std::set<const Symbol *> inductionVars;
3646
+ for (const LoopConstruct &loop : llvm::reverse (loopStack_)) {
3647
+ if (auto *doc{std::get_if<const parser::DoConstruct *>(&loop)}) {
3648
+ // Do-construct, collect the induction variable.
3649
+ if (auto &control{(*doc)->GetLoopControl ()}) {
3650
+ if (auto *b{std::get_if<parser::LoopControl::Bounds>(&control->u )}) {
3651
+ inductionVars.insert (b->name .thing .symbol );
3652
+ }
3653
+ }
3654
+ } else {
3655
+ // Omp-loop-construct, check if it's do/simd with an ORDERED clause.
3656
+ auto *loopc{std::get_if<const parser::OpenMPLoopConstruct *>(&loop)};
3657
+ assert (loopc && " Expecting OpenMPLoopConstruct" );
3658
+ llvm::omp::Directive loopDir{GetLoopDirective (**loopc)};
3659
+ if (loopDir == llvm::omp::OMPD_do || loopDir == llvm::omp::OMPD_simd) {
3660
+ auto IsOrdered{[](const parser::OmpClause &c) {
3661
+ return c.Id () == llvm::omp::OMPC_ordered;
3662
+ }};
3663
+ // If it has ORDERED clause, stop the traversal.
3664
+ if (llvm::any_of (GetLoopClauses (**loopc), IsOrdered)) {
3665
+ break ;
3666
+ }
3667
+ }
3668
+ }
3669
+ }
3670
+ for (const parser::OmpIteration &iter : vec) {
3671
+ auto &name{std::get<parser::Name>(iter.t )};
3672
+ if (!inductionVars.count (name.symbol )) {
3673
+ context_.Say (name.source ,
3674
+ " The iteration vector element '%s' is not an induction variable within the ORDERED loop nest" _err_en_US,
3675
+ name.ToString ());
3676
+ }
3677
+ }
3678
+ }
3679
+
3575
3680
void OmpStructureChecker::CheckCopyingPolymorphicAllocatable (
3576
3681
SymbolSourceMap &symbols, const llvm::omp::Clause clause) {
3577
3682
if (context_.ShouldWarn (common::UsageWarning::Portability)) {
@@ -4326,6 +4431,22 @@ void OmpStructureChecker::Enter(
4326
4431
CheckAllowedRequiresClause (llvm::omp::Clause::OMPC_unified_shared_memory);
4327
4432
}
4328
4433
4434
+ void OmpStructureChecker::Enter (const parser::DoConstruct &x) {
4435
+ Base::Enter (x);
4436
+ loopStack_.push_back (&x);
4437
+ }
4438
+
4439
+ void OmpStructureChecker::Leave (const parser::DoConstruct &x) {
4440
+ assert (!loopStack_.empty () && " Expecting non-empty loop stack" );
4441
+ const LoopConstruct &top = loopStack_.back ();
4442
+ #ifndef NDEBUG
4443
+ auto *doc{std::get_if<const parser::DoConstruct *>(&top)};
4444
+ assert (doc != nullptr && *doc == &x && " Mismatched loop constructs" );
4445
+ #endif
4446
+ loopStack_.pop_back ();
4447
+ Base::Leave (x);
4448
+ }
4449
+
4329
4450
void OmpStructureChecker::CheckAllowedRequiresClause (llvmOmpClause clause) {
4330
4451
CheckAllowedClause (clause);
4331
4452
0 commit comments