1 //===-- lib/Semantics/canonicalize-acc.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-acc.h"
10 #include "flang/Parser/parse-tree-visitor.h"
11 #include "flang/Semantics/tools.h"
13 // After Loop Canonicalization, rewrite OpenACC parse tree to make OpenACC
14 // Constructs more structured which provide explicit scopes for later
15 // structural checks and semantic analysis.
16 // 1. move structured DoConstruct into
17 // OpenACCLoopConstruct. Compilation will not proceed in case of errors
19 // 2. move structured DoConstruct into OpenACCCombinedConstruct. Move
20 // AccEndCombinedConstruct into OpenACCCombinedConstruct if present.
21 // Compilation will not proceed in case of errors after this pass.
22 namespace Fortran::semantics
{
24 using namespace parser::literals
;
26 class CanonicalizationOfAcc
{
28 template <typename T
> bool Pre(T
&) { return true; }
29 template <typename T
> void Post(T
&) {}
30 CanonicalizationOfAcc(parser::Messages
&messages
) : messages_
{messages
} {}
32 void Post(parser::Block
&block
) {
33 for (auto it
{block
.begin()}; it
!= block
.end(); ++it
) {
34 if (auto *accLoop
{parser::Unwrap
<parser::OpenACCLoopConstruct
>(*it
)}) {
35 RewriteOpenACCLoopConstruct(*accLoop
, block
, it
);
36 } else if (auto *accCombined
{
37 parser::Unwrap
<parser::OpenACCCombinedConstruct
>(*it
)}) {
38 RewriteOpenACCCombinedConstruct(*accCombined
, block
, it
);
39 } else if (auto *endDir
{
40 parser::Unwrap
<parser::AccEndCombinedDirective
>(*it
)}) {
41 // Unmatched AccEndCombinedDirective
42 messages_
.Say(endDir
->v
.source
,
43 "The %s directive must follow the DO loop associated with the "
44 "loop construct"_err_en_US
,
45 parser::ToUpperCaseLetters(endDir
->v
.source
.ToString()));
51 // Check constraint in 2.9.7
52 // If there are n tile sizes in the list, the loop construct must be
53 // immediately followed by n tightly-nested loops.
54 template <typename C
, typename D
>
55 void CheckTileClauseRestriction(const C
&x
) {
56 const auto &beginLoopDirective
= std::get
<D
>(x
.t
);
57 const auto &accClauseList
=
58 std::get
<parser::AccClauseList
>(beginLoopDirective
.t
);
59 for (const auto &clause
: accClauseList
.v
) {
60 if (const auto *tileClause
=
61 std::get_if
<parser::AccClause::Tile
>(&clause
.u
)) {
62 const parser::AccTileExprList
&tileExprList
= tileClause
->v
;
63 const std::list
<parser::AccTileExpr
> &listTileExpr
= tileExprList
.v
;
64 std::size_t tileArgNb
= listTileExpr
.size();
66 const auto &outer
{std::get
<std::optional
<parser::DoConstruct
>>(x
.t
)};
67 if (outer
->IsDoConcurrent()) {
68 return; // Tile is not allowed on DO CONURRENT
70 for (const parser::DoConstruct
*loop
{&*outer
}; loop
&& tileArgNb
> 0;
72 const auto &block
{std::get
<parser::Block
>(loop
->t
)};
73 const auto it
{block
.begin()};
74 loop
= it
!= block
.end() ? parser::Unwrap
<parser::DoConstruct
>(*it
)
79 messages_
.Say(beginLoopDirective
.source
,
80 "The loop construct with the TILE clause must be followed by %d "
81 "tightly-nested loops"_err_en_US
,
88 // Check constraint on line 1835 in Section 2.9
89 // A tile and collapse clause may not appear on loop that is associated with
91 template <typename C
, typename D
>
92 void CheckDoConcurrentClauseRestriction(const C
&x
) {
93 const auto &doCons
{std::get
<std::optional
<parser::DoConstruct
>>(x
.t
)};
94 if (!doCons
->IsDoConcurrent()) {
97 const auto &beginLoopDirective
= std::get
<D
>(x
.t
);
98 const auto &accClauseList
=
99 std::get
<parser::AccClauseList
>(beginLoopDirective
.t
);
100 for (const auto &clause
: accClauseList
.v
) {
101 if (std::holds_alternative
<parser::AccClause::Collapse
>(clause
.u
) ||
102 std::holds_alternative
<parser::AccClause::Tile
>(clause
.u
)) {
103 messages_
.Say(beginLoopDirective
.source
,
104 "TILE and COLLAPSE clause may not appear on loop construct "
105 "associated with DO CONCURRENT"_err_en_US
);
110 void RewriteOpenACCLoopConstruct(parser::OpenACCLoopConstruct
&x
,
111 parser::Block
&block
, parser::Block::iterator it
) {
112 // Check the sequence of DoConstruct in the same iteration
115 // ExecutableConstruct -> OpenACCConstruct -> OpenACCLoopConstruct
116 // ACCBeginLoopDirective
117 // ExecutableConstruct -> DoConstruct
120 // ExecutableConstruct -> OpenACCConstruct -> OpenACCLoopConstruct
121 // AccBeginLoopDirective
123 parser::Block::iterator nextIt
;
124 auto &beginDir
{std::get
<parser::AccBeginLoopDirective
>(x
.t
)};
125 auto &dir
{std::get
<parser::AccLoopDirective
>(beginDir
.t
)};
128 if (++nextIt
!= block
.end()) {
129 if (auto *doCons
{parser::Unwrap
<parser::DoConstruct
>(*nextIt
)}) {
130 if (!doCons
->GetLoopControl()) {
131 messages_
.Say(dir
.source
,
132 "DO loop after the %s directive must have loop control"_err_en_US
,
133 parser::ToUpperCaseLetters(dir
.source
.ToString()));
138 std::get
<std::optional
<parser::DoConstruct
>>(x
.t
) = std::move(*doCons
);
139 nextIt
= block
.erase(nextIt
);
141 CheckDoConcurrentClauseRestriction
<parser::OpenACCLoopConstruct
,
142 parser::AccBeginLoopDirective
>(x
);
143 CheckTileClauseRestriction
<parser::OpenACCLoopConstruct
,
144 parser::AccBeginLoopDirective
>(x
);
146 return; // found do-loop
149 messages_
.Say(dir
.source
,
150 "A DO loop must follow the %s directive"_err_en_US
,
151 parser::ToUpperCaseLetters(dir
.source
.ToString()));
154 void RewriteOpenACCCombinedConstruct(parser::OpenACCCombinedConstruct
&x
,
155 parser::Block
&block
, parser::Block::iterator it
) {
156 // Check the sequence of DoConstruct in the same iteration
159 // ExecutableConstruct -> OpenACCConstruct -> OpenACCCombinedConstruct
160 // ACCBeginCombinedDirective
161 // ExecutableConstruct -> DoConstruct
162 // ExecutableConstruct -> AccEndCombinedDirective (if available)
165 // ExecutableConstruct -> OpenACCConstruct -> OpenACCCombinedConstruct
166 // ACCBeginCombinedDirective
168 // AccEndCombinedDirective (if available)
169 parser::Block::iterator nextIt
;
170 auto &beginDir
{std::get
<parser::AccBeginCombinedDirective
>(x
.t
)};
171 auto &dir
{std::get
<parser::AccCombinedDirective
>(beginDir
.t
)};
174 if (++nextIt
!= block
.end()) {
175 if (auto *doCons
{parser::Unwrap
<parser::DoConstruct
>(*nextIt
)}) {
176 if (!doCons
->GetLoopControl()) {
177 messages_
.Say(dir
.source
,
178 "DO loop after the %s directive must have loop control"_err_en_US
,
179 parser::ToUpperCaseLetters(dir
.source
.ToString()));
183 std::get
<std::optional
<parser::DoConstruct
>>(x
.t
) = std::move(*doCons
);
184 nextIt
= block
.erase(nextIt
);
185 // try to match AccEndCombinedDirective
186 if (nextIt
!= block
.end()) {
188 parser::Unwrap
<parser::AccEndCombinedDirective
>(*nextIt
)}) {
189 std::get
<std::optional
<parser::AccEndCombinedDirective
>>(x
.t
) =
195 CheckDoConcurrentClauseRestriction
<parser::OpenACCCombinedConstruct
,
196 parser::AccBeginCombinedDirective
>(x
);
197 CheckTileClauseRestriction
<parser::OpenACCCombinedConstruct
,
198 parser::AccBeginCombinedDirective
>(x
);
200 return; // found do-loop
203 messages_
.Say(dir
.source
,
204 "A DO loop must follow the %s directive"_err_en_US
,
205 parser::ToUpperCaseLetters(dir
.source
.ToString()));
208 parser::Messages
&messages_
;
211 bool CanonicalizeAcc(parser::Messages
&messages
, parser::Program
&program
) {
212 CanonicalizationOfAcc acc
{messages
};
214 return !messages
.AnyFatalError();
216 } // namespace Fortran::semantics