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 @@ -225,6 +225,8 @@ void CheckCycleConstraints(const parser::OpenMPLoopConstruct &x); void CheckDistLinear(const parser::OpenMPLoopConstruct &x); void CheckSIMDNest(const parser::OpenMPConstruct &x); + void CheckCancellationNest( + const parser::CharBlock &source, const parser::OmpCancelType::Type &type); std::int64_t GetOrdCollapseLevel(const parser::OpenMPLoopConstruct &x); void CheckIfDoOrderedClause(const parser::OmpBlockDirective &blkDirectiv); bool CheckReductionOperators(const parser::OmpClause::Reduction &); 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 @@ -846,7 +846,9 @@ void OmpStructureChecker::Enter(const parser::OpenMPCancelConstruct &x) { const auto &dir{std::get(x.t)}; + const auto &type{std::get(x.t)}; PushContextAndClauseSets(dir.source, llvm::omp::Directive::OMPD_cancel); + CheckCancellationNest(dir.source, type.v); } void OmpStructureChecker::Leave(const parser::OpenMPCancelConstruct &) { @@ -867,8 +869,10 @@ void OmpStructureChecker::Enter( const parser::OpenMPCancellationPointConstruct &x) { const auto &dir{std::get(x.t)}; + const auto &type{std::get(x.t)}; PushContextAndClauseSets( dir.source, llvm::omp::Directive::OMPD_cancellation_point); + CheckCancellationNest(dir.source, type.v); } void OmpStructureChecker::Leave( @@ -876,6 +880,133 @@ dirContext_.pop_back(); } +void OmpStructureChecker::CheckCancellationNest( + const parser::CharBlock &source, const parser::OmpCancelType::Type &type) { + if (CurrentDirectiveIsNested()) { + // If construct-type-clause is taskgroup, the cancellation construct must be + // closely nested inside a task or a taskloop construct and the cancellation + // region must be closely nested inside a taskgroup region. If + // construct-type-clause is sections, the cancellation construct must be + // closely nested inside a sections or section construct. Otherwise, the + // cancellation construct must be closely nested inside an OpenMP construct + // that matches the type specified in construct-type-clause of the + // cancellation construct. + + OmpDirectiveSet allowedTaskgroupSet{ + llvm::omp::Directive::OMPD_task, llvm::omp::Directive::OMPD_taskloop}; + OmpDirectiveSet allowedSectionsSet{llvm::omp::Directive::OMPD_sections, + llvm::omp::Directive::OMPD_parallel_sections}; + OmpDirectiveSet allowedDoSet{llvm::omp::Directive::OMPD_do, + llvm::omp::Directive::OMPD_distribute_parallel_do, + llvm::omp::Directive::OMPD_parallel_do, + llvm::omp::Directive::OMPD_target_parallel_do, + llvm::omp::Directive::OMPD_target_teams_distribute_parallel_do, + llvm::omp::Directive::OMPD_teams_distribute_parallel_do}; + OmpDirectiveSet allowedParallelSet{llvm::omp::Directive::OMPD_parallel, + llvm::omp::Directive::OMPD_target_parallel}; + + bool eligibleCancellation{false}; + switch (type) { + case parser::OmpCancelType::Type::Taskgroup: + if (allowedTaskgroupSet.test(GetContextParent().directive)) { + eligibleCancellation = true; + if (dirContext_.size() >= 3) { + // Check if the cancellation region is closely nested inside a + // taskgroup region when there are more than two levels of directives + // in the directive context stack. + if (GetContextParent().directive == llvm::omp::Directive::OMPD_task || + FindClauseParent(llvm::omp::Clause::OMPC_nogroup)) { + for (int i = dirContext_.size() - 3; i >= 0; i--) { + if (dirContext_[i].directive == + llvm::omp::Directive::OMPD_taskgroup) { + break; + } + if (allowedParallelSet.test(dirContext_[i].directive)) { + eligibleCancellation = false; + break; + } + } + } + } + } + if (!eligibleCancellation) { + context_.Say(source, + "With %s clause, %s construct must be closely nested inside TASK " + "or TASKLOOP construct and %s region must be closely nested inside " + "TASKGROUP region"_err_en_US, + parser::ToUpperCaseLetters( + parser::OmpCancelType::EnumToString(type)), + ContextDirectiveAsFortran(), ContextDirectiveAsFortran()); + } + return; + case parser::OmpCancelType::Type::Sections: + if (allowedSectionsSet.test(GetContextParent().directive)) { + eligibleCancellation = true; + } + break; + case Fortran::parser::OmpCancelType::Type::Do: + if (allowedDoSet.test(GetContextParent().directive)) { + eligibleCancellation = true; + } + break; + case parser::OmpCancelType::Type::Parallel: + if (allowedParallelSet.test(GetContextParent().directive)) { + eligibleCancellation = true; + } + break; + default: + break; + } + if (!eligibleCancellation) { + context_.Say(source, + "With %s clause, %s construct cannot be closely nested inside %s " + "construct"_err_en_US, + parser::ToUpperCaseLetters(parser::OmpCancelType::EnumToString(type)), + ContextDirectiveAsFortran(), + parser::ToUpperCaseLetters( + getDirectiveName(GetContextParent().directive).str())); + } + } else { + // The cancellation directive cannot be orphaned. + switch (type) { + case parser::OmpCancelType::Type::Taskgroup: + context_.Say(source, + "%s %s directive is not closely nested inside " + "TASK or TASKLOOP"_err_en_US, + ContextDirectiveAsFortran(), + parser::ToUpperCaseLetters( + parser::OmpCancelType::EnumToString(type))); + break; + case parser::OmpCancelType::Type::Sections: + context_.Say(source, + "%s %s directive is not closely nested inside " + "SECTION or SECTIONS"_err_en_US, + ContextDirectiveAsFortran(), + parser::ToUpperCaseLetters( + parser::OmpCancelType::EnumToString(type))); + break; + case Fortran::parser::OmpCancelType::Type::Do: + context_.Say(source, + "%s %s directive is not closely nested inside " + "the construct that matches the DO clause type"_err_en_US, + ContextDirectiveAsFortran(), + parser::ToUpperCaseLetters( + parser::OmpCancelType::EnumToString(type))); + break; + case parser::OmpCancelType::Type::Parallel: + context_.Say(source, + "%s %s directive is not closely nested inside " + "the construct that matches the PARALLEL clause type"_err_en_US, + ContextDirectiveAsFortran(), + parser::ToUpperCaseLetters( + parser::OmpCancelType::EnumToString(type))); + break; + default: + break; + } + } +} + void OmpStructureChecker::Enter(const parser::OmpEndBlockDirective &x) { const auto &dir{std::get(x.t)}; ResetPartialContext(dir.source); diff --git a/flang/test/Semantics/omp-clause-validity01.f90 b/flang/test/Semantics/omp-clause-validity01.f90 --- a/flang/test/Semantics/omp-clause-validity01.f90 +++ b/flang/test/Semantics/omp-clause-validity01.f90 @@ -494,9 +494,6 @@ !ERROR: RELAXED clause is not allowed on the FLUSH directive !$omp flush relaxed - !$omp cancel DO - !$omp cancellation point parallel - ! 2.13.2 critical Construct ! !$omp critical (first) diff --git a/flang/test/Semantics/omp-nested-cancel.f90 b/flang/test/Semantics/omp-nested-cancel.f90 new file mode 100644 --- /dev/null +++ b/flang/test/Semantics/omp-nested-cancel.f90 @@ -0,0 +1,250 @@ +! RUN: %S/test_errors.sh %s %t %flang -fopenmp +! REQUIRES: shell + +! OpenMP Version 5.0 +! Check OpenMP construct validity for the following directives: +! 2.18.1 Cancel Construct + +program main + integer :: i, N = 10 + real :: a + + !ERROR: CANCEL TASKGROUP directive is not closely nested inside TASK or TASKLOOP + !$omp cancel taskgroup + + !ERROR: CANCEL SECTIONS directive is not closely nested inside SECTION or SECTIONS + !$omp cancel sections + + !ERROR: CANCEL DO directive is not closely nested inside the construct that matches the DO clause type + !$omp cancel do + + !ERROR: CANCEL PARALLEL directive is not closely nested inside the construct that matches the PARALLEL clause type + !$omp cancel parallel + + !$omp parallel + !$omp sections + !$omp cancel sections + !$omp section + a = 3.14 + !$omp end sections + !$omp end parallel + + !$omp sections + !$omp section + !$omp cancel sections + a = 3.14 + !$omp end sections + + !$omp parallel + !ERROR: With SECTIONS clause, CANCEL construct cannot be closely nested inside PARALLEL construct + !$omp cancel sections + a = 3.14 + !$omp end parallel + + !$omp parallel sections + !$omp cancel sections + a = 3.14 + !$omp end parallel sections + + !$omp do + do i = 1, N + a = 3.14 + !$omp cancel do + end do + !$omp end do + + !$omp parallel do + do i = 1, N + a = 3.14 + !$omp cancel do + end do + !$omp end parallel do + + !$omp target + !$omp teams + !$omp distribute parallel do + do i = 1, N + a = 3.14 + !$omp cancel do + end do + !$omp end distribute parallel do + !$omp end teams + !$omp end target + + !$omp target + !$omp teams distribute parallel do + do i = 1, N + a = 3.14 + !$omp cancel do + end do + !$omp end teams distribute parallel do + !$omp end target + + !$omp target teams distribute parallel do + do i = 1, N + a = 3.14 + !$omp cancel do + end do + !$omp end target teams distribute parallel do + + !$omp target parallel do + do i = 1, N + a = 3.14 + !$omp cancel do + end do + !$omp end target parallel do + + !$omp parallel + do i = 1, N + a = 3.14 + !ERROR: With DO clause, CANCEL construct cannot be closely nested inside PARALLEL construct + !$omp cancel do + end do + !$omp end parallel + + !$omp parallel + do i = 1, N + a = 3.14 + !$omp cancel parallel + end do + !$omp end parallel + + !$omp target parallel + do i = 1, N + a = 3.14 + !$omp cancel parallel + end do + !$omp end target parallel + + !$omp target parallel do + do i = 1, N + a = 3.14 + !ERROR: With PARALLEL clause, CANCEL construct cannot be closely nested inside TARGET PARALLEL DO construct + !$omp cancel parallel + end do + !$omp end target parallel do + + !$omp do + do i = 1, N + a = 3.14 + !ERROR: With PARALLEL clause, CANCEL construct cannot be closely nested inside DO construct + !$omp cancel parallel + end do + !$omp end do + +contains + subroutine sub1() + !$omp task + !$omp cancel taskgroup + a = 3.14 + !$omp end task + + !$omp taskloop + do i = 1, N + !$omp parallel + !$omp end parallel + !$omp cancel taskgroup + a = 3.14 + end do + !$omp end taskloop + + !$omp taskloop nogroup + do i = 1, N + !$omp cancel taskgroup + a = 3.14 + end do + + !$omp parallel + !ERROR: With TASKGROUP clause, CANCEL construct must be closely nested inside TASK or TASKLOOP construct and CANCEL region must be closely nested inside TASKGROUP region + !$omp cancel taskgroup + a = 3.14 + !$omp end parallel + + !$omp do + do i = 1, N + !$omp task + !$omp cancel taskgroup + a = 3.14 + !$omp end task + end do + !$omp end do + + !$omp parallel + !$omp taskgroup + !$omp task + !$omp cancel taskgroup + a = 3.14 + !$omp end task + !$omp end taskgroup + !$omp end parallel + + !$omp parallel + !$omp task + !ERROR: With TASKGROUP clause, CANCEL construct must be closely nested inside TASK or TASKLOOP construct and CANCEL region must be closely nested inside TASKGROUP region + !$omp cancel taskgroup + a = 3.14 + !$omp end task + !$omp end parallel + + !$omp parallel + !$omp do + do i = 1, N + !$omp task + !ERROR: With TASKGROUP clause, CANCEL construct must be closely nested inside TASK or TASKLOOP construct and CANCEL region must be closely nested inside TASKGROUP region + !$omp cancel taskgroup + a = 3.14 + !$omp end task + end do + !$omp end do + !$omp end parallel + + !$omp target parallel + !$omp task + !ERROR: With TASKGROUP clause, CANCEL construct must be closely nested inside TASK or TASKLOOP construct and CANCEL region must be closely nested inside TASKGROUP region + !$omp cancel taskgroup + a = 3.14 + !$omp end task + !$omp end target parallel + + !$omp parallel + !$omp taskloop private(j) nogroup + do i = 1, N + !ERROR: With TASKGROUP clause, CANCEL construct must be closely nested inside TASK or TASKLOOP construct and CANCEL region must be closely nested inside TASKGROUP region + !$omp cancel taskgroup + a = 3.14 + end do + !$omp end taskloop + !$omp end parallel + + !$omp parallel + !$omp taskloop + do i = 1, N + !$omp cancel taskgroup + a = 3.14 + end do + !$omp end taskloop + !$omp end parallel + + !$omp parallel + !$omp taskgroup + !$omp taskloop nogroup + do i = 1, N + !$omp cancel taskgroup + a = 3.14 + end do + !$omp end taskloop + !$omp end taskgroup + !$omp end parallel + + !$omp target parallel + !$omp taskloop nogroup + do i = 1, N + !ERROR: With TASKGROUP clause, CANCEL construct must be closely nested inside TASK or TASKLOOP construct and CANCEL region must be closely nested inside TASKGROUP region + !$omp cancel taskgroup + a = 3.14 + end do + !$omp end taskloop + !$omp end target parallel + end subroutine sub1 + +end program main diff --git a/flang/test/Semantics/omp-nested-cancellation-point.f90 b/flang/test/Semantics/omp-nested-cancellation-point.f90 new file mode 100644 --- /dev/null +++ b/flang/test/Semantics/omp-nested-cancellation-point.f90 @@ -0,0 +1,250 @@ +! RUN: %S/test_errors.sh %s %t %flang -fopenmp +! REQUIRES: shell + +! OpenMP Version 5.0 +! Check OpenMP construct validity for the following directives: +! 2.18.2 Cancellation Point Construct + +program main + integer :: i, N = 10 + real :: a + + !ERROR: CANCELLATION POINT TASKGROUP directive is not closely nested inside TASK or TASKLOOP + !$omp cancellation point taskgroup + + !ERROR: CANCELLATION POINT SECTIONS directive is not closely nested inside SECTION or SECTIONS + !$omp cancellation point sections + + !ERROR: CANCELLATION POINT DO directive is not closely nested inside the construct that matches the DO clause type + !$omp cancellation point do + + !ERROR: CANCELLATION POINT PARALLEL directive is not closely nested inside the construct that matches the PARALLEL clause type + !$omp cancellation point parallel + + !$omp parallel + !$omp sections + !$omp cancellation point sections + !$omp section + a = 3.14 + !$omp end sections + !$omp end parallel + + !$omp sections + !$omp section + !$omp cancellation point sections + a = 3.14 + !$omp end sections + + !$omp parallel + !ERROR: With SECTIONS clause, CANCELLATION POINT construct cannot be closely nested inside PARALLEL construct + !$omp cancellation point sections + a = 3.14 + !$omp end parallel + + !$omp parallel sections + !$omp cancellation point sections + a = 3.14 + !$omp end parallel sections + + !$omp do + do i = 1, N + a = 3.14 + !$omp cancellation point do + end do + !$omp end do + + !$omp parallel do + do i = 1, N + a = 3.14 + !$omp cancellation point do + end do + !$omp end parallel do + + !$omp target + !$omp teams + !$omp distribute parallel do + do i = 1, N + a = 3.14 + !$omp cancellation point do + end do + !$omp end distribute parallel do + !$omp end teams + !$omp end target + + !$omp target + !$omp teams distribute parallel do + do i = 1, N + a = 3.14 + !$omp cancellation point do + end do + !$omp end teams distribute parallel do + !$omp end target + + !$omp target teams distribute parallel do + do i = 1, N + a = 3.14 + !$omp cancellation point do + end do + !$omp end target teams distribute parallel do + + !$omp target parallel do + do i = 1, N + a = 3.14 + !$omp cancellation point do + end do + !$omp end target parallel do + + !$omp parallel + do i = 1, N + a = 3.14 + !ERROR: With DO clause, CANCELLATION POINT construct cannot be closely nested inside PARALLEL construct + !$omp cancellation point do + end do + !$omp end parallel + + !$omp parallel + do i = 1, N + a = 3.14 + !$omp cancellation point parallel + end do + !$omp end parallel + + !$omp target parallel + do i = 1, N + a = 3.14 + !$omp cancellation point parallel + end do + !$omp end target parallel + + !$omp target parallel do + do i = 1, N + a = 3.14 + !ERROR: With PARALLEL clause, CANCELLATION POINT construct cannot be closely nested inside TARGET PARALLEL DO construct + !$omp cancellation point parallel + end do + !$omp end target parallel do + + !$omp do + do i = 1, N + a = 3.14 + !ERROR: With PARALLEL clause, CANCELLATION POINT construct cannot be closely nested inside DO construct + !$omp cancellation point parallel + end do + !$omp end do + +contains + subroutine sub1() + !$omp task + !$omp cancellation point taskgroup + a = 3.14 + !$omp end task + + !$omp taskloop + do i = 1, N + !$omp parallel + !$omp end parallel + !$omp cancellation point taskgroup + a = 3.14 + end do + !$omp end taskloop + + !$omp taskloop nogroup + do i = 1, N + !$omp cancellation point taskgroup + a = 3.14 + end do + + !$omp parallel + !ERROR: With TASKGROUP clause, CANCELLATION POINT construct must be closely nested inside TASK or TASKLOOP construct and CANCELLATION POINT region must be closely nested inside TASKGROUP region + !$omp cancellation point taskgroup + a = 3.14 + !$omp end parallel + + !$omp do + do i = 1, N + !$omp task + !$omp cancellation point taskgroup + a = 3.14 + !$omp end task + end do + !$omp end do + + !$omp parallel + !$omp taskgroup + !$omp task + !$omp cancellation point taskgroup + a = 3.14 + !$omp end task + !$omp end taskgroup + !$omp end parallel + + !$omp parallel + !$omp task + !ERROR: With TASKGROUP clause, CANCELLATION POINT construct must be closely nested inside TASK or TASKLOOP construct and CANCELLATION POINT region must be closely nested inside TASKGROUP region + !$omp cancellation point taskgroup + a = 3.14 + !$omp end task + !$omp end parallel + + !$omp parallel + !$omp do + do i = 1, N + !$omp task + !ERROR: With TASKGROUP clause, CANCELLATION POINT construct must be closely nested inside TASK or TASKLOOP construct and CANCELLATION POINT region must be closely nested inside TASKGROUP region + !$omp cancellation point taskgroup + a = 3.14 + !$omp end task + end do + !$omp end do + !$omp end parallel + + !$omp target parallel + !$omp task + !ERROR: With TASKGROUP clause, CANCELLATION POINT construct must be closely nested inside TASK or TASKLOOP construct and CANCELLATION POINT region must be closely nested inside TASKGROUP region + !$omp cancellation point taskgroup + a = 3.14 + !$omp end task + !$omp end target parallel + + !$omp parallel + !$omp taskloop private(j) nogroup + do i = 1, N + !ERROR: With TASKGROUP clause, CANCELLATION POINT construct must be closely nested inside TASK or TASKLOOP construct and CANCELLATION POINT region must be closely nested inside TASKGROUP region + !$omp cancellation point taskgroup + a = 3.14 + end do + !$omp end taskloop + !$omp end parallel + + !$omp parallel + !$omp taskloop + do i = 1, N + !$omp cancellation point taskgroup + a = 3.14 + end do + !$omp end taskloop + !$omp end parallel + + !$omp parallel + !$omp taskgroup + !$omp taskloop nogroup + do i = 1, N + !$omp cancellation point taskgroup + a = 3.14 + end do + !$omp end taskloop + !$omp end taskgroup + !$omp end parallel + + !$omp target parallel + !$omp taskloop nogroup + do i = 1, N + !ERROR: With TASKGROUP clause, CANCELLATION POINT construct must be closely nested inside TASK or TASKLOOP construct and CANCELLATION POINT region must be closely nested inside TASKGROUP region + !$omp cancellation point taskgroup + a = 3.14 + end do + !$omp end taskloop + !$omp end target parallel + end subroutine sub1 + +end program main