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