From d899d9c2c1095efe958c64c260c3b0a3fad726f0 Mon Sep 17 00:00:00 2001 From: Nikola Matic Date: Mon, 30 Oct 2023 11:44:00 +0100 Subject: [PATCH] Accept empty optimizer sequence with Yul optimizer disabled Whitespace and newline validation for empty sequence Changelog and docs Kamil Revert "fixup! Accept empty optimizer sequence with Yul optimizer disabled" This reverts commit 1cbcc8e6b1edb0859876a8781f7b5ceb1722c154. --- Changelog.md | 3 +- docs/internals/optimizer.rst | 6 + libsolidity/interface/CompilerStack.cpp | 17 +++ libsolidity/interface/StandardCompiler.cpp | 20 ++- libyul/YulStack.cpp | 33 ++++- libyul/optimiser/Suite.cpp | 19 +++ libyul/optimiser/Suite.h | 4 + solc/CommandLineParser.cpp | 5 +- .../in.sol | 6 + .../input.json | 16 +++ .../output.json | 12 ++ .../in.sol | 6 + .../input.json | 16 +++ .../output.json | 9 ++ .../in.sol | 6 + .../input.json | 16 +++ .../output.json | 9 ++ .../output.json | 4 +- .../yul_optimizer_steps_disabled/err | 2 +- .../args | 1 + .../err | 11 ++ .../input.sol | 18 +++ .../output | 133 ++++++++++++++++++ test/solc/CommandLineParser.cpp | 36 +++++ 24 files changed, 393 insertions(+), 15 deletions(-) create mode 100644 test/cmdlineTests/standard_optimizer_yulDetails_optimiserSteps_no_yul/in.sol create mode 100644 test/cmdlineTests/standard_optimizer_yulDetails_optimiserSteps_no_yul/input.json create mode 100644 test/cmdlineTests/standard_optimizer_yulDetails_optimiserSteps_no_yul/output.json create mode 100644 test/cmdlineTests/standard_optimizer_yulDetails_optimiserSteps_with_empty_sequence_no_yul/in.sol create mode 100644 test/cmdlineTests/standard_optimizer_yulDetails_optimiserSteps_with_empty_sequence_no_yul/input.json create mode 100644 test/cmdlineTests/standard_optimizer_yulDetails_optimiserSteps_with_empty_sequence_no_yul/output.json create mode 100644 test/cmdlineTests/standard_optimizer_yulDetails_optimiserSteps_with_whitespace_newline_sequence_no_yul/in.sol create mode 100644 test/cmdlineTests/standard_optimizer_yulDetails_optimiserSteps_with_whitespace_newline_sequence_no_yul/input.json create mode 100644 test/cmdlineTests/standard_optimizer_yulDetails_optimiserSteps_with_whitespace_newline_sequence_no_yul/output.json create mode 100644 test/cmdlineTests/yul_optimizer_steps_without_optimize_empty_sequence/args create mode 100644 test/cmdlineTests/yul_optimizer_steps_without_optimize_empty_sequence/err create mode 100644 test/cmdlineTests/yul_optimizer_steps_without_optimize_empty_sequence/input.sol create mode 100644 test/cmdlineTests/yul_optimizer_steps_without_optimize_empty_sequence/output 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