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: | ||||
|  * Export compiler-generated utility sources via standard-json or combined-json. | ||||
|  * SMTChecker: Support events and low-level logs. | ||||
|  * SMTChecker: Support ``revert()``. | ||||
|  * SMTChecker: Support shifts. | ||||
|  * SMTChecker: Support structs. | ||||
| @ -16,13 +17,16 @@ Compiler Features: | ||||
|  * Yul Optimizer: Prune unused parameters in functions. | ||||
|  * Yul Optimizer: Inline into functions further down in the call graph first. | ||||
|  * Yul Optimizer: Try to simplify function names. | ||||
|  * Yul IR Generator: Report source locations related to unimplemented features. | ||||
| 
 | ||||
| 
 | ||||
| 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: Correct the warning for homonymous, but not shadowing declarations. | ||||
|  * 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 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. | ||||
| 
 | ||||
| ### 0.7.1 (2020-09-02) | ||||
|  | ||||
| @ -163,8 +163,13 @@ Homebrew formula directly from Github. | ||||
| View | ||||
| `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 | ||||
| specific commit of ``solidity.rb``. | ||||
| Copy the commit hash of the version you want and check it out on your machine. | ||||
| 
 | ||||
| .. 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``: | ||||
| 
 | ||||
| @ -172,7 +177,7 @@ Install it using ``brew``: | ||||
| 
 | ||||
|     brew unlink solidity | ||||
|     # 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. | ||||
| After the overlay is setup, ``solc`` can be installed in x86_64 architectures by: | ||||
|  | ||||
| @ -1110,6 +1110,7 @@ Abbreviation Full name | ||||
| ``L``        ``LoadResolver`` | ||||
| ``M``        ``LoopInvariantCodeMotion`` | ||||
| ``r``        ``RedundantAssignEliminator`` | ||||
| ``R``        ``ReasoningBasedSimplifier`` - highly experimental | ||||
| ``m``        ``Rematerialiser`` | ||||
| ``V``        ``SSAReverser`` | ||||
| ``a``        ``SSATransform`` | ||||
| @ -1121,6 +1122,10 @@ Abbreviation Full name | ||||
| 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. | ||||
| 
 | ||||
| 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: | ||||
| 
 | ||||
|  | ||||
| @ -9,6 +9,7 @@ set(sources | ||||
| 	SolverInterface.h | ||||
| 	Sorts.cpp | ||||
| 	Sorts.h | ||||
| 	Helpers.h | ||||
| ) | ||||
| 
 | ||||
| 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/Predicate.cpp | ||||
| 	formal/Predicate.h | ||||
| 	formal/PredicateSort.cpp | ||||
| 	formal/PredicateSort.h | ||||
| 	formal/SMTEncoder.cpp | ||||
| 	formal/SMTEncoder.h | ||||
| 	formal/SSAVariable.cpp | ||||
|  | ||||
| @ -1408,6 +1408,7 @@ BoolResult StringLiteralType::isImplicitlyConvertibleTo(Type const& _convertTo) | ||||
| 		return static_cast<size_t>(fixedBytes->numBytes()) >= m_value.size(); | ||||
| 	else if (auto arrayType = dynamic_cast<ArrayType const*>(&_convertTo)) | ||||
| 		return | ||||
| 			arrayType->location() != DataLocation::CallData && | ||||
| 			arrayType->isByteArray() && | ||||
| 			!(arrayType->dataStoredIn(DataLocation::Storage) && arrayType->isPointer()) && | ||||
| 			!(arrayType->isString() && !util::validateUTF8(value())); | ||||
| @ -3010,7 +3011,7 @@ TypePointers FunctionType::returnParameterTypesWithoutDynamicTypes() const | ||||
| 		m_kind == Kind::BareStaticCall | ||||
| 	) | ||||
| 		for (auto& param: returnParameterTypes) | ||||
| 			if (param->isDynamicallySized() && !param->dataStoredIn(DataLocation::Storage)) | ||||
| 			if (param->isDynamicallyEncoded() && !param->dataStoredIn(DataLocation::Storage)) | ||||
| 				param = TypeProvider::inaccessibleDynamic(); | ||||
| 
 | ||||
| 	return returnParameterTypes; | ||||
|  | ||||
| @ -164,7 +164,7 @@ string IRGenerator::generate( | ||||
| string IRGenerator::generate(Block const& _block) | ||||
| { | ||||
| 	IRGeneratorForStatements generator(m_context, m_utils); | ||||
| 	_block.accept(generator); | ||||
| 	generator.generate(_block); | ||||
| 	return generator.code(); | ||||
| } | ||||
| 
 | ||||
|  | ||||
| @ -38,6 +38,8 @@ | ||||
| #include <libyul/Dialect.h> | ||||
| #include <libyul/optimiser/ASTCopier.h> | ||||
| 
 | ||||
| #include <liblangutil/Exceptions.h> | ||||
| 
 | ||||
| #include <libsolutil/Whiskers.h> | ||||
| #include <libsolutil/StringUtils.h> | ||||
| #include <libsolutil/Keccak256.h> | ||||
| @ -152,8 +154,26 @@ string IRGeneratorForStatements::code() const | ||||
| 	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) | ||||
| { | ||||
| 	try | ||||
| 	{ | ||||
| 		setLocation(_varDecl); | ||||
| 
 | ||||
| 		solAssert(_varDecl.immutable() || m_context.isStateVariable(_varDecl), "Must be immutable or a state variable."); | ||||
| 		solAssert(!_varDecl.isConstant(), ""); | ||||
| 		if (!_varDecl.value()) | ||||
| @ -170,9 +190,20 @@ void IRGeneratorForStatements::initializeStateVar(VariableDeclaration const& _va | ||||
| 			*_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) | ||||
| { | ||||
| 	try | ||||
| 	{ | ||||
| 		setLocation(_varDecl); | ||||
| 
 | ||||
| 		solAssert(m_context.isLocalVariable(_varDecl), "Must be a local variable."); | ||||
| 
 | ||||
| 		auto const* type = _varDecl.type(); | ||||
| @ -183,17 +214,39 @@ void IRGeneratorForStatements::initializeLocalVar(VariableDeclaration const& _va | ||||
| 		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) | ||||
| { | ||||
| 	try | ||||
| 	{ | ||||
| 		setLocation(_expression); | ||||
| 
 | ||||
| 		_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) | ||||
| { | ||||
| 	try | ||||
| 	{ | ||||
| 		setLocation(_constant); | ||||
| 
 | ||||
| 		string functionName = IRNames::constantValueFunction(_constant); | ||||
| 		return m_context.functionCollector().createFunction(functionName, [&] { | ||||
| 			Whiskers templ(R"( | ||||
| @ -213,9 +266,18 @@ string IRGeneratorForStatements::constantValueFunction(VariableDeclaration const | ||||
| 			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) | ||||
| { | ||||
| 	setLocation(_varDeclStatement); | ||||
| 
 | ||||
| 	if (Expression const* expression = _varDeclStatement.initialValue()) | ||||
| 	{ | ||||
| 		if (_varDeclStatement.declarations().size() > 1) | ||||
| @ -249,14 +311,22 @@ bool IRGeneratorForStatements::visit(Conditional const& _conditional) | ||||
| { | ||||
| 	_conditional.condition().accept(*this); | ||||
| 
 | ||||
| 	setLocation(_conditional); | ||||
| 
 | ||||
| 	string condition = expressionAsType(_conditional.condition(), *TypeProvider::boolean()); | ||||
| 	declare(_conditional); | ||||
| 
 | ||||
| 	m_code << "switch " << condition << "\n" "case 0 {\n"; | ||||
| 
 | ||||
| 	_conditional.falseExpression().accept(*this); | ||||
| 	setLocation(_conditional); | ||||
| 
 | ||||
| 	assign(_conditional, _conditional.falseExpression()); | ||||
| 	m_code << "}\n" "default {\n"; | ||||
| 
 | ||||
| 	_conditional.trueExpression().accept(*this); | ||||
| 	setLocation(_conditional); | ||||
| 
 | ||||
| 	assign(_conditional, _conditional.trueExpression()); | ||||
| 	m_code << "}\n"; | ||||
| 
 | ||||
| @ -266,6 +336,7 @@ bool IRGeneratorForStatements::visit(Conditional const& _conditional) | ||||
| bool IRGeneratorForStatements::visit(Assignment const& _assignment) | ||||
| { | ||||
| 	_assignment.rightHandSide().accept(*this); | ||||
| 	setLocation(_assignment); | ||||
| 
 | ||||
| 	Token assignmentOperator = _assignment.assignmentOperator(); | ||||
| 	Token binaryOperator = | ||||
| @ -283,6 +354,7 @@ bool IRGeneratorForStatements::visit(Assignment const& _assignment) | ||||
| 	IRVariable value = convert(_assignment.rightHandSide(), *rightIntermediateType); | ||||
| 	_assignment.leftHandSide().accept(*this); | ||||
| 	solAssert(!!m_currentLValue, "LValue not retrieved."); | ||||
| 	setLocation(_assignment); | ||||
| 
 | ||||
| 	if (assignmentOperator != Token::Assign) | ||||
| 	{ | ||||
| @ -323,6 +395,8 @@ bool IRGeneratorForStatements::visit(Assignment const& _assignment) | ||||
| 
 | ||||
| bool IRGeneratorForStatements::visit(TupleExpression const& _tuple) | ||||
| { | ||||
| 	setLocation(_tuple); | ||||
| 
 | ||||
| 	if (_tuple.isInlineArray()) | ||||
| 	{ | ||||
| 		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]; | ||||
| 			component.accept(*this); | ||||
| 			setLocation(_tuple); | ||||
| 			IRVariable converted = convert(component, baseType); | ||||
| 			m_code << | ||||
| 				m_utils.writeToMemoryFunction(baseType) << | ||||
| @ -358,6 +433,7 @@ bool IRGeneratorForStatements::visit(TupleExpression const& _tuple) | ||||
| 		{ | ||||
| 			solAssert(_tuple.components().front(), ""); | ||||
| 			_tuple.components().front()->accept(*this); | ||||
| 			setLocation(_tuple); | ||||
| 			if (willBeWrittenTo) | ||||
| 				solAssert(!!m_currentLValue, ""); | ||||
| 			else | ||||
| @ -370,6 +446,7 @@ bool IRGeneratorForStatements::visit(TupleExpression const& _tuple) | ||||
| 				if (auto const& component = _tuple.components()[i]) | ||||
| 				{ | ||||
| 					component->accept(*this); | ||||
| 					setLocation(_tuple); | ||||
| 					if (willBeWrittenTo) | ||||
| 					{ | ||||
| 						solAssert(!!m_currentLValue, ""); | ||||
| @ -395,17 +472,20 @@ bool IRGeneratorForStatements::visit(TupleExpression const& _tuple) | ||||
| bool IRGeneratorForStatements::visit(IfStatement const& _ifStatement) | ||||
| { | ||||
| 	_ifStatement.condition().accept(*this); | ||||
| 	setLocation(_ifStatement); | ||||
| 	string condition = expressionAsType(_ifStatement.condition(), *TypeProvider::boolean()); | ||||
| 
 | ||||
| 	if (_ifStatement.falseStatement()) | ||||
| 	{ | ||||
| 		m_code << "switch " << condition << "\n" "case 0 {\n"; | ||||
| 		_ifStatement.falseStatement()->accept(*this); | ||||
| 		setLocation(_ifStatement); | ||||
| 		m_code << "}\n" "default {\n"; | ||||
| 	} | ||||
| 	else | ||||
| 		m_code << "if " << condition << " {\n"; | ||||
| 	_ifStatement.trueStatement().accept(*this); | ||||
| 	setLocation(_ifStatement); | ||||
| 	m_code << "}\n"; | ||||
| 
 | ||||
| 	return false; | ||||
| @ -413,6 +493,7 @@ bool IRGeneratorForStatements::visit(IfStatement const& _ifStatement) | ||||
| 
 | ||||
| bool IRGeneratorForStatements::visit(ForStatement const& _forStatement) | ||||
| { | ||||
| 	setLocation(_forStatement); | ||||
| 	generateLoop( | ||||
| 		_forStatement.body(), | ||||
| 		_forStatement.condition(), | ||||
| @ -425,6 +506,7 @@ bool IRGeneratorForStatements::visit(ForStatement const& _forStatement) | ||||
| 
 | ||||
| bool IRGeneratorForStatements::visit(WhileStatement const& _whileStatement) | ||||
| { | ||||
| 	setLocation(_whileStatement); | ||||
| 	generateLoop( | ||||
| 		_whileStatement.body(), | ||||
| 		&_whileStatement.condition(), | ||||
| @ -436,20 +518,23 @@ bool IRGeneratorForStatements::visit(WhileStatement const& _whileStatement) | ||||
| 	return false; | ||||
| } | ||||
| 
 | ||||
| bool IRGeneratorForStatements::visit(Continue const&) | ||||
| bool IRGeneratorForStatements::visit(Continue const& _continue) | ||||
| { | ||||
| 	setLocation(_continue); | ||||
| 	m_code << "continue\n"; | ||||
| 	return false; | ||||
| } | ||||
| 
 | ||||
| bool IRGeneratorForStatements::visit(Break const&) | ||||
| bool IRGeneratorForStatements::visit(Break const& _break) | ||||
| { | ||||
| 	setLocation(_break); | ||||
| 	m_code << "break\n"; | ||||
| 	return false; | ||||
| } | ||||
| 
 | ||||
| void IRGeneratorForStatements::endVisit(Return const& _return) | ||||
| { | ||||
| 	setLocation(_return); | ||||
| 	if (Expression const* value = _return.expression()) | ||||
| 	{ | ||||
| 		solAssert(_return.annotation().functionReturnParameters, "Invalid return parameters pointer."); | ||||
| @ -466,6 +551,7 @@ void IRGeneratorForStatements::endVisit(Return const& _return) | ||||
| 
 | ||||
| void IRGeneratorForStatements::endVisit(UnaryOperation const& _unaryOperation) | ||||
| { | ||||
| 	setLocation(_unaryOperation); | ||||
| 	Type const& resultType = type(_unaryOperation); | ||||
| 	Token const op = _unaryOperation.getOperator(); | ||||
| 
 | ||||
| @ -551,6 +637,8 @@ void IRGeneratorForStatements::endVisit(UnaryOperation const& _unaryOperation) | ||||
| 
 | ||||
| bool IRGeneratorForStatements::visit(BinaryOperation const& _binOp) | ||||
| { | ||||
| 	setLocation(_binOp); | ||||
| 
 | ||||
| 	solAssert(!!_binOp.annotation().commonType, ""); | ||||
| 	TypePointer commonType = _binOp.annotation().commonType; | ||||
| 	langutil::Token op = _binOp.getOperator(); | ||||
| @ -570,6 +658,7 @@ bool IRGeneratorForStatements::visit(BinaryOperation const& _binOp) | ||||
| 
 | ||||
| 	_binOp.leftExpression().accept(*this); | ||||
| 	_binOp.rightExpression().accept(*this); | ||||
| 	setLocation(_binOp); | ||||
| 
 | ||||
| 	if (TokenTraits::isCompareOp(op)) | ||||
| 	{ | ||||
| @ -629,6 +718,7 @@ bool IRGeneratorForStatements::visit(BinaryOperation const& _binOp) | ||||
| 
 | ||||
| bool IRGeneratorForStatements::visit(FunctionCall const& _functionCall) | ||||
| { | ||||
| 	setLocation(_functionCall); | ||||
| 	FunctionTypePointer functionType = dynamic_cast<FunctionType const*>(&type(_functionCall.expression())); | ||||
| 	if ( | ||||
| 		functionType && | ||||
| @ -643,6 +733,7 @@ bool IRGeneratorForStatements::visit(FunctionCall const& _functionCall) | ||||
| 
 | ||||
| void IRGeneratorForStatements::endVisit(FunctionCall const& _functionCall) | ||||
| { | ||||
| 	setLocation(_functionCall); | ||||
| 	auto functionCallKind = *_functionCall.annotation().kind; | ||||
| 
 | ||||
| 	if (functionCallKind == FunctionCallKind::TypeConversion) | ||||
| @ -1360,6 +1451,7 @@ void IRGeneratorForStatements::endVisit(FunctionCall const& _functionCall) | ||||
| 
 | ||||
| void IRGeneratorForStatements::endVisit(FunctionCallOptions const& _options) | ||||
| { | ||||
| 	setLocation(_options); | ||||
| 	FunctionType const& previousType = dynamic_cast<FunctionType const&>(*_options.expression().annotation().type); | ||||
| 
 | ||||
| 	solUnimplementedAssert(!previousType.bound(), ""); | ||||
| @ -1379,6 +1471,7 @@ void IRGeneratorForStatements::endVisit(FunctionCallOptions const& _options) | ||||
| 
 | ||||
| void IRGeneratorForStatements::endVisit(MemberAccess const& _memberAccess) | ||||
| { | ||||
| 	setLocation(_memberAccess); | ||||
| 	ASTString const& member = _memberAccess.memberName(); | ||||
| 	auto memberFunctionType = dynamic_cast<FunctionType const*>(_memberAccess.annotation().type); | ||||
| 	Type::Category objectCategory = _memberAccess.expression().annotation().type->category(); | ||||
| @ -1764,6 +1857,7 @@ void IRGeneratorForStatements::endVisit(MemberAccess const& _memberAccess) | ||||
| 
 | ||||
| bool IRGeneratorForStatements::visit(InlineAssembly const& _inlineAsm) | ||||
| { | ||||
| 	setLocation(_inlineAsm); | ||||
| 	CopyTranslate bodyCopier{_inlineAsm.dialect(), m_context, _inlineAsm.annotation().externalReferences}; | ||||
| 
 | ||||
| 	yul::Statement modified = bodyCopier(_inlineAsm.operations()); | ||||
| @ -1778,6 +1872,7 @@ bool IRGeneratorForStatements::visit(InlineAssembly const& _inlineAsm) | ||||
| 
 | ||||
| void IRGeneratorForStatements::endVisit(IndexAccess const& _indexAccess) | ||||
| { | ||||
| 	setLocation(_indexAccess); | ||||
| 	Type const& baseType = *_indexAccess.baseExpression().annotation().type; | ||||
| 
 | ||||
| 	if (baseType.category() == Type::Category::Mapping) | ||||
| @ -1913,6 +2008,7 @@ void IRGeneratorForStatements::endVisit(IndexAccess const& _indexAccess) | ||||
| 
 | ||||
| void IRGeneratorForStatements::endVisit(IndexRangeAccess const& _indexRangeAccess) | ||||
| { | ||||
| 	setLocation(_indexRangeAccess); | ||||
| 	Type const& baseType = *_indexRangeAccess.baseExpression().annotation().type; | ||||
| 	solAssert( | ||||
| 		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) | ||||
| { | ||||
| 	setLocation(_identifier); | ||||
| 	Declaration const* declaration = _identifier.annotation().referencedDeclaration; | ||||
| 	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) | ||||
| { | ||||
| 	setLocation(_literal); | ||||
| 	Type const& literalType = type(_literal); | ||||
| 
 | ||||
| 	switch (literalType.category()) | ||||
| @ -2039,6 +2137,7 @@ void IRGeneratorForStatements::handleVariableReference( | ||||
| 	Expression const& _referencingExpression | ||||
| ) | ||||
| { | ||||
| 	setLocation(_referencingExpression); | ||||
| 	if (_variable.isStateVariable() && _variable.isConstant()) | ||||
| 		define(_referencingExpression) << constantValueFunction(_variable) << "()\n"; | ||||
| 	else if (_variable.isStateVariable() && _variable.immutable()) | ||||
| @ -2480,6 +2579,7 @@ void IRGeneratorForStatements::appendAndOrOperatorCode(BinaryOperation const& _b | ||||
| 	solAssert(op == Token::Or || op == Token::And, ""); | ||||
| 
 | ||||
| 	_binOp.leftExpression().accept(*this); | ||||
| 	setLocation(_binOp); | ||||
| 
 | ||||
| 	IRVariable value(_binOp); | ||||
| 	define(value, _binOp.leftExpression()); | ||||
| @ -2488,6 +2588,7 @@ void IRGeneratorForStatements::appendAndOrOperatorCode(BinaryOperation const& _b | ||||
| 	else | ||||
| 		m_code << "if " << value.name() << " {\n"; | ||||
| 	_binOp.rightExpression().accept(*this); | ||||
| 	setLocation(_binOp); | ||||
| 	assign(value, _binOp.rightExpression()); | ||||
| 	m_code << "}\n"; | ||||
| } | ||||
| @ -2687,6 +2788,7 @@ bool IRGeneratorForStatements::visit(TryStatement const& _tryStatement) | ||||
| { | ||||
| 	Expression const& externalCall = _tryStatement.externalCall(); | ||||
| 	externalCall.accept(*this); | ||||
| 	setLocation(_tryStatement); | ||||
| 
 | ||||
| 	m_code << "switch iszero(" << IRNames::trySuccessConditionVariable(externalCall) << ")\n"; | ||||
| 
 | ||||
| @ -2707,6 +2809,7 @@ bool IRGeneratorForStatements::visit(TryStatement const& _tryStatement) | ||||
| 	} | ||||
| 
 | ||||
| 	successClause.block().accept(*this); | ||||
| 	setLocation(_tryStatement); | ||||
| 	m_code << "}\n"; | ||||
| 
 | ||||
| 	m_code << "default { // failure case\n"; | ||||
| @ -2797,3 +2900,8 @@ bool IRGeneratorForStatements::visit(TryCatchClause const& _clause) | ||||
| 	_clause.block().accept(*this); | ||||
| 	return false; | ||||
| } | ||||
| 
 | ||||
| void IRGeneratorForStatements::setLocation(ASTNode const& _node) | ||||
| { | ||||
| 	m_currentLocation = _node.location(); | ||||
| } | ||||
|  | ||||
| @ -47,6 +47,9 @@ public: | ||||
| 
 | ||||
| 	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.
 | ||||
| 	void initializeStateVar(VariableDeclaration const& _varDecl); | ||||
| 	/// Generates code to initialize the given local variable.
 | ||||
| @ -179,10 +182,13 @@ private: | ||||
| 
 | ||||
| 	static Type const& type(Expression const& _expression); | ||||
| 
 | ||||
| 	void setLocation(ASTNode const& _node); | ||||
| 
 | ||||
| 	std::ostringstream m_code; | ||||
| 	IRGenerationContext& m_context; | ||||
| 	YulUtilFunctions& m_utils; | ||||
| 	std::optional<IRLValue> m_currentLValue; | ||||
| 	langutil::SourceLocation m_currentLocation; | ||||
| }; | ||||
| 
 | ||||
| } | ||||
|  | ||||
| @ -22,6 +22,7 @@ | ||||
| #include <libsmtutil/Z3CHCInterface.h> | ||||
| #endif | ||||
| 
 | ||||
| #include <libsolidity/formal/PredicateSort.h> | ||||
| #include <libsolidity/formal/SymbolicTypes.h> | ||||
| 
 | ||||
| #include <libsolidity/ast/TypeProvider.h> | ||||
| @ -39,13 +40,14 @@ using namespace solidity::util; | ||||
| using namespace solidity::langutil; | ||||
| using namespace solidity::smtutil; | ||||
| using namespace solidity::frontend; | ||||
| using namespace solidity::frontend::smt; | ||||
| 
 | ||||
| CHC::CHC( | ||||
| 	smt::EncodingContext& _context, | ||||
| 	EncodingContext& _context, | ||||
| 	ErrorReporter& _errorReporter, | ||||
| 	[[maybe_unused]] map<util::h256, string> const& _smtlib2Responses, | ||||
| 	[[maybe_unused]] ReadCallback::Callback const& _smtCallback, | ||||
| 	smtutil::SMTSolverChoice _enabledSolvers | ||||
| 	SMTSolverChoice _enabledSolvers | ||||
| ): | ||||
| 	SMTEncoder(_context), | ||||
| 	m_outerErrorReporter(_errorReporter), | ||||
| @ -56,7 +58,7 @@ CHC::CHC( | ||||
| 	usesZ3 = false; | ||||
| #endif | ||||
| 	if (!usesZ3) | ||||
| 		m_interface = make_unique<smtutil::CHCSmtLib2Interface>(_smtlib2Responses, _smtCallback); | ||||
| 		m_interface = make_unique<CHCSmtLib2Interface>(_smtlib2Responses, _smtCallback); | ||||
| } | ||||
| 
 | ||||
| void CHC::analyze(SourceUnit const& _source) | ||||
| @ -79,7 +81,7 @@ void CHC::analyze(SourceUnit const& _source) | ||||
| 
 | ||||
| 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 {}; | ||||
| @ -92,13 +94,15 @@ bool CHC::visit(ContractDefinition const& _contract) | ||||
| 	initContract(_contract); | ||||
| 
 | ||||
| 	m_stateVariables = SMTEncoder::stateVariablesIncludingInheritedAndPrivate(_contract); | ||||
| 	m_stateSorts = stateSorts(_contract); | ||||
| 
 | ||||
| 	clearIndices(&_contract); | ||||
| 
 | ||||
| 	string suffix = _contract.name() + "_" + to_string(_contract.id()); | ||||
| 	m_constructorSummaryPredicate = createSymbolicBlock(constructorSort(), "summary_constructor_" + suffix, &_contract); | ||||
| 	m_implicitConstructorPredicate = createSymbolicBlock(arity0FunctionSort(), "implicit_constructor_" + suffix, &_contract); | ||||
| 	solAssert(m_currentContract, ""); | ||||
| 	m_constructorSummaryPredicate = createSymbolicBlock( | ||||
| 		constructorSort(*m_currentContract), | ||||
| 		"summary_constructor_" + contractSuffix(_contract), | ||||
| 		&_contract | ||||
| 	); | ||||
| 	auto stateExprs = currentStateVariables(); | ||||
| 	setCurrentBlock(*m_interfaces.at(m_currentContract), &stateExprs); | ||||
| 
 | ||||
| @ -108,7 +112,12 @@ bool CHC::visit(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); | ||||
| 	m_currentBlock = implicitConstructor; | ||||
| 	m_context.addAssertion(m_error.currentValue() == 0); | ||||
| @ -204,7 +213,12 @@ void CHC::endVisit(FunctionDefinition const& _function) | ||||
| 		if (_function.isConstructor()) | ||||
| 		{ | ||||
| 			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))); | ||||
| 
 | ||||
| 			clearIndices(m_currentContract, m_currentFunction); | ||||
| @ -566,7 +580,7 @@ void CHC::makeArrayPopVerificationTarget(FunctionCall const& _arrayPop) | ||||
| 
 | ||||
| 	auto memberAccess = dynamic_cast<MemberAccess const*>(&_arrayPop.expression()); | ||||
| 	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, ""); | ||||
| 
 | ||||
| 	auto previousError = m_error.currentValue(); | ||||
| @ -662,15 +676,15 @@ void CHC::resetSourceAnalysis() | ||||
| 	if (usesZ3) | ||||
| 	{ | ||||
| 		/// z3::fixedpoint does not have a reset mechanism, so we need to create another.
 | ||||
| 		m_interface.reset(new smtutil::Z3CHCInterface()); | ||||
| 		auto z3Interface = dynamic_cast<smtutil::Z3CHCInterface const*>(m_interface.get()); | ||||
| 		m_interface.reset(new Z3CHCInterface()); | ||||
| 		auto z3Interface = dynamic_cast<Z3CHCInterface const*>(m_interface.get()); | ||||
| 		solAssert(z3Interface, ""); | ||||
| 		m_context.setSolver(z3Interface->z3Interface()); | ||||
| 	} | ||||
| #endif | ||||
| 	if (!usesZ3) | ||||
| 	{ | ||||
| 		auto smtlib2Interface = dynamic_cast<smtutil::CHCSmtLib2Interface*>(m_interface.get()); | ||||
| 		auto smtlib2Interface = dynamic_cast<CHCSmtLib2Interface*>(m_interface.get()); | ||||
| 		smtlib2Interface->reset(); | ||||
| 		solAssert(smtlib2Interface, ""); | ||||
| 		m_context.setSolver(smtlib2Interface->smtlib2Interface()); | ||||
| @ -682,7 +696,6 @@ void CHC::resetSourceAnalysis() | ||||
| 
 | ||||
| void CHC::resetContractAnalysis() | ||||
| { | ||||
| 	m_stateSorts.clear(); | ||||
| 	m_stateVariables.clear(); | ||||
| 	m_unknownFunctionCallSeen = false; | ||||
| 	m_breakDest = nullptr; | ||||
| @ -739,117 +752,18 @@ set<frontend::Expression const*, CHC::IdCompare> CHC::transactionAssertions(ASTN | ||||
| 	return assertions; | ||||
| } | ||||
| 
 | ||||
| vector<smtutil::SortPointer> CHC::stateSorts(ContractDefinition const& _contract) | ||||
| SortPointer CHC::sort(FunctionDefinition const& _function) | ||||
| { | ||||
| 	return applyMap( | ||||
| 		SMTEncoder::stateVariablesIncludingInheritedAndPrivate(_contract), | ||||
| 		[](auto _var) { return smt::smtSortAbstractFunction(*_var->type()); } | ||||
| 	); | ||||
| 	return functionSort(_function, m_currentContract); | ||||
| } | ||||
| 
 | ||||
| smtutil::SortPointer CHC::constructorSort() | ||||
| { | ||||
| 	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) | ||||
| SortPointer CHC::sort(ASTNode const* _node) | ||||
| { | ||||
| 	if (auto funDef = dynamic_cast<FunctionDefinition const*>(_node)) | ||||
| 		return sort(*funDef); | ||||
| 
 | ||||
| 	auto fSort = dynamic_pointer_cast<smtutil::FunctionSort>(sort(*m_currentFunction)); | ||||
| 	solAssert(fSort, ""); | ||||
| 
 | ||||
| 	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 | ||||
| 	); | ||||
| 	solAssert(m_currentFunction, ""); | ||||
| 	return functionBodySort(*m_currentFunction, m_currentContract); | ||||
| } | ||||
| 
 | ||||
| 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) | ||||
| { | ||||
| 	auto block = createSymbolicBlock( | ||||
| 		summarySort(_function, _contract), | ||||
| 		functionSort(_function, &_contract), | ||||
| 		"summary_" + uniquePrefix() + "_" + predicateName(&_function, &_contract), | ||||
| 		&_function | ||||
| 	); | ||||
| @ -1157,14 +1071,14 @@ void CHC::addRule(smtutil::Expression const& _rule, string const& _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; | ||||
| 	tie(result, cex) = m_interface->query(_query); | ||||
| 	switch (result) | ||||
| 	{ | ||||
| 	case smtutil::CheckResult::SATISFIABLE: | ||||
| 	case CheckResult::SATISFIABLE: | ||||
| 	{ | ||||
| #ifdef HAVE_Z3 | ||||
| 		// 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, ""); | ||||
| 		spacer->setSpacerOptions(false); | ||||
| 
 | ||||
| 		smtutil::CheckResult resultNoOpt; | ||||
| 		CheckResult resultNoOpt; | ||||
| 		CHCSolverInterface::CexGraph cexNoOpt; | ||||
| 		tie(resultNoOpt, cexNoOpt) = m_interface->query(_query); | ||||
| 
 | ||||
| 		if (resultNoOpt == smtutil::CheckResult::SATISFIABLE) | ||||
| 		if (resultNoOpt == CheckResult::SATISFIABLE) | ||||
| 			cex = move(cexNoOpt); | ||||
| 
 | ||||
| 		spacer->setSpacerOptions(true); | ||||
| #endif | ||||
| 		break; | ||||
| 	} | ||||
| 	case smtutil::CheckResult::UNSATISFIABLE: | ||||
| 	case CheckResult::UNSATISFIABLE: | ||||
| 		break; | ||||
| 	case smtutil::CheckResult::UNKNOWN: | ||||
| 	case CheckResult::UNKNOWN: | ||||
| 		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."); | ||||
| 		break; | ||||
| 	case smtutil::CheckResult::ERROR: | ||||
| 	case CheckResult::ERROR: | ||||
| 		m_outerErrorReporter.warning(1218_error, _location, "Error trying to invoke SMT solver."); | ||||
| 		break; | ||||
| 	} | ||||
| @ -1339,9 +1253,9 @@ void CHC::checkAndReportTarget( | ||||
| 	createErrorBlock(); | ||||
| 	connectBlocks(_target.value, error(), _target.constraints && (_target.errorId == _errorId)); | ||||
| 	auto const& [result, model] = query(error(), _scope->location()); | ||||
| 	if (result == smtutil::CheckResult::UNSATISFIABLE) | ||||
| 	if (result == CheckResult::UNSATISFIABLE) | ||||
| 		m_safeTargets[_scope].insert(_target.type); | ||||
| 	else if (result == smtutil::CheckResult::SATISFIABLE) | ||||
| 	else if (result == CheckResult::SATISFIABLE) | ||||
| 	{ | ||||
| 		solAssert(!_satMsg.empty(), ""); | ||||
| 		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"); | ||||
| } | ||||
| 
 | ||||
| string CHC::cex2dot(smtutil::CHCSolverInterface::CexGraph const& _cex) | ||||
| string CHC::cex2dot(CHCSolverInterface::CexGraph const& _cex) | ||||
| { | ||||
| 	string dot = "digraph {\n"; | ||||
| 
 | ||||
| @ -1498,6 +1412,11 @@ string CHC::uniquePrefix() | ||||
| 	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 errorId = m_context.newUniqueId(); | ||||
|  | ||||
| @ -117,19 +117,8 @@ private: | ||||
| 
 | ||||
| 	/// 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(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.
 | ||||
| @ -246,10 +235,13 @@ private: | ||||
| 
 | ||||
| 	/// 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.
 | ||||
| 	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
 | ||||
| 	/// it into m_errorIds.
 | ||||
| 	unsigned newErrorId(Expression const& _expr); | ||||
| @ -257,10 +249,6 @@ private: | ||||
| 
 | ||||
| 	/// Predicates.
 | ||||
| 	//@{
 | ||||
| 	/// Implicit constructor predicate.
 | ||||
| 	/// Explicit constructors are handled as functions.
 | ||||
| 	Predicate const* m_implicitConstructorPredicate = nullptr; | ||||
| 
 | ||||
| 	/// Constructor summary predicate, exists after the constructor
 | ||||
| 	/// (implicit or explicit) and before the interface.
 | ||||
| 	Predicate const* m_constructorSummaryPredicate = nullptr; | ||||
| @ -292,9 +280,6 @@ private: | ||||
| 
 | ||||
| 	/// Variables.
 | ||||
| 	//@{
 | ||||
| 	/// State variables sorts.
 | ||||
| 	/// Used by all predicates.
 | ||||
| 	std::vector<smtutil::SortPointer> m_stateSorts; | ||||
| 	/// State variables.
 | ||||
| 	/// Used to create all predicates.
 | ||||
| 	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 <libsmtutil/SMTPortfolio.h> | ||||
| #include <libsmtutil/Helpers.h> | ||||
| 
 | ||||
| #include <boost/range/adaptors.hpp> | ||||
| #include <boost/range/adaptor/reversed.hpp> | ||||
| @ -675,6 +676,14 @@ void SMTEncoder::endVisit(FunctionCall const& _funCall) | ||||
| 	case FunctionType::Kind::ArrayPop: | ||||
| 		arrayPop(_funCall); | ||||
| 		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: | ||||
| 		m_errorReporter.warning( | ||||
| 			4588_error, | ||||
| @ -1497,11 +1506,7 @@ smtutil::Expression SMTEncoder::division(smtutil::Expression _left, smtutil::Exp | ||||
| { | ||||
| 	// Signed division in SMTLIB2 rounds differently for negative division.
 | ||||
| 	if (_type.isSigned()) | ||||
| 		return (smtutil::Expression::ite( | ||||
| 			_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)) | ||||
| 		)); | ||||
| 		return signedDivision(_left, _right); | ||||
| 	else | ||||
| 		return _left / _right; | ||||
| } | ||||
|  | ||||
| @ -524,6 +524,10 @@ bool CompilerStack::compile() | ||||
| 					{ | ||||
| 						if (m_generateEvmBytecode) | ||||
| 							compileContract(*contract, otherCompilers); | ||||
| 						if (m_generateIR || m_generateEwasm) | ||||
| 							generateIR(*contract); | ||||
| 						if (m_generateEwasm) | ||||
| 							generateEwasm(*contract); | ||||
| 					} | ||||
| 					catch (Error const& _error) | ||||
| 					{ | ||||
| @ -532,10 +536,28 @@ bool CompilerStack::compile() | ||||
| 						m_errorReporter.error(_error.errorId(), _error.type(), SourceLocation(), _error.what()); | ||||
| 						return false; | ||||
| 					} | ||||
| 					if (m_generateIR || m_generateEwasm) | ||||
| 						generateIR(*contract); | ||||
| 					if (m_generateEwasm) | ||||
| 						generateEwasm(*contract); | ||||
| 					catch (UnimplementedFeatureError const& _unimplementedError) | ||||
| 					{ | ||||
| 						if ( | ||||
| 							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; | ||||
| 	this->link(); | ||||
|  | ||||
| @ -132,6 +132,8 @@ add_library(yul | ||||
| 	optimiser/OptimiserStep.h | ||||
| 	optimiser/OptimizerUtilities.cpp | ||||
| 	optimiser/OptimizerUtilities.h | ||||
| 	optimiser/ReasoningBasedSimplifier.cpp | ||||
| 	optimiser/ReasoningBasedSimplifier.h | ||||
| 	optimiser/RedundantAssignEliminator.cpp | ||||
| 	optimiser/RedundantAssignEliminator.h | ||||
| 	optimiser/Rematerialiser.cpp | ||||
| @ -168,4 +170,5 @@ add_library(yul | ||||
| 	optimiser/VarNameCleaner.cpp | ||||
| 	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}; | ||||
| } | ||||
| 
 | ||||
| 
 | ||||
| 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 | ||||
| { | ||||
| 	if (_kind == LiteralKind::Boolean) | ||||
|  | ||||
| @ -77,6 +77,7 @@ struct Dialect: boost::noncopyable | ||||
| 	virtual bool validTypeForLiteral(LiteralKind _kind, YulString _value, YulString _type) const; | ||||
| 
 | ||||
| 	virtual Literal zeroLiteralForType(YulString _type) const; | ||||
| 	virtual Literal trueLiteral() const; | ||||
| 
 | ||||
| 	virtual std::set<YulString> fixedFunctionNames() const { return {}; } | ||||
| 
 | ||||
|  | ||||
| @ -20,6 +20,7 @@ | ||||
| 
 | ||||
| #include <libyul/Exceptions.h> | ||||
| 
 | ||||
| #include <optional> | ||||
| #include <string> | ||||
| #include <set> | ||||
| 
 | ||||
| @ -49,17 +50,41 @@ struct OptimiserStep | ||||
| 	virtual ~OptimiserStep() = default; | ||||
| 
 | ||||
| 	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; | ||||
| }; | ||||
| 
 | ||||
| template <class Step> | ||||
| 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} {} | ||||
| 	void run(OptimiserStepContext& _context, Block& _ast) const override | ||||
| 	{ | ||||
| 		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/ForLoopInitRewriter.h> | ||||
| #include <libyul/optimiser/ForLoopConditionIntoBody.h> | ||||
| #include <libyul/optimiser/ReasoningBasedSimplifier.h> | ||||
| #include <libyul/optimiser/Rematerialiser.h> | ||||
| #include <libyul/optimiser/UnusedFunctionParameterPruner.h> | ||||
| #include <libyul/optimiser/UnusedPruner.h> | ||||
| @ -184,6 +185,7 @@ map<string, unique_ptr<OptimiserStep>> const& OptimiserSuite::allSteps() | ||||
| 			LoopInvariantCodeMotion, | ||||
| 			NameSimplifier, | ||||
| 			RedundantAssignEliminator, | ||||
| 			ReasoningBasedSimplifier, | ||||
| 			Rematerialiser, | ||||
| 			SSAReverser, | ||||
| 			SSATransform, | ||||
| @ -221,6 +223,7 @@ map<string, char> const& OptimiserSuite::stepNameToAbbreviationMap() | ||||
| 		{LoadResolver::name,                  'L'}, | ||||
| 		{LoopInvariantCodeMotion::name,       'M'}, | ||||
| 		{NameSimplifier::name,                'N'}, | ||||
| 		{ReasoningBasedSimplifier::name,      'R'}, | ||||
| 		{RedundantAssignEliminator::name,     'r'}, | ||||
| 		{Rematerialiser::name,                'm'}, | ||||
| 		{SSAReverser::name,                   'V'}, | ||||
| @ -266,6 +269,7 @@ void OptimiserSuite::validateSequence(string const& _stepAbbreviations) | ||||
| 			insideLoop = false; | ||||
| 			break; | ||||
| 		default: | ||||
| 		{ | ||||
| 			yulAssert( | ||||
| 				string(NonStepAbbreviations).find(abbreviation) == string::npos, | ||||
| 				"Unhandled syntactic element in the abbreviation sequence" | ||||
| @ -275,6 +279,14 @@ void OptimiserSuite::validateSequence(string const& _stepAbbreviations) | ||||
| 				OptimizerException, | ||||
| 				"'"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"); | ||||
| } | ||||
|  | ||||
| @ -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 (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", "yul_unimplemented", "err")) | ||||
| 
 | ||||
|     # white list of ids which are not covered by tests | ||||
|     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 '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/\(Unimplemented feature error: .* in \).*$/\1FILENAME REMOVED/' "$stderr_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. | ||||
|         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); | ||||
| 	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); | ||||
| 
 | ||||
| 		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/ExpressionJoiner.h> | ||||
| #include <libyul/optimiser/OptimiserStep.h> | ||||
| #include <libyul/optimiser/ReasoningBasedSimplifier.h> | ||||
| #include <libyul/optimiser/SSAReverser.h> | ||||
| #include <libyul/optimiser/SSATransform.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 + "\".")); | ||||
| 	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(); | ||||
| 
 | ||||
| 	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); | ||||
| 		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") | ||||
| 	{ | ||||
| 		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/VarNameCleaner.h> | ||||
| #include <libyul/optimiser/Suite.h> | ||||
| #include <libyul/optimiser/ReasoningBasedSimplifier.h> | ||||
| 
 | ||||
| #include <libyul/backends/evm/EVMDialect.h> | ||||
| 
 | ||||
| @ -157,7 +158,7 @@ public: | ||||
| 			map<char, string> const& extraOptions = { | ||||
| 				{'#', "quit"}, | ||||
| 				{',', "VarNameCleaner"}, | ||||
| 				{';', "StackCompressor"}, | ||||
| 				{';', "StackCompressor"} | ||||
| 			}; | ||||
| 
 | ||||
| 			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.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) | ||||
|  | ||||
		Loading…
	
		Reference in New Issue
	
	Block a user