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