[flang] Fotran IR: part 5. Afforestation is the lowering of concrete parse trees
authorEric Schweitz <eschweitz@nvidia.com>
Wed, 20 Feb 2019 21:10:38 +0000 (13:10 -0800)
committerEric Schweitz <eschweitz@nvidia.com>
Wed, 20 Feb 2019 21:10:38 +0000 (13:10 -0800)
and semantics information into the Fortran IR.

Original-commit: flang-compiler/f18@d1dde126409d53864118b96363979d6dbcbd1b97
Reviewed-on: https://github.com/flang-compiler/f18/pull/297
Tree-same-pre-rewrite: false

flang/lib/IntermediateRepresentation/afforestation.cc [new file with mode: 0644]
flang/lib/IntermediateRepresentation/afforestation.h [new file with mode: 0644]

diff --git a/flang/lib/IntermediateRepresentation/afforestation.cc b/flang/lib/IntermediateRepresentation/afforestation.cc
new file mode 100644 (file)
index 0000000..3d1b283
--- /dev/null
@@ -0,0 +1,1963 @@
+// Copyright (c) 2019, NVIDIA CORPORATION.  All rights reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//     http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#include "afforestation.h"
+#include "builder.h"
+#include "mixin.h"
+#include "../evaluate/fold.h"
+#include "../evaluate/tools.h"
+#include "../parser/parse-tree-visitor.h"
+#include "llvm/ADT/SmallSet.h"
+#include "llvm/Support/FileSystem.h"
+#include "llvm/Support/raw_ostream.h"
+
+namespace Fortran::IntermediateRepresentation {
+
+static llvm::raw_ostream *debugChannel;
+static llvm::raw_ostream &DebugChannel() {
+  return debugChannel ? *debugChannel : llvm::errs();
+}
+static void SetDebugChannel(llvm::raw_ostream *output) {
+  debugChannel = output;
+}
+void SetDebugChannel(const std::string &filename) {
+  std::error_code ec;
+  SetDebugChannel(
+      new llvm::raw_fd_ostream(filename, ec, llvm::sys::fs::F_None));
+  CHECK(!ec);
+}
+
+namespace {
+struct LinearOp;
+
+using LinearLabelRef = unsigned;
+constexpr LinearLabelRef unspecifiedLabel{~0u};
+
+struct LinearLabelBuilder {
+  LinearLabelBuilder() : referenced(32), counter{0u} {}
+  LinearLabelRef getNext() {
+    LinearLabelRef next{counter++};
+    auto cap{referenced.capacity()};
+    if (cap < counter) referenced.reserve(2 * cap);
+    referenced[next] = false;
+    return next;
+  }
+  void setReferenced(LinearLabelRef label) { referenced[label] = true; }
+  bool isReferenced(LinearLabelRef label) const { return referenced[label]; }
+  std::vector<bool> referenced;
+  unsigned counter;
+};
+
+struct LinearLabel {
+  explicit LinearLabel(LinearLabelBuilder &builder)
+    : builder_{builder}, label_{builder.getNext()} {}
+  LinearLabel(const LinearLabel &that)
+    : builder_{that.builder_}, label_{that.label_} {}
+  LinearLabel &operator=(const LinearLabel &that) {
+    CHECK(&builder_ == &that.builder_);
+    label_ = that.label_;
+    return *this;
+  }
+  void setReferenced() const { builder_.setReferenced(label_); }
+  bool isReferenced() const { return builder_.isReferenced(label_); }
+  LinearLabelRef get() const { return label_; }
+  operator LinearLabelRef() const { return get(); }
+
+private:
+  LinearLabelBuilder &builder_;
+  LinearLabelRef label_;
+};
+
+struct LinearGoto {
+  struct LinearArtificial {};
+  LinearGoto(LinearLabelRef dest) : u{LinearArtificial{}}, target{dest} {}
+  template<typename T>
+  LinearGoto(const T &stmt, LinearLabelRef dest) : u{&stmt}, target{dest} {}
+  std::variant<const parser::CycleStmt *, const parser::ExitStmt *,
+      const parser::GotoStmt *, LinearArtificial>
+      u;
+  LinearLabelRef target;
+};
+
+struct LinearReturn
+  : public SumTypeCopyMixin<std::variant<const parser::FailImageStmt *,
+        const parser::ReturnStmt *, const parser::StopStmt *>> {
+  SUM_TYPE_COPY_MIXIN(LinearReturn)
+  template<typename T> LinearReturn(const T &stmt) : SumTypeCopyMixin{&stmt} {}
+};
+
+struct LinearConditionalGoto {
+  template<typename T>
+  LinearConditionalGoto(const T &cond, LinearLabelRef tb, LinearLabelRef fb)
+    : u{&cond}, trueLabel{tb}, falseLabel{fb} {}
+  std::variant<const parser::Statement<parser::IfThenStmt> *,
+      const parser::Statement<parser::ElseIfStmt> *, const parser::IfStmt *,
+      const parser::Statement<parser::NonLabelDoStmt> *>
+      u;
+  LinearLabelRef trueLabel;
+  LinearLabelRef falseLabel;
+};
+
+struct LinearIndirectGoto {
+  LinearIndirectGoto(
+      const semantics::Symbol *symbol, std::vector<LinearLabelRef> &&labelRefs)
+    : symbol{symbol}, labelRefs{labelRefs} {}
+  const semantics::Symbol *symbol;
+  std::vector<LinearLabelRef> labelRefs;
+};
+
+struct LinearSwitchingIO {
+  template<typename T>
+  LinearSwitchingIO(const T &io, LinearLabelRef next,
+      std::optional<LinearLabelRef> errLab,
+      std::optional<LinearLabelRef> eorLab = std::nullopt,
+      std::optional<LinearLabelRef> endLab = std::nullopt)
+    : u{&io}, next{next}, errLabel{errLab}, eorLabel{eorLab}, endLabel{endLab} {
+  }
+  std::variant<const parser::ReadStmt *, const parser::WriteStmt *,
+      const parser::WaitStmt *, const parser::OpenStmt *,
+      const parser::CloseStmt *, const parser::BackspaceStmt *,
+      const parser::EndfileStmt *, const parser::RewindStmt *,
+      const parser::FlushStmt *, const parser::InquireStmt *>
+      u;
+  LinearLabelRef next;
+  std::optional<LinearLabelRef> errLabel;
+  std::optional<LinearLabelRef> eorLabel;
+  std::optional<LinearLabelRef> endLabel;
+};
+
+struct LinearSwitch {
+  template<typename T>
+  LinearSwitch(const T &sw, const std::vector<LinearLabelRef> &refs)
+    : u{&sw}, refs{refs} {}
+  std::variant<const parser::CallStmt *, const parser::ComputedGotoStmt *,
+      const parser::ArithmeticIfStmt *, const parser::CaseConstruct *,
+      const parser::SelectRankConstruct *, const parser::SelectTypeConstruct *>
+      u;
+  const std::vector<LinearLabelRef> refs;
+};
+
+struct LinearAction {
+  LinearAction(const parser::Statement<parser::ActionStmt> &stmt) : v{&stmt} {}
+  parser::CharBlock getSource() const { return v->source; }
+
+  const parser::Statement<parser::ActionStmt> *v;
+};
+
+using ConstructVariant = std::variant<const parser::AssociateConstruct *,
+    const parser::BlockConstruct *, const parser::CaseConstruct *,
+    const parser::ChangeTeamConstruct *, const parser::CriticalConstruct *,
+    const parser::DoConstruct *, const parser::IfConstruct *,
+    const parser::SelectRankConstruct *, const parser::SelectTypeConstruct *,
+    const parser::WhereConstruct *, const parser::ForallConstruct *,
+    const parser::CompilerDirective *, const parser::OpenMPConstruct *,
+    const parser::OpenMPEndLoopDirective *>;
+
+struct LinearBeginConstruct : public SumTypeCopyMixin<ConstructVariant> {
+  SUM_TYPE_COPY_MIXIN(LinearBeginConstruct)
+  template<typename T>
+  LinearBeginConstruct(const T &c) : SumTypeCopyMixin{&c} {}
+};
+struct LinearEndConstruct : public SumTypeCopyMixin<ConstructVariant> {
+  SUM_TYPE_COPY_MIXIN(LinearEndConstruct)
+  template<typename T> LinearEndConstruct(const T &c) : SumTypeCopyMixin{&c} {}
+};
+
+template<typename CONSTRUCT>
+const char *GetConstructName(const CONSTRUCT &construct) {
+  return std::visit(
+      common::visitors{
+          [](const parser::AssociateConstruct *) { return "ASSOCIATE"; },
+          [](const parser::BlockConstruct *) { return "BLOCK"; },
+          [](const parser::CaseConstruct *) { return "SELECT CASE"; },
+          [](const parser::ChangeTeamConstruct *) { return "CHANGE TEAM"; },
+          [](const parser::CriticalConstruct *) { return "CRITICAL"; },
+          [](const parser::DoConstruct *) { return "DO"; },
+          [](const parser::IfConstruct *) { return "IF"; },
+          [](const parser::SelectRankConstruct *) { return "SELECT RANK"; },
+          [](const parser::SelectTypeConstruct *) { return "SELECT TYPE"; },
+          [](const parser::WhereConstruct *) { return "WHERE"; },
+          [](const parser::ForallConstruct *) { return "FORALL"; },
+          [](const parser::CompilerDirective *) { return "<directive>"; },
+          [](const parser::OpenMPConstruct *) { return "<open-mp>"; },
+          [](const parser::OpenMPEndLoopDirective *) {
+            return "<open-mp-end-loop>";
+          }},
+      construct.u);
+}
+
+struct AnalysisData {
+  std::map<parser::Label, LinearLabel> labelMap;
+  std::vector<std::tuple<const parser::Name *, LinearLabelRef, LinearLabelRef>>
+      nameStack;
+  LinearLabelBuilder labelBuilder;
+  std::map<const semantics::Symbol *, std::set<parser::Label>> assignMap;
+};
+
+void AddAssign(AnalysisData &ad, const semantics::Symbol *symbol,
+    const parser::Label &label) {
+  ad.assignMap[symbol].insert(label);
+}
+std::vector<LinearLabelRef> GetAssign(
+    AnalysisData &ad, const semantics::Symbol *symbol) {
+  std::vector<LinearLabelRef> result;
+  for (auto lab : ad.assignMap[symbol]) {
+    result.emplace_back(lab);
+  }
+  return result;
+}
+LinearLabel BuildNewLabel(AnalysisData &ad) {
+  return LinearLabel{ad.labelBuilder};
+}
+LinearLabel FetchLabel(AnalysisData &ad, const parser::Label &label) {
+  auto iter{ad.labelMap.find(label)};
+  if (iter == ad.labelMap.end()) {
+    LinearLabel ll{ad.labelBuilder};
+    ll.setReferenced();
+    ad.labelMap.insert({label, ll});
+    return ll;
+  }
+  return iter->second;
+}
+std::tuple<const parser::Name *, LinearLabelRef, LinearLabelRef> FindStack(
+    const std::vector<std::tuple<const parser::Name *, LinearLabelRef,
+        LinearLabelRef>> &stack,
+    const parser::Name *key) {
+  for (auto iter{stack.rbegin()}, iend{stack.rend()}; iter != iend; ++iter) {
+    if (std::get<0>(*iter) == key) return *iter;
+  }
+  SEMANTICS_FAILED("construct name not on stack");
+  return {};
+}
+template<typename T> parser::Label GetErr(const T &stmt) {
+  if constexpr (std::is_same_v<T, parser::ReadStmt> ||
+      std::is_same_v<T, parser::WriteStmt>) {
+    for (const auto &control : stmt.controls) {
+      if (std::holds_alternative<parser::ErrLabel>(control.u)) {
+        return std::get<parser::ErrLabel>(control.u).v;
+      }
+    }
+  }
+  if constexpr (std::is_same_v<T, parser::WaitStmt> ||
+      std::is_same_v<T, parser::OpenStmt> ||
+      std::is_same_v<T, parser::CloseStmt> ||
+      std::is_same_v<T, parser::BackspaceStmt> ||
+      std::is_same_v<T, parser::EndfileStmt> ||
+      std::is_same_v<T, parser::RewindStmt> ||
+      std::is_same_v<T, parser::FlushStmt>) {
+    for (const auto &spec : stmt.v) {
+      if (std::holds_alternative<parser::ErrLabel>(spec.u)) {
+        return std::get<parser::ErrLabel>(spec.u).v;
+      }
+    }
+  }
+  if constexpr (std::is_same_v<T, parser::InquireStmt>) {
+    for (const auto &spec : std::get<std::list<parser::InquireSpec>>(stmt.u)) {
+      if (std::holds_alternative<parser::ErrLabel>(spec.u)) {
+        return std::get<parser::ErrLabel>(spec.u).v;
+      }
+    }
+  }
+  return 0;
+}
+template<typename T> parser::Label GetEor(const T &stmt) {
+  if constexpr (std::is_same_v<T, parser::ReadStmt> ||
+      std::is_same_v<T, parser::WriteStmt>) {
+    for (const auto &control : stmt.controls) {
+      if (std::holds_alternative<parser::EorLabel>(control.u)) {
+        return std::get<parser::EorLabel>(control.u).v;
+      }
+    }
+  }
+  if constexpr (std::is_same_v<T, parser::WaitStmt>) {
+    for (const auto &waitSpec : stmt.v) {
+      if (std::holds_alternative<parser::EorLabel>(waitSpec.u)) {
+        return std::get<parser::EorLabel>(waitSpec.u).v;
+      }
+    }
+  }
+  return 0;
+}
+template<typename T> parser::Label GetEnd(const T &stmt) {
+  if constexpr (std::is_same_v<T, parser::ReadStmt> ||
+      std::is_same_v<T, parser::WriteStmt>) {
+    for (const auto &control : stmt.controls) {
+      if (std::holds_alternative<parser::EndLabel>(control.u)) {
+        return std::get<parser::EndLabel>(control.u).v;
+      }
+    }
+  }
+  if constexpr (std::is_same_v<T, parser::WaitStmt>) {
+    for (const auto &waitSpec : stmt.v) {
+      if (std::holds_alternative<parser::EndLabel>(waitSpec.u)) {
+        return std::get<parser::EndLabel>(waitSpec.u).v;
+      }
+    }
+  }
+  return 0;
+}
+template<typename T>
+void errLabelSpec(const T &s, std::list<LinearOp> &ops,
+    const parser::Statement<parser::ActionStmt> &ec, AnalysisData &ad) {
+  if (auto errLab{GetErr(s)}) {
+    std::optional<LinearLabelRef> errRef{FetchLabel(ad, errLab).get()};
+    LinearLabel next{BuildNewLabel(ad)};
+    ops.emplace_back(LinearSwitchingIO{s, next, errRef});
+    ops.emplace_back(next);
+  } else {
+    ops.emplace_back(LinearAction{ec});
+  }
+}
+template<typename T>
+void threeLabelSpec(const T &s, std::list<LinearOp> &ops,
+    const parser::Statement<parser::ActionStmt> &ec, AnalysisData &ad) {
+  auto errLab{GetErr(s)};
+  auto eorLab{GetEor(s)};
+  auto endLab{GetEnd(s)};
+  if (errLab || eorLab || endLab) {
+    std::optional<LinearLabelRef> errRef{std::nullopt};
+    if (errLab) errRef = FetchLabel(ad, errLab).get();
+    std::optional<LinearLabelRef> eorRef{std::nullopt};
+    if (eorLab) eorRef = FetchLabel(ad, eorLab).get();
+    std::optional<LinearLabelRef> endRef{std::nullopt};
+    if (endLab) endRef = FetchLabel(ad, endLab).get();
+    LinearLabel next{BuildNewLabel(ad)};
+    ops.emplace_back(LinearSwitchingIO{s, next, errRef, eorRef, endRef});
+    ops.emplace_back(next);
+  } else {
+    ops.emplace_back(LinearAction{ec});
+  }
+}
+template<typename T>
+std::vector<LinearLabelRef> toLabelRef(AnalysisData &ad, const T &labels) {
+  std::vector<LinearLabelRef> result;
+  for (auto label : labels) {
+    result.emplace_back(FetchLabel(ad, label).get());
+  }
+  CHECK(result.size() == labels.size());
+  return result;
+}
+
+bool hasAltReturns(const parser::CallStmt &callStmt) {
+  const auto &args{std::get<std::list<parser::ActualArgSpec>>(callStmt.v.t)};
+  for (const auto &arg : args) {
+    const auto &actual{std::get<parser::ActualArg>(arg.t)};
+    if (std::holds_alternative<parser::AltReturnSpec>(actual.u)) {
+      return true;
+    }
+  }
+  return false;
+}
+std::list<parser::Label> getAltReturnLabels(const parser::Call &call) {
+  std::list<parser::Label> result;
+  const auto &args{std::get<std::list<parser::ActualArgSpec>>(call.t)};
+  for (const auto &arg : args) {
+    const auto &actual{std::get<parser::ActualArg>(arg.t)};
+    if (const auto *p{std::get_if<parser::AltReturnSpec>(&actual.u)}) {
+      result.push_back(p->v);
+    }
+  }
+  return result;
+}
+LinearLabelRef NearestEnclosingDoConstruct(AnalysisData &ad) {
+  for (auto iterator{ad.nameStack.rbegin()}, endIterator{ad.nameStack.rend()};
+       iterator != endIterator; ++iterator) {
+    auto labelReference{std::get<2>(*iterator)};
+    if (labelReference != unspecifiedLabel) {
+      return labelReference;
+    }
+  }
+  SEMANTICS_FAILED("CYCLE|EXIT not in loop");
+  return unspecifiedLabel;
+}
+
+struct LinearOp
+  : public SumTypeMixin<std::variant<LinearLabel, LinearGoto, LinearReturn,
+        LinearConditionalGoto, LinearSwitchingIO, LinearSwitch, LinearAction,
+        LinearBeginConstruct, LinearEndConstruct, LinearIndirectGoto>> {
+  template<typename T> LinearOp(const T &thing) : SumTypeMixin{thing} {}
+  void dump() const;
+  static void Build(std::list<LinearOp> &ops,
+      const parser::Statement<parser::ActionStmt> &ec, AnalysisData &ad) {
+    std::visit(
+        common::visitors{
+            [&](const auto &s) { ops.emplace_back(LinearAction{ec}); },
+            [&](const common::Indirection<parser::CallStmt> &s) {
+              if (hasAltReturns(*s)) {
+                auto next{BuildNewLabel(ad)};
+                auto labels{toLabelRef(ad, getAltReturnLabels(s->v))};
+                labels.push_back(next);
+                ops.emplace_back(LinearSwitch{*s, std::move(labels)});
+                ops.emplace_back(next);
+              } else {
+                ops.emplace_back(LinearAction{ec});
+              }
+            },
+            [&](const common::Indirection<parser::AssignStmt> &s) {
+              AddAssign(ad, std::get<parser::Name>(s->t).symbol,
+                  std::get<parser::Label>(s->t));
+              ops.emplace_back(LinearAction{ec});
+            },
+            [&](const common::Indirection<parser::CycleStmt> &s) {
+              ops.emplace_back(LinearGoto{*s,
+                  s->v ? std::get<2>(FindStack(ad.nameStack, &*s->v))
+                       : NearestEnclosingDoConstruct(ad)});
+            },
+            [&](const common::Indirection<parser::ExitStmt> &s) {
+              ops.emplace_back(LinearGoto{*s,
+                  s->v ? std::get<1>(FindStack(ad.nameStack, &*s->v))
+                       : NearestEnclosingDoConstruct(ad)});
+            },
+            [&](const common::Indirection<parser::GotoStmt> &s) {
+              ops.emplace_back(LinearGoto{*s, FetchLabel(ad, s->v).get()});
+            },
+            [&](const parser::FailImageStmt &s) {
+              ops.emplace_back(LinearReturn{s});
+            },
+            [&](const common::Indirection<parser::ReturnStmt> &s) {
+              ops.emplace_back(LinearReturn{*s});
+            },
+            [&](const common::Indirection<parser::StopStmt> &s) {
+              ops.emplace_back(LinearAction{ec});
+              ops.emplace_back(LinearReturn{*s});
+            },
+            [&](const common::Indirection<const parser::ReadStmt> &s) {
+              threeLabelSpec(*s, ops, ec, ad);
+            },
+            [&](const common::Indirection<const parser::WriteStmt> &s) {
+              threeLabelSpec(*s, ops, ec, ad);
+            },
+            [&](const common::Indirection<const parser::WaitStmt> &s) {
+              threeLabelSpec(*s, ops, ec, ad);
+            },
+            [&](const common::Indirection<const parser::OpenStmt> &s) {
+              errLabelSpec(*s, ops, ec, ad);
+            },
+            [&](const common::Indirection<const parser::CloseStmt> &s) {
+              errLabelSpec(*s, ops, ec, ad);
+            },
+            [&](const common::Indirection<const parser::BackspaceStmt> &s) {
+              errLabelSpec(*s, ops, ec, ad);
+            },
+            [&](const common::Indirection<const parser::EndfileStmt> &s) {
+              errLabelSpec(*s, ops, ec, ad);
+            },
+            [&](const common::Indirection<const parser::RewindStmt> &s) {
+              errLabelSpec(*s, ops, ec, ad);
+            },
+            [&](const common::Indirection<const parser::FlushStmt> &s) {
+              errLabelSpec(*s, ops, ec, ad);
+            },
+            [&](const common::Indirection<const parser::InquireStmt> &s) {
+              errLabelSpec(*s, ops, ec, ad);
+            },
+            [&](const common::Indirection<parser::ComputedGotoStmt> &s) {
+              auto next{BuildNewLabel(ad)};
+              auto labels{
+                  toLabelRef(ad, std::get<std::list<parser::Label>>(s->t))};
+              labels.push_back(next);
+              ops.emplace_back(LinearSwitch{*s, std::move(labels)});
+              ops.emplace_back(next);
+            },
+            [&](const common::Indirection<parser::ArithmeticIfStmt> &s) {
+              ops.emplace_back(LinearSwitch{*s,
+                  toLabelRef(ad,
+                      std::list{std::get<1>(s->t), std::get<2>(s->t),
+                          std::get<3>(s->t)})});
+            },
+            [&](const common::Indirection<parser::AssignedGotoStmt> &s) {
+              ops.emplace_back(LinearIndirectGoto{
+                  std::get<parser::Name>(s->t).symbol,
+                  toLabelRef(ad, std::get<std::list<parser::Label>>(s->t))});
+            },
+            [&](const common::Indirection<parser::IfStmt> &s) {
+              auto then{BuildNewLabel(ad)};
+              auto endif{BuildNewLabel(ad)};
+              ops.emplace_back(LinearConditionalGoto{*s, then, endif});
+              ops.emplace_back(then);
+              ops.emplace_back(LinearAction{ec});
+              ops.emplace_back(endif);
+            },
+        },
+        ec.statement.u);
+  }
+};
+
+template<typename STMTTYPE, typename CT>
+Evaluation GetSwitchSelector(const CT *selectConstruct) {
+  const auto &selector{std::get<parser::Selector>(
+      std::get<parser::Statement<STMTTYPE>>(selectConstruct->t).statement.t)};
+  return std::visit(common::visitors{
+                        [](const parser::Expr &expression) {
+                          return Evaluation{expression.typedExpr.get()};
+                        },
+                        [](const parser::Variable &variable) {
+                          return Evaluation{&variable};
+                        },
+                    },
+      selector.u);
+}
+Evaluation GetSwitchRankSelector(
+    const parser::SelectRankConstruct *selectRankConstruct) {
+  return GetSwitchSelector<parser::SelectRankStmt>(selectRankConstruct);
+}
+Evaluation GetSwitchTypeSelector(
+    const parser::SelectTypeConstruct *selectTypeConstruct) {
+  return GetSwitchSelector<parser::SelectTypeStmt>(selectTypeConstruct);
+}
+Evaluation GetSwitchCaseSelector(const parser::CaseConstruct *caseConstruct) {
+  return Evaluation{std::get<parser::Scalar<parser::Expr>>(
+      std::get<parser::Statement<parser::SelectCaseStmt>>(caseConstruct->t)
+          .statement.t)
+                        .thing.typedExpr.get()};
+}
+template<typename STMTTYPE, typename CT>
+const std::optional<parser::Name> &GetSwitchAssociateName(
+    const CT *selectConstruct) {
+  return std::get<1>(
+      std::get<parser::Statement<STMTTYPE>>(selectConstruct->t).statement.t);
+}
+template<typename CONSTRUCT, typename GSF>
+void DumpSwitchWithSelector(
+    const CONSTRUCT *construct, char const *const name, GSF getSelector) {
+  auto selector{getSelector(construct)};
+  DebugChannel() << name << "(" << selector.dump();
+}
+
+void LinearOp::dump() const {
+  std::visit(
+      common::visitors{
+          [](const LinearLabel &t) {
+            DebugChannel() << "label: " << t.get() << '\n';
+          },
+          [](const LinearGoto &t) {
+            DebugChannel() << "goto " << t.target << '\n';
+          },
+          [](const LinearReturn &) { DebugChannel() << "return\n"; },
+          [](const LinearConditionalGoto &t) {
+            DebugChannel() << "cbranch (?) " << t.trueLabel << ' '
+                           << t.falseLabel << '\n';
+          },
+          [](const LinearSwitchingIO &t) {
+            DebugChannel() << "io-op";
+            if (t.errLabel) DebugChannel() << " ERR=" << t.errLabel.value();
+            if (t.eorLabel) DebugChannel() << " EOR=" << t.eorLabel.value();
+            if (t.endLabel) DebugChannel() << " END=" << t.endLabel.value();
+            DebugChannel() << '\n';
+          },
+          [](const LinearSwitch &lswitch) {
+            DebugChannel() << "switch-";
+            std::visit(
+                common::visitors{
+                    [](const parser::CaseConstruct *caseConstruct) {
+                      DumpSwitchWithSelector(
+                          caseConstruct, "case", GetSwitchCaseSelector);
+                    },
+                    [](const parser::SelectRankConstruct *selectRankConstruct) {
+                      DumpSwitchWithSelector(
+                          selectRankConstruct, "rank", GetSwitchRankSelector);
+                    },
+                    [](const parser::SelectTypeConstruct *selectTypeConstruct) {
+                      DumpSwitchWithSelector(
+                          selectTypeConstruct, "type", GetSwitchTypeSelector);
+                    },
+                    [](const parser::ComputedGotoStmt *computedGotoStmt) {
+                      DebugChannel() << "igoto(?";
+                    },
+                    [](const parser::ArithmeticIfStmt *arithmeticIfStmt) {
+                      DebugChannel() << "<=>(?";
+                    },
+                    [](const parser::CallStmt *callStmt) {
+                      DebugChannel() << "alt-return(?";
+                    },
+                },
+                lswitch.u);
+            DebugChannel() << ") [...]\n";
+          },
+          [](const LinearAction &t) {
+            DebugChannel() << "action: " << t.getSource().ToString() << '\n';
+          },
+          [](const LinearBeginConstruct &construct) {
+            DebugChannel() << "construct-" << GetConstructName(construct)
+                           << " {\n";
+          },
+          [](const LinearEndConstruct &construct) {
+            DebugChannel() << "} construct-" << GetConstructName(construct)
+                           << "\n";
+          },
+          [](const LinearIndirectGoto &) { DebugChannel() << "igoto\n"; },
+      },
+      u);
+}
+}  // end namespace
+
+struct ControlFlowAnalyzer {
+  explicit ControlFlowAnalyzer(std::list<LinearOp> &ops, AnalysisData &ad)
+    : linearOps{ops}, ad{ad} {}
+
+  LinearLabel buildNewLabel() { return BuildNewLabel(ad); }
+  LinearOp findLabel(const parser::Label &lab) {
+    auto iter{ad.labelMap.find(lab)};
+    if (iter == ad.labelMap.end()) {
+      LinearLabel ll{ad.labelBuilder};
+      ad.labelMap.insert({lab, ll});
+      return {ll};
+    }
+    return {iter->second};
+  }
+  template<typename A> constexpr bool Pre(const A &) { return true; }
+  template<typename A> constexpr void Post(const A &) {}
+  template<typename A> bool Pre(const parser::Statement<A> &stmt) {
+    if (stmt.label) {
+      linearOps.emplace_back(findLabel(*stmt.label));
+    }
+    if constexpr (std::is_same_v<A, parser::ActionStmt>) {
+      LinearOp::Build(linearOps, stmt, ad);
+    }
+    return true;
+  }
+
+  // named constructs
+  template<typename T> bool linearConstruct(const T &construct) {
+    std::list<LinearOp> ops;
+    LinearLabel label{buildNewLabel()};
+    const parser::Name *name{getName(construct)};
+    ad.nameStack.emplace_back(name, GetLabelRef(label), unspecifiedLabel);
+    ops.emplace_back(LinearBeginConstruct{construct});
+    ControlFlowAnalyzer cfa{ops, ad};
+    Walk(std::get<parser::Block>(construct.t), cfa);
+    ops.emplace_back(label);
+    ops.emplace_back(LinearEndConstruct{construct});
+    linearOps.splice(linearOps.end(), ops);
+    ad.nameStack.pop_back();
+    return false;
+  }
+  bool Pre(const parser::AssociateConstruct &c) { return linearConstruct(c); }
+  bool Pre(const parser::ChangeTeamConstruct &c) { return linearConstruct(c); }
+  bool Pre(const parser::CriticalConstruct &c) { return linearConstruct(c); }
+  bool Pre(const parser::BlockConstruct &construct) {
+    std::list<LinearOp> ops;
+    LinearLabel label{buildNewLabel()};
+    const auto &optName{
+        std::get<parser::Statement<parser::BlockStmt>>(construct.t)
+            .statement.v};
+    const parser::Name *name{optName ? &*optName : nullptr};
+    ad.nameStack.emplace_back(name, GetLabelRef(label), unspecifiedLabel);
+    ops.emplace_back(LinearBeginConstruct{construct});
+    ControlFlowAnalyzer cfa{ops, ad};
+    Walk(std::get<parser::Block>(construct.t), cfa);
+    ops.emplace_back(LinearEndConstruct{construct});
+    ops.emplace_back(label);
+    linearOps.splice(linearOps.end(), ops);
+    ad.nameStack.pop_back();
+    return false;
+  }
+  bool Pre(const parser::DoConstruct &construct) {
+    std::list<LinearOp> ops;
+    LinearLabel backedgeLab{buildNewLabel()};
+    LinearLabel entryLab{buildNewLabel()};
+    LinearLabel exitLab{buildNewLabel()};
+    const parser::Name *name{getName(construct)};
+    LinearLabelRef exitOpRef{GetLabelRef(exitLab)};
+    ad.nameStack.emplace_back(name, exitOpRef, GetLabelRef(backedgeLab));
+    ops.emplace_back(LinearBeginConstruct{construct});
+    ops.emplace_back(backedgeLab);
+    ops.emplace_back(LinearConditionalGoto{
+        std::get<parser::Statement<parser::NonLabelDoStmt>>(construct.t),
+        GetLabelRef(entryLab), exitOpRef});
+    ops.push_back(entryLab);
+    ControlFlowAnalyzer cfa{ops, ad};
+    Walk(std::get<parser::Block>(construct.t), cfa);
+    ops.emplace_back(LinearGoto{GetLabelRef(backedgeLab)});
+    ops.emplace_back(LinearEndConstruct{construct});
+    ops.emplace_back(exitLab);
+    linearOps.splice(linearOps.end(), ops);
+    ad.nameStack.pop_back();
+    return false;
+  }
+  bool Pre(const parser::IfConstruct &construct) {
+    std::list<LinearOp> ops;
+    LinearLabel thenLab{buildNewLabel()};
+    LinearLabel elseLab{buildNewLabel()};
+    LinearLabel exitLab{buildNewLabel()};
+    const parser::Name *name{getName(construct)};
+    ad.nameStack.emplace_back(name, GetLabelRef(exitLab), unspecifiedLabel);
+    ops.emplace_back(LinearBeginConstruct{construct});
+    ops.emplace_back(LinearConditionalGoto{
+        std::get<parser::Statement<parser::IfThenStmt>>(construct.t),
+        GetLabelRef(thenLab), GetLabelRef(elseLab)});
+    ops.emplace_back(thenLab);
+    ControlFlowAnalyzer cfa{ops, ad};
+    Walk(std::get<parser::Block>(construct.t), cfa);
+    LinearLabelRef exitOpRef{GetLabelRef(exitLab)};
+    ops.emplace_back(LinearGoto{exitOpRef});
+    for (const auto &elseIfBlock :
+        std::get<std::list<parser::IfConstruct::ElseIfBlock>>(construct.t)) {
+      ops.emplace_back(elseLab);
+      LinearLabel newThenLab{buildNewLabel()};
+      LinearLabel newElseLab{buildNewLabel()};
+      ops.emplace_back(LinearConditionalGoto{
+          std::get<parser::Statement<parser::ElseIfStmt>>(elseIfBlock.t),
+          GetLabelRef(newThenLab), GetLabelRef(newElseLab)});
+      ops.emplace_back(newThenLab);
+      Walk(std::get<parser::Block>(elseIfBlock.t), cfa);
+      ops.emplace_back(LinearGoto{exitOpRef});
+      elseLab = newElseLab;
+    }
+    ops.emplace_back(elseLab);
+    if (const auto &optElseBlock{
+            std::get<std::optional<parser::IfConstruct::ElseBlock>>(
+                construct.t)}) {
+      Walk(std::get<parser::Block>(optElseBlock->t), cfa);
+    }
+    ops.emplace_back(LinearGoto{exitOpRef});
+    ops.emplace_back(exitLab);
+    ops.emplace_back(LinearEndConstruct{construct});
+    linearOps.splice(linearOps.end(), ops);
+    ad.nameStack.pop_back();
+    return false;
+  }
+  template<typename A,
+      typename B = std::conditional_t<std::is_same_v<A, parser::CaseConstruct>,
+          parser::CaseConstruct::Case,
+          std::conditional_t<std::is_same_v<A, parser::SelectRankConstruct>,
+              parser::SelectRankConstruct::RankCase,
+              std::conditional_t<std::is_same_v<A, parser::SelectTypeConstruct>,
+                  parser::SelectTypeConstruct::TypeCase, void>>>>
+  bool Multiway(const A &construct) {
+    std::list<LinearOp> ops;
+    LinearLabel exitLab{buildNewLabel()};
+    const parser::Name *name{getName(construct)};
+    ad.nameStack.emplace_back(name, GetLabelRef(exitLab), unspecifiedLabel);
+    ops.emplace_back(LinearBeginConstruct{construct});
+    const auto N{std::get<std::list<B>>(construct.t).size()};
+    LinearLabelRef exitOpRef{GetLabelRef(exitLab)};
+    if (N > 0) {
+      typename std::list<B>::size_type i;
+      std::vector<LinearLabel> toLabels;
+      for (i = 0; i != N; ++i) {
+        toLabels.emplace_back(buildNewLabel());
+      }
+      std::vector<LinearLabelRef> targets;
+      for (i = 0; i != N; ++i) {
+        targets.emplace_back(GetLabelRef(toLabels[i]));
+      }
+      ops.emplace_back(LinearSwitch{construct, targets});
+      ControlFlowAnalyzer cfa{ops, ad};
+      i = 0;
+      for (const auto &caseBlock : std::get<std::list<B>>(construct.t)) {
+        ops.emplace_back(toLabels[i++]);
+        Walk(std::get<parser::Block>(caseBlock.t), cfa);
+        ops.emplace_back(LinearGoto{exitOpRef});
+      }
+    }
+    ops.emplace_back(exitLab);
+    ops.emplace_back(LinearEndConstruct{construct});
+    linearOps.splice(linearOps.end(), ops);
+    ad.nameStack.pop_back();
+    return false;
+  }
+  bool Pre(const parser::CaseConstruct &c) { return Multiway(c); }
+  bool Pre(const parser::SelectRankConstruct &c) { return Multiway(c); }
+  bool Pre(const parser::SelectTypeConstruct &c) { return Multiway(c); }
+  bool Pre(const parser::WhereConstruct &c) {
+    std::list<LinearOp> ops;
+    LinearLabel label{buildNewLabel()};
+    const parser::Name *name{getName(c)};
+    ad.nameStack.emplace_back(name, GetLabelRef(label), unspecifiedLabel);
+    ops.emplace_back(LinearBeginConstruct{c});
+    ControlFlowAnalyzer cfa{ops, ad};
+    Walk(std::get<std::list<parser::WhereBodyConstruct>>(c.t), cfa);
+    Walk(
+        std::get<std::list<parser::WhereConstruct::MaskedElsewhere>>(c.t), cfa);
+    Walk(std::get<std::optional<parser::WhereConstruct::Elsewhere>>(c.t), cfa);
+    ops.emplace_back(label);
+    ops.emplace_back(LinearEndConstruct{c});
+    linearOps.splice(linearOps.end(), ops);
+    ad.nameStack.pop_back();
+    return false;
+  }
+  bool Pre(const parser::ForallConstruct &construct) {
+    std::list<LinearOp> ops;
+    LinearLabel label{buildNewLabel()};
+    const parser::Name *name{getName(construct)};
+    ad.nameStack.emplace_back(name, GetLabelRef(label), unspecifiedLabel);
+    ops.emplace_back(LinearBeginConstruct{construct});
+    ControlFlowAnalyzer cfa{ops, ad};
+    Walk(std::get<std::list<parser::ForallBodyConstruct>>(construct.t), cfa);
+    ops.emplace_back(label);
+    ops.emplace_back(LinearEndConstruct{construct});
+    linearOps.splice(linearOps.end(), ops);
+    ad.nameStack.pop_back();
+    return false;
+  }
+  template<typename A> const parser::Name *getName(const A &a) {
+    const auto &optName{std::get<0>(std::get<0>(a.t).statement.t)};
+    return optName ? &*optName : nullptr;
+  }
+  LinearLabelRef GetLabelRef(const LinearLabel &label) {
+    label.setReferenced();
+    return label;
+  }
+  LinearLabelRef GetLabelRef(const parser::Label &label) {
+    return FetchLabel(ad, label);
+  }
+
+  std::list<LinearOp> &linearOps;
+  AnalysisData &ad;
+};
+
+struct SwitchArguments {
+  Evaluation exp;
+  LinearLabelRef defLab;
+  std::vector<SwitchStmt::ValueType> values;
+  std::vector<LinearLabelRef> labels;
+};
+struct SwitchCaseArguments {
+  Evaluation exp;
+  LinearLabelRef defLab;
+  std::vector<SwitchCaseStmt::ValueType> ranges;
+  std::vector<LinearLabelRef> labels;
+};
+struct SwitchRankArguments {
+  Evaluation exp;
+  LinearLabelRef defLab;
+  std::vector<SwitchRankStmt::ValueType> ranks;
+  std::vector<LinearLabelRef> labels;
+};
+struct SwitchTypeArguments {
+  Evaluation exp;
+  LinearLabelRef defLab;
+  std::vector<SwitchTypeStmt::ValueType> types;
+  std::vector<LinearLabelRef> labels;
+};
+
+template<typename T>
+static bool IsDefault(const typename T::ValueType &valueType) {
+  return std::holds_alternative<typename T::Default>(valueType);
+}
+template<typename T>
+void cleanupSwitchPairs(LinearLabelRef &defLab,
+    std::vector<typename T::ValueType> &values,
+    std::vector<LinearLabelRef> &labels) {
+  CHECK(values.size() == labels.size());
+  for (std::size_t i{0}, len{values.size()}; i < len; ++i) {
+    if (IsDefault<T>(values[i])) {
+      defLab = labels[i];
+      for (std::size_t j{i}; j < len - 1; ++j) {
+        values[j] = values[j + 1];
+        labels[j] = labels[j + 1];
+      }
+      values.pop_back();
+      labels.pop_back();
+      break;
+    }
+  }
+}
+
+static std::vector<SwitchCaseStmt::ValueType> populateSwitchValues(
+    const std::list<parser::CaseConstruct::Case> &list) {
+  std::vector<SwitchCaseStmt::ValueType> result;
+  for (auto &v : list) {
+    auto &caseSelector{std::get<parser::CaseSelector>(
+        std::get<parser::Statement<parser::CaseStmt>>(v.t).statement.t)};
+    if (std::holds_alternative<parser::Default>(caseSelector.u)) {
+      result.emplace_back(SwitchCaseStmt::Default{});
+    } else {
+      std::vector<SwitchCaseStmt::RangeAlternative> valueList;
+      for (auto &r :
+          std::get<std::list<parser::CaseValueRange>>(caseSelector.u)) {
+        std::visit(
+            common::visitors{
+                [&](const parser::CaseValue &caseValue) {
+                  valueList.emplace_back(SwitchCaseStmt::Exactly{
+                      caseValue.thing.thing->typedExpr.get()});
+                },
+                [&](const parser::CaseValueRange::Range &range) {
+                  if (range.lower.has_value()) {
+                    if (range.upper.has_value()) {
+                      valueList.emplace_back(SwitchCaseStmt::InclusiveRange{
+                          range.lower->thing.thing->typedExpr.get(),
+                          range.upper->thing.thing->typedExpr.get()});
+                    } else {
+                      valueList.emplace_back(SwitchCaseStmt::InclusiveAbove{
+                          range.lower->thing.thing->typedExpr.get()});
+                    }
+                  } else {
+                    valueList.emplace_back(SwitchCaseStmt::InclusiveBelow{
+                        range.upper->thing.thing->typedExpr.get()});
+                  }
+                },
+            },
+            r.u);
+      }
+      result.emplace_back(valueList);
+    }
+  }
+  return result;
+}
+static std::vector<SwitchRankStmt::ValueType> populateSwitchValues(
+    const std::list<parser::SelectRankConstruct::RankCase> &list) {
+  std::vector<SwitchRankStmt::ValueType> result;
+  for (auto &v : list) {
+    auto &rank{std::get<parser::SelectRankCaseStmt::Rank>(
+        std::get<parser::Statement<parser::SelectRankCaseStmt>>(v.t)
+            .statement.t)};
+    std::visit(common::visitors{
+                   [&](const parser::ScalarIntConstantExpr &expression) {
+                     result.emplace_back(SwitchRankStmt::Exactly{
+                         expression.thing.thing.thing->typedExpr.get()});
+                   },
+                   [&](const parser::Star &) {
+                     result.emplace_back(SwitchRankStmt::AssumedSize{});
+                   },
+                   [&](const parser::Default &) {
+                     result.emplace_back(SwitchRankStmt::Default{});
+                   },
+               },
+        rank.u);
+  }
+  return result;
+}
+static std::vector<SwitchTypeStmt::ValueType> populateSwitchValues(
+    const std::list<parser::SelectTypeConstruct::TypeCase> &list) {
+  std::vector<SwitchTypeStmt::ValueType> result;
+  for (auto &v : list) {
+    auto &guard{std::get<parser::TypeGuardStmt::Guard>(
+        std::get<parser::Statement<parser::TypeGuardStmt>>(v.t).statement.t)};
+    std::visit(common::visitors{
+                   [&](const parser::TypeSpec &typeSpec) {
+                     result.emplace_back(
+                         SwitchTypeStmt::TypeSpec{typeSpec.declTypeSpec});
+                   },
+                   [&](const parser::DerivedTypeSpec &derivedTypeSpec) {
+                     result.emplace_back(
+                         SwitchTypeStmt::DerivedTypeSpec{nullptr /*FIXME*/});
+                   },
+                   [&](const parser::Default &) {
+                     result.emplace_back(SwitchTypeStmt::Default{});
+                   },
+               },
+        guard.u);
+  }
+  return result;
+}
+static SwitchCaseArguments ComposeSwitchCaseArguments(
+    const parser::CaseConstruct *caseConstruct,
+    const std::vector<LinearLabelRef> &refs) {
+  auto &cases{
+      std::get<std::list<parser::CaseConstruct::Case>>(caseConstruct->t)};
+  SwitchCaseArguments result{GetSwitchCaseSelector(caseConstruct),
+      unspecifiedLabel, populateSwitchValues(cases), std::move(refs)};
+  cleanupSwitchPairs<SwitchCaseStmt>(
+      result.defLab, result.ranges, result.labels);
+  return result;
+}
+static SwitchRankArguments ComposeSwitchRankArguments(
+    const parser::SelectRankConstruct *selectRankConstruct,
+    const std::vector<LinearLabelRef> &refs) {
+  auto &ranks{std::get<std::list<parser::SelectRankConstruct::RankCase>>(
+      selectRankConstruct->t)};
+  SwitchRankArguments result{GetSwitchRankSelector(selectRankConstruct),
+      unspecifiedLabel, populateSwitchValues(ranks), std::move(refs)};
+  if (auto &name{GetSwitchAssociateName<parser::SelectRankStmt>(
+          selectRankConstruct)}) {
+    (void)name;  // get rid of warning
+    // TODO: handle associate-name -> Add an assignment stmt?
+  }
+  cleanupSwitchPairs<SwitchRankStmt>(
+      result.defLab, result.ranks, result.labels);
+  return result;
+}
+static SwitchTypeArguments ComposeSwitchTypeArguments(
+    const parser::SelectTypeConstruct *selectTypeConstruct,
+    const std::vector<LinearLabelRef> &refs) {
+  auto &types{std::get<std::list<parser::SelectTypeConstruct::TypeCase>>(
+      selectTypeConstruct->t)};
+  SwitchTypeArguments result{GetSwitchTypeSelector(selectTypeConstruct),
+      unspecifiedLabel, populateSwitchValues(types), std::move(refs)};
+  if (auto &name{GetSwitchAssociateName<parser::SelectTypeStmt>(
+          selectTypeConstruct)}) {
+    (void)name;  // get rid of warning
+    // TODO: handle associate-name -> Add an assignment stmt?
+  }
+  cleanupSwitchPairs<SwitchTypeStmt>(
+      result.defLab, result.types, result.labels);
+  return result;
+}
+
+static void buildMultiwayDefaultNext(SwitchArguments &result) {
+  result.defLab = result.labels.back();
+  result.labels.pop_back();
+}
+static SwitchArguments ComposeSwitchArgs(const LinearSwitch &op) {
+  SwitchArguments result{nullptr, unspecifiedLabel, {}, op.refs};
+  std::visit(common::visitors{
+                 [](const auto *) { WRONG_PATH(); },
+                 [&](const parser::ComputedGotoStmt *c) {
+                   result.exp = std::get<parser::ScalarIntExpr>(c->t)
+                                    .thing.thing->typedExpr.get();
+                   buildMultiwayDefaultNext(result);
+                 },
+                 [&](const parser::ArithmeticIfStmt *c) {
+                   result.exp = std::get<parser::Expr>(c->t).typedExpr.get();
+                 },
+                 [&](const parser::CallStmt *c) {
+                   result.exp = nullptr;  // fixme - result of call
+                   buildMultiwayDefaultNext(result);
+                 },
+             },
+      op.u);
+  return result;
+}
+
+template<typename T>
+const T *FindReadWriteSpecifier(
+    const std::list<parser::IoControlSpec> &specifiers) {
+  for (const auto &specifier : specifiers) {
+    if (auto *result{std::get_if<T>(&specifier.u)}) {
+      return result;
+    }
+  }
+  return nullptr;
+}
+const parser::IoUnit *FindReadWriteIoUnit(
+    const std::optional<parser::IoUnit> &ioUnit,
+    const std::list<parser::IoControlSpec> &specifiers) {
+  if (ioUnit.has_value()) {
+    return &ioUnit.value();
+  }
+  if (const auto *result{FindReadWriteSpecifier<parser::IoUnit>(specifiers)}) {
+    return result;
+  }
+  SEMANTICS_FAILED("no UNIT spec");
+  return {};
+}
+const parser::Format *FindReadWriteFormat(
+    const std::optional<parser::Format> &format,
+    const std::list<parser::IoControlSpec> &specifiers) {
+  if (format.has_value()) {
+    return &format.value();
+  }
+  return FindReadWriteSpecifier<parser::Format>(specifiers);
+}
+
+static Expression *AlwaysTrueExpression() {
+  auto result{common::SearchTypes(
+      evaluate::TypeKindVisitor<evaluate::TypeCategory::Logical,
+          evaluate::Constant, bool>{1, true})};
+  CHECK(result.has_value());
+  // TODO: this really ought to be hashconsed, but alas trees
+  return new evaluate::GenericExprWrapper(std::move(*result));
+}
+static Expression *LessThanOrEqualToComparison(
+    const semantics::Symbol *symbol, Expression *second) {
+  return second;  // FIXME
+}
+static Expression *GreaterThanOrEqualToComparison(
+    const semantics::Symbol *symbol, Expression *second) {
+  return second;  // FIXME
+}
+static Expression *BuildLoopLatchExpression(
+    const std::optional<parser::LoopControl> &loopControl) {
+  if (loopControl.has_value()) {
+    return std::visit(
+        common::visitors{
+            [](const parser::LoopBounds<parser::ScalarIntExpr> &loopBounds) {
+              auto *loopVariable{loopBounds.name.thing.thing.symbol};
+              auto *second{loopBounds.upper.thing.thing->typedExpr.get()};
+              if (loopBounds.step.has_value()) {
+                auto *step{loopBounds.step->thing.thing->typedExpr.get()};
+                SEMANTICS_CHECK(step, "DO step expression missing");
+                if (evaluate::IsConstantExpr(step->v)) {
+                  auto stepValue{evaluate::ToInt64(step->v)};
+                  SEMANTICS_CHECK(stepValue != 0, "step cannot be zero");
+                  if (stepValue > 0) {
+                    return LessThanOrEqualToComparison(loopVariable, second);
+                  }
+                  return GreaterThanOrEqualToComparison(loopVariable, second);
+                }
+                // expr ::= (step > 0 && v <= snd) || (step < 0 && v >= snd)
+                return AlwaysTrueExpression();  // FIXME
+              }
+              return LessThanOrEqualToComparison(loopVariable, second);
+            },
+            [](const parser::ScalarLogicalExpr &scalarLogicalExpr) {
+              auto &expression{scalarLogicalExpr.thing.thing};
+              SEMANTICS_CHECK(
+                  expression->typedExpr.get(), "DO WHILE condition missing");
+              return expression->typedExpr.get();
+            },
+            [](const parser::LoopControl::Concurrent &concurrent) {
+              return AlwaysTrueExpression();  // FIXME
+            },
+        },
+        loopControl->u);
+  }
+  return AlwaysTrueExpression();
+}
+
+static void CreateSwitchHelper(IntermediateRepresentationBuilder *builder,
+    const Evaluation &condition, BasicBlock *defaultCase,
+    const SwitchStmt::ValueSuccPairListType &rest) {
+  builder->CreateSwitch(condition, defaultCase, rest);
+}
+static void CreateSwitchCaseHelper(IntermediateRepresentationBuilder *builder,
+    const Evaluation &condition, BasicBlock *defaultCase,
+    const SwitchCaseStmt::ValueSuccPairListType &rest) {
+  builder->CreateSwitchCase(condition, defaultCase, rest);
+}
+static void CreateSwitchRankHelper(IntermediateRepresentationBuilder *builder,
+    const Evaluation &condition, BasicBlock *defaultCase,
+    const SwitchRankStmt::ValueSuccPairListType &rest) {
+  builder->CreateSwitchRank(condition, defaultCase, rest);
+}
+static void CreateSwitchTypeHelper(IntermediateRepresentationBuilder *builder,
+    const Evaluation &condition, BasicBlock *defaultCase,
+    const SwitchTypeStmt::ValueSuccPairListType &rest) {
+  builder->CreateSwitchType(condition, defaultCase, rest);
+}
+
+struct FortranIRLowering {
+  using LabelMapType = std::map<LinearLabelRef, BasicBlock *>;
+  using Closure = std::function<void(const LabelMapType &)>;
+
+  FortranIRLowering(semantics::SemanticsContext &sc, bool debugLinearIR)
+    : fir_{new Program("program_name")}, semanticsContext_{sc},
+      debugLinearIntermediateRepresentation_{debugLinearIR} {}
+  ~FortranIRLowering() { CHECK(!builder_); }
+
+  template<typename A> constexpr bool Pre(const A &) { return true; }
+  template<typename A> constexpr void Post(const A &) {}
+  void Post(const parser::MainProgram &mainp) {
+    std::string mainName{"_MAIN"s};
+    if (auto &ps{
+            std::get<std::optional<parser::Statement<parser::ProgramStmt>>>(
+                mainp.t)}) {
+      mainName = ps->statement.v.ToString();
+    }
+    ProcessRoutine(mainp, mainName);
+  }
+  void Post(const parser::FunctionSubprogram &subp) {
+    ProcessRoutine(subp,
+        std::get<parser::Name>(
+            std::get<parser::Statement<parser::FunctionStmt>>(subp.t)
+                .statement.t)
+            .ToString());
+  }
+  void Post(const parser::SubroutineSubprogram &subp) {
+    ProcessRoutine(subp,
+        std::get<parser::Name>(
+            std::get<parser::Statement<parser::SubroutineStmt>>(subp.t)
+                .statement.t)
+            .ToString());
+  }
+
+  Program *program() { return fir_; }
+  template<typename T>
+  void ProcessRoutine(const T &here, const std::string &name) {
+    CHECK(!fir_->containsProcedure(name));
+    auto *subp{fir_->getOrInsertProcedure(name, nullptr, {})};
+    builder_ = new IntermediateRepresentationBuilder(
+        *CreateBlock(subp->getLastRegion()));
+    AnalysisData ad;
+    ControlFlowAnalyzer linearize{linearOperations_, ad};
+    Walk(here, linearize);
+    if (debugLinearIntermediateRepresentation_) {
+      dumpLinearRepresentation();
+    }
+    ConstructIntermediateRepresentation(ad);
+    DrawRemainingArcs();
+    Cleanup();
+  }
+  void dumpLinearRepresentation() const {
+    for (const auto &op : linearOperations_) {
+      op.dump();
+    }
+    DebugChannel() << "--- END ---\n";
+  }
+  const Expression *CreatePointerValue(
+      const parser::PointerAssignmentStmt *stmt) {
+    // TODO: build a RHS expression to assign to a POINTER
+    return static_cast<const Expression *>(nullptr);
+  }
+  const Expression *CreateAllocationValue(const parser::Allocation *allocation,
+      const parser::AllocateStmt *statement) {
+    auto &obj{std::get<parser::AllocateObject>(allocation->t)};
+    (void)obj;
+    // TODO: build an expression for the allocation
+    return static_cast<const Expression *>(nullptr);
+  }
+  const Expression *CreateDeallocationValue(
+      const parser::AllocateObject *allocateObject,
+      const parser::DeallocateStmt *statement) {
+    // TODO: build an expression for the allocation
+    return static_cast<const Expression *>(nullptr);
+  }
+
+  // IO argument translations ...
+  IOCallArguments CreateBackspaceArguments(
+      const std::list<parser::PositionOrFlushSpec> &specifiers) {
+    return IOCallArguments{};
+  }
+  IOCallArguments CreateCloseArguments(
+      const std::list<parser::CloseStmt::CloseSpec> &specifiers) {
+    return IOCallArguments{};
+  }
+  IOCallArguments CreateEndfileArguments(
+      const std::list<parser::PositionOrFlushSpec> &specifiers) {
+    return IOCallArguments{};
+  }
+  IOCallArguments CreateFlushArguments(
+      const std::list<parser::PositionOrFlushSpec> &specifiers) {
+    return IOCallArguments{};
+  }
+  IOCallArguments CreateRewindArguments(
+      const std::list<parser::PositionOrFlushSpec> &specifiers) {
+    return IOCallArguments{};
+  }
+  IOCallArguments CreateInquireArguments(
+      const std::list<parser::InquireSpec> &specifiers) {
+    return IOCallArguments{};
+  }
+  IOCallArguments CreateInquireArguments(
+      const parser::InquireStmt::Iolength &iolength) {
+    return IOCallArguments{};
+  }
+  IOCallArguments CreateOpenArguments(
+      const std::list<parser::ConnectSpec> &specifiers) {
+    return IOCallArguments{};
+  }
+  IOCallArguments CreateWaitArguments(
+      const std::list<parser::WaitSpec> &specifiers) {
+    return IOCallArguments{};
+  }
+  IOCallArguments CreatePrintArguments(const parser::Format &format,
+      const std::list<parser::OutputItem> &outputs) {
+    return IOCallArguments{};
+  }
+  IOCallArguments CreateReadArguments(
+      const std::optional<parser::IoUnit> &ioUnit,
+      const std::optional<parser::Format> &format,
+      const std::list<parser::IoControlSpec> &controls,
+      const std::list<parser::InputItem> &inputs) {
+    return IOCallArguments{};
+  }
+  IOCallArguments CreateWriteArguments(
+      const std::optional<parser::IoUnit> &ioUnit,
+      const std::optional<parser::Format> &format,
+      const std::list<parser::IoControlSpec> &controls,
+      const std::list<parser::OutputItem> &outputs) {
+    return IOCallArguments{};
+  }
+
+  // Runtime argument translations ...
+  RuntimeCallArguments CreateEventPostArguments(
+      const parser::EventPostStmt &eventPostStatement) {
+    return RuntimeCallArguments{};
+  }
+  RuntimeCallArguments CreateEventWaitArguments(
+      const parser::EventWaitStmt &eventWaitStatement) {
+    return RuntimeCallArguments{};
+  }
+  RuntimeCallArguments CreateFailImageArguments(
+      const parser::FailImageStmt &failImageStatement) {
+    return RuntimeCallArguments{};
+  }
+  RuntimeCallArguments CreateFormTeamArguments(
+      const parser::FormTeamStmt &formTeamStatement) {
+    return RuntimeCallArguments{};
+  }
+  RuntimeCallArguments CreateLockArguments(
+      const parser::LockStmt &lockStatement) {
+    return RuntimeCallArguments{};
+  }
+  RuntimeCallArguments CreatePauseArguments(
+      const parser::PauseStmt &pauseStatement) {
+    return RuntimeCallArguments{};
+  }
+  RuntimeCallArguments CreateStopArguments(
+      const parser::StopStmt &stopStatement) {
+    return RuntimeCallArguments{};
+  }
+  RuntimeCallArguments CreateSyncAllArguments(
+      const parser::SyncAllStmt &syncAllStatement) {
+    return RuntimeCallArguments{};
+  }
+  RuntimeCallArguments CreateSyncImagesArguments(
+      const parser::SyncImagesStmt &syncImagesStatement) {
+    return RuntimeCallArguments{};
+  }
+  RuntimeCallArguments CreateSyncMemoryArguments(
+      const parser::SyncMemoryStmt &syncMemoryStatement) {
+    return RuntimeCallArguments{};
+  }
+  RuntimeCallArguments CreateSyncTeamArguments(
+      const parser::SyncTeamStmt &syncTeamStatement) {
+    return RuntimeCallArguments{};
+  }
+  RuntimeCallArguments CreateUnlockArguments(
+      const parser::UnlockStmt &unlockStatement) {
+    return RuntimeCallArguments{};
+  }
+
+  // CALL translations ...
+  const Value *CreateCalleeValue(
+      const parser::ProcedureDesignator &designator) {
+    return static_cast<const Value *>(nullptr);
+  }
+  CallArguments CreateCallArguments(
+      const std::list<parser::ActualArgSpec> &arguments) {
+    return CallArguments{};
+  }
+
+  void handleActionStatement(
+      AnalysisData &ad, const parser::Statement<parser::ActionStmt> &stmt) {
+    std::visit(
+        common::visitors{
+            [&](const common::Indirection<parser::AllocateStmt> &statement) {
+              for (auto &allocation :
+                  std::get<std::list<parser::Allocation>>(statement->t)) {
+                // TODO: add more arguments to builder as needed
+                builder_->CreateAlloc(
+                    CreateAllocationValue(&allocation, &*statement));
+              }
+            },
+            [&](const common::Indirection<parser::AssignmentStmt> &statement) {
+              builder_->CreateAssign(&std::get<parser::Variable>(statement->t),
+                  std::get<parser::Expr>(statement->t).typedExpr.get());
+            },
+            [&](const common::Indirection<parser::BackspaceStmt> &statement) {
+              builder_->CreateIOCall(InputOutputCallBackspace,
+                  CreateBackspaceArguments(statement->v));
+            },
+            [&](const common::Indirection<parser::CallStmt> &statement) {
+              builder_->CreateCall(static_cast<const FunctionType *>(nullptr),
+                  CreateCalleeValue(
+                      std::get<parser::ProcedureDesignator>(statement->v.t)),
+                  CreateCallArguments(
+                      std::get<std::list<parser::ActualArgSpec>>(
+                          statement->v.t)));
+            },
+            [&](const common::Indirection<parser::CloseStmt> &statement) {
+              builder_->CreateIOCall(
+                  InputOutputCallClose, CreateCloseArguments(statement->v));
+            },
+            [](const parser::ContinueStmt &) { WRONG_PATH(); },
+            [](const common::Indirection<parser::CycleStmt> &) {
+              WRONG_PATH();
+            },
+            [&](const common::Indirection<parser::DeallocateStmt> &statement) {
+              for (auto &allocateObject :
+                  std::get<std::list<parser::AllocateObject>>(statement->t)) {
+                builder_->CreateDealloc(
+                    CreateDeallocationValue(&allocateObject, &*statement));
+              }
+            },
+            [&](const common::Indirection<parser::EndfileStmt> &statement) {
+              builder_->CreateIOCall(
+                  InputOutputCallEndfile, CreateEndfileArguments(statement->v));
+            },
+            [&](const common::Indirection<parser::EventPostStmt> &statement) {
+              builder_->CreateRuntimeCall(
+                  RuntimeCallEventPost, CreateEventPostArguments(*statement));
+            },
+            [&](const common::Indirection<parser::EventWaitStmt> &statement) {
+              builder_->CreateRuntimeCall(
+                  RuntimeCallEventWait, CreateEventWaitArguments(*statement));
+            },
+            [](const common::Indirection<parser::ExitStmt> &) { WRONG_PATH(); },
+            [&](const parser::FailImageStmt &statement) {
+              builder_->CreateRuntimeCall(
+                  RuntimeCallFailImage, CreateFailImageArguments(statement));
+            },
+            [&](const common::Indirection<parser::FlushStmt> &statement) {
+              builder_->CreateIOCall(
+                  InputOutputCallFlush, CreateFlushArguments(statement->v));
+            },
+            [&](const common::Indirection<parser::FormTeamStmt> &statement) {
+              builder_->CreateRuntimeCall(
+                  RuntimeCallFormTeam, CreateFormTeamArguments(*statement));
+            },
+            [](const common::Indirection<parser::GotoStmt> &) { WRONG_PATH(); },
+            [](const common::Indirection<parser::IfStmt> &) { WRONG_PATH(); },
+            [&](const common::Indirection<parser::InquireStmt> &statement) {
+              std::visit(
+                  common::visitors{
+                      [&](const std::list<parser::InquireSpec> &specifiers) {
+                        builder_->CreateIOCall(InputOutputCallInquire,
+                            CreateInquireArguments(specifiers));
+                      },
+                      [&](const parser::InquireStmt::Iolength &iolength) {
+                        builder_->CreateIOCall(InputOutputCallInquire,
+                            CreateInquireArguments(iolength));
+                      },
+                  },
+                  statement->u);
+            },
+            [&](const common::Indirection<parser::LockStmt> &statement) {
+              builder_->CreateRuntimeCall(
+                  RuntimeCallLock, CreateLockArguments(*statement));
+            },
+            [&](const common::Indirection<parser::NullifyStmt> &statement) {
+              builder_->CreateNullify(&*statement);
+            },
+            [&](const common::Indirection<parser::OpenStmt> &statement) {
+              builder_->CreateIOCall(
+                  InputOutputCallOpen, CreateOpenArguments(statement->v));
+            },
+            [&](const common::Indirection<parser::PointerAssignmentStmt>
+                    &statement) {
+              builder_->CreatePointerAssign(
+                  std::get<parser::Expr>(statement->t).typedExpr.get(),
+                  CreatePointerValue(&*statement));
+            },
+            [&](const common::Indirection<parser::PrintStmt> &statement) {
+              builder_->CreateIOCall(InputOutputCallPrint,
+                  CreatePrintArguments(std::get<parser::Format>(statement->t),
+                      std::get<std::list<parser::OutputItem>>(statement->t)));
+            },
+            [&](const common::Indirection<parser::ReadStmt> &statement) {
+              builder_->CreateIOCall(InputOutputCallRead,
+                  CreateReadArguments(statement->iounit, statement->format,
+                      statement->controls, statement->items));
+            },
+            [](const common::Indirection<parser::ReturnStmt> &) {
+              WRONG_PATH();
+            },
+            [&](const common::Indirection<parser::RewindStmt> &statement) {
+              builder_->CreateIOCall(
+                  InputOutputCallRewind, CreateRewindArguments(statement->v));
+            },
+            [&](const common::Indirection<parser::StopStmt> &statement) {
+              builder_->CreateRuntimeCall(
+                  RuntimeCallStop, CreateStopArguments(*statement));
+            },
+            [&](const common::Indirection<parser::SyncAllStmt> &statement) {
+              builder_->CreateRuntimeCall(
+                  RuntimeCallSyncAll, CreateSyncAllArguments(*statement));
+            },
+            [&](const common::Indirection<parser::SyncImagesStmt> &statement) {
+              builder_->CreateRuntimeCall(
+                  RuntimeCallSyncImages, CreateSyncImagesArguments(*statement));
+            },
+            [&](const common::Indirection<parser::SyncMemoryStmt> &statement) {
+              builder_->CreateRuntimeCall(
+                  RuntimeCallSyncMemory, CreateSyncMemoryArguments(*statement));
+            },
+            [&](const common::Indirection<parser::SyncTeamStmt> &statement) {
+              builder_->CreateRuntimeCall(
+                  RuntimeCallSyncTeam, CreateSyncTeamArguments(*statement));
+            },
+            [&](const common::Indirection<parser::UnlockStmt> &statement) {
+              builder_->CreateRuntimeCall(
+                  RuntimeCallUnlock, CreateUnlockArguments(*statement));
+            },
+            [&](const common::Indirection<parser::WaitStmt> &statement) {
+              builder_->CreateIOCall(
+                  InputOutputCallWait, CreateWaitArguments(statement->v));
+            },
+            [](const common::Indirection<parser::WhereStmt> &) { /*fixme*/ },
+            [&](const common::Indirection<parser::WriteStmt> &statement) {
+              builder_->CreateIOCall(InputOutputCallWrite,
+                  CreateWriteArguments(statement->iounit, statement->format,
+                      statement->controls, statement->items));
+            },
+            [](const common::Indirection<parser::ComputedGotoStmt> &) {
+              WRONG_PATH();
+            },
+            [](const common::Indirection<parser::ForallStmt> &) { /*fixme*/ },
+            [](const common::Indirection<parser::ArithmeticIfStmt> &) {
+              WRONG_PATH();
+            },
+            [&](const common::Indirection<parser::AssignStmt> &statement) {
+              builder_->CreateAssign(
+                  std::get<parser::Name>(statement->t).symbol,
+                  blockMap_
+                      .find(
+                          FetchLabel(ad, std::get<parser::Label>(statement->t))
+                              .get())
+                      ->second);
+            },
+            [](const common::Indirection<parser::AssignedGotoStmt> &) {
+              WRONG_PATH();
+            },
+            [&](const common::Indirection<parser::PauseStmt> &statement) {
+              builder_->CreateRuntimeCall(
+                  RuntimeCallPause, CreatePauseArguments(*statement));
+            },
+        },
+        stmt.statement.u);
+  }
+  void handleLinearAction(const LinearAction &linearAction, AnalysisData &ad) {
+    handleActionStatement(ad, *linearAction.v);
+  }
+
+  // InitiateConstruct - many constructs require some initial setup
+  void InitiateConstruct(const parser::AssociateStmt *associateStmt) {
+    for (auto &association :
+        std::get<std::list<parser::Association>>(associateStmt->t)) {
+      auto &name{std::get<parser::Name>(association.t)};
+      // Evaluation eval{...};
+      (void)name;
+    }
+    builder_->CreateExpr(associateStmt);
+  }
+  void InitiateConstruct(const parser::SelectCaseStmt *selectCaseStmt) {
+    builder_->CreateExpr(
+        std::get<parser::Scalar<parser::Expr>>(selectCaseStmt->t)
+            .thing.typedExpr.get());
+  }
+  void InitiateConstruct(const parser::ChangeTeamStmt *changeTeamStmt) {
+    builder_->CreateExpr(changeTeamStmt);
+  }
+  void InitiateConstruct(const parser::NonLabelDoStmt *nonlabelDoStmt) {
+    // evaluate e1, e2 [, e3] ...
+    builder_->CreateExpr(nonlabelDoStmt);
+  }
+  void InitiateConstruct(const parser::IfThenStmt *ifThenStmt) {
+    builder_->CreateExpr(std::get<parser::ScalarLogicalExpr>(ifThenStmt->t)
+                             .thing.thing->typedExpr.get());
+  }
+  void InitiateConstruct(const parser::WhereConstructStmt *whereConstructStmt) {
+    builder_->CreateExpr(std::get<parser::LogicalExpr>(whereConstructStmt->t)
+                             .thing->typedExpr.get());
+  }
+  void InitiateConstruct(
+      const parser::ForallConstructStmt *forallConstructStmt) {
+    builder_->CreateExpr(forallConstructStmt);
+  }
+
+  void ConstructIntermediateRepresentation(AnalysisData &ad) {
+    for (auto iter{linearOperations_.begin()}, iend{linearOperations_.end()};
+         iter != iend; ++iter) {
+      const auto &op{*iter};
+      std::visit(
+          common::visitors{
+              [&](const LinearLabel &linearLabel) {
+                auto *newBlock{CreateBlock(builder_->GetCurrentRegion())};
+                blockMap_.insert({linearLabel.get(), newBlock});
+                if (builder_->GetInsertionPoint()) {
+                  builder_->CreateBranch(newBlock);
+                }
+                builder_->SetInsertionPoint(newBlock);
+              },
+              [&](const LinearGoto &linearGoto) {
+                CheckInsertionPoint();
+                AddOrQueueBranch(linearGoto.target);
+                builder_->ClearInsertionPoint();
+              },
+              [&](const LinearIndirectGoto &linearIGoto) {
+                CheckInsertionPoint();
+                AddOrQueueIGoto(ad, linearIGoto.symbol, linearIGoto.labelRefs);
+                builder_->ClearInsertionPoint();
+              },
+              [&](const LinearReturn &linearReturn) {
+                CheckInsertionPoint();
+                std::visit(common::visitors{
+                               [&](const parser::FailImageStmt *s) {
+                                 builder_->CreateRuntimeCall(
+                                     RuntimeCallFailImage,
+                                     CreateFailImageArguments(*s));
+                                 builder_->CreateUnreachable();
+                               },
+                               [&](const parser::ReturnStmt *s) {
+                                 if (s->v) {
+                                   builder_->CreateReturn(
+                                       s->v->thing.thing->typedExpr.get());
+                                 } else {
+                                   builder_->CreateRetVoid();
+                                 }
+                               },
+                               [&](const parser::StopStmt *s) {
+                                 builder_->CreateRuntimeCall(
+                                     RuntimeCallStop, CreateStopArguments(*s));
+                                 builder_->CreateUnreachable();
+                               },
+                           },
+                    linearReturn.u);
+                builder_->ClearInsertionPoint();
+              },
+              [&](const LinearConditionalGoto &linearConditionalGoto) {
+                CheckInsertionPoint();
+                std::visit(
+                    common::visitors{
+                        [&](const parser::Statement<parser::IfThenStmt>
+                                *statement) {
+                          const auto &expression{
+                              std::get<parser::ScalarLogicalExpr>(
+                                  statement->statement.t)
+                                  .thing.thing};
+                          SEMANTICS_CHECK(expression->typedExpr.get(),
+                              "IF THEN condition expression missing");
+                          AddOrQueueCGoto(expression->typedExpr.get(),
+                              linearConditionalGoto.trueLabel,
+                              linearConditionalGoto.falseLabel);
+                        },
+                        [&](const parser::Statement<parser::ElseIfStmt>
+                                *statement) {
+                          const auto &expression{
+                              std::get<parser::ScalarLogicalExpr>(
+                                  statement->statement.t)
+                                  .thing.thing};
+                          SEMANTICS_CHECK(expression->typedExpr.get(),
+                              "ELSE IF condition expression missing");
+                          AddOrQueueCGoto(expression->typedExpr.get(),
+                              linearConditionalGoto.trueLabel,
+                              linearConditionalGoto.falseLabel);
+                        },
+                        [&](const parser::IfStmt *statement) {
+                          const auto &expression{
+                              std::get<parser::ScalarLogicalExpr>(statement->t)
+                                  .thing.thing};
+                          SEMANTICS_CHECK(expression->typedExpr.get(),
+                              "IF condition expression missing");
+                          AddOrQueueCGoto(expression->typedExpr.get(),
+                              linearConditionalGoto.trueLabel,
+                              linearConditionalGoto.falseLabel);
+                        },
+                        [&](const parser::Statement<parser::NonLabelDoStmt>
+                                *statement) {
+                          AddOrQueueCGoto(
+                              BuildLoopLatchExpression(
+                                  std::get<std::optional<parser::LoopControl>>(
+                                      statement->statement.t)),
+                              linearConditionalGoto.trueLabel,
+                              linearConditionalGoto.falseLabel);
+                        }},
+                    linearConditionalGoto.u);
+                builder_->ClearInsertionPoint();
+              },
+              [&](const LinearSwitchingIO &linearIO) {
+                CheckInsertionPoint();
+                AddOrQueueSwitch<SwitchStmt>(
+                    nullptr, linearIO.next, {}, {}, CreateSwitchHelper);
+                builder_->ClearInsertionPoint();
+              },
+              [&](const LinearSwitch &linearSwitch) {
+                CheckInsertionPoint();
+                std::visit(common::visitors{
+                               [&](auto) {
+                                 auto args{ComposeSwitchArgs(linearSwitch)};
+                                 AddOrQueueSwitch<SwitchStmt>(args.exp,
+                                     args.defLab, args.values, args.labels,
+                                     CreateSwitchHelper);
+                               },
+                               [&](const parser::CaseConstruct *caseConstruct) {
+                                 auto args{ComposeSwitchCaseArguments(
+                                     caseConstruct, linearSwitch.refs)};
+                                 AddOrQueueSwitch<SwitchCaseStmt>(args.exp,
+                                     args.defLab, args.ranges, args.labels,
+                                     CreateSwitchCaseHelper);
+                               },
+                               [&](const parser::SelectRankConstruct
+                                       *selectRankConstruct) {
+                                 auto args{ComposeSwitchRankArguments(
+                                     selectRankConstruct, linearSwitch.refs)};
+                                 AddOrQueueSwitch<SwitchRankStmt>(args.exp,
+                                     args.defLab, args.ranks, args.labels,
+                                     CreateSwitchRankHelper);
+                               },
+                               [&](const parser::SelectTypeConstruct
+                                       *selectTypeConstruct) {
+                                 auto args{ComposeSwitchTypeArguments(
+                                     selectTypeConstruct, linearSwitch.refs)};
+                                 AddOrQueueSwitch<SwitchTypeStmt>(args.exp,
+                                     args.defLab, args.types, args.labels,
+                                     CreateSwitchTypeHelper);
+                               },
+                           },
+                    linearSwitch.u);
+                builder_->ClearInsertionPoint();
+              },
+              [&](const LinearAction &linearAction) {
+                CheckInsertionPoint();
+                handleLinearAction(linearAction, ad);
+              },
+              [&](const LinearBeginConstruct &linearConstruct) {
+                std::visit(
+                    common::visitors{
+                        [&](const parser::AssociateConstruct *construct) {
+                          const auto &statement{std::get<
+                              parser::Statement<parser::AssociateStmt>>(
+                              construct->t)};
+                          const auto &position{statement.source};
+                          EnterRegion(position);
+                          InitiateConstruct(&statement.statement);
+                        },
+                        [&](const parser::BlockConstruct *construct) {
+                          EnterRegion(
+                              std::get<parser::Statement<parser::BlockStmt>>(
+                                  construct->t)
+                                  .source);
+                        },
+                        [&](const parser::CaseConstruct *construct) {
+                          InitiateConstruct(
+                              &std::get<
+                                  parser::Statement<parser::SelectCaseStmt>>(
+                                  construct->t)
+                                   .statement);
+                        },
+                        [&](const parser::ChangeTeamConstruct *construct) {
+                          const auto &statement{std::get<
+                              parser::Statement<parser::ChangeTeamStmt>>(
+                              construct->t)};
+                          EnterRegion(statement.source);
+                          InitiateConstruct(&statement.statement);
+                        },
+                        [&](const parser::DoConstruct *construct) {
+                          const auto &statement{std::get<
+                              parser::Statement<parser::NonLabelDoStmt>>(
+                              construct->t)};
+                          EnterRegion(statement.source);
+                          InitiateConstruct(&statement.statement);
+                        },
+                        [&](const parser::IfConstruct *construct) {
+                          InitiateConstruct(
+                              &std::get<parser::Statement<parser::IfThenStmt>>(
+                                  construct->t)
+                                   .statement);
+                        },
+                        [&](const parser::SelectRankConstruct *construct) {
+                          const auto &statement{std::get<
+                              parser::Statement<parser::SelectRankStmt>>(
+                              construct->t)};
+                          EnterRegion(statement.source);
+                        },
+                        [&](const parser::SelectTypeConstruct *construct) {
+                          const auto &statement{std::get<
+                              parser::Statement<parser::SelectTypeStmt>>(
+                              construct->t)};
+                          EnterRegion(statement.source);
+                        },
+                        [&](const parser::WhereConstruct *construct) {
+                          InitiateConstruct(
+                              &std::get<parser::Statement<
+                                   parser::WhereConstructStmt>>(construct->t)
+                                   .statement);
+                        },
+                        [&](const parser::ForallConstruct *construct) {
+                          InitiateConstruct(
+                              &std::get<parser::Statement<
+                                   parser::ForallConstructStmt>>(construct->t)
+                                   .statement);
+                        },
+                        [](const parser::CriticalConstruct *) { /*fixme*/ },
+                        [](const parser::CompilerDirective *) { /*fixme*/ },
+                        [](const parser::OpenMPConstruct *) { /*fixme*/ },
+                        [](const parser::OpenMPEndLoopDirective
+                                *) { /*fixme*/ },
+                    },
+                    linearConstruct.u);
+                auto next{iter};
+                const auto &nextOp{*(++next)};
+                std::visit(common::visitors{
+                               [](const auto &) {},
+                               [&](const LinearLabel &linearLabel) {
+                                 blockMap_.insert({linearLabel.get(),
+                                     builder_->GetInsertionPoint()});
+                                 ++iter;
+                               },
+                           },
+                    nextOp.u);
+              },
+              [&](const LinearEndConstruct &linearConstruct) {
+                std::visit(
+                    common::visitors{
+                        [](const auto &) {},
+                        [&](const parser::BlockConstruct *) { ExitRegion(); },
+                        [&](const parser::DoConstruct *) { ExitRegion(); },
+                        [&](const parser::AssociateConstruct *) {
+                          ExitRegion();
+                        },
+                        [&](const parser::ChangeTeamConstruct *) {
+                          ExitRegion();
+                        },
+                        [&](const parser::SelectTypeConstruct *) {
+                          ExitRegion();
+                        },
+                    },
+                    linearConstruct.u);
+              },
+          },
+          op.u);
+    }
+  }
+  void EnterRegion(const parser::CharBlock &pos) {
+    auto *region{builder_->GetCurrentRegion()};
+    auto *scope{semanticsContext_.globalScope().FindScope(pos)};
+    auto *newRegion{Region::Create(region->getParent(), scope, region)};
+    auto *block{CreateBlock(newRegion)};
+    CheckInsertionPoint();
+    builder_->CreateBranch(block);
+    builder_->SetInsertionPoint(block);
+  }
+  void ExitRegion() {
+    builder_->SetCurrentRegion(builder_->GetCurrentRegion()->GetEnclosing());
+  }
+  void CheckInsertionPoint() {
+    if (!builder_->GetInsertionPoint()) {
+      builder_->SetInsertionPoint(CreateBlock(builder_->GetCurrentRegion()));
+    }
+  }
+  void AddOrQueueBranch(LinearLabelRef dest) {
+    auto iter{blockMap_.find(dest)};
+    if (iter != blockMap_.end()) {
+      builder_->CreateBranch(iter->second);
+    } else {
+      using namespace std::placeholders;
+      controlFlowEdgesToAdd_.emplace_back(std::bind(
+          [](IntermediateRepresentationBuilder *builder, BasicBlock *block,
+              LinearLabelRef dest, const LabelMapType &map) {
+            builder->SetInsertionPoint(block);
+            CHECK(map.find(dest) != map.end());
+            builder->CreateBranch(map.find(dest)->second);
+          },
+          builder_, builder_->GetInsertionPoint(), dest, _1));
+    }
+  }
+  void AddOrQueueCGoto(Expression *condition, LinearLabelRef trueBlock,
+      LinearLabelRef falseBlock) {
+    auto trueIter{blockMap_.find(trueBlock)};
+    auto falseIter{blockMap_.find(falseBlock)};
+    if (trueIter != blockMap_.end() && falseIter != blockMap_.end()) {
+      builder_->CreateConditionalBranch(
+          condition, trueIter->second, falseIter->second);
+    } else {
+      using namespace std::placeholders;
+      controlFlowEdgesToAdd_.emplace_back(std::bind(
+          [](IntermediateRepresentationBuilder *builder, BasicBlock *block,
+              Expression *expr, LinearLabelRef trueDest,
+              LinearLabelRef falseDest, const LabelMapType &map) {
+            builder->SetInsertionPoint(block);
+            CHECK(map.find(trueDest) != map.end());
+            CHECK(map.find(falseDest) != map.end());
+            builder->CreateConditionalBranch(
+                expr, map.find(trueDest)->second, map.find(falseDest)->second);
+          },
+          builder_, builder_->GetInsertionPoint(), condition, trueBlock,
+          falseBlock, _1));
+    }
+  }
+  template<typename SWITCHTYPE, typename F>
+  void AddOrQueueSwitch(const Evaluation &condition,
+      LinearLabelRef defaultLabel,
+      const std::vector<typename SWITCHTYPE::ValueType> &values,
+      const std::vector<LinearLabelRef> &labels, F function) {
+    auto defer{false};
+    auto defaultIter{blockMap_.find(defaultLabel)};
+    typename SWITCHTYPE::ValueSuccPairListType cases;
+    if (defaultIter == blockMap_.end()) {
+      defer = true;
+    } else {
+      CHECK(values.size() == labels.size());
+      auto valiter{values.begin()};
+      for (auto lab : labels) {
+        auto labIter{blockMap_.find(lab)};
+        if (labIter == blockMap_.end()) {
+          defer = true;
+          break;
+        } else {
+          cases.emplace_back(*valiter++, labIter->second);
+        }
+      }
+    }
+    if (defer) {
+      using namespace std::placeholders;
+      controlFlowEdgesToAdd_.emplace_back(std::bind(
+          [](IntermediateRepresentationBuilder *builder, BasicBlock *block,
+              const Evaluation &expr, LinearLabelRef defaultDest,
+              const std::vector<typename SWITCHTYPE::ValueType> &values,
+              const std::vector<LinearLabelRef> &labels, F function,
+              const LabelMapType &map) {
+            builder->SetInsertionPoint(block);
+            typename SWITCHTYPE::ValueSuccPairListType cases;
+            auto valiter{values.begin()};
+            for (auto &lab : labels) {
+              cases.emplace_back(*valiter++, map.find(lab)->second);
+            }
+            function(builder, expr, map.find(defaultDest)->second, cases);
+          },
+          builder_, builder_->GetInsertionPoint(), condition, defaultLabel,
+          values, labels, function, _1));
+    } else {
+      function(builder_, condition, defaultIter->second, cases);
+    }
+  }
+  Variable *ConvertToVariable(const semantics::Symbol *symbol) {
+    // FIXME: how to convert semantics::Symbol to evaluate::Variable?
+    return new Variable(symbol);
+  }
+  void AddOrQueueIGoto(AnalysisData &ad, const semantics::Symbol *symbol,
+      const std::vector<LinearLabelRef> &labels) {
+    auto useLabels{labels.empty() ? GetAssign(ad, symbol) : labels};
+    auto defer{false};
+    IndirectBrStmt::TargetListType blocks;
+    for (auto lab : useLabels) {
+      auto iter{blockMap_.find(lab)};
+      if (iter == blockMap_.end()) {
+        defer = true;
+        break;
+      } else {
+        blocks.push_back(iter->second);
+      }
+    }
+    if (defer) {
+      using namespace std::placeholders;
+      controlFlowEdgesToAdd_.emplace_back(std::bind(
+          [](IntermediateRepresentationBuilder *builder, BasicBlock *block,
+              Variable *variable, const std::vector<LinearLabelRef> &fixme,
+              const LabelMapType &map) {
+            builder->SetInsertionPoint(block);
+            builder->CreateIndirectBr(variable, {});  // FIXME
+          },
+          builder_, builder_->GetInsertionPoint(), nullptr /*symbol*/,
+          useLabels, _1));
+    } else {
+      builder_->CreateIndirectBr(ConvertToVariable(symbol), blocks);
+    }
+  }
+
+  void DrawRemainingArcs() {
+    for (auto &arc : controlFlowEdgesToAdd_) {
+      arc(blockMap_);
+    }
+  }
+
+  BasicBlock *CreateBlock(Region *region) { return BasicBlock::Create(region); }
+
+  void Cleanup() {
+    delete builder_;
+    builder_ = nullptr;
+    linearOperations_.clear();
+    controlFlowEdgesToAdd_.clear();
+    blockMap_.clear();
+  }
+
+  IntermediateRepresentationBuilder *builder_{nullptr};
+  Program *fir_;
+  std::list<LinearOp> linearOperations_;
+  std::list<Closure> controlFlowEdgesToAdd_;
+  LabelMapType blockMap_;
+  semantics::SemanticsContext &semanticsContext_;
+  bool debugLinearIntermediateRepresentation_;
+};
+
+Program *CreateFortranIR(const parser::Program &program,
+    semantics::SemanticsContext &semanticsContext, bool debugLinearIR) {
+  FortranIRLowering converter{semanticsContext, debugLinearIR};
+  Walk(program, converter);
+  return converter.program();
+}
+}
diff --git a/flang/lib/IntermediateRepresentation/afforestation.h b/flang/lib/IntermediateRepresentation/afforestation.h
new file mode 100644 (file)
index 0000000..bceec42
--- /dev/null
@@ -0,0 +1,37 @@
+// Copyright (c) 2019, NVIDIA CORPORATION.  All rights reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//     http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#ifndef FORTRAN_INTERMEDIATEREPRESENTATION_AFFORESTATION_H_
+#define FORTRAN_INTERMEDIATEREPRESENTATION_AFFORESTATION_H_
+
+#include <string>
+
+namespace Fortran::parser {
+class Program;
+}
+
+namespace Fortran::semantics {
+class SemanticsContext;
+}
+
+namespace Fortran::IntermediateRepresentation {
+class Program;
+
+void SetDebugChannel(const std::string &filename);
+
+Program *CreateFortranIR(const parser::Program &program,
+    semantics::SemanticsContext &semanticsContext, bool debugLinearIR);
+}
+
+#endif