Index: flang/lib/Semantics/check-omp-structure.h =================================================================== --- flang/lib/Semantics/check-omp-structure.h +++ flang/lib/Semantics/check-omp-structure.h @@ -207,6 +207,14 @@ const parser::OmpObjectList &, const llvm::omp::Clause); void GetSymbolsInObjectList( const parser::OmpObjectList &, std::vector &); + void CheckWorkshareBlockStmts(const parser::Block &, parser::CharBlock); + void CheckAssignmentInWorkshare(const parser::AssignmentStmt &); + void CheckExprInWorkshare(const parser::Expr &); + void CheckWhereStmtInWorkshare(const parser::WhereStmt &); + void CheckWhereConstructInWorkshare(const parser::WhereConstruct &); + void CheckWhereBodyInWorkshare(const std::list &); + void CheckForallBodyInWorkshare( + const std::list &); }; } // 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 @@ -130,6 +130,10 @@ case llvm::omp::OMPD_parallel: CheckNoBranching(block, llvm::omp::OMPD_parallel, beginDir.source); break; + case llvm::omp::OMPD_workshare: + case llvm::omp::OMPD_parallel_workshare: + CheckWorkshareBlockStmts(block, beginDir.source); + break; default: break; } @@ -804,4 +808,244 @@ } } +void OmpStructureChecker::CheckWorkshareBlockStmts( + const parser::Block &block, parser::CharBlock source) { + for (auto it{block.begin()}; it != block.end(); ++it) { + if (const auto *assignment{parser::Unwrap(*it)}) { + CheckAssignmentInWorkshare(*assignment); + } else if (const auto *forallStmt{ + parser::Unwrap(*it)}) { + const auto &forallAssignment{ + std::get>( + forallStmt->t)}; + if (const auto *assignment{std::get_if( + &forallAssignment.statement.u)}) { + CheckAssignmentInWorkshare(*assignment); + } + } else if (const auto *forallConstruct{ + parser::Unwrap(*it)}) { + const auto &forallBody{ + std::get>(forallConstruct->t)}; + CheckForallBodyInWorkshare(forallBody); + } else if (const auto *whereStmt{parser::Unwrap(*it)}) { + CheckWhereStmtInWorkshare(*whereStmt); + } else if (const auto *whereConstruct{ + parser::Unwrap(*it)}) { + CheckWhereConstructInWorkshare(*whereConstruct); + } else if (const auto *ompConstruct{ + parser::Unwrap(*it)}) { + + // Check assignment statements in OpenMP Atomic construct + if (const auto *ompAtomicConstruct{ + std::get_if(&ompConstruct->u)}) { + std::visit( + common::visitors{ + [&](const parser::OmpAtomicCapture &x) { + const auto &stmt1{ + std::get(x.t)}; + const auto &stmt2{ + std::get(x.t)}; + CheckAssignmentInWorkshare(stmt1.v.statement); + CheckAssignmentInWorkshare(stmt2.v.statement); + }, + [&](const auto &x) { + const auto &assignmentStmt{ + std::get>(x.t)}; + CheckAssignmentInWorkshare(assignmentStmt.statement); + }, + }, + ompAtomicConstruct->u); + } else if (const auto *ompCriticalConstruct{ + std::get_if( + &ompConstruct->u)}) { + // All the restrictions on the Workshare construct apply to the + // statements in the enclosing critical constructs + const auto &criticalBlock{ + std::get(ompCriticalConstruct->t)}; + CheckWorkshareBlockStmts(criticalBlock, source); + } else { + + // Check if OpenMP constructs enclosed in the Workshare Construct are + // 'Parallel' Constructs + llvm::omp::Directive currentDir; + const OmpDirectiveSet parallelDirSet{ + llvm::omp::Directive::OMPD_parallel, + llvm::omp::Directive::OMPD_parallel_do, + llvm::omp::Directive::OMPD_parallel_sections, + llvm::omp::Directive::OMPD_parallel_workshare, + llvm::omp::Directive::OMPD_parallel_do_simd}; + + if (const auto *ompBlockConstruct{ + std::get_if(&ompConstruct->u)}) { + const auto &beginBlockDir{ + std::get(ompBlockConstruct->t)}; + const auto &beginDir{ + std::get(beginBlockDir.t)}; + currentDir = beginDir.v; + } else if (const auto *ompLoopConstruct{ + std::get_if( + &ompConstruct->u)}) { + const auto &beginLoopDir{ + std::get(ompLoopConstruct->t)}; + const auto &beginDir{ + std::get(beginLoopDir.t)}; + currentDir = beginDir.v; + } else if (const auto *ompSectionsConstruct{ + std::get_if( + &ompConstruct->u)}) { + const auto &beginSectionsDir{ + std::get( + ompSectionsConstruct->t)}; + const auto &beginDir{ + std::get(beginSectionsDir.t)}; + currentDir = beginDir.v; + } + + if (!parallelDirSet.test(currentDir)) { + context_.Say(source, + "OpenMP constructs enclosed in WORKSHARE construct may consist " + "of ATOMIC, CRITICAL or PARALLEL constructs only"_err_en_US); + } + } + } else { + context_.Say(source, + "The structured block in a WORKSHARE construct may consist of only " + "scalar or array assignments, forall or where statements, " + "forall, where, atomic, critical or parallel constructs"_err_en_US); + } + } +} + +void OmpStructureChecker::CheckAssignmentInWorkshare( + const parser::AssignmentStmt &assignment) { + const auto &var{std::get(assignment.t)}; + const auto &expr{std::get(assignment.t)}; + const auto *lhs{GetExpr(var)}; + const auto *rhs{GetExpr(expr)}; + Tristate isDefined{semantics::IsDefinedAssignment( + lhs->GetType(), lhs->Rank(), rhs->GetType(), rhs->Rank())}; + if (isDefined == Tristate::Yes) { + context_.Say(expr.source, + "Defined assignment statement is not " + "allowed in a WORKSHARE construct"_err_en_US); + } + CheckExprInWorkshare(expr); +} + +void OmpStructureChecker::CheckExprInWorkshare(const parser::Expr &expr) { + if (const auto *e{GetExpr(expr)}) { + for (const Symbol &symbol : evaluate::CollectSymbols(*e)) { + if (const Symbol * root{GetAssociationRoot(symbol)}) { + if (IsFunction(*root) && + !(root->attrs().test(Attr::ELEMENTAL) || + root->attrs().test(Attr::INTRINSIC))) { + context_.Say(expr.source, + "User defined non-ELEMENTAL function " + "'%s' is not allowed in a WORKSHARE construct"_err_en_US, + root->name()); + } + } + } + } +} + +void OmpStructureChecker::CheckWhereStmtInWorkshare( + const parser::WhereStmt &whereStmt) { + const auto &logicalExpr{std::get(whereStmt.t)}; + const auto &assignment{std::get(whereStmt.t)}; + CheckExprInWorkshare(logicalExpr.thing.value()); + CheckAssignmentInWorkshare(assignment); +} + +void OmpStructureChecker::CheckWhereConstructInWorkshare( + const parser::WhereConstruct &whereConstruct) { + const auto &whereConstructStmt{ + std::get>( + whereConstruct.t)}; + const auto &constructStmtExpr{ + std::get(whereConstructStmt.statement.t)}; + CheckExprInWorkshare(constructStmtExpr.thing.value()); + + const auto &whereBody( + std::get>(whereConstruct.t)); + CheckWhereBodyInWorkshare(whereBody); + + const auto &maskedElsewhereList( + std::get>( + whereConstruct.t)); + for (const auto &maskedElsewhere : maskedElsewhereList) { + const auto &maskedElsewhereStmt{ + std::get>( + maskedElsewhere.t)}; + const auto &maskedElsewhereExpr{ + std::get(maskedElsewhereStmt.statement.t)}; + const auto &maskedElsewhereBody( + std::get>(maskedElsewhere.t)); + CheckExprInWorkshare(maskedElsewhereExpr.thing.value()); + CheckWhereBodyInWorkshare(maskedElsewhereBody); + } + + if (const auto &elsewhere{ + std::get>( + whereConstruct.t)}) { + const auto &elsewhereBody( + std::get>(elsewhere.value().t)); + CheckWhereBodyInWorkshare(elsewhereBody); + } +} + +void OmpStructureChecker::CheckWhereBodyInWorkshare( + const std::list &whereBodyList) { + for (const auto &whereBody : whereBodyList) { + std::visit(common::visitors{ + [&](const parser::Statement &x) { + CheckAssignmentInWorkshare(x.statement); + }, + [&](const parser::Statement &x) { + CheckWhereStmtInWorkshare(x.statement); + }, + [&](const common::Indirection &x) { + CheckWhereConstructInWorkshare(x.value()); + }, + }, + whereBody.u); + } +} + +void OmpStructureChecker::CheckForallBodyInWorkshare( + const std::list &forallBodyList) { + for (const auto &forallBody : forallBodyList) { + std::visit( + common::visitors{ + [&](const parser::Statement &x) { + if (const auto *assignment{ + std::get_if(&x.statement.u)}) + CheckAssignmentInWorkshare(*assignment); + }, + [&](const parser::Statement &x) { + CheckWhereStmtInWorkshare(x.statement); + }, + [&](const parser::WhereConstruct &x) { + CheckWhereConstructInWorkshare(x); + }, + [&](const common::Indirection &x) { + const auto &nestedForallBody{ + std::get>( + x.value().t)}; + CheckForallBodyInWorkshare(nestedForallBody); + }, + [&](const parser::Statement &x) { + const auto &forallAssignment{std::get< + parser::UnlabeledStatement>( + x.statement.t)}; + if (const auto *assignment{std::get_if( + &forallAssignment.statement.u)}) { + CheckAssignmentInWorkshare(*assignment); + } + }, + }, + forallBody.u); + } +} + } // namespace Fortran::semantics Index: flang/test/Semantics/omp-workshare01.f90 =================================================================== --- flang/test/Semantics/omp-workshare01.f90 +++ flang/test/Semantics/omp-workshare01.f90 @@ -1,6 +1,4 @@ ! RUN: %S/test_errors.sh %s %t %f18 -fopenmp -! XFAIL: * - ! OpenMP Version 4.5 ! 2.7.4 workshare Construct ! Invalid do construct inside !$omp workshare @@ -9,14 +7,25 @@ integer n, i real aa(n,n), bb(n,n), cc(n,n), dd(n,n), ee(n,n), ff(n,n) + !ERROR: The structured block in a WORKSHARE construct may consist of only scalar or array assignments, forall or where statements, forall, where, atomic, critical or parallel constructs + !ERROR: OpenMP constructs enclosed in WORKSHARE construct may consist of ATOMIC, CRITICAL or PARALLEL constructs only !$omp workshare - !ERROR: Unexpected do stmt inside !$omp workshare do i = 1, n print *, "omp workshare" end do + !$omp critical + !$omp single aa = bb + !$omp end single + !$omp end critical + + !$omp parallel + !$omp single cc = dd + !$omp end single + !$omp end parallel + ee = ff !$omp end workshare Index: flang/test/Semantics/omp-workshare02.f90 =================================================================== --- flang/test/Semantics/omp-workshare02.f90 +++ flang/test/Semantics/omp-workshare02.f90 @@ -1,6 +1,4 @@ ! RUN: %S/test_errors.sh %s %t %f18 -fopenmp -! XFAIL: * - ! OpenMP Version 4.5 ! 2.7.4 workshare Construct ! The !omp workshare construct must not contain any user defined @@ -8,22 +6,57 @@ module my_mod contains - function my_func(n) - integer :: my_func(n, n) + integer function my_func() my_func = 10 end function my_func end module my_mod subroutine workshare(aa, bb, cc, dd, ee, ff, n) use my_mod - integer n, i - real aa(n,n), bb(n,n), cc(n,n), dd(n,n), ee(n,n), ff(n,n) + integer n, i, j + real aa(n), bb(n), cc(n), dd(n), ee(n), ff(n) !$omp workshare - !ERROR: Non-ELEMENTAL function is not allowed in !$omp workshare construct - aa = my_func(n) + !ERROR: User defined non-ELEMENTAL function 'my_func' is not allowed in a WORKSHARE construct + aa = my_func() cc = dd ee = ff + + !ERROR: User defined non-ELEMENTAL function 'my_func' is not allowed in a WORKSHARE construct + where (aa .ne. my_func()) aa = bb * cc + !ERROR: User defined non-ELEMENTAL function 'my_func' is not allowed in a WORKSHARE construct + where (dd .lt. 5) dd = aa * my_func() + + !ERROR: User defined non-ELEMENTAL function 'my_func' is not allowed in a WORKSHARE construct + where (aa .ge. my_func()) + !ERROR: User defined non-ELEMENTAL function 'my_func' is not allowed in a WORKSHARE construct + cc = aa + my_func() + !ERROR: User defined non-ELEMENTAL function 'my_func' is not allowed in a WORKSHARE construct + elsewhere (aa .le. my_func()) + !ERROR: User defined non-ELEMENTAL function 'my_func' is not allowed in a WORKSHARE construct + cc = dd + my_func() + elsewhere + !ERROR: User defined non-ELEMENTAL function 'my_func' is not allowed in a WORKSHARE construct + cc = ee + my_func() + end where + + forall (j = 1:10) + aa(j) = aa(j) + bb(j) + + !ERROR: User defined non-ELEMENTAL function 'my_func' is not allowed in a WORKSHARE construct + where (cc .le. j) cc = cc + my_func() + end forall + + !$omp atomic update + !ERROR: User defined non-ELEMENTAL function 'my_func' is not allowed in a WORKSHARE construct + j = j + my_func() + + !$omp atomic capture + i = j + !ERROR: User defined non-ELEMENTAL function 'my_func' is not allowed in a WORKSHARE construct + j = j - my_func() + !$omp end atomic + !$omp end workshare end subroutine workshare Index: flang/test/Semantics/omp-workshare03.f90 =================================================================== --- /dev/null +++ flang/test/Semantics/omp-workshare03.f90 @@ -0,0 +1,32 @@ +! RUN: %S/test_errors.sh %s %t %f18 -fopenmp +! OpenMP Version 4.5 +! 2.7.4 workshare Construct +! All array assignments, scalar assignments, and masked array assignments +! must be intrinsic assignments. + +module defined_assign + interface assignment(=) + module procedure work_assign + end interface + + contains + subroutine work_assign(a,b) + integer , intent(out) :: a + logical , intent(in) :: b(:) + end subroutine work_assign +end module defined_assign + +program omp_workshare + use defined_assign + + integer:: a, aa(10), bb(10) + logical l(10) + l = .TRUE. + + !$omp workshare + !ERROR: Defined assignment statement is not allowed in a WORKSHARE construct + a = l + aa = bb + !$omp end workshare + +end program omp_workshare Index: flang/test/Semantics/omp-workshare04.f90 =================================================================== --- /dev/null +++ flang/test/Semantics/omp-workshare04.f90 @@ -0,0 +1,48 @@ +! RUN: %S/test_errors.sh %s %t %f18 -fopenmp +! OpenMP Version 4.5 +! 2.7.4 workshare Construct +! Checks for OpenMP Workshare construct + +subroutine omp_workshare(aa, bb, cc, dd, ee, ff, n) + integer i, j, n, a(10), b(10) + integer, pointer :: p + integer, target :: t + real aa(n,n), bb(n,n), cc(n,n), dd(n,n), ee(n,n), ff(n,n) + + !ERROR: The structured block in a WORKSHARE construct may consist of only scalar or array assignments, forall or where statements, forall, where, atomic, critical or parallel constructs + !$omp workshare + p => t + + !$omp parallel + cc = dd + !$omp end parallel + + !ERROR: OpenMP constructs enclosed in WORKSHARE construct may consist of ATOMIC, CRITICAL or PARALLEL constructs only + !$omp parallel workshare + !$omp single + ee = ff + !$omp end single + !$omp end parallel workshare + + where (aa .ne. 0) cc = bb / aa + + where (b .lt. 2) b = sum(a) + + where (aa .ge. 2.0) + cc = aa + bb + elsewhere + cc = dd + ee + end where + + forall (i = 1:10, n > i) a(i) = b(i) + + forall (j = 1:10) + a(j) = a(j) + b(j) + end forall + + !$omp atomic update + j = j + sum(a) + + !$omp end workshare + +end subroutine omp_workshare