/src/solidity/libyul/optimiser/Semantics.h
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 | | /** |
19 | | * Specific AST walkers that collect semantical facts. |
20 | | */ |
21 | | |
22 | | #pragma once |
23 | | |
24 | | #include <libyul/optimiser/ASTWalker.h> |
25 | | #include <libyul/SideEffects.h> |
26 | | #include <libyul/optimiser/CallGraphGenerator.h> |
27 | | #include <libyul/AST.h> |
28 | | |
29 | | #include <set> |
30 | | |
31 | | namespace solidity::yul |
32 | | { |
33 | | struct Dialect; |
34 | | |
35 | | /** |
36 | | * Specific AST walker that determines side-effect free-ness and movability of code. |
37 | | * Enters into function definitions. |
38 | | */ |
39 | | class SideEffectsCollector: public ASTWalker |
40 | | { |
41 | | public: |
42 | | explicit SideEffectsCollector( |
43 | | Dialect const& _dialect, |
44 | | std::map<YulString, SideEffects> const* _functionSideEffects = nullptr |
45 | 72.9M | ): m_dialect(_dialect), m_functionSideEffects(_functionSideEffects) {} |
46 | | SideEffectsCollector( |
47 | | Dialect const& _dialect, |
48 | | Expression const& _expression, |
49 | | std::map<YulString, SideEffects> const* _functionSideEffects = nullptr |
50 | | ); |
51 | | SideEffectsCollector(Dialect const& _dialect, Statement const& _statement); |
52 | | SideEffectsCollector( |
53 | | Dialect const& _dialect, |
54 | | Block const& _ast, |
55 | | std::map<YulString, SideEffects> const* _functionSideEffects = nullptr |
56 | | ); |
57 | | SideEffectsCollector( |
58 | | Dialect const& _dialect, |
59 | | ForLoop const& _ast, |
60 | | std::map<YulString, SideEffects> const* _functionSideEffects = nullptr |
61 | | ); |
62 | | |
63 | | using ASTWalker::operator(); |
64 | | void operator()(FunctionCall const& _functionCall) override; |
65 | | |
66 | 49.9M | bool movable() const { return m_sideEffects.movable; } |
67 | | |
68 | | bool movableRelativeTo(SideEffects const& _other, bool _codeContainsMSize) |
69 | 36.5k | { |
70 | 36.5k | if (!m_sideEffects.cannotLoop) |
71 | 0 | return false; |
72 | | |
73 | 36.5k | if (m_sideEffects.movable) |
74 | 36.5k | return true; |
75 | | |
76 | 0 | if ( |
77 | 0 | !m_sideEffects.movableApartFromEffects || |
78 | 0 | m_sideEffects.storage == SideEffects::Write || |
79 | 0 | m_sideEffects.otherState == SideEffects::Write || |
80 | 0 | m_sideEffects.memory == SideEffects::Write |
81 | 0 | ) |
82 | 0 | return false; |
83 | | |
84 | 0 | if (m_sideEffects.otherState == SideEffects::Read) |
85 | 0 | if (_other.otherState == SideEffects::Write) |
86 | 0 | return false; |
87 | | |
88 | 0 | if (m_sideEffects.storage == SideEffects::Read) |
89 | 0 | if (_other.storage == SideEffects::Write) |
90 | 0 | return false; |
91 | | |
92 | 0 | if (m_sideEffects.memory == SideEffects::Read) |
93 | 0 | if (_codeContainsMSize || _other.memory == SideEffects::Write) |
94 | 0 | return false; |
95 | | |
96 | 0 | return true; |
97 | 0 | } |
98 | | |
99 | | bool canBeRemoved(bool _allowMSizeModification = false) const |
100 | 7.29M | { |
101 | 7.29M | if (_allowMSizeModification) |
102 | 7.29M | return m_sideEffects.canBeRemovedIfNoMSize; |
103 | 0 | else |
104 | 0 | return m_sideEffects.canBeRemoved; |
105 | 7.29M | } |
106 | 0 | bool cannotLoop() const { return m_sideEffects.cannotLoop; } |
107 | 13.6M | bool invalidatesStorage() const { return m_sideEffects.storage == SideEffects::Write; } |
108 | 13.6M | bool invalidatesMemory() const { return m_sideEffects.memory == SideEffects::Write; } |
109 | | |
110 | 19.9k | SideEffects sideEffects() { return m_sideEffects; } |
111 | | |
112 | | private: |
113 | | Dialect const& m_dialect; |
114 | | std::map<YulString, SideEffects> const* m_functionSideEffects = nullptr; |
115 | | SideEffects m_sideEffects; |
116 | | }; |
117 | | |
118 | | /** |
119 | | * This class can be used to determine the side-effects of user-defined functions. |
120 | | * |
121 | | * It is given a dialect and a mapping that represents the direct calls from user-defined |
122 | | * functions to other user-defined functions and built-in functions. |
123 | | */ |
124 | | class SideEffectsPropagator |
125 | | { |
126 | | public: |
127 | | static std::map<YulString, SideEffects> sideEffects( |
128 | | Dialect const& _dialect, |
129 | | CallGraph const& _directCallGraph |
130 | | ); |
131 | | }; |
132 | | |
133 | | /** |
134 | | * Class that can be used to find out if certain code contains the MSize instruction |
135 | | * or a verbatim bytecode builtin (which is always assumed that it could contain MSize). |
136 | | * |
137 | | * Note that this is a purely syntactic property meaning that even if this is false, |
138 | | * the code can still contain calls to functions that contain the msize instruction. |
139 | | * |
140 | | * The only safe way to determine this is by passing the full AST. |
141 | | */ |
142 | | class MSizeFinder: public ASTWalker |
143 | | { |
144 | | public: |
145 | | static bool containsMSize(Dialect const& _dialect, Block const& _ast); |
146 | | |
147 | | using ASTWalker::operator(); |
148 | | void operator()(FunctionCall const& _funCall) override; |
149 | | |
150 | | private: |
151 | 204k | MSizeFinder(Dialect const& _dialect): m_dialect(_dialect) {} |
152 | | Dialect const& m_dialect; |
153 | | bool m_msizeFound = false; |
154 | | }; |
155 | | |
156 | | /** |
157 | | * Class that can be used to find out if the given function contains the ``leave`` statement. |
158 | | * |
159 | | * Returns true even in the case where the function definition contains another function definition |
160 | | * that contains the leave statement. |
161 | | */ |
162 | | class LeaveFinder: public ASTWalker |
163 | | { |
164 | | public: |
165 | | static bool containsLeave(FunctionDefinition const& _fun) |
166 | 95.9k | { |
167 | 95.9k | LeaveFinder f; |
168 | 95.9k | f(_fun); |
169 | 95.9k | return f.m_leaveFound; |
170 | 95.9k | } |
171 | | |
172 | | using ASTWalker::operator(); |
173 | 21.7k | void operator()(Leave const&) override { m_leaveFound = true; } |
174 | | |
175 | | private: |
176 | 95.9k | LeaveFinder() = default; |
177 | | |
178 | | bool m_leaveFound = false; |
179 | | }; |
180 | | |
181 | | /** |
182 | | * Specific AST walker that determines whether an expression is movable |
183 | | * and collects the referenced variables. |
184 | | * Can only be used on expressions. |
185 | | */ |
186 | | class MovableChecker: public SideEffectsCollector |
187 | | { |
188 | | public: |
189 | | explicit MovableChecker( |
190 | | Dialect const& _dialect, |
191 | | std::map<YulString, SideEffects> const* _functionSideEffects = nullptr |
192 | 51.6M | ): SideEffectsCollector(_dialect, _functionSideEffects) {} |
193 | | MovableChecker(Dialect const& _dialect, Expression const& _expression); |
194 | | |
195 | | void operator()(Identifier const& _identifier) override; |
196 | | |
197 | | /// Disallow visiting anything apart from Expressions (this throws). |
198 | | void visit(Statement const&) override; |
199 | | using ASTWalker::visit; |
200 | | |
201 | 147M | std::set<YulString> const& referencedVariables() const { return m_variableReferences; } |
202 | | |
203 | | private: |
204 | | /// Which variables the current expression references. |
205 | | std::set<YulString> m_variableReferences; |
206 | | }; |
207 | | |
208 | | struct ControlFlowSideEffects; |
209 | | |
210 | | /** |
211 | | * Helper class to find "irregular" control flow. |
212 | | * This includes termination, break, continue and leave. |
213 | | * In general, it is applied only to "simple" statements. The control-flow |
214 | | * of loops, switches and if statements is always "FlowOut" with the assumption |
215 | | * that the caller will descend into them. |
216 | | */ |
217 | | class TerminationFinder |
218 | | { |
219 | | public: |
220 | | /// "Terminate" here means that there is no continuing control-flow. |
221 | | /// If this is applied to a function that can revert or stop, but can also |
222 | | /// exit regularly, the property is set to "FlowOut". |
223 | | enum class ControlFlow { FlowOut, Break, Continue, Terminate, Leave }; |
224 | | |
225 | | TerminationFinder( |
226 | | Dialect const& _dialect, |
227 | | std::map<YulString, ControlFlowSideEffects> const* _functionSideEffects = nullptr |
228 | 1.75M | ): m_dialect(_dialect), m_functionSideEffects(_functionSideEffects) {} |
229 | | |
230 | | /// @returns the index of the first statement in the provided sequence |
231 | | /// that is an unconditional ``break``, ``continue``, ``leave`` or a |
232 | | /// call to a terminating function. |
233 | | /// If control flow can continue at the end of the list, |
234 | | /// returns `FlowOut` and ``size_t(-1)``. |
235 | | /// The function might return ``FlowOut`` even though control |
236 | | /// flow cannot actually continue. |
237 | | std::pair<ControlFlow, size_t> firstUnconditionalControlFlowChange( |
238 | | std::vector<Statement> const& _statements |
239 | | ); |
240 | | |
241 | | /// @returns the control flow type of the given statement. |
242 | | /// This function could return FlowOut even if control flow never continues. |
243 | | ControlFlow controlFlowKind(Statement const& _statement); |
244 | | |
245 | | /// @returns true if the expression contains a |
246 | | /// call to a terminating function, i.e. a function that does not have |
247 | | /// a regular "flow out" control-flow (it might also be recursive). |
248 | | bool containsNonContinuingFunctionCall(Expression const& _expr); |
249 | | |
250 | | private: |
251 | | Dialect const& m_dialect; |
252 | | std::map<YulString, ControlFlowSideEffects> const* m_functionSideEffects; |
253 | | }; |
254 | | |
255 | | } |