diff --git a/flang/lib/Semantics/check-omp-structure.h b/flang/lib/Semantics/check-omp-structure.h --- a/flang/lib/Semantics/check-omp-structure.h +++ b/flang/lib/Semantics/check-omp-structure.h @@ -226,6 +226,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); void CheckCancellationNest( const parser::CharBlock &source, const parser::OmpCancelType::Type &type); std::int64_t GetOrdCollapseLevel(const parser::OpenMPLoopConstruct &x); @@ -253,7 +254,12 @@ void ExitDirectiveNest(const int index) { directiveNest_[index]--; } int GetDirectiveNest(const int index) { return directiveNest_[index]; } - enum directiveNestType { SIMDNest, TargetBlockOnlyTeams, LastType }; + enum directiveNestType { + SIMDNest, + TargetBlockOnlyTeams, + TargetNest, + LastType + }; int directiveNest_[LastType + 1] = {0}; }; } // namespace Fortran::semantics diff --git a/flang/lib/Semantics/check-omp-structure.cpp b/flang/lib/Semantics/check-omp-structure.cpp --- a/flang/lib/Semantics/check-omp-structure.cpp +++ b/flang/lib/Semantics/check-omp-structure.cpp @@ -288,6 +288,9 @@ if (GetDirectiveNest(SIMDNest) > 0) { CheckSIMDNest(x); } + if (GetDirectiveNest(TargetNest) > 0) { + CheckTargetNest(x); + } } } @@ -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)}; @@ -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); @@ -710,6 +763,9 @@ if (GetDirectiveNest(TargetBlockOnlyTeams)) { ExitDirectiveNest(TargetBlockOnlyTeams); } + if (GetContext().directive == llvm::omp::Directive::OMPD_target) { + ExitDirectiveNest(TargetNest); + } dirContext_.pop_back(); } diff --git a/flang/test/Semantics/omp-nested-target.f90 b/flang/test/Semantics/omp-nested-target.f90 new file mode 100644 --- /dev/null +++ b/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