mirror of
				https://github.com/ethereum/solidity
				synced 2023-10-03 13:03:40 +00:00 
			
		
		
		
	Merge pull request #9823 from ethereum/develop
Merge develop into breaking.
This commit is contained in:
		
						commit
						9750286ee4
					
				| @ -9,6 +9,7 @@ Language Features: | |||||||
| 
 | 
 | ||||||
| Compiler Features: | Compiler Features: | ||||||
|  * Export compiler-generated utility sources via standard-json or combined-json. |  * Export compiler-generated utility sources via standard-json or combined-json. | ||||||
|  |  * SMTChecker: Support events and low-level logs. | ||||||
|  * SMTChecker: Support ``revert()``. |  * SMTChecker: Support ``revert()``. | ||||||
|  * SMTChecker: Support shifts. |  * SMTChecker: Support shifts. | ||||||
|  * SMTChecker: Support structs. |  * SMTChecker: Support structs. | ||||||
| @ -16,13 +17,16 @@ Compiler Features: | |||||||
|  * Yul Optimizer: Prune unused parameters in functions. |  * Yul Optimizer: Prune unused parameters in functions. | ||||||
|  * Yul Optimizer: Inline into functions further down in the call graph first. |  * Yul Optimizer: Inline into functions further down in the call graph first. | ||||||
|  * Yul Optimizer: Try to simplify function names. |  * Yul Optimizer: Try to simplify function names. | ||||||
|  |  * Yul IR Generator: Report source locations related to unimplemented features. | ||||||
| 
 | 
 | ||||||
| 
 | 
 | ||||||
| Bugfixes: | Bugfixes: | ||||||
|  |  * Code generator: Fix internal error on stripping dynamic types from return parameters on EVM versions without ``RETURNDATACOPY``. | ||||||
|  * Type Checker: Disallow ``virtual`` for modifiers in libraries. |  * Type Checker: Disallow ``virtual`` for modifiers in libraries. | ||||||
|  * Type Checker: Correct the warning for homonymous, but not shadowing declarations. |  * Type Checker: Correct the warning for homonymous, but not shadowing declarations. | ||||||
|  * ViewPureChecker: Prevent visibility check on constructors. |  * ViewPureChecker: Prevent visibility check on constructors. | ||||||
|  * Type system: Fix internal error on implicit conversion of contract instance to the type of its ``super``. |  * Type system: Fix internal error on implicit conversion of contract instance to the type of its ``super``. | ||||||
|  |  * Type system: Fix internal error on implicit conversion of string literal to a calldata string. | ||||||
|  * Type system: Fix named parameters in overloaded function and event calls being matched incorrectly if the order differs from the declaration. |  * Type system: Fix named parameters in overloaded function and event calls being matched incorrectly if the order differs from the declaration. | ||||||
| 
 | 
 | ||||||
| ### 0.7.1 (2020-09-02) | ### 0.7.1 (2020-09-02) | ||||||
|  | |||||||
| @ -163,8 +163,13 @@ Homebrew formula directly from Github. | |||||||
| View | View | ||||||
| `solidity.rb commits on Github <https://github.com/ethereum/homebrew-ethereum/commits/master/solidity.rb>`_. | `solidity.rb commits on Github <https://github.com/ethereum/homebrew-ethereum/commits/master/solidity.rb>`_. | ||||||
| 
 | 
 | ||||||
| Follow the history links until you have a raw file link of a | Copy the commit hash of the version you want and check it out on your machine. | ||||||
| specific commit of ``solidity.rb``. | 
 | ||||||
|  | .. code-block:: bash | ||||||
|  | 
 | ||||||
|  |     git clone https://github.com/ethereum/homebrew-ethereum.git | ||||||
|  |     cd homebrew-ethereum | ||||||
|  |     git checkout <your-hash-goes-here> | ||||||
| 
 | 
 | ||||||
| Install it using ``brew``: | Install it using ``brew``: | ||||||
| 
 | 
 | ||||||
| @ -172,7 +177,7 @@ Install it using ``brew``: | |||||||
| 
 | 
 | ||||||
|     brew unlink solidity |     brew unlink solidity | ||||||
|     # eg. Install 0.4.8 |     # eg. Install 0.4.8 | ||||||
|     brew install https://raw.githubusercontent.com/ethereum/homebrew-ethereum/77cce03da9f289e5a3ffe579840d3c5dc0a62717/solidity.rb |     brew install solidity.rb | ||||||
| 
 | 
 | ||||||
| Gentoo Linux has an `Ethereum overlay <https://overlays.gentoo.org/#ethereum>`_ that contains a solidity package. | Gentoo Linux has an `Ethereum overlay <https://overlays.gentoo.org/#ethereum>`_ that contains a solidity package. | ||||||
| After the overlay is setup, ``solc`` can be installed in x86_64 architectures by: | After the overlay is setup, ``solc`` can be installed in x86_64 architectures by: | ||||||
|  | |||||||
| @ -1110,6 +1110,7 @@ Abbreviation Full name | |||||||
| ``L``        ``LoadResolver`` | ``L``        ``LoadResolver`` | ||||||
| ``M``        ``LoopInvariantCodeMotion`` | ``M``        ``LoopInvariantCodeMotion`` | ||||||
| ``r``        ``RedundantAssignEliminator`` | ``r``        ``RedundantAssignEliminator`` | ||||||
|  | ``R``        ``ReasoningBasedSimplifier`` - highly experimental | ||||||
| ``m``        ``Rematerialiser`` | ``m``        ``Rematerialiser`` | ||||||
| ``V``        ``SSAReverser`` | ``V``        ``SSAReverser`` | ||||||
| ``a``        ``SSATransform`` | ``a``        ``SSATransform`` | ||||||
| @ -1121,6 +1122,10 @@ Abbreviation Full name | |||||||
| Some steps depend on properties ensured by ``BlockFlattener``, ``FunctionGrouper``, ``ForLoopInitRewriter``. | Some steps depend on properties ensured by ``BlockFlattener``, ``FunctionGrouper``, ``ForLoopInitRewriter``. | ||||||
| For this reason the Yul optimizer always applies them before applying any steps supplied by the user. | For this reason the Yul optimizer always applies them before applying any steps supplied by the user. | ||||||
| 
 | 
 | ||||||
|  | The ReasoningBasedSimplifier is an optimizer step that is currently not enabled | ||||||
|  | in the default set of steps. It uses an SMT solver to simplify arithmetic expressions | ||||||
|  | and boolean conditions. It has not received thorough testing or validation yet and can produce | ||||||
|  | non-reproducible results, so please use with care! | ||||||
| 
 | 
 | ||||||
| .. _erc20yul: | .. _erc20yul: | ||||||
| 
 | 
 | ||||||
|  | |||||||
| @ -9,6 +9,7 @@ set(sources | |||||||
| 	SolverInterface.h | 	SolverInterface.h | ||||||
| 	Sorts.cpp | 	Sorts.cpp | ||||||
| 	Sorts.h | 	Sorts.h | ||||||
|  | 	Helpers.h | ||||||
| ) | ) | ||||||
| 
 | 
 | ||||||
| if (${Z3_FOUND}) | if (${Z3_FOUND}) | ||||||
|  | |||||||
							
								
								
									
										58
									
								
								libsmtutil/Helpers.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										58
									
								
								libsmtutil/Helpers.h
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,58 @@ | |||||||
|  | /*
 | ||||||
|  | 	This file is part of solidity. | ||||||
|  | 
 | ||||||
|  | 	solidity is free software: you can redistribute it and/or modify | ||||||
|  | 	it under the terms of the GNU General Public License as published by | ||||||
|  | 	the Free Software Foundation, either version 3 of the License, or | ||||||
|  | 	(at your option) any later version. | ||||||
|  | 
 | ||||||
|  | 	solidity is distributed in the hope that it will be useful, | ||||||
|  | 	but WITHOUT ANY WARRANTY; without even the implied warranty of | ||||||
|  | 	MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the | ||||||
|  | 	GNU General Public License for more details. | ||||||
|  | 
 | ||||||
|  | 	You should have received a copy of the GNU General Public License | ||||||
|  | 	along with solidity.  If not, see <http://www.gnu.org/licenses/>.
 | ||||||
|  | */ | ||||||
|  | // SPDX-License-Identifier: GPL-3.0
 | ||||||
|  | 
 | ||||||
|  | #pragma once | ||||||
|  | 
 | ||||||
|  | #include <libsmtutil/SolverInterface.h> | ||||||
|  | 
 | ||||||
|  | namespace solidity::smtutil | ||||||
|  | { | ||||||
|  | 
 | ||||||
|  | /// Signed division in SMTLIB2 rounds differently than EVM.
 | ||||||
|  | /// This does not check for division by zero!
 | ||||||
|  | inline Expression signedDivision(Expression _left, Expression _right) | ||||||
|  | { | ||||||
|  | 	return Expression::ite( | ||||||
|  | 		_left >= 0, | ||||||
|  | 		Expression::ite(_right >= 0, _left / _right, 0 - (_left / (0 - _right))), | ||||||
|  | 		Expression::ite(_right >= 0, 0 - ((0 - _left) / _right), (0 - _left) / (0 - _right)) | ||||||
|  | 	); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | inline Expression abs(Expression _value) | ||||||
|  | { | ||||||
|  | 	return Expression::ite(_value >= 0, _value, 0 - _value); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | /// Signed modulo in SMTLIB2 behaves differently with regards
 | ||||||
|  | /// to the sign than EVM.
 | ||||||
|  | /// This does not check for modulo by zero!
 | ||||||
|  | inline Expression signedModulo(Expression _left, Expression _right) | ||||||
|  | { | ||||||
|  | 	return Expression::ite( | ||||||
|  | 		_left >= 0, | ||||||
|  | 		_left % _right, | ||||||
|  | 		Expression::ite( | ||||||
|  | 			(_left % _right) == 0, | ||||||
|  | 			0, | ||||||
|  | 			(_left % _right) - abs(_right) | ||||||
|  | 		) | ||||||
|  | 	); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | } | ||||||
| @ -104,6 +104,8 @@ set(sources | |||||||
| 	formal/ModelChecker.h | 	formal/ModelChecker.h | ||||||
| 	formal/Predicate.cpp | 	formal/Predicate.cpp | ||||||
| 	formal/Predicate.h | 	formal/Predicate.h | ||||||
|  | 	formal/PredicateSort.cpp | ||||||
|  | 	formal/PredicateSort.h | ||||||
| 	formal/SMTEncoder.cpp | 	formal/SMTEncoder.cpp | ||||||
| 	formal/SMTEncoder.h | 	formal/SMTEncoder.h | ||||||
| 	formal/SSAVariable.cpp | 	formal/SSAVariable.cpp | ||||||
|  | |||||||
| @ -1408,6 +1408,7 @@ BoolResult StringLiteralType::isImplicitlyConvertibleTo(Type const& _convertTo) | |||||||
| 		return static_cast<size_t>(fixedBytes->numBytes()) >= m_value.size(); | 		return static_cast<size_t>(fixedBytes->numBytes()) >= m_value.size(); | ||||||
| 	else if (auto arrayType = dynamic_cast<ArrayType const*>(&_convertTo)) | 	else if (auto arrayType = dynamic_cast<ArrayType const*>(&_convertTo)) | ||||||
| 		return | 		return | ||||||
|  | 			arrayType->location() != DataLocation::CallData && | ||||||
| 			arrayType->isByteArray() && | 			arrayType->isByteArray() && | ||||||
| 			!(arrayType->dataStoredIn(DataLocation::Storage) && arrayType->isPointer()) && | 			!(arrayType->dataStoredIn(DataLocation::Storage) && arrayType->isPointer()) && | ||||||
| 			!(arrayType->isString() && !util::validateUTF8(value())); | 			!(arrayType->isString() && !util::validateUTF8(value())); | ||||||
| @ -3010,7 +3011,7 @@ TypePointers FunctionType::returnParameterTypesWithoutDynamicTypes() const | |||||||
| 		m_kind == Kind::BareStaticCall | 		m_kind == Kind::BareStaticCall | ||||||
| 	) | 	) | ||||||
| 		for (auto& param: returnParameterTypes) | 		for (auto& param: returnParameterTypes) | ||||||
| 			if (param->isDynamicallySized() && !param->dataStoredIn(DataLocation::Storage)) | 			if (param->isDynamicallyEncoded() && !param->dataStoredIn(DataLocation::Storage)) | ||||||
| 				param = TypeProvider::inaccessibleDynamic(); | 				param = TypeProvider::inaccessibleDynamic(); | ||||||
| 
 | 
 | ||||||
| 	return returnParameterTypes; | 	return returnParameterTypes; | ||||||
|  | |||||||
| @ -164,7 +164,7 @@ string IRGenerator::generate( | |||||||
| string IRGenerator::generate(Block const& _block) | string IRGenerator::generate(Block const& _block) | ||||||
| { | { | ||||||
| 	IRGeneratorForStatements generator(m_context, m_utils); | 	IRGeneratorForStatements generator(m_context, m_utils); | ||||||
| 	_block.accept(generator); | 	generator.generate(_block); | ||||||
| 	return generator.code(); | 	return generator.code(); | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
|  | |||||||
| @ -38,6 +38,8 @@ | |||||||
| #include <libyul/Dialect.h> | #include <libyul/Dialect.h> | ||||||
| #include <libyul/optimiser/ASTCopier.h> | #include <libyul/optimiser/ASTCopier.h> | ||||||
| 
 | 
 | ||||||
|  | #include <liblangutil/Exceptions.h> | ||||||
|  | 
 | ||||||
| #include <libsolutil/Whiskers.h> | #include <libsolutil/Whiskers.h> | ||||||
| #include <libsolutil/StringUtils.h> | #include <libsolutil/StringUtils.h> | ||||||
| #include <libsolutil/Keccak256.h> | #include <libsolutil/Keccak256.h> | ||||||
| @ -152,70 +154,130 @@ string IRGeneratorForStatements::code() const | |||||||
| 	return m_code.str(); | 	return m_code.str(); | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
|  | void IRGeneratorForStatements::generate(Block const& _block) | ||||||
|  | { | ||||||
|  | 	try | ||||||
|  | 	{ | ||||||
|  | 		_block.accept(*this); | ||||||
|  | 	} | ||||||
|  | 	catch (langutil::UnimplementedFeatureError const& _error) | ||||||
|  | 	{ | ||||||
|  | 		if (!boost::get_error_info<langutil::errinfo_sourceLocation>(_error)) | ||||||
|  | 			_error << langutil::errinfo_sourceLocation(m_currentLocation); | ||||||
|  | 		throw _error; | ||||||
|  | 	} | ||||||
|  | } | ||||||
|  | 
 | ||||||
| void IRGeneratorForStatements::initializeStateVar(VariableDeclaration const& _varDecl) | void IRGeneratorForStatements::initializeStateVar(VariableDeclaration const& _varDecl) | ||||||
| { | { | ||||||
| 	solAssert(_varDecl.immutable() || m_context.isStateVariable(_varDecl), "Must be immutable or a state variable."); | 	try | ||||||
| 	solAssert(!_varDecl.isConstant(), ""); | 	{ | ||||||
| 	if (!_varDecl.value()) | 		setLocation(_varDecl); | ||||||
| 		return; |  | ||||||
| 
 | 
 | ||||||
| 	_varDecl.value()->accept(*this); | 		solAssert(_varDecl.immutable() || m_context.isStateVariable(_varDecl), "Must be immutable or a state variable."); | ||||||
| 	writeToLValue( | 		solAssert(!_varDecl.isConstant(), ""); | ||||||
| 		_varDecl.immutable() ? | 		if (!_varDecl.value()) | ||||||
| 		IRLValue{*_varDecl.annotation().type, IRLValue::Immutable{&_varDecl}} : | 			return; | ||||||
| 		IRLValue{*_varDecl.annotation().type, IRLValue::Storage{ | 
 | ||||||
| 			util::toCompactHexWithPrefix(m_context.storageLocationOfStateVariable(_varDecl).first), | 		_varDecl.value()->accept(*this); | ||||||
| 			m_context.storageLocationOfStateVariable(_varDecl).second | 		writeToLValue( | ||||||
| 		}}, | 			_varDecl.immutable() ? | ||||||
| 		*_varDecl.value() | 			IRLValue{*_varDecl.annotation().type, IRLValue::Immutable{&_varDecl}} : | ||||||
| 	); | 			IRLValue{*_varDecl.annotation().type, IRLValue::Storage{ | ||||||
|  | 				util::toCompactHexWithPrefix(m_context.storageLocationOfStateVariable(_varDecl).first), | ||||||
|  | 				m_context.storageLocationOfStateVariable(_varDecl).second | ||||||
|  | 			}}, | ||||||
|  | 			*_varDecl.value() | ||||||
|  | 		); | ||||||
|  | 	} | ||||||
|  | 	catch (langutil::UnimplementedFeatureError const& _error) | ||||||
|  | 	{ | ||||||
|  | 		if (!boost::get_error_info<langutil::errinfo_sourceLocation>(_error)) | ||||||
|  | 			_error << langutil::errinfo_sourceLocation(m_currentLocation); | ||||||
|  | 		throw _error; | ||||||
|  | 	} | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| void IRGeneratorForStatements::initializeLocalVar(VariableDeclaration const& _varDecl) | void IRGeneratorForStatements::initializeLocalVar(VariableDeclaration const& _varDecl) | ||||||
| { | { | ||||||
| 	solAssert(m_context.isLocalVariable(_varDecl), "Must be a local variable."); | 	try | ||||||
|  | 	{ | ||||||
|  | 		setLocation(_varDecl); | ||||||
| 
 | 
 | ||||||
| 	auto const* type = _varDecl.type(); | 		solAssert(m_context.isLocalVariable(_varDecl), "Must be a local variable."); | ||||||
| 	if (auto const* refType = dynamic_cast<ReferenceType const*>(type)) |  | ||||||
| 		if (refType->dataStoredIn(DataLocation::Storage) && refType->isPointer()) |  | ||||||
| 			return; |  | ||||||
| 
 | 
 | ||||||
| 	IRVariable zero = zeroValue(*type); | 		auto const* type = _varDecl.type(); | ||||||
| 	assign(m_context.localVariable(_varDecl), zero); | 		if (auto const* refType = dynamic_cast<ReferenceType const*>(type)) | ||||||
|  | 			if (refType->dataStoredIn(DataLocation::Storage) && refType->isPointer()) | ||||||
|  | 				return; | ||||||
|  | 
 | ||||||
|  | 		IRVariable zero = zeroValue(*type); | ||||||
|  | 		assign(m_context.localVariable(_varDecl), zero); | ||||||
|  | 	} | ||||||
|  | 	catch (langutil::UnimplementedFeatureError const& _error) | ||||||
|  | 	{ | ||||||
|  | 		if (!boost::get_error_info<langutil::errinfo_sourceLocation>(_error)) | ||||||
|  | 			_error << langutil::errinfo_sourceLocation(m_currentLocation); | ||||||
|  | 		throw _error; | ||||||
|  | 	} | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| IRVariable IRGeneratorForStatements::evaluateExpression(Expression const& _expression, Type const& _targetType) | IRVariable IRGeneratorForStatements::evaluateExpression(Expression const& _expression, Type const& _targetType) | ||||||
| { | { | ||||||
| 	_expression.accept(*this); | 	try | ||||||
| 	IRVariable variable{m_context.newYulVariable(), _targetType}; | 	{ | ||||||
| 	define(variable, _expression); | 		setLocation(_expression); | ||||||
| 	return variable; | 
 | ||||||
|  | 		_expression.accept(*this); | ||||||
|  | 		IRVariable variable{m_context.newYulVariable(), _targetType}; | ||||||
|  | 		define(variable, _expression); | ||||||
|  | 		return variable; | ||||||
|  | 	} | ||||||
|  | 	catch (langutil::UnimplementedFeatureError const& _error) | ||||||
|  | 	{ | ||||||
|  | 		if (!boost::get_error_info<langutil::errinfo_sourceLocation>(_error)) | ||||||
|  | 			_error << langutil::errinfo_sourceLocation(m_currentLocation); | ||||||
|  | 		throw _error; | ||||||
|  | 	} | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| string IRGeneratorForStatements::constantValueFunction(VariableDeclaration const& _constant) | string IRGeneratorForStatements::constantValueFunction(VariableDeclaration const& _constant) | ||||||
| { | { | ||||||
| 	string functionName = IRNames::constantValueFunction(_constant); | 	try | ||||||
| 	return m_context.functionCollector().createFunction(functionName, [&] { | 	{ | ||||||
| 		Whiskers templ(R"( | 		setLocation(_constant); | ||||||
| 			function <functionName>() -> <ret> { |  | ||||||
| 				<code> |  | ||||||
| 				<ret> := <value> |  | ||||||
| 			} |  | ||||||
| 		)"); |  | ||||||
| 		templ("functionName", functionName); |  | ||||||
| 		IRGeneratorForStatements generator(m_context, m_utils); |  | ||||||
| 		solAssert(_constant.value(), ""); |  | ||||||
| 		Type const& constantType = *_constant.type(); |  | ||||||
| 		templ("value", generator.evaluateExpression(*_constant.value(), constantType).commaSeparatedList()); |  | ||||||
| 		templ("code", generator.code()); |  | ||||||
| 		templ("ret", IRVariable("ret", constantType).commaSeparatedList()); |  | ||||||
| 
 | 
 | ||||||
| 		return templ.render(); | 		string functionName = IRNames::constantValueFunction(_constant); | ||||||
| 	}); | 		return m_context.functionCollector().createFunction(functionName, [&] { | ||||||
|  | 			Whiskers templ(R"( | ||||||
|  | 				function <functionName>() -> <ret> { | ||||||
|  | 					<code> | ||||||
|  | 					<ret> := <value> | ||||||
|  | 				} | ||||||
|  | 			)"); | ||||||
|  | 			templ("functionName", functionName); | ||||||
|  | 			IRGeneratorForStatements generator(m_context, m_utils); | ||||||
|  | 			solAssert(_constant.value(), ""); | ||||||
|  | 			Type const& constantType = *_constant.type(); | ||||||
|  | 			templ("value", generator.evaluateExpression(*_constant.value(), constantType).commaSeparatedList()); | ||||||
|  | 			templ("code", generator.code()); | ||||||
|  | 			templ("ret", IRVariable("ret", constantType).commaSeparatedList()); | ||||||
|  | 
 | ||||||
|  | 			return templ.render(); | ||||||
|  | 		}); | ||||||
|  | 	} | ||||||
|  | 	catch (langutil::UnimplementedFeatureError const& _error) | ||||||
|  | 	{ | ||||||
|  | 		if (!boost::get_error_info<langutil::errinfo_sourceLocation>(_error)) | ||||||
|  | 			_error << langutil::errinfo_sourceLocation(m_currentLocation); | ||||||
|  | 		throw _error; | ||||||
|  | 	} | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| void IRGeneratorForStatements::endVisit(VariableDeclarationStatement const& _varDeclStatement) | void IRGeneratorForStatements::endVisit(VariableDeclarationStatement const& _varDeclStatement) | ||||||
| { | { | ||||||
|  | 	setLocation(_varDeclStatement); | ||||||
|  | 
 | ||||||
| 	if (Expression const* expression = _varDeclStatement.initialValue()) | 	if (Expression const* expression = _varDeclStatement.initialValue()) | ||||||
| 	{ | 	{ | ||||||
| 		if (_varDeclStatement.declarations().size() > 1) | 		if (_varDeclStatement.declarations().size() > 1) | ||||||
| @ -249,14 +311,22 @@ bool IRGeneratorForStatements::visit(Conditional const& _conditional) | |||||||
| { | { | ||||||
| 	_conditional.condition().accept(*this); | 	_conditional.condition().accept(*this); | ||||||
| 
 | 
 | ||||||
|  | 	setLocation(_conditional); | ||||||
|  | 
 | ||||||
| 	string condition = expressionAsType(_conditional.condition(), *TypeProvider::boolean()); | 	string condition = expressionAsType(_conditional.condition(), *TypeProvider::boolean()); | ||||||
| 	declare(_conditional); | 	declare(_conditional); | ||||||
| 
 | 
 | ||||||
| 	m_code << "switch " << condition << "\n" "case 0 {\n"; | 	m_code << "switch " << condition << "\n" "case 0 {\n"; | ||||||
|  | 
 | ||||||
| 	_conditional.falseExpression().accept(*this); | 	_conditional.falseExpression().accept(*this); | ||||||
|  | 	setLocation(_conditional); | ||||||
|  | 
 | ||||||
| 	assign(_conditional, _conditional.falseExpression()); | 	assign(_conditional, _conditional.falseExpression()); | ||||||
| 	m_code << "}\n" "default {\n"; | 	m_code << "}\n" "default {\n"; | ||||||
|  | 
 | ||||||
| 	_conditional.trueExpression().accept(*this); | 	_conditional.trueExpression().accept(*this); | ||||||
|  | 	setLocation(_conditional); | ||||||
|  | 
 | ||||||
| 	assign(_conditional, _conditional.trueExpression()); | 	assign(_conditional, _conditional.trueExpression()); | ||||||
| 	m_code << "}\n"; | 	m_code << "}\n"; | ||||||
| 
 | 
 | ||||||
| @ -266,6 +336,7 @@ bool IRGeneratorForStatements::visit(Conditional const& _conditional) | |||||||
| bool IRGeneratorForStatements::visit(Assignment const& _assignment) | bool IRGeneratorForStatements::visit(Assignment const& _assignment) | ||||||
| { | { | ||||||
| 	_assignment.rightHandSide().accept(*this); | 	_assignment.rightHandSide().accept(*this); | ||||||
|  | 	setLocation(_assignment); | ||||||
| 
 | 
 | ||||||
| 	Token assignmentOperator = _assignment.assignmentOperator(); | 	Token assignmentOperator = _assignment.assignmentOperator(); | ||||||
| 	Token binaryOperator = | 	Token binaryOperator = | ||||||
| @ -283,6 +354,7 @@ bool IRGeneratorForStatements::visit(Assignment const& _assignment) | |||||||
| 	IRVariable value = convert(_assignment.rightHandSide(), *rightIntermediateType); | 	IRVariable value = convert(_assignment.rightHandSide(), *rightIntermediateType); | ||||||
| 	_assignment.leftHandSide().accept(*this); | 	_assignment.leftHandSide().accept(*this); | ||||||
| 	solAssert(!!m_currentLValue, "LValue not retrieved."); | 	solAssert(!!m_currentLValue, "LValue not retrieved."); | ||||||
|  | 	setLocation(_assignment); | ||||||
| 
 | 
 | ||||||
| 	if (assignmentOperator != Token::Assign) | 	if (assignmentOperator != Token::Assign) | ||||||
| 	{ | 	{ | ||||||
| @ -323,6 +395,8 @@ bool IRGeneratorForStatements::visit(Assignment const& _assignment) | |||||||
| 
 | 
 | ||||||
| bool IRGeneratorForStatements::visit(TupleExpression const& _tuple) | bool IRGeneratorForStatements::visit(TupleExpression const& _tuple) | ||||||
| { | { | ||||||
|  | 	setLocation(_tuple); | ||||||
|  | 
 | ||||||
| 	if (_tuple.isInlineArray()) | 	if (_tuple.isInlineArray()) | ||||||
| 	{ | 	{ | ||||||
| 		auto const& arrayType = dynamic_cast<ArrayType const&>(*_tuple.annotation().type); | 		auto const& arrayType = dynamic_cast<ArrayType const&>(*_tuple.annotation().type); | ||||||
| @ -339,6 +413,7 @@ bool IRGeneratorForStatements::visit(TupleExpression const& _tuple) | |||||||
| 		{ | 		{ | ||||||
| 			Expression const& component = *_tuple.components()[i]; | 			Expression const& component = *_tuple.components()[i]; | ||||||
| 			component.accept(*this); | 			component.accept(*this); | ||||||
|  | 			setLocation(_tuple); | ||||||
| 			IRVariable converted = convert(component, baseType); | 			IRVariable converted = convert(component, baseType); | ||||||
| 			m_code << | 			m_code << | ||||||
| 				m_utils.writeToMemoryFunction(baseType) << | 				m_utils.writeToMemoryFunction(baseType) << | ||||||
| @ -358,6 +433,7 @@ bool IRGeneratorForStatements::visit(TupleExpression const& _tuple) | |||||||
| 		{ | 		{ | ||||||
| 			solAssert(_tuple.components().front(), ""); | 			solAssert(_tuple.components().front(), ""); | ||||||
| 			_tuple.components().front()->accept(*this); | 			_tuple.components().front()->accept(*this); | ||||||
|  | 			setLocation(_tuple); | ||||||
| 			if (willBeWrittenTo) | 			if (willBeWrittenTo) | ||||||
| 				solAssert(!!m_currentLValue, ""); | 				solAssert(!!m_currentLValue, ""); | ||||||
| 			else | 			else | ||||||
| @ -370,6 +446,7 @@ bool IRGeneratorForStatements::visit(TupleExpression const& _tuple) | |||||||
| 				if (auto const& component = _tuple.components()[i]) | 				if (auto const& component = _tuple.components()[i]) | ||||||
| 				{ | 				{ | ||||||
| 					component->accept(*this); | 					component->accept(*this); | ||||||
|  | 					setLocation(_tuple); | ||||||
| 					if (willBeWrittenTo) | 					if (willBeWrittenTo) | ||||||
| 					{ | 					{ | ||||||
| 						solAssert(!!m_currentLValue, ""); | 						solAssert(!!m_currentLValue, ""); | ||||||
| @ -395,17 +472,20 @@ bool IRGeneratorForStatements::visit(TupleExpression const& _tuple) | |||||||
| bool IRGeneratorForStatements::visit(IfStatement const& _ifStatement) | bool IRGeneratorForStatements::visit(IfStatement const& _ifStatement) | ||||||
| { | { | ||||||
| 	_ifStatement.condition().accept(*this); | 	_ifStatement.condition().accept(*this); | ||||||
|  | 	setLocation(_ifStatement); | ||||||
| 	string condition = expressionAsType(_ifStatement.condition(), *TypeProvider::boolean()); | 	string condition = expressionAsType(_ifStatement.condition(), *TypeProvider::boolean()); | ||||||
| 
 | 
 | ||||||
| 	if (_ifStatement.falseStatement()) | 	if (_ifStatement.falseStatement()) | ||||||
| 	{ | 	{ | ||||||
| 		m_code << "switch " << condition << "\n" "case 0 {\n"; | 		m_code << "switch " << condition << "\n" "case 0 {\n"; | ||||||
| 		_ifStatement.falseStatement()->accept(*this); | 		_ifStatement.falseStatement()->accept(*this); | ||||||
|  | 		setLocation(_ifStatement); | ||||||
| 		m_code << "}\n" "default {\n"; | 		m_code << "}\n" "default {\n"; | ||||||
| 	} | 	} | ||||||
| 	else | 	else | ||||||
| 		m_code << "if " << condition << " {\n"; | 		m_code << "if " << condition << " {\n"; | ||||||
| 	_ifStatement.trueStatement().accept(*this); | 	_ifStatement.trueStatement().accept(*this); | ||||||
|  | 	setLocation(_ifStatement); | ||||||
| 	m_code << "}\n"; | 	m_code << "}\n"; | ||||||
| 
 | 
 | ||||||
| 	return false; | 	return false; | ||||||
| @ -413,6 +493,7 @@ bool IRGeneratorForStatements::visit(IfStatement const& _ifStatement) | |||||||
| 
 | 
 | ||||||
| bool IRGeneratorForStatements::visit(ForStatement const& _forStatement) | bool IRGeneratorForStatements::visit(ForStatement const& _forStatement) | ||||||
| { | { | ||||||
|  | 	setLocation(_forStatement); | ||||||
| 	generateLoop( | 	generateLoop( | ||||||
| 		_forStatement.body(), | 		_forStatement.body(), | ||||||
| 		_forStatement.condition(), | 		_forStatement.condition(), | ||||||
| @ -425,6 +506,7 @@ bool IRGeneratorForStatements::visit(ForStatement const& _forStatement) | |||||||
| 
 | 
 | ||||||
| bool IRGeneratorForStatements::visit(WhileStatement const& _whileStatement) | bool IRGeneratorForStatements::visit(WhileStatement const& _whileStatement) | ||||||
| { | { | ||||||
|  | 	setLocation(_whileStatement); | ||||||
| 	generateLoop( | 	generateLoop( | ||||||
| 		_whileStatement.body(), | 		_whileStatement.body(), | ||||||
| 		&_whileStatement.condition(), | 		&_whileStatement.condition(), | ||||||
| @ -436,20 +518,23 @@ bool IRGeneratorForStatements::visit(WhileStatement const& _whileStatement) | |||||||
| 	return false; | 	return false; | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| bool IRGeneratorForStatements::visit(Continue const&) | bool IRGeneratorForStatements::visit(Continue const& _continue) | ||||||
| { | { | ||||||
|  | 	setLocation(_continue); | ||||||
| 	m_code << "continue\n"; | 	m_code << "continue\n"; | ||||||
| 	return false; | 	return false; | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| bool IRGeneratorForStatements::visit(Break const&) | bool IRGeneratorForStatements::visit(Break const& _break) | ||||||
| { | { | ||||||
|  | 	setLocation(_break); | ||||||
| 	m_code << "break\n"; | 	m_code << "break\n"; | ||||||
| 	return false; | 	return false; | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| void IRGeneratorForStatements::endVisit(Return const& _return) | void IRGeneratorForStatements::endVisit(Return const& _return) | ||||||
| { | { | ||||||
|  | 	setLocation(_return); | ||||||
| 	if (Expression const* value = _return.expression()) | 	if (Expression const* value = _return.expression()) | ||||||
| 	{ | 	{ | ||||||
| 		solAssert(_return.annotation().functionReturnParameters, "Invalid return parameters pointer."); | 		solAssert(_return.annotation().functionReturnParameters, "Invalid return parameters pointer."); | ||||||
| @ -466,6 +551,7 @@ void IRGeneratorForStatements::endVisit(Return const& _return) | |||||||
| 
 | 
 | ||||||
| void IRGeneratorForStatements::endVisit(UnaryOperation const& _unaryOperation) | void IRGeneratorForStatements::endVisit(UnaryOperation const& _unaryOperation) | ||||||
| { | { | ||||||
|  | 	setLocation(_unaryOperation); | ||||||
| 	Type const& resultType = type(_unaryOperation); | 	Type const& resultType = type(_unaryOperation); | ||||||
| 	Token const op = _unaryOperation.getOperator(); | 	Token const op = _unaryOperation.getOperator(); | ||||||
| 
 | 
 | ||||||
| @ -551,6 +637,8 @@ void IRGeneratorForStatements::endVisit(UnaryOperation const& _unaryOperation) | |||||||
| 
 | 
 | ||||||
| bool IRGeneratorForStatements::visit(BinaryOperation const& _binOp) | bool IRGeneratorForStatements::visit(BinaryOperation const& _binOp) | ||||||
| { | { | ||||||
|  | 	setLocation(_binOp); | ||||||
|  | 
 | ||||||
| 	solAssert(!!_binOp.annotation().commonType, ""); | 	solAssert(!!_binOp.annotation().commonType, ""); | ||||||
| 	TypePointer commonType = _binOp.annotation().commonType; | 	TypePointer commonType = _binOp.annotation().commonType; | ||||||
| 	langutil::Token op = _binOp.getOperator(); | 	langutil::Token op = _binOp.getOperator(); | ||||||
| @ -570,6 +658,7 @@ bool IRGeneratorForStatements::visit(BinaryOperation const& _binOp) | |||||||
| 
 | 
 | ||||||
| 	_binOp.leftExpression().accept(*this); | 	_binOp.leftExpression().accept(*this); | ||||||
| 	_binOp.rightExpression().accept(*this); | 	_binOp.rightExpression().accept(*this); | ||||||
|  | 	setLocation(_binOp); | ||||||
| 
 | 
 | ||||||
| 	if (TokenTraits::isCompareOp(op)) | 	if (TokenTraits::isCompareOp(op)) | ||||||
| 	{ | 	{ | ||||||
| @ -629,6 +718,7 @@ bool IRGeneratorForStatements::visit(BinaryOperation const& _binOp) | |||||||
| 
 | 
 | ||||||
| bool IRGeneratorForStatements::visit(FunctionCall const& _functionCall) | bool IRGeneratorForStatements::visit(FunctionCall const& _functionCall) | ||||||
| { | { | ||||||
|  | 	setLocation(_functionCall); | ||||||
| 	FunctionTypePointer functionType = dynamic_cast<FunctionType const*>(&type(_functionCall.expression())); | 	FunctionTypePointer functionType = dynamic_cast<FunctionType const*>(&type(_functionCall.expression())); | ||||||
| 	if ( | 	if ( | ||||||
| 		functionType && | 		functionType && | ||||||
| @ -643,6 +733,7 @@ bool IRGeneratorForStatements::visit(FunctionCall const& _functionCall) | |||||||
| 
 | 
 | ||||||
| void IRGeneratorForStatements::endVisit(FunctionCall const& _functionCall) | void IRGeneratorForStatements::endVisit(FunctionCall const& _functionCall) | ||||||
| { | { | ||||||
|  | 	setLocation(_functionCall); | ||||||
| 	auto functionCallKind = *_functionCall.annotation().kind; | 	auto functionCallKind = *_functionCall.annotation().kind; | ||||||
| 
 | 
 | ||||||
| 	if (functionCallKind == FunctionCallKind::TypeConversion) | 	if (functionCallKind == FunctionCallKind::TypeConversion) | ||||||
| @ -1360,6 +1451,7 @@ void IRGeneratorForStatements::endVisit(FunctionCall const& _functionCall) | |||||||
| 
 | 
 | ||||||
| void IRGeneratorForStatements::endVisit(FunctionCallOptions const& _options) | void IRGeneratorForStatements::endVisit(FunctionCallOptions const& _options) | ||||||
| { | { | ||||||
|  | 	setLocation(_options); | ||||||
| 	FunctionType const& previousType = dynamic_cast<FunctionType const&>(*_options.expression().annotation().type); | 	FunctionType const& previousType = dynamic_cast<FunctionType const&>(*_options.expression().annotation().type); | ||||||
| 
 | 
 | ||||||
| 	solUnimplementedAssert(!previousType.bound(), ""); | 	solUnimplementedAssert(!previousType.bound(), ""); | ||||||
| @ -1379,6 +1471,7 @@ void IRGeneratorForStatements::endVisit(FunctionCallOptions const& _options) | |||||||
| 
 | 
 | ||||||
| void IRGeneratorForStatements::endVisit(MemberAccess const& _memberAccess) | void IRGeneratorForStatements::endVisit(MemberAccess const& _memberAccess) | ||||||
| { | { | ||||||
|  | 	setLocation(_memberAccess); | ||||||
| 	ASTString const& member = _memberAccess.memberName(); | 	ASTString const& member = _memberAccess.memberName(); | ||||||
| 	auto memberFunctionType = dynamic_cast<FunctionType const*>(_memberAccess.annotation().type); | 	auto memberFunctionType = dynamic_cast<FunctionType const*>(_memberAccess.annotation().type); | ||||||
| 	Type::Category objectCategory = _memberAccess.expression().annotation().type->category(); | 	Type::Category objectCategory = _memberAccess.expression().annotation().type->category(); | ||||||
| @ -1764,6 +1857,7 @@ void IRGeneratorForStatements::endVisit(MemberAccess const& _memberAccess) | |||||||
| 
 | 
 | ||||||
| bool IRGeneratorForStatements::visit(InlineAssembly const& _inlineAsm) | bool IRGeneratorForStatements::visit(InlineAssembly const& _inlineAsm) | ||||||
| { | { | ||||||
|  | 	setLocation(_inlineAsm); | ||||||
| 	CopyTranslate bodyCopier{_inlineAsm.dialect(), m_context, _inlineAsm.annotation().externalReferences}; | 	CopyTranslate bodyCopier{_inlineAsm.dialect(), m_context, _inlineAsm.annotation().externalReferences}; | ||||||
| 
 | 
 | ||||||
| 	yul::Statement modified = bodyCopier(_inlineAsm.operations()); | 	yul::Statement modified = bodyCopier(_inlineAsm.operations()); | ||||||
| @ -1778,6 +1872,7 @@ bool IRGeneratorForStatements::visit(InlineAssembly const& _inlineAsm) | |||||||
| 
 | 
 | ||||||
| void IRGeneratorForStatements::endVisit(IndexAccess const& _indexAccess) | void IRGeneratorForStatements::endVisit(IndexAccess const& _indexAccess) | ||||||
| { | { | ||||||
|  | 	setLocation(_indexAccess); | ||||||
| 	Type const& baseType = *_indexAccess.baseExpression().annotation().type; | 	Type const& baseType = *_indexAccess.baseExpression().annotation().type; | ||||||
| 
 | 
 | ||||||
| 	if (baseType.category() == Type::Category::Mapping) | 	if (baseType.category() == Type::Category::Mapping) | ||||||
| @ -1913,6 +2008,7 @@ void IRGeneratorForStatements::endVisit(IndexAccess const& _indexAccess) | |||||||
| 
 | 
 | ||||||
| void IRGeneratorForStatements::endVisit(IndexRangeAccess const& _indexRangeAccess) | void IRGeneratorForStatements::endVisit(IndexRangeAccess const& _indexRangeAccess) | ||||||
| { | { | ||||||
|  | 	setLocation(_indexRangeAccess); | ||||||
| 	Type const& baseType = *_indexRangeAccess.baseExpression().annotation().type; | 	Type const& baseType = *_indexRangeAccess.baseExpression().annotation().type; | ||||||
| 	solAssert( | 	solAssert( | ||||||
| 		baseType.category() == Type::Category::Array || baseType.category() == Type::Category::ArraySlice, | 		baseType.category() == Type::Category::Array || baseType.category() == Type::Category::ArraySlice, | ||||||
| @ -1959,6 +2055,7 @@ void IRGeneratorForStatements::endVisit(IndexRangeAccess const& _indexRangeAcces | |||||||
| 
 | 
 | ||||||
| void IRGeneratorForStatements::endVisit(Identifier const& _identifier) | void IRGeneratorForStatements::endVisit(Identifier const& _identifier) | ||||||
| { | { | ||||||
|  | 	setLocation(_identifier); | ||||||
| 	Declaration const* declaration = _identifier.annotation().referencedDeclaration; | 	Declaration const* declaration = _identifier.annotation().referencedDeclaration; | ||||||
| 	if (MagicVariableDeclaration const* magicVar = dynamic_cast<MagicVariableDeclaration const*>(declaration)) | 	if (MagicVariableDeclaration const* magicVar = dynamic_cast<MagicVariableDeclaration const*>(declaration)) | ||||||
| 	{ | 	{ | ||||||
| @ -2017,6 +2114,7 @@ void IRGeneratorForStatements::endVisit(Identifier const& _identifier) | |||||||
| 
 | 
 | ||||||
| bool IRGeneratorForStatements::visit(Literal const& _literal) | bool IRGeneratorForStatements::visit(Literal const& _literal) | ||||||
| { | { | ||||||
|  | 	setLocation(_literal); | ||||||
| 	Type const& literalType = type(_literal); | 	Type const& literalType = type(_literal); | ||||||
| 
 | 
 | ||||||
| 	switch (literalType.category()) | 	switch (literalType.category()) | ||||||
| @ -2039,6 +2137,7 @@ void IRGeneratorForStatements::handleVariableReference( | |||||||
| 	Expression const& _referencingExpression | 	Expression const& _referencingExpression | ||||||
| ) | ) | ||||||
| { | { | ||||||
|  | 	setLocation(_referencingExpression); | ||||||
| 	if (_variable.isStateVariable() && _variable.isConstant()) | 	if (_variable.isStateVariable() && _variable.isConstant()) | ||||||
| 		define(_referencingExpression) << constantValueFunction(_variable) << "()\n"; | 		define(_referencingExpression) << constantValueFunction(_variable) << "()\n"; | ||||||
| 	else if (_variable.isStateVariable() && _variable.immutable()) | 	else if (_variable.isStateVariable() && _variable.immutable()) | ||||||
| @ -2480,6 +2579,7 @@ void IRGeneratorForStatements::appendAndOrOperatorCode(BinaryOperation const& _b | |||||||
| 	solAssert(op == Token::Or || op == Token::And, ""); | 	solAssert(op == Token::Or || op == Token::And, ""); | ||||||
| 
 | 
 | ||||||
| 	_binOp.leftExpression().accept(*this); | 	_binOp.leftExpression().accept(*this); | ||||||
|  | 	setLocation(_binOp); | ||||||
| 
 | 
 | ||||||
| 	IRVariable value(_binOp); | 	IRVariable value(_binOp); | ||||||
| 	define(value, _binOp.leftExpression()); | 	define(value, _binOp.leftExpression()); | ||||||
| @ -2488,6 +2588,7 @@ void IRGeneratorForStatements::appendAndOrOperatorCode(BinaryOperation const& _b | |||||||
| 	else | 	else | ||||||
| 		m_code << "if " << value.name() << " {\n"; | 		m_code << "if " << value.name() << " {\n"; | ||||||
| 	_binOp.rightExpression().accept(*this); | 	_binOp.rightExpression().accept(*this); | ||||||
|  | 	setLocation(_binOp); | ||||||
| 	assign(value, _binOp.rightExpression()); | 	assign(value, _binOp.rightExpression()); | ||||||
| 	m_code << "}\n"; | 	m_code << "}\n"; | ||||||
| } | } | ||||||
| @ -2687,6 +2788,7 @@ bool IRGeneratorForStatements::visit(TryStatement const& _tryStatement) | |||||||
| { | { | ||||||
| 	Expression const& externalCall = _tryStatement.externalCall(); | 	Expression const& externalCall = _tryStatement.externalCall(); | ||||||
| 	externalCall.accept(*this); | 	externalCall.accept(*this); | ||||||
|  | 	setLocation(_tryStatement); | ||||||
| 
 | 
 | ||||||
| 	m_code << "switch iszero(" << IRNames::trySuccessConditionVariable(externalCall) << ")\n"; | 	m_code << "switch iszero(" << IRNames::trySuccessConditionVariable(externalCall) << ")\n"; | ||||||
| 
 | 
 | ||||||
| @ -2707,6 +2809,7 @@ bool IRGeneratorForStatements::visit(TryStatement const& _tryStatement) | |||||||
| 	} | 	} | ||||||
| 
 | 
 | ||||||
| 	successClause.block().accept(*this); | 	successClause.block().accept(*this); | ||||||
|  | 	setLocation(_tryStatement); | ||||||
| 	m_code << "}\n"; | 	m_code << "}\n"; | ||||||
| 
 | 
 | ||||||
| 	m_code << "default { // failure case\n"; | 	m_code << "default { // failure case\n"; | ||||||
| @ -2797,3 +2900,8 @@ bool IRGeneratorForStatements::visit(TryCatchClause const& _clause) | |||||||
| 	_clause.block().accept(*this); | 	_clause.block().accept(*this); | ||||||
| 	return false; | 	return false; | ||||||
| } | } | ||||||
|  | 
 | ||||||
|  | void IRGeneratorForStatements::setLocation(ASTNode const& _node) | ||||||
|  | { | ||||||
|  | 	m_currentLocation = _node.location(); | ||||||
|  | } | ||||||
|  | |||||||
| @ -47,6 +47,9 @@ public: | |||||||
| 
 | 
 | ||||||
| 	std::string code() const; | 	std::string code() const; | ||||||
| 
 | 
 | ||||||
|  | 	/// Generate the code for the statements in the block;
 | ||||||
|  | 	void generate(Block const& _block); | ||||||
|  | 
 | ||||||
| 	/// Generates code to initialize the given state variable.
 | 	/// Generates code to initialize the given state variable.
 | ||||||
| 	void initializeStateVar(VariableDeclaration const& _varDecl); | 	void initializeStateVar(VariableDeclaration const& _varDecl); | ||||||
| 	/// Generates code to initialize the given local variable.
 | 	/// Generates code to initialize the given local variable.
 | ||||||
| @ -179,10 +182,13 @@ private: | |||||||
| 
 | 
 | ||||||
| 	static Type const& type(Expression const& _expression); | 	static Type const& type(Expression const& _expression); | ||||||
| 
 | 
 | ||||||
|  | 	void setLocation(ASTNode const& _node); | ||||||
|  | 
 | ||||||
| 	std::ostringstream m_code; | 	std::ostringstream m_code; | ||||||
| 	IRGenerationContext& m_context; | 	IRGenerationContext& m_context; | ||||||
| 	YulUtilFunctions& m_utils; | 	YulUtilFunctions& m_utils; | ||||||
| 	std::optional<IRLValue> m_currentLValue; | 	std::optional<IRLValue> m_currentLValue; | ||||||
|  | 	langutil::SourceLocation m_currentLocation; | ||||||
| }; | }; | ||||||
| 
 | 
 | ||||||
| } | } | ||||||
|  | |||||||
| @ -22,6 +22,7 @@ | |||||||
| #include <libsmtutil/Z3CHCInterface.h> | #include <libsmtutil/Z3CHCInterface.h> | ||||||
| #endif | #endif | ||||||
| 
 | 
 | ||||||
|  | #include <libsolidity/formal/PredicateSort.h> | ||||||
| #include <libsolidity/formal/SymbolicTypes.h> | #include <libsolidity/formal/SymbolicTypes.h> | ||||||
| 
 | 
 | ||||||
| #include <libsolidity/ast/TypeProvider.h> | #include <libsolidity/ast/TypeProvider.h> | ||||||
| @ -39,13 +40,14 @@ using namespace solidity::util; | |||||||
| using namespace solidity::langutil; | using namespace solidity::langutil; | ||||||
| using namespace solidity::smtutil; | using namespace solidity::smtutil; | ||||||
| using namespace solidity::frontend; | using namespace solidity::frontend; | ||||||
|  | using namespace solidity::frontend::smt; | ||||||
| 
 | 
 | ||||||
| CHC::CHC( | CHC::CHC( | ||||||
| 	smt::EncodingContext& _context, | 	EncodingContext& _context, | ||||||
| 	ErrorReporter& _errorReporter, | 	ErrorReporter& _errorReporter, | ||||||
| 	[[maybe_unused]] map<util::h256, string> const& _smtlib2Responses, | 	[[maybe_unused]] map<util::h256, string> const& _smtlib2Responses, | ||||||
| 	[[maybe_unused]] ReadCallback::Callback const& _smtCallback, | 	[[maybe_unused]] ReadCallback::Callback const& _smtCallback, | ||||||
| 	smtutil::SMTSolverChoice _enabledSolvers | 	SMTSolverChoice _enabledSolvers | ||||||
| ): | ): | ||||||
| 	SMTEncoder(_context), | 	SMTEncoder(_context), | ||||||
| 	m_outerErrorReporter(_errorReporter), | 	m_outerErrorReporter(_errorReporter), | ||||||
| @ -56,7 +58,7 @@ CHC::CHC( | |||||||
| 	usesZ3 = false; | 	usesZ3 = false; | ||||||
| #endif | #endif | ||||||
| 	if (!usesZ3) | 	if (!usesZ3) | ||||||
| 		m_interface = make_unique<smtutil::CHCSmtLib2Interface>(_smtlib2Responses, _smtCallback); | 		m_interface = make_unique<CHCSmtLib2Interface>(_smtlib2Responses, _smtCallback); | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| void CHC::analyze(SourceUnit const& _source) | void CHC::analyze(SourceUnit const& _source) | ||||||
| @ -79,7 +81,7 @@ void CHC::analyze(SourceUnit const& _source) | |||||||
| 
 | 
 | ||||||
| vector<string> CHC::unhandledQueries() const | vector<string> CHC::unhandledQueries() const | ||||||
| { | { | ||||||
| 	if (auto smtlib2 = dynamic_cast<smtutil::CHCSmtLib2Interface const*>(m_interface.get())) | 	if (auto smtlib2 = dynamic_cast<CHCSmtLib2Interface const*>(m_interface.get())) | ||||||
| 		return smtlib2->unhandledQueries(); | 		return smtlib2->unhandledQueries(); | ||||||
| 
 | 
 | ||||||
| 	return {}; | 	return {}; | ||||||
| @ -92,13 +94,15 @@ bool CHC::visit(ContractDefinition const& _contract) | |||||||
| 	initContract(_contract); | 	initContract(_contract); | ||||||
| 
 | 
 | ||||||
| 	m_stateVariables = SMTEncoder::stateVariablesIncludingInheritedAndPrivate(_contract); | 	m_stateVariables = SMTEncoder::stateVariablesIncludingInheritedAndPrivate(_contract); | ||||||
| 	m_stateSorts = stateSorts(_contract); |  | ||||||
| 
 | 
 | ||||||
| 	clearIndices(&_contract); | 	clearIndices(&_contract); | ||||||
| 
 | 
 | ||||||
| 	string suffix = _contract.name() + "_" + to_string(_contract.id()); | 	solAssert(m_currentContract, ""); | ||||||
| 	m_constructorSummaryPredicate = createSymbolicBlock(constructorSort(), "summary_constructor_" + suffix, &_contract); | 	m_constructorSummaryPredicate = createSymbolicBlock( | ||||||
| 	m_implicitConstructorPredicate = createSymbolicBlock(arity0FunctionSort(), "implicit_constructor_" + suffix, &_contract); | 		constructorSort(*m_currentContract), | ||||||
|  | 		"summary_constructor_" + contractSuffix(_contract), | ||||||
|  | 		&_contract | ||||||
|  | 	); | ||||||
| 	auto stateExprs = currentStateVariables(); | 	auto stateExprs = currentStateVariables(); | ||||||
| 	setCurrentBlock(*m_interfaces.at(m_currentContract), &stateExprs); | 	setCurrentBlock(*m_interfaces.at(m_currentContract), &stateExprs); | ||||||
| 
 | 
 | ||||||
| @ -108,7 +112,12 @@ bool CHC::visit(ContractDefinition const& _contract) | |||||||
| 
 | 
 | ||||||
| void CHC::endVisit(ContractDefinition const& _contract) | void CHC::endVisit(ContractDefinition const& _contract) | ||||||
| { | { | ||||||
| 	auto implicitConstructor = (*m_implicitConstructorPredicate)({}); | 	auto implicitConstructorPredicate = createSymbolicBlock( | ||||||
|  | 		implicitConstructorSort(), | ||||||
|  | 		"implicit_constructor_" + contractSuffix(_contract), | ||||||
|  | 		&_contract | ||||||
|  | 	); | ||||||
|  | 	auto implicitConstructor = (*implicitConstructorPredicate)({}); | ||||||
| 	addRule(implicitConstructor, implicitConstructor.name); | 	addRule(implicitConstructor, implicitConstructor.name); | ||||||
| 	m_currentBlock = implicitConstructor; | 	m_currentBlock = implicitConstructor; | ||||||
| 	m_context.addAssertion(m_error.currentValue() == 0); | 	m_context.addAssertion(m_error.currentValue() == 0); | ||||||
| @ -204,7 +213,12 @@ void CHC::endVisit(FunctionDefinition const& _function) | |||||||
| 		if (_function.isConstructor()) | 		if (_function.isConstructor()) | ||||||
| 		{ | 		{ | ||||||
| 			string suffix = m_currentContract->name() + "_" + to_string(m_currentContract->id()); | 			string suffix = m_currentContract->name() + "_" + to_string(m_currentContract->id()); | ||||||
| 			auto constructorExit = createSymbolicBlock(constructorSort(), "constructor_exit_" + suffix, m_currentContract); | 			solAssert(m_currentContract, ""); | ||||||
|  | 			auto constructorExit = createSymbolicBlock( | ||||||
|  | 				constructorSort(*m_currentContract), | ||||||
|  | 				"constructor_exit_" + suffix, | ||||||
|  | 				m_currentContract | ||||||
|  | 			); | ||||||
| 			connectBlocks(m_currentBlock, predicate(*constructorExit, currentFunctionVariables(*m_currentContract))); | 			connectBlocks(m_currentBlock, predicate(*constructorExit, currentFunctionVariables(*m_currentContract))); | ||||||
| 
 | 
 | ||||||
| 			clearIndices(m_currentContract, m_currentFunction); | 			clearIndices(m_currentContract, m_currentFunction); | ||||||
| @ -566,7 +580,7 @@ void CHC::makeArrayPopVerificationTarget(FunctionCall const& _arrayPop) | |||||||
| 
 | 
 | ||||||
| 	auto memberAccess = dynamic_cast<MemberAccess const*>(&_arrayPop.expression()); | 	auto memberAccess = dynamic_cast<MemberAccess const*>(&_arrayPop.expression()); | ||||||
| 	solAssert(memberAccess, ""); | 	solAssert(memberAccess, ""); | ||||||
| 	auto symbArray = dynamic_pointer_cast<smt::SymbolicArrayVariable>(m_context.expression(memberAccess->expression())); | 	auto symbArray = dynamic_pointer_cast<SymbolicArrayVariable>(m_context.expression(memberAccess->expression())); | ||||||
| 	solAssert(symbArray, ""); | 	solAssert(symbArray, ""); | ||||||
| 
 | 
 | ||||||
| 	auto previousError = m_error.currentValue(); | 	auto previousError = m_error.currentValue(); | ||||||
| @ -662,15 +676,15 @@ void CHC::resetSourceAnalysis() | |||||||
| 	if (usesZ3) | 	if (usesZ3) | ||||||
| 	{ | 	{ | ||||||
| 		/// z3::fixedpoint does not have a reset mechanism, so we need to create another.
 | 		/// z3::fixedpoint does not have a reset mechanism, so we need to create another.
 | ||||||
| 		m_interface.reset(new smtutil::Z3CHCInterface()); | 		m_interface.reset(new Z3CHCInterface()); | ||||||
| 		auto z3Interface = dynamic_cast<smtutil::Z3CHCInterface const*>(m_interface.get()); | 		auto z3Interface = dynamic_cast<Z3CHCInterface const*>(m_interface.get()); | ||||||
| 		solAssert(z3Interface, ""); | 		solAssert(z3Interface, ""); | ||||||
| 		m_context.setSolver(z3Interface->z3Interface()); | 		m_context.setSolver(z3Interface->z3Interface()); | ||||||
| 	} | 	} | ||||||
| #endif | #endif | ||||||
| 	if (!usesZ3) | 	if (!usesZ3) | ||||||
| 	{ | 	{ | ||||||
| 		auto smtlib2Interface = dynamic_cast<smtutil::CHCSmtLib2Interface*>(m_interface.get()); | 		auto smtlib2Interface = dynamic_cast<CHCSmtLib2Interface*>(m_interface.get()); | ||||||
| 		smtlib2Interface->reset(); | 		smtlib2Interface->reset(); | ||||||
| 		solAssert(smtlib2Interface, ""); | 		solAssert(smtlib2Interface, ""); | ||||||
| 		m_context.setSolver(smtlib2Interface->smtlib2Interface()); | 		m_context.setSolver(smtlib2Interface->smtlib2Interface()); | ||||||
| @ -682,7 +696,6 @@ void CHC::resetSourceAnalysis() | |||||||
| 
 | 
 | ||||||
| void CHC::resetContractAnalysis() | void CHC::resetContractAnalysis() | ||||||
| { | { | ||||||
| 	m_stateSorts.clear(); |  | ||||||
| 	m_stateVariables.clear(); | 	m_stateVariables.clear(); | ||||||
| 	m_unknownFunctionCallSeen = false; | 	m_unknownFunctionCallSeen = false; | ||||||
| 	m_breakDest = nullptr; | 	m_breakDest = nullptr; | ||||||
| @ -739,117 +752,18 @@ set<frontend::Expression const*, CHC::IdCompare> CHC::transactionAssertions(ASTN | |||||||
| 	return assertions; | 	return assertions; | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| vector<smtutil::SortPointer> CHC::stateSorts(ContractDefinition const& _contract) | SortPointer CHC::sort(FunctionDefinition const& _function) | ||||||
| { | { | ||||||
| 	return applyMap( | 	return functionSort(_function, m_currentContract); | ||||||
| 		SMTEncoder::stateVariablesIncludingInheritedAndPrivate(_contract), |  | ||||||
| 		[](auto _var) { return smt::smtSortAbstractFunction(*_var->type()); } |  | ||||||
| 	); |  | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| smtutil::SortPointer CHC::constructorSort() | SortPointer CHC::sort(ASTNode const* _node) | ||||||
| { |  | ||||||
| 	solAssert(m_currentContract, ""); |  | ||||||
| 	if (auto const* constructor = m_currentContract->constructor()) |  | ||||||
| 		return sort(*constructor); |  | ||||||
| 
 |  | ||||||
| 	return make_shared<smtutil::FunctionSort>( |  | ||||||
| 		vector<smtutil::SortPointer>{smtutil::SortProvider::uintSort} + m_stateSorts, |  | ||||||
| 		smtutil::SortProvider::boolSort |  | ||||||
| 	); |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| smtutil::SortPointer CHC::interfaceSort() |  | ||||||
| { |  | ||||||
| 	solAssert(m_currentContract, ""); |  | ||||||
| 	return interfaceSort(*m_currentContract); |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| smtutil::SortPointer CHC::nondetInterfaceSort() |  | ||||||
| { |  | ||||||
| 	solAssert(m_currentContract, ""); |  | ||||||
| 	return nondetInterfaceSort(*m_currentContract); |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| smtutil::SortPointer CHC::interfaceSort(ContractDefinition const& _contract) |  | ||||||
| { |  | ||||||
| 	return make_shared<smtutil::FunctionSort>( |  | ||||||
| 		stateSorts(_contract), |  | ||||||
| 		smtutil::SortProvider::boolSort |  | ||||||
| 	); |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| smtutil::SortPointer CHC::nondetInterfaceSort(ContractDefinition const& _contract) |  | ||||||
| { |  | ||||||
| 	auto sorts = stateSorts(_contract); |  | ||||||
| 	return make_shared<smtutil::FunctionSort>( |  | ||||||
| 		sorts + sorts, |  | ||||||
| 		smtutil::SortProvider::boolSort |  | ||||||
| 	); |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| smtutil::SortPointer CHC::arity0FunctionSort() const |  | ||||||
| { |  | ||||||
| 	return make_shared<smtutil::FunctionSort>( |  | ||||||
| 		vector<smtutil::SortPointer>(), |  | ||||||
| 		smtutil::SortProvider::boolSort |  | ||||||
| 	); |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| /// A function in the symbolic CFG requires:
 |  | ||||||
| /// - Index of failed assertion. 0 means no assertion failed.
 |  | ||||||
| /// - 2 sets of state variables:
 |  | ||||||
| ///   - State variables at the beginning of the current function, immutable
 |  | ||||||
| ///   - Current state variables
 |  | ||||||
| ///    At the beginning of the function these must equal set 1
 |  | ||||||
| /// - 2 sets of input variables:
 |  | ||||||
| ///   - Input variables at the beginning of the current function, immutable
 |  | ||||||
| ///   - Current input variables
 |  | ||||||
| ///    At the beginning of the function these must equal set 1
 |  | ||||||
| /// - 1 set of output variables
 |  | ||||||
| smtutil::SortPointer CHC::sort(FunctionDefinition const& _function) |  | ||||||
| { |  | ||||||
| 	auto smtSort = [](auto _var) { return smt::smtSortAbstractFunction(*_var->type()); }; |  | ||||||
| 	auto inputSorts = applyMap(_function.parameters(), smtSort); |  | ||||||
| 	auto outputSorts = applyMap(_function.returnParameters(), smtSort); |  | ||||||
| 	return make_shared<smtutil::FunctionSort>( |  | ||||||
| 		vector<smtutil::SortPointer>{smtutil::SortProvider::uintSort} + m_stateSorts + inputSorts + m_stateSorts + inputSorts + outputSorts, |  | ||||||
| 		smtutil::SortProvider::boolSort |  | ||||||
| 	); |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| smtutil::SortPointer CHC::sort(ASTNode const* _node) |  | ||||||
| { | { | ||||||
| 	if (auto funDef = dynamic_cast<FunctionDefinition const*>(_node)) | 	if (auto funDef = dynamic_cast<FunctionDefinition const*>(_node)) | ||||||
| 		return sort(*funDef); | 		return sort(*funDef); | ||||||
| 
 | 
 | ||||||
| 	auto fSort = dynamic_pointer_cast<smtutil::FunctionSort>(sort(*m_currentFunction)); | 	solAssert(m_currentFunction, ""); | ||||||
| 	solAssert(fSort, ""); | 	return functionBodySort(*m_currentFunction, m_currentContract); | ||||||
| 
 |  | ||||||
| 	auto smtSort = [](auto _var) { return smt::smtSortAbstractFunction(*_var->type()); }; |  | ||||||
| 	return make_shared<smtutil::FunctionSort>( |  | ||||||
| 		fSort->domain + applyMap(m_currentFunction->localVariables(), smtSort), |  | ||||||
| 		smtutil::SortProvider::boolSort |  | ||||||
| 	); |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| smtutil::SortPointer CHC::summarySort(FunctionDefinition const& _function, ContractDefinition const& _contract) |  | ||||||
| { |  | ||||||
| 	auto stateVariables = SMTEncoder::stateVariablesIncludingInheritedAndPrivate(_contract); |  | ||||||
| 	auto sorts = stateSorts(_contract); |  | ||||||
| 
 |  | ||||||
| 	auto smtSort = [](auto _var) { return smt::smtSortAbstractFunction(*_var->type()); }; |  | ||||||
| 	auto inputSorts = applyMap(_function.parameters(), smtSort); |  | ||||||
| 	auto outputSorts = applyMap(_function.returnParameters(), smtSort); |  | ||||||
| 	return make_shared<smtutil::FunctionSort>( |  | ||||||
| 		vector<smtutil::SortPointer>{smtutil::SortProvider::uintSort} + |  | ||||||
| 			sorts + |  | ||||||
| 			inputSorts + |  | ||||||
| 			sorts + |  | ||||||
| 			inputSorts + |  | ||||||
| 			outputSorts, |  | ||||||
| 		smtutil::SortProvider::boolSort |  | ||||||
| 	); |  | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| Predicate const* CHC::createSymbolicBlock(SortPointer _sort, string const& _name, ASTNode const* _node) | Predicate const* CHC::createSymbolicBlock(SortPointer _sort, string const& _name, ASTNode const* _node) | ||||||
| @ -987,7 +901,7 @@ Predicate const* CHC::createBlock(ASTNode const* _node, string const& _prefix) | |||||||
| Predicate const* CHC::createSummaryBlock(FunctionDefinition const& _function, ContractDefinition const& _contract) | Predicate const* CHC::createSummaryBlock(FunctionDefinition const& _function, ContractDefinition const& _contract) | ||||||
| { | { | ||||||
| 	auto block = createSymbolicBlock( | 	auto block = createSymbolicBlock( | ||||||
| 		summarySort(_function, _contract), | 		functionSort(_function, &_contract), | ||||||
| 		"summary_" + uniquePrefix() + "_" + predicateName(&_function, &_contract), | 		"summary_" + uniquePrefix() + "_" + predicateName(&_function, &_contract), | ||||||
| 		&_function | 		&_function | ||||||
| 	); | 	); | ||||||
| @ -1157,14 +1071,14 @@ void CHC::addRule(smtutil::Expression const& _rule, string const& _ruleName) | |||||||
| 	m_interface->addRule(_rule, _ruleName); | 	m_interface->addRule(_rule, _ruleName); | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| pair<smtutil::CheckResult, CHCSolverInterface::CexGraph> CHC::query(smtutil::Expression const& _query, langutil::SourceLocation const& _location) | pair<CheckResult, CHCSolverInterface::CexGraph> CHC::query(smtutil::Expression const& _query, langutil::SourceLocation const& _location) | ||||||
| { | { | ||||||
| 	smtutil::CheckResult result; | 	CheckResult result; | ||||||
| 	CHCSolverInterface::CexGraph cex; | 	CHCSolverInterface::CexGraph cex; | ||||||
| 	tie(result, cex) = m_interface->query(_query); | 	tie(result, cex) = m_interface->query(_query); | ||||||
| 	switch (result) | 	switch (result) | ||||||
| 	{ | 	{ | ||||||
| 	case smtutil::CheckResult::SATISFIABLE: | 	case CheckResult::SATISFIABLE: | ||||||
| 	{ | 	{ | ||||||
| #ifdef HAVE_Z3 | #ifdef HAVE_Z3 | ||||||
| 		// Even though the problem is SAT, Spacer's pre processing makes counterexamples incomplete.
 | 		// Even though the problem is SAT, Spacer's pre processing makes counterexamples incomplete.
 | ||||||
| @ -1173,25 +1087,25 @@ pair<smtutil::CheckResult, CHCSolverInterface::CexGraph> CHC::query(smtutil::Exp | |||||||
| 		solAssert(spacer, ""); | 		solAssert(spacer, ""); | ||||||
| 		spacer->setSpacerOptions(false); | 		spacer->setSpacerOptions(false); | ||||||
| 
 | 
 | ||||||
| 		smtutil::CheckResult resultNoOpt; | 		CheckResult resultNoOpt; | ||||||
| 		CHCSolverInterface::CexGraph cexNoOpt; | 		CHCSolverInterface::CexGraph cexNoOpt; | ||||||
| 		tie(resultNoOpt, cexNoOpt) = m_interface->query(_query); | 		tie(resultNoOpt, cexNoOpt) = m_interface->query(_query); | ||||||
| 
 | 
 | ||||||
| 		if (resultNoOpt == smtutil::CheckResult::SATISFIABLE) | 		if (resultNoOpt == CheckResult::SATISFIABLE) | ||||||
| 			cex = move(cexNoOpt); | 			cex = move(cexNoOpt); | ||||||
| 
 | 
 | ||||||
| 		spacer->setSpacerOptions(true); | 		spacer->setSpacerOptions(true); | ||||||
| #endif | #endif | ||||||
| 		break; | 		break; | ||||||
| 	} | 	} | ||||||
| 	case smtutil::CheckResult::UNSATISFIABLE: | 	case CheckResult::UNSATISFIABLE: | ||||||
| 		break; | 		break; | ||||||
| 	case smtutil::CheckResult::UNKNOWN: | 	case CheckResult::UNKNOWN: | ||||||
| 		break; | 		break; | ||||||
| 	case smtutil::CheckResult::CONFLICTING: | 	case CheckResult::CONFLICTING: | ||||||
| 		m_outerErrorReporter.warning(1988_error, _location, "At least two SMT solvers provided conflicting answers. Results might not be sound."); | 		m_outerErrorReporter.warning(1988_error, _location, "At least two SMT solvers provided conflicting answers. Results might not be sound."); | ||||||
| 		break; | 		break; | ||||||
| 	case smtutil::CheckResult::ERROR: | 	case CheckResult::ERROR: | ||||||
| 		m_outerErrorReporter.warning(1218_error, _location, "Error trying to invoke SMT solver."); | 		m_outerErrorReporter.warning(1218_error, _location, "Error trying to invoke SMT solver."); | ||||||
| 		break; | 		break; | ||||||
| 	} | 	} | ||||||
| @ -1339,9 +1253,9 @@ void CHC::checkAndReportTarget( | |||||||
| 	createErrorBlock(); | 	createErrorBlock(); | ||||||
| 	connectBlocks(_target.value, error(), _target.constraints && (_target.errorId == _errorId)); | 	connectBlocks(_target.value, error(), _target.constraints && (_target.errorId == _errorId)); | ||||||
| 	auto const& [result, model] = query(error(), _scope->location()); | 	auto const& [result, model] = query(error(), _scope->location()); | ||||||
| 	if (result == smtutil::CheckResult::UNSATISFIABLE) | 	if (result == CheckResult::UNSATISFIABLE) | ||||||
| 		m_safeTargets[_scope].insert(_target.type); | 		m_safeTargets[_scope].insert(_target.type); | ||||||
| 	else if (result == smtutil::CheckResult::SATISFIABLE) | 	else if (result == CheckResult::SATISFIABLE) | ||||||
| 	{ | 	{ | ||||||
| 		solAssert(!_satMsg.empty(), ""); | 		solAssert(!_satMsg.empty(), ""); | ||||||
| 		m_unsafeTargets[_scope].insert(_target.type); | 		m_unsafeTargets[_scope].insert(_target.type); | ||||||
| @ -1477,7 +1391,7 @@ optional<string> CHC::generateCounterexample(CHCSolverInterface::CexGraph const& | |||||||
| 	return localState + "\nTransaction trace:\n" + boost::algorithm::join(boost::adaptors::reverse(path), "\n"); | 	return localState + "\nTransaction trace:\n" + boost::algorithm::join(boost::adaptors::reverse(path), "\n"); | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| string CHC::cex2dot(smtutil::CHCSolverInterface::CexGraph const& _cex) | string CHC::cex2dot(CHCSolverInterface::CexGraph const& _cex) | ||||||
| { | { | ||||||
| 	string dot = "digraph {\n"; | 	string dot = "digraph {\n"; | ||||||
| 
 | 
 | ||||||
| @ -1498,6 +1412,11 @@ string CHC::uniquePrefix() | |||||||
| 	return to_string(m_blockCounter++); | 	return to_string(m_blockCounter++); | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
|  | string CHC::contractSuffix(ContractDefinition const& _contract) | ||||||
|  | { | ||||||
|  | 	return _contract.name() + "_" + to_string(_contract.id()); | ||||||
|  | } | ||||||
|  | 
 | ||||||
| unsigned CHC::newErrorId(frontend::Expression const& _expr) | unsigned CHC::newErrorId(frontend::Expression const& _expr) | ||||||
| { | { | ||||||
| 	unsigned errorId = m_context.newUniqueId(); | 	unsigned errorId = m_context.newUniqueId(); | ||||||
|  | |||||||
| @ -117,19 +117,8 @@ private: | |||||||
| 
 | 
 | ||||||
| 	/// Sort helpers.
 | 	/// Sort helpers.
 | ||||||
| 	//@{
 | 	//@{
 | ||||||
| 	static std::vector<smtutil::SortPointer> stateSorts(ContractDefinition const& _contract); |  | ||||||
| 	smtutil::SortPointer constructorSort(); |  | ||||||
| 	smtutil::SortPointer interfaceSort(); |  | ||||||
| 	smtutil::SortPointer nondetInterfaceSort(); |  | ||||||
| 	static smtutil::SortPointer interfaceSort(ContractDefinition const& _const); |  | ||||||
| 	static smtutil::SortPointer nondetInterfaceSort(ContractDefinition const& _const); |  | ||||||
| 	smtutil::SortPointer arity0FunctionSort() const; |  | ||||||
| 	smtutil::SortPointer sort(FunctionDefinition const& _function); | 	smtutil::SortPointer sort(FunctionDefinition const& _function); | ||||||
| 	smtutil::SortPointer sort(ASTNode const* _block); | 	smtutil::SortPointer sort(ASTNode const* _block); | ||||||
| 	/// @returns the sort of a predicate that represents the summary of _function in the scope of _contract.
 |  | ||||||
| 	/// The _contract is also needed because the same function might be in many contracts due to inheritance,
 |  | ||||||
| 	/// where the sort changes because the set of state variables might change.
 |  | ||||||
| 	static smtutil::SortPointer summarySort(FunctionDefinition const& _function, ContractDefinition const& _contract); |  | ||||||
| 	//@}
 | 	//@}
 | ||||||
| 
 | 
 | ||||||
| 	/// Predicate helpers.
 | 	/// Predicate helpers.
 | ||||||
| @ -246,10 +235,13 @@ private: | |||||||
| 
 | 
 | ||||||
| 	/// Misc.
 | 	/// Misc.
 | ||||||
| 	//@{
 | 	//@{
 | ||||||
| 	/// Returns a prefix to be used in a new unique block name
 | 	/// @returns a prefix to be used in a new unique block name
 | ||||||
| 	/// and increases the block counter.
 | 	/// and increases the block counter.
 | ||||||
| 	std::string uniquePrefix(); | 	std::string uniquePrefix(); | ||||||
| 
 | 
 | ||||||
|  | 	/// @returns a suffix to be used by contract related predicates.
 | ||||||
|  | 	std::string contractSuffix(ContractDefinition const& _contract); | ||||||
|  | 
 | ||||||
| 	/// @returns a new unique error id associated with _expr and stores
 | 	/// @returns a new unique error id associated with _expr and stores
 | ||||||
| 	/// it into m_errorIds.
 | 	/// it into m_errorIds.
 | ||||||
| 	unsigned newErrorId(Expression const& _expr); | 	unsigned newErrorId(Expression const& _expr); | ||||||
| @ -257,10 +249,6 @@ private: | |||||||
| 
 | 
 | ||||||
| 	/// Predicates.
 | 	/// Predicates.
 | ||||||
| 	//@{
 | 	//@{
 | ||||||
| 	/// Implicit constructor predicate.
 |  | ||||||
| 	/// Explicit constructors are handled as functions.
 |  | ||||||
| 	Predicate const* m_implicitConstructorPredicate = nullptr; |  | ||||||
| 
 |  | ||||||
| 	/// Constructor summary predicate, exists after the constructor
 | 	/// Constructor summary predicate, exists after the constructor
 | ||||||
| 	/// (implicit or explicit) and before the interface.
 | 	/// (implicit or explicit) and before the interface.
 | ||||||
| 	Predicate const* m_constructorSummaryPredicate = nullptr; | 	Predicate const* m_constructorSummaryPredicate = nullptr; | ||||||
| @ -292,9 +280,6 @@ private: | |||||||
| 
 | 
 | ||||||
| 	/// Variables.
 | 	/// Variables.
 | ||||||
| 	//@{
 | 	//@{
 | ||||||
| 	/// State variables sorts.
 |  | ||||||
| 	/// Used by all predicates.
 |  | ||||||
| 	std::vector<smtutil::SortPointer> m_stateSorts; |  | ||||||
| 	/// State variables.
 | 	/// State variables.
 | ||||||
| 	/// Used to create all predicates.
 | 	/// Used to create all predicates.
 | ||||||
| 	std::vector<VariableDeclaration const*> m_stateVariables; | 	std::vector<VariableDeclaration const*> m_stateVariables; | ||||||
|  | |||||||
							
								
								
									
										111
									
								
								libsolidity/formal/PredicateSort.cpp
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										111
									
								
								libsolidity/formal/PredicateSort.cpp
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,111 @@ | |||||||
|  | /*
 | ||||||
|  | 	This file is part of solidity. | ||||||
|  | 
 | ||||||
|  | 	solidity is free software: you can redistribute it and/or modify | ||||||
|  | 	it under the terms of the GNU General Public License as published by | ||||||
|  | 	the Free Software Foundation, either version 3 of the License, or | ||||||
|  | 	(at your option) any later version. | ||||||
|  | 
 | ||||||
|  | 	solidity is distributed in the hope that it will be useful, | ||||||
|  | 	but WITHOUT ANY WARRANTY; without even the implied warranty of | ||||||
|  | 	MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the | ||||||
|  | 	GNU General Public License for more details. | ||||||
|  | 
 | ||||||
|  | 	You should have received a copy of the GNU General Public License | ||||||
|  | 	along with solidity.  If not, see <http://www.gnu.org/licenses/>.
 | ||||||
|  | */ | ||||||
|  | // SPDX-License-Identifier: GPL-3.0
 | ||||||
|  | 
 | ||||||
|  | #include <libsolidity/formal/PredicateSort.h> | ||||||
|  | 
 | ||||||
|  | #include <libsolidity/formal/SMTEncoder.h> | ||||||
|  | #include <libsolidity/formal/SymbolicTypes.h> | ||||||
|  | 
 | ||||||
|  | using namespace std; | ||||||
|  | using namespace solidity::util; | ||||||
|  | using namespace solidity::smtutil; | ||||||
|  | 
 | ||||||
|  | namespace solidity::frontend::smt | ||||||
|  | { | ||||||
|  | 
 | ||||||
|  | SortPointer interfaceSort(ContractDefinition const& _contract) | ||||||
|  | { | ||||||
|  | 	return make_shared<FunctionSort>( | ||||||
|  | 		stateSorts(_contract), | ||||||
|  | 		SortProvider::boolSort | ||||||
|  | 	); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | SortPointer nondetInterfaceSort(ContractDefinition const& _contract) | ||||||
|  | { | ||||||
|  | 	auto varSorts = stateSorts(_contract); | ||||||
|  | 	return make_shared<FunctionSort>( | ||||||
|  | 		varSorts + varSorts, | ||||||
|  | 		SortProvider::boolSort | ||||||
|  | 	); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | SortPointer implicitConstructorSort() | ||||||
|  | { | ||||||
|  | 	return arity0FunctionSort(); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | SortPointer constructorSort(ContractDefinition const& _contract) | ||||||
|  | { | ||||||
|  | 	if (auto const* constructor = _contract.constructor()) | ||||||
|  | 		return functionSort(*constructor, &_contract); | ||||||
|  | 
 | ||||||
|  | 	return make_shared<FunctionSort>( | ||||||
|  | 		vector<SortPointer>{SortProvider::uintSort} + stateSorts(_contract), | ||||||
|  | 		SortProvider::boolSort | ||||||
|  | 	); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | SortPointer functionSort(FunctionDefinition const& _function, ContractDefinition const* _contract) | ||||||
|  | { | ||||||
|  | 	auto smtSort = [](auto _var) { return smt::smtSortAbstractFunction(*_var->type()); }; | ||||||
|  | 	auto varSorts = _contract ? stateSorts(*_contract) : vector<SortPointer>{}; | ||||||
|  | 	auto inputSorts = applyMap(_function.parameters(), smtSort); | ||||||
|  | 	auto outputSorts = applyMap(_function.returnParameters(), smtSort); | ||||||
|  | 	return make_shared<FunctionSort>( | ||||||
|  | 		vector<SortPointer>{SortProvider::uintSort} + | ||||||
|  | 			varSorts + | ||||||
|  | 			inputSorts + | ||||||
|  | 			varSorts + | ||||||
|  | 			inputSorts + | ||||||
|  | 			outputSorts, | ||||||
|  | 		SortProvider::boolSort | ||||||
|  | 	); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | SortPointer functionBodySort(FunctionDefinition const& _function, ContractDefinition const* _contract) | ||||||
|  | { | ||||||
|  | 	auto fSort = dynamic_pointer_cast<FunctionSort>(functionSort(_function, _contract)); | ||||||
|  | 	solAssert(fSort, ""); | ||||||
|  | 
 | ||||||
|  | 	auto smtSort = [](auto _var) { return smt::smtSortAbstractFunction(*_var->type()); }; | ||||||
|  | 	return make_shared<FunctionSort>( | ||||||
|  | 		fSort->domain + applyMap(_function.localVariables(), smtSort), | ||||||
|  | 		SortProvider::boolSort | ||||||
|  | 	); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | SortPointer arity0FunctionSort() | ||||||
|  | { | ||||||
|  | 	return make_shared<FunctionSort>( | ||||||
|  | 		vector<SortPointer>(), | ||||||
|  | 		SortProvider::boolSort | ||||||
|  | 	); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | /// Helpers
 | ||||||
|  | 
 | ||||||
|  | vector<SortPointer> stateSorts(ContractDefinition const& _contract) | ||||||
|  | { | ||||||
|  | 	return applyMap( | ||||||
|  | 		SMTEncoder::stateVariablesIncludingInheritedAndPrivate(_contract), | ||||||
|  | 		[](auto _var) { return smt::smtSortAbstractFunction(*_var->type()); } | ||||||
|  | 	); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | } | ||||||
							
								
								
									
										82
									
								
								libsolidity/formal/PredicateSort.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										82
									
								
								libsolidity/formal/PredicateSort.h
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,82 @@ | |||||||
|  | /*
 | ||||||
|  | 	This file is part of solidity. | ||||||
|  | 
 | ||||||
|  | 	solidity is free software: you can redistribute it and/or modify | ||||||
|  | 	it under the terms of the GNU General Public License as published by | ||||||
|  | 	the Free Software Foundation, either version 3 of the License, or | ||||||
|  | 	(at your option) any later version. | ||||||
|  | 
 | ||||||
|  | 	solidity is distributed in the hope that it will be useful, | ||||||
|  | 	but WITHOUT ANY WARRANTY; without even the implied warranty of | ||||||
|  | 	MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the | ||||||
|  | 	GNU General Public License for more details. | ||||||
|  | 
 | ||||||
|  | 	You should have received a copy of the GNU General Public License | ||||||
|  | 	along with solidity.  If not, see <http://www.gnu.org/licenses/>.
 | ||||||
|  | */ | ||||||
|  | // SPDX-License-Identifier: GPL-3.0
 | ||||||
|  | 
 | ||||||
|  | #pragma once | ||||||
|  | 
 | ||||||
|  | #include <libsolidity/formal/Predicate.h> | ||||||
|  | 
 | ||||||
|  | #include <libsmtutil/Sorts.h> | ||||||
|  | 
 | ||||||
|  | namespace solidity::frontend::smt | ||||||
|  | { | ||||||
|  | 
 | ||||||
|  | /**
 | ||||||
|  |  * This file represents the specification for CHC predicate sorts. | ||||||
|  |  * Types of predicates: | ||||||
|  |  * | ||||||
|  |  * 1. Interface | ||||||
|  |  * The idle state of a contract. Signature: | ||||||
|  |  * interface(stateVariables). | ||||||
|  |  * | ||||||
|  |  * 2. Nondet interface | ||||||
|  |  * The nondeterminism behavior of a contract. Signature: | ||||||
|  |  * nondet_interface(stateVariables, stateVariables'). | ||||||
|  |  * | ||||||
|  |  * 3. Implicit constructor | ||||||
|  |  * The implicit constructor of a contract, that is, without input parameters. Signature: | ||||||
|  |  * implicit_constructor(). | ||||||
|  |  * | ||||||
|  |  * 4. Constructor entry/summary | ||||||
|  |  * The summary of an implicit constructor. Signature: | ||||||
|  |  * constructor_summary(error, stateVariables'). | ||||||
|  |  * | ||||||
|  |  * 5. Function entry/summary | ||||||
|  |  * The entry point of a function definition. Signature: | ||||||
|  |  * function_entry(error, stateVariables, inputVariables, stateVariables', inputVariables', outputVariables'). | ||||||
|  |  * | ||||||
|  |  * 6. Function body | ||||||
|  |  * Use for any predicate within a function. Signature: | ||||||
|  |  * function_body(error, stateVariables, inputVariables, stateVariables', inputVariables', outputVariables', localVariables). | ||||||
|  |  */ | ||||||
|  | 
 | ||||||
|  | /// @returns the interface predicate sort for _contract.
 | ||||||
|  | smtutil::SortPointer interfaceSort(ContractDefinition const& _contract); | ||||||
|  | 
 | ||||||
|  | /// @returns the nondeterminisc interface predicate sort for _contract.
 | ||||||
|  | smtutil::SortPointer nondetInterfaceSort(ContractDefinition const& _contract); | ||||||
|  | 
 | ||||||
|  | /// @returns the implicit constructor predicate sort.
 | ||||||
|  | smtutil::SortPointer implicitConstructorSort(); | ||||||
|  | 
 | ||||||
|  | /// @returns the constructor entry/summary predicate sort for _contract.
 | ||||||
|  | smtutil::SortPointer constructorSort(ContractDefinition const& _contract); | ||||||
|  | 
 | ||||||
|  | /// @returns the function entry/summary predicate sort for _function contained in _contract.
 | ||||||
|  | smtutil::SortPointer functionSort(FunctionDefinition const& _function, ContractDefinition const* _contract); | ||||||
|  | 
 | ||||||
|  | /// @returns the function body predicate sort for _function contained in _contract.
 | ||||||
|  | smtutil::SortPointer functionBodySort(FunctionDefinition const& _function, ContractDefinition const* _contract); | ||||||
|  | 
 | ||||||
|  | /// @returns the sort of a predicate without parameters.
 | ||||||
|  | smtutil::SortPointer arity0FunctionSort(); | ||||||
|  | 
 | ||||||
|  | /// Helpers
 | ||||||
|  | 
 | ||||||
|  | std::vector<smtutil::SortPointer> stateSorts(ContractDefinition const& _contract) ; | ||||||
|  | 
 | ||||||
|  | } | ||||||
| @ -23,6 +23,7 @@ | |||||||
| #include <libsolidity/formal/SymbolicTypes.h> | #include <libsolidity/formal/SymbolicTypes.h> | ||||||
| 
 | 
 | ||||||
| #include <libsmtutil/SMTPortfolio.h> | #include <libsmtutil/SMTPortfolio.h> | ||||||
|  | #include <libsmtutil/Helpers.h> | ||||||
| 
 | 
 | ||||||
| #include <boost/range/adaptors.hpp> | #include <boost/range/adaptors.hpp> | ||||||
| #include <boost/range/adaptor/reversed.hpp> | #include <boost/range/adaptor/reversed.hpp> | ||||||
| @ -675,6 +676,14 @@ void SMTEncoder::endVisit(FunctionCall const& _funCall) | |||||||
| 	case FunctionType::Kind::ArrayPop: | 	case FunctionType::Kind::ArrayPop: | ||||||
| 		arrayPop(_funCall); | 		arrayPop(_funCall); | ||||||
| 		break; | 		break; | ||||||
|  | 	case FunctionType::Kind::Log0: | ||||||
|  | 	case FunctionType::Kind::Log1: | ||||||
|  | 	case FunctionType::Kind::Log2: | ||||||
|  | 	case FunctionType::Kind::Log3: | ||||||
|  | 	case FunctionType::Kind::Log4: | ||||||
|  | 	case FunctionType::Kind::Event: | ||||||
|  | 		// These can be safely ignored.
 | ||||||
|  | 		break; | ||||||
| 	default: | 	default: | ||||||
| 		m_errorReporter.warning( | 		m_errorReporter.warning( | ||||||
| 			4588_error, | 			4588_error, | ||||||
| @ -1497,11 +1506,7 @@ smtutil::Expression SMTEncoder::division(smtutil::Expression _left, smtutil::Exp | |||||||
| { | { | ||||||
| 	// Signed division in SMTLIB2 rounds differently for negative division.
 | 	// Signed division in SMTLIB2 rounds differently for negative division.
 | ||||||
| 	if (_type.isSigned()) | 	if (_type.isSigned()) | ||||||
| 		return (smtutil::Expression::ite( | 		return signedDivision(_left, _right); | ||||||
| 			_left >= 0, |  | ||||||
| 			smtutil::Expression::ite(_right >= 0, _left / _right, 0 - (_left / (0 - _right))), |  | ||||||
| 			smtutil::Expression::ite(_right >= 0, 0 - ((0 - _left) / _right), (0 - _left) / (0 - _right)) |  | ||||||
| 		)); |  | ||||||
| 	else | 	else | ||||||
| 		return _left / _right; | 		return _left / _right; | ||||||
| } | } | ||||||
|  | |||||||
| @ -524,6 +524,10 @@ bool CompilerStack::compile() | |||||||
| 					{ | 					{ | ||||||
| 						if (m_generateEvmBytecode) | 						if (m_generateEvmBytecode) | ||||||
| 							compileContract(*contract, otherCompilers); | 							compileContract(*contract, otherCompilers); | ||||||
|  | 						if (m_generateIR || m_generateEwasm) | ||||||
|  | 							generateIR(*contract); | ||||||
|  | 						if (m_generateEwasm) | ||||||
|  | 							generateEwasm(*contract); | ||||||
| 					} | 					} | ||||||
| 					catch (Error const& _error) | 					catch (Error const& _error) | ||||||
| 					{ | 					{ | ||||||
| @ -532,10 +536,28 @@ bool CompilerStack::compile() | |||||||
| 						m_errorReporter.error(_error.errorId(), _error.type(), SourceLocation(), _error.what()); | 						m_errorReporter.error(_error.errorId(), _error.type(), SourceLocation(), _error.what()); | ||||||
| 						return false; | 						return false; | ||||||
| 					} | 					} | ||||||
| 					if (m_generateIR || m_generateEwasm) | 					catch (UnimplementedFeatureError const& _unimplementedError) | ||||||
| 						generateIR(*contract); | 					{ | ||||||
| 					if (m_generateEwasm) | 						if ( | ||||||
| 						generateEwasm(*contract); | 							SourceLocation const* sourceLocation = | ||||||
|  | 							boost::get_error_info<langutil::errinfo_sourceLocation>(_unimplementedError) | ||||||
|  | 						) | ||||||
|  | 						{ | ||||||
|  | 							string const* comment = _unimplementedError.comment(); | ||||||
|  | 							m_errorReporter.error( | ||||||
|  | 								1834_error, | ||||||
|  | 								Error::Type::CodeGenerationError, | ||||||
|  | 								*sourceLocation, | ||||||
|  | 								"Unimplemented feature error" + | ||||||
|  | 								((comment && !comment->empty()) ? ": " + *comment : string{}) + | ||||||
|  | 								" in " + | ||||||
|  | 								_unimplementedError.lineInfo() | ||||||
|  | 							); | ||||||
|  | 							return false; | ||||||
|  | 						} | ||||||
|  | 						else | ||||||
|  | 							throw; | ||||||
|  | 					} | ||||||
| 				} | 				} | ||||||
| 	m_stackState = CompilationSuccessful; | 	m_stackState = CompilationSuccessful; | ||||||
| 	this->link(); | 	this->link(); | ||||||
|  | |||||||
| @ -132,6 +132,8 @@ add_library(yul | |||||||
| 	optimiser/OptimiserStep.h | 	optimiser/OptimiserStep.h | ||||||
| 	optimiser/OptimizerUtilities.cpp | 	optimiser/OptimizerUtilities.cpp | ||||||
| 	optimiser/OptimizerUtilities.h | 	optimiser/OptimizerUtilities.h | ||||||
|  | 	optimiser/ReasoningBasedSimplifier.cpp | ||||||
|  | 	optimiser/ReasoningBasedSimplifier.h | ||||||
| 	optimiser/RedundantAssignEliminator.cpp | 	optimiser/RedundantAssignEliminator.cpp | ||||||
| 	optimiser/RedundantAssignEliminator.h | 	optimiser/RedundantAssignEliminator.h | ||||||
| 	optimiser/Rematerialiser.cpp | 	optimiser/Rematerialiser.cpp | ||||||
| @ -168,4 +170,5 @@ add_library(yul | |||||||
| 	optimiser/VarNameCleaner.cpp | 	optimiser/VarNameCleaner.cpp | ||||||
| 	optimiser/VarNameCleaner.h | 	optimiser/VarNameCleaner.h | ||||||
| ) | ) | ||||||
| target_link_libraries(yul PUBLIC evmasm solutil langutil) | 
 | ||||||
|  | target_link_libraries(yul PUBLIC evmasm solutil langutil smtutil) | ||||||
| @ -33,6 +33,15 @@ Literal Dialect::zeroLiteralForType(solidity::yul::YulString _type) const | |||||||
| 	return {SourceLocation{}, LiteralKind::Number, "0"_yulstring, _type}; | 	return {SourceLocation{}, LiteralKind::Number, "0"_yulstring, _type}; | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
|  | 
 | ||||||
|  | Literal Dialect::trueLiteral() const | ||||||
|  | { | ||||||
|  | 	if (boolType != defaultType) | ||||||
|  | 		return {SourceLocation{}, LiteralKind::Boolean, "true"_yulstring, boolType}; | ||||||
|  | 	else | ||||||
|  | 		return {SourceLocation{}, LiteralKind::Number, "1"_yulstring, defaultType}; | ||||||
|  | } | ||||||
|  | 
 | ||||||
| bool Dialect::validTypeForLiteral(LiteralKind _kind, YulString, YulString _type) const | bool Dialect::validTypeForLiteral(LiteralKind _kind, YulString, YulString _type) const | ||||||
| { | { | ||||||
| 	if (_kind == LiteralKind::Boolean) | 	if (_kind == LiteralKind::Boolean) | ||||||
|  | |||||||
| @ -77,6 +77,7 @@ struct Dialect: boost::noncopyable | |||||||
| 	virtual bool validTypeForLiteral(LiteralKind _kind, YulString _value, YulString _type) const; | 	virtual bool validTypeForLiteral(LiteralKind _kind, YulString _value, YulString _type) const; | ||||||
| 
 | 
 | ||||||
| 	virtual Literal zeroLiteralForType(YulString _type) const; | 	virtual Literal zeroLiteralForType(YulString _type) const; | ||||||
|  | 	virtual Literal trueLiteral() const; | ||||||
| 
 | 
 | ||||||
| 	virtual std::set<YulString> fixedFunctionNames() const { return {}; } | 	virtual std::set<YulString> fixedFunctionNames() const { return {}; } | ||||||
| 
 | 
 | ||||||
|  | |||||||
| @ -20,6 +20,7 @@ | |||||||
| 
 | 
 | ||||||
| #include <libyul/Exceptions.h> | #include <libyul/Exceptions.h> | ||||||
| 
 | 
 | ||||||
|  | #include <optional> | ||||||
| #include <string> | #include <string> | ||||||
| #include <set> | #include <set> | ||||||
| 
 | 
 | ||||||
| @ -49,17 +50,41 @@ struct OptimiserStep | |||||||
| 	virtual ~OptimiserStep() = default; | 	virtual ~OptimiserStep() = default; | ||||||
| 
 | 
 | ||||||
| 	virtual void run(OptimiserStepContext&, Block&) const = 0; | 	virtual void run(OptimiserStepContext&, Block&) const = 0; | ||||||
|  | 	/// @returns non-nullopt if the step cannot be run, for example because it requires
 | ||||||
|  | 	/// an SMT solver to be loaded, but none is available. In that case, the string
 | ||||||
|  | 	/// contains a human-readable reason.
 | ||||||
|  | 	virtual std::optional<std::string> invalidInCurrentEnvironment() const = 0; | ||||||
| 	std::string name; | 	std::string name; | ||||||
| }; | }; | ||||||
| 
 | 
 | ||||||
| template <class Step> | template <class Step> | ||||||
| struct OptimiserStepInstance: public OptimiserStep | struct OptimiserStepInstance: public OptimiserStep | ||||||
| { | { | ||||||
|  | private: | ||||||
|  | 	template<typename T> | ||||||
|  | 	struct HasInvalidInCurrentEnvironmentMethod | ||||||
|  | 	{ | ||||||
|  | 	private: | ||||||
|  | 		template<typename U> static auto test(int) -> decltype(U::invalidInCurrentEnvironment(), std::true_type()); | ||||||
|  | 		template<typename> static std::false_type test(...); | ||||||
|  | 
 | ||||||
|  | 	public: | ||||||
|  | 		static constexpr bool value = decltype(test<T>(0))::value; | ||||||
|  | 	}; | ||||||
|  | 
 | ||||||
|  | public: | ||||||
| 	OptimiserStepInstance(): OptimiserStep{Step::name} {} | 	OptimiserStepInstance(): OptimiserStep{Step::name} {} | ||||||
| 	void run(OptimiserStepContext& _context, Block& _ast) const override | 	void run(OptimiserStepContext& _context, Block& _ast) const override | ||||||
| 	{ | 	{ | ||||||
| 		Step::run(_context, _ast); | 		Step::run(_context, _ast); | ||||||
| 	} | 	} | ||||||
|  | 	std::optional<std::string> invalidInCurrentEnvironment() const override | ||||||
|  | 	{ | ||||||
|  | 		if constexpr (HasInvalidInCurrentEnvironmentMethod<Step>::value) | ||||||
|  | 			return Step::invalidInCurrentEnvironment(); | ||||||
|  | 		else | ||||||
|  | 			return std::nullopt; | ||||||
|  | 	}; | ||||||
| }; | }; | ||||||
| 
 | 
 | ||||||
| 
 | 
 | ||||||
|  | |||||||
							
								
								
									
										339
									
								
								libyul/optimiser/ReasoningBasedSimplifier.cpp
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										339
									
								
								libyul/optimiser/ReasoningBasedSimplifier.cpp
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,339 @@ | |||||||
|  | /*
 | ||||||
|  | 	This file is part of solidity. | ||||||
|  | 
 | ||||||
|  | 	solidity is free software: you can redistribute it and/or modify | ||||||
|  | 	it under the terms of the GNU General Public License as published by | ||||||
|  | 	the Free Software Foundation, either version 3 of the License, or | ||||||
|  | 	(at your option) any later version. | ||||||
|  | 
 | ||||||
|  | 	solidity is distributed in the hope that it will be useful, | ||||||
|  | 	but WITHOUT ANY WARRANTY; without even the implied warranty of | ||||||
|  | 	MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the | ||||||
|  | 	GNU General Public License for more details. | ||||||
|  | 
 | ||||||
|  | 	You should have received a copy of the GNU General Public License | ||||||
|  | 	along with solidity.  If not, see <http://www.gnu.org/licenses/>.
 | ||||||
|  | */ | ||||||
|  | 
 | ||||||
|  | #include <libyul/optimiser/ReasoningBasedSimplifier.h> | ||||||
|  | 
 | ||||||
|  | #include <libyul/optimiser/SSAValueTracker.h> | ||||||
|  | #include <libyul/optimiser/Semantics.h> | ||||||
|  | #include <libyul/AsmData.h> | ||||||
|  | #include <libyul/Utilities.h> | ||||||
|  | #include <libyul/Dialect.h> | ||||||
|  | 
 | ||||||
|  | #include <libyul/backends/evm/EVMDialect.h> | ||||||
|  | 
 | ||||||
|  | #include <libsmtutil/SMTPortfolio.h> | ||||||
|  | #include <libsmtutil/Helpers.h> | ||||||
|  | 
 | ||||||
|  | #include <libsolutil/Visitor.h> | ||||||
|  | #include <libsolutil/CommonData.h> | ||||||
|  | 
 | ||||||
|  | #include <utility> | ||||||
|  | #include <memory> | ||||||
|  | 
 | ||||||
|  | using namespace std; | ||||||
|  | using namespace solidity; | ||||||
|  | using namespace solidity::util; | ||||||
|  | using namespace solidity::yul; | ||||||
|  | using namespace solidity::smtutil; | ||||||
|  | 
 | ||||||
|  | void ReasoningBasedSimplifier::run(OptimiserStepContext& _context, Block& _ast) | ||||||
|  | { | ||||||
|  | 	set<YulString> ssaVars = SSAValueTracker::ssaVariables(_ast); | ||||||
|  | 	ReasoningBasedSimplifier{_context.dialect, ssaVars}(_ast); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | std::optional<string> ReasoningBasedSimplifier::invalidInCurrentEnvironment() | ||||||
|  | { | ||||||
|  | 	// SMTLib2 interface is always available, but we would like to have synchronous answers.
 | ||||||
|  | 	if (smtutil::SMTPortfolio{}.solvers() <= 1) | ||||||
|  | 		return string{"No SMT solvers available."}; | ||||||
|  | 	else | ||||||
|  | 		return nullopt; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | void ReasoningBasedSimplifier::operator()(VariableDeclaration& _varDecl) | ||||||
|  | { | ||||||
|  | 	if (_varDecl.variables.size() != 1 || !_varDecl.value) | ||||||
|  | 		return; | ||||||
|  | 	YulString varName = _varDecl.variables.front().name; | ||||||
|  | 	if (!m_ssaVariables.count(varName)) | ||||||
|  | 		return; | ||||||
|  | 	bool const inserted = m_variables.insert({varName, m_solver->newVariable("yul_" + varName.str(), defaultSort())}).second; | ||||||
|  | 	yulAssert(inserted, ""); | ||||||
|  | 	m_solver->addAssertion(m_variables.at(varName) == encodeExpression(*_varDecl.value)); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | void ReasoningBasedSimplifier::operator()(If& _if) | ||||||
|  | { | ||||||
|  | 	if (!SideEffectsCollector{m_dialect, *_if.condition}.movable()) | ||||||
|  | 		return; | ||||||
|  | 
 | ||||||
|  | 	smtutil::Expression condition = encodeExpression(*_if.condition); | ||||||
|  | 	m_solver->push(); | ||||||
|  | 	m_solver->addAssertion(condition == constantValue(0)); | ||||||
|  | 	CheckResult result = m_solver->check({}).first; | ||||||
|  | 	m_solver->pop(); | ||||||
|  | 	if (result == CheckResult::UNSATISFIABLE) | ||||||
|  | 	{ | ||||||
|  | 		Literal trueCondition = m_dialect.trueLiteral(); | ||||||
|  | 		trueCondition.location = locationOf(*_if.condition); | ||||||
|  | 		_if.condition = make_unique<yul::Expression>(move(trueCondition)); | ||||||
|  | 	} | ||||||
|  | 	else | ||||||
|  | 	{ | ||||||
|  | 		m_solver->push(); | ||||||
|  | 		m_solver->addAssertion(condition != constantValue(0)); | ||||||
|  | 		CheckResult result2 = m_solver->check({}).first; | ||||||
|  | 		m_solver->pop(); | ||||||
|  | 		if (result2 == CheckResult::UNSATISFIABLE) | ||||||
|  | 		{ | ||||||
|  | 			Literal falseCondition = m_dialect.zeroLiteralForType(m_dialect.boolType); | ||||||
|  | 			falseCondition.location = locationOf(*_if.condition); | ||||||
|  | 			_if.condition = make_unique<yul::Expression>(move(falseCondition)); | ||||||
|  | 			_if.body = yul::Block{}; | ||||||
|  | 			// Nothing left to be done.
 | ||||||
|  | 			return; | ||||||
|  | 		} | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
|  | 	m_solver->push(); | ||||||
|  | 	m_solver->addAssertion(condition != constantValue(0)); | ||||||
|  | 
 | ||||||
|  | 	ASTModifier::operator()(_if.body); | ||||||
|  | 
 | ||||||
|  | 	m_solver->pop(); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | ReasoningBasedSimplifier::ReasoningBasedSimplifier( | ||||||
|  | 	Dialect const& _dialect, | ||||||
|  | 	set<YulString> const& _ssaVariables | ||||||
|  | ): | ||||||
|  | 	m_dialect(_dialect), | ||||||
|  | 	m_ssaVariables(_ssaVariables), | ||||||
|  | 	m_solver(make_unique<smtutil::SMTPortfolio>()) | ||||||
|  | { | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | smtutil::Expression ReasoningBasedSimplifier::encodeExpression(yul::Expression const& _expression) | ||||||
|  | { | ||||||
|  | 	return std::visit(GenericVisitor{ | ||||||
|  | 		[&](FunctionCall const& _functionCall) | ||||||
|  | 		{ | ||||||
|  | 			if (auto const* dialect = dynamic_cast<EVMDialect const*>(&m_dialect)) | ||||||
|  | 				if (auto const* builtin = dialect->builtin(_functionCall.functionName.name)) | ||||||
|  | 					if (builtin->instruction) | ||||||
|  | 						return encodeEVMBuiltin(*builtin->instruction, _functionCall.arguments); | ||||||
|  | 			return newRestrictedVariable(); | ||||||
|  | 		}, | ||||||
|  | 		[&](Identifier const& _identifier) | ||||||
|  | 		{ | ||||||
|  | 			if ( | ||||||
|  | 				m_ssaVariables.count(_identifier.name) && | ||||||
|  | 				m_variables.count(_identifier.name) | ||||||
|  | 			) | ||||||
|  | 				return m_variables.at(_identifier.name); | ||||||
|  | 			else | ||||||
|  | 				return newRestrictedVariable(); | ||||||
|  | 		}, | ||||||
|  | 		[&](Literal const& _literal) | ||||||
|  | 		{ | ||||||
|  | 			return literalValue(_literal); | ||||||
|  | 		} | ||||||
|  | 	}, _expression); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | smtutil::Expression ReasoningBasedSimplifier::encodeEVMBuiltin( | ||||||
|  | 	evmasm::Instruction _instruction, | ||||||
|  | 	vector<yul::Expression> const& _arguments | ||||||
|  | ) | ||||||
|  | { | ||||||
|  | 	vector<smtutil::Expression> arguments = applyMap( | ||||||
|  | 		_arguments, | ||||||
|  | 		[this](yul::Expression const& _expr) { return encodeExpression(_expr); } | ||||||
|  | 	); | ||||||
|  | 	switch (_instruction) | ||||||
|  | 	{ | ||||||
|  | 	case evmasm::Instruction::ADD: | ||||||
|  | 		return wrap(arguments.at(0) + arguments.at(1)); | ||||||
|  | 	case evmasm::Instruction::MUL: | ||||||
|  | 		return wrap(arguments.at(0) * arguments.at(1)); | ||||||
|  | 	case evmasm::Instruction::SUB: | ||||||
|  | 		return wrap(arguments.at(0) - arguments.at(1)); | ||||||
|  | 	case evmasm::Instruction::DIV: | ||||||
|  | 		return smtutil::Expression::ite( | ||||||
|  | 			arguments.at(1) == constantValue(0), | ||||||
|  | 			constantValue(0), | ||||||
|  | 			arguments.at(0) / arguments.at(1) | ||||||
|  | 		); | ||||||
|  | 	case evmasm::Instruction::SDIV: | ||||||
|  | 		return smtutil::Expression::ite( | ||||||
|  | 			arguments.at(1) == constantValue(0), | ||||||
|  | 			constantValue(0), | ||||||
|  | 			// No `wrap()` needed here, because -2**255 / -1 results
 | ||||||
|  | 			// in 2**255 which is "converted" to its two's complement
 | ||||||
|  | 			// representation 2**255 in `signedToUnsigned`
 | ||||||
|  | 			signedToUnsigned(smtutil::signedDivision( | ||||||
|  | 				unsignedToSigned(arguments.at(0)), | ||||||
|  | 				unsignedToSigned(arguments.at(1)) | ||||||
|  | 			)) | ||||||
|  | 		); | ||||||
|  | 		break; | ||||||
|  | 	case evmasm::Instruction::MOD: | ||||||
|  | 		return smtutil::Expression::ite( | ||||||
|  | 			arguments.at(1) == constantValue(0), | ||||||
|  | 			constantValue(0), | ||||||
|  | 			arguments.at(0) % arguments.at(1) | ||||||
|  | 		); | ||||||
|  | 	case evmasm::Instruction::SMOD: | ||||||
|  | 		return smtutil::Expression::ite( | ||||||
|  | 			arguments.at(1) == constantValue(0), | ||||||
|  | 			constantValue(0), | ||||||
|  | 			signedToUnsigned(signedModulo( | ||||||
|  | 				unsignedToSigned(arguments.at(0)), | ||||||
|  | 				unsignedToSigned(arguments.at(1)) | ||||||
|  | 			)) | ||||||
|  | 		); | ||||||
|  | 		break; | ||||||
|  | 	case evmasm::Instruction::LT: | ||||||
|  | 		return booleanValue(arguments.at(0) < arguments.at(1)); | ||||||
|  | 	case evmasm::Instruction::SLT: | ||||||
|  | 		return booleanValue(unsignedToSigned(arguments.at(0)) < unsignedToSigned(arguments.at(1))); | ||||||
|  | 	case evmasm::Instruction::GT: | ||||||
|  | 		return booleanValue(arguments.at(0) > arguments.at(1)); | ||||||
|  | 	case evmasm::Instruction::SGT: | ||||||
|  | 		return booleanValue(unsignedToSigned(arguments.at(0)) > unsignedToSigned(arguments.at(1))); | ||||||
|  | 	case evmasm::Instruction::EQ: | ||||||
|  | 		return booleanValue(arguments.at(0) == arguments.at(1)); | ||||||
|  | 	case evmasm::Instruction::ISZERO: | ||||||
|  | 		return booleanValue(arguments.at(0) == constantValue(0)); | ||||||
|  | 	case evmasm::Instruction::AND: | ||||||
|  | 		return smtutil::Expression::ite( | ||||||
|  | 			(arguments.at(0) == 0 || arguments.at(0) == 1) && | ||||||
|  | 			(arguments.at(1) == 0 || arguments.at(1) == 1), | ||||||
|  | 			booleanValue(arguments.at(0) == 1 && arguments.at(1) == 1), | ||||||
|  | 			bv2int(int2bv(arguments.at(0)) & int2bv(arguments.at(1))) | ||||||
|  | 		); | ||||||
|  | 	case evmasm::Instruction::OR: | ||||||
|  | 		return smtutil::Expression::ite( | ||||||
|  | 			(arguments.at(0) == 0 || arguments.at(0) == 1) && | ||||||
|  | 			(arguments.at(1) == 0 || arguments.at(1) == 1), | ||||||
|  | 			booleanValue(arguments.at(0) == 1 || arguments.at(1) == 1), | ||||||
|  | 			bv2int(int2bv(arguments.at(0)) | int2bv(arguments.at(1))) | ||||||
|  | 		); | ||||||
|  | 	case evmasm::Instruction::XOR: | ||||||
|  | 		return bv2int(int2bv(arguments.at(0)) ^ int2bv(arguments.at(1))); | ||||||
|  | 	case evmasm::Instruction::NOT: | ||||||
|  | 		return smtutil::Expression(u256(-1)) - arguments.at(0); | ||||||
|  | 	case evmasm::Instruction::SHL: | ||||||
|  | 		return smtutil::Expression::ite( | ||||||
|  | 			arguments.at(0) > 255, | ||||||
|  | 			constantValue(0), | ||||||
|  | 			bv2int(int2bv(arguments.at(1)) << int2bv(arguments.at(0))) | ||||||
|  | 		); | ||||||
|  | 	case evmasm::Instruction::SHR: | ||||||
|  | 		return smtutil::Expression::ite( | ||||||
|  | 			arguments.at(0) > 255, | ||||||
|  | 			constantValue(0), | ||||||
|  | 			bv2int(int2bv(arguments.at(1)) >> int2bv(arguments.at(0))) | ||||||
|  | 		); | ||||||
|  | 	case evmasm::Instruction::SAR: | ||||||
|  | 		return smtutil::Expression::ite( | ||||||
|  | 			arguments.at(0) > 255, | ||||||
|  | 			constantValue(0), | ||||||
|  | 			bv2int(smtutil::Expression::ashr(int2bv(arguments.at(1)), int2bv(arguments.at(0)))) | ||||||
|  | 		); | ||||||
|  | 	case evmasm::Instruction::ADDMOD: | ||||||
|  | 		return smtutil::Expression::ite( | ||||||
|  | 			arguments.at(2) == constantValue(0), | ||||||
|  | 			constantValue(0), | ||||||
|  | 			(arguments.at(0) + arguments.at(1)) % arguments.at(2) | ||||||
|  | 		); | ||||||
|  | 	case evmasm::Instruction::MULMOD: | ||||||
|  | 		return smtutil::Expression::ite( | ||||||
|  | 			arguments.at(2) == constantValue(0), | ||||||
|  | 			constantValue(0), | ||||||
|  | 			(arguments.at(0) * arguments.at(1)) % arguments.at(2) | ||||||
|  | 		); | ||||||
|  | 	// TODO SIGNEXTEND
 | ||||||
|  | 	default: | ||||||
|  | 		break; | ||||||
|  | 	} | ||||||
|  | 	return newRestrictedVariable(); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | smtutil::Expression ReasoningBasedSimplifier::int2bv(smtutil::Expression _arg) const | ||||||
|  | { | ||||||
|  | 	return smtutil::Expression::int2bv(std::move(_arg), 256); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | smtutil::Expression ReasoningBasedSimplifier::bv2int(smtutil::Expression _arg) const | ||||||
|  | { | ||||||
|  | 	return smtutil::Expression::bv2int(std::move(_arg)); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | smtutil::Expression ReasoningBasedSimplifier::newVariable() | ||||||
|  | { | ||||||
|  | 	return m_solver->newVariable(uniqueName(), defaultSort()); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | smtutil::Expression ReasoningBasedSimplifier::newRestrictedVariable() | ||||||
|  | { | ||||||
|  | 	smtutil::Expression var = newVariable(); | ||||||
|  | 	m_solver->addAssertion(0 <= var && var < smtutil::Expression(bigint(1) << 256)); | ||||||
|  | 	return var; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | string ReasoningBasedSimplifier::uniqueName() | ||||||
|  | { | ||||||
|  | 	return "expr_" + to_string(m_varCounter++); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | shared_ptr<Sort> ReasoningBasedSimplifier::defaultSort() const | ||||||
|  | { | ||||||
|  | 	return SortProvider::intSort(); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | smtutil::Expression ReasoningBasedSimplifier::booleanValue(smtutil::Expression _value) const | ||||||
|  | { | ||||||
|  | 	return smtutil::Expression::ite(_value, constantValue(1), constantValue(0)); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | smtutil::Expression ReasoningBasedSimplifier::constantValue(size_t _value) const | ||||||
|  | { | ||||||
|  | 	return _value; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | smtutil::Expression ReasoningBasedSimplifier::literalValue(Literal const& _literal) const | ||||||
|  | { | ||||||
|  | 	return smtutil::Expression(valueOfLiteral(_literal)); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | smtutil::Expression ReasoningBasedSimplifier::unsignedToSigned(smtutil::Expression _value) | ||||||
|  | { | ||||||
|  | 	return smtutil::Expression::ite( | ||||||
|  | 		_value < smtutil::Expression(bigint(1) << 255), | ||||||
|  | 		_value, | ||||||
|  | 		_value - smtutil::Expression(bigint(1) << 256) | ||||||
|  | 	); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | smtutil::Expression ReasoningBasedSimplifier::signedToUnsigned(smtutil::Expression _value) | ||||||
|  | { | ||||||
|  | 	return smtutil::Expression::ite( | ||||||
|  | 		_value >= 0, | ||||||
|  | 		_value, | ||||||
|  | 		_value + smtutil::Expression(bigint(1) << 256) | ||||||
|  | 	); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | smtutil::Expression ReasoningBasedSimplifier::wrap(smtutil::Expression _value) | ||||||
|  | { | ||||||
|  | 	smtutil::Expression rest = newRestrictedVariable(); | ||||||
|  | 	smtutil::Expression multiplier = newVariable(); | ||||||
|  | 	m_solver->addAssertion(_value == multiplier * smtutil::Expression(bigint(1) << 256) + rest); | ||||||
|  | 	return rest; | ||||||
|  | } | ||||||
							
								
								
									
										98
									
								
								libyul/optimiser/ReasoningBasedSimplifier.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										98
									
								
								libyul/optimiser/ReasoningBasedSimplifier.h
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,98 @@ | |||||||
|  | /*
 | ||||||
|  | 	This file is part of solidity. | ||||||
|  | 
 | ||||||
|  | 	solidity is free software: you can redistribute it and/or modify | ||||||
|  | 	it under the terms of the GNU General Public License as published by | ||||||
|  | 	the Free Software Foundation, either version 3 of the License, or | ||||||
|  | 	(at your option) any later version. | ||||||
|  | 
 | ||||||
|  | 	solidity is distributed in the hope that it will be useful, | ||||||
|  | 	but WITHOUT ANY WARRANTY; without even the implied warranty of | ||||||
|  | 	MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the | ||||||
|  | 	GNU General Public License for more details. | ||||||
|  | 
 | ||||||
|  | 	You should have received a copy of the GNU General Public License | ||||||
|  | 	along with solidity.  If not, see <http://www.gnu.org/licenses/>.
 | ||||||
|  | */ | ||||||
|  | #pragma once | ||||||
|  | 
 | ||||||
|  | #include <libyul/optimiser/ASTWalker.h> | ||||||
|  | #include <libyul/optimiser/OptimiserStep.h> | ||||||
|  | #include <libyul/Dialect.h> | ||||||
|  | 
 | ||||||
|  | // because of instruction
 | ||||||
|  | #include <libyul/backends/evm/EVMDialect.h> | ||||||
|  | 
 | ||||||
|  | #include <map> | ||||||
|  | 
 | ||||||
|  | namespace solidity::smtutil | ||||||
|  | { | ||||||
|  | class SolverInterface; | ||||||
|  | class Expression; | ||||||
|  | struct Sort; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | namespace solidity::yul | ||||||
|  | { | ||||||
|  | 
 | ||||||
|  | /**
 | ||||||
|  |  * Reasoning-based simplifier. | ||||||
|  |  * This optimizer uses SMT solvers to check whether `if` conditions are constant. | ||||||
|  |  * - If `constraints AND condition` is UNSAT, the condition is never true and the whole body can be removed. | ||||||
|  |  * - If `constraints AND NOT condition` is UNSAT, the condition is always true and can be replaced by `1`. | ||||||
|  |  * The simplifications above can only be applied if the condition is movable. | ||||||
|  |  * | ||||||
|  |  * It is only effective on the EVM dialect, but safe to use on other dialects. | ||||||
|  |  * | ||||||
|  |  * Prerequisite: Disambiguator, SSATransform. | ||||||
|  |  */ | ||||||
|  | class ReasoningBasedSimplifier: public ASTModifier | ||||||
|  | { | ||||||
|  | public: | ||||||
|  | 	static constexpr char const* name{"ReasoningBasedSimplifier"}; | ||||||
|  | 	static void run(OptimiserStepContext& _context, Block& _ast); | ||||||
|  | 	static std::optional<std::string> invalidInCurrentEnvironment(); | ||||||
|  | 
 | ||||||
|  | 	using ASTModifier::operator(); | ||||||
|  | 	void operator()(VariableDeclaration& _varDecl) override; | ||||||
|  | 	void operator()(If& _if) override; | ||||||
|  | 
 | ||||||
|  | private: | ||||||
|  | 	explicit ReasoningBasedSimplifier( | ||||||
|  | 		Dialect const& _dialect, | ||||||
|  | 		std::set<YulString> const& _ssaVariables | ||||||
|  | 	); | ||||||
|  | 
 | ||||||
|  | 	smtutil::Expression encodeExpression( | ||||||
|  | 		Expression const& _expression | ||||||
|  | 	); | ||||||
|  | 
 | ||||||
|  | 	virtual smtutil::Expression encodeEVMBuiltin( | ||||||
|  | 		evmasm::Instruction _instruction, | ||||||
|  | 		std::vector<Expression> const& _arguments | ||||||
|  | 	); | ||||||
|  | 
 | ||||||
|  | 	smtutil::Expression int2bv(smtutil::Expression _arg) const; | ||||||
|  | 	smtutil::Expression bv2int(smtutil::Expression _arg) const; | ||||||
|  | 
 | ||||||
|  | 	smtutil::Expression newVariable(); | ||||||
|  | 	virtual smtutil::Expression newRestrictedVariable(); | ||||||
|  | 	std::string uniqueName(); | ||||||
|  | 
 | ||||||
|  | 	virtual std::shared_ptr<smtutil::Sort> defaultSort() const; | ||||||
|  | 	virtual smtutil::Expression booleanValue(smtutil::Expression _value) const; | ||||||
|  | 	virtual smtutil::Expression constantValue(size_t _value) const; | ||||||
|  | 	virtual smtutil::Expression literalValue(Literal const& _literal) const; | ||||||
|  | 	virtual smtutil::Expression unsignedToSigned(smtutil::Expression _value); | ||||||
|  | 	virtual smtutil::Expression signedToUnsigned(smtutil::Expression _value); | ||||||
|  | 	virtual smtutil::Expression wrap(smtutil::Expression _value); | ||||||
|  | 
 | ||||||
|  | 	Dialect const& m_dialect; | ||||||
|  | 	std::set<YulString> const& m_ssaVariables; | ||||||
|  | 	std::unique_ptr<smtutil::SolverInterface> m_solver; | ||||||
|  | 	std::map<YulString, smtutil::Expression> m_variables; | ||||||
|  | 
 | ||||||
|  | 	size_t m_varCounter = 0; | ||||||
|  | }; | ||||||
|  | 
 | ||||||
|  | } | ||||||
| @ -41,6 +41,7 @@ | |||||||
| #include <libyul/optimiser/ForLoopConditionOutOfBody.h> | #include <libyul/optimiser/ForLoopConditionOutOfBody.h> | ||||||
| #include <libyul/optimiser/ForLoopInitRewriter.h> | #include <libyul/optimiser/ForLoopInitRewriter.h> | ||||||
| #include <libyul/optimiser/ForLoopConditionIntoBody.h> | #include <libyul/optimiser/ForLoopConditionIntoBody.h> | ||||||
|  | #include <libyul/optimiser/ReasoningBasedSimplifier.h> | ||||||
| #include <libyul/optimiser/Rematerialiser.h> | #include <libyul/optimiser/Rematerialiser.h> | ||||||
| #include <libyul/optimiser/UnusedFunctionParameterPruner.h> | #include <libyul/optimiser/UnusedFunctionParameterPruner.h> | ||||||
| #include <libyul/optimiser/UnusedPruner.h> | #include <libyul/optimiser/UnusedPruner.h> | ||||||
| @ -184,6 +185,7 @@ map<string, unique_ptr<OptimiserStep>> const& OptimiserSuite::allSteps() | |||||||
| 			LoopInvariantCodeMotion, | 			LoopInvariantCodeMotion, | ||||||
| 			NameSimplifier, | 			NameSimplifier, | ||||||
| 			RedundantAssignEliminator, | 			RedundantAssignEliminator, | ||||||
|  | 			ReasoningBasedSimplifier, | ||||||
| 			Rematerialiser, | 			Rematerialiser, | ||||||
| 			SSAReverser, | 			SSAReverser, | ||||||
| 			SSATransform, | 			SSATransform, | ||||||
| @ -221,6 +223,7 @@ map<string, char> const& OptimiserSuite::stepNameToAbbreviationMap() | |||||||
| 		{LoadResolver::name,                  'L'}, | 		{LoadResolver::name,                  'L'}, | ||||||
| 		{LoopInvariantCodeMotion::name,       'M'}, | 		{LoopInvariantCodeMotion::name,       'M'}, | ||||||
| 		{NameSimplifier::name,                'N'}, | 		{NameSimplifier::name,                'N'}, | ||||||
|  | 		{ReasoningBasedSimplifier::name,      'R'}, | ||||||
| 		{RedundantAssignEliminator::name,     'r'}, | 		{RedundantAssignEliminator::name,     'r'}, | ||||||
| 		{Rematerialiser::name,                'm'}, | 		{Rematerialiser::name,                'm'}, | ||||||
| 		{SSAReverser::name,                   'V'}, | 		{SSAReverser::name,                   'V'}, | ||||||
| @ -266,6 +269,7 @@ void OptimiserSuite::validateSequence(string const& _stepAbbreviations) | |||||||
| 			insideLoop = false; | 			insideLoop = false; | ||||||
| 			break; | 			break; | ||||||
| 		default: | 		default: | ||||||
|  | 		{ | ||||||
| 			yulAssert( | 			yulAssert( | ||||||
| 				string(NonStepAbbreviations).find(abbreviation) == string::npos, | 				string(NonStepAbbreviations).find(abbreviation) == string::npos, | ||||||
| 				"Unhandled syntactic element in the abbreviation sequence" | 				"Unhandled syntactic element in the abbreviation sequence" | ||||||
| @ -275,6 +279,14 @@ void OptimiserSuite::validateSequence(string const& _stepAbbreviations) | |||||||
| 				OptimizerException, | 				OptimizerException, | ||||||
| 				"'"s + abbreviation + "' is not a valid step abbreviation" | 				"'"s + abbreviation + "' is not a valid step abbreviation" | ||||||
| 			); | 			); | ||||||
|  | 			optional<string> invalid = allSteps().at(stepAbbreviationToNameMap().at(abbreviation))->invalidInCurrentEnvironment(); | ||||||
|  | 			assertThrow( | ||||||
|  | 				!invalid.has_value(), | ||||||
|  | 				OptimizerException, | ||||||
|  | 				"'"s + abbreviation + "' is invalid in the current environment: " + *invalid | ||||||
|  | 			); | ||||||
|  | 
 | ||||||
|  | 		} | ||||||
| 		} | 		} | ||||||
| 	assertThrow(!insideLoop, OptimizerException, "Unbalanced brackets"); | 	assertThrow(!insideLoop, OptimizerException, "Unbalanced brackets"); | ||||||
| } | } | ||||||
|  | |||||||
| @ -188,6 +188,7 @@ def examine_id_coverage(top_dir, source_id_to_file_names, new_ids_only=False): | |||||||
|     # Warning (1878): SPDX license identifier not provided in source file. .... |     # Warning (1878): SPDX license identifier not provided in source file. .... | ||||||
|     # Warning (3420): Source file does not specify required compiler version! |     # Warning (3420): Source file does not specify required compiler version! | ||||||
|     test_ids |= find_ids_in_cmdline_test_err(path.join(top_dir, "test", "cmdlineTests", "error_codes", "err")) |     test_ids |= find_ids_in_cmdline_test_err(path.join(top_dir, "test", "cmdlineTests", "error_codes", "err")) | ||||||
|  |     test_ids |= find_ids_in_cmdline_test_err(path.join(top_dir, "test", "cmdlineTests", "yul_unimplemented", "err")) | ||||||
| 
 | 
 | ||||||
|     # white list of ids which are not covered by tests |     # white list of ids which are not covered by tests | ||||||
|     white_ids = { |     white_ids = { | ||||||
|  | |||||||
| @ -124,6 +124,7 @@ function test_solc_behaviour() | |||||||
|         sed -i.bak -e '/^Warning (3805): This is a pre-release compiler version, please do not use it in production./d' "$stderr_path" |         sed -i.bak -e '/^Warning (3805): This is a pre-release compiler version, please do not use it in production./d' "$stderr_path" | ||||||
|         sed -i.bak -e 's/\(^[ ]*auxdata: \)0x[0-9a-f]*$/\1AUXDATA REMOVED/' "$stdout_path" |         sed -i.bak -e 's/\(^[ ]*auxdata: \)0x[0-9a-f]*$/\1AUXDATA REMOVED/' "$stdout_path" | ||||||
|         sed -i.bak -e 's/ Consider adding "pragma .*$//' "$stderr_path" |         sed -i.bak -e 's/ Consider adding "pragma .*$//' "$stderr_path" | ||||||
|  |         sed -i.bak -e 's/\(Unimplemented feature error: .* in \).*$/\1FILENAME REMOVED/' "$stderr_path" | ||||||
|         sed -i.bak -e 's/"version": "[^"]*"/"version": "VERSION REMOVED"/' "$stdout_path" |         sed -i.bak -e 's/"version": "[^"]*"/"version": "VERSION REMOVED"/' "$stdout_path" | ||||||
|         # Remove trailing empty lines. Needs a line break to make OSX sed happy. |         # Remove trailing empty lines. Needs a line break to make OSX sed happy. | ||||||
|         sed -i.bak -e '1{/^$/d |         sed -i.bak -e '1{/^$/d | ||||||
|  | |||||||
							
								
								
									
										1
									
								
								test/cmdlineTests/yul_unimplemented/args
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										1
									
								
								test/cmdlineTests/yul_unimplemented/args
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1 @@ | |||||||
|  | --ir --error-codes | ||||||
							
								
								
									
										5
									
								
								test/cmdlineTests/yul_unimplemented/err
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										5
									
								
								test/cmdlineTests/yul_unimplemented/err
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,5 @@ | |||||||
|  | Error (1834): Unimplemented feature error: setToZero for type t_struct$_S_$4_storage not yet implemented! in FILENAME REMOVED | ||||||
|  |  --> yul_unimplemented/input.sol:9:9: | ||||||
|  |   | | ||||||
|  | 9 |         delete str; | ||||||
|  |   |         ^^^^^^^^^^ | ||||||
							
								
								
									
										1
									
								
								test/cmdlineTests/yul_unimplemented/exit
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										1
									
								
								test/cmdlineTests/yul_unimplemented/exit
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1 @@ | |||||||
|  | 1 | ||||||
							
								
								
									
										11
									
								
								test/cmdlineTests/yul_unimplemented/input.sol
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										11
									
								
								test/cmdlineTests/yul_unimplemented/input.sol
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,11 @@ | |||||||
|  | // SPDX-License-Identifier: GPL-3.0 | ||||||
|  | pragma solidity >=0.0; | ||||||
|  | contract test { | ||||||
|  |     struct S { | ||||||
|  |         uint x; | ||||||
|  |     } | ||||||
|  |     S str; | ||||||
|  |     constructor() { | ||||||
|  |         delete str; | ||||||
|  |     } | ||||||
|  | } | ||||||
| @ -54,6 +54,12 @@ bytes SolidityExecutionFramework::multiSourceCompileContract( | |||||||
| 	m_compiler.setRevertStringBehaviour(m_revertStrings); | 	m_compiler.setRevertStringBehaviour(m_revertStrings); | ||||||
| 	if (!m_compiler.compile()) | 	if (!m_compiler.compile()) | ||||||
| 	{ | 	{ | ||||||
|  | 		// The testing framework expects an exception for
 | ||||||
|  | 		// "unimplemented" yul IR generation.
 | ||||||
|  | 		if (m_compileViaYul) | ||||||
|  | 			for (auto const& error: m_compiler.errors()) | ||||||
|  | 				if (error->type() == langutil::Error::Type::CodeGenerationError) | ||||||
|  | 					BOOST_THROW_EXCEPTION(*error); | ||||||
| 		langutil::SourceReferenceFormatter formatter(std::cerr); | 		langutil::SourceReferenceFormatter formatter(std::cerr); | ||||||
| 
 | 
 | ||||||
| 		for (auto const& error: m_compiler.errors()) | 		for (auto const& error: m_compiler.errors()) | ||||||
|  | |||||||
							
								
								
									
										14
									
								
								test/libsolidity/semanticTests/arithmetics/signed_mod.sol
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										14
									
								
								test/libsolidity/semanticTests/arithmetics/signed_mod.sol
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,14 @@ | |||||||
|  | contract C { | ||||||
|  |     function f(int a, int b) public pure returns (int) { | ||||||
|  |         return a % b; | ||||||
|  |     } | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | // ==== | ||||||
|  | // compileViaYul: also | ||||||
|  | // ---- | ||||||
|  | // f(int256,int256): 7, 5 -> 2 | ||||||
|  | // f(int256,int256): 7, -5 -> 2 | ||||||
|  | // f(int256,int256): -7, 5 -> -2 | ||||||
|  | // f(int256,int256): -7, 5 -> -2 | ||||||
|  | // f(int256,int256): -5, -5 -> 0 | ||||||
| @ -0,0 +1,23 @@ | |||||||
|  | pragma experimental ABIEncoderV2; | ||||||
|  | 
 | ||||||
|  | contract C { | ||||||
|  |     struct S { | ||||||
|  |         bool[] b; | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     function f() public returns (uint256, bool[][2] memory, S[2] memory, uint256) { | ||||||
|  |         return ( | ||||||
|  |             5, | ||||||
|  |             [new bool[](1), new bool[](2)], | ||||||
|  |             [S(new bool[](2)), S(new bool[](5))], | ||||||
|  |             6 | ||||||
|  |         ); | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     function g() public returns (uint256, uint256) { | ||||||
|  |         (uint256 a, , , uint256 b) = this.f(); | ||||||
|  |         return (a, b); | ||||||
|  |     } | ||||||
|  | } | ||||||
|  | // ---- | ||||||
|  | // g() -> 5, 6 | ||||||
							
								
								
									
										28
									
								
								test/libsolidity/smtCheckerTests/special/event.sol
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										28
									
								
								test/libsolidity/smtCheckerTests/special/event.sol
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,28 @@ | |||||||
|  | pragma experimental SMTChecker; | ||||||
|  | 
 | ||||||
|  | contract C { | ||||||
|  | 	event Nudge(); | ||||||
|  | 	event SomeArgs(uint, uint); | ||||||
|  | 	event Caller(address, uint); | ||||||
|  | 	function f() payable external { | ||||||
|  | 		emit Nudge(); | ||||||
|  | 		emit SomeArgs(134, 567); | ||||||
|  | 		emit Caller(msg.sender, msg.value); | ||||||
|  | 	} | ||||||
|  | 	function g_data() pure internal returns (uint) { | ||||||
|  | 		assert(true); | ||||||
|  | 	} | ||||||
|  | 	function g() external { | ||||||
|  | 		emit SomeArgs(g_data(), g_data()); | ||||||
|  | 	} | ||||||
|  | 	bool x = true; | ||||||
|  | 	function h_data() view internal returns (uint) { | ||||||
|  | 		assert(x); | ||||||
|  | 	} | ||||||
|  | 	function h() external { | ||||||
|  | 		x = false; | ||||||
|  | 		emit SomeArgs(h_data(), h_data()); | ||||||
|  | 	} | ||||||
|  | } | ||||||
|  | // ---- | ||||||
|  | // Warning 6328: (440-449): Assertion violation happens here. | ||||||
							
								
								
									
										39
									
								
								test/libsolidity/smtCheckerTests/special/log.sol
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										39
									
								
								test/libsolidity/smtCheckerTests/special/log.sol
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,39 @@ | |||||||
|  | pragma experimental SMTChecker; | ||||||
|  | 
 | ||||||
|  | contract C { | ||||||
|  | 	function f() external { | ||||||
|  | 		bytes32 t1 = bytes32(uint256(0x1234)); | ||||||
|  | 		log0(t1); | ||||||
|  | 		log1(t1, t1); | ||||||
|  | 		log2(t1, t1, t1); | ||||||
|  | 		log3(t1, t1, t1, t1); | ||||||
|  | 		log4(t1, t1, t1, t1, t1); | ||||||
|  | 	} | ||||||
|  | 	function g_data() pure internal returns (bytes32) { | ||||||
|  | 		assert(true); | ||||||
|  | 		return bytes32(uint256(0x5678)); | ||||||
|  | 	} | ||||||
|  | 	function g() external { | ||||||
|  | 		// To test that the function call is actually visited. | ||||||
|  | 		log0(g_data()); | ||||||
|  | 		log1(g_data(), g_data()); | ||||||
|  | 		log2(g_data(), g_data(), g_data()); | ||||||
|  | 		log3(g_data(), g_data(), g_data(), g_data()); | ||||||
|  | 		log4(g_data(), g_data(), g_data(), g_data(), g_data()); | ||||||
|  | 	} | ||||||
|  | 	bool x = true; | ||||||
|  | 	function h_data() view internal returns (bytes32) { | ||||||
|  | 		assert(x); | ||||||
|  | 	} | ||||||
|  | 	function h() external { | ||||||
|  | 		// To test that the function call is actually visited. | ||||||
|  | 		x = false; | ||||||
|  | 		log0(h_data()); | ||||||
|  | 		log1(h_data(), h_data()); | ||||||
|  | 		log2(h_data(), h_data(), h_data()); | ||||||
|  | 		log3(h_data(), h_data(), h_data(), h_data()); | ||||||
|  | 		log4(h_data(), h_data(), h_data(), h_data(), h_data()); | ||||||
|  | 	} | ||||||
|  | } | ||||||
|  | // ---- | ||||||
|  | // Warning 6328: (668-677): Assertion violation happens here. | ||||||
| @ -0,0 +1,9 @@ | |||||||
|  | pragma experimental ABIEncoderV2; | ||||||
|  | 
 | ||||||
|  | contract C { | ||||||
|  |     function f() public pure returns(string[5] calldata) { | ||||||
|  |         return ["h", "e", "l", "l", "o"]; | ||||||
|  |     } | ||||||
|  | } | ||||||
|  | // ---- | ||||||
|  | // TypeError 6359: (122-147): Return argument type string memory[5] memory is not implicitly convertible to expected type (type of first return variable) string calldata[5] calldata. | ||||||
| @ -0,0 +1,17 @@ | |||||||
|  | contract C { | ||||||
|  |     function f1() public pure returns(string calldata) { | ||||||
|  |         return "hello"; | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     function f2() public pure returns(string calldata) { | ||||||
|  |         return unicode"hello"; | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     function f3() public pure returns(bytes calldata) { | ||||||
|  |         return hex"68656c6c6f"; | ||||||
|  |     } | ||||||
|  | } | ||||||
|  | // ---- | ||||||
|  | // TypeError 6359: (85-92): Return argument type literal_string "hello" is not implicitly convertible to expected type (type of first return variable) string calldata. | ||||||
|  | // TypeError 6359: (173-187): Return argument type literal_string "hello" is not implicitly convertible to expected type (type of first return variable) string calldata. | ||||||
|  | // TypeError 6359: (267-282): Return argument type literal_string "hello" is not implicitly convertible to expected type (type of first return variable) bytes calldata. | ||||||
| @ -0,0 +1,14 @@ | |||||||
|  | contract C { | ||||||
|  |     function g(string calldata _s) public {} | ||||||
|  |     function h(bytes calldata _b) public {} | ||||||
|  | 
 | ||||||
|  |     function f() public { | ||||||
|  |         g("hello"); | ||||||
|  |         g(unicode"hello"); | ||||||
|  |         h(hex"68656c6c6f"); | ||||||
|  |     } | ||||||
|  | } | ||||||
|  | // ---- | ||||||
|  | // TypeError 9553: (139-146): Invalid type for argument in function call. Invalid implicit conversion from literal_string "hello" to string calldata requested. | ||||||
|  | // TypeError 9553: (159-173): Invalid type for argument in function call. Invalid implicit conversion from literal_string "hello" to string calldata requested. | ||||||
|  | // TypeError 9553: (186-201): Invalid type for argument in function call. Invalid implicit conversion from literal_string "hello" to bytes calldata requested. | ||||||
| @ -53,6 +53,7 @@ | |||||||
| #include <libyul/optimiser/UnusedPruner.h> | #include <libyul/optimiser/UnusedPruner.h> | ||||||
| #include <libyul/optimiser/ExpressionJoiner.h> | #include <libyul/optimiser/ExpressionJoiner.h> | ||||||
| #include <libyul/optimiser/OptimiserStep.h> | #include <libyul/optimiser/OptimiserStep.h> | ||||||
|  | #include <libyul/optimiser/ReasoningBasedSimplifier.h> | ||||||
| #include <libyul/optimiser/SSAReverser.h> | #include <libyul/optimiser/SSAReverser.h> | ||||||
| #include <libyul/optimiser/SSATransform.h> | #include <libyul/optimiser/SSATransform.h> | ||||||
| #include <libyul/optimiser/Semantics.h> | #include <libyul/optimiser/Semantics.h> | ||||||
| @ -102,6 +103,9 @@ YulOptimizerTest::YulOptimizerTest(string const& _filename): | |||||||
| 		BOOST_THROW_EXCEPTION(runtime_error("Filename path has to contain a directory: \"" + _filename + "\".")); | 		BOOST_THROW_EXCEPTION(runtime_error("Filename path has to contain a directory: \"" + _filename + "\".")); | ||||||
| 	m_optimizerStep = std::prev(std::prev(path.end()))->string(); | 	m_optimizerStep = std::prev(std::prev(path.end()))->string(); | ||||||
| 
 | 
 | ||||||
|  | 	if (m_optimizerStep == "reasoningBasedSimplifier" && solidity::test::CommonOptions::get().disableSMT) | ||||||
|  | 		m_shouldRun = false; | ||||||
|  | 
 | ||||||
| 	m_source = m_reader.source(); | 	m_source = m_reader.source(); | ||||||
| 
 | 
 | ||||||
| 	auto dialectName = m_reader.stringSetting("dialect", "evm"); | 	auto dialectName = m_reader.stringSetting("dialect", "evm"); | ||||||
| @ -320,6 +324,11 @@ TestCase::TestResult YulOptimizerTest::run(ostream& _stream, string const& _line | |||||||
| 		LiteralRematerialiser::run(*m_context, *m_object->code); | 		LiteralRematerialiser::run(*m_context, *m_object->code); | ||||||
| 		StructuralSimplifier::run(*m_context, *m_object->code); | 		StructuralSimplifier::run(*m_context, *m_object->code); | ||||||
| 	} | 	} | ||||||
|  | 	else if (m_optimizerStep == "reasoningBasedSimplifier") | ||||||
|  | 	{ | ||||||
|  | 		disambiguate(); | ||||||
|  | 		ReasoningBasedSimplifier::run(*m_context, *m_object->code); | ||||||
|  | 	} | ||||||
| 	else if (m_optimizerStep == "equivalentFunctionCombiner") | 	else if (m_optimizerStep == "equivalentFunctionCombiner") | ||||||
| 	{ | 	{ | ||||||
| 		disambiguate(); | 		disambiguate(); | ||||||
|  | |||||||
| @ -0,0 +1,31 @@ | |||||||
|  | { | ||||||
|  |     let x := calldataload(0) | ||||||
|  |     let y := calldataload(32) | ||||||
|  |     let z := calldataload(64) | ||||||
|  |     let result := addmod(x, y, z) | ||||||
|  | 
 | ||||||
|  |     // should be zero | ||||||
|  |     if gt(result, z) { sstore(0, 1) } | ||||||
|  | 
 | ||||||
|  |     // addmod is equal to mod of sum for small numbers | ||||||
|  |     if and(and(lt(x, 1000), lt(y, 1000)), lt(z, 1000)) { | ||||||
|  |         if eq(result, mod(add(x, y), z)) { sstore(0, 9) } | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     // but not in general | ||||||
|  |     if and(and(gt(x, sub(0, 5)), gt(y, sub(0, 2))), eq(z, 3)) { | ||||||
|  |         if eq(result, mod(add(x, y), z)) { sstore(0, 5) } | ||||||
|  |     } | ||||||
|  | } | ||||||
|  | // ---- | ||||||
|  | // step: reasoningBasedSimplifier | ||||||
|  | // | ||||||
|  | // { | ||||||
|  | //     let x := calldataload(0) | ||||||
|  | //     let y := calldataload(32) | ||||||
|  | //     let z := calldataload(64) | ||||||
|  | //     let result := addmod(x, y, z) | ||||||
|  | //     if 0 { } | ||||||
|  | //     if and(and(lt(x, 1000), lt(y, 1000)), lt(z, 1000)) { if 1 { sstore(0, 9) } } | ||||||
|  | //     if and(and(gt(x, sub(0, 5)), gt(y, sub(0, 2))), eq(z, 3)) { if 0 { } } | ||||||
|  | // } | ||||||
| @ -0,0 +1,13 @@ | |||||||
|  | { | ||||||
|  |     let x := 7 | ||||||
|  |     let y := 8 | ||||||
|  |     if eq(add(x, y), 15) { } | ||||||
|  | } | ||||||
|  | // ---- | ||||||
|  | // step: reasoningBasedSimplifier | ||||||
|  | // | ||||||
|  | // { | ||||||
|  | //     let x := 7 | ||||||
|  | //     let y := 8 | ||||||
|  | //     if 1 { } | ||||||
|  | // } | ||||||
| @ -0,0 +1,19 @@ | |||||||
|  | { | ||||||
|  | 	function f() -> z | ||||||
|  | 	{ | ||||||
|  | 		z := 15 | ||||||
|  | 	} | ||||||
|  | 	let x := 7 | ||||||
|  | 	let y := 8 | ||||||
|  | 	if eq(add(x, y), f()) { } | ||||||
|  | } | ||||||
|  | // ---- | ||||||
|  | // step: reasoningBasedSimplifier | ||||||
|  | // | ||||||
|  | // { | ||||||
|  | //     function f() -> z | ||||||
|  | //     { z := 15 } | ||||||
|  | //     let x := 7 | ||||||
|  | //     let y := 8 | ||||||
|  | //     if eq(add(x, y), f()) { } | ||||||
|  | // } | ||||||
| @ -0,0 +1,23 @@ | |||||||
|  | { | ||||||
|  | 	function f() -> z | ||||||
|  | 	{ | ||||||
|  | 		sstore(1, 15) | ||||||
|  | 		z := 15 | ||||||
|  | 	} | ||||||
|  | 	let x := 7 | ||||||
|  | 	let y := 8 | ||||||
|  | 	if eq(add(x, y), f()) { } | ||||||
|  | } | ||||||
|  | // ---- | ||||||
|  | // step: reasoningBasedSimplifier | ||||||
|  | // | ||||||
|  | // { | ||||||
|  | //     function f() -> z | ||||||
|  | //     { | ||||||
|  | //         sstore(1, 15) | ||||||
|  | //         z := 15 | ||||||
|  | //     } | ||||||
|  | //     let x := 7 | ||||||
|  | //     let y := 8 | ||||||
|  | //     if eq(add(x, y), f()) { } | ||||||
|  | // } | ||||||
| @ -0,0 +1,32 @@ | |||||||
|  | { | ||||||
|  |     let vloc_x := calldataload(0) | ||||||
|  |     let vloc_y := calldataload(1) | ||||||
|  |     if lt(vloc_x, shl(100, 1)) { | ||||||
|  |         if lt(vloc_y, shl(100, 1)) { | ||||||
|  |             if iszero(and(iszero(iszero(vloc_x)), gt(vloc_y, div(not(0), vloc_x)))) { | ||||||
|  |                 let vloc := mul(vloc_x, vloc_y) | ||||||
|  |                 sstore(0, vloc) | ||||||
|  |             } | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  | } | ||||||
|  | // ==== | ||||||
|  | // EVMVersion: >=constantinople | ||||||
|  | // ---- | ||||||
|  | // step: reasoningBasedSimplifier | ||||||
|  | // | ||||||
|  | // { | ||||||
|  | //     let vloc_x := calldataload(0) | ||||||
|  | //     let vloc_y := calldataload(1) | ||||||
|  | //     if lt(vloc_x, shl(100, 1)) | ||||||
|  | //     { | ||||||
|  | //         if lt(vloc_y, shl(100, 1)) | ||||||
|  | //         { | ||||||
|  | //             if 1 | ||||||
|  | //             { | ||||||
|  | //                 let vloc := mul(vloc_x, vloc_y) | ||||||
|  | //                 sstore(0, vloc) | ||||||
|  | //             } | ||||||
|  | //         } | ||||||
|  | //     } | ||||||
|  | // } | ||||||
| @ -0,0 +1,31 @@ | |||||||
|  | { | ||||||
|  |     let x := calldataload(0) | ||||||
|  |     let y := calldataload(32) | ||||||
|  |     let z := calldataload(64) | ||||||
|  |     let result := mulmod(x, y, z) | ||||||
|  | 
 | ||||||
|  |     // should be zero | ||||||
|  |     if gt(result, z) { sstore(0, 1) } | ||||||
|  | 
 | ||||||
|  |     // mulmod is equal to mod of product for small numbers | ||||||
|  |     if and(and(lt(x, 1000), lt(y, 1000)), lt(z, 1000)) { | ||||||
|  |         if eq(result, mod(mul(x, y), z)) { sstore(0, 9) } | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     // but not in general | ||||||
|  |     if and(and(gt(x, sub(0, 5)), eq(y, 2)), eq(z, 3)) { | ||||||
|  |         if eq(result, mod(mul(x, y), z)) { sstore(0, 5) } | ||||||
|  |     } | ||||||
|  | } | ||||||
|  | // ---- | ||||||
|  | // step: reasoningBasedSimplifier | ||||||
|  | // | ||||||
|  | // { | ||||||
|  | //     let x := calldataload(0) | ||||||
|  | //     let y := calldataload(32) | ||||||
|  | //     let z := calldataload(64) | ||||||
|  | //     let result := mulmod(x, y, z) | ||||||
|  | //     if 0 { } | ||||||
|  | //     if and(and(lt(x, 1000), lt(y, 1000)), lt(z, 1000)) { if 1 { sstore(0, 9) } } | ||||||
|  | //     if and(and(gt(x, sub(0, 5)), eq(y, 2)), eq(z, 3)) { if 0 { } } | ||||||
|  | // } | ||||||
| @ -0,0 +1,16 @@ | |||||||
|  | { | ||||||
|  |     let x := sub(0, 7) | ||||||
|  |     let y := 2 | ||||||
|  |     // (-7)/2 == -3 on the evm | ||||||
|  |     if iszero(add(sdiv(x, y), 3)) { } | ||||||
|  |     if iszero(add(sdiv(x, y), 4)) { } | ||||||
|  | } | ||||||
|  | // ---- | ||||||
|  | // step: reasoningBasedSimplifier | ||||||
|  | // | ||||||
|  | // { | ||||||
|  | //     let x := sub(0, 7) | ||||||
|  | //     let y := 2 | ||||||
|  | //     if 1 { } | ||||||
|  | //     if 0 { } | ||||||
|  | // } | ||||||
| @ -0,0 +1,26 @@ | |||||||
|  | { | ||||||
|  |     let x := calldataload(2) | ||||||
|  |     let t := lt(x, 20) | ||||||
|  |     if t { | ||||||
|  |         if lt(x, 21) { } | ||||||
|  |         if lt(x, 20) { } | ||||||
|  |         if lt(x, 19) { } | ||||||
|  |         if gt(x, 20) { } | ||||||
|  |         if iszero(gt(x, 20)) { } | ||||||
|  |     } | ||||||
|  | } | ||||||
|  | // ---- | ||||||
|  | // step: reasoningBasedSimplifier | ||||||
|  | // | ||||||
|  | // { | ||||||
|  | //     let x := calldataload(2) | ||||||
|  | //     let t := lt(x, 20) | ||||||
|  | //     if t | ||||||
|  | //     { | ||||||
|  | //         if 1 { } | ||||||
|  | //         if 1 { } | ||||||
|  | //         if lt(x, 19) { } | ||||||
|  | //         if 0 { } | ||||||
|  | //         if 1 { } | ||||||
|  | //     } | ||||||
|  | // } | ||||||
| @ -0,0 +1,31 @@ | |||||||
|  | { | ||||||
|  |     let y := calldataload(0) | ||||||
|  |     let t := calldataload(32) | ||||||
|  | 
 | ||||||
|  |     if sgt(sub(y, 1), y) { | ||||||
|  |         // y - 1 > y, i.e. y is the most negative value | ||||||
|  |         if eq(sdiv(y, sub(0, 1)), y) { | ||||||
|  |             // should be true: y / -1 == y | ||||||
|  |             sstore(0, 7) | ||||||
|  |         } | ||||||
|  |         if iszero(eq(y, t)) { | ||||||
|  |             // t is not the most negative value | ||||||
|  |             if eq(sdiv(t, sub(0, 1)), sub(0, t)) { | ||||||
|  |                 // should be true: t / -1 = 0 - t | ||||||
|  |                 sstore(1, 7) | ||||||
|  |             } | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  | } | ||||||
|  | // ---- | ||||||
|  | // step: reasoningBasedSimplifier | ||||||
|  | // | ||||||
|  | // { | ||||||
|  | //     let y := calldataload(0) | ||||||
|  | //     let t := calldataload(32) | ||||||
|  | //     if sgt(sub(y, 1), y) | ||||||
|  | //     { | ||||||
|  | //         if 1 { sstore(0, 7) } | ||||||
|  | //         if iszero(eq(y, t)) { if 1 { sstore(1, 7) } } | ||||||
|  | //     } | ||||||
|  | // } | ||||||
| @ -0,0 +1,53 @@ | |||||||
|  | { | ||||||
|  |     // 7 % 5 == 2 | ||||||
|  |     // 7 % -5 == 2 | ||||||
|  |     // -7 % 5 == -2 | ||||||
|  |     // -7 % -5 == -2 | ||||||
|  |     // -5 % -5 == 0 | ||||||
|  |     let x := calldataload(0) | ||||||
|  |     let y := calldataload(32) | ||||||
|  |     let result := smod(x, y) | ||||||
|  |     if eq(x, 7) { | ||||||
|  |         if eq(y, 5) { | ||||||
|  |             if eq(result, 2) { sstore(0, 7)} | ||||||
|  |         } | ||||||
|  |         if eq(y, sub(0, 5)) { | ||||||
|  |             if eq(result, 2) { sstore(0, 7)} | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  |     if eq(x, sub(0, 7)) { | ||||||
|  |         if eq(y, 5) { | ||||||
|  |             if eq(result, sub(0, 2)) { sstore(0, 7)} | ||||||
|  |         } | ||||||
|  |         if eq(y, sub(0, 5)) { | ||||||
|  |             if eq(result, sub(0, 2)) { sstore(0, 7)} | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  |     if eq(x, sub(0, 5)) { | ||||||
|  |         if eq(y, sub(0, 5)) { | ||||||
|  |             if eq(result, 0) { sstore(0, 7)} | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  | } | ||||||
|  | // ---- | ||||||
|  | // step: reasoningBasedSimplifier | ||||||
|  | // | ||||||
|  | // { | ||||||
|  | //     let x := calldataload(0) | ||||||
|  | //     let y := calldataload(32) | ||||||
|  | //     let result := smod(x, y) | ||||||
|  | //     if eq(x, 7) | ||||||
|  | //     { | ||||||
|  | //         if eq(y, 5) { if 1 { sstore(0, 7) } } | ||||||
|  | //         if eq(y, sub(0, 5)) { if 1 { sstore(0, 7) } } | ||||||
|  | //     } | ||||||
|  | //     if eq(x, sub(0, 7)) | ||||||
|  | //     { | ||||||
|  | //         if eq(y, 5) { if 1 { sstore(0, 7) } } | ||||||
|  | //         if eq(y, sub(0, 5)) { if 1 { sstore(0, 7) } } | ||||||
|  | //     } | ||||||
|  | //     if eq(x, sub(0, 5)) | ||||||
|  | //     { | ||||||
|  | //         if eq(y, sub(0, 5)) { if 1 { sstore(0, 7) } } | ||||||
|  | //     } | ||||||
|  | // } | ||||||
| @ -0,0 +1,5 @@ | |||||||
|  | { } | ||||||
|  | // ---- | ||||||
|  | // step: reasoningBasedSimplifier | ||||||
|  | // | ||||||
|  | // { } | ||||||
| @ -0,0 +1,15 @@ | |||||||
|  | { | ||||||
|  |     let x := 7 | ||||||
|  |     let y := 8 | ||||||
|  |     if gt(sub(x, y), 20) { } | ||||||
|  |     if eq(sub(x, y), 0xffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff) {} | ||||||
|  | } | ||||||
|  | // ---- | ||||||
|  | // step: reasoningBasedSimplifier | ||||||
|  | // | ||||||
|  | // { | ||||||
|  | //     let x := 7 | ||||||
|  | //     let y := 8 | ||||||
|  | //     if 1 { } | ||||||
|  | //     if 1 { } | ||||||
|  | // } | ||||||
| @ -36,6 +36,7 @@ | |||||||
| #include <libyul/optimiser/StackCompressor.h> | #include <libyul/optimiser/StackCompressor.h> | ||||||
| #include <libyul/optimiser/VarNameCleaner.h> | #include <libyul/optimiser/VarNameCleaner.h> | ||||||
| #include <libyul/optimiser/Suite.h> | #include <libyul/optimiser/Suite.h> | ||||||
|  | #include <libyul/optimiser/ReasoningBasedSimplifier.h> | ||||||
| 
 | 
 | ||||||
| #include <libyul/backends/evm/EVMDialect.h> | #include <libyul/backends/evm/EVMDialect.h> | ||||||
| 
 | 
 | ||||||
| @ -157,7 +158,7 @@ public: | |||||||
| 			map<char, string> const& extraOptions = { | 			map<char, string> const& extraOptions = { | ||||||
| 				{'#', "quit"}, | 				{'#', "quit"}, | ||||||
| 				{',', "VarNameCleaner"}, | 				{',', "VarNameCleaner"}, | ||||||
| 				{';', "StackCompressor"}, | 				{';', "StackCompressor"} | ||||||
| 			}; | 			}; | ||||||
| 
 | 
 | ||||||
| 			printUsageBanner(abbreviationMap, extraOptions, 4); | 			printUsageBanner(abbreviationMap, extraOptions, 4); | ||||||
|  | |||||||
| @ -138,7 +138,7 @@ BOOST_AUTO_TEST_CASE(output_operator_should_create_concise_and_unambiguous_strin | |||||||
| 
 | 
 | ||||||
| 	BOOST_TEST(chromosome.length() == allSteps.size()); | 	BOOST_TEST(chromosome.length() == allSteps.size()); | ||||||
| 	BOOST_TEST(chromosome.optimisationSteps() == allSteps); | 	BOOST_TEST(chromosome.optimisationSteps() == allSteps); | ||||||
| 	BOOST_TEST(toString(chromosome) == "flcCUnDvejsxIOoighTLMNrmVatpud"); | 	BOOST_TEST(toString(chromosome) == "flcCUnDvejsxIOoighTLMNRrmVatpud"); | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| BOOST_AUTO_TEST_CASE(optimisationSteps_should_translate_chromosomes_genes_to_optimisation_step_names) | BOOST_AUTO_TEST_CASE(optimisationSteps_should_translate_chromosomes_genes_to_optimisation_step_names) | ||||||
|  | |||||||
		Loading…
	
		Reference in New Issue
	
	Block a user