1 //===-- lib/Semantics/canonicalize-omp.cpp --------------------------------===//
3 // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4 // See https://llvm.org/LICENSE.txt for license information.
5 // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
7 //===----------------------------------------------------------------------===//
9 #include "canonicalize-omp.h"
10 #include "flang/Parser/parse-tree-visitor.h"
12 // After Loop Canonicalization, rewrite OpenMP parse tree to make OpenMP
13 // Constructs more structured which provide explicit scopes for later
14 // structural checks and semantic analysis.
15 // 1. move structured DoConstruct and OmpEndLoopDirective into
16 // OpenMPLoopConstruct. Compilation will not proceed in case of errors
18 // 2. Associate declarative OMP allocation directives with their
19 // respective executable allocation directive
21 namespace Fortran::semantics
{
23 using namespace parser::literals
;
25 class CanonicalizationOfOmp
{
27 template <typename T
> bool Pre(T
&) { return true; }
28 template <typename T
> void Post(T
&) {}
29 CanonicalizationOfOmp(parser::Messages
&messages
) : messages_
{messages
} {}
31 void Post(parser::Block
&block
) {
32 for (auto it
{block
.begin()}; it
!= block
.end(); ++it
) {
33 if (auto *ompCons
{GetConstructIf
<parser::OpenMPConstruct
>(*it
)}) {
34 // OpenMPLoopConstruct
36 std::get_if
<parser::OpenMPLoopConstruct
>(&ompCons
->u
)}) {
37 RewriteOpenMPLoopConstruct(*ompLoop
, block
, it
);
39 } else if (auto *endDir
{
40 GetConstructIf
<parser::OmpEndLoopDirective
>(*it
)}) {
41 // Unmatched OmpEndLoopDirective
42 auto &dir
{std::get
<parser::OmpLoopDirective
>(endDir
->t
)};
43 messages_
.Say(dir
.source
,
44 "The %s directive must follow the DO loop associated with the "
45 "loop construct"_err_en_US
,
46 parser::ToUpperCaseLetters(dir
.source
.ToString()));
51 void Post(parser::ExecutionPart
&body
) { RewriteOmpAllocations(body
); }
54 template <typename T
> T
*GetConstructIf(parser::ExecutionPartConstruct
&x
) {
55 if (auto *y
{std::get_if
<parser::ExecutableConstruct
>(&x
.u
)}) {
56 if (auto *z
{std::get_if
<common::Indirection
<T
>>(&y
->u
)}) {
63 template <typename T
> T
*GetOmpIf(parser::ExecutionPartConstruct
&x
) {
64 if (auto *construct
{GetConstructIf
<parser::OpenMPConstruct
>(x
)}) {
65 if (auto *omp
{std::get_if
<T
>(&construct
->u
)}) {
72 void RewriteOpenMPLoopConstruct(parser::OpenMPLoopConstruct
&x
,
73 parser::Block
&block
, parser::Block::iterator it
) {
74 // Check the sequence of DoConstruct and OmpEndLoopDirective
75 // in the same iteration
78 // ExecutableConstruct -> OpenMPConstruct -> OpenMPLoopConstruct
79 // OmpBeginLoopDirective
80 // ExecutableConstruct -> DoConstruct
81 // ExecutableConstruct -> OmpEndLoopDirective (if available)
84 // ExecutableConstruct -> OpenMPConstruct -> OpenMPLoopConstruct
85 // OmpBeginLoopDirective
87 // OmpEndLoopDirective (if available)
88 parser::Block::iterator nextIt
;
89 auto &beginDir
{std::get
<parser::OmpBeginLoopDirective
>(x
.t
)};
90 auto &dir
{std::get
<parser::OmpLoopDirective
>(beginDir
.t
)};
93 while (++nextIt
!= block
.end()) {
94 // Ignore compiler directives.
95 if (GetConstructIf
<parser::CompilerDirective
>(*nextIt
))
98 if (auto *doCons
{GetConstructIf
<parser::DoConstruct
>(*nextIt
)}) {
99 if (doCons
->GetLoopControl()) {
101 std::get
<std::optional
<parser::DoConstruct
>>(x
.t
) =
103 nextIt
= block
.erase(nextIt
);
104 // try to match OmpEndLoopDirective
105 if (nextIt
!= block
.end()) {
107 GetConstructIf
<parser::OmpEndLoopDirective
>(*nextIt
)}) {
108 std::get
<std::optional
<parser::OmpEndLoopDirective
>>(x
.t
) =
114 messages_
.Say(dir
.source
,
115 "DO loop after the %s directive must have loop control"_err_en_US
,
116 parser::ToUpperCaseLetters(dir
.source
.ToString()));
119 messages_
.Say(dir
.source
,
120 "A DO loop must follow the %s directive"_err_en_US
,
121 parser::ToUpperCaseLetters(dir
.source
.ToString()));
123 // If we get here, we either found a loop, or issued an error message.
128 void RewriteOmpAllocations(parser::ExecutionPart
&body
) {
129 // Rewrite leading declarative allocations so they are nested
130 // within their respective executable allocate directive
133 // ExecutionPartConstruct -> OpenMPDeclarativeAllocate
134 // ExecutionPartConstruct -> OpenMPDeclarativeAllocate
135 // ExecutionPartConstruct -> OpenMPExecutableAllocate
138 // ExecutionPartConstruct -> OpenMPExecutableAllocate
139 // ExecutionPartConstruct -> OpenMPDeclarativeAllocate
140 // ExecutionPartConstruct -> OpenMPDeclarativeAllocate
141 for (auto it
= body
.v
.rbegin(); it
!= body
.v
.rend();) {
142 if (auto *exec
= GetOmpIf
<parser::OpenMPExecutableAllocate
>(*(it
++))) {
143 parser::OpenMPDeclarativeAllocate
*decl
;
144 std::list
<parser::OpenMPDeclarativeAllocate
> subAllocates
;
145 while (it
!= body
.v
.rend() &&
146 (decl
= GetOmpIf
<parser::OpenMPDeclarativeAllocate
>(*it
))) {
147 subAllocates
.push_front(std::move(*decl
));
148 it
= decltype(it
)(body
.v
.erase(std::next(it
).base()));
150 if (!subAllocates
.empty()) {
151 std::get
<std::optional
<std::list
<parser::OpenMPDeclarativeAllocate
>>>(
152 exec
->t
) = {std::move(subAllocates
)};
158 parser::Messages
&messages_
;
161 bool CanonicalizeOmp(parser::Messages
&messages
, parser::Program
&program
) {
162 CanonicalizationOfOmp omp
{messages
};
164 return !messages
.AnyFatalError();
166 } // namespace Fortran::semantics