/src/solidity/libyul/optimiser/LoopInvariantCodeMotion.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 | | #include <libyul/optimiser/LoopInvariantCodeMotion.h> |
20 | | |
21 | | #include <libyul/optimiser/CallGraphGenerator.h> |
22 | | #include <libyul/optimiser/NameCollector.h> |
23 | | #include <libyul/optimiser/Semantics.h> |
24 | | #include <libyul/optimiser/SSAValueTracker.h> |
25 | | #include <libyul/AST.h> |
26 | | #include <libsolutil/CommonData.h> |
27 | | |
28 | | #include <utility> |
29 | | |
30 | | using namespace std; |
31 | | using namespace solidity; |
32 | | using namespace solidity::yul; |
33 | | |
34 | | void LoopInvariantCodeMotion::run(OptimiserStepContext& _context, Block& _ast) |
35 | 30.7k | { |
36 | 30.7k | map<YulString, SideEffects> functionSideEffects = |
37 | 30.7k | SideEffectsPropagator::sideEffects(_context.dialect, CallGraphGenerator::callGraph(_ast)); |
38 | 30.7k | bool containsMSize = MSizeFinder::containsMSize(_context.dialect, _ast); |
39 | 30.7k | set<YulString> ssaVars = SSAValueTracker::ssaVariables(_ast); |
40 | 30.7k | LoopInvariantCodeMotion{_context.dialect, ssaVars, functionSideEffects, containsMSize}(_ast); |
41 | 30.7k | } |
42 | | |
43 | | void LoopInvariantCodeMotion::operator()(Block& _block) |
44 | 472k | { |
45 | 472k | util::iterateReplacing( |
46 | 472k | _block.statements, |
47 | 472k | [&](Statement& _s) -> optional<vector<Statement>> |
48 | 2.96M | { |
49 | 2.96M | visit(_s); |
50 | 2.96M | if (holds_alternative<ForLoop>(_s)) |
51 | 73.4k | return rewriteLoop(get<ForLoop>(_s)); |
52 | 2.88M | else |
53 | 2.88M | return {}; |
54 | 2.96M | } |
55 | 472k | ); |
56 | 472k | } |
57 | | |
58 | | bool LoopInvariantCodeMotion::canBePromoted( |
59 | | VariableDeclaration const& _varDecl, |
60 | | set<YulString> const& _varsDefinedInCurrentScope, |
61 | | SideEffects const& _forLoopSideEffects |
62 | | ) const |
63 | 572k | { |
64 | | // A declaration can be promoted iff |
65 | | // 1. Its LHS is a SSA variable |
66 | | // 2. Its RHS only references SSA variables declared outside of the current scope |
67 | | // 3. Its RHS is movable |
68 | | |
69 | 572k | for (auto const& var: _varDecl.variables) |
70 | 572k | if (!m_ssaVariables.count(var.name)) |
71 | 7.26k | return false; |
72 | 564k | if (_varDecl.value) |
73 | 564k | { |
74 | 564k | for (auto const& ref: ReferencesCounter::countReferences(*_varDecl.value, ReferencesCounter::OnlyVariables)) |
75 | 463k | if (_varsDefinedInCurrentScope.count(ref.first) || !m_ssaVariables.count(ref.first)) |
76 | 114k | return false; |
77 | 450k | SideEffectsCollector sideEffects{m_dialect, *_varDecl.value, &m_functionSideEffects}; |
78 | 450k | if (!sideEffects.movableRelativeTo(_forLoopSideEffects, m_containsMSize)) |
79 | 1.54k | return false; |
80 | 450k | } |
81 | 448k | return true; |
82 | 564k | } |
83 | | |
84 | | optional<vector<Statement>> LoopInvariantCodeMotion::rewriteLoop(ForLoop& _for) |
85 | 73.4k | { |
86 | 73.4k | assertThrow(_for.pre.statements.empty(), OptimizerException, ""); |
87 | | |
88 | 73.4k | auto forLoopSideEffects = |
89 | 73.4k | SideEffectsCollector{m_dialect, _for, &m_functionSideEffects}.sideEffects(); |
90 | | |
91 | 73.4k | vector<Statement> replacement; |
92 | 73.4k | for (Block* block: {&_for.post, &_for.body}) |
93 | 146k | { |
94 | 146k | set<YulString> varsDefinedInScope; |
95 | 146k | util::iterateReplacing( |
96 | 146k | block->statements, |
97 | 146k | [&](Statement& _s) -> optional<vector<Statement>> |
98 | 1.08M | { |
99 | 1.08M | if (holds_alternative<VariableDeclaration>(_s)) |
100 | 572k | { |
101 | 572k | VariableDeclaration const& varDecl = std::get<VariableDeclaration>(_s); |
102 | 572k | if (canBePromoted(varDecl, varsDefinedInScope, forLoopSideEffects)) |
103 | 448k | { |
104 | 448k | replacement.emplace_back(std::move(_s)); |
105 | | // Do not add the variables declared here to varsDefinedInScope because we are moving them. |
106 | 448k | return vector<Statement>{}; |
107 | 448k | } |
108 | 123k | for (auto const& var: varDecl.variables) |
109 | 124k | varsDefinedInScope.insert(var.name); |
110 | 123k | } |
111 | 637k | return {}; |
112 | 1.08M | } |
113 | 146k | ); |
114 | 146k | } |
115 | 73.4k | if (replacement.empty()) |
116 | 10.6k | return {}; |
117 | 62.8k | else |
118 | 62.8k | { |
119 | 62.8k | replacement.emplace_back(std::move(_for)); |
120 | 62.8k | return { std::move(replacement) }; |
121 | 62.8k | } |
122 | 73.4k | } |