|
37 | 37 |
|
38 | 38 | #include "llvm/ADT/ArrayRef.h" |
39 | 39 | #include "llvm/ADT/STLExtras.h" |
| 40 | +#include "llvm/ADT/StringExtras.h" |
40 | 41 | #include "llvm/ADT/StringRef.h" |
41 | 42 | #include "llvm/Frontend/OpenMP/OMP.h" |
42 | 43 |
|
@@ -141,6 +142,64 @@ class OmpWorkshareBlockChecker { |
141 | 142 | parser::CharBlock source_; |
142 | 143 | }; |
143 | 144 |
|
| 145 | +// 'OmpWorkdistributeBlockChecker' is used to check the validity of the |
| 146 | +// assignment statements and the expressions enclosed in an OpenMP |
| 147 | +// WORKDISTRIBUTE construct |
| 148 | +class OmpWorkdistributeBlockChecker { |
| 149 | +public: |
| 150 | + OmpWorkdistributeBlockChecker( |
| 151 | + SemanticsContext &context, parser::CharBlock source) |
| 152 | + : context_{context}, source_{source} {} |
| 153 | + |
| 154 | + template <typename T> bool Pre(const T &) { return true; } |
| 155 | + template <typename T> void Post(const T &) {} |
| 156 | + |
| 157 | + bool Pre(const parser::AssignmentStmt &assignment) { |
| 158 | + const auto &var{std::get<parser::Variable>(assignment.t)}; |
| 159 | + const auto &expr{std::get<parser::Expr>(assignment.t)}; |
| 160 | + const auto *lhs{GetExpr(context_, var)}; |
| 161 | + const auto *rhs{GetExpr(context_, expr)}; |
| 162 | + if (lhs && rhs) { |
| 163 | + Tristate isDefined{semantics::IsDefinedAssignment( |
| 164 | + lhs->GetType(), lhs->Rank(), rhs->GetType(), rhs->Rank())}; |
| 165 | + if (isDefined == Tristate::Yes) { |
| 166 | + context_.Say(expr.source, |
| 167 | + "Defined assignment statement is not allowed in a WORKDISTRIBUTE construct"_err_en_US); |
| 168 | + } |
| 169 | + } |
| 170 | + return true; |
| 171 | + } |
| 172 | + |
| 173 | + bool Pre(const parser::Expr &expr) { |
| 174 | + if (const auto *e{GetExpr(context_, expr)}) { |
| 175 | + if (!e) |
| 176 | + return false; |
| 177 | + for (const Symbol &symbol : evaluate::CollectSymbols(*e)) { |
| 178 | + const Symbol &root{GetAssociationRoot(symbol)}; |
| 179 | + if (IsFunction(root)) { |
| 180 | + std::vector<std::string> attrs; |
| 181 | + if (!IsElementalProcedure(root)) { |
| 182 | + attrs.push_back("non-ELEMENTAL"); |
| 183 | + } |
| 184 | + if (root.attrs().test(Attr::IMPURE)) { |
| 185 | + attrs.push_back("IMPURE"); |
| 186 | + } |
| 187 | + std::string attrsStr = |
| 188 | + attrs.empty() ? "" : " " + llvm::join(attrs, ", "); |
| 189 | + context_.Say(expr.source, |
| 190 | + "User defined%s function '%s' is not allowed in a WORKDISTRIBUTE construct"_err_en_US, |
| 191 | + attrsStr, root.name()); |
| 192 | + } |
| 193 | + } |
| 194 | + } |
| 195 | + return false; |
| 196 | + } |
| 197 | + |
| 198 | +private: |
| 199 | + SemanticsContext &context_; |
| 200 | + parser::CharBlock source_; |
| 201 | +}; |
| 202 | + |
144 | 203 | // `OmpUnitedTaskDesignatorChecker` is used to check if the designator |
145 | 204 | // can appear within the TASK construct |
146 | 205 | class OmpUnitedTaskDesignatorChecker { |
@@ -819,6 +878,12 @@ void OmpStructureChecker::Enter(const parser::OpenMPBlockConstruct &x) { |
819 | 878 | "TARGET construct with nested TEAMS region contains statements or " |
820 | 879 | "directives outside of the TEAMS construct"_err_en_US); |
821 | 880 | } |
| 881 | + if (GetContext().directive == llvm::omp::Directive::OMPD_workdistribute && |
| 882 | + GetContextParent().directive != llvm::omp::Directive::OMPD_teams) { |
| 883 | + context_.Say(parser::FindSourceLocation(x), |
| 884 | + "%s region can only be strictly nested within TEAMS region"_err_en_US, |
| 885 | + ContextDirectiveAsFortran()); |
| 886 | + } |
822 | 887 | } |
823 | 888 |
|
824 | 889 | CheckNoBranching(block, beginDir.v, beginDir.source); |
@@ -900,6 +965,17 @@ void OmpStructureChecker::Enter(const parser::OpenMPBlockConstruct &x) { |
900 | 965 | HasInvalidWorksharingNesting( |
901 | 966 | beginDir.source, llvm::omp::nestedWorkshareErrSet); |
902 | 967 | break; |
| 968 | + case llvm::omp::OMPD_workdistribute: |
| 969 | + if (!CurrentDirectiveIsNested()) { |
| 970 | + context_.Say(beginDir.source, |
| 971 | + "A WORKDISTRIBUTE region must be nested inside TEAMS region only."_err_en_US); |
| 972 | + } |
| 973 | + CheckWorkdistributeBlockStmts(block, beginDir.source); |
| 974 | + break; |
| 975 | + case llvm::omp::OMPD_teams_workdistribute: |
| 976 | + case llvm::omp::OMPD_target_teams_workdistribute: |
| 977 | + CheckWorkdistributeBlockStmts(block, beginDir.source); |
| 978 | + break; |
903 | 979 | case llvm::omp::Directive::OMPD_scope: |
904 | 980 | case llvm::omp::Directive::OMPD_single: |
905 | 981 | // TODO: This check needs to be extended while implementing nesting of |
@@ -4385,6 +4461,27 @@ void OmpStructureChecker::CheckWorkshareBlockStmts( |
4385 | 4461 | } |
4386 | 4462 | } |
4387 | 4463 |
|
| 4464 | +void OmpStructureChecker::CheckWorkdistributeBlockStmts( |
| 4465 | + const parser::Block &block, parser::CharBlock source) { |
| 4466 | + unsigned version{context_.langOptions().OpenMPVersion}; |
| 4467 | + unsigned since{60}; |
| 4468 | + if (version < since) |
| 4469 | + context_.Say(source, |
| 4470 | + "WORKDISTRIBUTE construct is not allowed in %s, %s"_err_en_US, |
| 4471 | + ThisVersion(version), TryVersion(since)); |
| 4472 | + |
| 4473 | + OmpWorkdistributeBlockChecker ompWorkdistributeBlockChecker{context_, source}; |
| 4474 | + |
| 4475 | + for (auto it{block.begin()}; it != block.end(); ++it) { |
| 4476 | + if (parser::Unwrap<parser::AssignmentStmt>(*it)) { |
| 4477 | + parser::Walk(*it, ompWorkdistributeBlockChecker); |
| 4478 | + } else { |
| 4479 | + context_.Say(source, |
| 4480 | + "The structured block in a WORKDISTRIBUTE construct may consist of only SCALAR or ARRAY assignments"_err_en_US); |
| 4481 | + } |
| 4482 | + } |
| 4483 | +} |
| 4484 | + |
4388 | 4485 | void OmpStructureChecker::CheckIfContiguous(const parser::OmpObject &object) { |
4389 | 4486 | if (auto contig{IsContiguous(context_, object)}; contig && !*contig) { |
4390 | 4487 | const parser::Name *name{GetObjectName(object)}; |
|
0 commit comments