/src/solidity/libyul/optimiser/ControlFlowSimplifier.cpp
Line | Count | Source |
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 | 72.1k | { |
44 | 72.1k | return {_debugData, FunctionCall{ |
45 | 72.1k | _debugData, |
46 | 72.1k | BuiltinName{_debugData, _discardFunction}, |
47 | 72.1k | {std::move(_expression)} |
48 | 72.1k | }}; |
49 | 72.1k | } |
50 | | |
51 | | void removeEmptyDefaultFromSwitch(Switch& _switchStmt) |
52 | 309k | { |
53 | 309k | ranges::actions::remove_if( |
54 | 309k | _switchStmt.cases, |
55 | 929k | [](Case const& _case) { return !_case.value && _case.body.statements.empty(); } |
56 | 309k | ); |
57 | 309k | } |
58 | | |
59 | | void removeEmptyCasesFromSwitch(Switch& _switchStmt) |
60 | 309k | { |
61 | 309k | if (hasDefaultCase(_switchStmt)) |
62 | 215k | return; |
63 | | |
64 | 94.7k | ranges::actions::remove_if( |
65 | 94.7k | _switchStmt.cases, |
66 | 182k | [](Case const& _case) { return _case.body.statements.empty(); } |
67 | 94.7k | ); |
68 | 94.7k | } |
69 | | |
70 | | } |
71 | | |
72 | | void ControlFlowSimplifier::run(OptimiserStepContext& _context, Block& _ast) |
73 | 622k | { |
74 | 622k | ControlFlowSimplifier{_context.dialect}(_ast); |
75 | 622k | } |
76 | | |
77 | | void ControlFlowSimplifier::operator()(Block& _block) |
78 | 8.57M | { |
79 | 8.57M | simplify(_block.statements); |
80 | 8.57M | } |
81 | | |
82 | | void ControlFlowSimplifier::operator()(FunctionDefinition& _funDef) |
83 | 1.52M | { |
84 | 1.52M | ASTModifier::operator()(_funDef); |
85 | 1.52M | if (!_funDef.body.statements.empty() && std::holds_alternative<Leave>(_funDef.body.statements.back())) |
86 | 15.3k | _funDef.body.statements.pop_back(); |
87 | 1.52M | } |
88 | | |
89 | | void ControlFlowSimplifier::visit(Statement& _st) |
90 | 62.7M | { |
91 | 62.7M | if (std::holds_alternative<ForLoop>(_st)) |
92 | 1.33M | { |
93 | 1.33M | ForLoop& forLoop = std::get<ForLoop>(_st); |
94 | 1.33M | yulAssert(forLoop.pre.statements.empty(), ""); |
95 | | |
96 | 1.33M | size_t outerBreak = m_numBreakStatements; |
97 | 1.33M | size_t outerContinue = m_numContinueStatements; |
98 | 1.33M | m_numBreakStatements = 0; |
99 | 1.33M | m_numContinueStatements = 0; |
100 | | |
101 | 1.33M | ASTModifier::visit(_st); |
102 | | |
103 | 1.33M | if (!forLoop.body.statements.empty()) |
104 | 1.12M | { |
105 | 1.12M | bool isTerminating = false; |
106 | 1.12M | TerminationFinder::ControlFlow controlFlow = TerminationFinder{m_dialect}.controlFlowKind(forLoop.body.statements.back()); |
107 | 1.12M | if (controlFlow == TerminationFinder::ControlFlow::Break) |
108 | 17.5k | { |
109 | 17.5k | isTerminating = true; |
110 | 17.5k | --m_numBreakStatements; |
111 | 17.5k | } |
112 | 1.10M | else if ( |
113 | 1.10M | controlFlow == TerminationFinder::ControlFlow::Terminate || |
114 | 1.08M | controlFlow == TerminationFinder::ControlFlow::Leave |
115 | 1.10M | ) |
116 | 24.1k | isTerminating = true; |
117 | | |
118 | 1.12M | if (isTerminating && m_numContinueStatements == 0 && m_numBreakStatements == 0) |
119 | 11.9k | { |
120 | 11.9k | If replacement{forLoop.debugData, std::move(forLoop.condition), std::move(forLoop.body)}; |
121 | 11.9k | if (controlFlow == TerminationFinder::ControlFlow::Break) |
122 | 6.17k | replacement.body.statements.resize(replacement.body.statements.size() - 1); |
123 | 11.9k | _st = std::move(replacement); |
124 | 11.9k | } |
125 | 1.12M | } |
126 | | |
127 | 1.33M | m_numBreakStatements = outerBreak; |
128 | 1.33M | m_numContinueStatements = outerContinue; |
129 | 1.33M | } |
130 | 61.3M | else |
131 | 61.3M | ASTModifier::visit(_st); |
132 | 62.7M | } |
133 | | |
134 | | void ControlFlowSimplifier::simplify(std::vector<yul::Statement>& _statements) |
135 | 8.66M | { |
136 | 8.66M | GenericVisitor visitor{ |
137 | 8.66M | VisitorFallback<OptionalStatements>{}, |
138 | 8.66M | [&](If& _ifStmt) -> OptionalStatements { |
139 | 910k | if (_ifStmt.body.statements.empty() && m_dialect.discardFunctionHandle()) |
140 | 30.1k | { |
141 | 30.1k | OptionalStatements s = std::vector<Statement>{}; |
142 | 30.1k | s->emplace_back(makeDiscardCall( |
143 | 30.1k | _ifStmt.debugData, |
144 | 30.1k | *m_dialect.discardFunctionHandle(), |
145 | 30.1k | std::move(*_ifStmt.condition) |
146 | 30.1k | )); |
147 | 30.1k | return s; |
148 | 30.1k | } |
149 | 880k | return {}; |
150 | 910k | }, |
151 | 8.66M | [&](Switch& _switchStmt) -> OptionalStatements { |
152 | 309k | removeEmptyDefaultFromSwitch(_switchStmt); |
153 | 309k | removeEmptyCasesFromSwitch(_switchStmt); |
154 | | |
155 | 309k | if (_switchStmt.cases.empty()) |
156 | 21.2k | return reduceNoCaseSwitch(_switchStmt); |
157 | 288k | else if (_switchStmt.cases.size() == 1) |
158 | 30.1k | return reduceSingleCaseSwitch(_switchStmt); |
159 | | |
160 | 258k | return {}; |
161 | 309k | } |
162 | 8.66M | }; |
163 | 8.66M | iterateReplacing( |
164 | 8.66M | _statements, |
165 | 8.66M | [&](Statement& _stmt) -> OptionalStatements |
166 | 62.7M | { |
167 | 62.7M | OptionalStatements result = std::visit(visitor, _stmt); |
168 | 62.7M | if (result) |
169 | 81.5k | simplify(*result); |
170 | 62.7M | else |
171 | 62.7M | visit(_stmt); |
172 | 62.7M | return result; |
173 | 62.7M | } |
174 | 8.66M | ); |
175 | 8.66M | } |
176 | | |
177 | | OptionalStatements ControlFlowSimplifier::reduceNoCaseSwitch(Switch& _switchStmt) const |
178 | 21.2k | { |
179 | 21.2k | yulAssert(_switchStmt.cases.empty(), "Expected no case!"); |
180 | 21.2k | std::optional<BuiltinHandle> discardFunctionHandle = |
181 | 21.2k | m_dialect.discardFunctionHandle(); |
182 | 21.2k | if (!discardFunctionHandle) |
183 | 0 | return {}; |
184 | | |
185 | 21.2k | return make_vector<Statement>(makeDiscardCall( |
186 | 21.2k | debugDataOf(*_switchStmt.expression), |
187 | 21.2k | *discardFunctionHandle, |
188 | 21.2k | std::move(*_switchStmt.expression) |
189 | 21.2k | )); |
190 | 21.2k | } |
191 | | |
192 | | OptionalStatements ControlFlowSimplifier::reduceSingleCaseSwitch(Switch& _switchStmt) const |
193 | 30.1k | { |
194 | 30.1k | yulAssert(_switchStmt.cases.size() == 1, "Expected only one case!"); |
195 | | |
196 | 30.1k | auto& switchCase = _switchStmt.cases.front(); |
197 | 30.1k | langutil::DebugData::ConstPtr debugData = debugDataOf(*_switchStmt.expression); |
198 | 30.1k | if (switchCase.value) |
199 | 9.38k | { |
200 | 9.38k | if (!m_dialect.equalityFunctionHandle()) |
201 | 0 | return {}; |
202 | 9.38k | BuiltinName const builtinName{debugData, *m_dialect.equalityFunctionHandle()}; |
203 | 9.38k | return make_vector<Statement>(If{ |
204 | 9.38k | std::move(_switchStmt.debugData), |
205 | 9.38k | std::make_unique<Expression>(FunctionCall{ |
206 | 9.38k | debugData, |
207 | 9.38k | builtinName, |
208 | 9.38k | {std::move(*switchCase.value), std::move(*_switchStmt.expression)} |
209 | 9.38k | }), |
210 | 9.38k | std::move(switchCase.body) |
211 | 9.38k | }); |
212 | 9.38k | } |
213 | 20.7k | else |
214 | 20.7k | { |
215 | 20.7k | if (!m_dialect.discardFunctionHandle()) |
216 | 0 | return {}; |
217 | | |
218 | 20.7k | return make_vector<Statement>( |
219 | 20.7k | makeDiscardCall( |
220 | 20.7k | debugData, |
221 | 20.7k | *m_dialect.discardFunctionHandle(), |
222 | 20.7k | std::move(*_switchStmt.expression) |
223 | 20.7k | ), |
224 | 20.7k | std::move(switchCase.body) |
225 | 20.7k | ); |
226 | 20.7k | } |
227 | 30.1k | } |
228 | | |