/src/solidity/libyul/backends/evm/EVMBuiltins.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/backends/evm/EVMBuiltins.h> |
20 | | |
21 | | #include <libyul/AST.h> |
22 | | #include <libyul/Object.h> |
23 | | #include <libyul/Utilities.h> |
24 | | |
25 | | #include <libevmasm/AssemblyItem.h> |
26 | | |
27 | | #include <libsolutil/StringUtils.h> |
28 | | |
29 | | #include <range/v3/algorithm/all_of.hpp> |
30 | | |
31 | | using namespace solidity; |
32 | | using namespace solidity::yul; |
33 | | |
34 | | namespace |
35 | | { |
36 | | |
37 | | BuiltinFunctionForEVM createFunction( |
38 | | std::string const& _name, |
39 | | size_t _params, |
40 | | size_t _returns, |
41 | | SideEffects _sideEffects, |
42 | | ControlFlowSideEffects _controlFlowSideEffects, |
43 | | std::vector<std::optional<LiteralKind>> _literalArguments, |
44 | | std::function<void(FunctionCall const&, AbstractAssembly&, BuiltinContext&)> _generateCode |
45 | | ) |
46 | 1.69k | { |
47 | 1.69k | yulAssert(_literalArguments.size() == _params || _literalArguments.empty(), ""); |
48 | | |
49 | 1.69k | BuiltinFunctionForEVM f; |
50 | 1.69k | f.name = _name; |
51 | 1.69k | f.numParameters = _params; |
52 | 1.69k | f.numReturns = _returns; |
53 | 1.69k | f.sideEffects = _sideEffects; |
54 | 1.69k | f.controlFlowSideEffects = _controlFlowSideEffects; |
55 | 1.69k | f.literalArguments = std::move(_literalArguments); |
56 | 1.69k | f.isMSize = false; |
57 | 1.69k | f.instruction = {}; |
58 | 1.69k | f.generateCode = std::move(_generateCode); |
59 | 1.69k | return f; |
60 | 1.69k | } |
61 | | |
62 | | BuiltinFunctionForEVM instructionBuiltin(evmasm::Instruction const& _instruction, langutil::EVMVersion const& _evmVersion) |
63 | 1.42k | { |
64 | 1.42k | evmasm::InstructionInfo const info = evmasm::instructionInfo(_instruction, _evmVersion); |
65 | 1.42k | BuiltinFunctionForEVM f; |
66 | 1.42k | f.name = util::toLower(info.name); |
67 | 1.42k | f.numParameters = static_cast<size_t>(info.args); |
68 | 1.42k | f.numReturns = static_cast<size_t>(info.ret); |
69 | 1.42k | f.sideEffects = EVMBuiltins::sideEffectsOfInstruction(_instruction); |
70 | 1.42k | f.controlFlowSideEffects = ControlFlowSideEffects::fromInstruction(_instruction); |
71 | 1.42k | f.isMSize = _instruction == evmasm::Instruction::MSIZE; |
72 | 1.42k | f.literalArguments.clear(); |
73 | 1.42k | f.instruction = _instruction; |
74 | 1.42k | f.generateCode = [_instruction]( |
75 | 1.42k | FunctionCall const&, |
76 | 1.42k | AbstractAssembly& _assembly, |
77 | 1.42k | BuiltinContext& |
78 | 1.42k | ) |
79 | 1.67M | { |
80 | 1.67M | _assembly.appendInstruction(_instruction); |
81 | 1.67M | }; |
82 | 1.42k | return f; |
83 | 1.42k | } |
84 | | |
85 | | BuiltinFunctionForEVM linkersymbolBuiltin() |
86 | 12 | { |
87 | 12 | return createFunction( |
88 | 12 | "linkersymbol", |
89 | 12 | 1, |
90 | 12 | 1, |
91 | 12 | SideEffects{}, |
92 | 12 | ControlFlowSideEffects{}, |
93 | 12 | {LiteralKind::String}, |
94 | 1.54k | [](FunctionCall const& _call, AbstractAssembly& _assembly, BuiltinContext&) { |
95 | 1.54k | yulAssert(_call.arguments.size() == 1, ""); |
96 | 1.54k | Expression const& arg = _call.arguments.front(); |
97 | 1.54k | _assembly.appendLinkerSymbol(formatLiteral(std::get<Literal>(arg))); |
98 | 1.54k | } |
99 | 12 | ); |
100 | 12 | } |
101 | | |
102 | | BuiltinFunctionForEVM memoryguardBuiltin() |
103 | 12 | { |
104 | 12 | return createFunction( |
105 | 12 | "memoryguard", |
106 | 12 | 1, |
107 | 12 | 1, |
108 | 12 | SideEffects{}, |
109 | 12 | ControlFlowSideEffects{}, |
110 | 12 | {LiteralKind::Number}, |
111 | 23.0k | [](FunctionCall const& _call, AbstractAssembly& _assembly, BuiltinContext&) { |
112 | 23.0k | yulAssert(_call.arguments.size() == 1, ""); |
113 | 23.0k | Literal const* literal = std::get_if<Literal>(&_call.arguments.front()); |
114 | 23.0k | yulAssert(literal, ""); |
115 | 23.0k | _assembly.appendConstant(literal->value.value()); |
116 | 23.0k | } |
117 | 12 | ); |
118 | 12 | } |
119 | | |
120 | | BuiltinFunctionForEVM datasizeBuiltin() |
121 | 12 | { |
122 | 12 | return createFunction( |
123 | 12 | "datasize", |
124 | 12 | 1, |
125 | 12 | 1, |
126 | 12 | SideEffects{}, |
127 | 12 | ControlFlowSideEffects{}, |
128 | 12 | {LiteralKind::String}, |
129 | 6.48k | [](FunctionCall const& _call, AbstractAssembly& _assembly, BuiltinContext& _context) { |
130 | 6.48k | yulAssert(_context.currentObject, "No object available."); |
131 | 6.48k | yulAssert(_call.arguments.size() == 1, ""); |
132 | 6.48k | Expression const& arg = _call.arguments.front(); |
133 | 6.48k | YulName const dataName (formatLiteral(std::get<Literal>(arg))); |
134 | 6.48k | if (_context.currentObject->name == dataName.str()) |
135 | 309 | _assembly.appendAssemblySize(); |
136 | 6.18k | else |
137 | 6.18k | { |
138 | 6.18k | std::vector<AbstractAssembly::SubID> subIdPath = |
139 | 6.18k | _context.subIDs.count(dataName.str()) == 0 ? |
140 | 0 | _context.currentObject->pathToSubObject(dataName.str()) : |
141 | 6.18k | std::vector{_context.subIDs.at(dataName.str())}; |
142 | 6.18k | yulAssert(!subIdPath.empty(), "Could not find assembly object <" + dataName.str() + ">."); |
143 | 6.18k | _assembly.appendDataSize(subIdPath); |
144 | 6.18k | } |
145 | 6.48k | } |
146 | 12 | ); |
147 | 12 | } |
148 | | |
149 | | BuiltinFunctionForEVM dataoffsetBuiltin() |
150 | 12 | { |
151 | 12 | return createFunction("dataoffset", 1, 1, SideEffects{}, ControlFlowSideEffects{}, {LiteralKind::String}, []( |
152 | 12 | FunctionCall const& _call, |
153 | 12 | AbstractAssembly& _assembly, |
154 | 12 | BuiltinContext& _context |
155 | 5.41k | ) { |
156 | 5.41k | yulAssert(_context.currentObject, "No object available."); |
157 | 5.41k | yulAssert(_call.arguments.size() == 1, ""); |
158 | 5.41k | Expression const& arg = _call.arguments.front(); |
159 | 5.41k | YulName const dataName (formatLiteral(std::get<Literal>(arg))); |
160 | 5.41k | if (_context.currentObject->name == dataName.str()) |
161 | 8 | _assembly.appendConstant(0); |
162 | 5.40k | else |
163 | 5.40k | { |
164 | 5.40k | std::vector<AbstractAssembly::SubID> subIdPath = |
165 | 5.40k | _context.subIDs.count(dataName.str()) == 0 ? |
166 | 73 | _context.currentObject->pathToSubObject(dataName.str()) : |
167 | 5.40k | std::vector{_context.subIDs.at(dataName.str())}; |
168 | 5.40k | yulAssert(!subIdPath.empty(), "Could not find assembly object <" + dataName.str() + ">."); |
169 | 5.40k | _assembly.appendDataOffset(subIdPath); |
170 | 5.40k | } |
171 | 5.41k | }); |
172 | 12 | } |
173 | | |
174 | | BuiltinFunctionForEVM datacopyBuiltin() |
175 | 12 | { |
176 | 12 | return createFunction( |
177 | 12 | "datacopy", |
178 | 12 | 3, |
179 | 12 | 0, |
180 | 12 | EVMBuiltins::sideEffectsOfInstruction(evmasm::Instruction::CODECOPY), |
181 | 12 | ControlFlowSideEffects::fromInstruction(evmasm::Instruction::CODECOPY), |
182 | 12 | {}, |
183 | 12 | []( |
184 | 12 | FunctionCall const&, |
185 | 12 | AbstractAssembly& _assembly, |
186 | 12 | BuiltinContext& |
187 | 157 | ) { |
188 | 157 | _assembly.appendInstruction(evmasm::Instruction::CODECOPY); |
189 | 157 | } |
190 | 12 | ); |
191 | 12 | } |
192 | | |
193 | | BuiltinFunctionForEVM setimmutableBuiltin() |
194 | 12 | { |
195 | 12 | return createFunction( |
196 | 12 | "setimmutable", |
197 | 12 | 3, |
198 | 12 | 0, |
199 | 12 | SideEffects{ |
200 | 12 | false, // movable |
201 | 12 | false, // movableApartFromEffects |
202 | 12 | false, // canBeRemoved |
203 | 12 | false, // canBeRemovedIfNotMSize |
204 | 12 | true, // cannotLoop |
205 | 12 | SideEffects::None, // otherState |
206 | 12 | SideEffects::None, // storage |
207 | 12 | SideEffects::Write, // memory |
208 | 12 | SideEffects::None // transientStorage |
209 | 12 | }, |
210 | 12 | ControlFlowSideEffects{}, |
211 | 12 | {std::nullopt, LiteralKind::String, std::nullopt}, |
212 | 12 | []( |
213 | 12 | FunctionCall const& _call, |
214 | 12 | AbstractAssembly& _assembly, |
215 | 12 | BuiltinContext& |
216 | 132 | ) { |
217 | 132 | yulAssert(_call.arguments.size() == 3, ""); |
218 | 132 | auto const identifier = (formatLiteral(std::get<Literal>(_call.arguments[1]))); |
219 | 132 | _assembly.appendImmutableAssignment(identifier); |
220 | 132 | } |
221 | 12 | ); |
222 | 12 | } |
223 | | |
224 | | BuiltinFunctionForEVM loadimmutableBuiltin() |
225 | 12 | { |
226 | 12 | return createFunction( |
227 | 12 | "loadimmutable", |
228 | 12 | 1, |
229 | 12 | 1, |
230 | 12 | SideEffects{}, |
231 | 12 | ControlFlowSideEffects{}, |
232 | 12 | {LiteralKind::String}, |
233 | 12 | []( |
234 | 12 | FunctionCall const& _call, |
235 | 12 | AbstractAssembly& _assembly, |
236 | 12 | BuiltinContext& |
237 | 105 | ) { |
238 | 105 | yulAssert(_call.arguments.size() == 1, ""); |
239 | 105 | _assembly.appendImmutable(formatLiteral(std::get<Literal>(_call.arguments.front()))); |
240 | 105 | } |
241 | 12 | ); |
242 | 12 | } |
243 | | } |
244 | | |
245 | | EVMBuiltins::EVMBuiltins() |
246 | 12 | { |
247 | 12 | for (auto const& [name, opcode]: evmasm::c_instructions) |
248 | 1.81k | { |
249 | 1.81k | if ( |
250 | 1.81k | evmasm::SemanticInformation::isSwapInstruction(opcode) || |
251 | 1.62k | evmasm::SemanticInformation::isDupInstruction(opcode) |
252 | 1.81k | ) |
253 | 384 | continue; |
254 | | |
255 | | // difficulty was replaced by prevrandao after london |
256 | 1.42k | if (opcode == evmasm::Instruction::PREVRANDAO && name == "DIFFICULTY") |
257 | 12 | m_scopesAndFunctions.emplace_back(instruction, instructionBuiltin(opcode, langutil::EVMVersion::london())); |
258 | 1.41k | else |
259 | 1.41k | m_scopesAndFunctions.emplace_back(instruction, instructionBuiltin(opcode, langutil::EVMVersion::current())); |
260 | 1.42k | } |
261 | | |
262 | 12 | m_scopesAndFunctions.emplace_back(objectAccess, linkersymbolBuiltin()); |
263 | 12 | m_scopesAndFunctions.emplace_back(objectAccess, memoryguardBuiltin()); |
264 | | |
265 | 12 | m_scopesAndFunctions.emplace_back(objectAccess, datasizeBuiltin()); |
266 | 12 | m_scopesAndFunctions.emplace_back(objectAccess, dataoffsetBuiltin()); |
267 | 12 | m_scopesAndFunctions.emplace_back(objectAccess, datacopyBuiltin()); |
268 | 12 | m_scopesAndFunctions.emplace_back(objectAccess, setimmutableBuiltin()); |
269 | 12 | m_scopesAndFunctions.emplace_back(objectAccess, loadimmutableBuiltin()); |
270 | | |
271 | 12 | static size_t constexpr verbatimPrefixLength = std::char_traits<char>::length("verbatim_"); |
272 | 12 | for (auto const& [scope, builtin]: m_scopesAndFunctions) |
273 | 1.51k | { |
274 | 1.51k | yulAssert( |
275 | 1.51k | builtin.name.substr(0, verbatimPrefixLength) != "verbatim_", |
276 | 1.51k | "Builtin functions besides verbatim should not start with the verbatim_ prefix." |
277 | 1.51k | ); |
278 | 1.51k | } |
279 | 12 | } |
280 | | |
281 | | BuiltinFunctionForEVM EVMBuiltins::createVerbatimFunction(size_t const _arguments, size_t const _returnVariables) |
282 | 1.61k | { |
283 | 1.61k | BuiltinFunctionForEVM builtinFunction = createFunction( |
284 | 1.61k | "verbatim_" + std::to_string(_arguments) + "i_" + std::to_string(_returnVariables) + "o", |
285 | 1.61k | 1 + _arguments, |
286 | 1.61k | _returnVariables, |
287 | 1.61k | SideEffects::worst(), |
288 | 1.61k | ControlFlowSideEffects::worst(), // Worst control flow side effects because verbatim can do anything. |
289 | 1.61k | std::vector<std::optional<LiteralKind>>{LiteralKind::String} + std::vector<std::optional<LiteralKind>>(_arguments), |
290 | 1.61k | [=]( |
291 | 1.61k | FunctionCall const& _call, |
292 | 1.61k | AbstractAssembly& _assembly, |
293 | 1.61k | BuiltinContext& |
294 | 1.61k | ) { |
295 | 1.23k | yulAssert(_call.arguments.size() == (1 + _arguments), ""); |
296 | 1.23k | Expression const& bytecode = _call.arguments.front(); |
297 | | |
298 | 1.23k | _assembly.appendVerbatim( |
299 | 1.23k | util::asBytes(formatLiteral(std::get<Literal>(bytecode))), |
300 | 1.23k | _arguments, |
301 | 1.23k | _returnVariables |
302 | 1.23k | ); |
303 | 1.23k | } |
304 | 1.61k | ); |
305 | 1.61k | builtinFunction.isMSize = true; |
306 | 1.61k | return builtinFunction; |
307 | 1.61k | } |
308 | | |
309 | | SideEffects EVMBuiltins::sideEffectsOfInstruction(evmasm::Instruction _instruction) |
310 | 1.44k | { |
311 | 1.44k | auto translate = [](evmasm::SemanticInformation::Effect _e) -> SideEffects::Effect |
312 | 5.76k | { |
313 | 5.76k | return static_cast<SideEffects::Effect>(_e); |
314 | 5.76k | }; |
315 | | |
316 | 1.44k | return SideEffects{ |
317 | 1.44k | evmasm::SemanticInformation::movable(_instruction), |
318 | 1.44k | evmasm::SemanticInformation::movableApartFromEffects(_instruction), |
319 | 1.44k | evmasm::SemanticInformation::canBeRemoved(_instruction), |
320 | 1.44k | evmasm::SemanticInformation::canBeRemovedIfNoMSize(_instruction), |
321 | 1.44k | true, // cannotLoop |
322 | 1.44k | translate(evmasm::SemanticInformation::otherState(_instruction)), |
323 | 1.44k | translate(evmasm::SemanticInformation::storage(_instruction)), |
324 | 1.44k | translate(evmasm::SemanticInformation::memory(_instruction)), |
325 | 1.44k | translate(evmasm::SemanticInformation::transientStorage(_instruction)), |
326 | 1.44k | }; |
327 | 1.44k | } |