Index: flang/lib/Semantics/check-directive-structure.h =================================================================== --- flang/lib/Semantics/check-directive-structure.h +++ flang/lib/Semantics/check-directive-structure.h @@ -203,12 +203,6 @@ GetContext().actualClauses.push_back(type); } - void EnterSIMDNest() { simdNest_++; } - - void ExitSIMDNest() { simdNest_--; } - - int GetSIMDNest() { return simdNest_; } - // Check if the given clause is present in the current context const PC *FindClause(C type) { auto it{GetContext().clauseInfo.find(type)}; @@ -320,7 +314,6 @@ directiveClausesMap_; std::string ClauseSetToString(const common::EnumSet set); - int simdNest_{0}; }; template Index: flang/lib/Semantics/check-omp-structure.h =================================================================== --- flang/lib/Semantics/check-omp-structure.h +++ flang/lib/Semantics/check-omp-structure.h @@ -225,6 +225,7 @@ void CheckCycleConstraints(const parser::OpenMPLoopConstruct &x); void CheckDistLinear(const parser::OpenMPLoopConstruct &x); void CheckSIMDNest(const parser::OpenMPConstruct &x); + void CheckTargetNest(const parser::OpenMPConstruct &x); std::int64_t GetOrdCollapseLevel(const parser::OpenMPLoopConstruct &x); void CheckIfDoOrderedClause(const parser::OmpBlockDirective &blkDirectiv); bool CheckReductionOperators(const parser::OmpClause::Reduction &); @@ -246,6 +247,12 @@ void CheckPredefinedAllocatorRestriction( const parser::CharBlock &source, const parser::Name &name); bool isPredefinedAllocator{false}; + void EnterDirectiveNest(const int index) { directiveNest_[index]++; } + void ExitDirectiveNest(const int index) { directiveNest_[index]--; } + int GetDirectiveNest(const int index) { return directiveNest_[index]; } + + enum directiveNestType { SIMDNest, TargetNest, LastType }; + int directiveNest_[LastType + 1] = {0}; }; } // namespace Fortran::semantics #endif // FORTRAN_SEMANTICS_CHECK_OMP_STRUCTURE_H_ Index: flang/lib/Semantics/check-omp-structure.cpp =================================================================== --- flang/lib/Semantics/check-omp-structure.cpp +++ flang/lib/Semantics/check-omp-structure.cpp @@ -285,9 +285,12 @@ // called individually for each construct. Therefore a // dirContext_ size `1` means the current construct is nested if (dirContext_.size() >= 1) { - if (GetSIMDNest() > 0) { + if (GetDirectiveNest(SIMDNest) > 0) { CheckSIMDNest(x); } + if (GetDirectiveNest(TargetNest) > 0) { + CheckTargetNest(x); + } } } @@ -306,7 +309,7 @@ PushContextAndClauseSets(beginDir.source, beginDir.v); if (llvm::omp::simdSet.test(GetContext().directive)) { - EnterSIMDNest(); + EnterDirectiveNest(SIMDNest); } if (beginDir.v == llvm::omp::Directive::OMPD_do) { @@ -473,6 +476,53 @@ } } +void OmpStructureChecker::CheckTargetNest(const parser::OpenMPConstruct &c) { + // 2.12.5 Target Construct Restriction + bool eligibleTarget{true}; + llvm::omp::Directive ineligibleTargetDir; + std::visit( + common::visitors{ + [&](const parser::OpenMPBlockConstruct &c) { + const auto &beginBlockDir{ + std::get(c.t)}; + const auto &beginDir{ + std::get(beginBlockDir.t)}; + if (beginDir.v == llvm::omp::Directive::OMPD_target_data) { + eligibleTarget = false; + ineligibleTargetDir = beginDir.v; + } + }, + [&](const parser::OpenMPStandaloneConstruct &c) { + std::visit( + common::visitors{ + [&](const parser::OpenMPSimpleStandaloneConstruct &c) { + const auto &dir{ + std::get(c.t)}; + if (dir.v == llvm::omp::Directive::OMPD_target_update || + dir.v == + llvm::omp::Directive::OMPD_target_enter_data || + dir.v == + llvm::omp::Directive::OMPD_target_exit_data) { + eligibleTarget = false; + ineligibleTargetDir = dir.v; + } + }, + [&](const auto &c) {}, + }, + c.u); + }, + [&](const auto &c) {}, + }, + c.u); + if (!eligibleTarget) { + context_.Say(parser::FindSourceLocation(c), + "If %s directive is nested inside TARGET region, the behaviour " + "is unspecified"_en_US, + parser::ToUpperCaseLetters( + getDirectiveName(ineligibleTargetDir).str())); + } +} + std::int64_t OmpStructureChecker::GetOrdCollapseLevel( const parser::OpenMPLoopConstruct &x) { const auto &beginLoopDir{std::get(x.t)}; @@ -585,7 +635,7 @@ void OmpStructureChecker::Leave(const parser::OpenMPLoopConstruct &) { if (llvm::omp::simdSet.test(GetContext().directive)) { - ExitSIMDNest(); + ExitDirectiveNest(SIMDNest); } dirContext_.pop_back(); } @@ -616,6 +666,9 @@ CheckMatching(beginDir, endDir); PushContextAndClauseSets(beginDir.source, beginDir.v); + if (GetContext().directive == llvm::omp::Directive::OMPD_target) { + EnterDirectiveNest(TargetNest); + } if (CurrentDirectiveIsNested()) { CheckIfDoOrderedClause(beginDir); @@ -683,6 +736,9 @@ } void OmpStructureChecker::Leave(const parser::OpenMPBlockConstruct &) { + if (GetContext().directive == llvm::omp::Directive::OMPD_target) { + ExitDirectiveNest(TargetNest); + } dirContext_.pop_back(); } Index: flang/test/Semantics/omp-nested-target.f90 =================================================================== --- /dev/null +++ flang/test/Semantics/omp-nested-target.f90 @@ -0,0 +1,54 @@ +! RUN: %S/test_errors.sh %s %t %flang_fc1 -fopenmp +! REQUIRES: shell + +! OpenMP Version 5.0 +! Check OpenMP construct validity for the following directives: +! 2.12.5 Target Construct + +program main + integer :: i, j, N = 10 + real :: a, arrayA(512), arrayB(512), ai(10) + real, allocatable :: B(:) + + !$omp target + !WARNING: If TARGET UPDATE directive is nested inside TARGET region, the behaviour is unspecified + !$omp target update from(arrayA) to(arrayB) + do i = 1, 512 + arrayA(i) = arrayB(i) + end do + !$omp end target + + !$omp parallel + !$omp target + !$omp parallel + !WARNING: If TARGET UPDATE directive is nested inside TARGET region, the behaviour is unspecified + !$omp target update from(arrayA) to(arrayB) + do i = 1, 512 + arrayA(i) = arrayB(i) + end do + !$omp end parallel + !$omp end target + !$omp end parallel + + !$omp target + !WARNING: If TARGET DATA directive is nested inside TARGET region, the behaviour is unspecified + !$omp target data map(to: a) + do i = 1, N + a = 3.14 + end do + !$omp end target data + !$omp end target + + allocate(B(N)) + !$omp target + !WARNING: If TARGET ENTER DATA directive is nested inside TARGET region, the behaviour is unspecified + !$omp target enter data map(alloc:B) + !$omp end target + + !$omp target + !WARNING: If TARGET EXIT DATA directive is nested inside TARGET region, the behaviour is unspecified + !$omp target exit data map(delete:B) + !$omp end target + deallocate(B) + +end program main