From f121e7e9dd34a65474d6f92fc9ce23d6fd0396d3 Mon Sep 17 00:00:00 2001 From: Markus Osterlund / robriks <80549215+robriks@users.noreply.github.com> Date: Wed, 17 May 2023 07:10:07 -0400 Subject: [PATCH 01/14] Update abi-spec.rst Rearranged the calldata encoding explanations for the example functions so they are more easy to understand since they now visually match the order the functions are declared: Foo.bar() Foo.baz() Foo.sam() Maybe I'm smolbrain but it was confusing to reference the functions when they are declared in a different order than they are explained. I kept imagining calldata encoding layouts for bar() even though the example I was looking at discussed baz() instead. Minor change but makes a big difference for less experienced calldataooors like me. --- docs/abi-spec.rst | 30 +++++++++++++++--------------- 1 file changed, 15 insertions(+), 15 deletions(-) diff --git a/docs/abi-spec.rst b/docs/abi-spec.rst index 41967dd46d6b..0d48b51dd706 100644 --- a/docs/abi-spec.rst +++ b/docs/abi-spec.rst @@ -252,7 +252,21 @@ Given the contract: } -Thus, for our ``Foo`` example if we wanted to call ``baz`` with the parameters ``69`` and +Thus, for our ``Foo`` example, if we wanted to call ``bar`` with the argument ``["abc", "def"]``, we would pass 68 bytes total, broken down into: + +- ``0xfce353f6``: the Method ID. This is derived from the signature ``bar(bytes3[2])``. +- ``0x6162630000000000000000000000000000000000000000000000000000000000``: the first part of the first + parameter, a ``bytes3`` value ``"abc"`` (left-aligned). +- ``0x6465660000000000000000000000000000000000000000000000000000000000``: the second part of the first + parameter, a ``bytes3`` value ``"def"`` (left-aligned). + +In total: + +.. code-block:: none + + 0xfce353f661626300000000000000000000000000000000000000000000000000000000006465660000000000000000000000000000000000000000000000000000000000 + +If we wanted to call ``baz`` with the parameters ``69`` and ``true``, we would pass 68 bytes total, which can be broken down into: - ``0xcdcd77c0``: the Method ID. This is derived as the first 4 bytes of the Keccak hash of @@ -271,20 +285,6 @@ In total: It returns a single ``bool``. If, for example, it were to return ``false``, its output would be the single byte array ``0x0000000000000000000000000000000000000000000000000000000000000000``, a single bool. -If we wanted to call ``bar`` with the argument ``["abc", "def"]``, we would pass 68 bytes total, broken down into: - -- ``0xfce353f6``: the Method ID. This is derived from the signature ``bar(bytes3[2])``. -- ``0x6162630000000000000000000000000000000000000000000000000000000000``: the first part of the first - parameter, a ``bytes3`` value ``"abc"`` (left-aligned). -- ``0x6465660000000000000000000000000000000000000000000000000000000000``: the second part of the first - parameter, a ``bytes3`` value ``"def"`` (left-aligned). - -In total: - -.. code-block:: none - - 0xfce353f661626300000000000000000000000000000000000000000000000000000000006465660000000000000000000000000000000000000000000000000000000000 - If we wanted to call ``sam`` with the arguments ``"dave"``, ``true`` and ``[1,2,3]``, we would pass 292 bytes total, broken down into: From 609ef154d08d1e947097ff6343dd7511813db8ea Mon Sep 17 00:00:00 2001 From: Nikola Matic Date: Wed, 25 Oct 2023 14:00:37 +0200 Subject: [PATCH 02/14] Set version to 0.8.23 --- CMakeLists.txt | 2 +- Changelog.md | 11 +++++++++++ 2 files changed, 12 insertions(+), 1 deletion(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 73e7930b7222..298df418cd11 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -21,7 +21,7 @@ include(EthPolicy) eth_policy() # project name and version should be set after cmake_policy CMP0048 -set(PROJECT_VERSION "0.8.22") +set(PROJECT_VERSION "0.8.23") # OSX target needed in order to support std::visit set(CMAKE_OSX_DEPLOYMENT_TARGET "10.14") project(solidity VERSION ${PROJECT_VERSION} LANGUAGES C CXX) diff --git a/Changelog.md b/Changelog.md index 8c74b49cf72c..4e48228d3f56 100644 --- a/Changelog.md +++ b/Changelog.md @@ -1,3 +1,14 @@ +### 0.8.23 (unreleased) + +Language Features: + + +Compiler Features: + + +Bugfixes: + + ### 0.8.22 (2023-10-25) Language Features: From 23bcc69478ae5d9b2759bedb2ede47ea7f1ff78d Mon Sep 17 00:00:00 2001 From: Nikola Matic Date: Thu, 26 Oct 2023 16:34:49 +0200 Subject: [PATCH 03/14] Fix wrong file path in standard json tests --- .../input.json | 2 +- .../input.json | 2 +- .../input.json | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/test/cmdlineTests/standard_optimizer_yulDetails_optimiserSteps_with_empty_cleanup_sequence/input.json b/test/cmdlineTests/standard_optimizer_yulDetails_optimiserSteps_with_empty_cleanup_sequence/input.json index 0903ccfebd44..7d2f529ccf7f 100644 --- a/test/cmdlineTests/standard_optimizer_yulDetails_optimiserSteps_with_empty_cleanup_sequence/input.json +++ b/test/cmdlineTests/standard_optimizer_yulDetails_optimiserSteps_with_empty_cleanup_sequence/input.json @@ -1,7 +1,7 @@ { "language": "Solidity", "sources": { - "A": {"urls": ["standard_optimizer_yulDetails_optimiserSteps_with_cleanup_sequence/in.sol"]} + "A": {"urls": ["standard_optimizer_yulDetails_optimiserSteps_with_empty_cleanup_sequence/in.sol"]} }, "settings": { "optimizer": { diff --git a/test/cmdlineTests/standard_optimizer_yulDetails_optimiserSteps_with_empty_optimisation_sequence/input.json b/test/cmdlineTests/standard_optimizer_yulDetails_optimiserSteps_with_empty_optimisation_sequence/input.json index 4086aff9fb14..9c603d6f7f0b 100644 --- a/test/cmdlineTests/standard_optimizer_yulDetails_optimiserSteps_with_empty_optimisation_sequence/input.json +++ b/test/cmdlineTests/standard_optimizer_yulDetails_optimiserSteps_with_empty_optimisation_sequence/input.json @@ -1,7 +1,7 @@ { "language": "Solidity", "sources": { - "A": {"urls": ["standard_optimizer_yulDetails_optimiserSteps_with_cleanup_sequence/in.sol"]} + "A": {"urls": ["standard_optimizer_yulDetails_optimiserSteps_with_empty_optimisation_sequence/in.sol"]} }, "settings": { "optimizer": { diff --git a/test/cmdlineTests/standard_optimizer_yulDetails_optimiserSteps_with_empty_sequence/input.json b/test/cmdlineTests/standard_optimizer_yulDetails_optimiserSteps_with_empty_sequence/input.json index 4f66682223c3..5780bea83aa8 100644 --- a/test/cmdlineTests/standard_optimizer_yulDetails_optimiserSteps_with_empty_sequence/input.json +++ b/test/cmdlineTests/standard_optimizer_yulDetails_optimiserSteps_with_empty_sequence/input.json @@ -1,7 +1,7 @@ { "language": "Solidity", "sources": { - "A": {"urls": ["standard_optimizer_yulDetails_optimiserSteps_with_cleanup_sequence/in.sol"]} + "A": {"urls": ["standard_optimizer_yulDetails_optimiserSteps_with_empty_sequence/in.sol"]} }, "settings": { "optimizer": { From 74c2440416f9dcd2472d4d19203bae6ac359b8a7 Mon Sep 17 00:00:00 2001 From: Nikola Matic Date: Wed, 25 Oct 2023 12:45:23 +0200 Subject: [PATCH 04/14] Update the release checklist --- ReleaseChecklist.md | 1 + 1 file changed, 1 insertion(+) diff --git a/ReleaseChecklist.md b/ReleaseChecklist.md index db07fb888555..505852496f83 100644 --- a/ReleaseChecklist.md +++ b/ReleaseChecklist.md @@ -58,6 +58,7 @@ At least a day before the release: Remove different variants of the same name manually before using the output. - [ ] Check that all tests on the latest commit in ``develop`` are green. - [ ] Click the ``Publish release`` button on the release page, creating the tag. + **Important: Must not be done before all the PRs, including changelog cleanup and date, are merged.** - [ ] Wait for the CI runs on the tag itself. ### Upload Release Artifacts and Publish Binaries From 79b1f55cc51334cee7a38364bdcb0599702dd598 Mon Sep 17 00:00:00 2001 From: Nikola Matic Date: Thu, 26 Oct 2023 13:42:55 +0200 Subject: [PATCH 05/14] Remove references to homebrew-ethereum --- ReleaseChecklist.md | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/ReleaseChecklist.md b/ReleaseChecklist.md index 505852496f83..db8bd426b66d 100644 --- a/ReleaseChecklist.md +++ b/ReleaseChecklist.md @@ -2,8 +2,7 @@ ### Requirements - [ ] GitHub account with access to [solidity](https://github.com/ethereum/solidity), [solc-js](https://github.com/ethereum/solc-js), - [solc-bin](https://github.com/ethereum/solc-bin), [homebrew-ethereum](https://github.com/ethereum/homebrew-ethereum), - [solidity-website](https://github.com/ethereum/solidity-website). + [solc-bin](https://github.com/ethereum/solc-bin), [solidity-website](https://github.com/ethereum/solidity-website). - [ ] DockerHub account with push rights to the [``solc`` image](https://hub.docker.com/r/ethereum/solc). - [ ] Launchpad (Ubuntu One) account with a membership in the ["Ethereum" team](https://launchpad.net/~ethereum) and a gnupg key for your email in the ``ethereum.org`` domain (has to be version 1, gpg2 won't work). @@ -21,7 +20,6 @@ At least a day before the release: - [ ] Rerun CI on the top commits of main branches in all repositories that do not have daily activity by creating a test branch or PR: - [ ] ``solc-js`` - [ ] ``solc-bin`` (make sure the bytecode comparison check did run) - - [ ] ``homebrew-ethereum`` - [ ] (Optional) Create a prerelease in our Ubuntu PPA by following the steps in the PPA section below on ``develop`` rather than on a tag. This is recommended especially when dealing with PPA for the first time, when we add a new Ubuntu version or when the PPA scripts were modified in this release cycle. - [ ] Verify that the release tarball of ``solc-js`` works. @@ -74,7 +72,6 @@ At least a day before the release: ### Homebrew and MacOS - [ ] Update the version and the hash (``sha256sum solidity_$VERSION.tar.gz``) in the [``solidity`` formula in Homebrew core repository](https://github.com/Homebrew/homebrew-core/blob/master/Formula/solidity.rb). - - [ ] Update the version and the hash (``sha256sum solidity_$VERSION.tar.gz``) in [our custom ``solidity`` Homebrew formula](https://github.com/ethereum/homebrew-ethereum/blob/master/solidity.rb). ### Docker - [ ] Run ``./scripts/docker_deploy_manual.sh v$VERSION``. From 7bf058b0c53a30b5040cb8905fdf7313f25bd42b Mon Sep 17 00:00:00 2001 From: Daniel Kirchner Date: Wed, 25 Oct 2023 14:26:22 +0200 Subject: [PATCH 06/14] Update ubuntu versions in PPA scripts. --- scripts/deps-ppa/static_z3.sh | 2 +- scripts/release_ppa.sh | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/scripts/deps-ppa/static_z3.sh b/scripts/deps-ppa/static_z3.sh index 5ee6623a6922..67cb15ce8a2c 100755 --- a/scripts/deps-ppa/static_z3.sh +++ b/scripts/deps-ppa/static_z3.sh @@ -41,7 +41,7 @@ sourcePPAConfig # Sanity check checkDputEntries "\[cpp-build-deps\]" -DISTRIBUTIONS="focal jammy kinetic lunar" +DISTRIBUTIONS="focal jammy lunar mantic" for distribution in $DISTRIBUTIONS do diff --git a/scripts/release_ppa.sh b/scripts/release_ppa.sh index bd98865d6ebc..252fd4c49c64 100755 --- a/scripts/release_ppa.sh +++ b/scripts/release_ppa.sh @@ -68,7 +68,7 @@ packagename=solc # This needs to be a still active release static_build_distribution=focal -DISTRIBUTIONS="focal jammy kinetic lunar" +DISTRIBUTIONS="focal jammy lunar mantic" if is_release then @@ -127,7 +127,7 @@ mv solidity solc mkdir -p ./solc/deps/downloads/ 2>/dev/null || true wget -O ./solc/deps/downloads/jsoncpp-1.9.3.tar.gz https://github.com/open-source-parsers/jsoncpp/archive/1.9.3.tar.gz wget -O ./solc/deps/downloads/range-v3-0.12.0.tar.gz https://github.com/ericniebler/range-v3/archive/0.12.0.tar.gz -wget -O ./solc/deps/downloads/fmt-8.0.1.tar.gz https://github.com/fmtlib/fmt/archive/8.0.1.tar.gz +wget -O ./solc/deps/downloads/fmt-9.1.0.tar.gz https://github.com/fmtlib/fmt/archive/9.1.0.tar.gz # Determine version cd solc From 4d10cb57241f35a0b5e500c46476e183a113de08 Mon Sep 17 00:00:00 2001 From: Nuzair Date: Tue, 7 Nov 2023 20:02:48 +0900 Subject: [PATCH 07/14] Update README.md for X (Twitter) new brand guidelines (#14665) * Update README.md for X (Twitter) new brand guidelines * suggested changes --- README.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index 6242a69f0b54..e64f8cbfd332 100644 --- a/README.md +++ b/README.md @@ -3,10 +3,10 @@ [![Matrix Chat](https://img.shields.io/badge/Matrix%20-chat-brightgreen?style=plastic&logo=matrix)](https://matrix.to/#/#ethereum_solidity:gitter.im) [![Gitter Chat](https://img.shields.io/badge/Gitter%20-chat-brightgreen?style=plastic&logo=gitter)](https://gitter.im/ethereum/solidity) [![Solidity Forum](https://img.shields.io/badge/Solidity_Forum%20-discuss-brightgreen?style=plastic&logo=discourse)](https://forum.soliditylang.org/) -[![Twitter Follow](https://img.shields.io/twitter/follow/solidity_lang?style=plastic&logo=twitter)](https://twitter.com/solidity_lang) +[![X Follow](https://img.shields.io/twitter/follow/solidity_lang?style=plastic&logo=x)](https://X.com/solidity_lang) [![Mastodon Follow](https://img.shields.io/mastodon/follow/000335908?domain=https%3A%2F%2Ffosstodon.org%2F&logo=mastodon&style=plastic)](https://fosstodon.org/@solidity) -You can talk to us on Gitter and Matrix, tweet at us on Twitter or create a new topic in the Solidity forum. Questions, feedback, and suggestions are welcome! +You can talk to us on Gitter and Matrix, tweet at us on X (previously Twitter) or create a new topic in the Solidity forum. Questions, feedback, and suggestions are welcome! Solidity is a statically typed, contract-oriented, high-level language for implementing smart contracts on the Ethereum platform. From d899d9c2c1095efe958c64c260c3b0a3fad726f0 Mon Sep 17 00:00:00 2001 From: Nikola Matic Date: Mon, 30 Oct 2023 11:44:00 +0100 Subject: [PATCH 08/14] 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 From cc56cb5921e28b3a80b8120702cde92f773f4ad9 Mon Sep 17 00:00:00 2001 From: Daniel Kirchner Date: Wed, 8 Nov 2023 00:45:37 +0100 Subject: [PATCH 09/14] Add verbatim bug test case. --- test/libyul/objectCompiler/verbatim_bug.yul | 109 ++++++++++++++++++++ 1 file changed, 109 insertions(+) create mode 100644 test/libyul/objectCompiler/verbatim_bug.yul diff --git a/test/libyul/objectCompiler/verbatim_bug.yul b/test/libyul/objectCompiler/verbatim_bug.yul new file mode 100644 index 000000000000..dcd39af8d500 --- /dev/null +++ b/test/libyul/objectCompiler/verbatim_bug.yul @@ -0,0 +1,109 @@ +object "a" { + code { + let dummy := 0xAABBCCDDEEFF + let input := sload(0) + let output + + switch input + case 0x00 { + // Note that due to a bug the following disappeared from the assembly output. + output := verbatim_1i_1o(hex"506000", dummy) + } + case 0x01 { + output := 1 + } + case 0x02 { + output := verbatim_1i_1o(hex"506002", dummy) + } + case 0x03 { + output := 3 + } + + sstore(0, output) + } +} +// ==== +// optimizationPreset: full +// ---- +// Assembly: +// /* "source":87:88 */ +// 0x00 +// /* "source":81:89 */ +// dup1 +// sload +// /* "source":139:307 */ +// dup1 +// iszero +// tag_5 +// jumpi +// /* "source":316:361 */ +// dup1 +// /* "source":321:325 */ +// 0x01 +// /* "source":316:361 */ +// eq +// tag_3 +// jumpi +// /* "source":370:448 */ +// dup1 +// /* "source":375:379 */ +// 0x02 +// /* "source":370:448 */ +// eq +// tag_5 +// jumpi +// /* "source":462:466 */ +// 0x03 +// /* "source":457:502 */ +// eq +// tag_7 +// jumpi +// /* "source":87:88 */ +// 0x00 +// /* "source":512:529 */ +// sstore +// /* "source":118:502 */ +// stop +// /* "source":467:502 */ +// tag_7: +// /* "source":481:492 */ +// pop +// /* "source":491:492 */ +// 0x03 +// /* "source":87:88 */ +// 0x00 +// /* "source":512:529 */ +// sstore +// /* "source":118:502 */ +// stop +// /* "source":380:448 */ +// tag_5: +// /* "source":404:438 */ +// pop +// pop +// /* "source":45:59 */ +// 0xaabbccddeeff +// /* "source":404:438 */ +// verbatimbytecode_506002 +// /* "source":87:88 */ +// 0x00 +// /* "source":512:529 */ +// sstore +// /* "source":118:502 */ +// stop +// /* "source":326:361 */ +// tag_3: +// /* "source":340:351 */ +// pop +// pop +// /* "source":350:351 */ +// 0x01 +// /* "source":87:88 */ +// 0x00 +// /* "source":512:529 */ +// sstore +// /* "source":118:502 */ +// stop +// Bytecode: 5f805480156026578060011460365780600214602657600314601f575f55005b5060035f55005b505065aabbccddeeff5060025f55005b505060015f5500 +// Opcodes: PUSH0 DUP1 SLOAD DUP1 ISZERO PUSH1 0x26 JUMPI DUP1 PUSH1 0x1 EQ PUSH1 0x36 JUMPI DUP1 PUSH1 0x2 EQ PUSH1 0x26 JUMPI PUSH1 0x3 EQ PUSH1 0x1F JUMPI PUSH0 SSTORE STOP JUMPDEST POP PUSH1 0x3 PUSH0 SSTORE STOP JUMPDEST POP POP PUSH6 0xAABBCCDDEEFF POP PUSH1 0x2 PUSH0 SSTORE STOP JUMPDEST POP POP PUSH1 0x1 PUSH0 SSTORE STOP +// SourceMappings: 87:1:0:-:0;81:8;;139:168;;;;316:45;321:4;316:45;;;370:78;375:4;370:78;;;462:4;457:45;;;87:1;512:17;118:384;467:35;481:11;491:1;87;512:17;118:384;380:68;404:34;;45:14;404:34;87:1;512:17;118:384;326:35;340:11;;350:1;87;512:17;118:384 From 48fdbd39b3db163ee563222d15b15c79fcd3cc95 Mon Sep 17 00:00:00 2001 From: Daniel Kirchner Date: Thu, 26 Oct 2023 01:11:00 +0200 Subject: [PATCH 10/14] Fix assembly item comparison for verbatim. Co-authored-by: Matheus Aguiar --- Changelog.md | 4 ++++ docs/bugs.json | 10 +++++++++ docs/bugs_by_version.json | 24 +++++++++++++++++++-- libevmasm/AssemblyItem.h | 2 +- test/libyul/objectCompiler/verbatim_bug.yul | 23 ++++++++++++++++---- 5 files changed, 56 insertions(+), 7 deletions(-) diff --git a/Changelog.md b/Changelog.md index d096eeddd1e6..c72aea22c114 100644 --- a/Changelog.md +++ b/Changelog.md @@ -1,5 +1,9 @@ ### 0.8.23 (unreleased) +Important Bugfixes: + * Optimizer: Fix block deduplicator bug which led to blocks which are identical apart from the contents of ``verbatim`` instructions to be treated as equivalent and thus collapsed into a single one. + + Language Features: diff --git a/docs/bugs.json b/docs/bugs.json index 6245bdeda5af..6e2468600537 100644 --- a/docs/bugs.json +++ b/docs/bugs.json @@ -1,4 +1,14 @@ [ + { + "uid": "SOL-2023-3", + "name": "VerbatimInvalidDeduplication", + "summary": "All ``verbatim`` blocks are considered identical by deduplicator and can incorrectly be unified when surrounded by identical opcodes.", + "description": "The block deduplicator is a step of the opcode-based optimizer which identifies equivalent assembly blocks and merges them into a single one. However, when blocks contained ``verbatim``, their comparison was performed incorrectly, leading to the collapse of assembly blocks which are identical except for the contents of the ``verbatim`` items. Since ``verbatim`` is only available in Yul, compilation of Solidity sources is not affected.", + "link": "https://blog.soliditylang.org/2023/X/Y/Z/", + "introduced": "0.8.5", + "fixed": "0.8.23", + "severity": "low" + }, { "uid": "SOL-2023-2", "name": "FullInlinerNonExpressionSplitArgumentEvaluationOrder", diff --git a/docs/bugs_by_version.json b/docs/bugs_by_version.json index 982444b10330..50132d0ed9ef 100644 --- a/docs/bugs_by_version.json +++ b/docs/bugs_by_version.json @@ -1743,6 +1743,7 @@ }, "0.8.10": { "bugs": [ + "VerbatimInvalidDeduplication", "FullInlinerNonExpressionSplitArgumentEvaluationOrder", "MissingSideEffectsOnSelectorAccess", "AbiReencodingHeadOverflowWithStaticArrayCleanup", @@ -1754,6 +1755,7 @@ }, "0.8.11": { "bugs": [ + "VerbatimInvalidDeduplication", "FullInlinerNonExpressionSplitArgumentEvaluationOrder", "MissingSideEffectsOnSelectorAccess", "AbiReencodingHeadOverflowWithStaticArrayCleanup", @@ -1766,6 +1768,7 @@ }, "0.8.12": { "bugs": [ + "VerbatimInvalidDeduplication", "FullInlinerNonExpressionSplitArgumentEvaluationOrder", "MissingSideEffectsOnSelectorAccess", "AbiReencodingHeadOverflowWithStaticArrayCleanup", @@ -1778,6 +1781,7 @@ }, "0.8.13": { "bugs": [ + "VerbatimInvalidDeduplication", "FullInlinerNonExpressionSplitArgumentEvaluationOrder", "MissingSideEffectsOnSelectorAccess", "StorageWriteRemovalBeforeConditionalTermination", @@ -1791,6 +1795,7 @@ }, "0.8.14": { "bugs": [ + "VerbatimInvalidDeduplication", "FullInlinerNonExpressionSplitArgumentEvaluationOrder", "MissingSideEffectsOnSelectorAccess", "StorageWriteRemovalBeforeConditionalTermination", @@ -1802,6 +1807,7 @@ }, "0.8.15": { "bugs": [ + "VerbatimInvalidDeduplication", "FullInlinerNonExpressionSplitArgumentEvaluationOrder", "MissingSideEffectsOnSelectorAccess", "StorageWriteRemovalBeforeConditionalTermination", @@ -1811,6 +1817,7 @@ }, "0.8.16": { "bugs": [ + "VerbatimInvalidDeduplication", "FullInlinerNonExpressionSplitArgumentEvaluationOrder", "MissingSideEffectsOnSelectorAccess", "StorageWriteRemovalBeforeConditionalTermination" @@ -1819,6 +1826,7 @@ }, "0.8.17": { "bugs": [ + "VerbatimInvalidDeduplication", "FullInlinerNonExpressionSplitArgumentEvaluationOrder", "MissingSideEffectsOnSelectorAccess" ], @@ -1826,6 +1834,7 @@ }, "0.8.18": { "bugs": [ + "VerbatimInvalidDeduplication", "FullInlinerNonExpressionSplitArgumentEvaluationOrder", "MissingSideEffectsOnSelectorAccess" ], @@ -1833,6 +1842,7 @@ }, "0.8.19": { "bugs": [ + "VerbatimInvalidDeduplication", "FullInlinerNonExpressionSplitArgumentEvaluationOrder", "MissingSideEffectsOnSelectorAccess" ], @@ -1854,17 +1864,22 @@ }, "0.8.20": { "bugs": [ + "VerbatimInvalidDeduplication", "FullInlinerNonExpressionSplitArgumentEvaluationOrder", "MissingSideEffectsOnSelectorAccess" ], "released": "2023-05-10" }, "0.8.21": { - "bugs": [], + "bugs": [ + "VerbatimInvalidDeduplication" + ], "released": "2023-07-19" }, "0.8.22": { - "bugs": [], + "bugs": [ + "VerbatimInvalidDeduplication" + ], "released": "2023-10-25" }, "0.8.3": { @@ -1894,6 +1909,7 @@ }, "0.8.5": { "bugs": [ + "VerbatimInvalidDeduplication", "FullInlinerNonExpressionSplitArgumentEvaluationOrder", "MissingSideEffectsOnSelectorAccess", "AbiReencodingHeadOverflowWithStaticArrayCleanup", @@ -1906,6 +1922,7 @@ }, "0.8.6": { "bugs": [ + "VerbatimInvalidDeduplication", "FullInlinerNonExpressionSplitArgumentEvaluationOrder", "MissingSideEffectsOnSelectorAccess", "AbiReencodingHeadOverflowWithStaticArrayCleanup", @@ -1918,6 +1935,7 @@ }, "0.8.7": { "bugs": [ + "VerbatimInvalidDeduplication", "FullInlinerNonExpressionSplitArgumentEvaluationOrder", "MissingSideEffectsOnSelectorAccess", "AbiReencodingHeadOverflowWithStaticArrayCleanup", @@ -1930,6 +1948,7 @@ }, "0.8.8": { "bugs": [ + "VerbatimInvalidDeduplication", "FullInlinerNonExpressionSplitArgumentEvaluationOrder", "MissingSideEffectsOnSelectorAccess", "AbiReencodingHeadOverflowWithStaticArrayCleanup", @@ -1943,6 +1962,7 @@ }, "0.8.9": { "bugs": [ + "VerbatimInvalidDeduplication", "FullInlinerNonExpressionSplitArgumentEvaluationOrder", "MissingSideEffectsOnSelectorAccess", "AbiReencodingHeadOverflowWithStaticArrayCleanup", diff --git a/libevmasm/AssemblyItem.h b/libevmasm/AssemblyItem.h index 40f98dae875c..93be394733ca 100644 --- a/libevmasm/AssemblyItem.h +++ b/libevmasm/AssemblyItem.h @@ -140,7 +140,7 @@ class AssemblyItem else if (type() == Operation) return instruction() < _other.instruction(); else if (type() == VerbatimBytecode) - return *m_verbatimBytecode == *_other.m_verbatimBytecode; + return *m_verbatimBytecode < *_other.m_verbatimBytecode; else return data() < _other.data(); } diff --git a/test/libyul/objectCompiler/verbatim_bug.yul b/test/libyul/objectCompiler/verbatim_bug.yul index dcd39af8d500..778494f0b311 100644 --- a/test/libyul/objectCompiler/verbatim_bug.yul +++ b/test/libyul/objectCompiler/verbatim_bug.yul @@ -34,7 +34,7 @@ object "a" { // /* "source":139:307 */ // dup1 // iszero -// tag_5 +// tag_1 // jumpi // /* "source":316:361 */ // dup1 @@ -104,6 +104,21 @@ object "a" { // sstore // /* "source":118:502 */ // stop -// Bytecode: 5f805480156026578060011460365780600214602657600314601f575f55005b5060035f55005b505065aabbccddeeff5060025f55005b505060015f5500 -// Opcodes: PUSH0 DUP1 SLOAD DUP1 ISZERO PUSH1 0x26 JUMPI DUP1 PUSH1 0x1 EQ PUSH1 0x36 JUMPI DUP1 PUSH1 0x2 EQ PUSH1 0x26 JUMPI PUSH1 0x3 EQ PUSH1 0x1F JUMPI PUSH0 SSTORE STOP JUMPDEST POP PUSH1 0x3 PUSH0 SSTORE STOP JUMPDEST POP POP PUSH6 0xAABBCCDDEEFF POP PUSH1 0x2 PUSH0 SSTORE STOP JUMPDEST POP POP PUSH1 0x1 PUSH0 SSTORE STOP -// SourceMappings: 87:1:0:-:0;81:8;;139:168;;;;316:45;321:4;316:45;;;370:78;375:4;370:78;;;462:4;457:45;;;87:1;512:17;118:384;467:35;481:11;491:1;87;512:17;118:384;380:68;404:34;;45:14;404:34;87:1;512:17;118:384;326:35;340:11;;350:1;87;512:17;118:384 +// /* "source":149:307 */ +// tag_1: +// /* "source":263:297 */ +// pop +// pop +// /* "source":45:59 */ +// 0xaabbccddeeff +// /* "source":263:297 */ +// verbatimbytecode_506000 +// /* "source":87:88 */ +// 0x00 +// /* "source":512:529 */ +// sstore +// /* "source":118:502 */ +// stop +// Bytecode: 5f80548015603e578060011460365780600214602657600314601f575f55005b5060035f55005b505065aabbccddeeff5060025f55005b505060015f55005b505065aabbccddeeff5060005f5500 +// Opcodes: PUSH0 DUP1 SLOAD DUP1 ISZERO PUSH1 0x3E JUMPI DUP1 PUSH1 0x1 EQ PUSH1 0x36 JUMPI DUP1 PUSH1 0x2 EQ PUSH1 0x26 JUMPI PUSH1 0x3 EQ PUSH1 0x1F JUMPI PUSH0 SSTORE STOP JUMPDEST POP PUSH1 0x3 PUSH0 SSTORE STOP JUMPDEST POP POP PUSH6 0xAABBCCDDEEFF POP PUSH1 0x2 PUSH0 SSTORE STOP JUMPDEST POP POP PUSH1 0x1 PUSH0 SSTORE STOP JUMPDEST POP POP PUSH6 0xAABBCCDDEEFF POP PUSH1 0x0 PUSH0 SSTORE STOP +// SourceMappings: 87:1:0:-:0;81:8;;139:168;;;;316:45;321:4;316:45;;;370:78;375:4;370:78;;;462:4;457:45;;;87:1;512:17;118:384;467:35;481:11;491:1;87;512:17;118:384;380:68;404:34;;45:14;404:34;87:1;512:17;118:384;326:35;340:11;;350:1;87;512:17;118:384;149:158;263:34;;45:14;263:34;87:1;512:17;118:384 From 41ceb483fafb550ae9dd121f322cfc1f2e32cd91 Mon Sep 17 00:00:00 2001 From: Matheus Aguiar Date: Tue, 7 Nov 2023 23:00:23 -0300 Subject: [PATCH 11/14] Add cmdline test --- .../~deduplicator-verbatim-bug/test.sh | 19 +++++++++++++++++ .../verbatim_inside_identical_blocks.yul | 21 +++++++++++++++++++ 2 files changed, 40 insertions(+) create mode 100755 test/cmdlineTests/~deduplicator-verbatim-bug/test.sh create mode 100644 test/cmdlineTests/~deduplicator-verbatim-bug/verbatim_inside_identical_blocks.yul diff --git a/test/cmdlineTests/~deduplicator-verbatim-bug/test.sh b/test/cmdlineTests/~deduplicator-verbatim-bug/test.sh new file mode 100755 index 000000000000..19b57e8caab3 --- /dev/null +++ b/test/cmdlineTests/~deduplicator-verbatim-bug/test.sh @@ -0,0 +1,19 @@ +#!/usr/bin/env bash +set -eo pipefail + +# This is a regression test against https://github.com/ethereum/solidity/issues/14640 +# The bug caused the block deduplicator to incorrectly merge two blocks which have +# verbatim items surrounded by identical opcodes. Due to the bug, the contents of +# the verbatim were ignored and the blocks merged into a single one. + +# shellcheck source=scripts/common.sh +source "${REPO_ROOT}/scripts/common.sh" +# shellcheck source=scripts/common_cmdline.sh +source "${REPO_ROOT}/scripts/common_cmdline.sh" + +SCRIPT_DIR=$(cd "$(dirname "$0")" && pwd) +YUL_SOURCE="${SCRIPT_DIR}/verbatim_inside_identical_blocks.yul" + +VERBATIM_OCCURRENCES=$(< "$YUL_SOURCE" "$SOLC" --strict-assembly - --optimize --asm | grep -e "verbatim" -c) + +[[ $VERBATIM_OCCURRENCES == 2 ]] || assertFail "Incorrect number of verbatim items in assembly." diff --git a/test/cmdlineTests/~deduplicator-verbatim-bug/verbatim_inside_identical_blocks.yul b/test/cmdlineTests/~deduplicator-verbatim-bug/verbatim_inside_identical_blocks.yul new file mode 100644 index 000000000000..1dc9a4615eaa --- /dev/null +++ b/test/cmdlineTests/~deduplicator-verbatim-bug/verbatim_inside_identical_blocks.yul @@ -0,0 +1,21 @@ +{ + let special := 0xFFFFFFFFFFFF + let input := sload(0) + let output + + switch input + case 0x00 { + output := verbatim_1i_1o(hex"506000", special) + } + case 0x01 { + output := 1 + } + case 0x02 { + output := verbatim_1i_1o(hex"506002", special) + } + case 0x03 { + output := 3 + } + + sstore(0, output) +} From 8292d7b53a1572dad7d4b422497078d42d17c273 Mon Sep 17 00:00:00 2001 From: Nikola Matic Date: Wed, 8 Nov 2023 09:17:49 +0100 Subject: [PATCH 12/14] Remove superflous language features category from Changelog --- Changelog.md | 5 ----- 1 file changed, 5 deletions(-) diff --git a/Changelog.md b/Changelog.md index c72aea22c114..03bfd2ce4920 100644 --- a/Changelog.md +++ b/Changelog.md @@ -4,15 +4,10 @@ Important Bugfixes: * Optimizer: Fix block deduplicator bug which led to blocks which are identical apart from the contents of ``verbatim`` instructions to be treated as equivalent and thus collapsed into a single one. -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: - ### 0.8.22 (2023-10-25) From 7e84be61e31bab9a7d9a6659ff4cf95417c13bbc Mon Sep 17 00:00:00 2001 From: Nikola Matic Date: Wed, 8 Nov 2023 10:05:42 +0100 Subject: [PATCH 13/14] Set date for 0.8.23 release --- Changelog.md | 2 +- docs/bugs_by_version.json | 4 ++++ 2 files changed, 5 insertions(+), 1 deletion(-) diff --git a/Changelog.md b/Changelog.md index 03bfd2ce4920..ef5416fb3dcb 100644 --- a/Changelog.md +++ b/Changelog.md @@ -1,4 +1,4 @@ -### 0.8.23 (unreleased) +### 0.8.23 (2023-11-08) Important Bugfixes: * Optimizer: Fix block deduplicator bug which led to blocks which are identical apart from the contents of ``verbatim`` instructions to be treated as equivalent and thus collapsed into a single one. diff --git a/docs/bugs_by_version.json b/docs/bugs_by_version.json index 50132d0ed9ef..152e8d97a4d5 100644 --- a/docs/bugs_by_version.json +++ b/docs/bugs_by_version.json @@ -1882,6 +1882,10 @@ ], "released": "2023-10-25" }, + "0.8.23": { + "bugs": [], + "released": "2023-11-08" + }, "0.8.3": { "bugs": [ "FullInlinerNonExpressionSplitArgumentEvaluationOrder", From 5cafa8c82618db013546755864412a3db8074641 Mon Sep 17 00:00:00 2001 From: Nikola Matic Date: Wed, 8 Nov 2023 12:25:03 +0100 Subject: [PATCH 14/14] Fix link in bugs.json for 0.8.23 --- docs/bugs.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/bugs.json b/docs/bugs.json index 6e2468600537..c853f95eddea 100644 --- a/docs/bugs.json +++ b/docs/bugs.json @@ -4,7 +4,7 @@ "name": "VerbatimInvalidDeduplication", "summary": "All ``verbatim`` blocks are considered identical by deduplicator and can incorrectly be unified when surrounded by identical opcodes.", "description": "The block deduplicator is a step of the opcode-based optimizer which identifies equivalent assembly blocks and merges them into a single one. However, when blocks contained ``verbatim``, their comparison was performed incorrectly, leading to the collapse of assembly blocks which are identical except for the contents of the ``verbatim`` items. Since ``verbatim`` is only available in Yul, compilation of Solidity sources is not affected.", - "link": "https://blog.soliditylang.org/2023/X/Y/Z/", + "link": "https://blog.soliditylang.org/2023/11/08/verbatim-invalid-deduplication-bug/", "introduced": "0.8.5", "fixed": "0.8.23", "severity": "low"