[flang-commits] [flang] [flang][OpenMP] Move CanonicalizationOfOmp member definitions out of … (PR #232872)
Krzysztof Parzyszek via flang-commits
flang-commits at lists.llvm.org
Fri Oct 16 10:55:23 PDT 2026
https://github.com/kparzysz created https://github.com/llvm/llvm-project/pull/232872
…class
Keep only declarations (and the trivial constructor) in the class, and define each member function out of line after the class. Each function's leading comment moves with its implementation. No functional change.
>From ef711e4576871b6837f3cc295bc89d43bd7e1d8f Mon Sep 17 00:00:00 2001
From: Krzysztof Parzyszek <Krzysztof.Parzyszek at amd.com>
Date: Fri, 16 Oct 2026 12:40:06 -0500
Subject: [PATCH] [flang][OpenMP] Move CanonicalizationOfOmp member definitions
out of class
Keep only declarations (and the trivial constructor) in the class, and
define each member function out of line after the class. Each function's
leading comment moves with its implementation. No functional change.
Co-Authored-By: Claude Opus 4 (1M context) <noreply at anthropic.com>
---
flang/lib/Semantics/canonicalize-omp.cpp | 550 ++++++++++++-----------
1 file changed, 290 insertions(+), 260 deletions(-)
diff --git a/flang/lib/Semantics/canonicalize-omp.cpp b/flang/lib/Semantics/canonicalize-omp.cpp
index fe96a63b3982bd..1f5daa050779c2 100644
--- a/flang/lib/Semantics/canonicalize-omp.cpp
+++ b/flang/lib/Semantics/canonicalize-omp.cpp
@@ -18,7 +18,6 @@
// respective executable allocation directive
// 2. TBD
namespace Fortran::semantics {
-
using namespace parser::literals;
class CanonicalizationOfOmp {
@@ -28,307 +27,338 @@ class CanonicalizationOfOmp {
CanonicalizationOfOmp(SemanticsContext &context)
: messages_{context.messages()} {}
- // 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) {
- auto *spec = &std::get<parser::BlockSpecificationPart>(x.t).v;
- auto *block = &std::get<parser::Block>(x.t);
- blockForSpec_.insert(std::make_pair(spec, block));
- return true;
- }
- bool Pre(parser::MainProgram &x) {
- auto *spec = &std::get<parser::SpecificationPart>(x.t);
- auto *block = &std::get<parser::ExecutionPart>(x.t).v;
- blockForSpec_.insert(std::make_pair(spec, block));
- return true;
- }
- bool Pre(parser::FunctionSubprogram &x) {
- auto *spec = &std::get<parser::SpecificationPart>(x.t);
- auto *block = &std::get<parser::ExecutionPart>(x.t).v;
- blockForSpec_.insert(std::make_pair(spec, block));
- return true;
- }
- bool Pre(parser::SubroutineSubprogram &x) {
- auto *spec = &std::get<parser::SpecificationPart>(x.t);
- auto *block = &std::get<parser::ExecutionPart>(x.t).v;
- blockForSpec_.insert(std::make_pair(spec, block));
- return true;
- }
- bool Pre(parser::SeparateModuleSubprogram &x) {
- auto *spec = &std::get<parser::SpecificationPart>(x.t);
- auto *block = &std::get<parser::ExecutionPart>(x.t).v;
- blockForSpec_.insert(std::make_pair(spec, block));
- return true;
- }
+ bool Pre(parser::BlockConstruct &x);
+ bool Pre(parser::MainProgram &x);
+ bool Pre(parser::FunctionSubprogram &x);
+ bool Pre(parser::SubroutineSubprogram &x);
+ bool Pre(parser::SeparateModuleSubprogram &x);
- void Post(parser::SpecificationPart &spec) {
- CanonicalizeUtilityConstructs(spec);
- CanonicalizeAllocateDirectives(spec);
- }
+ void Post(parser::SpecificationPart &spec);
private:
- // 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);
- }
- }
- }
+ void CanonicalizeAllocateDirectives(parser::SpecificationPart &spec);
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));
- }
+ parser::OmpAllocateDirective *alo, parser::ExecutionPartConstruct &&epc);
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> &decls, parser::Block &body);
- std::list<parser::DeclarationConstruct>::reverse_iterator rlast = [&]() {
- for (auto rit = decls.rbegin(), rend = decls.rend(); rit != rend; ++rit) {
- if (getAllocate(&*rit) == nullptr) {
- return rit;
- }
- }
- 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));
- }
+ void MakeExecutableAllocateFromOmps(
+ std::list<parser::OpenMPDeclarativeConstruct> &omps, parser::Block &body);
- body.pop_front();
- body.push_front(std::move(epc));
- decls.erase(rlast.base(), decls.end());
- }
- }
+ void CanonicalizeUtilityConstructs(parser::SpecificationPart &spec);
- void MakeExecutableAllocateFromOmps(
+ void MoveUtilityConstructsFromDecls(
+ std::list<parser::DeclarationConstruct> &decls, parser::Block &block);
+
+ void MoveUtilityConstructsFromOmps(
std::list<parser::OpenMPDeclarativeConstruct> &omps,
- parser::Block &body) {
- using OpenMPDeclarativeConstruct = parser::OpenMPDeclarativeConstruct;
+ parser::Block &block);
- 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));
- }
+ // Mapping from the specification parts to the blocks that follow in the
+ // same construct. This is for converting utility constructs to executable
+ // constructs.
+ std::map<parser::SpecificationPart *, parser::Block *> blockForSpec_;
+ parser::Messages &messages_;
+};
- body.pop_front();
- body.push_front(std::move(epc));
- omps.erase(rlast.base(), omps.end());
- }
- }
+// Pre-visit all constructs that have both a specification part and
+// an execution part, and store the connection between the two.
+bool CanonicalizationOfOmp::Pre(parser::BlockConstruct &x) {
+ auto *spec = &std::get<parser::BlockSpecificationPart>(x.t).v;
+ auto *block = &std::get<parser::Block>(x.t);
+ blockForSpec_.insert(std::make_pair(spec, block));
+ return true;
+}
+
+bool CanonicalizationOfOmp::Pre(parser::MainProgram &x) {
+ auto *spec = &std::get<parser::SpecificationPart>(x.t);
+ auto *block = &std::get<parser::ExecutionPart>(x.t).v;
+ blockForSpec_.insert(std::make_pair(spec, block));
+ return true;
+}
+
+bool CanonicalizationOfOmp::Pre(parser::FunctionSubprogram &x) {
+ auto *spec = &std::get<parser::SpecificationPart>(x.t);
+ auto *block = &std::get<parser::ExecutionPart>(x.t).v;
+ blockForSpec_.insert(std::make_pair(spec, block));
+ return true;
+}
- // Canonicalization of utility constructs.
- //
- // This addresses the issue of utility constructs that appear at the
- // boundary between the specification and the execution parts, e.g.
- // subroutine foo
- // integer :: x ! Specification
- // !$omp nothing
- // x = 1 ! Execution
- // ...
- // end
- //
- // Utility constructs (error and nothing) can appear in both the
- // specification part and the execution part, except "error at(execution)",
- // which cannot be present in the specification part (whereas any utility
- // construct can be in the execution part).
- // When a utility construct is at the boundary, it should preferably be
- // parsed as an element of the execution part, but since the specification
- // part is parsed first, the utility construct ends up belonging to the
- // specification part.
- //
- // To allow the likes of the following code to compile, move all utility
- // construct that are at the end of the specification part to the beginning
- // of the execution part.
- //
- // subroutine foo
- // !$omp error at(execution) ! Initially parsed as declarative construct.
- // ! Move it to the execution part.
- // end
-
- void CanonicalizeUtilityConstructs(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;
+bool CanonicalizationOfOmp::Pre(parser::SubroutineSubprogram &x) {
+ auto *spec = &std::get<parser::SpecificationPart>(x.t);
+ auto *block = &std::get<parser::ExecutionPart>(x.t).v;
+ blockForSpec_.insert(std::make_pair(spec, block));
+ return true;
+}
+
+bool CanonicalizationOfOmp::Pre(parser::SeparateModuleSubprogram &x) {
+ auto *spec = &std::get<parser::SpecificationPart>(x.t);
+ auto *block = &std::get<parser::ExecutionPart>(x.t).v;
+ blockForSpec_.insert(std::make_pair(spec, block));
+ return true;
+}
+
+void CanonicalizationOfOmp::Post(parser::SpecificationPart &spec) {
+ CanonicalizeUtilityConstructs(spec);
+ CanonicalizeAllocateDirectives(spec);
+}
+
+// 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 CanonicalizationOfOmp::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);
+ }
}
- parser::Block &block = *found->second;
+ 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 is the list (2) is empty.
+ // 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()) {
- MoveUtilityConstructsFromDecls(decls, block);
+ MakeExecutableAllocateFromDecls(decls, block);
} else {
- MoveUtilityConstructsFromOmps(omps, block);
+ MakeExecutableAllocateFromOmps(omps, block);
}
}
+}
- void MoveUtilityConstructsFromDecls(
- std::list<parser::DeclarationConstruct> &decls, parser::Block &block) {
- // Find the trailing range of DeclarationConstructs that are OpenMP
- // utility construct, that are to be moved to the execution part.
- std::list<parser::DeclarationConstruct>::reverse_iterator rlast = [&]() {
- for (auto rit = decls.rbegin(), rend = decls.rend(); rit != rend; ++rit) {
- parser::DeclarationConstruct &dc = *rit;
- if (!std::holds_alternative<parser::SpecificationConstruct>(dc.u)) {
- return rit;
- }
- auto &sc = std::get<parser::SpecificationConstruct>(dc.u);
- using OpenMPDeclarativeConstruct =
- common::Indirection<parser::OpenMPDeclarativeConstruct>;
- if (!std::holds_alternative<OpenMPDeclarativeConstruct>(sc.u)) {
- return rit;
- }
- // Got OpenMPDeclarativeConstruct. If it's not a utility construct
- // then stop.
- auto &odc = std::get<OpenMPDeclarativeConstruct>(sc.u).value();
- if (!std::holds_alternative<parser::OmpUtilityDirective>(odc.u)) {
- return rit;
+parser::ExecutionPartConstruct CanonicalizationOfOmp::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 CanonicalizationOfOmp::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 decls.rend();
- }();
-
- std::transform(decls.rbegin(), rlast, std::front_inserter(block),
- [](parser::DeclarationConstruct &dc) {
- auto &sc = std::get<parser::SpecificationConstruct>(dc.u);
- using OpenMPDeclarativeConstruct =
- common::Indirection<parser::OpenMPDeclarativeConstruct>;
- auto &oc = std::get<OpenMPDeclarativeConstruct>(sc.u).value();
- auto &ut = std::get<parser::OmpUtilityDirective>(oc.u);
+ }
+ return static_cast<parser::OmpAllocateDirective *>(nullptr);
+ };
- return parser::ExecutionPartConstruct(parser::ExecutableConstruct(
- common::Indirection(parser::OpenMPConstruct(std::move(ut)))));
- });
+ std::list<parser::DeclarationConstruct>::reverse_iterator rlast = [&]() {
+ for (auto rit = decls.rbegin(), rend = decls.rend(); rit != rend; ++rit) {
+ if (getAllocate(&*rit) == nullptr) {
+ return rit;
+ }
+ }
+ 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 MoveUtilityConstructsFromOmps(
- std::list<parser::OpenMPDeclarativeConstruct> &omps,
- parser::Block &block) {
- using OpenMPDeclarativeConstruct = parser::OpenMPDeclarativeConstruct;
- // Find the trailing range of OpenMPDeclarativeConstruct that are OpenMP
- // utility construct, that are to be moved to the execution part.
- std::list<OpenMPDeclarativeConstruct>::reverse_iterator rlast = [&]() {
- for (auto rit = omps.rbegin(), rend = omps.rend(); rit != rend; ++rit) {
- OpenMPDeclarativeConstruct &dc = *rit;
- if (!std::holds_alternative<parser::OmpUtilityDirective>(dc.u)) {
- return rit;
- }
- }
- return omps.rend();
- }();
+void CanonicalizationOfOmp::MakeExecutableAllocateFromOmps(
+ std::list<parser::OpenMPDeclarativeConstruct> &omps, parser::Block &body) {
+ using OpenMPDeclarativeConstruct = parser::OpenMPDeclarativeConstruct;
- std::transform(omps.rbegin(), rlast, std::front_inserter(block),
- [](parser::OpenMPDeclarativeConstruct &dc) {
- auto &ut = std::get<parser::OmpUtilityDirective>(dc.u);
- return parser::ExecutionPartConstruct(parser::ExecutableConstruct(
- common::Indirection(parser::OpenMPConstruct(std::move(ut)))));
- });
+ 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());
}
+}
- // Mapping from the specification parts to the blocks that follow in the
- // same construct. This is for converting utility constructs to executable
- // constructs.
- std::map<parser::SpecificationPart *, parser::Block *> blockForSpec_;
- parser::Messages &messages_;
-};
+// Canonicalization of utility constructs.
+//
+// This addresses the issue of utility constructs that appear at the
+// boundary between the specification and the execution parts, e.g.
+// subroutine foo
+// integer :: x ! Specification
+// !$omp nothing
+// x = 1 ! Execution
+// ...
+// end
+//
+// Utility constructs (error and nothing) can appear in both the
+// specification part and the execution part, except "error at(execution)",
+// which cannot be present in the specification part (whereas any utility
+// construct can be in the execution part).
+// When a utility construct is at the boundary, it should preferably be
+// parsed as an element of the execution part, but since the specification
+// part is parsed first, the utility construct ends up belonging to the
+// specification part.
+//
+// To allow the likes of the following code to compile, move all utility
+// construct that are at the end of the specification part to the beginning
+// of the execution part.
+//
+// subroutine foo
+// !$omp error at(execution) ! Initially parsed as declarative construct.
+// ! Move it to the execution part.
+// end
+
+void CanonicalizationOfOmp::CanonicalizeUtilityConstructs(
+ 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;
+
+ // 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 is 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()) {
+ MoveUtilityConstructsFromDecls(decls, block);
+ } else {
+ MoveUtilityConstructsFromOmps(omps, block);
+ }
+}
+
+void CanonicalizationOfOmp::MoveUtilityConstructsFromDecls(
+ std::list<parser::DeclarationConstruct> &decls, parser::Block &block) {
+ // Find the trailing range of DeclarationConstructs that are OpenMP
+ // utility construct, that are to be moved to the execution part.
+ std::list<parser::DeclarationConstruct>::reverse_iterator rlast = [&]() {
+ for (auto rit = decls.rbegin(), rend = decls.rend(); rit != rend; ++rit) {
+ parser::DeclarationConstruct &dc = *rit;
+ if (!std::holds_alternative<parser::SpecificationConstruct>(dc.u)) {
+ return rit;
+ }
+ auto &sc = std::get<parser::SpecificationConstruct>(dc.u);
+ using OpenMPDeclarativeConstruct =
+ common::Indirection<parser::OpenMPDeclarativeConstruct>;
+ if (!std::holds_alternative<OpenMPDeclarativeConstruct>(sc.u)) {
+ return rit;
+ }
+ // Got OpenMPDeclarativeConstruct. If it's not a utility construct
+ // then stop.
+ auto &odc = std::get<OpenMPDeclarativeConstruct>(sc.u).value();
+ if (!std::holds_alternative<parser::OmpUtilityDirective>(odc.u)) {
+ return rit;
+ }
+ }
+ return decls.rend();
+ }();
+
+ std::transform(decls.rbegin(), rlast, std::front_inserter(block),
+ [](parser::DeclarationConstruct &dc) {
+ auto &sc = std::get<parser::SpecificationConstruct>(dc.u);
+ using OpenMPDeclarativeConstruct =
+ common::Indirection<parser::OpenMPDeclarativeConstruct>;
+ auto &oc = std::get<OpenMPDeclarativeConstruct>(sc.u).value();
+ auto &ut = std::get<parser::OmpUtilityDirective>(oc.u);
+
+ return parser::ExecutionPartConstruct(parser::ExecutableConstruct(
+ common::Indirection(parser::OpenMPConstruct(std::move(ut)))));
+ });
+
+ decls.erase(rlast.base(), decls.end());
+}
+
+void CanonicalizationOfOmp::MoveUtilityConstructsFromOmps(
+ std::list<parser::OpenMPDeclarativeConstruct> &omps, parser::Block &block) {
+ using OpenMPDeclarativeConstruct = parser::OpenMPDeclarativeConstruct;
+ // Find the trailing range of OpenMPDeclarativeConstruct that are OpenMP
+ // utility construct, that are to be moved to the execution part.
+ std::list<OpenMPDeclarativeConstruct>::reverse_iterator rlast = [&]() {
+ for (auto rit = omps.rbegin(), rend = omps.rend(); rit != rend; ++rit) {
+ OpenMPDeclarativeConstruct &dc = *rit;
+ if (!std::holds_alternative<parser::OmpUtilityDirective>(dc.u)) {
+ return rit;
+ }
+ }
+ return omps.rend();
+ }();
+
+ std::transform(omps.rbegin(), rlast, std::front_inserter(block),
+ [](parser::OpenMPDeclarativeConstruct &dc) {
+ auto &ut = std::get<parser::OmpUtilityDirective>(dc.u);
+ return parser::ExecutionPartConstruct(parser::ExecutableConstruct(
+ common::Indirection(parser::OpenMPConstruct(std::move(ut)))));
+ });
+
+ omps.erase(rlast.base(), omps.end());
+}
bool CanonicalizeOmp(SemanticsContext &context, parser::Program &program) {
CanonicalizationOfOmp omp{context};
More information about the flang-commits
mailing list