1 //===-- lib/Semantics/canonicalize-do.cpp ---------------------------------===//
2 //
3 // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4 // See https://llvm.org/LICENSE.txt for license information.
5 // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
6 //
7 //===----------------------------------------------------------------------===//
8
9 #include "canonicalize-do.h"
10 #include "flang/Parser/parse-tree-visitor.h"
11
12 namespace Fortran::parser {
13
14 class CanonicalizationOfDoLoops {
15 struct LabelInfo {
16 Block::iterator iter;
17 Label label;
18 };
19
20 public:
Pre(T &)21 template <typename T> bool Pre(T &) { return true; }
Post(T &)22 template <typename T> void Post(T &) {}
Post(Block & block)23 void Post(Block &block) {
24 std::vector<LabelInfo> stack;
25 for (auto i{block.begin()}, end{block.end()}; i != end; ++i) {
26 if (auto *executableConstruct{std::get_if<ExecutableConstruct>(&i->u)}) {
27 std::visit(
28 common::visitors{
29 [](auto &) {},
30 // Labels on end-stmt of constructs are accepted by f18 as an
31 // extension.
32 [&](common::Indirection<AssociateConstruct> &associate) {
33 CanonicalizeIfMatch(block, stack, i,
34 std::get<Statement<EndAssociateStmt>>(
35 associate.value().t));
36 },
37 [&](common::Indirection<BlockConstruct> &blockConstruct) {
38 CanonicalizeIfMatch(block, stack, i,
39 std::get<Statement<EndBlockStmt>>(
40 blockConstruct.value().t));
41 },
42 [&](common::Indirection<ChangeTeamConstruct> &changeTeam) {
43 CanonicalizeIfMatch(block, stack, i,
44 std::get<Statement<EndChangeTeamStmt>>(
45 changeTeam.value().t));
46 },
47 [&](common::Indirection<CriticalConstruct> &critical) {
48 CanonicalizeIfMatch(block, stack, i,
49 std::get<Statement<EndCriticalStmt>>(critical.value().t));
50 },
51 [&](common::Indirection<DoConstruct> &doConstruct) {
52 CanonicalizeIfMatch(block, stack, i,
53 std::get<Statement<EndDoStmt>>(doConstruct.value().t));
54 },
55 [&](common::Indirection<IfConstruct> &ifConstruct) {
56 CanonicalizeIfMatch(block, stack, i,
57 std::get<Statement<EndIfStmt>>(ifConstruct.value().t));
58 },
59 [&](common::Indirection<CaseConstruct> &caseConstruct) {
60 CanonicalizeIfMatch(block, stack, i,
61 std::get<Statement<EndSelectStmt>>(
62 caseConstruct.value().t));
63 },
64 [&](common::Indirection<SelectRankConstruct> &selectRank) {
65 CanonicalizeIfMatch(block, stack, i,
66 std::get<Statement<EndSelectStmt>>(selectRank.value().t));
67 },
68 [&](common::Indirection<SelectTypeConstruct> &selectType) {
69 CanonicalizeIfMatch(block, stack, i,
70 std::get<Statement<EndSelectStmt>>(selectType.value().t));
71 },
72 [&](common::Indirection<ForallConstruct> &forall) {
73 CanonicalizeIfMatch(block, stack, i,
74 std::get<Statement<EndForallStmt>>(forall.value().t));
75 },
76 [&](common::Indirection<WhereConstruct> &where) {
77 CanonicalizeIfMatch(block, stack, i,
78 std::get<Statement<EndWhereStmt>>(where.value().t));
79 },
80 [&](Statement<common::Indirection<LabelDoStmt>> &labelDoStmt) {
81 auto &label{std::get<Label>(labelDoStmt.statement.value().t)};
82 stack.push_back(LabelInfo{i, label});
83 },
84 [&](Statement<common::Indirection<EndDoStmt>> &endDoStmt) {
85 CanonicalizeIfMatch(block, stack, i, endDoStmt);
86 },
87 [&](Statement<ActionStmt> &actionStmt) {
88 CanonicalizeIfMatch(block, stack, i, actionStmt);
89 },
90 },
91 executableConstruct->u);
92 }
93 }
94 }
95
96 private:
97 template <typename T>
CanonicalizeIfMatch(Block & originalBlock,std::vector<LabelInfo> & stack,Block::iterator & i,Statement<T> & statement)98 void CanonicalizeIfMatch(Block &originalBlock, std::vector<LabelInfo> &stack,
99 Block::iterator &i, Statement<T> &statement) {
100 if (!stack.empty() && statement.label &&
101 stack.back().label == *statement.label) {
102 auto currentLabel{stack.back().label};
103 if constexpr (std::is_same_v<T, common::Indirection<EndDoStmt>>) {
104 std::get<ExecutableConstruct>(i->u).u = Statement<ActionStmt>{
105 std::optional<Label>{currentLabel}, ContinueStmt{}};
106 }
107 auto next{++i};
108 do {
109 Block block;
110 auto doLoop{stack.back().iter};
111 auto originalSource{
112 std::get<Statement<common::Indirection<LabelDoStmt>>>(
113 std::get<ExecutableConstruct>(doLoop->u).u)
114 .source};
115 block.splice(block.begin(), originalBlock, ++stack.back().iter, next);
116 auto &labelDo{std::get<Statement<common::Indirection<LabelDoStmt>>>(
117 std::get<ExecutableConstruct>(doLoop->u).u)};
118 auto &loopControl{
119 std::get<std::optional<LoopControl>>(labelDo.statement.value().t)};
120 auto &name{std::get<std::optional<Name>>(labelDo.statement.value().t)};
121 Statement<NonLabelDoStmt> nonLabelDoStmt{std::move(labelDo.label),
122 NonLabelDoStmt{
123 std::make_tuple(common::Clone(name), std::move(loopControl))}};
124 nonLabelDoStmt.source = originalSource;
125 std::get<ExecutableConstruct>(doLoop->u).u =
126 common::Indirection<DoConstruct>{
127 std::make_tuple(std::move(nonLabelDoStmt), std::move(block),
128 Statement<EndDoStmt>{
129 std::optional<Label>{}, EndDoStmt{std::move(name)}})};
130 stack.pop_back();
131 } while (!stack.empty() && stack.back().label == currentLabel);
132 i = --next;
133 }
134 }
135 };
136
CanonicalizeDo(Program & program)137 bool CanonicalizeDo(Program &program) {
138 CanonicalizationOfDoLoops canonicalizationOfDoLoops;
139 Walk(program, canonicalizationOfDoLoops);
140 return true;
141 }
142
143 } // namespace Fortran::parser
144