/src/solidity/libsolidity/codegen/CompilerContext.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 | | * @author Christian <c@ethdev.com> |
20 | | * @date 2014 |
21 | | * Utilities for the solidity compiler. |
22 | | */ |
23 | | |
24 | | #include <libsolidity/codegen/CompilerContext.h> |
25 | | |
26 | | #include <libsolidity/ast/AST.h> |
27 | | #include <libsolidity/codegen/Compiler.h> |
28 | | #include <libsolidity/codegen/CompilerUtils.h> |
29 | | #include <libsolidity/interface/Version.h> |
30 | | |
31 | | #include <libyul/AST.h> |
32 | | #include <libyul/AsmParser.h> |
33 | | #include <libyul/AsmPrinter.h> |
34 | | #include <libyul/AsmAnalysis.h> |
35 | | #include <libyul/AsmAnalysisInfo.h> |
36 | | #include <libyul/backends/evm/AsmCodeGen.h> |
37 | | #include <libyul/backends/evm/EVMDialect.h> |
38 | | #include <libyul/backends/evm/EVMMetrics.h> |
39 | | #include <libyul/optimiser/Suite.h> |
40 | | #include <libyul/Object.h> |
41 | | #include <libyul/YulName.h> |
42 | | #include <libyul/Utilities.h> |
43 | | |
44 | | #include <libsolutil/Whiskers.h> |
45 | | #include <libsolutil/FunctionSelector.h> |
46 | | #include <libsolutil/StackTooDeepString.h> |
47 | | |
48 | | #include <liblangutil/ErrorReporter.h> |
49 | | #include <liblangutil/Scanner.h> |
50 | | #include <liblangutil/SourceReferenceFormatter.h> |
51 | | |
52 | | #include <utility> |
53 | | |
54 | | // Change to "define" to output all intermediate code |
55 | | #undef SOL_OUTPUT_ASM |
56 | | |
57 | | |
58 | | using namespace solidity; |
59 | | using namespace solidity::util; |
60 | | using namespace solidity::evmasm; |
61 | | using namespace solidity::frontend; |
62 | | using namespace solidity::langutil; |
63 | | |
64 | | void CompilerContext::addStateVariable( |
65 | | VariableDeclaration const& _declaration, |
66 | | u256 const& _storageOffset, |
67 | | unsigned _byteOffset |
68 | | ) |
69 | 11.2k | { |
70 | 11.2k | m_stateVariables[&_declaration] = std::make_pair(_storageOffset, _byteOffset); |
71 | 11.2k | } |
72 | | |
73 | | void CompilerContext::addImmutable(VariableDeclaration const& _variable) |
74 | 312 | { |
75 | 312 | solAssert(_variable.immutable(), "Attempted to register a non-immutable variable as immutable."); |
76 | 312 | solUnimplementedAssert(_variable.annotation().type->isValueType(), "Only immutable variables of value type are supported."); |
77 | 312 | solAssert(m_runtimeContext, "Attempted to register an immutable variable for runtime code generation."); |
78 | 312 | m_immutableVariables[&_variable] = CompilerUtils::generalPurposeMemoryStart + *m_reservedMemory; |
79 | 312 | solAssert(_variable.annotation().type->memoryHeadSize() == 32, "Memory writes might overlap."); |
80 | 312 | *m_reservedMemory += _variable.annotation().type->memoryHeadSize(); |
81 | 312 | } |
82 | | |
83 | | size_t CompilerContext::immutableMemoryOffset(VariableDeclaration const& _variable) const |
84 | 813 | { |
85 | 813 | solAssert(m_immutableVariables.count(&_variable), "Memory offset of unknown immutable queried."); |
86 | 813 | solAssert(m_runtimeContext, "Attempted to fetch the memory offset of an immutable variable during runtime code generation."); |
87 | 813 | return m_immutableVariables.at(&_variable); |
88 | 813 | } |
89 | | |
90 | | std::vector<std::string> CompilerContext::immutableVariableSlotNames(VariableDeclaration const& _variable) |
91 | 443 | { |
92 | 443 | std::string baseName = std::to_string(_variable.id()); |
93 | 443 | solAssert(_variable.annotation().type->sizeOnStack() > 0, ""); |
94 | 443 | if (_variable.annotation().type->sizeOnStack() == 1) |
95 | 443 | return {baseName}; |
96 | 0 | std::vector<std::string> names; |
97 | 0 | auto collectSlotNames = [&](std::string const& _baseName, Type const* type, auto const& _recurse) -> void { |
98 | 0 | for (auto const& [slot, type]: type->stackItems()) |
99 | 0 | if (type) |
100 | 0 | _recurse(_baseName + " " + slot, type, _recurse); |
101 | 0 | else |
102 | 0 | names.emplace_back(_baseName); |
103 | 0 | }; |
104 | 0 | collectSlotNames(baseName, _variable.annotation().type, collectSlotNames); |
105 | 0 | return names; |
106 | 443 | } |
107 | | |
108 | | size_t CompilerContext::reservedMemory() |
109 | 21.4k | { |
110 | 21.4k | solAssert(m_reservedMemory.has_value(), "Reserved memory was used before "); |
111 | 21.4k | size_t reservedMemory = *m_reservedMemory; |
112 | 21.4k | m_reservedMemory = std::nullopt; |
113 | 21.4k | return reservedMemory; |
114 | 21.4k | } |
115 | | |
116 | | void CompilerContext::startFunction(Declaration const& _function) |
117 | 15.4k | { |
118 | 15.4k | m_functionCompilationQueue.startFunction(_function); |
119 | 15.4k | *this << functionEntryLabel(_function); |
120 | 15.4k | } |
121 | | |
122 | | void CompilerContext::callLowLevelFunction( |
123 | | std::string const& _name, |
124 | | unsigned _inArgs, |
125 | | unsigned _outArgs, |
126 | | std::function<void(CompilerContext&)> const& _generator |
127 | | ) |
128 | 4.04k | { |
129 | 4.04k | evmasm::AssemblyItem retTag = pushNewTag(); |
130 | 4.04k | CompilerUtils(*this).moveIntoStack(_inArgs); |
131 | | |
132 | 4.04k | *this << lowLevelFunctionTag(_name, _inArgs, _outArgs, _generator); |
133 | | |
134 | 4.04k | appendJump(evmasm::AssemblyItem::JumpType::IntoFunction); |
135 | 4.04k | adjustStackOffset(static_cast<int>(_outArgs) - 1 - static_cast<int>(_inArgs)); |
136 | 4.04k | *this << retTag.tag(); |
137 | 4.04k | } |
138 | | |
139 | | void CompilerContext::callYulFunction( |
140 | | std::string const& _name, |
141 | | unsigned _inArgs, |
142 | | unsigned _outArgs |
143 | | ) |
144 | 160k | { |
145 | 160k | m_externallyUsedYulFunctions.insert(_name); |
146 | 160k | auto const retTag = pushNewTag(); |
147 | 160k | CompilerUtils(*this).moveIntoStack(_inArgs); |
148 | 160k | appendJumpTo(namedTag(_name, _inArgs, _outArgs, {}), evmasm::AssemblyItem::JumpType::IntoFunction); |
149 | 160k | adjustStackOffset(static_cast<int>(_outArgs) - 1 - static_cast<int>(_inArgs)); |
150 | 160k | *this << retTag.tag(); |
151 | 160k | } |
152 | | |
153 | | evmasm::AssemblyItem CompilerContext::lowLevelFunctionTag( |
154 | | std::string const& _name, |
155 | | unsigned _inArgs, |
156 | | unsigned _outArgs, |
157 | | std::function<void(CompilerContext&)> const& _generator |
158 | | ) |
159 | 4.24k | { |
160 | 4.24k | auto it = m_lowLevelFunctions.find(_name); |
161 | 4.24k | if (it == m_lowLevelFunctions.end()) |
162 | 3.76k | { |
163 | 3.76k | evmasm::AssemblyItem tag = newTag().pushTag(); |
164 | 3.76k | m_lowLevelFunctions.insert(make_pair(_name, tag)); |
165 | 3.76k | m_lowLevelFunctionGenerationQueue.push(make_tuple(_name, _inArgs, _outArgs, _generator)); |
166 | 3.76k | return tag; |
167 | 3.76k | } |
168 | 482 | else |
169 | 482 | return it->second; |
170 | 4.24k | } |
171 | | |
172 | | void CompilerContext::appendMissingLowLevelFunctions() |
173 | 21.6k | { |
174 | 25.3k | while (!m_lowLevelFunctionGenerationQueue.empty()) |
175 | 3.76k | { |
176 | 3.76k | std::string name; |
177 | 3.76k | unsigned inArgs; |
178 | 3.76k | unsigned outArgs; |
179 | 3.76k | std::function<void(CompilerContext&)> generator; |
180 | 3.76k | tie(name, inArgs, outArgs, generator) = m_lowLevelFunctionGenerationQueue.front(); |
181 | 3.76k | m_lowLevelFunctionGenerationQueue.pop(); |
182 | | |
183 | 3.76k | setStackOffset(static_cast<int>(inArgs) + 1); |
184 | 3.76k | *this << m_lowLevelFunctions.at(name).tag(); |
185 | 3.76k | generator(*this); |
186 | 3.76k | CompilerUtils(*this).moveToStackTop(outArgs); |
187 | 3.76k | appendJump(evmasm::AssemblyItem::JumpType::OutOfFunction); |
188 | 3.76k | solAssert(stackHeight() == outArgs, "Invalid stack height in low-level function " + name + "."); |
189 | 3.76k | } |
190 | 21.6k | } |
191 | | |
192 | | void CompilerContext::appendYulUtilityFunctions(OptimiserSettings const& _optimiserSettings) |
193 | 21.6k | { |
194 | 21.6k | solAssert(!m_appendYulUtilityFunctionsRan, "requestedYulFunctions called more than once."); |
195 | 21.6k | m_appendYulUtilityFunctionsRan = true; |
196 | | |
197 | 21.6k | std::string code = m_yulFunctionCollector.requestedFunctions(); |
198 | 21.6k | if (!code.empty()) |
199 | 7.59k | { |
200 | 7.59k | appendInlineAssembly( |
201 | 7.59k | yul::reindent("{\n" + std::move(code) + "\n}"), |
202 | 7.59k | {}, |
203 | 7.59k | m_externallyUsedYulFunctions, |
204 | 7.59k | true, |
205 | 7.59k | _optimiserSettings, |
206 | 7.59k | yulUtilityFileName() |
207 | 7.59k | ); |
208 | 7.59k | solAssert(!m_generatedYulUtilityCode.empty(), ""); |
209 | 7.59k | } |
210 | 21.6k | } |
211 | | |
212 | | void CompilerContext::addVariable( |
213 | | VariableDeclaration const& _declaration, |
214 | | unsigned _offsetToCurrent |
215 | | ) |
216 | 26.2k | { |
217 | 26.2k | solAssert(m_asm->deposit() >= 0 && unsigned(m_asm->deposit()) >= _offsetToCurrent, ""); |
218 | 26.2k | unsigned sizeOnStack = _declaration.annotation().type->sizeOnStack(); |
219 | | // Variables should not have stack size other than [1, 2], |
220 | | // but that might change when new types are introduced. |
221 | 26.2k | solAssert(sizeOnStack == 1 || sizeOnStack == 2, ""); |
222 | 26.2k | m_localVariables[&_declaration].push_back(unsigned(m_asm->deposit()) - _offsetToCurrent); |
223 | 26.2k | } |
224 | | |
225 | | void CompilerContext::removeVariable(Declaration const& _declaration) |
226 | 26.2k | { |
227 | 26.2k | solAssert(m_localVariables.count(&_declaration) && !m_localVariables[&_declaration].empty(), ""); |
228 | 26.2k | m_localVariables[&_declaration].pop_back(); |
229 | 26.2k | if (m_localVariables[&_declaration].empty()) |
230 | 26.0k | m_localVariables.erase(&_declaration); |
231 | 26.2k | } |
232 | | |
233 | | void CompilerContext::removeVariablesAboveStackHeight(unsigned _stackHeight) |
234 | 17.9k | { |
235 | 17.9k | std::vector<Declaration const*> toRemove; |
236 | 17.9k | for (auto _var: m_localVariables) |
237 | 39.0k | { |
238 | 39.0k | solAssert(!_var.second.empty(), ""); |
239 | 39.0k | solAssert(_var.second.back() <= stackHeight(), ""); |
240 | 39.0k | if (_var.second.back() >= _stackHeight) |
241 | 7.84k | toRemove.push_back(_var.first); |
242 | 39.0k | } |
243 | 17.9k | for (auto _var: toRemove) |
244 | 7.84k | removeVariable(*_var); |
245 | 17.9k | } |
246 | | |
247 | | unsigned CompilerContext::numberOfLocalVariables() const |
248 | 12.2k | { |
249 | 12.2k | return static_cast<unsigned>(m_localVariables.size()); |
250 | 12.2k | } |
251 | | |
252 | | std::shared_ptr<evmasm::Assembly> CompilerContext::compiledContract(ContractDefinition const& _contract) const |
253 | 176 | { |
254 | 176 | auto ret = m_otherCompilers.find(&_contract); |
255 | 176 | solAssert(ret != m_otherCompilers.end(), "Compiled contract not found."); |
256 | 176 | return ret->second->assemblyPtr(); |
257 | 176 | } |
258 | | |
259 | | std::shared_ptr<evmasm::Assembly> CompilerContext::compiledContractRuntime(ContractDefinition const& _contract) const |
260 | 8 | { |
261 | 8 | auto ret = m_otherCompilers.find(&_contract); |
262 | 8 | solAssert(ret != m_otherCompilers.end(), "Compiled contract not found."); |
263 | 8 | return ret->second->runtimeAssemblyPtr(); |
264 | 8 | } |
265 | | |
266 | | bool CompilerContext::isLocalVariable(Declaration const* _declaration) const |
267 | 96.4k | { |
268 | 96.4k | return !!m_localVariables.count(_declaration); |
269 | 96.4k | } |
270 | | |
271 | | evmasm::AssemblyItem CompilerContext::functionEntryLabel(Declaration const& _declaration) |
272 | 37.0k | { |
273 | 37.0k | return m_functionCompilationQueue.entryLabel(_declaration, *this); |
274 | 37.0k | } |
275 | | |
276 | | evmasm::AssemblyItem CompilerContext::functionEntryLabelIfExists(Declaration const& _declaration) const |
277 | 0 | { |
278 | 0 | return m_functionCompilationQueue.entryLabelIfExists(_declaration); |
279 | 0 | } |
280 | | |
281 | | FunctionDefinition const& CompilerContext::superFunction(FunctionDefinition const& _function, ContractDefinition const& _base) |
282 | 61 | { |
283 | 61 | solAssert(m_mostDerivedContract, "No most derived contract set."); |
284 | 61 | ContractDefinition const* super = _base.superContract(mostDerivedContract()); |
285 | 61 | solAssert(super, "Super contract not available."); |
286 | | |
287 | 61 | FunctionDefinition const& resolvedFunction = _function.resolveVirtual(mostDerivedContract(), super); |
288 | 61 | solAssert(resolvedFunction.isImplemented(), ""); |
289 | | |
290 | 61 | return resolvedFunction; |
291 | 61 | } |
292 | | |
293 | | ContractDefinition const& CompilerContext::mostDerivedContract() const |
294 | 20.2k | { |
295 | 20.2k | solAssert(m_mostDerivedContract, "Most derived contract not set."); |
296 | 20.2k | return *m_mostDerivedContract; |
297 | 20.2k | } |
298 | | |
299 | | Declaration const* CompilerContext::nextFunctionToCompile() const |
300 | 49.1k | { |
301 | 49.1k | return m_functionCompilationQueue.nextFunctionToCompile(); |
302 | 49.1k | } |
303 | | |
304 | | unsigned CompilerContext::baseStackOffsetOfVariable(Declaration const& _declaration) const |
305 | 124k | { |
306 | 124k | auto res = m_localVariables.find(&_declaration); |
307 | 124k | solAssert(res != m_localVariables.end(), "Variable not found on stack."); |
308 | 124k | solAssert(!res->second.empty(), ""); |
309 | 124k | return res->second.back(); |
310 | 124k | } |
311 | | |
312 | | unsigned CompilerContext::baseToCurrentStackOffset(unsigned _baseOffset) const |
313 | 132k | { |
314 | 132k | return static_cast<unsigned>(m_asm->deposit()) - _baseOffset - 1; |
315 | 132k | } |
316 | | |
317 | | unsigned CompilerContext::currentToBaseStackOffset(unsigned _offset) const |
318 | 8.91k | { |
319 | 8.91k | return static_cast<unsigned>(m_asm->deposit()) - _offset - 1; |
320 | 8.91k | } |
321 | | |
322 | | std::pair<u256, unsigned> CompilerContext::storageLocationOfVariable(Declaration const& _declaration) const |
323 | 26.2k | { |
324 | 26.2k | auto it = m_stateVariables.find(&_declaration); |
325 | 26.2k | solAssert(it != m_stateVariables.end(), "Variable not found in storage."); |
326 | 26.2k | return it->second; |
327 | 26.2k | } |
328 | | |
329 | | CompilerContext& CompilerContext::appendJump(evmasm::AssemblyItem::JumpType _jumpType) |
330 | 259k | { |
331 | 259k | evmasm::AssemblyItem item(Instruction::JUMP); |
332 | 259k | item.setJumpType(_jumpType); |
333 | 259k | return *this << item; |
334 | 259k | } |
335 | | |
336 | | CompilerContext& CompilerContext::appendPanic(util::PanicCode _code) |
337 | 113k | { |
338 | 113k | callYulFunction(utilFunctions().panicFunction(_code), 0, 0); |
339 | 113k | return *this; |
340 | 113k | } |
341 | | |
342 | | CompilerContext& CompilerContext::appendConditionalPanic(util::PanicCode _code) |
343 | 111k | { |
344 | 111k | *this << Instruction::ISZERO; |
345 | 111k | evmasm::AssemblyItem afterTag = appendConditionalJump(); |
346 | 111k | appendPanic(_code); |
347 | 111k | *this << afterTag; |
348 | 111k | return *this; |
349 | 111k | } |
350 | | |
351 | | CompilerContext& CompilerContext::appendRevert(std::string const& _message) |
352 | 11.1k | { |
353 | 11.1k | appendInlineAssembly("{ " + revertReasonIfDebug(_message) + " }"); |
354 | 11.1k | return *this; |
355 | 11.1k | } |
356 | | |
357 | | CompilerContext& CompilerContext::appendConditionalRevert(bool _forwardReturnData, std::string const& _message) |
358 | 21.1k | { |
359 | 21.1k | if (_forwardReturnData && m_evmVersion.supportsReturndata()) |
360 | 2.17k | appendInlineAssembly(R"({ |
361 | 2.17k | if condition { |
362 | 2.17k | returndatacopy(0, 0, returndatasize()) |
363 | 2.17k | revert(0, returndatasize()) |
364 | 2.17k | } |
365 | 2.17k | })", {"condition"}); |
366 | 18.9k | else |
367 | 18.9k | appendInlineAssembly("{ if condition { " + revertReasonIfDebug(_message) + " } }", {"condition"}); |
368 | 21.1k | *this << Instruction::POP; |
369 | 21.1k | return *this; |
370 | 21.1k | } |
371 | | |
372 | | void CompilerContext::resetVisitedNodes(ASTNode const* _node) |
373 | 21.4k | { |
374 | 21.4k | std::stack<ASTNode const*> newStack; |
375 | 21.4k | newStack.push(_node); |
376 | 21.4k | std::swap(m_visitedNodes, newStack); |
377 | 21.4k | updateSourceLocation(); |
378 | 21.4k | } |
379 | | |
380 | | void CompilerContext::appendInlineAssembly( |
381 | | std::string const& _assembly, |
382 | | std::vector<std::string> const& _localVariables, |
383 | | std::set<std::string> const& _externallyUsedFunctions, |
384 | | bool _system, |
385 | | OptimiserSettings const& _optimiserSettings, |
386 | | std::string _sourceName |
387 | | ) |
388 | 48.0k | { |
389 | 48.0k | unsigned startStackHeight = stackHeight(); |
390 | | |
391 | 48.0k | std::set<yul::YulName> externallyUsedIdentifiers; |
392 | 48.0k | for (auto const& fun: _externallyUsedFunctions) |
393 | 20.6k | externallyUsedIdentifiers.insert(yul::YulName(fun)); |
394 | 48.0k | for (auto const& var: _localVariables) |
395 | 35.6k | externallyUsedIdentifiers.insert(yul::YulName(var)); |
396 | | |
397 | 48.0k | yul::ExternalIdentifierAccess identifierAccess; |
398 | 48.0k | identifierAccess.resolve = [&]( |
399 | 48.0k | yul::Identifier const& _identifier, |
400 | 48.0k | yul::IdentifierContext, |
401 | 48.0k | bool _insideFunction |
402 | 48.0k | ) -> bool |
403 | 1.32M | { |
404 | 1.32M | if (_insideFunction) |
405 | 1.25M | return false; |
406 | 71.3k | return util::contains(_localVariables, _identifier.name.str()); |
407 | 1.32M | }; |
408 | 48.0k | identifierAccess.generateCode = [&]( |
409 | 48.0k | yul::Identifier const& _identifier, |
410 | 48.0k | yul::IdentifierContext _context, |
411 | 48.0k | yul::AbstractAssembly& _assembly |
412 | 48.0k | ) |
413 | 53.3k | { |
414 | 53.3k | solAssert(_context == yul::IdentifierContext::RValue || _context == yul::IdentifierContext::LValue, ""); |
415 | 53.3k | auto it = std::find(_localVariables.begin(), _localVariables.end(), _identifier.name.str()); |
416 | 53.3k | solAssert(it != _localVariables.end(), ""); |
417 | 53.3k | auto stackDepth = static_cast<size_t>(distance(it, _localVariables.end())); |
418 | 53.3k | size_t stackDiff = static_cast<size_t>(_assembly.stackHeight()) - startStackHeight + stackDepth; |
419 | 53.3k | if (_context == yul::IdentifierContext::LValue) |
420 | 6.58k | stackDiff -= 1; |
421 | 53.3k | if (stackDiff < 1 || stackDiff > reachableStackDepth()) |
422 | 53.3k | BOOST_THROW_EXCEPTION( |
423 | 53.3k | StackTooDeepError() << |
424 | 53.3k | errinfo_sourceLocation(nativeLocationOf(_identifier)) << |
425 | 53.3k | util::errinfo_comment(util::stackTooDeepString) |
426 | 53.3k | ); |
427 | 53.3k | if (_context == yul::IdentifierContext::RValue) |
428 | 46.7k | _assembly.appendInstruction(dupInstruction(static_cast<unsigned>(stackDiff))); |
429 | 6.58k | else |
430 | 6.58k | { |
431 | 6.58k | _assembly.appendInstruction(swapInstruction(static_cast<unsigned>(stackDiff))); |
432 | 6.58k | _assembly.appendInstruction(Instruction::POP); |
433 | 6.58k | } |
434 | 53.3k | }; |
435 | | |
436 | 48.0k | ErrorList errors; |
437 | 48.0k | ErrorReporter errorReporter(errors); |
438 | 48.0k | langutil::CharStream charStream(_assembly, _sourceName); |
439 | 48.0k | yul::EVMDialect const& dialect = yul::EVMDialect::strictAssemblyForEVM(m_evmVersion); |
440 | 48.0k | std::optional<langutil::SourceLocation> locationOverride; |
441 | 48.0k | if (!_system) |
442 | 40.4k | locationOverride = m_asm->currentSourceLocation(); |
443 | 48.0k | std::shared_ptr<yul::AST> parserResult = |
444 | 48.0k | yul::Parser(errorReporter, dialect, std::move(locationOverride)) |
445 | 48.0k | .parse(charStream); |
446 | | #ifdef SOL_OUTPUT_ASM |
447 | | std::cout << yul::AsmPrinter::format(*parserResult) << std::endl; |
448 | | #endif |
449 | | |
450 | 48.0k | auto reportError = [&](std::string const& _context) |
451 | 48.0k | { |
452 | 0 | std::string message = |
453 | 0 | "Error parsing/analyzing inline assembly block:\n" + |
454 | 0 | _context + "\n" |
455 | 0 | "------------------ Input: -----------------\n" + |
456 | 0 | _assembly + "\n" |
457 | 0 | "------------------ Errors: ----------------\n"; |
458 | 0 | for (auto const& error: errorReporter.errors()) |
459 | | // TODO if we have "locationOverride", it will be the wrong char stream, |
460 | | // but we do not have access to the solidity scanner. |
461 | 0 | message += SourceReferenceFormatter::formatErrorInformation(*error, charStream); |
462 | 0 | message += "-------------------------------------------\n"; |
463 | |
|
464 | 0 | solAssert(false, message); |
465 | 0 | }; |
466 | | |
467 | 48.0k | yul::AsmAnalysisInfo analysisInfo; |
468 | 48.0k | bool analyzerResult = false; |
469 | 48.0k | if (parserResult) |
470 | 48.0k | analyzerResult = yul::AsmAnalyzer( |
471 | 48.0k | analysisInfo, |
472 | 48.0k | errorReporter, |
473 | 48.0k | dialect, |
474 | 48.0k | identifierAccess.resolve |
475 | 48.0k | ).analyze(parserResult->root()); |
476 | 48.0k | if (!parserResult || errorReporter.hasErrorsWarningsOrInfos() || !analyzerResult) |
477 | 0 | reportError("Invalid assembly generated by code generator."); |
478 | 48.0k | std::shared_ptr<yul::AST const> toBeAssembledAST = parserResult; |
479 | | |
480 | | // Several optimizer steps cannot handle externally supplied stack variables, |
481 | | // so we essentially only optimize the ABI functions. |
482 | 48.0k | if (_optimiserSettings.runYulOptimiser && _localVariables.empty()) |
483 | 1.50k | { |
484 | 1.50k | yul::Object obj; |
485 | 1.50k | obj.setCode(parserResult, std::make_shared<yul::AsmAnalysisInfo>(analysisInfo)); |
486 | | |
487 | 1.50k | solAssert(!dialect.providesObjectAccess()); |
488 | 1.50k | optimizeYul(obj, _optimiserSettings, externallyUsedIdentifiers); |
489 | | |
490 | 1.50k | if (_system) |
491 | 1.50k | { |
492 | | // Store as generated sources, but first re-parse to update the source references. |
493 | 1.50k | solAssert(m_generatedYulUtilityCode.empty(), ""); |
494 | 1.50k | m_generatedYulUtilityCode = yul::AsmPrinter::format(*obj.code()); |
495 | 1.50k | langutil::CharStream charStream(m_generatedYulUtilityCode, _sourceName); |
496 | 1.50k | obj.setCode(yul::Parser(errorReporter, dialect).parse(charStream)); |
497 | 1.50k | obj.analysisInfo = std::make_shared<yul::AsmAnalysisInfo>(yul::AsmAnalyzer::analyzeStrictAssertCorrect(obj)); |
498 | 1.50k | } |
499 | | |
500 | 1.50k | analysisInfo = std::move(*obj.analysisInfo); |
501 | 1.50k | toBeAssembledAST = obj.code(); |
502 | | |
503 | | #ifdef SOL_OUTPUT_ASM |
504 | | std::cout << "After optimizer:" << std::endl; |
505 | | std::cout << yul::AsmPrinter::format(*parserResult) << std::endl; |
506 | | #endif |
507 | 1.50k | } |
508 | 46.5k | else if (_system) |
509 | 6.09k | { |
510 | | // Store as generated source. |
511 | 6.09k | solAssert(m_generatedYulUtilityCode.empty(), ""); |
512 | 6.09k | m_generatedYulUtilityCode = _assembly; |
513 | 6.09k | } |
514 | | |
515 | 48.0k | if (errorReporter.hasErrorsWarningsOrInfos()) |
516 | 0 | reportError("Failed to analyze inline assembly block."); |
517 | | |
518 | 48.0k | solAssert(!errorReporter.hasErrorsWarningsOrInfos(), "Failed to analyze inline assembly block."); |
519 | 48.0k | yul::CodeGenerator::assemble( |
520 | 48.0k | toBeAssembledAST->root(), |
521 | 48.0k | analysisInfo, |
522 | 48.0k | *m_asm, |
523 | 48.0k | m_evmVersion, |
524 | 48.0k | identifierAccess.generateCode, |
525 | 48.0k | _system, |
526 | 48.0k | _optimiserSettings.optimizeStackAllocation |
527 | 48.0k | ); |
528 | | |
529 | | // Reset the source location to the one of the node (instead of the CODEGEN source location) |
530 | 48.0k | updateSourceLocation(); |
531 | 48.0k | } |
532 | | |
533 | | |
534 | | void CompilerContext::optimizeYul(yul::Object& _object, OptimiserSettings const& _optimiserSettings, std::set<yul::YulName> const& _externalIdentifiers) |
535 | 2.55k | { |
536 | 2.55k | yulAssert(_object.dialect()); |
537 | 2.55k | auto const* evmDialect = dynamic_cast<yul::EVMDialect const*>(_object.dialect()); |
538 | 2.55k | yulAssert(evmDialect); |
539 | | #ifdef SOL_OUTPUT_ASM |
540 | | std::cout << yul::AsmPrinter::format(*_object.code()) << std::endl; |
541 | | #endif |
542 | | |
543 | 2.55k | bool const isCreation = runtimeContext() != nullptr; |
544 | 2.55k | yul::GasMeter meter(*evmDialect, isCreation, _optimiserSettings.expectedExecutionsPerDeployment); |
545 | 2.55k | yul::OptimiserSuite::run( |
546 | 2.55k | &meter, |
547 | 2.55k | _object, |
548 | 2.55k | _optimiserSettings.optimizeStackAllocation, |
549 | 2.55k | _optimiserSettings.yulOptimiserSteps, |
550 | 2.55k | _optimiserSettings.yulOptimiserCleanupSteps, |
551 | 2.55k | isCreation? std::nullopt : std::make_optional(_optimiserSettings.expectedExecutionsPerDeployment), |
552 | 2.55k | _externalIdentifiers |
553 | 2.55k | ); |
554 | | |
555 | | #ifdef SOL_OUTPUT_ASM |
556 | | std::cout << "After optimizer:" << std::endl; |
557 | | std::cout << yul::AsmPrinter::format(*_object.code()) << std::endl; |
558 | | #endif |
559 | 2.55k | } |
560 | | |
561 | | std::string CompilerContext::revertReasonIfDebug(std::string const& _message) |
562 | 30.3k | { |
563 | 30.3k | return YulUtilFunctions::revertReasonIfDebugBody( |
564 | 30.3k | m_revertStrings, |
565 | 30.3k | "mload(" + std::to_string(CompilerUtils::freeMemoryPointer) + ")", |
566 | 30.3k | _message |
567 | 30.3k | ); |
568 | 30.3k | } |
569 | | |
570 | | void CompilerContext::updateSourceLocation() |
571 | 1.97M | { |
572 | 1.97M | m_asm->setSourceLocation(m_visitedNodes.empty() ? SourceLocation() : m_visitedNodes.top()->location()); |
573 | 1.97M | } |
574 | | |
575 | | evmasm::AssemblyItem CompilerContext::FunctionCompilationQueue::entryLabel( |
576 | | Declaration const& _declaration, |
577 | | CompilerContext& _context |
578 | | ) |
579 | 37.0k | { |
580 | 37.0k | auto res = m_entryLabels.find(&_declaration); |
581 | 37.0k | if (res == m_entryLabels.end()) |
582 | 15.4k | { |
583 | 15.4k | size_t params = 0; |
584 | 15.4k | size_t returns = 0; |
585 | 15.4k | if (auto const* function = dynamic_cast<FunctionDefinition const*>(&_declaration)) |
586 | 13.6k | { |
587 | 13.6k | FunctionType functionType(*function, FunctionType::Kind::Internal); |
588 | 13.6k | params = CompilerUtils::sizeOnStack(functionType.parameterTypes()); |
589 | 13.6k | returns = CompilerUtils::sizeOnStack(functionType.returnParameterTypes()); |
590 | 13.6k | } |
591 | | |
592 | | // some name that cannot clash with yul function names. |
593 | 15.4k | std::string labelName = "@" + _declaration.name() + "_" + std::to_string(_declaration.id()); |
594 | 15.4k | evmasm::AssemblyItem tag = _context.namedTag( |
595 | 15.4k | labelName, |
596 | 15.4k | params, |
597 | 15.4k | returns, |
598 | 15.4k | _declaration.id() |
599 | 15.4k | ); |
600 | 15.4k | m_entryLabels.insert(std::make_pair(&_declaration, tag)); |
601 | 15.4k | m_functionsToCompile.push(&_declaration); |
602 | 15.4k | return tag.tag(); |
603 | 15.4k | } |
604 | 21.6k | else |
605 | 21.6k | return res->second.tag(); |
606 | | |
607 | 37.0k | } |
608 | | |
609 | | evmasm::AssemblyItem CompilerContext::FunctionCompilationQueue::entryLabelIfExists(Declaration const& _declaration) const |
610 | 0 | { |
611 | 0 | auto res = m_entryLabels.find(&_declaration); |
612 | 0 | return res == m_entryLabels.end() ? evmasm::AssemblyItem(evmasm::UndefinedItem) : res->second.tag(); |
613 | 0 | } |
614 | | |
615 | | Declaration const* CompilerContext::FunctionCompilationQueue::nextFunctionToCompile() const |
616 | 49.1k | { |
617 | 50.7k | while (!m_functionsToCompile.empty()) |
618 | 21.2k | { |
619 | 21.2k | if (m_alreadyCompiledFunctions.count(m_functionsToCompile.front())) |
620 | 1.55k | m_functionsToCompile.pop(); |
621 | 19.7k | else |
622 | 19.7k | return m_functionsToCompile.front(); |
623 | 21.2k | } |
624 | 29.4k | return nullptr; |
625 | 49.1k | } |
626 | | |
627 | | void CompilerContext::FunctionCompilationQueue::startFunction(Declaration const& _function) |
628 | 15.4k | { |
629 | 15.4k | if (!m_functionsToCompile.empty() && m_functionsToCompile.front() == &_function) |
630 | 13.8k | m_functionsToCompile.pop(); |
631 | 15.4k | m_alreadyCompiledFunctions.insert(&_function); |
632 | 15.4k | } |