@@ -50,6 +50,43 @@ class CanonicalizationOfOmp {
50
50
51
51
void Post (parser::ExecutionPart &body) { RewriteOmpAllocations (body); }
52
52
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
+
53
90
private:
54
91
template <typename T> T *GetConstructIf (parser::ExecutionPartConstruct &x) {
55
92
if (auto *y{std::get_if<parser::ExecutableConstruct>(&x.u )}) {
@@ -155,6 +192,131 @@ class CanonicalizationOfOmp {
155
192
}
156
193
}
157
194
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_;
158
320
parser::Messages &messages_;
159
321
};
160
322
0 commit comments