diff --git a/docs/contributing.rst b/docs/contributing.rst index 561f3f43e..a78d2e5d7 100644 --- a/docs/contributing.rst +++ b/docs/contributing.rst @@ -88,10 +88,11 @@ Solidity includes different types of tests, most of them bundled into the `Boost C++ Test Framework `_ application ``soltest``. Running ``build/test/soltest` or its wrapper ``scripts/soltest.sh`` is sufficient for most changes. -Some tests require the ``libevmone.so`` library, others require ``libz3``. +Some tests require the ``evmone`` library, others require ``libz3``. -The test system will automatically try to discover the location of ``libevmone.so`` -starting from the current directory. If it does not find it, the relevant tests +The test system will automatically try to discover the location of the ``evmone`` library +starting from the current directory. The required file is called ``libevmone.so`` on Linux systems, +``evmone.dll`` on Windows systems and ``libevmone.dylib`` on MacOS. If it is not found, the relevant tests are skipped. To run all tests, download the library from `Github `_ and either place it in the project root path or inside the ``deps`` folder. diff --git a/libyul/Dialect.h b/libyul/Dialect.h index 64377cc37..5fd938c1e 100644 --- a/libyul/Dialect.h +++ b/libyul/Dialect.h @@ -61,6 +61,7 @@ struct Dialect: boost::noncopyable virtual BuiltinFunction const* discardFunction() const { return nullptr; } virtual BuiltinFunction const* equalityFunction() const { return nullptr; } + virtual BuiltinFunction const* booleanNegationFunction() const { return nullptr; } virtual std::set fixedFunctionNames() const { return {}; } diff --git a/libyul/backends/evm/EVMDialect.h b/libyul/backends/evm/EVMDialect.h index f4c0e4eed..1c47c8005 100644 --- a/libyul/backends/evm/EVMDialect.h +++ b/libyul/backends/evm/EVMDialect.h @@ -70,6 +70,7 @@ struct EVMDialect: public Dialect BuiltinFunctionForEVM const* discardFunction() const override { return builtin("pop"_yulstring); } BuiltinFunctionForEVM const* equalityFunction() const override { return builtin("eq"_yulstring); } + BuiltinFunctionForEVM const* booleanNegationFunction() const override { return builtin("iszero"_yulstring); } static EVMDialect const& looseAssemblyForEVM(langutil::EVMVersion _version); static EVMDialect const& strictAssemblyForEVM(langutil::EVMVersion _version); diff --git a/libyul/backends/wasm/WasmDialect.h b/libyul/backends/wasm/WasmDialect.h index 45bea3bca..e77381a13 100644 --- a/libyul/backends/wasm/WasmDialect.h +++ b/libyul/backends/wasm/WasmDialect.h @@ -47,6 +47,7 @@ struct WasmDialect: public Dialect BuiltinFunction const* builtin(YulString _name) const override; BuiltinFunction const* discardFunction() const override { return builtin("drop"_yulstring); } BuiltinFunction const* equalityFunction() const override { return builtin("i64.eq"_yulstring); } + BuiltinFunction const* booleanNegationFunction() const override { return builtin("i64.eqz"_yulstring); } std::set fixedFunctionNames() const override { return {"main"_yulstring}; } diff --git a/libyul/optimiser/ForLoopConditionIntoBody.cpp b/libyul/optimiser/ForLoopConditionIntoBody.cpp index b169d0a51..919b194f1 100644 --- a/libyul/optimiser/ForLoopConditionIntoBody.cpp +++ b/libyul/optimiser/ForLoopConditionIntoBody.cpp @@ -25,7 +25,7 @@ using namespace yul; void ForLoopConditionIntoBody::operator()(ForLoop& _forLoop) { - if (_forLoop.condition->type() != typeid(Literal)) + if (m_dialect.booleanNegationFunction() && _forLoop.condition->type() != typeid(Literal)) { langutil::SourceLocation loc = locationOf(*_forLoop.condition); _forLoop.body.statements.insert( @@ -33,9 +33,9 @@ void ForLoopConditionIntoBody::operator()(ForLoop& _forLoop) If { loc, make_unique( - FunctionalInstruction { + FunctionCall { loc, - eth::Instruction::ISZERO, + {loc, m_dialect.booleanNegationFunction()->name}, make_vector(std::move(*_forLoop.condition)) } ), diff --git a/libyul/optimiser/ForLoopConditionIntoBody.h b/libyul/optimiser/ForLoopConditionIntoBody.h index bbb1b47c2..508511758 100644 --- a/libyul/optimiser/ForLoopConditionIntoBody.h +++ b/libyul/optimiser/ForLoopConditionIntoBody.h @@ -17,6 +17,7 @@ #pragma once #include +#include namespace yul { @@ -29,17 +30,22 @@ namespace yul * By moving the iteration check part into the ForLoop body, we can apply expression splitter * to the condition expression. * - * This rewritter will skip loops that already have literal constant as iteration condition. + * This rewriter will skip loops that already have literal constant as iteration condition. * * Requirements: * - The Disambiguator must be run upfront. * - To avoid unnecessary rewrite, it is recommended to run this rewriter after StructuralSimplifier. + * - Only works for dialects with a builtin boolean negation function. */ class ForLoopConditionIntoBody: public ASTModifier { public: + ForLoopConditionIntoBody(Dialect const& _dialect): m_dialect(_dialect) {} using ASTModifier::operator(); void operator()(ForLoop& _forLoop) override; + +private: + Dialect const& m_dialect; }; } diff --git a/libyul/optimiser/Rematerialiser.cpp b/libyul/optimiser/Rematerialiser.cpp index e11ed98f3..c9088a431 100644 --- a/libyul/optimiser/Rematerialiser.cpp +++ b/libyul/optimiser/Rematerialiser.cpp @@ -71,9 +71,9 @@ void Rematerialiser::visit(Expression& _e) if (_e.type() == typeid(Identifier)) { Identifier& identifier = boost::get(_e); - if (m_value.count(identifier.name)) + YulString name = identifier.name; + if (m_value.count(name)) { - YulString name = identifier.name; assertThrow(m_value.at(name), OptimizerException, ""); auto const& value = *m_value.at(name); size_t refs = m_referenceCounts[name]; @@ -93,3 +93,20 @@ void Rematerialiser::visit(Expression& _e) } DataFlowAnalyzer::visit(_e); } + +void LiteralRematerialiser::visit(Expression& _e) +{ + if (_e.type() == typeid(Identifier)) + { + Identifier& identifier = boost::get(_e); + YulString name = identifier.name; + if (m_value.count(name)) + { + Expression const* value = m_value.at(name); + assertThrow(value, OptimizerException, ""); + if (value->type() == typeid(Literal)) + _e = *value; + } + } + DataFlowAnalyzer::visit(_e); +} diff --git a/libyul/optimiser/Rematerialiser.h b/libyul/optimiser/Rematerialiser.h index 6871cdaa9..339a6a38f 100644 --- a/libyul/optimiser/Rematerialiser.h +++ b/libyul/optimiser/Rematerialiser.h @@ -68,4 +68,25 @@ protected: std::set m_varsToAlwaysRematerialize; }; +/** + * If a variable is referenced that is known to have a literal + * value at that point, replace it by a literal. + * + * This is mostly used so that other components do not have to rely + * on the data flow analyzer. + * + * Prerequisite: Disambiguator, ForLoopInitRewriter. + */ +class LiteralRematerialiser: public DataFlowAnalyzer +{ +public: + LiteralRematerialiser(Dialect const& _dialect): + DataFlowAnalyzer(_dialect) + {} + + using ASTModifier::visit; + void visit(Expression& _e) override; +}; + + } diff --git a/libyul/optimiser/SSAReverser.cpp b/libyul/optimiser/SSAReverser.cpp index a9a49fdb6..5bc6ab5c6 100644 --- a/libyul/optimiser/SSAReverser.cpp +++ b/libyul/optimiser/SSAReverser.cpp @@ -56,18 +56,24 @@ void SSAReverser::operator()(Block& _block) identifier && identifier->name == varDecl->variables.front().name ) - return make_vector( - Assignment{ - std::move(assignment->location), - assignment->variableNames, - std::move(varDecl->value) - }, - VariableDeclaration{ - std::move(varDecl->location), - std::move(varDecl->variables), - std::make_unique(std::move(assignment->variableNames.front())) - } - ); + { + // in the special case a == a_1, just remove the assignment + if (assignment->variableNames.front().name == identifier->name) + return make_vector(std::move(_stmt1)); + else + return make_vector( + Assignment{ + std::move(assignment->location), + assignment->variableNames, + std::move(varDecl->value) + }, + VariableDeclaration{ + std::move(varDecl->location), + std::move(varDecl->variables), + std::make_unique(std::move(assignment->variableNames.front())) + } + ); + } } // Replaces // let a_1 := E diff --git a/libyul/optimiser/SSAReverser.h b/libyul/optimiser/SSAReverser.h index 67abeb568..7845bff5d 100644 --- a/libyul/optimiser/SSAReverser.h +++ b/libyul/optimiser/SSAReverser.h @@ -40,6 +40,13 @@ class AssignmentCounter; * a := E * let a_1 := a * + * In the special case + * let a := E + * a := a + * + * the redundant assignment "a := a" is removed. + * + * * Secondly, the SSA transform will rewrite * * let a := E diff --git a/libyul/optimiser/StructuralSimplifier.cpp b/libyul/optimiser/StructuralSimplifier.cpp index 98e70a3ee..b9d941e43 100644 --- a/libyul/optimiser/StructuralSimplifier.cpp +++ b/libyul/optimiser/StructuralSimplifier.cpp @@ -61,29 +61,7 @@ OptionalStatements replaceConstArgSwitch(Switch& _switchStmt, u256 const& _const void StructuralSimplifier::operator()(Block& _block) { - pushScope(false); simplify(_block.statements); - popScope(); -} - -boost::optional StructuralSimplifier::hasLiteralValue(Expression const& _expression) const -{ - Expression const* expr = &_expression; - - if (expr->type() == typeid(Identifier)) - { - Identifier const& ident = boost::get(*expr); - if (m_value.count(ident.name)) - expr = m_value.at(ident.name); - } - - if (expr && expr->type() == typeid(Literal)) - { - Literal const& literal = boost::get(*expr); - return valueOfLiteral(literal); - } - - return boost::optional(); } void StructuralSimplifier::simplify(std::vector& _statements) @@ -124,34 +102,24 @@ void StructuralSimplifier::simplify(std::vector& _statements) bool StructuralSimplifier::expressionAlwaysTrue(Expression const& _expression) { - return boost::apply_visitor(GenericFallbackReturnsVisitor( - [&](Identifier const& _identifier) -> bool { - if (auto expr = m_value[_identifier.name]) - return expressionAlwaysTrue(*expr); - return false; - }, - [](Literal const& _literal) -> bool { - return - (_literal.kind == LiteralKind::Boolean && _literal.value == "true"_yulstring) || - (_literal.kind == LiteralKind::Number && valueOfNumberLiteral(_literal) != u256(0)) - ; - } - ), _expression); + if (boost::optional value = hasLiteralValue(_expression)) + return *value != 0; + else + return false; } bool StructuralSimplifier::expressionAlwaysFalse(Expression const& _expression) { - return boost::apply_visitor(GenericFallbackReturnsVisitor( - [&](Identifier const& _identifier) -> bool { - if (auto expr = m_value[_identifier.name]) - return expressionAlwaysFalse(*expr); - return false; - }, - [](Literal const& _literal) -> bool { - return - (_literal.kind == LiteralKind::Boolean && _literal.value == "false"_yulstring) || - (_literal.kind == LiteralKind::Number && valueOfNumberLiteral(_literal) == u256(0)) - ; - } - ), _expression); + if (boost::optional value = hasLiteralValue(_expression)) + return *value == 0; + else + return false; +} + +boost::optional StructuralSimplifier::hasLiteralValue(Expression const& _expression) const +{ + if (_expression.type() == typeid(Literal)) + return valueOfLiteral(boost::get(_expression)); + else + return boost::optional(); } diff --git a/libyul/optimiser/StructuralSimplifier.h b/libyul/optimiser/StructuralSimplifier.h index a003abd6e..34a077a6f 100644 --- a/libyul/optimiser/StructuralSimplifier.h +++ b/libyul/optimiser/StructuralSimplifier.h @@ -30,16 +30,16 @@ namespace yul * - replace switch with const expr with matching case body * - replace for with false condition by its initialization part * - * Prerequisite: Disambiguator, ForLoopInitRewriter. + * The LiteralRematerialiser should be run before this. + * + * Prerequisite: Disambiguator. * * Important: Can only be used on EVM code. */ -class StructuralSimplifier: public DataFlowAnalyzer +class StructuralSimplifier: public ASTModifier { public: - explicit StructuralSimplifier(Dialect const& _dialect): DataFlowAnalyzer(_dialect) {} - - using DataFlowAnalyzer::operator(); + using ASTModifier::operator(); void operator()(Block& _block) override; private: void simplify(std::vector& _statements); diff --git a/libyul/optimiser/Suite.cpp b/libyul/optimiser/Suite.cpp index ac92761d7..97feac36b 100644 --- a/libyul/optimiser/Suite.cpp +++ b/libyul/optimiser/Suite.cpp @@ -33,6 +33,7 @@ #include #include #include +#include #include #include #include @@ -91,8 +92,10 @@ void OptimiserSuite::run( UnusedPruner::runUntilStabilisedOnFullAST(_dialect, ast, reservedIdentifiers); BlockFlattener{}(ast); ControlFlowSimplifier{_dialect}(ast); - StructuralSimplifier{_dialect}(ast); + LiteralRematerialiser{_dialect}(ast); + StructuralSimplifier{}(ast); ControlFlowSimplifier{_dialect}(ast); + ForLoopConditionIntoBody{_dialect}(ast); BlockFlattener{}(ast); // None of the above can make stack problems worse. @@ -125,7 +128,8 @@ void OptimiserSuite::run( { // still in SSA, perform structural simplification ControlFlowSimplifier{_dialect}(ast); - StructuralSimplifier{_dialect}(ast); + LiteralRematerialiser{_dialect}(ast); + StructuralSimplifier{}(ast); ControlFlowSimplifier{_dialect}(ast); BlockFlattener{}(ast); DeadCodeEliminator{_dialect}(ast); @@ -182,7 +186,8 @@ void OptimiserSuite::run( RedundantAssignEliminator::run(_dialect, ast); LoadResolver::run(_dialect, ast); ExpressionSimplifier::run(_dialect, ast); - StructuralSimplifier{_dialect}(ast); + LiteralRematerialiser{_dialect}(ast); + StructuralSimplifier{}(ast); BlockFlattener{}(ast); DeadCodeEliminator{_dialect}(ast); ControlFlowSimplifier{_dialect}(ast); diff --git a/test/Common.cpp b/test/Common.cpp index a6dd23e0b..48966d9a3 100644 --- a/test/Common.cpp +++ b/test/Common.cpp @@ -76,7 +76,7 @@ std::string EVMOneEnvOrDefaultPath() }; for (auto const& basePath: searchPath) { - fs::path p = basePath / "libevmone.so"; + fs::path p = basePath / evmoneFilename; if (fs::exists(p)) return p.string(); } @@ -92,7 +92,7 @@ CommonOptions::CommonOptions(std::string _caption): options.add_options() ("evm-version", po::value(&evmVersionString), "which evm version to use") ("testpath", po::value(&this->testPath)->default_value(dev::test::testPath()), "path to test files") - ("evmonepath", po::value(&evmonePath)->default_value(EVMOneEnvOrDefaultPath()), "path to libevmone.so") + ("evmonepath", po::value(&evmonePath)->default_value(EVMOneEnvOrDefaultPath()), "path to evmone library") ("no-smt", po::bool_switch(&disableSMT), "disable SMT checker"); } diff --git a/test/Common.h b/test/Common.h index 6daf17675..ebbe2d2ad 100644 --- a/test/Common.h +++ b/test/Common.h @@ -30,6 +30,18 @@ namespace dev namespace test { +#ifdef _WIN32 +static constexpr auto evmoneFilename = "evmone.dll"; +static constexpr auto evmoneDownloadLink = "https://github.com/ethereum/evmone/releases/download/v0.1.0/evmone-0.1.0-windows-amd64.zip"; +#elif defined(__APPLE__) +static constexpr auto evmoneFilename = "libevmone.dylib"; +static constexpr auto evmoneDownloadLink = "https://github.com/ethereum/evmone/releases/download/v0.1.0/evmone-0.1.0-darwin-x86_64.tar.gz"; +#else +static constexpr auto evmoneFilename = "libevmone.so"; +static constexpr auto evmoneDownloadLink = "https://github.com/ethereum/evmone/releases/download/v0.1.0/evmone-0.1.0-linux-x86_64.tar.gz"; +#endif + + struct ConfigException : public Exception {}; struct CommonOptions: boost::noncopyable diff --git a/test/EVMHost.cpp b/test/EVMHost.cpp index 5122f3846..19d8f6f77 100644 --- a/test/EVMHost.cpp +++ b/test/EVMHost.cpp @@ -61,7 +61,7 @@ EVMHost::EVMHost(langutil::EVMVersion _evmVersion, evmc::vm* _vm): { if (!m_vm) { - cerr << "Unable to find library libevmone.so" << endl; + cerr << "Unable to find evmone library" << endl; assertThrow(false, Exception, ""); } diff --git a/test/boostTest.cpp b/test/boostTest.cpp index 87cc07525..fd33f667e 100644 --- a/test/boostTest.cpp +++ b/test/boostTest.cpp @@ -143,9 +143,9 @@ test_suite* init_unit_test_suite( int /*argc*/, char* /*argv*/[] ) bool disableSemantics = !dev::test::EVMHost::getVM(dev::test::Options::get().evmonePath.string()); if (disableSemantics) { - cout << "Unable to find libevmone.so. Please provide the path using -- --evmonepath ." << endl; + cout << "Unable to find " << dev::test::evmoneFilename << ". Please provide the path using -- --evmonepath ." << endl; cout << "You can download it at" << endl; - cout << "https://github.com/ethereum/evmone/releases/download/v0.1.0/evmone-0.1.0-linux-x86_64.tar.gz" << endl; + cout << dev::test::evmoneDownloadLink << endl; cout << endl << "--- SKIPPING ALL SEMANTICS TESTS ---" << endl << endl; } // Include the interactive tests in the automatic tests as well diff --git a/test/cmdlineTests/yul_stack_opt/output b/test/cmdlineTests/yul_stack_opt/output index 0f987c7e6..7168d526b 100644 --- a/test/cmdlineTests/yul_stack_opt/output +++ b/test/cmdlineTests/yul_stack_opt/output @@ -11,20 +11,20 @@ object "object" { } function fun() -> a3, b3, c3, d3, e3, f3, g3, h3, i3, j3, k3, l3, m3, n3, o3, p3 { - let a := 1 - sstore(a, a) - sstore(2, a) - sstore(3, a) - sstore(4, a) - sstore(5, a) - sstore(6, a) - sstore(7, a) - sstore(8, a) - sstore(9, a) - sstore(10, a) - sstore(11, a) - sstore(12, a) - sstore(13, a) + let _1 := 1 + sstore(_1, _1) + sstore(2, _1) + sstore(3, _1) + sstore(4, _1) + sstore(5, _1) + sstore(6, _1) + sstore(7, _1) + sstore(8, _1) + sstore(9, _1) + sstore(10, _1) + sstore(11, _1) + sstore(12, _1) + sstore(13, _1) } } } @@ -103,79 +103,77 @@ tag_2: 0x00 /* "yul_stack_opt/input.sol":98:99 */ 0x01 - /* "yul_stack_opt/input.sol":139:140 */ dup1 - /* "yul_stack_opt/input.sol":136:137 */ dup2 /* "yul_stack_opt/input.sol":129:141 */ sstore - /* "yul_stack_opt/input.sol":162:163 */ + /* "yul_stack_opt/input.sol":98:99 */ dup1 /* "yul_stack_opt/input.sol":151:160 */ 0x02 /* "yul_stack_opt/input.sol":144:164 */ sstore - /* "yul_stack_opt/input.sol":185:186 */ + /* "yul_stack_opt/input.sol":98:99 */ dup1 /* "yul_stack_opt/input.sol":174:183 */ 0x03 /* "yul_stack_opt/input.sol":167:187 */ sstore - /* "yul_stack_opt/input.sol":208:209 */ + /* "yul_stack_opt/input.sol":98:99 */ dup1 /* "yul_stack_opt/input.sol":197:206 */ 0x04 /* "yul_stack_opt/input.sol":190:210 */ sstore - /* "yul_stack_opt/input.sol":231:232 */ + /* "yul_stack_opt/input.sol":98:99 */ dup1 /* "yul_stack_opt/input.sol":220:229 */ 0x05 /* "yul_stack_opt/input.sol":213:233 */ sstore - /* "yul_stack_opt/input.sol":254:255 */ + /* "yul_stack_opt/input.sol":98:99 */ dup1 /* "yul_stack_opt/input.sol":243:252 */ 0x06 /* "yul_stack_opt/input.sol":236:256 */ sstore - /* "yul_stack_opt/input.sol":277:278 */ + /* "yul_stack_opt/input.sol":98:99 */ dup1 /* "yul_stack_opt/input.sol":266:275 */ 0x07 /* "yul_stack_opt/input.sol":259:279 */ sstore - /* "yul_stack_opt/input.sol":300:301 */ + /* "yul_stack_opt/input.sol":98:99 */ dup1 /* "yul_stack_opt/input.sol":289:298 */ 0x08 /* "yul_stack_opt/input.sol":282:302 */ sstore - /* "yul_stack_opt/input.sol":323:324 */ + /* "yul_stack_opt/input.sol":98:99 */ dup1 /* "yul_stack_opt/input.sol":312:321 */ 0x09 /* "yul_stack_opt/input.sol":305:325 */ sstore - /* "yul_stack_opt/input.sol":346:347 */ + /* "yul_stack_opt/input.sol":98:99 */ dup1 /* "yul_stack_opt/input.sol":335:344 */ 0x0a /* "yul_stack_opt/input.sol":328:348 */ sstore - /* "yul_stack_opt/input.sol":370:371 */ + /* "yul_stack_opt/input.sol":98:99 */ dup1 /* "yul_stack_opt/input.sol":358:368 */ 0x0b /* "yul_stack_opt/input.sol":351:372 */ sstore - /* "yul_stack_opt/input.sol":394:395 */ + /* "yul_stack_opt/input.sol":98:99 */ dup1 /* "yul_stack_opt/input.sol":382:392 */ 0x0c /* "yul_stack_opt/input.sol":375:396 */ sstore - /* "yul_stack_opt/input.sol":418:419 */ + /* "yul_stack_opt/input.sol":98:99 */ dup1 /* "yul_stack_opt/input.sol":406:416 */ 0x0d diff --git a/test/libsolidity/gasTests/abiv2_optimised.sol b/test/libsolidity/gasTests/abiv2_optimised.sol index dcd52b1e2..f22576380 100644 --- a/test/libsolidity/gasTests/abiv2_optimised.sol +++ b/test/libsolidity/gasTests/abiv2_optimised.sol @@ -17,9 +17,9 @@ contract C { // optimize-yul: true // ---- // creation: -// codeDepositCost: 610600 -// executionCost: 645 -// totalCost: 611245 +// codeDepositCost: 624200 +// executionCost: 657 +// totalCost: 624857 // external: // a(): 429 // b(uint256): 884 diff --git a/test/libsolidity/util/BytesUtils.cpp b/test/libsolidity/util/BytesUtils.cpp index 0d55a7920..af104e86e 100644 --- a/test/libsolidity/util/BytesUtils.cpp +++ b/test/libsolidity/util/BytesUtils.cpp @@ -208,21 +208,21 @@ string BytesUtils::formatRawBytes( dev::solidity::test::ParameterList const& _parameters, string _linePrefix) { - soltestAssert( - _bytes.size() == ContractABIUtils::encodingSize(_parameters), - "Got " + to_string(_bytes.size()) + " bytes, but expected " + - to_string(ContractABIUtils::encodingSize(_parameters)) + " bytes." - ); - stringstream os; + ParameterList parameters; auto it = _bytes.begin(); - for (auto const& parameter: _parameters) + if (_bytes.size() != ContractABIUtils::encodingSize(_parameters)) + parameters = ContractABIUtils::defaultParameters(ceil(_bytes.size() / 32)); + else + parameters = _parameters; + + for (auto const& parameter: parameters) { bytes byteRange{it, it + static_cast(parameter.abiType.size)}; os << _linePrefix << byteRange; - if (¶meter != &_parameters.back()) + if (¶meter != ¶meters.back()) os << endl; it += static_cast(parameter.abiType.size); @@ -279,16 +279,17 @@ string BytesUtils::formatBytesRange( bool _highlight ) { - soltestAssert( - _bytes.size() == ContractABIUtils::encodingSize(_parameters), - "Got " + to_string(_bytes.size()) + " bytes, but expected " + - to_string(ContractABIUtils::encodingSize(_parameters)) + " bytes." - ); - stringstream os; + ParameterList parameters; auto it = _bytes.begin(); - for (auto const& parameter: _parameters) + if (_bytes.size() != ContractABIUtils::encodingSize(_parameters)) + parameters = ContractABIUtils::defaultParameters(ceil(_bytes.size() / 32)); + else + parameters = _parameters; + + + for (auto const& parameter: parameters) { bytes byteRange{it, it + static_cast(parameter.abiType.size)}; @@ -301,7 +302,7 @@ string BytesUtils::formatBytesRange( else os << parameter.rawString; - if (¶meter != &_parameters.back()) + if (¶meter != ¶meters.back()) os << ", "; it += static_cast(parameter.abiType.size); diff --git a/test/libsolidity/util/TestFunctionCall.cpp b/test/libsolidity/util/TestFunctionCall.cpp index a63e02865..25b60a184 100644 --- a/test/libsolidity/util/TestFunctionCall.cpp +++ b/test/libsolidity/util/TestFunctionCall.cpp @@ -140,7 +140,11 @@ string TestFunctionCall::format( string bytesOutput = abiParams ? BytesUtils::formatRawBytes(output, abiParams.get(), _linePrefix) : - _linePrefix + "[]"; + BytesUtils::formatRawBytes( + output, + ContractABIUtils::defaultParameters(ceil(output.size() / 32)), + _linePrefix + ); _errorReporter.warning( "The call to \"" + m_call.signature + "\" returned \n" + diff --git a/test/libyul/YulOptimizerTest.cpp b/test/libyul/YulOptimizerTest.cpp index 88e8c4e2b..b0d6d928c 100644 --- a/test/libyul/YulOptimizerTest.cpp +++ b/test/libyul/YulOptimizerTest.cpp @@ -141,7 +141,7 @@ TestCase::TestResult YulOptimizerTest::run(ostream& _stream, string const& _line else if (m_optimizerStep == "forLoopConditionIntoBody") { disambiguate(); - ForLoopConditionIntoBody{}(*m_ast); + ForLoopConditionIntoBody{*m_dialect}(*m_ast); } else if (m_optimizerStep == "forLoopInitRewriter") { @@ -279,7 +279,9 @@ TestCase::TestResult YulOptimizerTest::run(ostream& _stream, string const& _line else if (m_optimizerStep == "structuralSimplifier") { disambiguate(); - StructuralSimplifier{*m_dialect}(*m_ast); + ForLoopInitRewriter{}(*m_ast); + LiteralRematerialiser{*m_dialect}(*m_ast); + StructuralSimplifier{}(*m_ast); } else if (m_optimizerStep == "equivalentFunctionCombiner") { diff --git a/test/libyul/yulOptimizerTests/commonSubexpressionEliminator/loop.yul b/test/libyul/yulOptimizerTests/commonSubexpressionEliminator/loop.yul new file mode 100644 index 000000000..4725a444e --- /dev/null +++ b/test/libyul/yulOptimizerTests/commonSubexpressionEliminator/loop.yul @@ -0,0 +1,77 @@ +{ + let _1 := 0 + let _33 := calldataload(_1) + let sum_50_141 := _1 + let sum_50_146 := _1 + let sum_50 := _1 + let length_51 := calldataload(_33) + let i_53_142 := _1 + let i_53_147 := _1 + let i_53 := _1 + for { } + 1 + { + let _108 := 1 + let i_53_121 := add(i_53, _108) + let i_53_144 := i_53_121 + let i_53_149 := i_53_121 + i_53 := i_53_121 + } + { + let _109 := lt(i_53, length_51) + let _110 := iszero(_109) + if _110 { break } + let _114_128 := iszero(_109) + if _114_128 { revert(_1, _1) } + let _13_129 := 0x20 + let _115_130 := mul(i_53, _13_129) + let _116_131 := add(_33, _115_130) + let _117_132 := add(_116_131, _13_129) + let v_122_133 := calldataload(_117_132) + let sum_50_120 := add(sum_50, v_122_133) + let sum_50_143 := sum_50_120 + let sum_50_148 := sum_50_120 + sum_50 := sum_50_120 + } + sstore(_1, sum_50) +} +// ==== +// step: commonSubexpressionEliminator +// ---- +// { +// let _1 := 0 +// let _33 := calldataload(_1) +// let sum_50_141 := _1 +// let sum_50_146 := _1 +// let sum_50 := _1 +// let length_51 := calldataload(_33) +// let i_53_142 := _1 +// let i_53_147 := _1 +// let i_53 := _1 +// for { } +// 1 +// { +// let _108 := 1 +// let i_53_121 := add(i_53, _108) +// let i_53_144 := i_53_121 +// let i_53_149 := i_53_121 +// i_53 := i_53_121 +// } +// { +// let _109 := lt(i_53, length_51) +// let _110 := iszero(_109) +// if _110 { break } +// let _114_128 := _110 +// if _110 { revert(_1, _1) } +// let _13_129 := 0x20 +// let _115_130 := mul(i_53, _13_129) +// let _116_131 := add(_33, _115_130) +// let _117_132 := add(_116_131, _13_129) +// let v_122_133 := calldataload(_117_132) +// let sum_50_120 := add(sum_50, v_122_133) +// let sum_50_143 := sum_50_120 +// let sum_50_148 := sum_50_120 +// sum_50 := sum_50_120 +// } +// sstore(_1, sum_50) +// } diff --git a/test/libyul/yulOptimizerTests/fullSuite/abi_example1.yul b/test/libyul/yulOptimizerTests/fullSuite/abi_example1.yul index fdb6e724e..6d2a36895 100644 --- a/test/libyul/yulOptimizerTests/fullSuite/abi_example1.yul +++ b/test/libyul/yulOptimizerTests/fullSuite/abi_example1.yul @@ -462,34 +462,35 @@ // ---- // { // { -// let _1 := 0x20 -// let _2 := 0 -// let _3 := mload(_2) -// let pos := _1 -// let length := mload(_3) -// mstore(_1, length) +// let _1 := 0 +// let _2 := mload(_1) +// let pos := 0x20 +// let pos_1 := pos +// let length := mload(_2) +// mstore(pos, length) // pos := 64 -// let srcPtr := add(_3, _1) -// let i := _2 -// for { } lt(i, length) { i := add(i, 1) } +// let srcPtr := add(_2, pos_1) +// let i := _1 +// for { } 1 { i := add(i, 1) } // { +// if iszero(lt(i, length)) { break } // abi_encode_t_array$_t_contract$_C_$55_$3_memory_to_t_array$_t_address_$3_memory_ptr(mload(srcPtr), pos) -// srcPtr := add(srcPtr, _1) +// srcPtr := add(srcPtr, pos_1) // pos := add(pos, 0x60) // } -// let _4 := mload(64) -// let _5 := mload(_1) -// if slt(sub(_4, _5), 128) { revert(_2, _2) } -// let offset := calldataload(add(_5, 64)) -// let _6 := 0xffffffffffffffff -// if gt(offset, _6) { revert(_2, _2) } -// let value2 := abi_decode_t_array$_t_uint256_$dyn_memory_ptr(add(_5, offset), _4) -// let offset_1 := calldataload(add(_5, 96)) -// if gt(offset_1, _6) { revert(_2, _2) } -// let value3 := abi_decode_t_array$_t_array$_t_uint256_$2_memory_$dyn_memory_ptr(add(_5, offset_1), _4) -// sstore(calldataload(_5), calldataload(add(_5, _1))) +// let _3 := mload(64) +// let _4 := mload(pos_1) +// if slt(sub(_3, _4), 128) { revert(_1, _1) } +// let offset := calldataload(add(_4, 64)) +// let _5 := 0xffffffffffffffff +// if gt(offset, _5) { revert(_1, _1) } +// let value2 := abi_decode_t_array$_t_uint256_$dyn_memory_ptr(add(_4, offset), _3) +// let offset_1 := calldataload(add(_4, 96)) +// if gt(offset_1, _5) { revert(_1, _1) } +// let value3 := abi_decode_t_array$_t_array$_t_uint256_$2_memory_$dyn_memory_ptr(add(_4, offset_1), _3) +// sstore(calldataload(_4), calldataload(add(_4, pos_1))) // sstore(value2, value3) -// sstore(_2, pos) +// sstore(_1, pos) // } // function abi_decode_t_array$_t_array$_t_uint256_$2_memory_$dyn_memory_ptr(offset, end) -> array // { @@ -503,8 +504,9 @@ // let src := add(offset, _1) // if gt(add(add(offset, mul(length, 0x40)), _1), end) { revert(0, 0) } // let i := 0 -// for { } lt(i, length) { i := add(i, 1) } +// for { } 1 { i := add(i, 1) } // { +// if iszero(lt(i, length)) { break } // if iszero(slt(add(src, 0x1f), end)) { revert(0, 0) } // let dst_1 := allocateMemory(array_allocation_size_t_array$_t_uint256_$2_memory(0x2)) // let dst_2 := dst_1 @@ -512,8 +514,9 @@ // let _2 := add(src, 0x40) // if gt(_2, end) { revert(0, 0) } // let i_1 := 0 -// for { } lt(i_1, 0x2) { i_1 := add(i_1, 1) } +// for { } 1 { i_1 := add(i_1, 1) } // { +// if iszero(lt(i_1, 0x2)) { break } // mstore(dst_1, calldataload(src_1)) // dst_1 := add(dst_1, _1) // src_1 := add(src_1, _1) @@ -535,8 +538,9 @@ // let src := add(offset, _1) // if gt(add(add(offset, mul(length, _1)), _1), end) { revert(0, 0) } // let i := 0 -// for { } lt(i, length) { i := add(i, 1) } +// for { } 1 { i := add(i, 1) } // { +// if iszero(lt(i, length)) { break } // mstore(dst, calldataload(src)) // dst := add(dst, _1) // src := add(src, _1) @@ -546,8 +550,9 @@ // { // let srcPtr := value // let i := 0 -// for { } lt(i, 0x3) { i := add(i, 1) } +// for { } 1 { i := add(i, 1) } // { +// if iszero(lt(i, 0x3)) { break } // mstore(pos, and(mload(srcPtr), sub(shl(160, 1), 1))) // srcPtr := add(srcPtr, 0x20) // pos := add(pos, 0x20) diff --git a/test/libyul/yulOptimizerTests/fullSuite/aztec.yul b/test/libyul/yulOptimizerTests/fullSuite/aztec.yul index 82a6dcb6a..20662f53a 100644 --- a/test/libyul/yulOptimizerTests/fullSuite/aztec.yul +++ b/test/libyul/yulOptimizerTests/fullSuite/aztec.yul @@ -238,8 +238,8 @@ // let notes := add(0x04, calldataload(0x04)) // let m := calldataload(0x24) // let n := calldataload(notes) -// let gen_order := 0x30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f0000001 -// let challenge := mod(calldataload(0x44), gen_order) +// let _1 := 0x30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f0000001 +// let challenge := mod(calldataload(0x44), _1) // if gt(m, n) // { // mstore(0x00, 404) @@ -249,72 +249,71 @@ // mstore(0x2a0, caller()) // mstore(0x2c0, kn) // mstore(0x2e0, m) -// kn := mulmod(sub(gen_order, kn), challenge, gen_order) +// kn := mulmod(sub(_1, kn), challenge, _1) // hashCommitments(notes, n) // let b := add(0x300, mul(n, 0x80)) // let i := 0 // let i_1 := i -// for { } lt(i, n) { i := add(i, 0x01) } +// for { } 1 { i := add(i, 0x01) } // { -// let _1 := add(calldataload(0x04), mul(i, 0xc0)) -// let noteIndex := add(_1, 0x24) +// if iszero(lt(i, n)) { break } +// let _2 := add(calldataload(0x04), mul(i, 0xc0)) +// let noteIndex := add(_2, 0x24) // let k := i_1 -// let a := calldataload(add(_1, 0x44)) +// let a := calldataload(add(_2, 0x44)) // let c := challenge -// let _2 := add(i, 0x01) -// switch eq(_2, n) +// let _3 := add(i, 0x01) +// switch eq(_3, n) // case 1 { // k := kn -// if eq(m, n) { k := sub(gen_order, kn) } +// if eq(m, n) { k := sub(_1, kn) } // } // case 0 { k := calldataload(noteIndex) } // validateCommitment(noteIndex, k, a) -// switch gt(_2, m) +// switch gt(_3, m) // case 1 { -// kn := addmod(kn, sub(gen_order, k), gen_order) -// let x := mod(mload(i_1), gen_order) -// k := mulmod(k, x, gen_order) -// a := mulmod(a, x, gen_order) -// c := mulmod(challenge, x, gen_order) +// kn := addmod(kn, sub(_1, k), _1) +// let x := mod(mload(i_1), _1) +// k := mulmod(k, x, _1) +// a := mulmod(a, x, _1) +// c := mulmod(challenge, x, _1) // mstore(i_1, keccak256(i_1, 0x20)) // } -// case 0 { -// kn := addmod(kn, k, gen_order) -// } -// let _3 := 0x40 -// calldatacopy(0xe0, add(_1, 164), _3) -// calldatacopy(0x20, add(_1, 100), _3) -// mstore(0x120, sub(gen_order, c)) +// case 0 { kn := addmod(kn, k, _1) } +// let _4 := 0x40 +// calldatacopy(0xe0, add(_2, 164), _4) +// calldatacopy(0x20, add(_2, 100), _4) +// mstore(0x120, sub(_1, c)) // mstore(0x60, k) // mstore(0xc0, a) -// let result := call(gas(), 7, i_1, 0xe0, 0x60, 0x1a0, _3) -// let result_1 := and(result, call(gas(), 7, i_1, 0x20, 0x60, 0x120, _3)) -// let result_2 := and(result_1, call(gas(), 7, i_1, 0x80, 0x60, 0x160, _3)) -// let result_3 := and(result_2, call(gas(), 6, i_1, 0x120, 0x80, 0x160, _3)) -// result := and(result_3, call(gas(), 6, i_1, 0x160, 0x80, b, _3)) +// let result := call(gas(), 7, i_1, 0xe0, 0x60, 0x1a0, _4) +// let result_1 := and(result, call(gas(), 7, i_1, 0x20, 0x60, 0x120, _4)) +// let result_2 := and(result_1, call(gas(), 7, i_1, 0x80, 0x60, 0x160, _4)) +// let result_3 := and(result_2, call(gas(), 6, i_1, 0x120, 0x80, 0x160, _4)) +// result := and(result_3, call(gas(), 6, i_1, 0x160, 0x80, b, _4)) // if eq(i, m) // { // mstore(0x260, mload(0x20)) -// mstore(0x280, mload(_3)) +// mstore(0x280, mload(_4)) // mstore(0x1e0, mload(0xe0)) // mstore(0x200, sub(0x30644e72e131a029b85045b68181585d97816a916871ca8d3c208c16d87cfd47, mload(0x100))) // } // if gt(i, m) // { // mstore(0x60, c) -// let result_4 := and(result, call(gas(), 7, i_1, 0x20, 0x60, 0x220, _3)) -// let result_5 := and(result_4, call(gas(), 6, i_1, 0x220, 0x80, 0x260, _3)) -// result := and(result_5, call(gas(), 6, i_1, 0x1a0, 0x80, 0x1e0, _3)) +// let result_4 := and(result, call(gas(), 7, i_1, 0x20, 0x60, 0x220, _4)) +// let result_5 := and(result_4, call(gas(), 6, i_1, 0x220, 0x80, 0x260, _4)) +// result := and(result_5, call(gas(), 6, i_1, 0x1a0, 0x80, 0x1e0, _4)) // } // if iszero(result) // { // mstore(i_1, 400) // revert(i_1, 0x20) // } -// b := add(b, _3) +// b := add(b, _4) // } // if lt(m, n) { validatePairing(0x64) } -// if iszero(eq(mod(keccak256(0x2a0, add(b, not(671))), gen_order), challenge)) +// if iszero(eq(mod(keccak256(0x2a0, add(b, not(671))), _1), challenge)) // { // mstore(i_1, 404) // revert(i_1, 0x20) @@ -360,13 +359,13 @@ // } // function validateCommitment(note, k, a) // { -// let gen_order := 0x30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f0000001 -// let field_order := 0x30644e72e131a029b85045b68181585d97816a916871ca8d3c208c16d87cfd47 // let gammaX := calldataload(add(note, 0x40)) // let gammaY := calldataload(add(note, 0x60)) // let sigmaX := calldataload(add(note, 0x80)) // let sigmaY := calldataload(add(note, 0xa0)) -// if iszero(and(and(and(eq(mod(a, gen_order), a), gt(a, 1)), and(eq(mod(k, gen_order), k), gt(k, 1))), and(eq(addmod(mulmod(mulmod(sigmaX, sigmaX, field_order), sigmaX, field_order), 3, field_order), mulmod(sigmaY, sigmaY, field_order)), eq(addmod(mulmod(mulmod(gammaX, gammaX, field_order), gammaX, field_order), 3, field_order), mulmod(gammaY, gammaY, field_order))))) +// let _1 := 0x30644e72e131a029b85045b68181585d97816a916871ca8d3c208c16d87cfd47 +// let _2 := 0x30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f0000001 +// if iszero(and(and(and(eq(mod(a, _2), a), gt(a, 1)), and(eq(mod(k, _2), k), gt(k, 1))), and(eq(addmod(mulmod(mulmod(sigmaX, sigmaX, _1), sigmaX, _1), 3, _1), mulmod(sigmaY, sigmaY, _1)), eq(addmod(mulmod(mulmod(gammaX, gammaX, _1), gammaX, _1), 3, _1), mulmod(gammaY, gammaY, _1))))) // { // mstore(0x00, 400) // revert(0x00, 0x20) @@ -375,8 +374,9 @@ // function hashCommitments(notes, n) // { // let i := 0 -// for { } lt(i, n) { i := add(i, 0x01) } +// for { } 1 { i := add(i, 0x01) } // { +// if iszero(lt(i, n)) { break } // calldatacopy(add(0x300, mul(i, 0x80)), add(add(notes, mul(i, 0xc0)), 0x60), 0x80) // } // mstore(0, keccak256(0x300, mul(n, 0x80))) diff --git a/test/libyul/yulOptimizerTests/ssaReverser/self_assign.yul b/test/libyul/yulOptimizerTests/ssaReverser/self_assign.yul new file mode 100644 index 000000000..48b9d39cf --- /dev/null +++ b/test/libyul/yulOptimizerTests/ssaReverser/self_assign.yul @@ -0,0 +1,8 @@ +{ + let a := calldataload(0) + a := a +} +// ==== +// step: ssaReverser +// ---- +// { let a := calldataload(0) } diff --git a/test/tools/isoltest.cpp b/test/tools/isoltest.cpp index 9e142079f..08b11f40b 100644 --- a/test/tools/isoltest.cpp +++ b/test/tools/isoltest.cpp @@ -417,9 +417,9 @@ int main(int argc, char const *argv[]) bool disableSemantics = !dev::test::EVMHost::getVM(options.evmonePath.string()); if (disableSemantics) { - cout << "Unable to find libevmone.so. Please provide the path using --evmonepath ." << endl; + cout << "Unable to find " << dev::test::evmoneFilename << ". Please provide the path using --evmonepath ." << endl; cout << "You can download it at" << endl; - cout << "https://github.com/ethereum/evmone/releases/download/v0.1.0/evmone-0.1.0-linux-x86_64.tar.gz" << endl; + cout << dev::test::evmoneDownloadLink << endl; cout << endl << "--- SKIPPING ALL SEMANTICS TESTS ---" << endl << endl; } @@ -462,7 +462,7 @@ int main(int argc, char const *argv[]) cout << "." << endl; if (disableSemantics) - cout << "\nNOTE: Skipped semantics tests because libevmone.so could not be found.\n" << endl; + cout << "\nNOTE: Skipped semantics tests because " << dev::test::evmoneFilename << " could not be found.\n" << endl; return global_stats ? 0 : 1; } diff --git a/test/tools/ossfuzz/CMakeLists.txt b/test/tools/ossfuzz/CMakeLists.txt index 4528583de..1eca9fb2b 100644 --- a/test/tools/ossfuzz/CMakeLists.txt +++ b/test/tools/ossfuzz/CMakeLists.txt @@ -17,24 +17,29 @@ if (OSSFUZZ) endif() if (OSSFUZZ) - #[[FuzzingEngine.a is provided by oss-fuzz's Dockerized build environment]] add_executable(solc_opt_ossfuzz solc_opt_ossfuzz.cpp ../fuzzer_common.cpp) - target_link_libraries(solc_opt_ossfuzz PRIVATE libsolc evmasm FuzzingEngine.a) + target_link_libraries(solc_opt_ossfuzz PRIVATE libsolc evmasm) + set_target_properties(solc_opt_ossfuzz PROPERTIES LINK_FLAGS "-fsanitize=fuzzer") add_executable(solc_noopt_ossfuzz solc_noopt_ossfuzz.cpp ../fuzzer_common.cpp) - target_link_libraries(solc_noopt_ossfuzz PRIVATE libsolc evmasm FuzzingEngine.a) + target_link_libraries(solc_noopt_ossfuzz PRIVATE libsolc evmasm) + set_target_properties(solc_noopt_ossfuzz PROPERTIES LINK_FLAGS "-fsanitize=fuzzer") add_executable(const_opt_ossfuzz const_opt_ossfuzz.cpp ../fuzzer_common.cpp) - target_link_libraries(const_opt_ossfuzz PRIVATE libsolc evmasm FuzzingEngine.a) + target_link_libraries(const_opt_ossfuzz PRIVATE libsolc evmasm) + set_target_properties(const_opt_ossfuzz PROPERTIES LINK_FLAGS "-fsanitize=fuzzer") add_executable(strictasm_diff_ossfuzz strictasm_diff_ossfuzz.cpp yulFuzzerCommon.cpp) - target_link_libraries(strictasm_diff_ossfuzz PRIVATE libsolc evmasm yulInterpreter FuzzingEngine.a) + target_link_libraries(strictasm_diff_ossfuzz PRIVATE libsolc evmasm yulInterpreter) + set_target_properties(strictasm_diff_ossfuzz PROPERTIES LINK_FLAGS "-fsanitize=fuzzer") add_executable(strictasm_opt_ossfuzz strictasm_opt_ossfuzz.cpp) - target_link_libraries(strictasm_opt_ossfuzz PRIVATE yul FuzzingEngine.a) + target_link_libraries(strictasm_opt_ossfuzz PRIVATE yul) + set_target_properties(strictasm_opt_ossfuzz PROPERTIES LINK_FLAGS "-fsanitize=fuzzer") add_executable(strictasm_assembly_ossfuzz strictasm_assembly_ossfuzz.cpp) - target_link_libraries(strictasm_assembly_ossfuzz PRIVATE yul FuzzingEngine.a) + target_link_libraries(strictasm_assembly_ossfuzz PRIVATE yul) + set_target_properties(strictasm_assembly_ossfuzz PROPERTIES LINK_FLAGS "-fsanitize=fuzzer") add_executable(yul_proto_ossfuzz yulProtoFuzzer.cpp protoToYul.cpp yulProto.pb.cc) target_include_directories(yul_proto_ossfuzz PRIVATE /usr/include/libprotobuf-mutator) @@ -42,7 +47,8 @@ if (OSSFUZZ) protobuf-mutator-libfuzzer.a protobuf-mutator.a protobuf.a - FuzzingEngine.a) + ) + set_target_properties(yul_proto_ossfuzz PROPERTIES LINK_FLAGS "-fsanitize=fuzzer") add_executable(yul_proto_diff_ossfuzz yulProto_diff_ossfuzz.cpp yulFuzzerCommon.cpp protoToYul.cpp yulProto.pb.cc) target_include_directories(yul_proto_diff_ossfuzz PRIVATE /usr/include/libprotobuf-mutator) @@ -51,7 +57,8 @@ if (OSSFUZZ) protobuf-mutator-libfuzzer.a protobuf-mutator.a protobuf.a - FuzzingEngine.a) + ) + set_target_properties(yul_proto_diff_ossfuzz PROPERTIES LINK_FLAGS "-fsanitize=fuzzer") add_executable(abiv2_proto_ossfuzz ../../EVMHost.cpp @@ -68,8 +75,8 @@ if (OSSFUZZ) protobuf-mutator-libfuzzer.a protobuf-mutator.a protobuf.a - FuzzingEngine.a ) + set_target_properties(abiv2_proto_ossfuzz PROPERTIES LINK_FLAGS "-fsanitize=fuzzer") else() add_library(solc_opt_ossfuzz solc_opt_ossfuzz.cpp diff --git a/test/tools/ossfuzz/protoToYul.cpp b/test/tools/ossfuzz/protoToYul.cpp index 70f0e3ec5..345797afb 100644 --- a/test/tools/ossfuzz/protoToYul.cpp +++ b/test/tools/ossfuzz/protoToYul.cpp @@ -54,6 +54,10 @@ string ProtoConverter::createHex(string const& _hexBytes) // Use a dictionary token. if (tmp.empty()) tmp = dictionaryToken(HexPrefix::DontAdd); + // Hex literals must have even number of digits + if (tmp.size() % 2) + tmp.insert(0, "0"); + yulAssert(tmp.size() <= 64, "Proto Fuzzer: Dictionary token too large"); return tmp; } @@ -158,6 +162,12 @@ void ProtoConverter::visit(Expression const& _x) case Expression::kCreate: visit(_x.create()); break; + case Expression::kUnopdata: + if (m_isObject) + visit(_x.unopdata()); + else + m_output << dictionaryToken(); + break; case Expression::EXPR_ONEOF_NOT_SET: m_output << dictionaryToken(); break; @@ -432,7 +442,14 @@ void ProtoConverter::visit(NullaryOp const& _x) void ProtoConverter::visit(CopyFunc const& _x) { - switch (_x.ct()) + CopyFunc_CopyType type = _x.ct(); + + // datacopy() is valid only if we are inside + // a yul object. + if (type == CopyFunc::DATA && !m_isObject) + return; + + switch (type) { case CopyFunc::CALLDATA: m_output << "calldatacopy"; @@ -443,6 +460,9 @@ void ProtoConverter::visit(CopyFunc const& _x) case CopyFunc::RETURNDATA: m_output << "returndatacopy"; break; + case CopyFunc::DATA: + m_output << "datacopy"; + break; } m_output << "("; visit(_x.target()); @@ -988,6 +1008,23 @@ void ProtoConverter::visit(TerminatingStmt const& _x) } } +void ProtoConverter::visit(UnaryOpData const& _x) +{ + switch (_x.op()) + { + case UnaryOpData::SIZE: + m_output << Whiskers(R"(datasize(""))") + ("id", getObjectIdentifier(_x.identifier())) + .render(); + break; + case UnaryOpData::OFFSET: + m_output << Whiskers(R"(dataoffset(""))") + ("id", getObjectIdentifier(_x.identifier())) + .render(); + break; + } +} + void ProtoConverter::visit(Statement const& _x) { switch (_x.stmt_oneof_case()) @@ -1347,21 +1384,89 @@ void ProtoConverter::visit(PopStmt const& _x) m_output << ")\n"; } +string ProtoConverter::getObjectIdentifier(ObjectId const& _x) +{ + unsigned currentId = currentObjectId(); + yulAssert(m_objectScopeTree.size() > currentId, "Proto fuzzer: Error referencing object"); + std::vector objectIdsInScope = m_objectScopeTree[currentId]; + return objectIdsInScope[_x.id() % objectIdsInScope.size()]; +} + +void ProtoConverter::visit(Code const& _x) +{ + m_output << "code {\n"; + visit(_x.block()); + m_output << "}\n"; +} + +void ProtoConverter::visit(Data const& _x) +{ + // TODO: Generate random data block identifier + m_output << "data \"" << s_dataIdentifier << "\" hex\"" << createHex(_x.hex()) << "\"\n"; +} + +void ProtoConverter::visit(Object const& _x) +{ + // object "object" { + // ... + // } + m_output << "object " << newObjectId() << " {\n"; + visit(_x.code()); + if (_x.has_data()) + visit(_x.data()); + if (_x.has_sub_obj()) + visit(_x.sub_obj()); + m_output << "}\n"; +} + +void ProtoConverter::buildObjectScopeTree(Object const& _x) +{ + // Identifies object being visited + string objectId = newObjectId(false); + vector node{objectId}; + if (_x.has_data()) + node.push_back(s_dataIdentifier); + if (_x.has_sub_obj()) + { + // Identifies sub object whose numeric suffix is + // m_objectId + string subObjectId = "object" + to_string(m_objectId); + node.push_back(subObjectId); + // TODO: Add sub-object to object's ancestors once + // nested access is implemented. + m_objectScopeTree.push_back(node); + buildObjectScopeTree(_x.sub_obj()); + } + else + m_objectScopeTree.push_back(node); +} + void ProtoConverter::visit(Program const& _x) { // Initialize input size m_inputSize = _x.ByteSizeLong(); - /* Program template is as follows - * Zero or more statements. If function definition is present, it is - * called post definition. - * Example: function foo(x_0) -> x_1 {} - * x_2 := foo(calldataload(0)) - * sstore(0, x_2) - */ - m_output << "{\n"; - visit(_x.block()); - m_output << "}\n"; + // Program is either a yul object or a block of + // statements. + switch (_x.program_oneof_case()) + { + case Program::kBlock: + m_output << "{\n"; + visit(_x.block()); + m_output << "}\n"; + break; + case Program::kObj: + m_isObject = true; + buildObjectScopeTree(_x.obj()); + // Reset object id counter + m_objectId = 0; + visit(_x.obj()); + break; + case Program::PROGRAM_ONEOF_NOT_SET: + // {} is a trivial yul program + m_output << "{}"; + break; + } } string ProtoConverter::programToString(Program const& _input) diff --git a/test/tools/ossfuzz/protoToYul.h b/test/tools/ossfuzz/protoToYul.h index f2530bcd5..ef149d198 100644 --- a/test/tools/ossfuzz/protoToYul.h +++ b/test/tools/ossfuzz/protoToYul.h @@ -26,8 +26,10 @@ #include #include + #include #include +#include namespace yul { @@ -46,6 +48,8 @@ public: m_counter = 0; m_inputSize = 0; m_inFunctionDef = false; + m_objectId = 0; + m_isObject = false; } ProtoConverter(ProtoConverter const&) = delete; ProtoConverter(ProtoConverter&&) = delete; @@ -88,6 +92,10 @@ private: void visit(PopStmt const&); void visit(LowLevelCall const&); void visit(Create const&); + void visit(UnaryOpData const&); + void visit(Object const&); + void visit(Data const&); + void visit(Code const&); void visit(Program const&); /// Creates a new scope, and adds @a _funcParams to it if it @@ -109,6 +117,7 @@ private: /// Accepts an arbitrary string, removes all characters that are neither /// alphabets nor digits from it and returns the said string. std::string createAlphaNum(std::string const& _strBytes); + enum class NumFunctionReturns { None, @@ -233,6 +242,11 @@ private: /// Removes entry from m_functionMap and m_functionName void updateFunctionMaps(std::string const& _x); + /// Build a tree of objects that contains the object/data + /// identifiers that are in scope in a given object. + /// @param _x root object of the yul protobuf specification. + void buildObjectScopeTree(Object const& _x); + /// Returns a pseudo-random dictionary token. /// @param _p Enum that decides if the returned token is hex prefixed ("0x") or not /// @return Dictionary token at the index computed using a @@ -256,6 +270,30 @@ private: return "foo_" + functionTypeToString(_type) + "_" + std::to_string(counter()); } + /// Returns a pseudo-randomly chosen object identifier that is in the + /// scope of the Yul object being visited. + std::string getObjectIdentifier(ObjectId const& _x); + + /// Return new object identifier as string. Identifier string + /// is a template of the form "\"object\"" where is + /// a monotonically increasing object ID counter. + /// @param _decorate If true (default value), object ID is + /// enclosed within double quotes. + std::string newObjectId(bool _decorate = true) + { + return dev::Whiskers(R"("object")") + ("decorate", _decorate) + ("id", std::to_string(m_objectId++)) + .render(); + } + + /// Returns the object counter value corresponding to the object + /// being visited. + unsigned currentObjectId() + { + return m_objectId - 1; + } + std::ostringstream m_output; /// Variables in current scope std::stack> m_scopeVars; @@ -271,9 +309,13 @@ private: std::stack> m_switchLiteralSetPerScope; // Look-up table per function type that holds the number of input (output) function parameters std::map> m_functionSigMap; + /// Tree of objects and their scopes + std::vector> m_objectScopeTree; // mod input/output parameters impose an upper bound on the number of input/output parameters a function may have. static unsigned constexpr s_modInputParams = 5; static unsigned constexpr s_modOutputParams = 5; + /// Hard-coded identifier for a Yul object's data block + static auto constexpr s_dataIdentifier = "datablock"; /// Predicate to keep track of for body scope. If true, break/continue /// statements can not be created. bool m_inForBodyScope; @@ -288,6 +330,11 @@ private: unsigned m_inputSize; /// Predicate that is true if inside function definition, false otherwise bool m_inFunctionDef; + /// Index used for naming objects + unsigned m_objectId; + /// Flag to track whether program is an object (true) or a statement block + /// (false: default value) + bool m_isObject; }; } } diff --git a/test/tools/ossfuzz/yulProto.proto b/test/tools/ossfuzz/yulProto.proto index 52fd632ec..6871a63ac 100644 --- a/test/tools/ossfuzz/yulProto.proto +++ b/test/tools/ossfuzz/yulProto.proto @@ -167,6 +167,15 @@ message UnaryOp { required Expression operand = 2; } +message UnaryOpData { + enum UOpData { + SIZE = 1; + OFFSET = 2; + } + required UOpData op = 1; + required ObjectId identifier = 2; +} + message TernaryOp { enum TOp { ADDM = 0; @@ -183,6 +192,7 @@ message CopyFunc { CALLDATA = 0; CODE = 1; RETURNDATA = 2; + DATA = 3; } required CopyType ct = 1; required Expression target = 2; @@ -197,6 +207,18 @@ message ExtCodeCopy { required Expression size = 4; } +message ObjectId { + required uint64 id = 1; +} + +message DataSize { + required ObjectId identifier = 1; +} + +message DataOffset { + required ObjectId identifier = 1; +} + message NullaryOp { enum NOp { PC = 1; @@ -258,6 +280,7 @@ message Expression { FunctionCall func_expr = 7; LowLevelCall lowcall = 8; Create create = 9; + UnaryOpData unopdata = 10; } } @@ -362,8 +385,25 @@ message Block { repeated Statement statements = 1; } -message Program { +message Object { + required Code code = 1; + optional Data data = 2; + optional Object sub_obj = 3; +} + +message Code { required Block block = 1; } +message Data { + required string hex = 1; +} + +message Program { + oneof program_oneof { + Block block = 1; + Object obj = 2; + } +} + package yul.test.yul_fuzzer; diff --git a/test/tools/ossfuzz/yulProto_diff_ossfuzz.cpp b/test/tools/ossfuzz/yulProto_diff_ossfuzz.cpp index 4102bb4b0..41cd6edab 100644 --- a/test/tools/ossfuzz/yulProto_diff_ossfuzz.cpp +++ b/test/tools/ossfuzz/yulProto_diff_ossfuzz.cpp @@ -25,7 +25,9 @@ #include #include #include + #include +#include #include @@ -37,6 +39,20 @@ using namespace langutil; using namespace dev; using namespace yul::test; +namespace +{ +void printErrors(ostream& _stream, ErrorList const& _errors) +{ + SourceReferenceFormatter formatter(_stream); + + for (auto const& error: _errors) + formatter.printExceptionInformation( + *error, + (error->type() == Error::Type::Warning) ? "Warning" : "Error" + ); +} +} + DEFINE_PROTO_FUZZER(Program const& _input) { ProtoConverter converter; @@ -67,7 +83,10 @@ DEFINE_PROTO_FUZZER(Program const& _input) // Parse protobuf mutated YUL code if (!stack.parseAndAnalyze("source", yul_source) || !stack.parserResult()->code || !stack.parserResult()->analysisInfo) + { + printErrors(std::cout, stack.errors()); return; + } } catch (Exception const&) { diff --git a/test/tools/yulopti.cpp b/test/tools/yulopti.cpp index eff091714..e916e8206 100644 --- a/test/tools/yulopti.cpp +++ b/test/tools/yulopti.cpp @@ -151,7 +151,7 @@ public: ForLoopInitRewriter{}(*m_ast); break; case 'O': - ForLoopConditionIntoBody{}(*m_ast); + ForLoopConditionIntoBody{m_dialect}(*m_ast); break; case 'c': CommonSubexpressionEliminator::run(m_dialect, *m_ast); @@ -184,7 +184,7 @@ public: ExpressionSimplifier::run(m_dialect, *m_ast); break; case 't': - (StructuralSimplifier{m_dialect})(*m_ast); + StructuralSimplifier{}(*m_ast); break; case 'n': (ControlFlowSimplifier{m_dialect})(*m_ast);