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 @@ -205,6 +205,11 @@ void CheckPredefinedAllocatorRestriction( const parser::CharBlock &source, const parser::Name &name); bool isPredefinedAllocator{false}; + + void CheckAllowedRequiresClause(llvmOmpClause clause); + bool deviceConstructFound_{false}; + bool atomicDirectiveDefaultOrderFound_{false}; + void EnterDirectiveNest(const int index) { directiveNest_[index]++; } void ExitDirectiveNest(const int index) { directiveNest_[index]--; } int GetDirectiveNest(const int index) { return directiveNest_[index]; } 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 @@ -400,6 +400,13 @@ EnterDirectiveNest(SIMDNest); } + // Combined target loop constructs are target device constructs. Keep track of + // whether any such construct has been visited to later check that REQUIRES + // directives for target-related options don't appear after them. + if (llvm::omp::allTargetSet.test(beginDir.v)) { + deviceConstructFound_ = true; + } + if (beginDir.v == llvm::omp::Directive::OMPD_do) { // 2.7.1 do-clause -> private-clause | // firstprivate-clause | @@ -791,6 +798,13 @@ CheckNoBranching(block, beginDir.v, beginDir.source); + // Target block constructs are target device constructs. Keep track of + // whether any such construct has been visited to later check that REQUIRES + // directives for target-related options don't appear after them. + if (llvm::omp::allTargetSet.test(beginDir.v)) { + deviceConstructFound_ = true; + } + switch (beginDir.v) { case llvm::omp::Directive::OMPD_target: if (CheckTargetBlockOnlyTeams(block)) { @@ -1189,22 +1203,47 @@ void OmpStructureChecker::Leave(const parser::OpenMPDeclareTargetConstruct &x) { const auto &dir{std::get(x.t)}; const auto &spec{std::get(x.t)}; + // Handle both forms of DECLARE TARGET. + // - Extended list: It behaves as if there was an ENTER/TO clause with the + // list of objects as argument. It accepts no explicit clauses. + // - With clauses. if (const auto *objectList{parser::Unwrap(spec.u)}) { + deviceConstructFound_ = true; CheckSymbolNames(dir.source, *objectList); CheckIsVarPartOfAnotherVar(dir.source, *objectList); CheckThreadprivateOrDeclareTargetVar(*objectList); } else if (const auto *clauseList{ parser::Unwrap(spec.u)}) { + bool toClauseFound{false}, deviceTypeClauseFound{false}; for (const auto &clause : clauseList->v) { - if (const auto *toClause{std::get_if(&clause.u)}) { - CheckSymbolNames(dir.source, toClause->v); - CheckIsVarPartOfAnotherVar(dir.source, toClause->v); - CheckThreadprivateOrDeclareTargetVar(toClause->v); - } else if (const auto *linkClause{ - std::get_if(&clause.u)}) { - CheckSymbolNames(dir.source, linkClause->v); - CheckIsVarPartOfAnotherVar(dir.source, linkClause->v); - CheckThreadprivateOrDeclareTargetVar(linkClause->v); + common::visit( + common::visitors{ + [&](const parser::OmpClause::To &toClause) { + toClauseFound = true; + CheckSymbolNames(dir.source, toClause.v); + CheckIsVarPartOfAnotherVar(dir.source, toClause.v); + CheckThreadprivateOrDeclareTargetVar(toClause.v); + }, + [&](const parser::OmpClause::Link &linkClause) { + CheckSymbolNames(dir.source, linkClause.v); + CheckIsVarPartOfAnotherVar(dir.source, linkClause.v); + CheckThreadprivateOrDeclareTargetVar(linkClause.v); + }, + [&](const parser::OmpClause::DeviceType &deviceTypeClause) { + deviceTypeClauseFound = true; + if (deviceTypeClause.v.v != + parser::OmpDeviceTypeClause::Type::Host) { + // Function / subroutine explicitly marked as runnable by the + // target device. + deviceConstructFound_ = true; + } + }, + [&](const auto &) {}, + }, + clause.u); + + if (toClauseFound && !deviceTypeClauseFound) { + deviceConstructFound_ = true; } } } @@ -1721,6 +1760,9 @@ if (rightHandClauseList) { checkForValidMemoryOrderClause(rightHandClauseList); } + if (numMemoryOrderClause == 0) { + atomicDirectiveDefaultOrderFound_ = true; + } } void OmpStructureChecker::Enter(const parser::OpenMPAtomicConstruct &x) { @@ -1917,7 +1959,6 @@ // Following clauses do not have a separate node in parse-tree.h. CHECK_SIMPLE_CLAUSE(AcqRel, OMPC_acq_rel) CHECK_SIMPLE_CLAUSE(Acquire, OMPC_acquire) -CHECK_SIMPLE_CLAUSE(AtomicDefaultMemOrder, OMPC_atomic_default_mem_order) CHECK_SIMPLE_CLAUSE(Affinity, OMPC_affinity) CHECK_SIMPLE_CLAUSE(Capture, OMPC_capture) CHECK_SIMPLE_CLAUSE(Default, OMPC_default) @@ -1926,7 +1967,6 @@ CHECK_SIMPLE_CLAUSE(Detach, OMPC_detach) CHECK_SIMPLE_CLAUSE(DeviceType, OMPC_device_type) CHECK_SIMPLE_CLAUSE(DistSchedule, OMPC_dist_schedule) -CHECK_SIMPLE_CLAUSE(DynamicAllocators, OMPC_dynamic_allocators) CHECK_SIMPLE_CLAUSE(Exclusive, OMPC_exclusive) CHECK_SIMPLE_CLAUSE(Final, OMPC_final) CHECK_SIMPLE_CLAUSE(Flush, OMPC_flush) @@ -1939,7 +1979,6 @@ CHECK_SIMPLE_CLAUSE(Nontemporal, OMPC_nontemporal) CHECK_SIMPLE_CLAUSE(Order, OMPC_order) CHECK_SIMPLE_CLAUSE(Read, OMPC_read) -CHECK_SIMPLE_CLAUSE(ReverseOffload, OMPC_reverse_offload) CHECK_SIMPLE_CLAUSE(Threadprivate, OMPC_threadprivate) CHECK_SIMPLE_CLAUSE(Threads, OMPC_threads) CHECK_SIMPLE_CLAUSE(Inbranch, OMPC_inbranch) @@ -1960,8 +1999,6 @@ CHECK_SIMPLE_CLAUSE(Sizes, OMPC_sizes) CHECK_SIMPLE_CLAUSE(TaskReduction, OMPC_task_reduction) CHECK_SIMPLE_CLAUSE(To, OMPC_to) -CHECK_SIMPLE_CLAUSE(UnifiedAddress, OMPC_unified_address) -CHECK_SIMPLE_CLAUSE(UnifiedSharedMemory, OMPC_unified_shared_memory) CHECK_SIMPLE_CLAUSE(Uniform, OMPC_uniform) CHECK_SIMPLE_CLAUSE(Unknown, OMPC_unknown) CHECK_SIMPLE_CLAUSE(Untied, OMPC_untied) @@ -2962,4 +2999,49 @@ clause.u); } +void OmpStructureChecker::Enter( + const parser::OmpClause::AtomicDefaultMemOrder &x) { + CheckAllowedRequiresClause(llvm::omp::Clause::OMPC_atomic_default_mem_order); +} + +void OmpStructureChecker::Enter(const parser::OmpClause::DynamicAllocators &x) { + CheckAllowedRequiresClause(llvm::omp::Clause::OMPC_dynamic_allocators); +} + +void OmpStructureChecker::Enter(const parser::OmpClause::ReverseOffload &x) { + CheckAllowedRequiresClause(llvm::omp::Clause::OMPC_reverse_offload); +} + +void OmpStructureChecker::Enter(const parser::OmpClause::UnifiedAddress &x) { + CheckAllowedRequiresClause(llvm::omp::Clause::OMPC_unified_address); +} + +void OmpStructureChecker::Enter( + const parser::OmpClause::UnifiedSharedMemory &x) { + CheckAllowedRequiresClause(llvm::omp::Clause::OMPC_unified_shared_memory); +} + +void OmpStructureChecker::CheckAllowedRequiresClause(llvmOmpClause clause) { + CheckAllowed(clause); + + if (clause == llvm::omp::Clause::OMPC_atomic_default_mem_order) { + // Check that it does not appear after an atomic operation without memory + // order + if (atomicDirectiveDefaultOrderFound_) { + context_.Say(GetContext().clauseSource, + "REQUIRES directive with '%s' clause found lexically after atomic " + "operation without a memory order clause"_err_en_US, + parser::ToUpperCaseLetters(getClauseName(clause).str())); + } + } else { + // Check that it does not appear after a device construct + if (deviceConstructFound_) { + context_.Say(GetContext().clauseSource, + "REQUIRES directive with '%s' clause found lexically after device " + "construct"_err_en_US, + parser::ToUpperCaseLetters(getClauseName(clause).str())); + } + } +} + } // namespace Fortran::semantics diff --git a/flang/test/Semantics/OpenMP/requires.f90 b/flang/test/Semantics/OpenMP/requires01.f90 rename from flang/test/Semantics/OpenMP/requires.f90 rename to flang/test/Semantics/OpenMP/requires01.f90 diff --git a/flang/test/Semantics/OpenMP/requires02.f90 b/flang/test/Semantics/OpenMP/requires02.f90 new file mode 100644 --- /dev/null +++ b/flang/test/Semantics/OpenMP/requires02.f90 @@ -0,0 +1,17 @@ +! RUN: %python %S/../test_errors.py %s %flang -fopenmp +! OpenMP Version 5.0 +! 2.4 Requires directive +! All atomic_default_mem_order clauses in 'requires' directives must come +! strictly before any atomic directives on which the memory_order clause is not +! specified. + +subroutine f + integer :: a = 0 + !$omp atomic + a = a + 1 +end subroutine f + +subroutine g + !ERROR: REQUIRES directive with 'ATOMIC_DEFAULT_MEM_ORDER' clause found lexically after atomic operation without a memory order clause + !$omp requires atomic_default_mem_order(relaxed) +end subroutine g diff --git a/flang/test/Semantics/OpenMP/requires03.f90 b/flang/test/Semantics/OpenMP/requires03.f90 new file mode 100644 --- /dev/null +++ b/flang/test/Semantics/OpenMP/requires03.f90 @@ -0,0 +1,21 @@ +! RUN: %python %S/../test_errors.py %s %flang -fopenmp +! OpenMP Version 5.0 +! 2.4 Requires directive +! Target-related clauses in 'requires' directives must come strictly before any +! device constructs, such as target regions. + +subroutine f + !$omp target + !$omp end target +end subroutine f + +subroutine g + !ERROR: REQUIRES directive with 'DYNAMIC_ALLOCATORS' clause found lexically after device construct + !$omp requires dynamic_allocators + !ERROR: REQUIRES directive with 'REVERSE_OFFLOAD' clause found lexically after device construct + !$omp requires reverse_offload + !ERROR: REQUIRES directive with 'UNIFIED_ADDRESS' clause found lexically after device construct + !$omp requires unified_address + !ERROR: REQUIRES directive with 'UNIFIED_SHARED_MEMORY' clause found lexically after device construct + !$omp requires unified_shared_memory +end subroutine g diff --git a/flang/test/Semantics/OpenMP/requires04.f90 b/flang/test/Semantics/OpenMP/requires04.f90 new file mode 100644 --- /dev/null +++ b/flang/test/Semantics/OpenMP/requires04.f90 @@ -0,0 +1,20 @@ +! RUN: %python %S/../test_errors.py %s %flang -fopenmp +! OpenMP Version 5.0 +! 2.4 Requires directive +! Target-related clauses in 'requires' directives must come strictly before any +! device constructs, such as declare target with device_type=nohost|any. + +subroutine f + !$omp declare target device_type(nohost) +end subroutine f + +subroutine g + !ERROR: REQUIRES directive with 'DYNAMIC_ALLOCATORS' clause found lexically after device construct + !$omp requires dynamic_allocators + !ERROR: REQUIRES directive with 'REVERSE_OFFLOAD' clause found lexically after device construct + !$omp requires reverse_offload + !ERROR: REQUIRES directive with 'UNIFIED_ADDRESS' clause found lexically after device construct + !$omp requires unified_address + !ERROR: REQUIRES directive with 'UNIFIED_SHARED_MEMORY' clause found lexically after device construct + !$omp requires unified_shared_memory +end subroutine g diff --git a/flang/test/Semantics/OpenMP/requires05.f90 b/flang/test/Semantics/OpenMP/requires05.f90 new file mode 100644 --- /dev/null +++ b/flang/test/Semantics/OpenMP/requires05.f90 @@ -0,0 +1,20 @@ +! RUN: %python %S/../test_errors.py %s %flang -fopenmp +! OpenMP Version 5.0 +! 2.4 Requires directive +! Target-related clauses in 'requires' directives must come strictly before any +! device constructs, such as declare target with 'to' clause and no device_type. + +subroutine f + !$omp declare target to(f) +end subroutine f + +subroutine g + !ERROR: REQUIRES directive with 'DYNAMIC_ALLOCATORS' clause found lexically after device construct + !$omp requires dynamic_allocators + !ERROR: REQUIRES directive with 'REVERSE_OFFLOAD' clause found lexically after device construct + !$omp requires reverse_offload + !ERROR: REQUIRES directive with 'UNIFIED_ADDRESS' clause found lexically after device construct + !$omp requires unified_address + !ERROR: REQUIRES directive with 'UNIFIED_SHARED_MEMORY' clause found lexically after device construct + !$omp requires unified_shared_memory +end subroutine g diff --git a/flang/test/Semantics/OpenMP/requires06.f90 b/flang/test/Semantics/OpenMP/requires06.f90 new file mode 100644 --- /dev/null +++ b/flang/test/Semantics/OpenMP/requires06.f90 @@ -0,0 +1,20 @@ +! RUN: %python %S/../test_errors.py %s %flang -fopenmp +! OpenMP Version 5.0 +! 2.4 Requires directive +! Target-related clauses in 'requires' directives must come strictly before any +! device constructs, such as declare target with extended list. + +subroutine f + !$omp declare target (f) +end subroutine f + +subroutine g + !ERROR: REQUIRES directive with 'DYNAMIC_ALLOCATORS' clause found lexically after device construct + !$omp requires dynamic_allocators + !ERROR: REQUIRES directive with 'REVERSE_OFFLOAD' clause found lexically after device construct + !$omp requires reverse_offload + !ERROR: REQUIRES directive with 'UNIFIED_ADDRESS' clause found lexically after device construct + !$omp requires unified_address + !ERROR: REQUIRES directive with 'UNIFIED_SHARED_MEMORY' clause found lexically after device construct + !$omp requires unified_shared_memory +end subroutine g diff --git a/flang/test/Semantics/OpenMP/requires07.f90 b/flang/test/Semantics/OpenMP/requires07.f90 new file mode 100644 --- /dev/null +++ b/flang/test/Semantics/OpenMP/requires07.f90 @@ -0,0 +1,21 @@ +! RUN: %python %S/../test_errors.py %s %flang -fopenmp +! OpenMP Version 5.0 +! 2.4 Requires directive +! Target-related clauses in 'requires' directives must come strictly before any +! device constructs, such as target parallel regions. + +subroutine f + !$omp target parallel + !$omp end target parallel +end subroutine f + +subroutine g + !ERROR: REQUIRES directive with 'DYNAMIC_ALLOCATORS' clause found lexically after device construct + !$omp requires dynamic_allocators + !ERROR: REQUIRES directive with 'REVERSE_OFFLOAD' clause found lexically after device construct + !$omp requires reverse_offload + !ERROR: REQUIRES directive with 'UNIFIED_ADDRESS' clause found lexically after device construct + !$omp requires unified_address + !ERROR: REQUIRES directive with 'UNIFIED_SHARED_MEMORY' clause found lexically after device construct + !$omp requires unified_shared_memory +end subroutine g diff --git a/flang/test/Semantics/OpenMP/requires08.f90 b/flang/test/Semantics/OpenMP/requires08.f90 new file mode 100644 --- /dev/null +++ b/flang/test/Semantics/OpenMP/requires08.f90 @@ -0,0 +1,23 @@ +! RUN: %python %S/../test_errors.py %s %flang -fopenmp +! OpenMP Version 5.0 +! 2.4 Requires directive +! Target-related clauses in 'requires' directives must come strictly before any +! device constructs, such as target teams distribute parallel do loops. + +subroutine f + !$omp target teams distribute parallel do + do i=1, 10 + end do + !$omp end target teams distribute parallel do +end subroutine f + +subroutine g + !ERROR: REQUIRES directive with 'DYNAMIC_ALLOCATORS' clause found lexically after device construct + !$omp requires dynamic_allocators + !ERROR: REQUIRES directive with 'REVERSE_OFFLOAD' clause found lexically after device construct + !$omp requires reverse_offload + !ERROR: REQUIRES directive with 'UNIFIED_ADDRESS' clause found lexically after device construct + !$omp requires unified_address + !ERROR: REQUIRES directive with 'UNIFIED_SHARED_MEMORY' clause found lexically after device construct + !$omp requires unified_shared_memory +end subroutine g