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