88
99#include " canonicalize-omp.h"
1010#include " flang/Parser/parse-tree-visitor.h"
11+ #include " flang/Parser/parse-tree.h"
1112
1213// After Loop Canonicalization, rewrite OpenMP parse tree to make OpenMP
1314// Constructs more structured which provide explicit scopes for later
@@ -125,6 +126,16 @@ class CanonicalizationOfOmp {
125126 parser::Block::iterator nextIt;
126127 auto &beginDir{std::get<parser::OmpBeginLoopDirective>(x.t )};
127128 auto &dir{std::get<parser::OmpLoopDirective>(beginDir.t )};
129+ auto missingDoConstruct = [](auto &dir, auto &messages) {
130+ messages.Say (dir.source ,
131+ " A DO loop must follow the %s directive" _err_en_US,
132+ parser::ToUpperCaseLetters (dir.source .ToString ()));
133+ };
134+ auto tileUnrollError = [](auto &dir, auto &messages) {
135+ messages.Say (dir.source ,
136+ " If a loop construct has been fully unrolled, it cannot then be tiled" _err_en_US,
137+ parser::ToUpperCaseLetters (dir.source .ToString ()));
138+ };
128139
129140 nextIt = it;
130141 while (++nextIt != block.end ()) {
@@ -135,31 +146,95 @@ class CanonicalizationOfOmp {
135146 if (auto *doCons{GetConstructIf<parser::DoConstruct>(*nextIt)}) {
136147 if (doCons->GetLoopControl ()) {
137148 // move DoConstruct
138- std::get<std::optional<parser::DoConstruct>>(x.t ) =
149+ std::get<std::optional<std::variant<parser::DoConstruct,
150+ common::Indirection<parser::OpenMPLoopConstruct>>>>(x.t ) =
139151 std::move (*doCons);
140152 nextIt = block.erase (nextIt);
141153 // try to match OmpEndLoopDirective
142- if (nextIt != block.end ()) {
143- if (auto *endDir{
144- GetConstructIf<parser::OmpEndLoopDirective>(*nextIt)}) {
145- std::get<std::optional<parser::OmpEndLoopDirective>>(x.t ) =
146- std::move (*endDir);
147- block.erase (nextIt);
148- }
154+ if (auto *endDir{
155+ GetConstructIf<parser::OmpEndLoopDirective>(*nextIt)}) {
156+ std::get<std::optional<parser::OmpEndLoopDirective>>(x.t ) =
157+ std::move (*endDir);
158+ nextIt = block.erase (nextIt);
149159 }
150160 } else {
151161 messages_.Say (dir.source ,
152162 " DO loop after the %s directive must have loop control" _err_en_US,
153163 parser::ToUpperCaseLetters (dir.source .ToString ()));
154164 }
165+ } else if (auto *ompLoopCons{
166+ GetOmpIf<parser::OpenMPLoopConstruct>(*nextIt)}) {
167+ // We should allow UNROLL and TILE constructs to be inserted between an
168+ // OpenMP Loop Construct and the DO loop itself
169+ auto &nestedBeginDirective =
170+ std::get<parser::OmpBeginLoopDirective>(ompLoopCons->t );
171+ auto &nestedBeginLoopDirective =
172+ std::get<parser::OmpLoopDirective>(nestedBeginDirective.t );
173+ if ((nestedBeginLoopDirective.v == llvm::omp::Directive::OMPD_unroll ||
174+ nestedBeginLoopDirective.v ==
175+ llvm::omp::Directive::OMPD_tile) &&
176+ !(nestedBeginLoopDirective.v == llvm::omp::Directive::OMPD_unroll &&
177+ dir.v == llvm::omp::Directive::OMPD_tile)) {
178+ // iterate through the remaining block items to find the end directive
179+ // for the unroll/tile directive.
180+ parser::Block::iterator endIt;
181+ endIt = nextIt;
182+ while (endIt != block.end ()) {
183+ if (auto *endDir{
184+ GetConstructIf<parser::OmpEndLoopDirective>(*endIt)}) {
185+ auto &endLoopDirective =
186+ std::get<parser::OmpLoopDirective>(endDir->t );
187+ if (endLoopDirective.v == dir.v ) {
188+ std::get<std::optional<parser::OmpEndLoopDirective>>(x.t ) =
189+ std::move (*endDir);
190+ endIt = block.erase (endIt);
191+ continue ;
192+ }
193+ }
194+ ++endIt;
195+ }
196+ RewriteOpenMPLoopConstruct (*ompLoopCons, block, nextIt);
197+ auto &ompLoop = std::get<std::optional<parser::NestedConstruct>>(x.t );
198+ ompLoop =
199+ std::optional<parser::NestedConstruct>{parser::NestedConstruct{
200+ common::Indirection{std::move (*ompLoopCons)}}};
201+ nextIt = block.erase (nextIt);
202+ } else if (nestedBeginLoopDirective.v ==
203+ llvm::omp::Directive::OMPD_unroll &&
204+ dir.v == llvm::omp::Directive::OMPD_tile) {
205+ // if a loop has been unrolled, the user can not then tile that loop
206+ // as it has been unrolled
207+ parser::OmpClauseList &unrollClauseList{
208+ std::get<parser::OmpClauseList>(nestedBeginDirective.t )};
209+ if (unrollClauseList.v .empty ()) {
210+ // if the clause list is empty for an unroll construct, we assume
211+ // the loop is being fully unrolled
212+ tileUnrollError (dir, messages_);
213+ } else {
214+ // parse the clauses for the unroll directive to find the full
215+ // clause
216+ for (auto clause{unrollClauseList.v .begin ()};
217+ clause != unrollClauseList.v .end (); ++clause) {
218+ if (clause->Id () == llvm::omp::OMPC_full) {
219+ tileUnrollError (dir, messages_);
220+ }
221+ }
222+ }
223+ } else {
224+ messages_.Say (nestedBeginLoopDirective.source ,
225+ " Only Loop Transformation Constructs or Loop Nests can be nested within Loop Constructs" _err_en_US,
226+ parser::ToUpperCaseLetters (
227+ nestedBeginLoopDirective.source .ToString ()));
228+ }
155229 } else {
156- messages_.Say (dir.source ,
157- " A DO loop must follow the %s directive" _err_en_US,
158- parser::ToUpperCaseLetters (dir.source .ToString ()));
230+ missingDoConstruct (dir, messages_);
159231 }
160232 // If we get here, we either found a loop, or issued an error message.
161233 return ;
162234 }
235+ if (nextIt == block.end ()) {
236+ missingDoConstruct (dir, messages_);
237+ }
163238 }
164239
165240 void RewriteOmpAllocations (parser::ExecutionPart &body) {
0 commit comments