/src/solidity/libyul/optimiser/ExpressionSplitter.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 turns complex expressions into multiple variable |
20 | | * declarations. |
21 | | */ |
22 | | |
23 | | #include <libyul/optimiser/ExpressionSplitter.h> |
24 | | |
25 | | #include <libyul/optimiser/OptimiserStep.h> |
26 | | |
27 | | #include <libyul/AST.h> |
28 | | #include <libyul/Dialect.h> |
29 | | #include <libyul/Utilities.h> |
30 | | |
31 | | #include <libsolutil/CommonData.h> |
32 | | |
33 | | using namespace solidity; |
34 | | using namespace solidity::yul; |
35 | | using namespace solidity::util; |
36 | | using namespace solidity::langutil; |
37 | | |
38 | | ExpressionSplitter::ExpressionSplitter(Dialect const& _dialect, NameDispenser& _nameDispenser): |
39 | 325k | m_dialect(_dialect), |
40 | 325k | m_nameDispenser(_nameDispenser) |
41 | 325k | {} |
42 | | |
43 | 325k | ExpressionSplitter::~ExpressionSplitter() = default; |
44 | | |
45 | | void ExpressionSplitter::run(OptimiserStepContext& _context, Block& _ast) |
46 | 325k | { |
47 | 325k | ExpressionSplitter{_context.dialect, _context.dispenser}(_ast); |
48 | 325k | } |
49 | | |
50 | | void ExpressionSplitter::operator()(FunctionCall& _funCall) |
51 | 7.92M | { |
52 | 7.92M | BuiltinFunction const* builtin = resolveBuiltinFunction(_funCall.functionName, m_dialect); |
53 | | |
54 | 21.8M | for (size_t i = _funCall.arguments.size(); i > 0; i--) |
55 | 13.9M | if (!builtin || !builtin->literalArgument(i - 1)) |
56 | 13.7M | outlineExpression(_funCall.arguments[i - 1]); |
57 | 7.92M | } |
58 | | |
59 | | void ExpressionSplitter::operator()(If& _if) |
60 | 356k | { |
61 | 356k | outlineExpression(*_if.condition); |
62 | 356k | (*this)(_if.body); |
63 | 356k | } |
64 | | |
65 | | void ExpressionSplitter::operator()(Switch& _switch) |
66 | 94.5k | { |
67 | 94.5k | outlineExpression(*_switch.expression); |
68 | 94.5k | for (auto& _case: _switch.cases) |
69 | | // Do not visit the case expression, nothing to split there. |
70 | 297k | (*this)(_case.body); |
71 | 94.5k | } |
72 | | |
73 | | void ExpressionSplitter::operator()(ForLoop& _loop) |
74 | 412k | { |
75 | 412k | (*this)(_loop.pre); |
76 | | // Do not visit the condition because we cannot split expressions there. |
77 | 412k | (*this)(_loop.post); |
78 | 412k | (*this)(_loop.body); |
79 | 412k | } |
80 | | |
81 | | void ExpressionSplitter::operator()(Block& _block) |
82 | 3.22M | { |
83 | 3.22M | std::vector<Statement> saved; |
84 | 3.22M | swap(saved, m_statementsToPrefix); |
85 | | |
86 | 3.22M | std::function<std::optional<std::vector<Statement>>(Statement&)> f = |
87 | 12.8M | [&](Statement& _statement) -> std::optional<std::vector<Statement>> { |
88 | 12.8M | m_statementsToPrefix.clear(); |
89 | 12.8M | visit(_statement); |
90 | 12.8M | if (m_statementsToPrefix.empty()) |
91 | 8.09M | return {}; |
92 | 4.76M | m_statementsToPrefix.emplace_back(std::move(_statement)); |
93 | 4.76M | return std::move(m_statementsToPrefix); |
94 | 12.8M | }; |
95 | 3.22M | iterateReplacing(_block.statements, f); |
96 | | |
97 | 3.22M | swap(saved, m_statementsToPrefix); |
98 | 3.22M | } |
99 | | |
100 | | void ExpressionSplitter::outlineExpression(Expression& _expr) |
101 | 14.2M | { |
102 | 14.2M | if (std::holds_alternative<Identifier>(_expr)) |
103 | 4.06M | return; |
104 | | |
105 | 10.1M | visit(_expr); |
106 | | |
107 | 10.1M | langutil::DebugData::ConstPtr debugData = debugDataOf(_expr); |
108 | 10.1M | YulName var = m_nameDispenser.newName({}); |
109 | 10.1M | m_statementsToPrefix.emplace_back(VariableDeclaration{ |
110 | 10.1M | debugData, |
111 | 10.1M | {{NameWithDebugData{debugData, var}}}, |
112 | 10.1M | std::make_unique<Expression>(std::move(_expr)) |
113 | 10.1M | }); |
114 | 10.1M | _expr = Identifier{debugData, var}; |
115 | 10.1M | } |
116 | | |