diff --git a/Changelog.md b/Changelog.md index 4e48228d3f56..d096eeddd1e6 100644 --- a/Changelog.md +++ b/Changelog.md @@ -4,7 +4,8 @@ Language Features: Compiler Features: - + * Commandline Interface: An empty ``--yul-optimizations`` sequence can now be always provided. + * Standard JSON Interface: An empty ``optimizerSteps`` sequence can now always be provided. Bugfixes: diff --git a/docs/internals/optimizer.rst b/docs/internals/optimizer.rst index b3e144e94744..15a25f960003 100644 --- a/docs/internals/optimizer.rst +++ b/docs/internals/optimizer.rst @@ -30,6 +30,11 @@ for a stand-alone Yul mode. The `peephole optimizer `_ is always enabled by default and can only be turned off via the :ref:`Standard JSON `. +.. note:: + An empty optimizer sequence is accepted even without ``--optimize`` in order to fully disable + the user-supplied portion of the Yul :ref:`optimizer sequence `, as by default, + even when the optimizer is not turned on, the :ref:`unused pruner ` step will be run. + You can find more details on both optimizer modules and their optimization steps below. Benefits of Optimizing Solidity Code @@ -329,6 +334,7 @@ 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. +.. _selecting-optimizations: Selecting Optimizations ----------------------- diff --git a/libsolidity/interface/CompilerStack.cpp b/libsolidity/interface/CompilerStack.cpp index 14b335016395..eb0ed3550036 100644 --- a/libsolidity/interface/CompilerStack.cpp +++ b/libsolidity/interface/CompilerStack.cpp @@ -69,6 +69,7 @@ #include #include #include +#include #include #include @@ -99,6 +100,7 @@ using namespace solidity; using namespace solidity::langutil; using namespace solidity::frontend; using namespace solidity::stdlib; +using namespace solidity::yul; using namespace std::string_literals; using solidity::util::errinfo_comment; @@ -1672,6 +1674,21 @@ std::string CompilerStack::createMetadata(Contract const& _contract, bool _forIR details["yulDetails"]["stackAllocation"] = m_optimiserSettings.optimizeStackAllocation; details["yulDetails"]["optimizerSteps"] = m_optimiserSettings.yulOptimiserSteps + ":" + m_optimiserSettings.yulOptimiserCleanupSteps; } + else if ( + OptimiserSuite::isEmptyOptimizerSequence(m_optimiserSettings.yulOptimiserSteps) && + OptimiserSuite::isEmptyOptimizerSequence(m_optimiserSettings.yulOptimiserCleanupSteps) + ) + { + solAssert(m_optimiserSettings.optimizeStackAllocation == false); + details["yulDetails"] = Json::objectValue; + details["yulDetails"]["optimizerSteps"] = ":"; + } + else + { + solAssert(m_optimiserSettings.optimizeStackAllocation == false); + solAssert(m_optimiserSettings.yulOptimiserSteps == OptimiserSettings::DefaultYulOptimiserSteps); + solAssert(m_optimiserSettings.yulOptimiserCleanupSteps == OptimiserSettings::DefaultYulOptimiserCleanupSteps); + } meta["settings"]["optimizer"]["details"] = std::move(details); } diff --git a/libsolidity/interface/StandardCompiler.cpp b/libsolidity/interface/StandardCompiler.cpp index 91a748053131..b74f764c5592 100644 --- a/libsolidity/interface/StandardCompiler.cpp +++ b/libsolidity/interface/StandardCompiler.cpp @@ -456,12 +456,16 @@ std::optional checkOptimizerDetail(Json::Value const& _details, std return {}; } -std::optional checkOptimizerDetailSteps(Json::Value const& _details, std::string const& _name, std::string& _optimiserSetting, std::string& _cleanupSetting) +std::optional checkOptimizerDetailSteps(Json::Value const& _details, std::string const& _name, std::string& _optimiserSetting, std::string& _cleanupSetting, bool _runYulOptimizer) { if (_details.isMember(_name)) { if (_details[_name].isString()) { + std::string const fullSequence = _details[_name].asString(); + if (!_runYulOptimizer && !OptimiserSuite::isEmptyOptimizerSequence(fullSequence)) + return formatFatalError(Error::Type::JSONError, "If Yul optimizer is disabled, only an empty optimizerSteps sequence is accepted."); + try { yul::OptimiserSuite::validateSequence(_details[_name].asString()); @@ -474,7 +478,6 @@ std::optional checkOptimizerDetailSteps(Json::Value const& _details ); } - std::string const fullSequence = _details[_name].asString(); auto const delimiterPos = fullSequence.find(":"); _optimiserSetting = fullSequence.substr(0, delimiterPos); @@ -605,22 +608,27 @@ std::variant parseOptimizerSettings(Json::Value if (details.isMember("yulDetails")) { if (!settings.runYulOptimiser) - return formatFatalError(Error::Type::JSONError, "\"Providing yulDetails requires Yul optimizer to be enabled."); + { + if (checkKeys(details["yulDetails"], {"optimizerSteps"}, "settings.optimizer.details.yulDetails")) + return formatFatalError(Error::Type::JSONError, "Only optimizerSteps can be set in yulDetails when Yul optimizer is disabled."); + if (auto error = checkOptimizerDetailSteps(details["yulDetails"], "optimizerSteps", settings.yulOptimiserSteps, settings.yulOptimiserCleanupSteps, settings.runYulOptimiser)) + return *error; + return {std::move(settings)}; + } if (auto result = checkKeys(details["yulDetails"], {"stackAllocation", "optimizerSteps"}, "settings.optimizer.details.yulDetails")) return *result; if (auto error = checkOptimizerDetail(details["yulDetails"], "stackAllocation", settings.optimizeStackAllocation)) return *error; - if (auto error = checkOptimizerDetailSteps(details["yulDetails"], "optimizerSteps", settings.yulOptimiserSteps, settings.yulOptimiserCleanupSteps)) + if (auto error = checkOptimizerDetailSteps(details["yulDetails"], "optimizerSteps", settings.yulOptimiserSteps, settings.yulOptimiserCleanupSteps, settings.runYulOptimiser)) return *error; } } - return { std::move(settings) }; + return {std::move(settings)}; } } - std::variant StandardCompiler::parseInput(Json::Value const& _input) { InputsAndSettings ret; diff --git a/libyul/YulStack.cpp b/libyul/YulStack.cpp index a5d72c7bb5d7..6c89cadbdfd9 100644 --- a/libyul/YulStack.cpp +++ b/libyul/YulStack.cpp @@ -34,12 +34,14 @@ #include #include #include +#include #include #include using namespace solidity; +using namespace solidity::frontend; using namespace solidity::yul; using namespace solidity::langutil; @@ -164,14 +166,39 @@ void YulStack::optimize(Object& _object, bool _isCreation) if (EVMDialect const* evmDialect = dynamic_cast(&dialect)) meter = std::make_unique(*evmDialect, _isCreation, m_optimiserSettings.expectedExecutionsPerDeployment); + auto [optimizeStackAllocation, yulOptimiserSteps, yulOptimiserCleanupSteps] = [&]() -> std::tuple + { + if (!m_optimiserSettings.runYulOptimiser) + { + // Yul optimizer disabled, but empty sequence (:) explicitly provided + if (OptimiserSuite::isEmptyOptimizerSequence(m_optimiserSettings.yulOptimiserSteps + ":" + m_optimiserSettings.yulOptimiserCleanupSteps)) + return std::make_tuple(true, "", ""); + // Yul optimizer disabled, and no sequence explicitly provided (assumes default sequence) + else + { + yulAssert( + m_optimiserSettings.yulOptimiserSteps == OptimiserSettings::DefaultYulOptimiserSteps && + m_optimiserSettings.yulOptimiserCleanupSteps == OptimiserSettings::DefaultYulOptimiserCleanupSteps + ); + return std::make_tuple(true, "u", ""); + } + + } + return std::make_tuple( + m_optimiserSettings.optimizeStackAllocation, + m_optimiserSettings.yulOptimiserSteps, + m_optimiserSettings.yulOptimiserCleanupSteps + ); + }(); + OptimiserSuite::run( dialect, meter.get(), _object, // Defaults are the minimum necessary to avoid running into "Stack too deep" constantly. - m_optimiserSettings.runYulOptimiser ? m_optimiserSettings.optimizeStackAllocation : true, - m_optimiserSettings.runYulOptimiser ? m_optimiserSettings.yulOptimiserSteps : "u", - m_optimiserSettings.runYulOptimiser ? m_optimiserSettings.yulOptimiserCleanupSteps : "", + optimizeStackAllocation, + yulOptimiserSteps, + yulOptimiserCleanupSteps, _isCreation ? std::nullopt : std::make_optional(m_optimiserSettings.expectedExecutionsPerDeployment), {} ); diff --git a/libyul/optimiser/Suite.cpp b/libyul/optimiser/Suite.cpp index 58561ee10a15..545ae4de06d7 100644 --- a/libyul/optimiser/Suite.cpp +++ b/libyul/optimiser/Suite.cpp @@ -383,6 +383,25 @@ void OptimiserSuite::validateSequence(std::string_view _stepAbbreviations) assertThrow(nestingLevel == 0, OptimizerException, "Unbalanced brackets"); } +bool OptimiserSuite::isEmptyOptimizerSequence(std::string const& _sequence) +{ + size_t delimiterCount{0}; + for (char const step: _sequence) + switch (step) + { + case ':': + if (++delimiterCount > 1) + return false; + break; + case ' ': + case '\n': + break; + default: + return false; + } + return true; +} + void OptimiserSuite::runSequence(std::string_view _stepAbbreviations, Block& _ast, bool _repeatUntilStable) { validateSequence(_stepAbbreviations); diff --git a/libyul/optimiser/Suite.h b/libyul/optimiser/Suite.h index abed2720e2a5..6e3886da3b2d 100644 --- a/libyul/optimiser/Suite.h +++ b/libyul/optimiser/Suite.h @@ -76,6 +76,10 @@ class OptimiserSuite /// Ensures that specified sequence of step abbreviations is well-formed and can be executed. /// @throw OptimizerException if the sequence is invalid static void validateSequence(std::string_view _stepAbbreviations); + /// Check whether the provided sequence is empty provided that the allowed characters are + /// whitespace, newline and : + static bool isEmptyOptimizerSequence(std::string const& _sequence); + void runSequence(std::vector const& _steps, Block& _ast); void runSequence(std::string_view _stepAbbreviations, Block& _ast, bool _repeatUntilStable = false); diff --git a/solc/CommandLineParser.cpp b/solc/CommandLineParser.cpp index 0c7484fd4017..3ed1b7b634ca 100644 --- a/solc/CommandLineParser.cpp +++ b/solc/CommandLineParser.cpp @@ -33,6 +33,7 @@ #include using namespace solidity::langutil; +using namespace solidity::yul; namespace po = boost::program_options; @@ -1228,8 +1229,8 @@ void CommandLineParser::processArgs() if (m_args.count(g_strYulOptimizations)) { OptimiserSettings optimiserSettings = m_options.optimiserSettings(); - if (!optimiserSettings.runYulOptimiser) - solThrow(CommandLineValidationError, "--" + g_strYulOptimizations + " is invalid if Yul optimizer is disabled"); + if (!optimiserSettings.runYulOptimiser && !OptimiserSuite::isEmptyOptimizerSequence(m_args[g_strYulOptimizations].as())) + solThrow(CommandLineValidationError, "--" + g_strYulOptimizations + " is invalid with a non-empty sequence if Yul optimizer is disabled."); try { diff --git a/test/cmdlineTests/standard_optimizer_yulDetails_optimiserSteps_no_yul/in.sol b/test/cmdlineTests/standard_optimizer_yulDetails_optimiserSteps_no_yul/in.sol new file mode 100644 index 000000000000..9e5350072a2c --- /dev/null +++ b/test/cmdlineTests/standard_optimizer_yulDetails_optimiserSteps_no_yul/in.sol @@ -0,0 +1,6 @@ +// SPDX-License-Identifier: GPL-3.0 +pragma solidity >=0.0; + +contract C { + function f() public pure {} +} diff --git a/test/cmdlineTests/standard_optimizer_yulDetails_optimiserSteps_no_yul/input.json b/test/cmdlineTests/standard_optimizer_yulDetails_optimiserSteps_no_yul/input.json new file mode 100644 index 000000000000..511860250476 --- /dev/null +++ b/test/cmdlineTests/standard_optimizer_yulDetails_optimiserSteps_no_yul/input.json @@ -0,0 +1,16 @@ +{ + "language": "Solidity", + "sources": { + "A": {"urls": ["standard_optimizer_yulDetails_optimiserSteps_no_yul/in.sol"]} + }, + "settings": { + "optimizer": { + "details": { + "yul": false, + "yulDetails": { + "optimizerSteps": "u:fdntOc" + } + } + } + } +} diff --git a/test/cmdlineTests/standard_optimizer_yulDetails_optimiserSteps_no_yul/output.json b/test/cmdlineTests/standard_optimizer_yulDetails_optimiserSteps_no_yul/output.json new file mode 100644 index 000000000000..fa3c9d93edc5 --- /dev/null +++ b/test/cmdlineTests/standard_optimizer_yulDetails_optimiserSteps_no_yul/output.json @@ -0,0 +1,12 @@ +{ + "errors": + [ + { + "component": "general", + "formattedMessage": "If Yul optimizer is disabled, only an empty optimizerSteps sequence is accepted.", + "message": "If Yul optimizer is disabled, only an empty optimizerSteps sequence is accepted.", + "severity": "error", + "type": "JSONError" + } + ] +} diff --git a/test/cmdlineTests/standard_optimizer_yulDetails_optimiserSteps_with_empty_sequence_no_yul/in.sol b/test/cmdlineTests/standard_optimizer_yulDetails_optimiserSteps_with_empty_sequence_no_yul/in.sol new file mode 100644 index 000000000000..9e5350072a2c --- /dev/null +++ b/test/cmdlineTests/standard_optimizer_yulDetails_optimiserSteps_with_empty_sequence_no_yul/in.sol @@ -0,0 +1,6 @@ +// SPDX-License-Identifier: GPL-3.0 +pragma solidity >=0.0; + +contract C { + function f() public pure {} +} diff --git a/test/cmdlineTests/standard_optimizer_yulDetails_optimiserSteps_with_empty_sequence_no_yul/input.json b/test/cmdlineTests/standard_optimizer_yulDetails_optimiserSteps_with_empty_sequence_no_yul/input.json new file mode 100644 index 000000000000..9de7beea915a --- /dev/null +++ b/test/cmdlineTests/standard_optimizer_yulDetails_optimiserSteps_with_empty_sequence_no_yul/input.json @@ -0,0 +1,16 @@ +{ + "language": "Solidity", + "sources": { + "A": {"urls": ["standard_optimizer_yulDetails_optimiserSteps_with_empty_sequence_no_yul/in.sol"]} + }, + "settings": { + "optimizer": { + "details": { + "yul": false, + "yulDetails": { + "optimizerSteps": ":" + } + } + } + } +} diff --git a/test/cmdlineTests/standard_optimizer_yulDetails_optimiserSteps_with_empty_sequence_no_yul/output.json b/test/cmdlineTests/standard_optimizer_yulDetails_optimiserSteps_with_empty_sequence_no_yul/output.json new file mode 100644 index 000000000000..acf3b74ef1de --- /dev/null +++ b/test/cmdlineTests/standard_optimizer_yulDetails_optimiserSteps_with_empty_sequence_no_yul/output.json @@ -0,0 +1,9 @@ +{ + "sources": + { + "A": + { + "id": 0 + } + } +} diff --git a/test/cmdlineTests/standard_optimizer_yulDetails_optimiserSteps_with_whitespace_newline_sequence_no_yul/in.sol b/test/cmdlineTests/standard_optimizer_yulDetails_optimiserSteps_with_whitespace_newline_sequence_no_yul/in.sol new file mode 100644 index 000000000000..9e5350072a2c --- /dev/null +++ b/test/cmdlineTests/standard_optimizer_yulDetails_optimiserSteps_with_whitespace_newline_sequence_no_yul/in.sol @@ -0,0 +1,6 @@ +// SPDX-License-Identifier: GPL-3.0 +pragma solidity >=0.0; + +contract C { + function f() public pure {} +} diff --git a/test/cmdlineTests/standard_optimizer_yulDetails_optimiserSteps_with_whitespace_newline_sequence_no_yul/input.json b/test/cmdlineTests/standard_optimizer_yulDetails_optimiserSteps_with_whitespace_newline_sequence_no_yul/input.json new file mode 100644 index 000000000000..321ac8e99ab8 --- /dev/null +++ b/test/cmdlineTests/standard_optimizer_yulDetails_optimiserSteps_with_whitespace_newline_sequence_no_yul/input.json @@ -0,0 +1,16 @@ +{ + "language": "Solidity", + "sources": { + "A": {"urls": ["standard_optimizer_yulDetails_optimiserSteps_with_whitespace_newline_sequence_no_yul/in.sol"]} + }, + "settings": { + "optimizer": { + "details": { + "yul": false, + "yulDetails": { + "optimizerSteps": "\n : " + } + } + } + } +} diff --git a/test/cmdlineTests/standard_optimizer_yulDetails_optimiserSteps_with_whitespace_newline_sequence_no_yul/output.json b/test/cmdlineTests/standard_optimizer_yulDetails_optimiserSteps_with_whitespace_newline_sequence_no_yul/output.json new file mode 100644 index 000000000000..acf3b74ef1de --- /dev/null +++ b/test/cmdlineTests/standard_optimizer_yulDetails_optimiserSteps_with_whitespace_newline_sequence_no_yul/output.json @@ -0,0 +1,9 @@ +{ + "sources": + { + "A": + { + "id": 0 + } + } +} diff --git a/test/cmdlineTests/standard_optimizer_yulDetails_without_yul/output.json b/test/cmdlineTests/standard_optimizer_yulDetails_without_yul/output.json index 4e6d7b6f4ddc..2470015e1fb0 100644 --- a/test/cmdlineTests/standard_optimizer_yulDetails_without_yul/output.json +++ b/test/cmdlineTests/standard_optimizer_yulDetails_without_yul/output.json @@ -3,8 +3,8 @@ [ { "component": "general", - "formattedMessage": "\"Providing yulDetails requires Yul optimizer to be enabled.", - "message": "\"Providing yulDetails requires Yul optimizer to be enabled.", + "formattedMessage": "Only optimizerSteps can be set in yulDetails when Yul optimizer is disabled.", + "message": "Only optimizerSteps can be set in yulDetails when Yul optimizer is disabled.", "severity": "error", "type": "JSONError" } diff --git a/test/cmdlineTests/yul_optimizer_steps_disabled/err b/test/cmdlineTests/yul_optimizer_steps_disabled/err index a557351ea247..1abbab8bde55 100644 --- a/test/cmdlineTests/yul_optimizer_steps_disabled/err +++ b/test/cmdlineTests/yul_optimizer_steps_disabled/err @@ -1 +1 @@ -Error: --yul-optimizations is invalid if Yul optimizer is disabled +Error: --yul-optimizations is invalid with a non-empty sequence if Yul optimizer is disabled. diff --git a/test/cmdlineTests/yul_optimizer_steps_without_optimize_empty_sequence/args b/test/cmdlineTests/yul_optimizer_steps_without_optimize_empty_sequence/args new file mode 100644 index 000000000000..fef6c6531e19 --- /dev/null +++ b/test/cmdlineTests/yul_optimizer_steps_without_optimize_empty_sequence/args @@ -0,0 +1 @@ +--ir-optimized --metadata --yul-optimizations : diff --git a/test/cmdlineTests/yul_optimizer_steps_without_optimize_empty_sequence/err b/test/cmdlineTests/yul_optimizer_steps_without_optimize_empty_sequence/err new file mode 100644 index 000000000000..95e30df2e83d --- /dev/null +++ b/test/cmdlineTests/yul_optimizer_steps_without_optimize_empty_sequence/err @@ -0,0 +1,11 @@ +Warning: Unused local variable. + --> yul_optimizer_steps_without_optimize_empty_sequence/input.sol:13:9: + | +13 | uint b = a; + | ^^^^^^ + +Warning: Unused local variable. + --> yul_optimizer_steps_without_optimize_empty_sequence/input.sol:14:9: + | +14 | uint c = a; + | ^^^^^^ diff --git a/test/cmdlineTests/yul_optimizer_steps_without_optimize_empty_sequence/input.sol b/test/cmdlineTests/yul_optimizer_steps_without_optimize_empty_sequence/input.sol new file mode 100644 index 000000000000..9e1b04ab1c47 --- /dev/null +++ b/test/cmdlineTests/yul_optimizer_steps_without_optimize_empty_sequence/input.sol @@ -0,0 +1,18 @@ +// SPDX-License-Identifier: GPL-3.0 +pragma solidity >=0.0; + +contract C +{ + constructor() {} + + function foo() public pure returns (bool) + { + // given the empty optimizer sequence ``:``, ``b`` and ``c`` should not be removed in the + // optimized IR as the ``UnusedPruner`` step will not be run. + uint a = 100; + uint b = a; + uint c = a; + + return a == 100; + } +} diff --git a/test/cmdlineTests/yul_optimizer_steps_without_optimize_empty_sequence/output b/test/cmdlineTests/yul_optimizer_steps_without_optimize_empty_sequence/output new file mode 100644 index 000000000000..9ee68107929a --- /dev/null +++ b/test/cmdlineTests/yul_optimizer_steps_without_optimize_empty_sequence/output @@ -0,0 +1,133 @@ + +======= yul_optimizer_steps_without_optimize_empty_sequence/input.sol:C ======= +Optimized IR: +/// @use-src 0:"yul_optimizer_steps_without_optimize_empty_sequence/input.sol" +object "C_28" { + code { + { + /// @src 0:60:410 "contract C..." + mstore(64, memoryguard(0x80)) + if callvalue() + { + revert_error_ca66f745a3ce8ff40e2ccaf1ad45db7774001b90d25810abd9040049be7bf4bb() + } + constructor_C() + let _1 := allocate_unbounded() + codecopy(_1, dataoffset("C_28_deployed"), datasize("C_28_deployed")) + return(_1, datasize("C_28_deployed")) + } + function allocate_unbounded() -> memPtr + { memPtr := mload(64) } + function revert_error_ca66f745a3ce8ff40e2ccaf1ad45db7774001b90d25810abd9040049be7bf4bb() + { revert(0, 0) } + /// @ast-id 5 @src 0:77:93 "constructor() {}" + function constructor_C() + { } + } + /// @use-src 0:"yul_optimizer_steps_without_optimize_empty_sequence/input.sol" + object "C_28_deployed" { + code { + { + /// @src 0:60:410 "contract C..." + mstore(64, memoryguard(0x80)) + if iszero(lt(calldatasize(), 4)) + { + let selector := shift_right_unsigned(calldataload(0)) + switch selector + case 0xc2985578 { external_fun_foo() } + default { } + } + revert_error_42b3090547df1d2001c96683413b8cf91c1b902ef5e3cb8d9f6f304cf7446f74() + } + function shift_right_unsigned(value) -> newValue + { newValue := shr(224, value) } + function allocate_unbounded() -> memPtr + { memPtr := mload(64) } + function revert_error_ca66f745a3ce8ff40e2ccaf1ad45db7774001b90d25810abd9040049be7bf4bb() + { revert(0, 0) } + function revert_error_dbdddcbe895c83990c08b3492a0e83918d802a52331272ac6fdb6a7c4aea3b1b() + { revert(0, 0) } + function abi_decode(headStart, dataEnd) + { + if slt(sub(dataEnd, headStart), 0) + { + revert_error_dbdddcbe895c83990c08b3492a0e83918d802a52331272ac6fdb6a7c4aea3b1b() + } + } + function cleanup_bool(value) -> cleaned + { + cleaned := iszero(iszero(value)) + } + function abi_encode_bool_to_bool(value, pos) + { + mstore(pos, cleanup_bool(value)) + } + function abi_encode_bool(headStart, value0) -> tail + { + tail := add(headStart, 32) + abi_encode_bool_to_bool(value0, add(headStart, 0)) + } + function external_fun_foo() + { + if callvalue() + { + revert_error_ca66f745a3ce8ff40e2ccaf1ad45db7774001b90d25810abd9040049be7bf4bb() + } + abi_decode(4, calldatasize()) + let ret := fun_foo() + let memPos := allocate_unbounded() + let memEnd := abi_encode_bool(memPos, ret) + return(memPos, sub(memEnd, memPos)) + } + function revert_error_42b3090547df1d2001c96683413b8cf91c1b902ef5e3cb8d9f6f304cf7446f74() + { revert(0, 0) } + function zero_value_for_split_bool() -> ret + { ret := 0 } + function cleanup_rational_by(value) -> cleaned + { cleaned := value } + function cleanup_uint256(value) -> cleaned + { cleaned := value } + function identity(value) -> ret + { ret := value } + function convert_rational_by_to_uint256(value) -> converted + { + converted := cleanup_uint256(identity(cleanup_rational_by(value))) + } + /// @ast-id 27 @src 0:99:408 "function foo() public pure returns (bool)..." + function fun_foo() -> var + { + /// @src 0:135:139 "bool" + let zero_bool := zero_value_for_split_bool() + var := zero_bool + /// @src 0:332:335 "100" + let expr := 0x64 + /// @src 0:323:335 "uint a = 100" + let var_a := convert_rational_by_to_uint256(expr) + /// @src 0:354:355 "a" + let _1 := var_a + let expr_1 := _1 + /// @src 0:345:355 "uint b = a" + let var_b := expr_1 + /// @src 0:374:375 "a" + let _2 := var_a + let expr_2 := _2 + /// @src 0:365:375 "uint c = a" + let var_c := expr_2 + /// @src 0:393:394 "a" + let _3 := var_a + let expr_3 := _3 + /// @src 0:398:401 "100" + let expr_4 := 0x64 + /// @src 0:393:401 "a == 100" + let expr_5 := eq(cleanup_uint256(expr_3), convert_rational_by_to_uint256(expr_4)) + /// @src 0:386:401 "return a == 100" + var := expr_5 + leave + } + } + data ".metadata" hex"" + } +} + +Metadata: +{"compiler":{"version": ""},"language":"Solidity","output":{"abi":[{"inputs":[],"stateMutability":"nonpayable","type":"constructor"},{"inputs":[],"name":"foo","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"pure","type":"function"}],"devdoc":{"kind":"dev","methods":{},"version":1},"userdoc":{"kind":"user","methods":{},"version":1}},"settings":{"compilationTarget":{"yul_optimizer_steps_without_optimize_empty_sequence/input.sol":"C"},"evmVersion":"shanghai","libraries":{},"metadata":{"bytecodeHash":"ipfs"},"optimizer":{"details":{"constantOptimizer":false,"cse":false,"deduplicate":false,"inliner":false,"jumpdestRemover":true,"orderLiterals":false,"peephole":true,"simpleCounterForLoopUncheckedIncrement":true,"yul":false,"yulDetails":{"optimizerSteps":":"}},"runs":200},"remappings":[]},"sources":{"yul_optimizer_steps_without_optimize_empty_sequence/input.sol":{"keccak256":"0x3fc910e345ce1ee62bfa6b0f66931ee632c08265b25b6139cfbbfe4d2f8d5dd8","license":"GPL-3.0","urls":["bzz-raw://e557e9ad2c2e420a669c06ae456b0b790d77d2d6d492cd8540e6b244388a5140","dweb:/ipfs/QmaNiZmC2Mo3YxGiehs1n3dVTjZwD7FguX7EUtpeshMVuR"]}},"version":1} diff --git a/test/solc/CommandLineParser.cpp b/test/solc/CommandLineParser.cpp index caad98f999a2..28ca2c48a1a0 100644 --- a/test/solc/CommandLineParser.cpp +++ b/test/solc/CommandLineParser.cpp @@ -572,6 +572,42 @@ BOOST_AUTO_TEST_CASE(invalid_optimiser_sequences) } } +BOOST_AUTO_TEST_CASE(valid_empty_optimizer_sequences_without_optimize) +{ + vector const validSequenceInputs { + " :", + ": ", + "\n : \n", + ":" + }; + + vector> const expectedParsedSequences { + {" ", ""}, + {"", " "}, + {"\n ", " \n"}, + {"", ""} + }; + + BOOST_CHECK_EQUAL(validSequenceInputs.size(), expectedParsedSequences.size()); + + for (size_t i = 0; i < validSequenceInputs.size(); ++i) + { + CommandLineOptions const& commandLineOptions = parseCommandLine({"solc", "contract.sol", "--yul-optimizations=" + validSequenceInputs[i]}); + auto const& [expectedYulOptimiserSteps, expectedYulCleanupSteps] = expectedParsedSequences[i]; + BOOST_CHECK_EQUAL(commandLineOptions.optimiserSettings().yulOptimiserSteps, expectedYulOptimiserSteps); + BOOST_CHECK_EQUAL(commandLineOptions.optimiserSettings().yulOptimiserCleanupSteps, expectedYulCleanupSteps); + } +} + +BOOST_AUTO_TEST_CASE(invalid_optimizer_sequence_without_optimize) +{ + string const invalidSequence{"u: "}; + string const expectedErrorMessage{"--yul-optimizations is invalid with a non-empty sequence if Yul optimizer is disabled."}; + vector commandLineOptions{"solc", "contract.sol", "--yul-optimizations=" + invalidSequence}; + auto hasCorrectMessage = [&](CommandLineValidationError const& _exception) { return _exception.what() == expectedErrorMessage; }; + BOOST_CHECK_EXCEPTION(parseCommandLine(commandLineOptions), CommandLineValidationError, hasCorrectMessage); +} + BOOST_AUTO_TEST_SUITE_END() } // namespace solidity::frontend::test