@@ -50,6 +50,43 @@ class CanonicalizationOfOmp {
5050
5151 void Post (parser::ExecutionPart &body) { RewriteOmpAllocations (body); }
5252
53+ // Pre-visit all constructs that have both a specification part and
54+ // an execution part, and store the connection between the two.
55+ bool Pre (parser::BlockConstruct &x) {
56+ auto *spec = &std::get<parser::BlockSpecificationPart>(x.t ).v ;
57+ auto *block = &std::get<parser::Block>(x.t );
58+ blockForSpec_.insert (std::make_pair (spec, block));
59+ return true ;
60+ }
61+ bool Pre (parser::MainProgram &x) {
62+ auto *spec = &std::get<parser::SpecificationPart>(x.t );
63+ auto *block = &std::get<parser::ExecutionPart>(x.t ).v ;
64+ blockForSpec_.insert (std::make_pair (spec, block));
65+ return true ;
66+ }
67+ bool Pre (parser::FunctionSubprogram &x) {
68+ auto *spec = &std::get<parser::SpecificationPart>(x.t );
69+ auto *block = &std::get<parser::ExecutionPart>(x.t ).v ;
70+ blockForSpec_.insert (std::make_pair (spec, block));
71+ return true ;
72+ }
73+ bool Pre (parser::SubroutineSubprogram &x) {
74+ auto *spec = &std::get<parser::SpecificationPart>(x.t );
75+ auto *block = &std::get<parser::ExecutionPart>(x.t ).v ;
76+ blockForSpec_.insert (std::make_pair (spec, block));
77+ return true ;
78+ }
79+ bool Pre (parser::SeparateModuleSubprogram &x) {
80+ auto *spec = &std::get<parser::SpecificationPart>(x.t );
81+ auto *block = &std::get<parser::ExecutionPart>(x.t ).v ;
82+ blockForSpec_.insert (std::make_pair (spec, block));
83+ return true ;
84+ }
85+
86+ void Post (parser::SpecificationPart &spec) {
87+ CanonicalizeUtilityConstructs (spec);
88+ }
89+
5390private:
5491 template <typename T> T *GetConstructIf (parser::ExecutionPartConstruct &x) {
5592 if (auto *y{std::get_if<parser::ExecutableConstruct>(&x.u )}) {
@@ -155,6 +192,131 @@ class CanonicalizationOfOmp {
155192 }
156193 }
157194
195+ // Canonicalization of utility constructs.
196+ //
197+ // This addresses the issue of utility constructs that appear at the
198+ // boundary between the specification and the execution parts, e.g.
199+ // subroutine foo
200+ // integer :: x ! Specification
201+ // !$omp nothing
202+ // x = 1 ! Execution
203+ // ...
204+ // end
205+ //
206+ // Utility constructs (error and nothing) can appear in both the
207+ // specification part and the execution part, except "error at(execution)",
208+ // which cannot be present in the specification part (whereas any utility
209+ // construct can be in the execution part).
210+ // When a utility construct is at the boundary, it should preferably be
211+ // parsed as an element of the execution part, but since the specification
212+ // part is parsed first, the utility construct ends up belonging to the
213+ // specification part.
214+ //
215+ // To allow the likes of the following code to compile, move all utility
216+ // construct that are at the end of the specification part to the beginning
217+ // of the execution part.
218+ //
219+ // subroutine foo
220+ // !$omp error at(execution) ! Initially parsed as declarative construct.
221+ // ! Move it to the execution part.
222+ // end
223+
224+ void CanonicalizeUtilityConstructs (parser::SpecificationPart &spec) {
225+ auto found = blockForSpec_.find (&spec);
226+ if (found == blockForSpec_.end ()) {
227+ // There is no corresponding execution part, so there is nothing to do.
228+ return ;
229+ }
230+ parser::Block &block = *found->second ;
231+
232+ // There are two places where an OpenMP declarative construct can
233+ // show up in the tuple in specification part:
234+ // (1) in std::list<OpenMPDeclarativeConstruct>, or
235+ // (2) in std::list<DeclarationConstruct>.
236+ // The case (1) is only possible is the list (2) is empty.
237+
238+ auto &omps =
239+ std::get<std::list<parser::OpenMPDeclarativeConstruct>>(spec.t );
240+ auto &decls = std::get<std::list<parser::DeclarationConstruct>>(spec.t );
241+
242+ if (!decls.empty ()) {
243+ MoveUtilityConstructsFromDecls (decls, block);
244+ } else {
245+ MoveUtilityConstructsFromOmps (omps, block);
246+ }
247+ }
248+
249+ void MoveUtilityConstructsFromDecls (
250+ std::list<parser::DeclarationConstruct> &decls, parser::Block &block) {
251+ // Find the trailing range of DeclarationConstructs that are OpenMP
252+ // utility construct, that are to be moved to the execution part.
253+ std::list<parser::DeclarationConstruct>::reverse_iterator rlast = [&]() {
254+ for (auto rit = decls.rbegin (), rend = decls.rend (); rit != rend; ++rit) {
255+ parser::DeclarationConstruct &dc = *rit;
256+ if (!std::holds_alternative<parser::SpecificationConstruct>(dc.u )) {
257+ return rit;
258+ }
259+ auto &sc = std::get<parser::SpecificationConstruct>(dc.u );
260+ using OpenMPDeclarativeConstruct =
261+ common::Indirection<parser::OpenMPDeclarativeConstruct>;
262+ if (!std::holds_alternative<OpenMPDeclarativeConstruct>(sc.u )) {
263+ return rit;
264+ }
265+ // Got OpenMPDeclarativeConstruct. If it's not a utility construct
266+ // then stop.
267+ auto &odc = std::get<OpenMPDeclarativeConstruct>(sc.u ).value ();
268+ if (!std::holds_alternative<parser::OpenMPUtilityConstruct>(odc.u )) {
269+ return rit;
270+ }
271+ }
272+ return decls.rend ();
273+ }();
274+
275+ std::transform (decls.rbegin (), rlast, std::front_inserter (block),
276+ [](parser::DeclarationConstruct &dc) {
277+ auto &sc = std::get<parser::SpecificationConstruct>(dc.u );
278+ using OpenMPDeclarativeConstruct =
279+ common::Indirection<parser::OpenMPDeclarativeConstruct>;
280+ auto &oc = std::get<OpenMPDeclarativeConstruct>(sc.u ).value ();
281+ auto &ut = std::get<parser::OpenMPUtilityConstruct>(oc.u );
282+
283+ return parser::ExecutionPartConstruct (parser::ExecutableConstruct (
284+ common::Indirection (parser::OpenMPConstruct (std::move (ut)))));
285+ });
286+
287+ decls.erase (rlast.base (), decls.end ());
288+ }
289+
290+ void MoveUtilityConstructsFromOmps (
291+ std::list<parser::OpenMPDeclarativeConstruct> &omps,
292+ parser::Block &block) {
293+ using OpenMPDeclarativeConstruct = parser::OpenMPDeclarativeConstruct;
294+ // Find the trailing range of OpenMPDeclarativeConstruct that are OpenMP
295+ // utility construct, that are to be moved to the execution part.
296+ std::list<OpenMPDeclarativeConstruct>::reverse_iterator rlast = [&]() {
297+ for (auto rit = omps.rbegin (), rend = omps.rend (); rit != rend; ++rit) {
298+ OpenMPDeclarativeConstruct &dc = *rit;
299+ if (!std::holds_alternative<parser::OpenMPUtilityConstruct>(dc.u )) {
300+ return rit;
301+ }
302+ }
303+ return omps.rend ();
304+ }();
305+
306+ std::transform (omps.rbegin (), rlast, std::front_inserter (block),
307+ [](parser::OpenMPDeclarativeConstruct &dc) {
308+ auto &ut = std::get<parser::OpenMPUtilityConstruct>(dc.u );
309+ return parser::ExecutionPartConstruct (parser::ExecutableConstruct (
310+ common::Indirection (parser::OpenMPConstruct (std::move (ut)))));
311+ });
312+
313+ omps.erase (rlast.base (), omps.end ());
314+ }
315+
316+ // Mapping from the specification parts to the blocks that follow in the
317+ // same construct. This is for converting utility constructs to executable
318+ // constructs.
319+ std::map<parser::SpecificationPart *, parser::Block *> blockForSpec_;
158320 parser::Messages &messages_;
159321};
160322
0 commit comments