/src/solidity/libyul/backends/evm/NoOutputAssembly.cpp
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 | | * Assembly interface that ignores everything. Can be used as a backend for a compilation dry-run. |
20 | | */ |
21 | | |
22 | | #include <libyul/backends/evm/NoOutputAssembly.h> |
23 | | |
24 | | #include <libyul/AST.h> |
25 | | #include <libyul/Exceptions.h> |
26 | | |
27 | | #include <libevmasm/Instruction.h> |
28 | | |
29 | | #include <range/v3/view/enumerate.hpp> |
30 | | #include <range/v3/view/iota.hpp> |
31 | | |
32 | | using namespace solidity; |
33 | | using namespace solidity::yul; |
34 | | using namespace solidity::util; |
35 | | using namespace solidity::langutil; |
36 | | |
37 | | namespace |
38 | | { |
39 | | |
40 | | void modifyBuiltinToNoOutput(BuiltinFunctionForEVM& _builtin) |
41 | 834 | { |
42 | 834 | _builtin.generateCode = [_builtin](FunctionCall const& _call, AbstractAssembly& _assembly, BuiltinContext&) |
43 | 839k | { |
44 | 839k | for (size_t i: ranges::views::iota(0u, _call.arguments.size())) |
45 | 1.72M | if (!_builtin.literalArgument(i)) |
46 | 1.69M | _assembly.appendInstruction(evmasm::Instruction::POP); |
47 | | |
48 | 1.41M | for (size_t i = 0; i < _builtin.numReturns; i++) |
49 | 573k | _assembly.appendConstant(u256(0)); |
50 | 839k | }; |
51 | 834 | } |
52 | | |
53 | | } |
54 | | |
55 | | void NoOutputAssembly::appendInstruction(evmasm::Instruction _instr) |
56 | 6.55M | { |
57 | 6.55M | m_stackHeight += instructionInfo(_instr, m_evmVersion).ret - instructionInfo(_instr, m_evmVersion).args; |
58 | 6.55M | } |
59 | | |
60 | | void NoOutputAssembly::appendConstant(u256 const&) |
61 | 2.02M | { |
62 | 2.02M | appendInstruction(evmasm::pushInstruction(1)); |
63 | 2.02M | } |
64 | | |
65 | | void NoOutputAssembly::appendLabel(LabelID) |
66 | 532k | { |
67 | 532k | appendInstruction(evmasm::Instruction::JUMPDEST); |
68 | 532k | } |
69 | | |
70 | | void NoOutputAssembly::appendLabelReference(LabelID) |
71 | 531k | { |
72 | 531k | appendInstruction(evmasm::pushInstruction(1)); |
73 | 531k | } |
74 | | |
75 | | NoOutputAssembly::LabelID NoOutputAssembly::newLabelId() |
76 | 532k | { |
77 | 532k | return 1; |
78 | 532k | } |
79 | | |
80 | | AbstractAssembly::LabelID NoOutputAssembly::namedLabel(std::string const&, size_t, size_t, std::optional<size_t>) |
81 | 0 | { |
82 | 0 | return 1; |
83 | 0 | } |
84 | | |
85 | | void NoOutputAssembly::appendLinkerSymbol(std::string const&) |
86 | 0 | { |
87 | 0 | yulAssert(false, "Linker symbols not yet implemented."); |
88 | 0 | } |
89 | | |
90 | | void NoOutputAssembly::appendVerbatim(bytes, size_t _arguments, size_t _returnVariables) |
91 | 0 | { |
92 | 0 | m_stackHeight += static_cast<int>(_returnVariables) - static_cast<int>(_arguments); |
93 | 0 | } |
94 | | |
95 | | void NoOutputAssembly::appendJump(int _stackDiffAfter, JumpType) |
96 | 394k | { |
97 | 394k | appendInstruction(evmasm::Instruction::JUMP); |
98 | 394k | m_stackHeight += _stackDiffAfter; |
99 | 394k | } |
100 | | |
101 | | void NoOutputAssembly::appendJumpTo(LabelID _labelId, int _stackDiffAfter, JumpType _jumpType) |
102 | 300k | { |
103 | 300k | appendLabelReference(_labelId); |
104 | 300k | appendJump(_stackDiffAfter, _jumpType); |
105 | 300k | } |
106 | | |
107 | | void NoOutputAssembly::appendJumpToIf(LabelID _labelId, JumpType) |
108 | 72.7k | { |
109 | 72.7k | appendLabelReference(_labelId); |
110 | 72.7k | appendInstruction(evmasm::Instruction::JUMPI); |
111 | 72.7k | } |
112 | | |
113 | | void NoOutputAssembly::appendAssemblySize() |
114 | 0 | { |
115 | 0 | appendInstruction(evmasm::Instruction::PUSH1); |
116 | 0 | } |
117 | | |
118 | | void NoOutputAssembly::appendDupN(size_t) |
119 | 0 | { |
120 | 0 | m_stackHeight++; |
121 | 0 | } |
122 | | |
123 | | std::pair<std::shared_ptr<AbstractAssembly>, AbstractAssembly::SubID> NoOutputAssembly::createSubAssembly(bool, std::string) |
124 | 0 | { |
125 | 0 | yulAssert(false, "Sub assemblies not implemented."); |
126 | 0 | return {}; |
127 | 0 | } |
128 | | |
129 | | AbstractAssembly::FunctionID NoOutputAssembly::registerFunction(uint8_t _args, uint8_t _rets, bool) |
130 | 0 | { |
131 | 0 | yulAssert(m_context.numFunctions <= std::numeric_limits<AbstractAssembly::FunctionID>::max()); |
132 | 0 | AbstractAssembly::FunctionID id = static_cast<AbstractAssembly::FunctionID>(m_context.numFunctions++); |
133 | 0 | m_context.functionSignatures[id] = {_args, _rets}; |
134 | 0 | return id; |
135 | 0 | } |
136 | | |
137 | | void NoOutputAssembly::beginFunction(FunctionID _functionID) |
138 | 0 | { |
139 | 0 | yulAssert(m_currentFunctionID == 0, "Attempted to begin a function before ending the last one."); |
140 | 0 | yulAssert(m_context.functionSignatures.count(_functionID) == 1, "Filling unregistered function."); |
141 | 0 | yulAssert(m_stackHeight == 0, "Non-empty stack on beginFunction call."); |
142 | 0 | m_currentFunctionID = _functionID; |
143 | 0 | } |
144 | | |
145 | | void NoOutputAssembly::endFunction() |
146 | 0 | { |
147 | 0 | yulAssert(m_currentFunctionID != 0, "End function without begin function."); |
148 | 0 | auto const [_, rets] = m_context.functionSignatures.at(m_currentFunctionID); |
149 | 0 | yulAssert(m_stackHeight == rets, "Stack height mismatch at function end."); |
150 | 0 | m_currentFunctionID = 0; |
151 | 0 | } |
152 | | |
153 | | void NoOutputAssembly::appendFunctionCall(FunctionID _functionID) |
154 | 0 | { |
155 | 0 | auto [args, rets] = m_context.functionSignatures.at(_functionID); |
156 | 0 | m_stackHeight += static_cast<int>(rets) - static_cast<int>(args); |
157 | 0 | solAssert(m_stackHeight >= 0); |
158 | 0 | } |
159 | | |
160 | | void NoOutputAssembly::appendFunctionReturn() |
161 | 0 | { |
162 | 0 | yulAssert(m_currentFunctionID != 0, "End function without begin function."); |
163 | 0 | auto const [_, rets] = m_context.functionSignatures.at(m_currentFunctionID); |
164 | 0 | yulAssert(m_stackHeight == rets, "Stack height mismatch at function end."); |
165 | 0 | } |
166 | | |
167 | | void NoOutputAssembly::appendDataOffset(std::vector<AbstractAssembly::SubID> const&) |
168 | 0 | { |
169 | 0 | appendInstruction(evmasm::Instruction::PUSH1); |
170 | 0 | } |
171 | | |
172 | | void NoOutputAssembly::appendDataSize(std::vector<AbstractAssembly::SubID> const&) |
173 | 0 | { |
174 | 0 | appendInstruction(evmasm::Instruction::PUSH1); |
175 | 0 | } |
176 | | |
177 | | AbstractAssembly::SubID NoOutputAssembly::appendData(bytes const&) |
178 | 0 | { |
179 | 0 | return {1}; |
180 | 0 | } |
181 | | |
182 | | |
183 | | void NoOutputAssembly::appendImmutable(std::string const&) |
184 | 0 | { |
185 | 0 | yulAssert(false, "loadimmutable not implemented."); |
186 | 0 | } |
187 | | |
188 | | void NoOutputAssembly::appendImmutableAssignment(std::string const&) |
189 | 0 | { |
190 | 0 | yulAssert(false, "setimmutable not implemented."); |
191 | 0 | } |
192 | | |
193 | | void NoOutputAssembly::appendAuxDataLoadN(uint16_t) |
194 | 0 | { |
195 | 0 | yulAssert(false, "auxdataloadn not implemented."); |
196 | 0 | } |
197 | | |
198 | | void NoOutputAssembly::appendEOFCreate(ContainerID) |
199 | 0 | { |
200 | 0 | yulAssert(false, "eofcreate not implemented."); |
201 | |
|
202 | 0 | } |
203 | | void NoOutputAssembly::appendReturnContract(ContainerID) |
204 | 0 | { |
205 | 0 | yulAssert(false, "returncontract not implemented."); |
206 | 0 | } |
207 | | |
208 | | NoOutputEVMDialect::NoOutputEVMDialect(EVMDialect const& _copyFrom): |
209 | | EVMDialect(_copyFrom.evmVersion(), _copyFrom.eofVersion(), _copyFrom.providesObjectAccess()) |
210 | 21.5k | { |
211 | | // save the modified functions here - note that this only has to be done once because we modify all of |
212 | | // them in one go, later reference pointers to this static vector |
213 | 21.5k | static std::vector<BuiltinFunctionForEVM> noOutputBuiltins = defineNoOutputBuiltins(); |
214 | | |
215 | 21.5k | yulAssert(m_functions.size() == noOutputBuiltins.size(), "Function count mismatch."); |
216 | 21.5k | for (auto const& [index, builtinFunction]: m_functions | ranges::views::enumerate) |
217 | 2.99M | { |
218 | 2.99M | if (builtinFunction) |
219 | 1.70M | m_functions[index] = &noOutputBuiltins[index]; |
220 | 1.28M | else |
221 | 1.28M | m_functions[index] = nullptr; |
222 | 2.99M | } |
223 | 21.5k | } |
224 | | |
225 | | BuiltinFunctionForEVM const& NoOutputEVMDialect::builtin(BuiltinHandle const& _handle) const |
226 | 2.54M | { |
227 | 2.54M | if (isVerbatimHandle(_handle)) |
228 | | // for verbatims the modification is performed lazily as they are stored in a lookup table fashion |
229 | 0 | if ( |
230 | 0 | auto& builtin = m_verbatimFunctions[_handle.id]; |
231 | 0 | !builtin |
232 | 0 | ) |
233 | 0 | { |
234 | 0 | builtin = std::make_unique<BuiltinFunctionForEVM>(createVerbatimFunctionFromHandle(_handle)); |
235 | 0 | modifyBuiltinToNoOutput(*builtin); |
236 | 0 | } |
237 | 2.54M | return EVMDialect::builtin(_handle); |
238 | 2.54M | } |
239 | | |
240 | | std::vector<BuiltinFunctionForEVM> NoOutputEVMDialect::defineNoOutputBuiltins() |
241 | 6 | { |
242 | 6 | std::vector<BuiltinFunctionForEVM> modifiedBuiltins; |
243 | 6 | modifiedBuiltins.reserve(allBuiltins().functions().size()); |
244 | | |
245 | 6 | for (auto const& [_, builtin]: allBuiltins().functions()) |
246 | 834 | { |
247 | 834 | auto noOutputFunction = builtin; |
248 | 834 | modifyBuiltinToNoOutput(noOutputFunction); |
249 | 834 | modifiedBuiltins.push_back(std::move(noOutputFunction)); |
250 | 834 | } |
251 | | |
252 | 6 | return modifiedBuiltins; |
253 | 6 | } |