/src/solidity/libyul/optimiser/Suite.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 | | /** |
19 | | * Optimiser suite that combines all steps and also provides the settings for the heuristics. |
20 | | */ |
21 | | |
22 | | #include <libyul/optimiser/Suite.h> |
23 | | |
24 | | #include <libyul/optimiser/Disambiguator.h> |
25 | | #include <libyul/optimiser/VarDeclInitializer.h> |
26 | | #include <libyul/optimiser/BlockFlattener.h> |
27 | | #include <libyul/optimiser/CallGraphGenerator.h> |
28 | | #include <libyul/optimiser/CircularReferencesPruner.h> |
29 | | #include <libyul/optimiser/ControlFlowSimplifier.h> |
30 | | #include <libyul/optimiser/ConditionalSimplifier.h> |
31 | | #include <libyul/optimiser/ConditionalUnsimplifier.h> |
32 | | #include <libyul/optimiser/DeadCodeEliminator.h> |
33 | | #include <libyul/optimiser/FunctionGrouper.h> |
34 | | #include <libyul/optimiser/FunctionHoister.h> |
35 | | #include <libyul/optimiser/EqualStoreEliminator.h> |
36 | | #include <libyul/optimiser/EquivalentFunctionCombiner.h> |
37 | | #include <libyul/optimiser/ExpressionSplitter.h> |
38 | | #include <libyul/optimiser/ExpressionJoiner.h> |
39 | | #include <libyul/optimiser/ExpressionInliner.h> |
40 | | #include <libyul/optimiser/FullInliner.h> |
41 | | #include <libyul/optimiser/ForLoopConditionIntoBody.h> |
42 | | #include <libyul/optimiser/ForLoopConditionOutOfBody.h> |
43 | | #include <libyul/optimiser/ForLoopInitRewriter.h> |
44 | | #include <libyul/optimiser/ForLoopConditionIntoBody.h> |
45 | | #include <libyul/optimiser/FunctionSpecializer.h> |
46 | | #include <libyul/optimiser/Rematerialiser.h> |
47 | | #include <libyul/optimiser/UnusedFunctionParameterPruner.h> |
48 | | #include <libyul/optimiser/UnusedPruner.h> |
49 | | #include <libyul/optimiser/ExpressionSimplifier.h> |
50 | | #include <libyul/optimiser/CommonSubexpressionEliminator.h> |
51 | | #include <libyul/optimiser/Semantics.h> |
52 | | #include <libyul/optimiser/SSAReverser.h> |
53 | | #include <libyul/optimiser/SSATransform.h> |
54 | | #include <libyul/optimiser/StackCompressor.h> |
55 | | #include <libyul/optimiser/StackLimitEvader.h> |
56 | | #include <libyul/optimiser/StructuralSimplifier.h> |
57 | | #include <libyul/optimiser/SyntacticalEquality.h> |
58 | | #include <libyul/optimiser/UnusedAssignEliminator.h> |
59 | | #include <libyul/optimiser/UnusedStoreEliminator.h> |
60 | | #include <libyul/optimiser/VarNameCleaner.h> |
61 | | #include <libyul/optimiser/LoadResolver.h> |
62 | | #include <libyul/optimiser/LoopInvariantCodeMotion.h> |
63 | | #include <libyul/optimiser/Metrics.h> |
64 | | #include <libyul/optimiser/NameSimplifier.h> |
65 | | #include <libyul/backends/evm/ConstantOptimiser.h> |
66 | | #include <libyul/AsmAnalysis.h> |
67 | | #include <libyul/AsmAnalysisInfo.h> |
68 | | #include <libyul/AsmPrinter.h> |
69 | | #include <libyul/AST.h> |
70 | | #include <libyul/Object.h> |
71 | | |
72 | | #include <libyul/backends/evm/NoOutputAssembly.h> |
73 | | |
74 | | #include <libsolutil/CommonData.h> |
75 | | #include <libsolutil/Profiler.h> |
76 | | |
77 | | #include <libyul/CompilabilityChecker.h> |
78 | | |
79 | | #include <range/v3/view/map.hpp> |
80 | | #include <range/v3/action/remove.hpp> |
81 | | #include <range/v3/algorithm/count.hpp> |
82 | | #include <range/v3/algorithm/none_of.hpp> |
83 | | |
84 | | #include <limits> |
85 | | #include <tuple> |
86 | | |
87 | | using namespace solidity; |
88 | | using namespace solidity::yul; |
89 | | using namespace std::string_literals; |
90 | | |
91 | | void OptimiserSuite::run( |
92 | | GasMeter const* _meter, |
93 | | Object& _object, |
94 | | bool _optimizeStackAllocation, |
95 | | std::string_view _optimisationSequence, |
96 | | std::string_view _optimisationCleanupSequence, |
97 | | std::optional<std::uint64_t> _expectedExecutionsPerDeployment, |
98 | | std::set<YulName> const& _externallyUsedIdentifiers |
99 | | ) |
100 | 97.1k | { |
101 | 97.1k | yulAssert(_object.dialect()); |
102 | 97.1k | auto const& dialect = *_object.dialect(); |
103 | 97.1k | EVMDialect const* evmDialect = dynamic_cast<EVMDialect const*>(_object.dialect()); |
104 | 97.1k | bool usesOptimizedCodeGenerator = |
105 | 97.1k | _optimizeStackAllocation && |
106 | 97.1k | evmDialect && |
107 | 97.1k | evmDialect->evmVersion().canOverchargeGasForCall() && |
108 | 77.2k | evmDialect->providesObjectAccess(); |
109 | 97.1k | std::set<YulName> reservedIdentifiers = _externallyUsedIdentifiers; |
110 | | |
111 | 97.1k | Block astRoot; |
112 | 97.1k | { |
113 | 97.1k | PROFILER_PROBE("Disambiguator", probe); |
114 | 97.1k | astRoot = std::get<Block>(Disambiguator( |
115 | 97.1k | dialect, |
116 | 97.1k | *_object.analysisInfo, |
117 | 97.1k | reservedIdentifiers |
118 | 97.1k | )(_object.code()->root())); |
119 | 97.1k | } |
120 | | |
121 | 97.1k | NameDispenser dispenser{dialect, astRoot, reservedIdentifiers}; |
122 | 97.1k | OptimiserStepContext context{dialect, dispenser, reservedIdentifiers, _expectedExecutionsPerDeployment}; |
123 | | |
124 | 97.1k | OptimiserSuite suite(context, Debug::None); |
125 | | |
126 | | // Some steps depend on properties ensured by FunctionHoister, BlockFlattener, FunctionGrouper and |
127 | | // ForLoopInitRewriter. Run them first to be able to run arbitrary sequences safely. |
128 | 97.1k | suite.runSequence("hgfo", astRoot); |
129 | | |
130 | | // Now the user-supplied part |
131 | 97.1k | suite.runSequence(_optimisationSequence, astRoot); |
132 | | |
133 | | // This is a tuning parameter, but actually just prevents infinite loops. |
134 | 97.1k | size_t stackCompressorMaxIterations = 16; |
135 | 97.1k | suite.runSequence("g", astRoot); |
136 | | |
137 | | // We ignore the return value because we will get a much better error |
138 | | // message once we perform code generation. |
139 | 97.1k | if (!usesOptimizedCodeGenerator) |
140 | 22.2k | { |
141 | 22.2k | PROFILER_PROBE("StackCompressor", probe); |
142 | 22.2k | _object.setCode(std::make_shared<AST>(dialect, std::move(astRoot))); |
143 | 22.2k | astRoot = std::get<1>(StackCompressor::run( |
144 | 22.2k | _object, |
145 | 22.2k | _optimizeStackAllocation, |
146 | 22.2k | stackCompressorMaxIterations |
147 | 22.2k | )); |
148 | 22.2k | } |
149 | | |
150 | | // Run the user-supplied clean up sequence |
151 | 97.1k | suite.runSequence(_optimisationCleanupSequence, astRoot); |
152 | | // Hard-coded FunctionGrouper step is used to bring the AST into a canonical form required by the StackCompressor |
153 | | // and StackLimitEvader. This is hard-coded as the last step, as some previously executed steps may break the |
154 | | // aforementioned form, thus causing the StackCompressor/StackLimitEvader to throw. |
155 | 97.1k | suite.runSequence("g", astRoot); |
156 | | |
157 | 97.1k | if (evmDialect) |
158 | 97.1k | { |
159 | 97.1k | yulAssert(_meter, ""); |
160 | 97.1k | { |
161 | 97.1k | PROFILER_PROBE("ConstantOptimiser", probe); |
162 | 97.1k | ConstantOptimiser{*evmDialect, *_meter}(astRoot); |
163 | 97.1k | } |
164 | 97.1k | if (usesOptimizedCodeGenerator) |
165 | 74.9k | { |
166 | 74.9k | { |
167 | 74.9k | PROFILER_PROBE("StackCompressor", probe); |
168 | 74.9k | _object.setCode(std::make_shared<AST>(dialect, std::move(astRoot))); |
169 | 74.9k | astRoot = std::get<1>(StackCompressor::run( |
170 | 74.9k | _object, |
171 | 74.9k | _optimizeStackAllocation, |
172 | 74.9k | stackCompressorMaxIterations |
173 | 74.9k | )); |
174 | 74.9k | } |
175 | 74.9k | if (evmDialect->providesObjectAccess()) |
176 | 74.9k | { |
177 | 74.9k | PROFILER_PROBE("StackLimitEvader", probe); |
178 | 74.9k | _object.setCode(std::make_shared<AST>(dialect, std::move(astRoot))); |
179 | 74.9k | astRoot = StackLimitEvader::run(suite.m_context, _object); |
180 | 74.9k | } |
181 | 74.9k | } |
182 | 22.2k | else if (evmDialect->providesObjectAccess() && _optimizeStackAllocation) |
183 | 19.6k | { |
184 | 19.6k | PROFILER_PROBE("StackLimitEvader", probe); |
185 | 19.6k | _object.setCode(std::make_shared<AST>(dialect, std::move(astRoot))); |
186 | 19.6k | astRoot = StackLimitEvader::run(suite.m_context, _object); |
187 | 19.6k | } |
188 | 97.1k | } |
189 | | |
190 | 97.1k | dispenser.reset(astRoot); |
191 | 97.1k | { |
192 | 97.1k | PROFILER_PROBE("NameSimplifier", probe); |
193 | 97.1k | NameSimplifier::run(suite.m_context, astRoot); |
194 | 97.1k | } |
195 | 97.1k | { |
196 | 97.1k | PROFILER_PROBE("VarNameCleaner", probe); |
197 | 97.1k | VarNameCleaner::run(suite.m_context, astRoot); |
198 | 97.1k | } |
199 | | |
200 | 97.1k | _object.setCode(std::make_shared<AST>(dialect, std::move(astRoot))); |
201 | 97.1k | _object.analysisInfo = std::make_shared<AsmAnalysisInfo>(AsmAnalyzer::analyzeStrictAssertCorrect(_object)); |
202 | 97.1k | } |
203 | | |
204 | | namespace |
205 | | { |
206 | | |
207 | | template <class... Step> |
208 | | std::map<std::string, std::unique_ptr<OptimiserStep>> optimiserStepCollection() |
209 | 8 | { |
210 | 8 | std::map<std::string, std::unique_ptr<OptimiserStep>> ret; |
211 | 8 | for (std::unique_ptr<OptimiserStep>& s: util::make_vector<std::unique_ptr<OptimiserStep>>( |
212 | 8 | (std::make_unique<OptimiserStepInstance<Step>>())... |
213 | 8 | )) |
214 | 256 | { |
215 | 256 | yulAssert(!ret.count(s->name), ""); |
216 | 256 | ret[s->name] = std::move(s); |
217 | 256 | } |
218 | 8 | return ret; |
219 | 8 | } |
220 | | |
221 | | } |
222 | | |
223 | | std::map<std::string, std::unique_ptr<OptimiserStep>> const& OptimiserSuite::allSteps() |
224 | 25.6M | { |
225 | 25.6M | static std::map<std::string, std::unique_ptr<OptimiserStep>> instance; |
226 | 25.6M | if (instance.empty()) |
227 | 8 | instance = optimiserStepCollection< |
228 | 8 | BlockFlattener, |
229 | 8 | CircularReferencesPruner, |
230 | 8 | CommonSubexpressionEliminator, |
231 | 8 | ConditionalSimplifier, |
232 | 8 | ConditionalUnsimplifier, |
233 | 8 | ControlFlowSimplifier, |
234 | 8 | DeadCodeEliminator, |
235 | 8 | EqualStoreEliminator, |
236 | 8 | EquivalentFunctionCombiner, |
237 | 8 | ExpressionInliner, |
238 | 8 | ExpressionJoiner, |
239 | 8 | ExpressionSimplifier, |
240 | 8 | ExpressionSplitter, |
241 | 8 | ForLoopConditionIntoBody, |
242 | 8 | ForLoopConditionOutOfBody, |
243 | 8 | ForLoopInitRewriter, |
244 | 8 | FullInliner, |
245 | 8 | FunctionGrouper, |
246 | 8 | FunctionHoister, |
247 | 8 | FunctionSpecializer, |
248 | 8 | LiteralRematerialiser, |
249 | 8 | LoadResolver, |
250 | 8 | LoopInvariantCodeMotion, |
251 | 8 | UnusedAssignEliminator, |
252 | 8 | UnusedStoreEliminator, |
253 | 8 | Rematerialiser, |
254 | 8 | SSAReverser, |
255 | 8 | SSATransform, |
256 | 8 | StructuralSimplifier, |
257 | 8 | UnusedFunctionParameterPruner, |
258 | 8 | UnusedPruner, |
259 | 8 | VarDeclInitializer |
260 | 8 | >(); |
261 | | // Does not include VarNameCleaner because it destroys the property of unique names. |
262 | | // Does not include NameSimplifier. |
263 | 25.6M | return instance; |
264 | 25.6M | } |
265 | | |
266 | | std::map<std::string, char> const& OptimiserSuite::stepNameToAbbreviationMap() |
267 | 8 | { |
268 | 8 | static std::map<std::string, char> lookupTable{ |
269 | 8 | {BlockFlattener::name, 'f'}, |
270 | 8 | {CircularReferencesPruner::name, 'l'}, |
271 | 8 | {CommonSubexpressionEliminator::name, 'c'}, |
272 | 8 | {ConditionalSimplifier::name, 'C'}, |
273 | 8 | {ConditionalUnsimplifier::name, 'U'}, |
274 | 8 | {ControlFlowSimplifier::name, 'n'}, |
275 | 8 | {DeadCodeEliminator::name, 'D'}, |
276 | 8 | {EqualStoreEliminator::name, 'E'}, |
277 | 8 | {EquivalentFunctionCombiner::name, 'v'}, |
278 | 8 | {ExpressionInliner::name, 'e'}, |
279 | 8 | {ExpressionJoiner::name, 'j'}, |
280 | 8 | {ExpressionSimplifier::name, 's'}, |
281 | 8 | {ExpressionSplitter::name, 'x'}, |
282 | 8 | {ForLoopConditionIntoBody::name, 'I'}, |
283 | 8 | {ForLoopConditionOutOfBody::name, 'O'}, |
284 | 8 | {ForLoopInitRewriter::name, 'o'}, |
285 | 8 | {FullInliner::name, 'i'}, |
286 | 8 | {FunctionGrouper::name, 'g'}, |
287 | 8 | {FunctionHoister::name, 'h'}, |
288 | 8 | {FunctionSpecializer::name, 'F'}, |
289 | 8 | {LiteralRematerialiser::name, 'T'}, |
290 | 8 | {LoadResolver::name, 'L'}, |
291 | 8 | {LoopInvariantCodeMotion::name, 'M'}, |
292 | 8 | {UnusedAssignEliminator::name, 'r'}, |
293 | 8 | {UnusedStoreEliminator::name, 'S'}, |
294 | 8 | {Rematerialiser::name, 'm'}, |
295 | 8 | {SSAReverser::name, 'V'}, |
296 | 8 | {SSATransform::name, 'a'}, |
297 | 8 | {StructuralSimplifier::name, 't'}, |
298 | 8 | {UnusedFunctionParameterPruner::name, 'p'}, |
299 | 8 | {UnusedPruner::name, 'u'}, |
300 | 8 | {VarDeclInitializer::name, 'd'}, |
301 | 8 | }; |
302 | 8 | yulAssert(lookupTable.size() == allSteps().size(), ""); |
303 | 8 | yulAssert(( |
304 | 8 | util::convertContainer<std::set<char>>(std::string(NonStepAbbreviations)) - |
305 | 8 | util::convertContainer<std::set<char>>(lookupTable | ranges::views::values) |
306 | 8 | ).size() == std::string(NonStepAbbreviations).size(), |
307 | 8 | "Step abbreviation conflicts with a character reserved for another syntactic element" |
308 | 8 | ); |
309 | | |
310 | 8 | return lookupTable; |
311 | 8 | } |
312 | | |
313 | | std::map<char, std::string> const& OptimiserSuite::stepAbbreviationToNameMap() |
314 | 52.2M | { |
315 | 52.2M | static std::map<char, std::string> lookupTable = util::invertMap(stepNameToAbbreviationMap()); |
316 | | |
317 | 52.2M | return lookupTable; |
318 | 52.2M | } |
319 | | |
320 | | void OptimiserSuite::validateSequence(std::string_view _stepAbbreviations) |
321 | 1.13M | { |
322 | 1.13M | int8_t nestingLevel = 0; |
323 | 1.13M | int8_t colonDelimiters = 0; |
324 | 1.13M | for (char abbreviation: _stepAbbreviations) |
325 | 14.9M | switch (abbreviation) |
326 | 14.9M | { |
327 | 370k | case ' ': |
328 | 370k | case '\n': |
329 | 370k | break; |
330 | 648k | case '[': |
331 | 648k | assertThrow(nestingLevel < std::numeric_limits<int8_t>::max(), OptimizerException, "Brackets nested too deep"); |
332 | 648k | nestingLevel++; |
333 | 648k | break; |
334 | 648k | case ']': |
335 | 648k | nestingLevel--; |
336 | 648k | assertThrow(nestingLevel >= 0, OptimizerException, "Unbalanced brackets"); |
337 | 648k | break; |
338 | 0 | case ':': |
339 | 0 | ++colonDelimiters; |
340 | 0 | assertThrow(nestingLevel == 0, OptimizerException, "Cleanup sequence delimiter cannot be placed inside the brackets"); |
341 | 0 | assertThrow(colonDelimiters <= 1, OptimizerException, "Too many cleanup sequence delimiters"); |
342 | 0 | break; |
343 | 13.2M | default: |
344 | 13.2M | { |
345 | 13.2M | yulAssert( |
346 | 13.2M | std::string(NonStepAbbreviations).find(abbreviation) == std::string::npos, |
347 | 13.2M | "Unhandled syntactic element in the abbreviation sequence" |
348 | 13.2M | ); |
349 | 13.2M | assertThrow( |
350 | 13.2M | stepAbbreviationToNameMap().find(abbreviation) != stepAbbreviationToNameMap().end(), |
351 | 13.2M | OptimizerException, |
352 | 13.2M | "'"s + abbreviation + "' is not a valid step abbreviation" |
353 | 13.2M | ); |
354 | 13.2M | std::optional<std::string> invalid = allSteps().at(stepAbbreviationToNameMap().at(abbreviation))->invalidInCurrentEnvironment(); |
355 | 13.2M | assertThrow( |
356 | 13.2M | !invalid.has_value(), |
357 | 13.2M | OptimizerException, |
358 | 13.2M | "'"s + abbreviation + "' is invalid in the current environment: " + *invalid |
359 | 13.2M | ); |
360 | 13.2M | } |
361 | 14.9M | } |
362 | 1.13M | assertThrow(nestingLevel == 0, OptimizerException, "Unbalanced brackets"); |
363 | 1.13M | } |
364 | | |
365 | | bool OptimiserSuite::isEmptyOptimizerSequence(std::string const& _sequence) |
366 | 2.21k | { |
367 | 2.21k | return |
368 | 2.21k | ranges::count(_sequence, ':') == 1 && |
369 | 2.21k | ranges::none_of(_sequence, [](auto _step) { return _step != ':' && _step != ' ' && _step != '\n'; }); |
370 | 2.21k | } |
371 | | |
372 | | void OptimiserSuite::runSequence(std::string_view _stepAbbreviations, Block& _ast, bool _repeatUntilStable) |
373 | 1.13M | { |
374 | 1.13M | validateSequence(_stepAbbreviations); |
375 | | |
376 | | // This splits 'aaa[bbb]ccc...' into 'aaa' and '[bbb]ccc...'. |
377 | 1.13M | auto extractNonNestedPrefix = [](std::string_view _tail) -> std::tuple<std::string_view, std::string_view> |
378 | 1.77M | { |
379 | 14.1M | for (size_t i = 0; i < _tail.size(); ++i) |
380 | 13.0M | { |
381 | 13.0M | yulAssert(_tail[i] != ']'); |
382 | 13.0M | if (_tail[i] == '[') |
383 | 648k | return {_tail.substr(0, i), _tail.substr(i)}; |
384 | 13.0M | } |
385 | 1.13M | return {_tail, {}}; |
386 | 1.77M | }; |
387 | | |
388 | | // This splits '[bbb]ccc...' into 'bbb' and 'ccc...'. |
389 | 1.13M | auto extractBracketContent = [](std::string_view _tail) -> std::tuple<std::string_view, std::string_view> |
390 | 1.13M | { |
391 | 648k | yulAssert(!_tail.empty() && _tail[0] == '['); |
392 | | |
393 | 648k | size_t contentLength = 0; |
394 | 648k | int8_t nestingLevel = 1; |
395 | 648k | for (char abbreviation: _tail.substr(1)) |
396 | 1.94M | { |
397 | 1.94M | if (abbreviation == '[') |
398 | 0 | { |
399 | 0 | yulAssert(nestingLevel < std::numeric_limits<int8_t>::max()); |
400 | 0 | ++nestingLevel; |
401 | 0 | } |
402 | 1.94M | else if (abbreviation == ']') |
403 | 648k | { |
404 | 648k | --nestingLevel; |
405 | 648k | if (nestingLevel == 0) |
406 | 648k | break; |
407 | 648k | } |
408 | 1.29M | ++contentLength; |
409 | 1.29M | } |
410 | 648k | yulAssert(nestingLevel == 0); |
411 | 648k | yulAssert(_tail[contentLength + 1] == ']'); |
412 | | |
413 | 648k | return {_tail.substr(1, contentLength), _tail.substr(contentLength + 2)}; |
414 | 648k | }; |
415 | | |
416 | 1.13M | auto abbreviationsToSteps = [](std::string_view _sequence) -> std::vector<std::string> |
417 | 1.83M | { |
418 | 1.83M | std::vector<std::string> steps; |
419 | 1.83M | for (char abbreviation: _sequence) |
420 | 12.7M | if (abbreviation != ' ' && abbreviation != '\n') |
421 | 12.3M | steps.emplace_back(stepAbbreviationToNameMap().at(abbreviation)); |
422 | 1.83M | return steps; |
423 | 1.83M | }; |
424 | | |
425 | 1.13M | std::vector<std::tuple<std::string_view, bool>> subsequences; |
426 | 1.13M | std::string_view tail = _stepAbbreviations; |
427 | 1.78M | while (!tail.empty()) |
428 | 1.77M | { |
429 | 1.77M | std::string_view subsequence; |
430 | 1.77M | tie(subsequence, tail) = extractNonNestedPrefix(tail); |
431 | 1.77M | if (subsequence.size() > 0) |
432 | 1.77M | subsequences.push_back({subsequence, false}); |
433 | | |
434 | 1.77M | if (tail.empty()) |
435 | 1.13M | break; |
436 | | |
437 | 648k | tie(subsequence, tail) = extractBracketContent(tail); |
438 | 648k | if (subsequence.size() > 0) |
439 | 648k | subsequences.push_back({subsequence, true}); |
440 | 648k | } |
441 | | |
442 | | // NOTE: If _repeatUntilStable is false, the value will not be used so do not calculate it. |
443 | 1.13M | size_t codeSize = (_repeatUntilStable ? CodeSize::codeSizeIncludingFunctions(_ast) : 0); |
444 | | |
445 | 1.19M | for (size_t round = 0; round < MaxRounds; ++round) |
446 | 1.19M | { |
447 | 1.19M | for (auto const& [subsequence, repeat]: subsequences) |
448 | 2.48M | { |
449 | 2.48M | if (repeat) |
450 | 648k | runSequence(subsequence, _ast, true); |
451 | 1.83M | else |
452 | 1.83M | runSequence(abbreviationsToSteps(subsequence), _ast); |
453 | 2.48M | } |
454 | | |
455 | 1.19M | if (!_repeatUntilStable) |
456 | 485k | break; |
457 | | |
458 | 707k | size_t newSize = CodeSize::codeSizeIncludingFunctions(_ast); |
459 | 707k | if (newSize == codeSize) |
460 | 648k | break; |
461 | 58.9k | codeSize = newSize; |
462 | 58.9k | } |
463 | 1.13M | } |
464 | | |
465 | | void OptimiserSuite::runSequence(std::vector<std::string> const& _steps, Block& _ast) |
466 | 1.83M | { |
467 | 1.83M | std::unique_ptr<Block> copy; |
468 | 1.83M | if (m_debug == Debug::PrintChanges) |
469 | 0 | copy = std::make_unique<Block>(std::get<Block>(ASTCopier{}(_ast))); |
470 | 1.83M | for (std::string const& step: _steps) |
471 | 12.3M | { |
472 | 12.3M | if (m_debug == Debug::PrintStep) |
473 | 0 | std::cout << "Running " << step << std::endl; |
474 | | |
475 | 12.3M | { |
476 | 12.3M | PROFILER_PROBE(step, probe); |
477 | 12.3M | allSteps().at(step)->run(m_context, _ast); |
478 | 12.3M | } |
479 | | |
480 | 12.3M | if (m_debug == Debug::PrintChanges) |
481 | 0 | { |
482 | | // TODO should add switch to also compare variable names! |
483 | 0 | if (SyntacticallyEqual{}.statementEqual(_ast, *copy)) |
484 | 0 | std::cout << "== Running " << step << " did not cause changes." << std::endl; |
485 | 0 | else |
486 | 0 | { |
487 | 0 | std::cout << "== Running " << step << " changed the AST." << std::endl; |
488 | 0 | std::cout << AsmPrinter{m_context.dialect}(_ast) << std::endl; |
489 | 0 | copy = std::make_unique<Block>(std::get<Block>(ASTCopier{}(_ast))); |
490 | 0 | } |
491 | 0 | } |
492 | 12.3M | } |
493 | 1.83M | } |