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