/src/solidity/libyul/optimiser/UnusedAssignEliminator.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 component that removes assignments to variables that are not used |
20 | | * until they go out of scope or are re-assigned. |
21 | | */ |
22 | | |
23 | | #include <libyul/optimiser/UnusedAssignEliminator.h> |
24 | | |
25 | | #include <libyul/optimiser/Semantics.h> |
26 | | #include <libyul/optimiser/OptimizerUtilities.h> |
27 | | #include <libyul/ControlFlowSideEffectsCollector.h> |
28 | | #include <libyul/Utilities.h> |
29 | | #include <libyul/AST.h> |
30 | | #include <libyul/AsmPrinter.h> |
31 | | |
32 | | #include <libsolutil/CommonData.h> |
33 | | |
34 | | #include <range/v3/action/remove_if.hpp> |
35 | | |
36 | | #include <iostream> |
37 | | |
38 | | using namespace solidity; |
39 | | using namespace solidity::yul; |
40 | | |
41 | | void UnusedAssignEliminator::run(OptimiserStepContext& _context, Block& _ast) |
42 | 582k | { |
43 | 582k | UnusedAssignEliminator uae{ |
44 | 582k | _context.dialect, |
45 | 582k | ControlFlowSideEffectsCollector{_context.dialect, _ast}.functionSideEffectsNamed() |
46 | 582k | }; |
47 | 582k | uae(_ast); |
48 | | |
49 | 582k | uae.m_storesToRemove += uae.m_allStores - uae.m_usedStores; |
50 | | |
51 | 582k | std::set<Statement const*> toRemove{uae.m_storesToRemove.begin(), uae.m_storesToRemove.end()}; |
52 | 582k | StatementRemover remover{toRemove}; |
53 | 582k | remover(_ast); |
54 | 582k | } |
55 | | |
56 | | void UnusedAssignEliminator::operator()(Identifier const& _identifier) |
57 | 103M | { |
58 | 103M | markUsed(_identifier.name); |
59 | 103M | } |
60 | | |
61 | | void UnusedAssignEliminator::operator()(Assignment const& _assignment) |
62 | 3.84M | { |
63 | 3.84M | visit(*_assignment.value); |
64 | | // Do not visit the variables because they are Identifiers |
65 | 3.84M | } |
66 | | |
67 | | |
68 | | void UnusedAssignEliminator::operator()(FunctionDefinition const& _functionDefinition) |
69 | 1.50M | { |
70 | 1.50M | ScopedSaveAndRestore outerReturnVariables(m_returnVariables, {}); |
71 | | |
72 | 1.50M | for (auto const& retParam: _functionDefinition.returnVariables) |
73 | 1.37M | m_returnVariables.insert(retParam.name); |
74 | | |
75 | 1.50M | UnusedStoreBase::operator()(_functionDefinition); |
76 | 1.50M | } |
77 | | |
78 | | void UnusedAssignEliminator::operator()(FunctionCall const& _functionCall) |
79 | 26.3M | { |
80 | 26.3M | UnusedStoreBase::operator()(_functionCall); |
81 | | |
82 | 26.3M | ControlFlowSideEffects sideEffects; |
83 | 26.3M | if (BuiltinFunction const* builtin = resolveBuiltinFunction(_functionCall.functionName, m_dialect)) |
84 | 22.2M | sideEffects = builtin->controlFlowSideEffects; |
85 | 4.01M | else |
86 | 4.01M | { |
87 | 4.01M | yulAssert(std::holds_alternative<Identifier>(_functionCall.functionName)); |
88 | 4.01M | sideEffects = m_controlFlowSideEffects.at(std::get<Identifier>(_functionCall.functionName).name); |
89 | 4.01M | } |
90 | | |
91 | 26.3M | if (!sideEffects.canContinue) |
92 | | // We do not return from the current function, so it is OK to also |
93 | | // clear the return variables. |
94 | 619k | m_activeStores.clear(); |
95 | 26.3M | } |
96 | | |
97 | | void UnusedAssignEliminator::operator()(Leave const&) |
98 | 216k | { |
99 | 216k | for (YulName name: m_returnVariables) |
100 | 79.5k | markUsed(name); |
101 | 216k | m_activeStores.clear(); |
102 | 216k | } |
103 | | |
104 | | void UnusedAssignEliminator::operator()(Block const& _block) |
105 | 11.4M | { |
106 | 11.4M | UnusedStoreBase::operator()(_block); |
107 | | |
108 | 11.4M | for (auto const& statement: _block.statements) |
109 | 103M | if (auto const* varDecl = std::get_if<VariableDeclaration>(&statement)) |
110 | 81.5M | for (auto const& var: varDecl->variables) |
111 | 82.2M | m_activeStores.erase(var.name); |
112 | 11.4M | } |
113 | | |
114 | | void UnusedAssignEliminator::visit(Statement const& _statement) |
115 | 103M | { |
116 | 103M | UnusedStoreBase::visit(_statement); |
117 | | |
118 | 103M | if (auto const* assignment = std::get_if<Assignment>(&_statement)) |
119 | 3.84M | { |
120 | | // We do not remove assignments whose values might have side-effects, |
121 | | // but clear the active stores to the assigned variables in any case. |
122 | 3.84M | if (SideEffectsCollector{m_dialect, *assignment->value}.movable()) |
123 | 3.81M | { |
124 | 3.81M | m_allStores.insert(&_statement); |
125 | 3.81M | for (auto const& var: assignment->variableNames) |
126 | 3.81M | m_activeStores[var.name] = {&_statement}; |
127 | 3.81M | } |
128 | 38.6k | else |
129 | 38.6k | for (auto const& var: assignment->variableNames) |
130 | 38.6k | m_activeStores[var.name].clear(); |
131 | 3.84M | } |
132 | 103M | } |
133 | | |
134 | | void UnusedAssignEliminator::shortcutNestedLoop(ActiveStores const& _zeroRuns) |
135 | 805k | { |
136 | | // Shortcut to avoid horrible runtime: |
137 | | // Change all assignments that were newly introduced in the for loop to "used". |
138 | | // We do not have to do that with the "break" or "continue" paths, because |
139 | | // they will be joined later anyway. |
140 | | |
141 | 805k | for (auto& [variable, stores]: m_activeStores) |
142 | 713k | { |
143 | 713k | auto zeroIt = _zeroRuns.find(variable); |
144 | 713k | for (auto& assignment: stores) |
145 | 751k | { |
146 | 751k | if (zeroIt != _zeroRuns.end() && zeroIt->second.count(assignment)) |
147 | 194k | continue; |
148 | 557k | m_usedStores.insert(assignment); |
149 | 557k | } |
150 | 713k | } |
151 | 805k | } |
152 | | |
153 | | void UnusedAssignEliminator::finalizeFunctionDefinition(FunctionDefinition const& _functionDefinition) |
154 | 1.50M | { |
155 | 1.50M | for (auto const& retParam: _functionDefinition.returnVariables) |
156 | 1.37M | markUsed(retParam.name); |
157 | 1.50M | } |
158 | | |
159 | | void UnusedAssignEliminator::markUsed(YulName _variable) |
160 | 105M | { |
161 | 105M | for (auto& assignment: m_activeStores[_variable]) |
162 | 3.38M | m_usedStores.insert(assignment); |
163 | 105M | m_activeStores.erase(_variable); |
164 | 105M | } |