diff options
Diffstat (limited to 'flang/lib/Semantics/canonicalize-omp.cpp')
| -rw-r--r-- | flang/lib/Semantics/canonicalize-omp.cpp | 156 |
1 files changed, 130 insertions, 26 deletions
diff --git a/flang/lib/Semantics/canonicalize-omp.cpp b/flang/lib/Semantics/canonicalize-omp.cpp index c884658..a11c525 100644 --- a/flang/lib/Semantics/canonicalize-omp.cpp +++ b/flang/lib/Semantics/canonicalize-omp.cpp @@ -51,8 +51,6 @@ public: } // Block list } - void Post(parser::ExecutionPart &body) { RewriteOmpAllocations(body); } - // Pre-visit all constructs that have both a specification part and // an execution part, and store the connection between the two. bool Pre(parser::BlockConstruct &x) { @@ -88,6 +86,7 @@ public: void Post(parser::SpecificationPart &spec) { CanonicalizeUtilityConstructs(spec); + CanonicalizeAllocateDirectives(spec); } void Post(parser::OmpMapClause &map) { CanonicalizeMapModifiers(map); } @@ -239,33 +238,138 @@ private: } } - void RewriteOmpAllocations(parser::ExecutionPart &body) { - // Rewrite leading declarative allocations so they are nested - // within their respective executable allocate directive - // - // Original: - // ExecutionPartConstruct -> OpenMPDeclarativeAllocate - // ExecutionPartConstruct -> OpenMPDeclarativeAllocate - // ExecutionPartConstruct -> OpenMPExecutableAllocate - // - // After rewriting: - // ExecutionPartConstruct -> OpenMPExecutableAllocate - // ExecutionPartConstruct -> OpenMPDeclarativeAllocate - // ExecutionPartConstruct -> OpenMPDeclarativeAllocate - for (auto it = body.v.rbegin(); it != body.v.rend();) { - if (auto *exec = GetOmpIf<parser::OpenMPExecutableAllocate>(*(it++))) { - parser::OpenMPDeclarativeAllocate *decl; - std::list<parser::OpenMPDeclarativeAllocate> subAllocates; - while (it != body.v.rend() && - (decl = GetOmpIf<parser::OpenMPDeclarativeAllocate>(*it))) { - subAllocates.push_front(std::move(*decl)); - it = decltype(it)(body.v.erase(std::next(it).base())); + // Canonicalization of allocate directives + // + // In OpenMP 5.0 and 5.1 the allocate directive could either be a declarative + // one or an executable one. As usual in such cases, this poses a problem + // when the directive appears at the boundary between the specification part + // and the execution part. + // The executable form can actually consist of several adjacent directives, + // whereas the declarative form is always standalone. Additionally, the + // executable form must be associated with an allocate statement. + // + // The parser tries to parse declarative statements first, so in the + // following case, the two directives will be declarative, even though + // they should be treated as a single executable form: + // integer, allocatable :: x, y ! Specification + // !$omp allocate(x) + // !$omp allocate(y) + // allocate(x, y) ! Execution + // + void CanonicalizeAllocateDirectives(parser::SpecificationPart &spec) { + auto found = blockForSpec_.find(&spec); + if (found == blockForSpec_.end()) { + // There is no corresponding execution part, so there is nothing to do. + return; + } + parser::Block &block = *found->second; + + auto isAllocateStmt = [](const parser::ExecutionPartConstruct &epc) { + if (auto *ec = std::get_if<parser::ExecutableConstruct>(&epc.u)) { + if (auto *as = + std::get_if<parser::Statement<parser::ActionStmt>>(&ec->u)) { + return std::holds_alternative< + common::Indirection<parser::AllocateStmt>>(as->statement.u); + } + } + return false; + }; + + if (!block.empty() && isAllocateStmt(block.front())) { + // There are two places where an OpenMP declarative construct can + // show up in the tuple in specification part: + // (1) in std::list<OpenMPDeclarativeConstruct>, or + // (2) in std::list<DeclarationConstruct>. + // The case (1) is only possible if the list (2) is empty. + + auto &omps = + std::get<std::list<parser::OpenMPDeclarativeConstruct>>(spec.t); + auto &decls = std::get<std::list<parser::DeclarationConstruct>>(spec.t); + + if (!decls.empty()) { + MakeExecutableAllocateFromDecls(decls, block); + } else { + MakeExecutableAllocateFromOmps(omps, block); + } + } + } + + parser::ExecutionPartConstruct EmbedInExec( + parser::OmpAllocateDirective *alo, parser::ExecutionPartConstruct &&epc) { + // Nest current epc inside the allocate directive. + std::get<parser::Block>(alo->t).push_front(std::move(epc)); + // Set the new epc to be the ExecutionPartConstruct made from + // the allocate directive. + parser::OpenMPConstruct opc(std::move(*alo)); + common::Indirection<parser::OpenMPConstruct> ind(std::move(opc)); + parser::ExecutableConstruct ec(std::move(ind)); + return parser::ExecutionPartConstruct(std::move(ec)); + } + + void MakeExecutableAllocateFromDecls( + std::list<parser::DeclarationConstruct> &decls, parser::Block &body) { + using OpenMPDeclarativeConstruct = + common::Indirection<parser::OpenMPDeclarativeConstruct>; + + auto getAllocate = [](parser::DeclarationConstruct *dc) { + if (auto *sc = std::get_if<parser::SpecificationConstruct>(&dc->u)) { + if (auto *odc = std::get_if<OpenMPDeclarativeConstruct>(&sc->u)) { + if (auto *alo = + std::get_if<parser::OmpAllocateDirective>(&odc->value().u)) { + return alo; + } + } + } + return static_cast<parser::OmpAllocateDirective *>(nullptr); + }; + + std::list<parser::DeclarationConstruct>::reverse_iterator rlast = [&]() { + for (auto rit = decls.rbegin(), rend = decls.rend(); rit != rend; ++rit) { + if (getAllocate(&*rit) == nullptr) { + return rit; } - if (!subAllocates.empty()) { - std::get<std::optional<std::list<parser::OpenMPDeclarativeAllocate>>>( - exec->t) = {std::move(subAllocates)}; + } + return decls.rend(); + }(); + + if (rlast != decls.rbegin()) { + // We have already checked that the first statement in body is + // ALLOCATE. + parser::ExecutionPartConstruct epc(std::move(body.front())); + for (auto rit = decls.rbegin(); rit != rlast; ++rit) { + epc = EmbedInExec(getAllocate(&*rit), std::move(epc)); + } + + body.pop_front(); + body.push_front(std::move(epc)); + decls.erase(rlast.base(), decls.end()); + } + } + + void MakeExecutableAllocateFromOmps( + std::list<parser::OpenMPDeclarativeConstruct> &omps, + parser::Block &body) { + using OpenMPDeclarativeConstruct = parser::OpenMPDeclarativeConstruct; + + std::list<OpenMPDeclarativeConstruct>::reverse_iterator rlast = [&]() { + for (auto rit = omps.rbegin(), rend = omps.rend(); rit != rend; ++rit) { + if (!std::holds_alternative<parser::OmpAllocateDirective>(rit->u)) { + return rit; } } + return omps.rend(); + }(); + + if (rlast != omps.rbegin()) { + parser::ExecutionPartConstruct epc(std::move(body.front())); + for (auto rit = omps.rbegin(); rit != rlast; ++rit) { + epc = EmbedInExec( + &std::get<parser::OmpAllocateDirective>(rit->u), std::move(epc)); + } + + body.pop_front(); + body.push_front(std::move(epc)); + omps.erase(rlast.base(), omps.end()); } } |
