/src/solidity/libyul/optimiser/ControlFlowSimplifier.cpp
Line | Count | Source (jump to first uncovered line) |
1 | | /* |
2 | | This file is part of solidity. |
3 | | |
4 | | solidity is free software: you can redistribute it and/or modify |
5 | | it under the terms of the GNU General Public License as published by |
6 | | the Free Software Foundation, either version 3 of the License, or |
7 | | (at your option) any later version. |
8 | | |
9 | | solidity is distributed in the hope that it will be useful, |
10 | | but WITHOUT ANY WARRANTY; without even the implied warranty of |
11 | | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
12 | | GNU General Public License for more details. |
13 | | |
14 | | You should have received a copy of the GNU General Public License |
15 | | along with solidity. If not, see <http://www.gnu.org/licenses/>. |
16 | | */ |
17 | | // SPDX-License-Identifier: GPL-3.0 |
18 | | #include <libyul/optimiser/ControlFlowSimplifier.h> |
19 | | #include <libyul/optimiser/Semantics.h> |
20 | | #include <libyul/optimiser/OptimiserStep.h> |
21 | | #include <libyul/AST.h> |
22 | | #include <libyul/Utilities.h> |
23 | | #include <libyul/Dialect.h> |
24 | | #include <libsolutil/CommonData.h> |
25 | | #include <libsolutil/Visitor.h> |
26 | | |
27 | | #include <range/v3/action/remove_if.hpp> |
28 | | |
29 | | using namespace solidity; |
30 | | using namespace solidity::util; |
31 | | using namespace solidity::yul; |
32 | | |
33 | | using OptionalStatements = std::optional<std::vector<Statement>>; |
34 | | |
35 | | namespace |
36 | | { |
37 | | |
38 | | ExpressionStatement makeDiscardCall( |
39 | | langutil::DebugData::ConstPtr const& _debugData, |
40 | | BuiltinHandle const& _discardFunction, |
41 | | Expression&& _expression |
42 | | ) |
43 | 47.4k | { |
44 | 47.4k | return {_debugData, FunctionCall{ |
45 | 47.4k | _debugData, |
46 | 47.4k | BuiltinName{_debugData, _discardFunction}, |
47 | 47.4k | {std::move(_expression)} |
48 | 47.4k | }}; |
49 | 47.4k | } |
50 | | |
51 | | void removeEmptyDefaultFromSwitch(Switch& _switchStmt) |
52 | 193k | { |
53 | 193k | ranges::actions::remove_if( |
54 | 193k | _switchStmt.cases, |
55 | 601k | [](Case const& _case) { return !_case.value && _case.body.statements.empty(); } |
56 | 193k | ); |
57 | 193k | } |
58 | | |
59 | | void removeEmptyCasesFromSwitch(Switch& _switchStmt) |
60 | 193k | { |
61 | 193k | if (hasDefaultCase(_switchStmt)) |
62 | 120k | return; |
63 | | |
64 | 72.9k | ranges::actions::remove_if( |
65 | 72.9k | _switchStmt.cases, |
66 | 144k | [](Case const& _case) { return _case.body.statements.empty(); } |
67 | 72.9k | ); |
68 | 72.9k | } |
69 | | |
70 | | } |
71 | | |
72 | | void ControlFlowSimplifier::run(OptimiserStepContext& _context, Block& _ast) |
73 | 464k | { |
74 | 464k | ControlFlowSimplifier{_context.dialect}(_ast); |
75 | 464k | } |
76 | | |
77 | | void ControlFlowSimplifier::operator()(Block& _block) |
78 | 7.09M | { |
79 | 7.09M | simplify(_block.statements); |
80 | 7.09M | } |
81 | | |
82 | | void ControlFlowSimplifier::operator()(FunctionDefinition& _funDef) |
83 | 1.33M | { |
84 | 1.33M | ASTModifier::operator()(_funDef); |
85 | 1.33M | if (!_funDef.body.statements.empty() && std::holds_alternative<Leave>(_funDef.body.statements.back())) |
86 | 5.90k | _funDef.body.statements.pop_back(); |
87 | 1.33M | } |
88 | | |
89 | | void ControlFlowSimplifier::visit(Statement& _st) |
90 | 51.4M | { |
91 | 51.4M | if (std::holds_alternative<ForLoop>(_st)) |
92 | 1.16M | { |
93 | 1.16M | ForLoop& forLoop = std::get<ForLoop>(_st); |
94 | 1.16M | yulAssert(forLoop.pre.statements.empty(), ""); |
95 | | |
96 | 1.16M | size_t outerBreak = m_numBreakStatements; |
97 | 1.16M | size_t outerContinue = m_numContinueStatements; |
98 | 1.16M | m_numBreakStatements = 0; |
99 | 1.16M | m_numContinueStatements = 0; |
100 | | |
101 | 1.16M | ASTModifier::visit(_st); |
102 | | |
103 | 1.16M | if (!forLoop.body.statements.empty()) |
104 | 973k | { |
105 | 973k | bool isTerminating = false; |
106 | 973k | TerminationFinder::ControlFlow controlFlow = TerminationFinder{m_dialect}.controlFlowKind(forLoop.body.statements.back()); |
107 | 973k | if (controlFlow == TerminationFinder::ControlFlow::Break) |
108 | 9.59k | { |
109 | 9.59k | isTerminating = true; |
110 | 9.59k | --m_numBreakStatements; |
111 | 9.59k | } |
112 | 964k | else if ( |
113 | 964k | controlFlow == TerminationFinder::ControlFlow::Terminate || |
114 | 964k | controlFlow == TerminationFinder::ControlFlow::Leave |
115 | 964k | ) |
116 | 19.6k | isTerminating = true; |
117 | | |
118 | 973k | if (isTerminating && m_numContinueStatements == 0 && m_numBreakStatements == 0) |
119 | 8.19k | { |
120 | 8.19k | If replacement{forLoop.debugData, std::move(forLoop.condition), std::move(forLoop.body)}; |
121 | 8.19k | if (controlFlow == TerminationFinder::ControlFlow::Break) |
122 | 3.79k | replacement.body.statements.resize(replacement.body.statements.size() - 1); |
123 | 8.19k | _st = std::move(replacement); |
124 | 8.19k | } |
125 | 973k | } |
126 | | |
127 | 1.16M | m_numBreakStatements = outerBreak; |
128 | 1.16M | m_numContinueStatements = outerContinue; |
129 | 1.16M | } |
130 | 50.2M | else |
131 | 50.2M | ASTModifier::visit(_st); |
132 | 51.4M | } |
133 | | |
134 | | void ControlFlowSimplifier::simplify(std::vector<yul::Statement>& _statements) |
135 | 7.14M | { |
136 | 7.14M | GenericVisitor visitor{ |
137 | 7.14M | VisitorFallback<OptionalStatements>{}, |
138 | 7.14M | [&](If& _ifStmt) -> OptionalStatements { |
139 | 788k | if (_ifStmt.body.statements.empty() && m_dialect.discardFunctionHandle()) |
140 | 23.3k | { |
141 | 23.3k | OptionalStatements s = std::vector<Statement>{}; |
142 | 23.3k | s->emplace_back(makeDiscardCall( |
143 | 23.3k | _ifStmt.debugData, |
144 | 23.3k | *m_dialect.discardFunctionHandle(), |
145 | 23.3k | std::move(*_ifStmt.condition) |
146 | 23.3k | )); |
147 | 23.3k | return s; |
148 | 23.3k | } |
149 | 765k | return {}; |
150 | 788k | }, |
151 | 7.14M | [&](Switch& _switchStmt) -> OptionalStatements { |
152 | 193k | removeEmptyDefaultFromSwitch(_switchStmt); |
153 | 193k | removeEmptyCasesFromSwitch(_switchStmt); |
154 | | |
155 | 193k | if (_switchStmt.cases.empty()) |
156 | 13.2k | return reduceNoCaseSwitch(_switchStmt); |
157 | 180k | else if (_switchStmt.cases.size() == 1) |
158 | 17.4k | return reduceSingleCaseSwitch(_switchStmt); |
159 | | |
160 | 162k | return {}; |
161 | 193k | } |
162 | 7.14M | }; |
163 | 7.14M | iterateReplacing( |
164 | 7.14M | _statements, |
165 | 7.14M | [&](Statement& _stmt) -> OptionalStatements |
166 | 51.4M | { |
167 | 51.4M | OptionalStatements result = std::visit(visitor, _stmt); |
168 | 51.4M | if (result) |
169 | 54.0k | simplify(*result); |
170 | 51.4M | else |
171 | 51.4M | visit(_stmt); |
172 | 51.4M | return result; |
173 | 51.4M | } |
174 | 7.14M | ); |
175 | 7.14M | } |
176 | | |
177 | | OptionalStatements ControlFlowSimplifier::reduceNoCaseSwitch(Switch& _switchStmt) const |
178 | 13.2k | { |
179 | 13.2k | yulAssert(_switchStmt.cases.empty(), "Expected no case!"); |
180 | 13.2k | std::optional<BuiltinHandle> discardFunctionHandle = |
181 | 13.2k | m_dialect.discardFunctionHandle(); |
182 | 13.2k | if (!discardFunctionHandle) |
183 | 0 | return {}; |
184 | | |
185 | 13.2k | return make_vector<Statement>(makeDiscardCall( |
186 | 13.2k | debugDataOf(*_switchStmt.expression), |
187 | 13.2k | *discardFunctionHandle, |
188 | 13.2k | std::move(*_switchStmt.expression) |
189 | 13.2k | )); |
190 | 13.2k | } |
191 | | |
192 | | OptionalStatements ControlFlowSimplifier::reduceSingleCaseSwitch(Switch& _switchStmt) const |
193 | 17.4k | { |
194 | 17.4k | yulAssert(_switchStmt.cases.size() == 1, "Expected only one case!"); |
195 | | |
196 | 17.4k | auto& switchCase = _switchStmt.cases.front(); |
197 | 17.4k | langutil::DebugData::ConstPtr debugData = debugDataOf(*_switchStmt.expression); |
198 | 17.4k | if (switchCase.value) |
199 | 6.62k | { |
200 | 6.62k | if (!m_dialect.equalityFunctionHandle()) |
201 | 0 | return {}; |
202 | 6.62k | BuiltinName const builtinName{debugData, *m_dialect.equalityFunctionHandle()}; |
203 | 6.62k | return make_vector<Statement>(If{ |
204 | 6.62k | std::move(_switchStmt.debugData), |
205 | 6.62k | std::make_unique<Expression>(FunctionCall{ |
206 | 6.62k | debugData, |
207 | 6.62k | builtinName, |
208 | 6.62k | {std::move(*switchCase.value), std::move(*_switchStmt.expression)} |
209 | 6.62k | }), |
210 | 6.62k | std::move(switchCase.body) |
211 | 6.62k | }); |
212 | 6.62k | } |
213 | 10.7k | else |
214 | 10.7k | { |
215 | 10.7k | if (!m_dialect.discardFunctionHandle()) |
216 | 0 | return {}; |
217 | | |
218 | 10.7k | return make_vector<Statement>( |
219 | 10.7k | makeDiscardCall( |
220 | 10.7k | debugData, |
221 | 10.7k | *m_dialect.discardFunctionHandle(), |
222 | 10.7k | std::move(*_switchStmt.expression) |
223 | 10.7k | ), |
224 | 10.7k | std::move(switchCase.body) |
225 | 10.7k | ); |
226 | 10.7k | } |
227 | 17.4k | } |
228 | | |