1 // Copyright (c) 2019, NVIDIA CORPORATION. All rights reserved.
2 //
3 // Licensed under the Apache License, Version 2.0 (the "License");
4 // you may not use this file except in compliance with the License.
5 // You may obtain a copy of the License at
6 //
7 // http://www.apache.org/licenses/LICENSE-2.0
8 //
9 // Unless required by applicable law or agreed to in writing, software
10 // distributed under the License is distributed on an "AS IS" BASIS,
11 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 // See the License for the specific language governing permissions and
13 // limitations under the License.
14
15 #include "flattened.h"
16 #include "../parser/parse-tree-visitor.h"
17 #include "../semantics/symbol.h"
18
19 namespace Fortran::burnside {
20 namespace flat {
21
22 // Labels are numbered [0 .. `n`] consecutively. They are unsigned. Not all
23 // labels are numbered. The unnumbered ones are given the value UINT_MAX. `n`
24 // should never approach UINT_MAX.
LabelBuilder()25 LabelBuilder::LabelBuilder() : referenced(32), counter{0u} {}
26
getNext()27 LabelRef LabelBuilder::getNext() {
28 LabelRef next{counter++};
29 auto cap{referenced.size()};
30 if (cap < counter) {
31 referenced.resize(2 * cap);
32 }
33 referenced.reset(next);
34 return next;
35 }
36
setReferenced(LabelRef label)37 void LabelBuilder::setReferenced(LabelRef label) {
38 CHECK(label < referenced.getBitCapacity());
39 referenced.set(label);
40 }
41
isReferenced(LabelRef label) const42 bool LabelBuilder::isReferenced(LabelRef label) const {
43 CHECK(label < referenced.getBitCapacity());
44 return referenced.test(label);
45 }
46
LabelOp(LabelBuilder & builder)47 LabelOp::LabelOp(LabelBuilder &builder)
48 : builder{builder}, label{builder.getNext()} {}
49
LabelOp(const LabelOp & that)50 LabelOp::LabelOp(const LabelOp &that)
51 : builder{that.builder}, label{that.label} {}
52
operator =(const LabelOp & that)53 LabelOp &LabelOp::operator=(const LabelOp &that) {
54 CHECK(&builder == &that.builder);
55 label = that.label;
56 return *this;
57 }
58
setReferenced() const59 void LabelOp::setReferenced() const { builder.setReferenced(label); }
60
isReferenced() const61 bool LabelOp::isReferenced() const { return builder.isReferenced(label); }
62
AddAssign(AnalysisData & ad,const semantics::Symbol * symbol,const parser::Label & label)63 static void AddAssign(AnalysisData &ad, const semantics::Symbol *symbol,
64 const parser::Label &label) {
65 ad.assignMap[symbol].insert(label);
66 }
67
GetAssign(AnalysisData & ad,const semantics::Symbol * symbol)68 std::vector<LabelRef> GetAssign(
69 AnalysisData &ad, const semantics::Symbol *symbol) {
70 std::vector<LabelRef> result;
71 for (auto lab : ad.assignMap[symbol]) {
72 result.emplace_back(lab);
73 }
74 return result;
75 }
76
FindStack(const std::vector<std::tuple<const parser::Name *,LabelRef,LabelRef>> & stack,const parser::Name * key)77 static std::tuple<const parser::Name *, LabelRef, LabelRef> FindStack(
78 const std::vector<std::tuple<const parser::Name *, LabelRef, LabelRef>>
79 &stack,
80 const parser::Name *key) {
81 for (auto iter{stack.rbegin()}, iend{stack.rend()}; iter != iend; ++iter) {
82 if (std::get<0>(*iter) == key) {
83 return *iter;
84 }
85 }
86 assert(false && "construct name not on stack");
87 return {};
88 }
89
FetchLabel(AnalysisData & ad,const parser::Label & label)90 LabelOp FetchLabel(AnalysisData &ad, const parser::Label &label) {
91 auto iter{ad.labelMap.find(label)};
92 if (iter == ad.labelMap.end()) {
93 LabelOp ll{ad.labelBuilder};
94 ll.setReferenced();
95 ad.labelMap.insert({label, ll});
96 return ll;
97 }
98 return iter->second;
99 }
100
BuildNewLabel(AnalysisData & ad)101 static LabelOp BuildNewLabel(AnalysisData &ad) {
102 return LabelOp{ad.labelBuilder};
103 }
104
GetErr(const A & stmt)105 template<typename A> parser::Label GetErr(const A &stmt) {
106 if constexpr (std::is_same_v<A, parser::ReadStmt> ||
107 std::is_same_v<A, parser::WriteStmt>) {
108 for (const auto &control : stmt.controls) {
109 if (std::holds_alternative<parser::ErrLabel>(control.u)) {
110 return std::get<parser::ErrLabel>(control.u).v;
111 }
112 }
113 }
114 if constexpr (std::is_same_v<A, parser::WaitStmt> ||
115 std::is_same_v<A, parser::OpenStmt> ||
116 std::is_same_v<A, parser::CloseStmt> ||
117 std::is_same_v<A, parser::BackspaceStmt> ||
118 std::is_same_v<A, parser::EndfileStmt> ||
119 std::is_same_v<A, parser::RewindStmt> ||
120 std::is_same_v<A, parser::FlushStmt>) {
121 for (const auto &spec : stmt.v) {
122 if (std::holds_alternative<parser::ErrLabel>(spec.u)) {
123 return std::get<parser::ErrLabel>(spec.u).v;
124 }
125 }
126 }
127 if constexpr (std::is_same_v<A, parser::InquireStmt>) {
128 for (const auto &spec : std::get<std::list<parser::InquireSpec>>(stmt.u)) {
129 if (std::holds_alternative<parser::ErrLabel>(spec.u)) {
130 return std::get<parser::ErrLabel>(spec.u).v;
131 }
132 }
133 }
134 return 0;
135 }
136
GetEor(const A & stmt)137 template<typename A> parser::Label GetEor(const A &stmt) {
138 if constexpr (std::is_same_v<A, parser::ReadStmt> ||
139 std::is_same_v<A, parser::WriteStmt>) {
140 for (const auto &control : stmt.controls) {
141 if (std::holds_alternative<parser::EorLabel>(control.u)) {
142 return std::get<parser::EorLabel>(control.u).v;
143 }
144 }
145 }
146 if constexpr (std::is_same_v<A, parser::WaitStmt>) {
147 for (const auto &waitSpec : stmt.v) {
148 if (std::holds_alternative<parser::EorLabel>(waitSpec.u)) {
149 return std::get<parser::EorLabel>(waitSpec.u).v;
150 }
151 }
152 }
153 return 0;
154 }
155
GetEnd(const A & stmt)156 template<typename A> parser::Label GetEnd(const A &stmt) {
157 if constexpr (std::is_same_v<A, parser::ReadStmt> ||
158 std::is_same_v<A, parser::WriteStmt>) {
159 for (const auto &control : stmt.controls) {
160 if (std::holds_alternative<parser::EndLabel>(control.u)) {
161 return std::get<parser::EndLabel>(control.u).v;
162 }
163 }
164 }
165 if constexpr (std::is_same_v<A, parser::WaitStmt>) {
166 for (const auto &waitSpec : stmt.v) {
167 if (std::holds_alternative<parser::EndLabel>(waitSpec.u)) {
168 return std::get<parser::EndLabel>(waitSpec.u).v;
169 }
170 }
171 }
172 return 0;
173 }
174
175 template<typename A>
errLabelSpec(const A & s,std::list<Op> & ops,const parser::Statement<parser::ActionStmt> & ec,AnalysisData & ad)176 void errLabelSpec(const A &s, std::list<Op> &ops,
177 const parser::Statement<parser::ActionStmt> &ec, AnalysisData &ad) {
178 if (auto errLab{GetErr(s)}) {
179 std::optional<LabelRef> errRef{FetchLabel(ad, errLab).get()};
180 LabelOp next{BuildNewLabel(ad)};
181 ops.emplace_back(SwitchIOOp{s, next, ec.source, errRef});
182 ops.emplace_back(next);
183 } else {
184 ops.emplace_back(ActionOp{ec});
185 }
186 }
187
188 template<typename A>
threeLabelSpec(const A & s,std::list<Op> & ops,const parser::Statement<parser::ActionStmt> & ec,AnalysisData & ad)189 void threeLabelSpec(const A &s, std::list<Op> &ops,
190 const parser::Statement<parser::ActionStmt> &ec, AnalysisData &ad) {
191 auto errLab{GetErr(s)};
192 auto eorLab{GetEor(s)};
193 auto endLab{GetEnd(s)};
194 if (errLab || eorLab || endLab) {
195 std::optional<LabelRef> errRef;
196 if (errLab) {
197 errRef = FetchLabel(ad, errLab).get();
198 }
199 std::optional<LabelRef> eorRef;
200 if (eorLab) {
201 eorRef = FetchLabel(ad, eorLab).get();
202 }
203 std::optional<LabelRef> endRef;
204 if (endLab) {
205 endRef = FetchLabel(ad, endLab).get();
206 }
207 auto next{BuildNewLabel(ad)};
208 ops.emplace_back(SwitchIOOp{s, next, ec.source, errRef, eorRef, endRef});
209 ops.emplace_back(next);
210 } else {
211 ops.emplace_back(ActionOp{ec});
212 }
213 }
214
215 template<typename A>
toLabelRef(AnalysisData & ad,const A & labels)216 std::vector<LabelRef> toLabelRef(AnalysisData &ad, const A &labels) {
217 std::vector<LabelRef> result;
218 for (auto label : labels) {
219 result.emplace_back(FetchLabel(ad, label).get());
220 }
221 CHECK(result.size() == labels.size());
222 return result;
223 }
224
225 template<typename A>
toLabelRef(const LabelOp & next,AnalysisData & ad,const A & labels)226 std::vector<LabelRef> toLabelRef(
227 const LabelOp &next, AnalysisData &ad, const A &labels) {
228 std::vector<LabelRef> result;
229 result.emplace_back(next);
230 auto refs{toLabelRef(ad, labels)};
231 result.insert(result.end(), refs.begin(), refs.end());
232 CHECK(result.size() == labels.size() + 1);
233 return result;
234 }
235
hasAltReturns(const parser::CallStmt & callStmt)236 static bool hasAltReturns(const parser::CallStmt &callStmt) {
237 const auto &args{std::get<std::list<parser::ActualArgSpec>>(callStmt.v.t)};
238 for (const auto &arg : args) {
239 const auto &actual{std::get<parser::ActualArg>(arg.t)};
240 if (std::holds_alternative<parser::AltReturnSpec>(actual.u)) {
241 return true;
242 }
243 }
244 return false;
245 }
246
getAltReturnLabels(const parser::Call & call)247 static std::list<parser::Label> getAltReturnLabels(const parser::Call &call) {
248 std::list<parser::Label> result;
249 const auto &args{std::get<std::list<parser::ActualArgSpec>>(call.t)};
250 for (const auto &arg : args) {
251 const auto &actual{std::get<parser::ActualArg>(arg.t)};
252 if (const auto *p{std::get_if<parser::AltReturnSpec>(&actual.u)}) {
253 result.push_back(p->v);
254 }
255 }
256 return result;
257 }
258
NearestEnclosingDoConstruct(AnalysisData & ad)259 static LabelRef NearestEnclosingDoConstruct(AnalysisData &ad) {
260 for (auto iterator{ad.constructContextStack.rbegin()},
261 endIterator{ad.constructContextStack.rend()};
262 iterator != endIterator; ++iterator) {
263 auto labelReference{std::get<2>(*iterator)};
264 if (labelReference != UnspecifiedLabel) {
265 return labelReference;
266 }
267 }
268 assert(false && "CYCLE|EXIT not in loop");
269 return UnspecifiedLabel;
270 }
271
GetSource(const A * s)272 template<typename A> std::string GetSource(const A *s) {
273 return s->source.ToString();
274 }
275
GetSource(const B * s)276 template<typename A, typename B> std::string GetSource(const B *s) {
277 return GetSource(&std::get<parser::Statement<A>>(s->t));
278 }
279
Build(std::list<Op> & ops,const parser::Statement<parser::ActionStmt> & ec,AnalysisData & ad)280 void Op::Build(std::list<Op> &ops,
281 const parser::Statement<parser::ActionStmt> &ec, AnalysisData &ad) {
282 std::visit(
283 common::visitors{
284 [&](const auto &) { ops.emplace_back(ActionOp{ec}); },
285 [&](const common::Indirection<parser::CallStmt> &s) {
286 if (hasAltReturns(s.value())) {
287 auto next{BuildNewLabel(ad)};
288 auto alts{getAltReturnLabels(s.value().v)};
289 auto labels{toLabelRef(next, ad, alts)};
290 ops.emplace_back(
291 SwitchOp{s.value(), std::move(labels), ec.source});
292 ops.emplace_back(next);
293 } else {
294 ops.emplace_back(ActionOp{ec});
295 }
296 },
297 [&](const common::Indirection<parser::AssignStmt> &s) {
298 AddAssign(ad, std::get<parser::Name>(s.value().t).symbol,
299 std::get<parser::Label>(s.value().t));
300 ops.emplace_back(ActionOp{ec});
301 },
302 [&](const common::Indirection<parser::CycleStmt> &s) {
303 ops.emplace_back(GotoOp{s.value(),
304 s.value().v ? std::get<2>(FindStack(ad.constructContextStack,
305 &s.value().v.value()))
306 : NearestEnclosingDoConstruct(ad),
307 ec.source});
308 },
309 [&](const common::Indirection<parser::ExitStmt> &s) {
310 ops.emplace_back(GotoOp{s.value(),
311 s.value().v ? std::get<1>(FindStack(ad.constructContextStack,
312 &s.value().v.value()))
313 : NearestEnclosingDoConstruct(ad),
314 ec.source});
315 },
316 [&](const common::Indirection<parser::GotoStmt> &s) {
317 ops.emplace_back(GotoOp{
318 s.value(), FetchLabel(ad, s.value().v).get(), ec.source});
319 },
320 [&](const parser::FailImageStmt &s) {
321 ops.emplace_back(ReturnOp{s, ec.source});
322 },
323 [&](const common::Indirection<parser::ReturnStmt> &s) {
324 ops.emplace_back(ReturnOp{s.value(), ec.source});
325 },
326 [&](const common::Indirection<parser::StopStmt> &s) {
327 ops.emplace_back(ActionOp{ec});
328 ops.emplace_back(ReturnOp{s.value(), ec.source});
329 },
330 [&](const common::Indirection<const parser::ReadStmt> &s) {
331 threeLabelSpec(s.value(), ops, ec, ad);
332 },
333 [&](const common::Indirection<const parser::WriteStmt> &s) {
334 threeLabelSpec(s.value(), ops, ec, ad);
335 },
336 [&](const common::Indirection<const parser::WaitStmt> &s) {
337 threeLabelSpec(s.value(), ops, ec, ad);
338 },
339 [&](const common::Indirection<const parser::OpenStmt> &s) {
340 errLabelSpec(s.value(), ops, ec, ad);
341 },
342 [&](const common::Indirection<const parser::CloseStmt> &s) {
343 errLabelSpec(s.value(), ops, ec, ad);
344 },
345 [&](const common::Indirection<const parser::BackspaceStmt> &s) {
346 errLabelSpec(s.value(), ops, ec, ad);
347 },
348 [&](const common::Indirection<const parser::EndfileStmt> &s) {
349 errLabelSpec(s.value(), ops, ec, ad);
350 },
351 [&](const common::Indirection<const parser::RewindStmt> &s) {
352 errLabelSpec(s.value(), ops, ec, ad);
353 },
354 [&](const common::Indirection<const parser::FlushStmt> &s) {
355 errLabelSpec(s.value(), ops, ec, ad);
356 },
357 [&](const common::Indirection<const parser::InquireStmt> &s) {
358 errLabelSpec(s.value(), ops, ec, ad);
359 },
360 [&](const common::Indirection<parser::ComputedGotoStmt> &s) {
361 auto next{BuildNewLabel(ad)};
362 auto labels{toLabelRef(
363 next, ad, std::get<std::list<parser::Label>>(s.value().t))};
364 ops.emplace_back(SwitchOp{s.value(), std::move(labels), ec.source});
365 ops.emplace_back(next);
366 },
367 [&](const common::Indirection<parser::ArithmeticIfStmt> &s) {
368 ops.emplace_back(SwitchOp{s.value(),
369 toLabelRef(ad,
370 std::list{std::get<1>(s.value().t),
371 std::get<2>(s.value().t), std::get<3>(s.value().t)}),
372 ec.source});
373 },
374 [&](const common::Indirection<parser::AssignedGotoStmt> &s) {
375 ops.emplace_back(
376 IndirectGotoOp{std::get<parser::Name>(s.value().t).symbol,
377 toLabelRef(
378 ad, std::get<std::list<parser::Label>>(s.value().t))});
379 },
380 [&](const common::Indirection<parser::IfStmt> &s) {
381 auto then{BuildNewLabel(ad)};
382 auto endif{BuildNewLabel(ad)};
383 ops.emplace_back(ConditionalGotoOp{s.value(), then, endif});
384 ops.emplace_back(then);
385 ops.emplace_back(ActionOp{ec});
386 ops.emplace_back(endif);
387 },
388 },
389 ec.statement.u);
390 }
391
392 template<typename> struct ElementMap;
393 template<> struct ElementMap<parser::CaseConstruct> {
394 using type = parser::CaseConstruct::Case;
395 };
396 template<> struct ElementMap<parser::SelectRankConstruct> {
397 using type = parser::SelectRankConstruct::RankCase;
398 };
399 template<> struct ElementMap<parser::SelectTypeConstruct> {
400 using type = parser::SelectTypeConstruct::TypeCase;
401 };
402
403 struct ControlFlowAnalyzer {
ControlFlowAnalyzerFortran::burnside::flat::ControlFlowAnalyzer404 explicit ControlFlowAnalyzer(std::list<Op> &ops, AnalysisData &ad)
405 : linearOps{ops}, ad{ad} {}
406
buildNewLabelFortran::burnside::flat::ControlFlowAnalyzer407 LabelOp buildNewLabel() { return BuildNewLabel(ad); }
408
findLabelFortran::burnside::flat::ControlFlowAnalyzer409 Op findLabel(const parser::Label &lab) {
410 auto iter{ad.labelMap.find(lab)};
411 if (iter == ad.labelMap.end()) {
412 LabelOp ll{ad.labelBuilder};
413 ad.labelMap.insert({lab, ll});
414 return {ll};
415 }
416 return {iter->second};
417 }
418
PreFortran::burnside::flat::ControlFlowAnalyzer419 template<typename A> constexpr bool Pre(const A &) { return true; }
PostFortran::burnside::flat::ControlFlowAnalyzer420 template<typename A> constexpr void Post(const A &) {}
421
PreFortran::burnside::flat::ControlFlowAnalyzer422 template<typename A> bool Pre(const parser::Statement<A> &stmt) {
423 if (stmt.label) {
424 linearOps.emplace_back(findLabel(*stmt.label));
425 }
426 if constexpr (std::is_same_v<A, parser::ActionStmt>) {
427 Op::Build(linearOps, stmt, ad);
428 }
429 return true;
430 }
431 template<typename A>
appendIfLabeledFortran::burnside::flat::ControlFlowAnalyzer432 void appendIfLabeled(const parser::Statement<A> &stmt, std::list<Op> &ops) {
433 if (stmt.label) {
434 ops.emplace_back(findLabel(*stmt.label));
435 }
436 }
437
438 // named constructs
linearConstructFortran::burnside::flat::ControlFlowAnalyzer439 template<typename A> bool linearConstruct(const A &construct) {
440 std::list<Op> ops;
441 LabelOp label{buildNewLabel()};
442 const parser::Name *name{getName(construct)};
443 ad.constructContextStack.emplace_back(
444 name, GetLabelRef(label), UnspecifiedLabel);
445 appendIfLabeled(std::get<0>(construct.t), ops);
446 ops.emplace_back(BeginOp{construct});
447 ControlFlowAnalyzer cfa{ops, ad};
448 Walk(std::get<parser::Block>(construct.t), cfa);
449 ops.emplace_back(label);
450 appendIfLabeled(std::get<2>(construct.t), ops);
451 ops.emplace_back(EndOp{construct});
452 linearOps.splice(linearOps.end(), ops);
453 ad.constructContextStack.pop_back();
454 return false;
455 }
456
PreFortran::burnside::flat::ControlFlowAnalyzer457 bool Pre(const parser::AssociateConstruct &c) { return linearConstruct(c); }
PreFortran::burnside::flat::ControlFlowAnalyzer458 bool Pre(const parser::ChangeTeamConstruct &c) { return linearConstruct(c); }
PreFortran::burnside::flat::ControlFlowAnalyzer459 bool Pre(const parser::CriticalConstruct &c) { return linearConstruct(c); }
460
PreFortran::burnside::flat::ControlFlowAnalyzer461 bool Pre(const parser::BlockConstruct &construct) {
462 std::list<Op> ops;
463 LabelOp label{buildNewLabel()};
464 const auto &optName{
465 std::get<parser::Statement<parser::BlockStmt>>(construct.t)
466 .statement.v};
467 const parser::Name *name{optName ? &*optName : nullptr};
468 ad.constructContextStack.emplace_back(
469 name, GetLabelRef(label), UnspecifiedLabel);
470 appendIfLabeled(
471 std::get<parser::Statement<parser::BlockStmt>>(construct.t), ops);
472 ops.emplace_back(BeginOp{construct});
473 ControlFlowAnalyzer cfa{ops, ad};
474 Walk(std::get<parser::Block>(construct.t), cfa);
475 appendIfLabeled(
476 std::get<parser::Statement<parser::EndBlockStmt>>(construct.t), ops);
477 ops.emplace_back(EndOp{construct});
478 ops.emplace_back(label);
479 linearOps.splice(linearOps.end(), ops);
480 ad.constructContextStack.pop_back();
481 return false;
482 }
483
484 /// `DO` constructs can be lowered to `fir.loop` if they meet some
485 /// constraints, otherwise they are lowered to a CFG.
PreFortran::burnside::flat::ControlFlowAnalyzer486 bool Pre(const parser::DoConstruct &construct) {
487 std::list<Op> ops;
488 LabelOp backedgeLab{buildNewLabel()};
489 LabelOp incrementLab{buildNewLabel()};
490 LabelOp entryLab{buildNewLabel()};
491 LabelOp exitLab{buildNewLabel()};
492 const parser::Name *name{getName(construct)};
493 LabelRef exitOpRef{GetLabelRef(exitLab)};
494 ad.constructContextStack.emplace_back(
495 name, exitOpRef, GetLabelRef(incrementLab));
496 appendIfLabeled(
497 std::get<parser::Statement<parser::NonLabelDoStmt>>(construct.t), ops);
498 ops.emplace_back(BeginOp{construct});
499 ops.emplace_back(GotoOp{GetLabelRef(backedgeLab)});
500 ops.emplace_back(incrementLab);
501 ops.emplace_back(DoIncrementOp{construct});
502 ops.emplace_back(backedgeLab);
503 ops.emplace_back(DoCompareOp{construct});
504 ops.emplace_back(ConditionalGotoOp{
505 std::get<parser::Statement<parser::NonLabelDoStmt>>(construct.t),
506 GetLabelRef(entryLab), exitOpRef});
507 ops.push_back(entryLab);
508 ControlFlowAnalyzer cfa{ops, ad};
509 Walk(std::get<parser::Block>(construct.t), cfa);
510 appendIfLabeled(
511 std::get<parser::Statement<parser::EndDoStmt>>(construct.t), ops);
512 ops.emplace_back(GotoOp{GetLabelRef(incrementLab)});
513 ops.emplace_back(EndOp{construct});
514 ops.emplace_back(exitLab);
515 linearOps.splice(linearOps.end(), ops);
516 ad.constructContextStack.pop_back();
517 return false;
518 }
519
520 /// `IF` constructs can be lowered to `fir.where` if they meet some
521 /// constraints, otherwise they are lowered to a CFG.
PreFortran::burnside::flat::ControlFlowAnalyzer522 bool Pre(const parser::IfConstruct &construct) {
523 std::list<Op> ops;
524 LabelOp thenLab{buildNewLabel()};
525 LabelOp elseLab{buildNewLabel()};
526 LabelOp exitLab{buildNewLabel()};
527 const parser::Name *name{getName(construct)};
528 ad.constructContextStack.emplace_back(
529 name, GetLabelRef(exitLab), UnspecifiedLabel);
530 appendIfLabeled(
531 std::get<parser::Statement<parser::IfThenStmt>>(construct.t), ops);
532 ops.emplace_back(BeginOp{construct});
533 ops.emplace_back(ConditionalGotoOp{
534 std::get<parser::Statement<parser::IfThenStmt>>(construct.t),
535 GetLabelRef(thenLab), GetLabelRef(elseLab)});
536 ops.emplace_back(thenLab);
537 ControlFlowAnalyzer cfa{ops, ad};
538 Walk(std::get<parser::Block>(construct.t), cfa);
539 LabelRef exitOpRef{GetLabelRef(exitLab)};
540 ops.emplace_back(GotoOp{exitOpRef});
541 for (const auto &elseIfBlock :
542 std::get<std::list<parser::IfConstruct::ElseIfBlock>>(construct.t)) {
543 appendIfLabeled(
544 std::get<parser::Statement<parser::ElseIfStmt>>(elseIfBlock.t), ops);
545 ops.emplace_back(elseLab);
546 LabelOp newThenLab{buildNewLabel()};
547 LabelOp newElseLab{buildNewLabel()};
548 ops.emplace_back(ConditionalGotoOp{
549 std::get<parser::Statement<parser::ElseIfStmt>>(elseIfBlock.t),
550 GetLabelRef(newThenLab), GetLabelRef(newElseLab)});
551 ops.emplace_back(newThenLab);
552 Walk(std::get<parser::Block>(elseIfBlock.t), cfa);
553 ops.emplace_back(GotoOp{exitOpRef});
554 elseLab = newElseLab;
555 }
556 ops.emplace_back(elseLab);
557 if (const auto &optElseBlock{
558 std::get<std::optional<parser::IfConstruct::ElseBlock>>(
559 construct.t)}) {
560 appendIfLabeled(
561 std::get<parser::Statement<parser::ElseStmt>>(optElseBlock->t), ops);
562 Walk(std::get<parser::Block>(optElseBlock->t), cfa);
563 }
564 ops.emplace_back(GotoOp{exitOpRef});
565 ops.emplace_back(exitLab);
566 appendIfLabeled(
567 std::get<parser::Statement<parser::EndIfStmt>>(construct.t), ops);
568 ops.emplace_back(EndOp{construct});
569 linearOps.splice(linearOps.end(), ops);
570 ad.constructContextStack.pop_back();
571 return false;
572 }
573
MultiwayFortran::burnside::flat::ControlFlowAnalyzer574 template<typename A> bool Multiway(const A &construct) {
575 using B = typename ElementMap<A>::type;
576 std::list<Op> ops;
577 LabelOp exitLab{buildNewLabel()};
578 const parser::Name *name{getName(construct)};
579 ad.constructContextStack.emplace_back(
580 name, GetLabelRef(exitLab), UnspecifiedLabel);
581 appendIfLabeled(std::get<0>(construct.t), ops);
582 ops.emplace_back(BeginOp{construct});
583 const auto N{std::get<std::list<B>>(construct.t).size()};
584 LabelRef exitOpRef{GetLabelRef(exitLab)};
585 if (N > 0) {
586 typename std::list<B>::size_type i;
587 std::vector<LabelOp> toLabels;
588 for (i = 0; i != N; ++i) {
589 toLabels.emplace_back(buildNewLabel());
590 }
591 std::vector<LabelRef> targets;
592 for (i = 0; i != N; ++i) {
593 targets.emplace_back(GetLabelRef(toLabels[i]));
594 }
595 ops.emplace_back(
596 SwitchOp{construct, targets, std::get<0>(construct.t).source});
597 ControlFlowAnalyzer cfa{ops, ad};
598 i = 0;
599 for (const auto &caseBlock : std::get<std::list<B>>(construct.t)) {
600 ops.emplace_back(toLabels[i++]);
601 appendIfLabeled(std::get<0>(caseBlock.t), ops);
602 Walk(std::get<parser::Block>(caseBlock.t), cfa);
603 ops.emplace_back(GotoOp{exitOpRef});
604 }
605 }
606 ops.emplace_back(exitLab);
607 appendIfLabeled(std::get<2>(construct.t), ops);
608 ops.emplace_back(EndOp{construct});
609 linearOps.splice(linearOps.end(), ops);
610 ad.constructContextStack.pop_back();
611 return false;
612 }
613
PreFortran::burnside::flat::ControlFlowAnalyzer614 bool Pre(const parser::CaseConstruct &c) { return Multiway(c); }
PreFortran::burnside::flat::ControlFlowAnalyzer615 bool Pre(const parser::SelectRankConstruct &c) { return Multiway(c); }
PreFortran::burnside::flat::ControlFlowAnalyzer616 bool Pre(const parser::SelectTypeConstruct &c) { return Multiway(c); }
617
PreFortran::burnside::flat::ControlFlowAnalyzer618 bool Pre(const parser::WhereConstruct &c) {
619 std::list<Op> ops;
620 LabelOp label{buildNewLabel()};
621 const parser::Name *name{getName(c)};
622 ad.constructContextStack.emplace_back(
623 name, GetLabelRef(label), UnspecifiedLabel);
624 appendIfLabeled(
625 std::get<parser::Statement<parser::WhereConstructStmt>>(c.t), ops);
626 ops.emplace_back(BeginOp{c});
627 ControlFlowAnalyzer cfa{ops, ad};
628 Walk(std::get<std::list<parser::WhereBodyConstruct>>(c.t), cfa);
629 Walk(
630 std::get<std::list<parser::WhereConstruct::MaskedElsewhere>>(c.t), cfa);
631 Walk(std::get<std::optional<parser::WhereConstruct::Elsewhere>>(c.t), cfa);
632 ops.emplace_back(label);
633 appendIfLabeled(
634 std::get<parser::Statement<parser::EndWhereStmt>>(c.t), ops);
635 ops.emplace_back(EndOp{c});
636 linearOps.splice(linearOps.end(), ops);
637 ad.constructContextStack.pop_back();
638 return false;
639 }
640
PreFortran::burnside::flat::ControlFlowAnalyzer641 bool Pre(const parser::ForallConstruct &construct) {
642 std::list<Op> ops;
643 LabelOp label{buildNewLabel()};
644 const parser::Name *name{getName(construct)};
645 ad.constructContextStack.emplace_back(
646 name, GetLabelRef(label), UnspecifiedLabel);
647 appendIfLabeled(
648 std::get<parser::Statement<parser::ForallConstructStmt>>(construct.t),
649 ops);
650 ops.emplace_back(BeginOp{construct});
651 ControlFlowAnalyzer cfa{ops, ad};
652 Walk(std::get<std::list<parser::ForallBodyConstruct>>(construct.t), cfa);
653 ops.emplace_back(label);
654 appendIfLabeled(
655 std::get<parser::Statement<parser::EndForallStmt>>(construct.t), ops);
656 ops.emplace_back(EndOp{construct});
657 linearOps.splice(linearOps.end(), ops);
658 ad.constructContextStack.pop_back();
659 return false;
660 }
661
getNameFortran::burnside::flat::ControlFlowAnalyzer662 template<typename A> const parser::Name *getName(const A &a) {
663 const auto &optName{std::get<0>(std::get<0>(a.t).statement.t)};
664 return optName ? &*optName : nullptr;
665 }
666
GetLabelRefFortran::burnside::flat::ControlFlowAnalyzer667 LabelRef GetLabelRef(const LabelOp &label) {
668 label.setReferenced();
669 return label;
670 }
671
GetLabelRefFortran::burnside::flat::ControlFlowAnalyzer672 LabelRef GetLabelRef(const parser::Label &label) {
673 return FetchLabel(ad, label);
674 }
675
676 std::list<Op> &linearOps;
677 AnalysisData &ad;
678 };
679
680 } // namespace flat
681
682 template<typename A>
CreateFlatIR(const A & ptree,std::list<flat::Op> & ops,AnalysisData & ad)683 void CreateFlatIR(const A &ptree, std::list<flat::Op> &ops, AnalysisData &ad) {
684 flat::ControlFlowAnalyzer linearize{ops, ad};
685 Walk(ptree, linearize);
686 }
687
688 #define INSTANTIATE_EXPLICITLY(T) \
689 template void CreateFlatIR<parser::T>( \
690 const parser::T &, std::list<flat::Op> &, AnalysisData &)
691 INSTANTIATE_EXPLICITLY(MainProgram);
692 INSTANTIATE_EXPLICITLY(FunctionSubprogram);
693 INSTANTIATE_EXPLICITLY(SubroutineSubprogram);
694
695 } // namespace burnside
696