From 50bca3d1eb2c1218fbfe31c88e8c771835b1ec27 Mon Sep 17 00:00:00 2001 From: William Morriss Date: Wed, 1 Jul 2026 16:27:33 -0500 Subject: [PATCH 01/10] feat(yul): add SwitchSplitter optimizer pass Assisted-by: Claude:claude-sonnet-4-6 --- libsolidity/interface/OptimiserSettings.h | 4 +- libyul/CMakeLists.txt | 2 + libyul/optimiser/Suite.cpp | 3 + libyul/optimiser/SwitchSplitter.cpp | 173 ++++++++++++++++++ libyul/optimiser/SwitchSplitter.h | 83 +++++++++ test/libyul/YulOptimizerTestCommon.cpp | 8 + .../switchSplitter/nested_switch.yul | 111 +++++++++++ .../switchSplitter/no_split_few_cases.yul | 48 +++++ .../switchSplitter/no_split_large_default.yul | 70 +++++++ .../split_complex_expression.yul | 61 ++++++ .../switchSplitter/split_eight_cases.yul | 66 +++++++ .../switchSplitter/split_seven_cases.yul | 60 ++++++ .../switchSplitter/split_sixteen_cases.yul | 126 +++++++++++++ .../switchSplitter/split_with_default.yul | 83 +++++++++ 14 files changed, 896 insertions(+), 2 deletions(-) create mode 100644 libyul/optimiser/SwitchSplitter.cpp create mode 100644 libyul/optimiser/SwitchSplitter.h create mode 100644 test/libyul/yulOptimizerTests/switchSplitter/nested_switch.yul create mode 100644 test/libyul/yulOptimizerTests/switchSplitter/no_split_few_cases.yul create mode 100644 test/libyul/yulOptimizerTests/switchSplitter/no_split_large_default.yul create mode 100644 test/libyul/yulOptimizerTests/switchSplitter/split_complex_expression.yul create mode 100644 test/libyul/yulOptimizerTests/switchSplitter/split_eight_cases.yul create mode 100644 test/libyul/yulOptimizerTests/switchSplitter/split_seven_cases.yul create mode 100644 test/libyul/yulOptimizerTests/switchSplitter/split_sixteen_cases.yul create mode 100644 test/libyul/yulOptimizerTests/switchSplitter/split_with_default.yul diff --git a/libsolidity/interface/OptimiserSettings.h b/libsolidity/interface/OptimiserSettings.h index fe8288aac18f..12034614f8ec 100644 --- a/libsolidity/interface/OptimiserSettings.h +++ b/libsolidity/interface/OptimiserSettings.h @@ -43,7 +43,7 @@ struct OptimiserSettings static char constexpr DefaultYulOptimiserSteps[] = "dfDvulfnTUtnIf" // None of these can make stack problems worse - "xa[r]EscLM" // Turn into SSA and simplify + "xBa[r]EscLM" // Turn into SSA and simplify "Vcul [j]" // Reverse SSA // should have good "compilability" property here. @@ -55,7 +55,7 @@ struct OptimiserSettings "scCTUt" "vifM" // Run full inliner - "x[scCTUt] TOntnfDIul" // Perform structural simplification + "x[scCTUtB] TOntnfDIul" // Perform structural simplification "vifM" // Run full inliner "jmul[jul] VcTOcul jmul"; // Make source short and pretty diff --git a/libyul/CMakeLists.txt b/libyul/CMakeLists.txt index 6c421646b8e3..b0c3e57b1286 100644 --- a/libyul/CMakeLists.txt +++ b/libyul/CMakeLists.txt @@ -237,6 +237,8 @@ add_library(yul optimiser/StackToMemoryMover.h optimiser/StructuralSimplifier.cpp optimiser/StructuralSimplifier.h + optimiser/SwitchSplitter.cpp + optimiser/SwitchSplitter.h optimiser/Substitution.cpp optimiser/Substitution.h optimiser/Suite.cpp diff --git a/libyul/optimiser/Suite.cpp b/libyul/optimiser/Suite.cpp index 4176913b649a..b8034fbb266a 100644 --- a/libyul/optimiser/Suite.cpp +++ b/libyul/optimiser/Suite.cpp @@ -54,6 +54,7 @@ #include #include #include +#include #include #include #include @@ -254,6 +255,7 @@ std::map> const& OptimiserSuite::all SSAReverser, SSATransform, StructuralSimplifier, + SwitchSplitter, UnusedFunctionParameterPruner, UnusedPruner, VarDeclInitializer @@ -295,6 +297,7 @@ std::map const& OptimiserSuite::stepNameToAbbreviationMap() {SSAReverser::name, 'V'}, {SSATransform::name, 'a'}, {StructuralSimplifier::name, 't'}, + {SwitchSplitter::name, 'B'}, {UnusedFunctionParameterPruner::name, 'p'}, {UnusedPruner::name, 'u'}, {VarDeclInitializer::name, 'd'}, diff --git a/libyul/optimiser/SwitchSplitter.cpp b/libyul/optimiser/SwitchSplitter.cpp new file mode 100644 index 000000000000..7e57d9c9c9b9 --- /dev/null +++ b/libyul/optimiser/SwitchSplitter.cpp @@ -0,0 +1,173 @@ +/* + This file is part of solidity. + + solidity is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + solidity is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with solidity. If not, see . +*/ +// SPDX-License-Identifier: GPL-3.0 +#include + +#include +#include +#include +#include +#include + +#include +#include + +#include +#include +#include + +using namespace solidity; +using namespace solidity::util; +using namespace solidity::yul; + +using OptionalStatements = std::optional>; + +void SwitchSplitter::run(OptimiserStepContext& _context, Block& _ast) +{ + SwitchSplitter{_context}(_ast); +} + +SwitchSplitter::SwitchSplitter(OptimiserStepContext const& _context): + m_runs(_context.expectedExecutionsPerDeployment.value_or(0)) +{ + if (auto const* evmDialect = dynamic_cast(&_context.dialect)) + m_gtHandle = evmDialect->findBuiltin("gt"); +} + +void SwitchSplitter::operator()(Block& _block) +{ + simplify(_block.statements); +} + +void SwitchSplitter::simplify(std::vector& _statements) +{ + iterateReplacing( + _statements, + [&](Statement& _stmt) -> OptionalStatements + { + if (auto* sw = std::get_if(&_stmt)) + if (auto result = tryTransform(*sw)) + { + simplify(*result); + return result; + } + this->visit(_stmt); + return {}; + } + ); +} + +std::optional> SwitchSplitter::tryTransform(Switch& _switch) +{ + if (!m_gtHandle) + return {}; + + // Requires a simple identifier — ensured by ExpressionSplitter running first. + auto const* exprIdent = std::get_if(_switch.expression.get()); + if (!exprIdent) + return {}; + + // Separate literal cases from the default case. + std::vector literalCases; + Block const* defaultBody = nullptr; + static Block const emptyBlock{}; + + for (auto const& c: _switch.cases) + if (c.value) + literalCases.push_back(&c); + else + defaultBody = &c.body; + + if (!defaultBody) + defaultBody = &emptyBlock; + + // Only transform if the top-level split is profitable. + if (!shouldSplit(literalCases.size(), defaultBody->statements.size())) + return {}; + + std::sort(literalCases.begin(), literalCases.end(), [](Case const* _a, Case const* _b) { + return _a->value->value.value() < _b->value->value.value(); + }); + + return buildTree(literalCases, *exprIdent, *defaultBody, _switch.debugData); +} + +std::vector SwitchSplitter::buildTree( + std::span _cases, + Identifier const& _expr, + Block const& _defaultBody, + langutil::DebugData::ConstPtr _debugData +) +{ + size_t n = _cases.size(); + bool const hasDefault = !_defaultBody.statements.empty(); + + if (!shouldSplit(n, _defaultBody.statements.size())) + { + // Leaf: small switch; default case added only when present. + std::vector switchCases; + switchCases.reserve(n + (hasDefault ? 1 : 0)); + for (auto const* c: _cases) + switchCases.push_back(Case{ + c->debugData, + std::make_unique(*c->value), + ASTCopier{}.translate(c->body) + }); + if (hasDefault) + switchCases.push_back(Case{_debugData, nullptr, ASTCopier{}.translate(_defaultBody)}); + std::vector result; + result.emplace_back(Switch{_debugData, std::make_unique(_expr), std::move(switchCases)}); + return result; + } + + // Split: pivot stays in lower half → switch gt(expr, pivot) case 1 { upper } default { lower }. + size_t pivotIdx = (n - 1) / 2; + Case const* pivot = _cases[pivotIdx]; + + auto lowerStmts = buildTree(_cases.subspan(0, pivotIdx + 1), _expr, _defaultBody, _debugData); + auto upperStmts = buildTree(_cases.subspan(pivotIdx + 1), _expr, _defaultBody, _debugData); + + std::vector switchCases; + switchCases.reserve(2); + switchCases.push_back(Case{ + _debugData, + std::make_unique(Literal{_debugData, LiteralKind::Number, LiteralValue{u256(1)}}), + Block{_debugData, std::move(upperStmts)} + }); + switchCases.push_back(Case{_debugData, nullptr, Block{_debugData, std::move(lowerStmts)}}); + + std::vector result; + result.emplace_back(Switch{ + _debugData, + std::make_unique(FunctionCall{ + _debugData, + BuiltinName{_debugData, *m_gtHandle}, + {Expression{_expr}, Expression{*pivot->value}} + }), + std::move(switchCases) + }); + return result; +} + +bool SwitchSplitter::shouldSplit(size_t _n, size_t _defaultBodySize) const +{ + if (_n <= 4) + return false; + // Same as ContractCompiler::appendInternalSelector but overhead += k for default body duplication. + size_t const overhead = 17 + _defaultBodySize; + return m_runs * 6 * (_n - 4) > overhead * evmasm::GasCosts::createDataGas; +} diff --git a/libyul/optimiser/SwitchSplitter.h b/libyul/optimiser/SwitchSplitter.h new file mode 100644 index 000000000000..e7df8d0a6b5d --- /dev/null +++ b/libyul/optimiser/SwitchSplitter.h @@ -0,0 +1,83 @@ +/* + This file is part of solidity. + + solidity is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + solidity is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with solidity. If not, see . +*/ +// SPDX-License-Identifier: GPL-3.0 +#pragma once + +#include +#include +#include + +#include + +#include +#include +#include + +namespace solidity::yul +{ + +/** + * Transforms a large Yul switch statement into a binary search tree of nested switches, + * reducing runtime dispatch from O(n) to O(log n). + * + * The transformation is applied only when the runtime gas savings over + * expectedExecutionsPerDeployment executions outweigh the extra code size (creation gas): + * runs * 6 * (n - 4) > (17 + k) * createDataGas + * where k is the default body statement count (proxy for bytecode size). + * The 17-byte split overhead comes from the gt-switch structure; k accounts for each split + * duplicating the default body into one additional leaf. + * + * Applied recursively: each sub-slice re-evaluates the formula. + * The default case body is duplicated into each leaf branch when present. + * + * Prerequisite: Disambiguator, ExpressionSplitter. + * The switch expression must already be a simple identifier (ensured by ExpressionSplitter). + * + * Important: Can only be used on EVM code. + */ +class SwitchSplitter: public ASTModifier +{ +public: + static constexpr char const* name{"SwitchSplitter"}; + static void run(OptimiserStepContext& _context, Block& _ast); + + using ASTModifier::operator(); + void operator()(Block& _block) override; + +private: + explicit SwitchSplitter(OptimiserStepContext const& _context); + + void simplify(std::vector& _statements); + + // Returns replacement statements if the switch should be transformed, nullopt otherwise. + std::optional> tryTransform(Switch& _switch); + + // Recursively builds the binary search tree for a sorted, non-empty slice of literal cases. + std::vector buildTree( + std::span _cases, + Identifier const& _expr, + Block const& _defaultBody, + langutil::DebugData::ConstPtr _debugData + ); + + bool shouldSplit(size_t _n, size_t _defaultBodySize) const; + + std::optional m_gtHandle; + size_t m_runs = 0; +}; + +} diff --git a/test/libyul/YulOptimizerTestCommon.cpp b/test/libyul/YulOptimizerTestCommon.cpp index cd54318c19ef..138d4dfcf181 100644 --- a/test/libyul/YulOptimizerTestCommon.cpp +++ b/test/libyul/YulOptimizerTestCommon.cpp @@ -56,6 +56,7 @@ #include #include #include +#include #include #include #include @@ -369,6 +370,13 @@ YulOptimizerTestCommon::YulOptimizerTestCommon(std::shared_ptr _ob StructuralSimplifier::run(*m_context, block); return block; }}, + {"switchSplitter", [&]() { + auto block = disambiguate(); + updateContext(block); + ExpressionSplitter::run(*m_context, block); + SwitchSplitter::run(*m_context, block); + return block; + }}, {"equivalentFunctionCombiner", [&]() { auto block = disambiguate(); updateContext(block); diff --git a/test/libyul/yulOptimizerTests/switchSplitter/nested_switch.yul b/test/libyul/yulOptimizerTests/switchSplitter/nested_switch.yul new file mode 100644 index 000000000000..5658181b7f40 --- /dev/null +++ b/test/libyul/yulOptimizerTests/switchSplitter/nested_switch.yul @@ -0,0 +1,111 @@ +{ + let sel := calldataload(0) + switch sel + case 1 { sstore(0, 1) } + case 2 { sstore(0, 2) } + case 3 { sstore(0, 3) } + case 4 { sstore(0, 4) } + case 5 { + let inner := calldataload(32) + switch inner + case 10 { sstore(1, 10) } + case 11 { sstore(1, 11) } + case 12 { sstore(1, 12) } + case 13 { sstore(1, 13) } + case 14 { sstore(1, 14) } + case 15 { sstore(1, 15) } + case 16 { sstore(1, 16) } + } + case 6 { sstore(0, 6) } + case 7 { sstore(0, 7) } +} +// ---- +// step: switchSplitter +// +// { +// let _1 := 0 +// let sel := calldataload(_1) +// switch gt(sel, 4) +// case 1 { +// switch sel +// case 5 { +// let _10 := 32 +// let inner := calldataload(_10) +// switch gt(inner, 13) +// case 1 { +// switch inner +// case 14 { +// let _19 := 14 +// let _20 := 1 +// sstore(_20, _19) +// } +// case 15 { +// let _21 := 15 +// let _22 := 1 +// sstore(_22, _21) +// } +// case 16 { +// let _23 := 16 +// let _24 := 1 +// sstore(_24, _23) +// } +// } +// default { +// switch inner +// case 10 { +// let _11 := 10 +// let _12 := 1 +// sstore(_12, _11) +// } +// case 11 { +// let _13 := 11 +// let _14 := 1 +// sstore(_14, _13) +// } +// case 12 { +// let _15 := 12 +// let _16 := 1 +// sstore(_16, _15) +// } +// case 13 { +// let _17 := 13 +// let _18 := 1 +// sstore(_18, _17) +// } +// } +// } +// case 6 { +// let _25 := 6 +// let _26 := 0 +// sstore(_26, _25) +// } +// case 7 { +// let _27 := 7 +// let _28 := 0 +// sstore(_28, _27) +// } +// } +// default { +// switch sel +// case 1 { +// let _2 := 1 +// let _3 := 0 +// sstore(_3, _2) +// } +// case 2 { +// let _4 := 2 +// let _5 := 0 +// sstore(_5, _4) +// } +// case 3 { +// let _6 := 3 +// let _7 := 0 +// sstore(_7, _6) +// } +// case 4 { +// let _8 := 4 +// let _9 := 0 +// sstore(_9, _8) +// } +// } +// } diff --git a/test/libyul/yulOptimizerTests/switchSplitter/no_split_few_cases.yul b/test/libyul/yulOptimizerTests/switchSplitter/no_split_few_cases.yul new file mode 100644 index 000000000000..6301d7f2642f --- /dev/null +++ b/test/libyul/yulOptimizerTests/switchSplitter/no_split_few_cases.yul @@ -0,0 +1,48 @@ +{ + let sel := calldataload(0) + switch sel + case 1 { sstore(0, 1) } + case 2 { sstore(0, 2) } + case 3 { sstore(0, 3) } + case 4 { sstore(0, 4) } + case 5 { sstore(0, 5) } + case 6 { sstore(0, 6) } +} +// ---- +// step: switchSplitter +// +// { +// let _1 := 0 +// let sel := calldataload(_1) +// switch sel +// case 1 { +// let _2 := 1 +// let _3 := 0 +// sstore(_3, _2) +// } +// case 2 { +// let _4 := 2 +// let _5 := 0 +// sstore(_5, _4) +// } +// case 3 { +// let _6 := 3 +// let _7 := 0 +// sstore(_7, _6) +// } +// case 4 { +// let _8 := 4 +// let _9 := 0 +// sstore(_9, _8) +// } +// case 5 { +// let _10 := 5 +// let _11 := 0 +// sstore(_11, _10) +// } +// case 6 { +// let _12 := 6 +// let _13 := 0 +// sstore(_13, _12) +// } +// } diff --git a/test/libyul/yulOptimizerTests/switchSplitter/no_split_large_default.yul b/test/libyul/yulOptimizerTests/switchSplitter/no_split_large_default.yul new file mode 100644 index 000000000000..81c334ed3f16 --- /dev/null +++ b/test/libyul/yulOptimizerTests/switchSplitter/no_split_large_default.yul @@ -0,0 +1,70 @@ +{ + let sel := calldataload(0) + switch sel + case 1 { sstore(0, 1) } + case 2 { sstore(0, 2) } + case 3 { sstore(0, 3) } + case 4 { sstore(0, 4) } + case 5 { sstore(0, 5) } + case 6 { sstore(0, 6) } + case 7 { sstore(0, 7) } + default { + mstore(0, 0x08c379a0) + mstore(4, 0x20) + revert(0, 0x44) + } +} +// ---- +// step: switchSplitter +// +// { +// let _1 := 0 +// let sel := calldataload(_1) +// switch sel +// case 1 { +// let _2 := 1 +// let _3 := 0 +// sstore(_3, _2) +// } +// case 2 { +// let _4 := 2 +// let _5 := 0 +// sstore(_5, _4) +// } +// case 3 { +// let _6 := 3 +// let _7 := 0 +// sstore(_7, _6) +// } +// case 4 { +// let _8 := 4 +// let _9 := 0 +// sstore(_9, _8) +// } +// case 5 { +// let _10 := 5 +// let _11 := 0 +// sstore(_11, _10) +// } +// case 6 { +// let _12 := 6 +// let _13 := 0 +// sstore(_13, _12) +// } +// case 7 { +// let _14 := 7 +// let _15 := 0 +// sstore(_15, _14) +// } +// default { +// let _16 := 0x08c379a0 +// let _17 := 0 +// mstore(_17, _16) +// let _18 := 0x20 +// let _19 := 4 +// mstore(_19, _18) +// let _20 := 0x44 +// let _21 := 0 +// revert(_21, _20) +// } +// } diff --git a/test/libyul/yulOptimizerTests/switchSplitter/split_complex_expression.yul b/test/libyul/yulOptimizerTests/switchSplitter/split_complex_expression.yul new file mode 100644 index 000000000000..d8ee0ac9901a --- /dev/null +++ b/test/libyul/yulOptimizerTests/switchSplitter/split_complex_expression.yul @@ -0,0 +1,61 @@ +{ + switch add(calldataload(0), 1) + case 1 { sstore(0, 1) } + case 2 { sstore(0, 2) } + case 3 { sstore(0, 3) } + case 4 { sstore(0, 4) } + case 5 { sstore(0, 5) } + case 6 { sstore(0, 6) } + case 7 { sstore(0, 7) } +} +// ---- +// step: switchSplitter +// +// { +// let _1 := 1 +// let _2 := 0 +// let _3 := calldataload(_2) +// let _4 := add(_3, _1) +// switch gt(_4, 4) +// case 1 { +// switch _4 +// case 5 { +// let _13 := 5 +// let _14 := 0 +// sstore(_14, _13) +// } +// case 6 { +// let _15 := 6 +// let _16 := 0 +// sstore(_16, _15) +// } +// case 7 { +// let _17 := 7 +// let _18 := 0 +// sstore(_18, _17) +// } +// } +// default { +// switch _4 +// case 1 { +// let _5 := 1 +// let _6 := 0 +// sstore(_6, _5) +// } +// case 2 { +// let _7 := 2 +// let _8 := 0 +// sstore(_8, _7) +// } +// case 3 { +// let _9 := 3 +// let _10 := 0 +// sstore(_10, _9) +// } +// case 4 { +// let _11 := 4 +// let _12 := 0 +// sstore(_12, _11) +// } +// } +// } diff --git a/test/libyul/yulOptimizerTests/switchSplitter/split_eight_cases.yul b/test/libyul/yulOptimizerTests/switchSplitter/split_eight_cases.yul new file mode 100644 index 000000000000..77e337740ba4 --- /dev/null +++ b/test/libyul/yulOptimizerTests/switchSplitter/split_eight_cases.yul @@ -0,0 +1,66 @@ +{ + let sel := calldataload(0) + switch sel + case 1 { sstore(0, 1) } + case 2 { sstore(0, 2) } + case 3 { sstore(0, 3) } + case 4 { sstore(0, 4) } + case 5 { sstore(0, 5) } + case 6 { sstore(0, 6) } + case 7 { sstore(0, 7) } + case 8 { sstore(0, 8) } +} +// ---- +// step: switchSplitter +// +// { +// let _1 := 0 +// let sel := calldataload(_1) +// switch gt(sel, 4) +// case 1 { +// switch sel +// case 5 { +// let _10 := 5 +// let _11 := 0 +// sstore(_11, _10) +// } +// case 6 { +// let _12 := 6 +// let _13 := 0 +// sstore(_13, _12) +// } +// case 7 { +// let _14 := 7 +// let _15 := 0 +// sstore(_15, _14) +// } +// case 8 { +// let _16 := 8 +// let _17 := 0 +// sstore(_17, _16) +// } +// } +// default { +// switch sel +// case 1 { +// let _2 := 1 +// let _3 := 0 +// sstore(_3, _2) +// } +// case 2 { +// let _4 := 2 +// let _5 := 0 +// sstore(_5, _4) +// } +// case 3 { +// let _6 := 3 +// let _7 := 0 +// sstore(_7, _6) +// } +// case 4 { +// let _8 := 4 +// let _9 := 0 +// sstore(_9, _8) +// } +// } +// } diff --git a/test/libyul/yulOptimizerTests/switchSplitter/split_seven_cases.yul b/test/libyul/yulOptimizerTests/switchSplitter/split_seven_cases.yul new file mode 100644 index 000000000000..8bcd9922c6e6 --- /dev/null +++ b/test/libyul/yulOptimizerTests/switchSplitter/split_seven_cases.yul @@ -0,0 +1,60 @@ +{ + let sel := calldataload(0) + switch sel + case 1 { sstore(0, 1) } + case 2 { sstore(0, 2) } + case 3 { sstore(0, 3) } + case 4 { sstore(0, 4) } + case 5 { sstore(0, 5) } + case 6 { sstore(0, 6) } + case 7 { sstore(0, 7) } +} +// ---- +// step: switchSplitter +// +// { +// let _1 := 0 +// let sel := calldataload(_1) +// switch gt(sel, 4) +// case 1 { +// switch sel +// case 5 { +// let _10 := 5 +// let _11 := 0 +// sstore(_11, _10) +// } +// case 6 { +// let _12 := 6 +// let _13 := 0 +// sstore(_13, _12) +// } +// case 7 { +// let _14 := 7 +// let _15 := 0 +// sstore(_15, _14) +// } +// } +// default { +// switch sel +// case 1 { +// let _2 := 1 +// let _3 := 0 +// sstore(_3, _2) +// } +// case 2 { +// let _4 := 2 +// let _5 := 0 +// sstore(_5, _4) +// } +// case 3 { +// let _6 := 3 +// let _7 := 0 +// sstore(_7, _6) +// } +// case 4 { +// let _8 := 4 +// let _9 := 0 +// sstore(_9, _8) +// } +// } +// } diff --git a/test/libyul/yulOptimizerTests/switchSplitter/split_sixteen_cases.yul b/test/libyul/yulOptimizerTests/switchSplitter/split_sixteen_cases.yul new file mode 100644 index 000000000000..dad4579ef763 --- /dev/null +++ b/test/libyul/yulOptimizerTests/switchSplitter/split_sixteen_cases.yul @@ -0,0 +1,126 @@ +{ + let sel := calldataload(0) + switch sel + case 1 { sstore(0, 1) } + case 2 { sstore(0, 2) } + case 3 { sstore(0, 3) } + case 4 { sstore(0, 4) } + case 5 { sstore(0, 5) } + case 6 { sstore(0, 6) } + case 7 { sstore(0, 7) } + case 8 { sstore(0, 8) } + case 9 { sstore(0, 9) } + case 10 { sstore(0, 10) } + case 11 { sstore(0, 11) } + case 12 { sstore(0, 12) } + case 13 { sstore(0, 13) } + case 14 { sstore(0, 14) } + case 15 { sstore(0, 15) } + case 16 { sstore(0, 16) } +} +// ---- +// step: switchSplitter +// +// { +// let _1 := 0 +// let sel := calldataload(_1) +// switch gt(sel, 8) +// case 1 { +// switch gt(sel, 12) +// case 1 { +// switch sel +// case 13 { +// let _26 := 13 +// let _27 := 0 +// sstore(_27, _26) +// } +// case 14 { +// let _28 := 14 +// let _29 := 0 +// sstore(_29, _28) +// } +// case 15 { +// let _30 := 15 +// let _31 := 0 +// sstore(_31, _30) +// } +// case 16 { +// let _32 := 16 +// let _33 := 0 +// sstore(_33, _32) +// } +// } +// default { +// switch sel +// case 9 { +// let _18 := 9 +// let _19 := 0 +// sstore(_19, _18) +// } +// case 10 { +// let _20 := 10 +// let _21 := 0 +// sstore(_21, _20) +// } +// case 11 { +// let _22 := 11 +// let _23 := 0 +// sstore(_23, _22) +// } +// case 12 { +// let _24 := 12 +// let _25 := 0 +// sstore(_25, _24) +// } +// } +// } +// default { +// switch gt(sel, 4) +// case 1 { +// switch sel +// case 5 { +// let _10 := 5 +// let _11 := 0 +// sstore(_11, _10) +// } +// case 6 { +// let _12 := 6 +// let _13 := 0 +// sstore(_13, _12) +// } +// case 7 { +// let _14 := 7 +// let _15 := 0 +// sstore(_15, _14) +// } +// case 8 { +// let _16 := 8 +// let _17 := 0 +// sstore(_17, _16) +// } +// } +// default { +// switch sel +// case 1 { +// let _2 := 1 +// let _3 := 0 +// sstore(_3, _2) +// } +// case 2 { +// let _4 := 2 +// let _5 := 0 +// sstore(_5, _4) +// } +// case 3 { +// let _6 := 3 +// let _7 := 0 +// sstore(_7, _6) +// } +// case 4 { +// let _8 := 4 +// let _9 := 0 +// sstore(_9, _8) +// } +// } +// } +// } diff --git a/test/libyul/yulOptimizerTests/switchSplitter/split_with_default.yul b/test/libyul/yulOptimizerTests/switchSplitter/split_with_default.yul new file mode 100644 index 000000000000..41da2e16def4 --- /dev/null +++ b/test/libyul/yulOptimizerTests/switchSplitter/split_with_default.yul @@ -0,0 +1,83 @@ +{ + let sel := calldataload(0) + switch sel + case 1 { sstore(0, 1) } + case 2 { sstore(0, 2) } + case 3 { sstore(0, 3) } + case 4 { sstore(0, 4) } + case 5 { sstore(0, 5) } + case 6 { sstore(0, 6) } + case 7 { sstore(0, 7) } + case 8 { sstore(0, 8) } + case 9 { sstore(0, 9) } + default { sstore(0, 99) } +} +// ---- +// step: switchSplitter +// +// { +// let _1 := 0 +// let sel := calldataload(_1) +// switch gt(sel, 5) +// case 1 { +// switch sel +// case 6 { +// let _12 := 6 +// let _13 := 0 +// sstore(_13, _12) +// } +// case 7 { +// let _14 := 7 +// let _15 := 0 +// sstore(_15, _14) +// } +// case 8 { +// let _16 := 8 +// let _17 := 0 +// sstore(_17, _16) +// } +// case 9 { +// let _18 := 9 +// let _19 := 0 +// sstore(_19, _18) +// } +// default { +// let _20 := 99 +// let _21 := 0 +// sstore(_21, _20) +// } +// } +// default { +// switch sel +// case 1 { +// let _2 := 1 +// let _3 := 0 +// sstore(_3, _2) +// } +// case 2 { +// let _4 := 2 +// let _5 := 0 +// sstore(_5, _4) +// } +// case 3 { +// let _6 := 3 +// let _7 := 0 +// sstore(_7, _6) +// } +// case 4 { +// let _8 := 4 +// let _9 := 0 +// sstore(_9, _8) +// } +// case 5 { +// let _10 := 5 +// let _11 := 0 +// sstore(_11, _10) +// } +// default { +// let _20 := 99 +// let _21 := 0 +// sstore(_21, _20) +// } +// } +// } From fa6b156d1bfa78b48f540bfc92d70084be198421 Mon Sep 17 00:00:00 2001 From: William Morriss Date: Wed, 1 Jul 2026 21:34:47 -0500 Subject: [PATCH 02/10] test(yulPhaser): update abbreviation string for SwitchSplitter step Assisted-by: Claude:claude-sonnet-4-6 --- test/yulPhaser/Chromosome.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test/yulPhaser/Chromosome.cpp b/test/yulPhaser/Chromosome.cpp index 5503de1bfe34..e40b6cf8c03b 100644 --- a/test/yulPhaser/Chromosome.cpp +++ b/test/yulPhaser/Chromosome.cpp @@ -137,7 +137,7 @@ BOOST_AUTO_TEST_CASE(output_operator_should_create_concise_and_unambiguous_strin BOOST_TEST(chromosome.length() == allSteps.size()); BOOST_TEST(chromosome.optimisationSteps() == allSteps); - BOOST_TEST(toString(chromosome) == "flcCUnDEvejsxIOoighFTLMmVatrpuSd"); + BOOST_TEST(toString(chromosome) == "flcCUnDEvejsxIOoighFTLMmVatBrpuSd"); } BOOST_AUTO_TEST_CASE(optimisationSteps_should_translate_chromosomes_genes_to_optimisation_step_names) From 899d1e955ffd289831519617bc1af125e537def7 Mon Sep 17 00:00:00 2001 From: William Morriss Date: Thu, 2 Jul 2026 01:42:52 -0500 Subject: [PATCH 03/10] test: regenerate gas expectations for SwitchSplitter Assisted-by: Claude:claude-sonnet-4-6 --- .../abicoder/abi_encode_calldata_slice.sol | 8 +-- .../function_type_array_to_storage.sol | 8 +-- .../array/dynamic_arrays_in_storage.sol | 4 +- ...nvalid_encoding_for_storage_byte_array.sol | 8 +-- ...rray_cleanup_after_overwrite_with_long.sol | 4 +- .../externalContracts/FixedFeeRegistrar.sol | 10 +-- .../externalContracts/prbmath_signed.sol | 28 ++++---- .../externalContracts/prbmath_unsigned.sol | 28 ++++---- .../storage/static_array_copy_cleanup.sol | 8 +-- ...rage_boundary_struct_array_mixed_types.sol | 68 +++++++++---------- ...torage_boundary_struct_array_multislot.sol | 20 +++--- .../storage_boundary_struct_array_packed.sol | 12 ++-- .../multiple_inheritance.sol | 4 +- .../state_variable_dynamic_array.sol | 8 +-- .../userDefinedValueType/erc20.sol | 20 +++--- .../semanticTests/various/erc20.sol | 20 +++--- .../various/selfdestruct_post_cancun.sol | 8 +-- .../selfdestruct_post_cancun_redeploy.sol | 16 ++--- 18 files changed, 141 insertions(+), 141 deletions(-) diff --git a/test/libsolidity/semanticTests/abicoder/abi_encode_calldata_slice.sol b/test/libsolidity/semanticTests/abicoder/abi_encode_calldata_slice.sol index 5f492debc859..053cbc18dd2c 100644 --- a/test/libsolidity/semanticTests/abicoder/abi_encode_calldata_slice.sol +++ b/test/libsolidity/semanticTests/abicoder/abi_encode_calldata_slice.sol @@ -59,12 +59,12 @@ contract C { // EVMVersion: >homestead // ---- // test_bytes() -> -// gas irOptimized: 314884 +// gas irOptimized: 314018 // gas legacy: 305816 // gas legacyOptimized: 253573 -// gas ssaCFGOptimized: 311064 +// gas ssaCFGOptimized: 310077 // test_uint256() -> -// gas irOptimized: 448346 +// gas irOptimized: 447689 // gas legacy: 421304 // gas legacyOptimized: 351544 -// gas ssaCFGOptimized: 440615 +// gas ssaCFGOptimized: 439773 diff --git a/test/libsolidity/semanticTests/array/copying/function_type_array_to_storage.sol b/test/libsolidity/semanticTests/array/copying/function_type_array_to_storage.sol index 5545c30e32ff..dd7d683e0dd9 100644 --- a/test/libsolidity/semanticTests/array/copying/function_type_array_to_storage.sol +++ b/test/libsolidity/semanticTests/array/copying/function_type_array_to_storage.sol @@ -46,13 +46,13 @@ contract C { } // ---- // test() -> 0x20, 0x14, "[a called][b called]" -// gas irOptimized: 116512 +// gas irOptimized: 116532 // gas legacy: 120226 // gas legacyOptimized: 116941 -// gas ssaCFGOptimized: 116470 +// gas ssaCFGOptimized: 116516 // test2() -> 0x20, 0x14, "[b called][a called]" // test3() -> 0x20, 0x14, "[b called][a called]" -// gas irOptimized: 103138 +// gas irOptimized: 103242 // gas legacy: 105192 // gas legacyOptimized: 103752 -// gas ssaCFGOptimized: 103069 +// gas ssaCFGOptimized: 103258 diff --git a/test/libsolidity/semanticTests/array/dynamic_arrays_in_storage.sol b/test/libsolidity/semanticTests/array/dynamic_arrays_in_storage.sol index f66e7fa46dcf..787eee693f0c 100644 --- a/test/libsolidity/semanticTests/array/dynamic_arrays_in_storage.sol +++ b/test/libsolidity/semanticTests/array/dynamic_arrays_in_storage.sol @@ -41,10 +41,10 @@ contract c { // ---- // getLengths() -> 0, 0 // setLengths(uint256,uint256): 48, 49 -> -// gas irOptimized: 112674 +// gas irOptimized: 112710 // gas legacy: 108272 // gas legacyOptimized: 100268 -// gas ssaCFGOptimized: 112668 +// gas ssaCFGOptimized: 112699 // getLengths() -> 48, 49 // setIDStatic(uint256): 11 -> // getID(uint256): 2 -> 11 diff --git a/test/libsolidity/semanticTests/array/invalid_encoding_for_storage_byte_array.sol b/test/libsolidity/semanticTests/array/invalid_encoding_for_storage_byte_array.sol index 1454e3ebe507..a23caf51807e 100644 --- a/test/libsolidity/semanticTests/array/invalid_encoding_for_storage_byte_array.sol +++ b/test/libsolidity/semanticTests/array/invalid_encoding_for_storage_byte_array.sol @@ -40,19 +40,19 @@ contract C { // copyFromStorageShort() // x() -> 0x20, 3, 0x6162630000000000000000000000000000000000000000000000000000000000 // copyFromStorageLong() -// gas irOptimized: 121092 +// gas irOptimized: 120863 // gas legacy: 121904 // gas legacyOptimized: 121393 -// gas ssaCFGOptimized: 121077 +// gas ssaCFGOptimized: 120849 // x() -> 0x20, 0x25, 0x3132333435363738393031323334353637383930313233343536373839303132, 0x3334353637000000000000000000000000000000000000000000000000000000 // copyToStorage() // x() -> 0x20, 0x25, 0x3132333435363738393031323334353637383930313233343536373839303132, 0x3334353637000000000000000000000000000000000000000000000000000000 // y() -> 0x20, 0x25, 0x3132333435363738393031323334353637383930313233343536373839303132, 0x3334353637000000000000000000000000000000000000000000000000000000 // del() -// gas irOptimized: 29404 +// gas irOptimized: 29292 // x() -> 0x20, 0x00 // invalidateXLong() -// gas irOptimized: 47028 +// gas irOptimized: 46985 // x() -> FAILURE, hex"4e487b71", 0x22 // abiEncode() -> FAILURE, hex"4e487b71", 0x22 // abiEncodePacked() -> FAILURE, hex"4e487b71", 0x22 diff --git a/test/libsolidity/semanticTests/array/long_byte_array_cleanup_after_overwrite_with_long.sol b/test/libsolidity/semanticTests/array/long_byte_array_cleanup_after_overwrite_with_long.sol index ca46ecc7e0bf..a03d10075487 100644 --- a/test/libsolidity/semanticTests/array/long_byte_array_cleanup_after_overwrite_with_long.sol +++ b/test/libsolidity/semanticTests/array/long_byte_array_cleanup_after_overwrite_with_long.sol @@ -88,10 +88,10 @@ contract C { // arrayLength() -> 0 // canaryValue() -> 0xffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff // fillArray() -// gas irOptimized: 197352 +// gas irOptimized: 197292 // gas legacy: 220574 // gas legacyOptimized: 206839 -// gas ssaCFGOptimized: 197540 +// gas ssaCFGOptimized: 197481 // arrayLength() -> 96 // canaryValue() -> 0xffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff // getArrayBytes(uint256,uint256): 0, 5 -> 0x20, 5, 0x0102030405000000000000000000000000000000000000000000000000000000 diff --git a/test/libsolidity/semanticTests/externalContracts/FixedFeeRegistrar.sol b/test/libsolidity/semanticTests/externalContracts/FixedFeeRegistrar.sol index 422373e37cdd..b6764e3ea00f 100644 --- a/test/libsolidity/semanticTests/externalContracts/FixedFeeRegistrar.sol +++ b/test/libsolidity/semanticTests/externalContracts/FixedFeeRegistrar.sol @@ -74,17 +74,17 @@ contract FixedFeeRegistrar is Registrar { } // ---- // constructor() -// gas irOptimized: 78076 -// gas irOptimized code: 307400 +// gas irOptimized: 77804 +// gas irOptimized code: 309400 // gas legacy: 115395 // gas legacy code: 792400 // gas legacyOptimized: 84598 // gas legacyOptimized code: 388000 -// gas ssaCFGOptimized: 78844 -// gas ssaCFGOptimized code: 321800 +// gas ssaCFGOptimized: 79092 +// gas ssaCFGOptimized code: 325200 // reserve(string), 69 ether: 0x20, 3, "abc" -> // ~ emit Changed(string): #0x4e03657aea45a94fc7d47ba826c8d667c0d1e6e33a64a036ec44f58fa12d6c45 -// gas irOptimized: 45741 +// gas irOptimized: 45769 // gas legacy: 46698 // gas legacyOptimized: 45948 // owner(string): 0x20, 3, "abc" -> 0x1212121212121212121212121212120000000012 diff --git a/test/libsolidity/semanticTests/externalContracts/prbmath_signed.sol b/test/libsolidity/semanticTests/externalContracts/prbmath_signed.sol index b94076ac543e..43642da452ea 100644 --- a/test/libsolidity/semanticTests/externalContracts/prbmath_signed.sol +++ b/test/libsolidity/semanticTests/externalContracts/prbmath_signed.sol @@ -48,51 +48,51 @@ contract test { } // ---- // constructor() -// gas irOptimized: 177903 -// gas irOptimized code: 1674400 +// gas irOptimized: 185887 +// gas irOptimized code: 1784400 // gas legacy: 209723 // gas legacy code: 2205000 // gas legacyOptimized: 178012 // gas legacyOptimized code: 1669600 -// gas ssaCFGOptimized: 175028 -// gas ssaCFGOptimized code: 1639200 +// gas ssaCFGOptimized: 183035 +// gas ssaCFGOptimized code: 1748600 // div(int256,int256): 3141592653589793238, 88714123 -> 35412542528203691288251815328 -// gas irOptimized: 22045 +// gas irOptimized: 22073 // gas legacy: 22736 // gas legacyOptimized: 22264 // exp(int256): 3141592653589793238 -> 23140692632779268978 -// gas irOptimized: 24452 +// gas irOptimized: 24370 // gas legacy: 25124 // gas legacyOptimized: 24351 // exp2(int256): 3141592653589793238 -> 8824977827076287620 -// gas irOptimized: 24162 +// gas irOptimized: 24081 // gas legacy: 24787 // gas legacyOptimized: 24105 // gm(int256,int256): 3141592653589793238, 88714123 -> 16694419339601 -// gas irOptimized: 22805 +// gas irOptimized: 22724 // gas legacy: 23202 // gas legacyOptimized: 22685 // log10(int256): 3141592653589793238 -> 4971498726941338506 -// gas irOptimized: 30300 +// gas irOptimized: 28859 // gas legacy: 32841 // gas legacyOptimized: 30249 // log2(int256): 3141592653589793238 -> 1651496129472318782 -// gas irOptimized: 28566 +// gas irOptimized: 28485 // gas legacy: 30979 // gas legacyOptimized: 28357 // mul(int256,int256): 3141592653589793238, 88714123 -> 278703637 -// gas irOptimized: 22147 +// gas irOptimized: 22066 // gas legacy: 22775 // gas legacyOptimized: 22288 // pow(int256,uint256): 3141592653589793238, 5 -> 306019684785281453040 -// gas irOptimized: 22488 +// gas irOptimized: 22516 // gas legacy: 23453 // gas legacyOptimized: 22921 // sqrt(int256): 3141592653589793238 -> 1772453850905516027 -// gas irOptimized: 22458 +// gas irOptimized: 22486 // gas legacy: 22784 // gas legacyOptimized: 22420 // benchmark(int256): 3141592653589793238 -> 998882724338592125, 1000000000000000000, 1000000000000000000 -// gas irOptimized: 34893 +// gas irOptimized: 34921 // gas legacy: 35244 // gas legacyOptimized: 33996 diff --git a/test/libsolidity/semanticTests/externalContracts/prbmath_unsigned.sol b/test/libsolidity/semanticTests/externalContracts/prbmath_unsigned.sol index d9f5d75fea9d..26f28e124e2a 100644 --- a/test/libsolidity/semanticTests/externalContracts/prbmath_unsigned.sol +++ b/test/libsolidity/semanticTests/externalContracts/prbmath_unsigned.sol @@ -48,51 +48,51 @@ contract test { } // ---- // constructor() -// gas irOptimized: 170626 -// gas irOptimized code: 1577400 +// gas irOptimized: 177791 +// gas irOptimized code: 1671200 // gas legacy: 195206 // gas legacy code: 1999000 // gas legacyOptimized: 168857 // gas legacyOptimized code: 1556200 -// gas ssaCFGOptimized: 167591 -// gas ssaCFGOptimized code: 1544400 +// gas ssaCFGOptimized: 175312 +// gas ssaCFGOptimized code: 1641400 // div(uint256,uint256): 3141592653589793238, 88714123 -> 35412542528203691288251815328 -// gas irOptimized: 21912 +// gas irOptimized: 21831 // gas legacy: 22475 // gas legacyOptimized: 21998 // exp(uint256): 3141592653589793238 -> 23140692632779268978 -// gas irOptimized: 24334 +// gas irOptimized: 24261 // gas legacy: 25026 // gas legacyOptimized: 24253 // exp2(uint256): 3141592653589793238 -> 8824977827076287620 -// gas irOptimized: 24115 +// gas irOptimized: 24042 // gas legacy: 24738 // gas legacyOptimized: 24058 // gm(uint256,uint256): 3141592653589793238, 88714123 -> 16694419339601 -// gas irOptimized: 22786 +// gas irOptimized: 22819 // gas legacy: 23244 // gas legacyOptimized: 22727 // log10(uint256): 3141592653589793238 -> 0x44fe4fc084a52b8a -// gas irOptimized: 30079 +// gas irOptimized: 28518 // gas legacy: 32808 // gas legacyOptimized: 29912 // log2(uint256): 3141592653589793238 -> 1651496129472318782 -// gas irOptimized: 28107 +// gas irOptimized: 28143 // gas legacy: 30900 // gas legacyOptimized: 27994 // mul(uint256,uint256): 3141592653589793238, 88714123 -> 278703637 -// gas irOptimized: 21988 +// gas irOptimized: 21915 // gas legacy: 22581 // gas legacyOptimized: 22089 // pow(uint256,uint256): 3141592653589793238, 5 -> 306019684785281453040 -// gas irOptimized: 22316 +// gas irOptimized: 22352 // gas legacy: 23196 // gas legacyOptimized: 22652 // sqrt(uint256): 3141592653589793238 -> 1772453850905516027 -// gas irOptimized: 22481 +// gas irOptimized: 22517 // gas legacy: 22803 // gas legacyOptimized: 22439 // benchmark(uint256): 3141592653589793238 -> 998882724338592125, 1000000000000000000, 1000000000000000000 -// gas irOptimized: 33961 +// gas irOptimized: 33997 // gas legacy: 34006 // gas legacyOptimized: 32724 diff --git a/test/libsolidity/semanticTests/storage/static_array_copy_cleanup.sol b/test/libsolidity/semanticTests/storage/static_array_copy_cleanup.sol index c75fb7ccba22..3f9972ac30fb 100644 --- a/test/libsolidity/semanticTests/storage/static_array_copy_cleanup.sol +++ b/test/libsolidity/semanticTests/storage/static_array_copy_cleanup.sol @@ -68,17 +68,17 @@ contract C { // getSourceAsUint() -> 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 // getDestAsUint() -> 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 // fillSource() -// gas irOptimized: 135018 +// gas irOptimized: 134959 // gas legacy: 146851 // gas legacyOptimized: 137549 -// gas ssaCFGOptimized: 137978 +// gas ssaCFGOptimized: 137919 // canary() -> 0xffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff // getSourceAsUint() -> 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20 // fillDest() -// gas irOptimized: 248706 +// gas irOptimized: 248731 // gas legacy: 272468 // gas legacyOptimized: 253871 -// gas ssaCFGOptimized: 254266 +// gas ssaCFGOptimized: 254291 // canary() -> 0xffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff // getSourceAsUint() -> 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20 // getDestAsUint() -> 100, 101, 102, 103, 104, 105, 106, 107, 108, 109, 110, 111, 112, 113, 114, 115, 116, 117, 118, 119, 120, 121, 122, 123, 124, 125, 126, 127, 128, 129, 130, 131, 132, 133, 134, 135, 136, 137, 138, 139 diff --git a/test/libsolidity/semanticTests/storage/storage_boundary_struct_array_mixed_types.sol b/test/libsolidity/semanticTests/storage/storage_boundary_struct_array_mixed_types.sol index 2aed15fbe473..89d78d6889ca 100644 --- a/test/libsolidity/semanticTests/storage/storage_boundary_struct_array_mixed_types.sol +++ b/test/libsolidity/semanticTests/storage/storage_boundary_struct_array_mixed_types.sol @@ -98,92 +98,92 @@ contract C { // ---- // canaryValue() -> 0xffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff // boundaryArray() -> 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 -// gas irOptimized: 113169 +// gas irOptimized: 113102 // gas legacy: 120742 // gas legacyOptimized: 112518 -// gas ssaCFGOptimized: 113202 +// gas ssaCFGOptimized: 113140 // destArray() -> 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 -// gas irOptimized: 113078 +// gas irOptimized: 113098 // gas legacy: 120738 // gas legacyOptimized: 112505 -// gas ssaCFGOptimized: 113111 +// gas ssaCFGOptimized: 113136 // fillBoundaryArray() -// gas irOptimized: 912522 +// gas irOptimized: 912455 // gas legacy: 930728 // gas legacyOptimized: 916628 -// gas ssaCFGOptimized: 915317 +// gas ssaCFGOptimized: 915255 // canaryValue() -> 0xffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff // boundaryArray() -> 1, 2, 3, 4, true, 6, 7, 8, 9, true, 11, 12, 13, 14, true, 16, 17, 18, 19, true, 21, 22, 23, 24, true, 26, 27, 28, 29, true, 31, 32, 33, 34, true, 36, 37, 38, 39, true, 41, 42, 43, 44, true, 46, 47, 48, 49, true -// gas irOptimized: 113169 +// gas irOptimized: 113102 // gas legacy: 120742 // gas legacyOptimized: 112518 -// gas ssaCFGOptimized: 113202 +// gas ssaCFGOptimized: 113140 // destArray() -> 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 -// gas irOptimized: 113078 +// gas irOptimized: 113098 // gas legacy: 120738 // gas legacyOptimized: 112505 -// gas ssaCFGOptimized: 113111 +// gas ssaCFGOptimized: 113136 // copyFromBoundary() -// gas irOptimized: 994579 +// gas irOptimized: 994599 // gas legacy: 1023407 // gas legacyOptimized: 994746 -// gas ssaCFGOptimized: 996228 +// gas ssaCFGOptimized: 996253 // canaryValue() -> 0xffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff // boundaryArray() -> 1, 2, 3, 4, true, 6, 7, 8, 9, true, 11, 12, 13, 14, true, 16, 17, 18, 19, true, 21, 22, 23, 24, true, 26, 27, 28, 29, true, 31, 32, 33, 34, true, 36, 37, 38, 39, true, 41, 42, 43, 44, true, 46, 47, 48, 49, true -// gas irOptimized: 113169 +// gas irOptimized: 113102 // gas legacy: 120742 // gas legacyOptimized: 112518 -// gas ssaCFGOptimized: 113202 +// gas ssaCFGOptimized: 113140 // destArray() -> 1, 2, 3, 4, true, 6, 7, 8, 9, true, 11, 12, 13, 14, true, 16, 17, 18, 19, true, 21, 22, 23, 24, true, 26, 27, 28, 29, true, 31, 32, 33, 34, true, 36, 37, 38, 39, true, 41, 42, 43, 44, true, 46, 47, 48, 49, true -// gas irOptimized: 113078 +// gas irOptimized: 113098 // gas legacy: 120738 // gas legacyOptimized: 112505 -// gas ssaCFGOptimized: 113111 +// gas ssaCFGOptimized: 113136 // fillDestArray() -// gas irOptimized: 200426 +// gas irOptimized: 200443 // gas legacy: 218746 // gas legacyOptimized: 204648 -// gas ssaCFGOptimized: 203221 +// gas ssaCFGOptimized: 203193 // canaryValue() -> 0xffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff // boundaryArray() -> 1, 2, 3, 4, true, 6, 7, 8, 9, true, 11, 12, 13, 14, true, 16, 17, 18, 19, true, 21, 22, 23, 24, true, 26, 27, 28, 29, true, 31, 32, 33, 34, true, 36, 37, 38, 39, true, 41, 42, 43, 44, true, 46, 47, 48, 49, true -// gas irOptimized: 113169 +// gas irOptimized: 113102 // gas legacy: 120742 // gas legacyOptimized: 112518 -// gas ssaCFGOptimized: 113202 +// gas ssaCFGOptimized: 113140 // destArray() -> 51, 52, 53, 54, true, 56, 57, 58, 59, true, 61, 62, 63, 64, true, 66, 67, 68, 69, true, 71, 72, 73, 74, true, 76, 77, 78, 79, true, 81, 82, 83, 84, true, 86, 87, 88, 89, true, 91, 92, 93, 94, true, 96, 97, 98, 99, true -// gas irOptimized: 113078 +// gas irOptimized: 113098 // gas legacy: 120738 // gas legacyOptimized: 112505 -// gas ssaCFGOptimized: 113111 +// gas ssaCFGOptimized: 113136 // copyToBoundary() -// gas irOptimized: 282623 +// gas irOptimized: 282556 // gas legacy: 311362 // gas legacyOptimized: 282712 -// gas ssaCFGOptimized: 284272 +// gas ssaCFGOptimized: 284210 // canaryValue() -> 0xffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff // boundaryArray() -> 51, 52, 53, 54, true, 56, 57, 58, 59, true, 61, 62, 63, 64, true, 66, 67, 68, 69, true, 71, 72, 73, 74, true, 76, 77, 78, 79, true, 81, 82, 83, 84, true, 86, 87, 88, 89, true, 91, 92, 93, 94, true, 96, 97, 98, 99, true -// gas irOptimized: 113169 +// gas irOptimized: 113102 // gas legacy: 120742 // gas legacyOptimized: 112518 -// gas ssaCFGOptimized: 113202 +// gas ssaCFGOptimized: 113140 // destArray() -> 51, 52, 53, 54, true, 56, 57, 58, 59, true, 61, 62, 63, 64, true, 66, 67, 68, 69, true, 71, 72, 73, 74, true, 76, 77, 78, 79, true, 81, 82, 83, 84, true, 86, 87, 88, 89, true, 91, 92, 93, 94, true, 96, 97, 98, 99, true -// gas irOptimized: 113078 +// gas irOptimized: 113098 // gas legacy: 120738 // gas legacyOptimized: 112505 -// gas ssaCFGOptimized: 113111 +// gas ssaCFGOptimized: 113136 // deleteBoundaryArray() -// gas irOptimized: 177968 +// gas irOptimized: 177913 // gas legacy: 180995 // gas legacyOptimized: 178182 -// gas ssaCFGOptimized: 177963 +// gas ssaCFGOptimized: 177913 // canaryValue() -> 0xffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff // boundaryArray() -> 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 -// gas irOptimized: 113169 +// gas irOptimized: 113102 // gas legacy: 120742 // gas legacyOptimized: 112518 -// gas ssaCFGOptimized: 113202 +// gas ssaCFGOptimized: 113140 // destArray() -> 51, 52, 53, 54, true, 56, 57, 58, 59, true, 61, 62, 63, 64, true, 66, 67, 68, 69, true, 71, 72, 73, 74, true, 76, 77, 78, 79, true, 81, 82, 83, 84, true, 86, 87, 88, 89, true, 91, 92, 93, 94, true, 96, 97, 98, 99, true -// gas irOptimized: 113078 +// gas irOptimized: 113098 // gas legacy: 120738 // gas legacyOptimized: 112505 -// gas ssaCFGOptimized: 113111 +// gas ssaCFGOptimized: 113136 diff --git a/test/libsolidity/semanticTests/storage/storage_boundary_struct_array_multislot.sol b/test/libsolidity/semanticTests/storage/storage_boundary_struct_array_multislot.sol index ce4a30c1e799..9550a90a551d 100644 --- a/test/libsolidity/semanticTests/storage/storage_boundary_struct_array_multislot.sol +++ b/test/libsolidity/semanticTests/storage/storage_boundary_struct_array_multislot.sol @@ -92,42 +92,42 @@ contract C { // boundaryArray() -> 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 // destArray() -> 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 // fillBoundaryArray() -// gas irOptimized: 688719 +// gas irOptimized: 688652 // gas legacy: 700075 // gas legacyOptimized: 691605 -// gas ssaCFGOptimized: 688794 +// gas ssaCFGOptimized: 688732 // canaryValue() -> 0xffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff // boundaryArray() -> 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30 // destArray() -> 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 // copyFromBoundary() -// gas irOptimized: 748779 +// gas irOptimized: 748799 // gas legacy: 767297 // gas legacyOptimized: 748756 -// gas ssaCFGOptimized: 748738 +// gas ssaCFGOptimized: 748763 // canaryValue() -> 0xffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff // boundaryArray() -> 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30 // destArray() -> 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30 // fillDestArray() -// gas irOptimized: 175623 +// gas irOptimized: 175640 // gas legacy: 187093 // gas legacyOptimized: 178625 -// gas ssaCFGOptimized: 175698 +// gas ssaCFGOptimized: 175580 // canaryValue() -> 0xffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff // boundaryArray() -> 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30 // destArray() -> 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, 52, 53, 54, 55, 56, 57, 58, 59, 60 // copyToBoundary() -// gas irOptimized: 235823 +// gas irOptimized: 235756 // gas legacy: 254252 // gas legacyOptimized: 235722 -// gas ssaCFGOptimized: 235782 +// gas ssaCFGOptimized: 235720 // canaryValue() -> 0xffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff // boundaryArray() -> 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, 52, 53, 54, 55, 56, 57, 58, 59, 60 // destArray() -> 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, 52, 53, 54, 55, 56, 57, 58, 59, 60 // deleteBoundaryArray() -// gas irOptimized: 137824 +// gas irOptimized: 137769 // gas legacy: 137971 // gas legacyOptimized: 137750 -// gas ssaCFGOptimized: 137819 +// gas ssaCFGOptimized: 137769 // canaryValue() -> 0xffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff // boundaryArray() -> 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 // destArray() -> 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, 52, 53, 54, 55, 56, 57, 58, 59, 60 diff --git a/test/libsolidity/semanticTests/storage/storage_boundary_struct_array_packed.sol b/test/libsolidity/semanticTests/storage/storage_boundary_struct_array_packed.sol index 13c4634fb1ad..0b364e5b129d 100644 --- a/test/libsolidity/semanticTests/storage/storage_boundary_struct_array_packed.sol +++ b/test/libsolidity/semanticTests/storage/storage_boundary_struct_array_packed.sol @@ -96,18 +96,18 @@ contract C { // boundaryArray() -> 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 // destArray() -> 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 // fillBoundaryArray() -// gas irOptimized: 248990 +// gas irOptimized: 248923 // gas legacy: 272856 // gas legacyOptimized: 253856 -// gas ssaCFGOptimized: 254545 +// gas ssaCFGOptimized: 254483 // canaryValue() -> 0xffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff // boundaryArray() -> 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40 // destArray() -> 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 // copyFromBoundary() -// gas irOptimized: 274279 +// gas irOptimized: 274299 // gas legacy: 298927 // gas legacyOptimized: 272256 -// gas ssaCFGOptimized: 275618 +// gas ssaCFGOptimized: 275643 // canaryValue() -> 0xffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff // boundaryArray() -> 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40 // destArray() -> 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40 @@ -118,10 +118,10 @@ contract C { // destArray() -> 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, 62, 63, 64, 65, 66, 67, 68, 69, 70, 71, 72, 73, 74, 75, 76, 77, 78, 79, 80 // gas legacy: 59729 // copyToBoundary() -// gas irOptimized: 103323 +// gas irOptimized: 103256 // gas legacy: 127882 // gas legacyOptimized: 101222 -// gas ssaCFGOptimized: 104662 +// gas ssaCFGOptimized: 104600 // canaryValue() -> 0xffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff // boundaryArray() -> 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, 62, 63, 64, 65, 66, 67, 68, 69, 70, 71, 72, 73, 74, 75, 76, 77, 78, 79, 80 // destArray() -> 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, 62, 63, 64, 65, 66, 67, 68, 69, 70, 71, 72, 73, 74, 75, 76, 77, 78, 79, 80 diff --git a/test/libsolidity/semanticTests/storageLayoutSpecifier/multiple_inheritance.sol b/test/libsolidity/semanticTests/storageLayoutSpecifier/multiple_inheritance.sol index 168c67b6de02..8dfa48c0d469 100644 --- a/test/libsolidity/semanticTests/storageLayoutSpecifier/multiple_inheritance.sol +++ b/test/libsolidity/semanticTests/storageLayoutSpecifier/multiple_inheritance.sol @@ -31,10 +31,10 @@ contract D is A, B, C layout at 42 { } // ---- // test() -> 1, 2, 3, 5 -// gas irOptimized: 110112 +// gas irOptimized: 110052 // gas legacy: 111881 // gas legacyOptimized: 110945 -// gas ssaCFGOptimized: 110209 +// gas ssaCFGOptimized: 110150 // x() -> 1 // y() -> 2 // w() -> 3 diff --git a/test/libsolidity/semanticTests/storageLayoutSpecifier/state_variable_dynamic_array.sol b/test/libsolidity/semanticTests/storageLayoutSpecifier/state_variable_dynamic_array.sol index 4127e47934fc..2ba4333d39b3 100644 --- a/test/libsolidity/semanticTests/storageLayoutSpecifier/state_variable_dynamic_array.sol +++ b/test/libsolidity/semanticTests/storageLayoutSpecifier/state_variable_dynamic_array.sol @@ -34,18 +34,18 @@ contract C is A layout at 42 { } // ---- // initA() -> 1, 2, 3 -// gas irOptimized: 111986 +// gas irOptimized: 111926 // gas legacy: 111679 // gas legacyOptimized: 111150 -// gas ssaCFGOptimized: 111949 +// gas ssaCFGOptimized: 111890 // arrayA(uint256): 0 -> 1 // arrayALength() -> 3 // arrayCLength() -> 0 // initCFromAInReverse() -> 3, 2, 1 -// gas irOptimized: 121281 +// gas irOptimized: 121309 // gas legacy: 121937 // gas legacyOptimized: 120853 -// gas ssaCFGOptimized: 121205 +// gas ssaCFGOptimized: 121233 // clearA() -> // arrayC(uint256): 0 -> 3 // arrayALength() -> 0 diff --git a/test/libsolidity/semanticTests/userDefinedValueType/erc20.sol b/test/libsolidity/semanticTests/userDefinedValueType/erc20.sol index 23cb720ade00..bad614d35fe7 100644 --- a/test/libsolidity/semanticTests/userDefinedValueType/erc20.sol +++ b/test/libsolidity/semanticTests/userDefinedValueType/erc20.sol @@ -113,38 +113,38 @@ contract ERC20 { // ---- // constructor() // ~ emit Transfer(address,address,uint256): #0x00, #0x1212121212121212121212121212120000000012, 0x14 -// gas irOptimized: 121322 -// gas irOptimized code: 234600 +// gas irOptimized: 121410 +// gas irOptimized code: 236600 // gas legacy: 163350 // gas legacy code: 671400 // gas legacyOptimized: 127464 // gas legacyOptimized code: 285400 -// gas ssaCFGOptimized: 121536 -// gas ssaCFGOptimized code: 237200 +// gas ssaCFGOptimized: 121688 +// gas ssaCFGOptimized code: 240000 // totalSupply() -> 20 -// gas irOptimized: 23334 +// gas irOptimized: 23370 // gas legacy: 23645 // gas legacyOptimized: 23367 // transfer(address,uint256): 2, 5 -> true // ~ emit Transfer(address,address,uint256): #0x1212121212121212121212121212120000000012, #0x02, 0x05 -// gas irOptimized: 50997 +// gas irOptimized: 50946 // gas legacy: 52323 // gas legacyOptimized: 51380 // decreaseAllowance(address,uint256): 2, 0 -> true // ~ emit Approval(address,address,uint256): #0x1212121212121212121212121212120000000012, #0x02, 0x00 -// gas irOptimized: 26054 +// gas irOptimized: 26003 // gas legacy: 27165 // gas legacyOptimized: 26320 // decreaseAllowance(address,uint256): 2, 1 -> FAILURE, hex"4e487b71", 0x11 -// gas irOptimized: 23954 +// gas irOptimized: 23903 // gas legacy: 24500 // gas legacyOptimized: 24076 // transfer(address,uint256): 2, 14 -> true // ~ emit Transfer(address,address,uint256): #0x1212121212121212121212121212120000000012, #0x02, 0x0e -// gas irOptimized: 33897 +// gas irOptimized: 33846 // gas legacy: 35223 // gas legacyOptimized: 34280 // transfer(address,uint256): 2, 2 -> FAILURE, hex"4e487b71", 0x11 -// gas irOptimized: 23990 +// gas irOptimized: 23939 // gas legacy: 24483 // gas legacyOptimized: 24072 diff --git a/test/libsolidity/semanticTests/various/erc20.sol b/test/libsolidity/semanticTests/various/erc20.sol index e68abd8864e6..563c25c22b0e 100644 --- a/test/libsolidity/semanticTests/various/erc20.sol +++ b/test/libsolidity/semanticTests/various/erc20.sol @@ -96,38 +96,38 @@ contract ERC20 { // ---- // constructor() // ~ emit Transfer(address,address,uint256): #0x00, #0x1212121212121212121212121212120000000012, 0x14 -// gas irOptimized: 121632 -// gas irOptimized code: 236800 +// gas irOptimized: 121762 +// gas irOptimized code: 238800 // gas legacy: 159957 // gas legacy code: 647600 // gas legacyOptimized: 126934 // gas legacyOptimized code: 282000 -// gas ssaCFGOptimized: 122072 -// gas ssaCFGOptimized code: 241600 +// gas ssaCFGOptimized: 122266 +// gas ssaCFGOptimized code: 244400 // totalSupply() -> 20 -// gas irOptimized: 23334 +// gas irOptimized: 23370 // gas legacy: 23519 // gas legacyOptimized: 23367 // transfer(address,uint256): 2, 5 -> true // ~ emit Transfer(address,address,uint256): #0x1212121212121212121212121212120000000012, #0x02, 0x05 -// gas irOptimized: 51054 +// gas irOptimized: 51003 // gas legacy: 52073 // gas legacyOptimized: 51298 // decreaseAllowance(address,uint256): 2, 0 -> true // ~ emit Approval(address,address,uint256): #0x1212121212121212121212121212120000000012, #0x02, 0x00 -// gas irOptimized: 26087 +// gas irOptimized: 26036 // gas legacy: 26977 // gas legacyOptimized: 26279 // decreaseAllowance(address,uint256): 2, 1 -> FAILURE, hex"4e487b71", 0x11 -// gas irOptimized: 23981 +// gas irOptimized: 23930 // gas legacy: 24462 // gas legacyOptimized: 24056 // transfer(address,uint256): 2, 14 -> true // ~ emit Transfer(address,address,uint256): #0x1212121212121212121212121212120000000012, #0x02, 0x0e -// gas irOptimized: 33954 +// gas irOptimized: 33903 // gas legacy: 34973 // gas legacyOptimized: 34198 // transfer(address,uint256): 2, 2 -> FAILURE, hex"4e487b71", 0x11 -// gas irOptimized: 24020 +// gas irOptimized: 23969 // gas legacy: 24445 // gas legacyOptimized: 24052 diff --git a/test/libsolidity/semanticTests/various/selfdestruct_post_cancun.sol b/test/libsolidity/semanticTests/various/selfdestruct_post_cancun.sol index 62d4efa01130..4dda52b9a697 100644 --- a/test/libsolidity/semanticTests/various/selfdestruct_post_cancun.sol +++ b/test/libsolidity/semanticTests/various/selfdestruct_post_cancun.sol @@ -64,14 +64,14 @@ contract D { // EVMVersion: >=cancun // ---- // constructor(), 1 ether -> -// gas irOptimized: 67028 -// gas irOptimized code: 175400 +// gas irOptimized: 67200 +// gas irOptimized code: 177400 // gas legacy: 76227 // gas legacy code: 298200 // gas legacyOptimized: 66516 // gas legacyOptimized code: 168000 -// gas ssaCFGOptimized: 66800 -// gas ssaCFGOptimized code: 172400 +// gas ssaCFGOptimized: 67103 +// gas ssaCFGOptimized code: 175800 // exists() -> false // test_create_and_terminate() -> // exists() -> false diff --git a/test/libsolidity/semanticTests/various/selfdestruct_post_cancun_redeploy.sol b/test/libsolidity/semanticTests/various/selfdestruct_post_cancun_redeploy.sol index 091aa86f65b2..2b4cf78db95b 100644 --- a/test/libsolidity/semanticTests/various/selfdestruct_post_cancun_redeploy.sol +++ b/test/libsolidity/semanticTests/various/selfdestruct_post_cancun_redeploy.sol @@ -82,24 +82,24 @@ contract D { // EVMVersion: >=cancun // ---- // constructor(), 1 ether -> -// gas irOptimized: 132974 -// gas irOptimized code: 293800 +// gas irOptimized: 133122 +// gas irOptimized code: 295800 // gas legacy: 151236 // gas legacy code: 533800 // gas legacyOptimized: 131436 // gas legacyOptimized code: 276600 -// gas ssaCFGOptimized: 132039 -// gas ssaCFGOptimized code: 282000 +// gas ssaCFGOptimized: 132299 +// gas ssaCFGOptimized code: 285400 // exists() -> false // test_deploy_and_terminate() -> // ~ emit Deployed(address,bytes32) from 0x137aa4dfc0911524504fcd4d98501f179bc13b4a: 0x7e6580007e709ac52945fae182c61131d42634e8, 0x1234000000000000000000000000000000000000000000000000000000000000 -// gas irOptimized: 96528 +// gas irOptimized: 96469 // gas irOptimized code: 20800 // gas legacy: 97788 // gas legacy code: 20800 // gas legacyOptimized: 96043 // gas legacyOptimized code: 20800 -// gas ssaCFGOptimized: 96391 +// gas ssaCFGOptimized: 96332 // gas ssaCFGOptimized code: 20800 // exists() -> false // deploy_create2() -> @@ -110,7 +110,7 @@ contract D { // test_balance_after_selfdestruct() -> // exists() -> true // deploy_create2() -> FAILURE -// gas irOptimized: 96903654 +// gas irOptimized: 96903655 // gas legacy: 96903658 // gas legacyOptimized: 96903639 -// gas ssaCFGOptimized: 96903647 +// gas ssaCFGOptimized: 96903648 From 65b8cdbc9524d4c34fe8beb6268a9caaedda3ae9 Mon Sep 17 00:00:00 2001 From: William Morriss Date: Sat, 4 Jul 2026 20:50:30 -0500 Subject: [PATCH 04/10] fix(yul): size SwitchSplitter overhead to the fulcrum's actual byte width Assisted-by: Claude:claude-sonnet-4-6 --- libyul/optimiser/SwitchSplitter.cpp | 25 +++++++++++-------- libyul/optimiser/SwitchSplitter.h | 11 ++++---- .../externalContracts/prbmath_signed.sol | 8 +++--- .../externalContracts/prbmath_unsigned.sol | 8 +++--- 4 files changed, 29 insertions(+), 23 deletions(-) diff --git a/libyul/optimiser/SwitchSplitter.cpp b/libyul/optimiser/SwitchSplitter.cpp index 7e57d9c9c9b9..9d7414dcef61 100644 --- a/libyul/optimiser/SwitchSplitter.cpp +++ b/libyul/optimiser/SwitchSplitter.cpp @@ -25,6 +25,7 @@ #include #include +#include #include #include @@ -95,14 +96,14 @@ std::optional> SwitchSplitter::tryTransform(Switch& _swit if (!defaultBody) defaultBody = &emptyBlock; - // Only transform if the top-level split is profitable. - if (!shouldSplit(literalCases.size(), defaultBody->statements.size())) - return {}; - std::sort(literalCases.begin(), literalCases.end(), [](Case const* _a, Case const* _b) { return _a->value->value.value() < _b->value->value.value(); }); + // Only transform if the top-level split is profitable. + if (!shouldSplit(literalCases, defaultBody->statements.size())) + return {}; + return buildTree(literalCases, *exprIdent, *defaultBody, _switch.debugData); } @@ -116,7 +117,7 @@ std::vector SwitchSplitter::buildTree( size_t n = _cases.size(); bool const hasDefault = !_defaultBody.statements.empty(); - if (!shouldSplit(n, _defaultBody.statements.size())) + if (!shouldSplit(_cases, _defaultBody.statements.size())) { // Leaf: small switch; default case added only when present. std::vector switchCases; @@ -163,11 +164,15 @@ std::vector SwitchSplitter::buildTree( return result; } -bool SwitchSplitter::shouldSplit(size_t _n, size_t _defaultBodySize) const +bool SwitchSplitter::shouldSplit(std::span _cases, size_t _defaultBodySize) const { - if (_n <= 4) + size_t const n = _cases.size(); + if (n <= 4) return false; - // Same as ContractCompiler::appendInternalSelector but overhead += k for default body duplication. - size_t const overhead = 17 + _defaultBodySize; - return m_runs * 6 * (_n - 4) > overhead * evmasm::GasCosts::createDataGas; + + // Overhead scales with the fulcrum's actual encoding size. + size_t const pivotIdx = (n - 1) / 2; + unsigned const fulcrumSize = numberEncodingSize(_cases[pivotIdx]->value->value.value()); + size_t const overhead = 13 + fulcrumSize + _defaultBodySize; + return m_runs * 6 * (n - 4) > overhead * evmasm::GasCosts::createDataGas; } diff --git a/libyul/optimiser/SwitchSplitter.h b/libyul/optimiser/SwitchSplitter.h index e7df8d0a6b5d..c865882f4535 100644 --- a/libyul/optimiser/SwitchSplitter.h +++ b/libyul/optimiser/SwitchSplitter.h @@ -36,10 +36,11 @@ namespace solidity::yul * * The transformation is applied only when the runtime gas savings over * expectedExecutionsPerDeployment executions outweigh the extra code size (creation gas): - * runs * 6 * (n - 4) > (17 + k) * createDataGas - * where k is the default body statement count (proxy for bytecode size). - * The 17-byte split overhead comes from the gt-switch structure; k accounts for each split - * duplicating the default body into one additional leaf. + * runs * 6 * (n - 4) > (13 + f + k) * createDataGas + * where f is the encoding size in bytes of the fulcrum (pivot) literal and k is the + * default body statement count (proxy for bytecode size). The 13-byte base overhead + * comes from the gt-switch structure; k accounts for each split duplicating the default + * body into one additional leaf. * * Applied recursively: each sub-slice re-evaluates the formula. * The default case body is duplicated into each leaf branch when present. @@ -74,7 +75,7 @@ class SwitchSplitter: public ASTModifier langutil::DebugData::ConstPtr _debugData ); - bool shouldSplit(size_t _n, size_t _defaultBodySize) const; + bool shouldSplit(std::span _cases, size_t _defaultBodySize) const; std::optional m_gtHandle; size_t m_runs = 0; diff --git a/test/libsolidity/semanticTests/externalContracts/prbmath_signed.sol b/test/libsolidity/semanticTests/externalContracts/prbmath_signed.sol index 43642da452ea..d3cb2450e342 100644 --- a/test/libsolidity/semanticTests/externalContracts/prbmath_signed.sol +++ b/test/libsolidity/semanticTests/externalContracts/prbmath_signed.sol @@ -48,14 +48,14 @@ contract test { } // ---- // constructor() -// gas irOptimized: 185887 -// gas irOptimized code: 1784400 +// gas irOptimized: 183786 +// gas irOptimized code: 1760800 // gas legacy: 209723 // gas legacy code: 2205000 // gas legacyOptimized: 178012 // gas legacyOptimized code: 1669600 -// gas ssaCFGOptimized: 183035 -// gas ssaCFGOptimized code: 1748600 +// gas ssaCFGOptimized: 180903 +// gas ssaCFGOptimized code: 1725000 // div(int256,int256): 3141592653589793238, 88714123 -> 35412542528203691288251815328 // gas irOptimized: 22073 // gas legacy: 22736 diff --git a/test/libsolidity/semanticTests/externalContracts/prbmath_unsigned.sol b/test/libsolidity/semanticTests/externalContracts/prbmath_unsigned.sol index 26f28e124e2a..789be5d9d833 100644 --- a/test/libsolidity/semanticTests/externalContracts/prbmath_unsigned.sol +++ b/test/libsolidity/semanticTests/externalContracts/prbmath_unsigned.sol @@ -48,14 +48,14 @@ contract test { } // ---- // constructor() -// gas irOptimized: 177791 -// gas irOptimized code: 1671200 +// gas irOptimized: 175855 +// gas irOptimized code: 1650800 // gas legacy: 195206 // gas legacy code: 1999000 // gas legacyOptimized: 168857 // gas legacyOptimized code: 1556200 -// gas ssaCFGOptimized: 175312 -// gas ssaCFGOptimized code: 1641400 +// gas ssaCFGOptimized: 173407 +// gas ssaCFGOptimized code: 1621000 // div(uint256,uint256): 3141592653589793238, 88714123 -> 35412542528203691288251815328 // gas irOptimized: 21831 // gas legacy: 22475 From 77a205095c6a689300b34f7add203a8a8194c59a Mon Sep 17 00:00:00 2001 From: William Morriss Date: Sun, 5 Jul 2026 22:02:32 -0500 Subject: [PATCH 05/10] refactor(yul): size SwitchSplitter default-body overhead via CodeSize metric Assisted-by: Claude:claude-sonnet-4-6 --- libyul/optimiser/SwitchSplitter.cpp | 9 +++++---- libyul/optimiser/SwitchSplitter.h | 8 ++++---- 2 files changed, 9 insertions(+), 8 deletions(-) diff --git a/libyul/optimiser/SwitchSplitter.cpp b/libyul/optimiser/SwitchSplitter.cpp index 9d7414dcef61..ca05bb95d972 100644 --- a/libyul/optimiser/SwitchSplitter.cpp +++ b/libyul/optimiser/SwitchSplitter.cpp @@ -18,6 +18,7 @@ #include #include +#include #include #include #include @@ -101,7 +102,7 @@ std::optional> SwitchSplitter::tryTransform(Switch& _swit }); // Only transform if the top-level split is profitable. - if (!shouldSplit(literalCases, defaultBody->statements.size())) + if (!shouldSplit(literalCases, *defaultBody)) return {}; return buildTree(literalCases, *exprIdent, *defaultBody, _switch.debugData); @@ -117,7 +118,7 @@ std::vector SwitchSplitter::buildTree( size_t n = _cases.size(); bool const hasDefault = !_defaultBody.statements.empty(); - if (!shouldSplit(_cases, _defaultBody.statements.size())) + if (!shouldSplit(_cases, _defaultBody)) { // Leaf: small switch; default case added only when present. std::vector switchCases; @@ -164,7 +165,7 @@ std::vector SwitchSplitter::buildTree( return result; } -bool SwitchSplitter::shouldSplit(std::span _cases, size_t _defaultBodySize) const +bool SwitchSplitter::shouldSplit(std::span _cases, Block const& _defaultBody) const { size_t const n = _cases.size(); if (n <= 4) @@ -173,6 +174,6 @@ bool SwitchSplitter::shouldSplit(std::span _cases, size_t _de // Overhead scales with the fulcrum's actual encoding size. size_t const pivotIdx = (n - 1) / 2; unsigned const fulcrumSize = numberEncodingSize(_cases[pivotIdx]->value->value.value()); - size_t const overhead = 13 + fulcrumSize + _defaultBodySize; + size_t const overhead = 13 + fulcrumSize + CodeSize::codeSize(_defaultBody); return m_runs * 6 * (n - 4) > overhead * evmasm::GasCosts::createDataGas; } diff --git a/libyul/optimiser/SwitchSplitter.h b/libyul/optimiser/SwitchSplitter.h index c865882f4535..21ce2997b2d1 100644 --- a/libyul/optimiser/SwitchSplitter.h +++ b/libyul/optimiser/SwitchSplitter.h @@ -38,9 +38,9 @@ namespace solidity::yul * expectedExecutionsPerDeployment executions outweigh the extra code size (creation gas): * runs * 6 * (n - 4) > (13 + f + k) * createDataGas * where f is the encoding size in bytes of the fulcrum (pivot) literal and k is the - * default body statement count (proxy for bytecode size). The 13-byte base overhead - * comes from the gt-switch structure; k accounts for each split duplicating the default - * body into one additional leaf. + * CodeSize estimate for the default body (proxy for bytecode size). The 13-byte base + * overhead comes from the gt-switch structure; k accounts for each split duplicating + * the default body into one additional leaf. * * Applied recursively: each sub-slice re-evaluates the formula. * The default case body is duplicated into each leaf branch when present. @@ -75,7 +75,7 @@ class SwitchSplitter: public ASTModifier langutil::DebugData::ConstPtr _debugData ); - bool shouldSplit(std::span _cases, size_t _defaultBodySize) const; + bool shouldSplit(std::span _cases, Block const& _defaultBody) const; std::optional m_gtHandle; size_t m_runs = 0; From aabc676f670f0ee63aed099662538d097fe1690c Mon Sep 17 00:00:00 2001 From: William Morriss Date: Sun, 2 Aug 2026 03:46:27 -0500 Subject: [PATCH 06/10] docs(yul): clarify ExpressionSplitter is not a hard prerequisite for SwitchSplitter Assisted-by: Claude:claude-sonnet-4-6 --- libyul/optimiser/SwitchSplitter.cpp | 3 ++- libyul/optimiser/SwitchSplitter.h | 15 +++++++++++++-- 2 files changed, 15 insertions(+), 3 deletions(-) diff --git a/libyul/optimiser/SwitchSplitter.cpp b/libyul/optimiser/SwitchSplitter.cpp index ca05bb95d972..b11966ac8b04 100644 --- a/libyul/optimiser/SwitchSplitter.cpp +++ b/libyul/optimiser/SwitchSplitter.cpp @@ -78,7 +78,8 @@ std::optional> SwitchSplitter::tryTransform(Switch& _swit if (!m_gtHandle) return {}; - // Requires a simple identifier — ensured by ExpressionSplitter running first. + // Only switches on a simple identifier are transformed; without ExpressionSplitter + // having run first, more complex expressions are left untouched here. auto const* exprIdent = std::get_if(_switch.expression.get()); if (!exprIdent) return {}; diff --git a/libyul/optimiser/SwitchSplitter.h b/libyul/optimiser/SwitchSplitter.h index 21ce2997b2d1..223ef02df1e0 100644 --- a/libyul/optimiser/SwitchSplitter.h +++ b/libyul/optimiser/SwitchSplitter.h @@ -42,11 +42,22 @@ namespace solidity::yul * overhead comes from the gt-switch structure; k accounts for each split duplicating * the default body into one additional leaf. * + * This mirrors the formula ContractCompiler::appendInternalSelector uses to decide + * whether to binary-split the legacy function selector dispatch (ContractCompiler.cpp, + * see the comment above that function for the full per-opcode derivation of the 6 and 4 + * constants). That formula fixes the split overhead at 17 bytes because selectors are + * always 4-byte literals; here the fulcrum can be any literal, so the fixed 4 is broken + * out into f and the remaining 13-byte base overhead is unchanged (13 + 4 == 17). + * * Applied recursively: each sub-slice re-evaluates the formula. * The default case body is duplicated into each leaf branch when present. * - * Prerequisite: Disambiguator, ExpressionSplitter. - * The switch expression must already be a simple identifier (ensured by ExpressionSplitter). + * Prerequisite: Disambiguator. + * + * ExpressionSplitter is recommended, though not required for correctness: this step only + * transforms switches whose expression is already a simple identifier, so without + * ExpressionSplitter having run first, switches with more complex expressions are simply + * left untouched rather than causing incorrect output. * * Important: Can only be used on EVM code. */ From a6e0b6c38c8817fd36a7e6e827f509805ad02b91 Mon Sep 17 00:00:00 2001 From: William Morriss Date: Sun, 2 Aug 2026 23:29:24 -0500 Subject: [PATCH 07/10] fix(yul): price SwitchSplitter's creation-code switches at the calldata gas rate Assisted-by: Claude:claude-sonnet-4-6 --- libyul/optimiser/SwitchSplitter.cpp | 11 ++- libyul/optimiser/SwitchSplitter.h | 3 + .../prbmath_constructor_switch.sol | 29 ++++++++ .../switchsplitter_constructor_switch.sol | 72 +++++++++++++++++++ 4 files changed, 112 insertions(+), 3 deletions(-) create mode 100644 test/libsolidity/semanticTests/externalContracts/prbmath_constructor_switch.sol create mode 100644 test/libsolidity/semanticTests/externalContracts/switchsplitter_constructor_switch.sol diff --git a/libyul/optimiser/SwitchSplitter.cpp b/libyul/optimiser/SwitchSplitter.cpp index b11966ac8b04..640623ba059a 100644 --- a/libyul/optimiser/SwitchSplitter.cpp +++ b/libyul/optimiser/SwitchSplitter.cpp @@ -44,10 +44,15 @@ void SwitchSplitter::run(OptimiserStepContext& _context, Block& _ast) } SwitchSplitter::SwitchSplitter(OptimiserStepContext const& _context): - m_runs(_context.expectedExecutionsPerDeployment.value_or(0)) + // nullopt means creation code + m_runs(_context.expectedExecutionsPerDeployment.value_or(1)), + m_isCreation(!_context.expectedExecutionsPerDeployment) { if (auto const* evmDialect = dynamic_cast(&_context.dialect)) + { m_gtHandle = evmDialect->findBuiltin("gt"); + m_evmVersion = evmDialect->evmVersion(); + } } void SwitchSplitter::operator()(Block& _block) @@ -175,6 +180,6 @@ bool SwitchSplitter::shouldSplit(std::span _cases, Block cons // Overhead scales with the fulcrum's actual encoding size. size_t const pivotIdx = (n - 1) / 2; unsigned const fulcrumSize = numberEncodingSize(_cases[pivotIdx]->value->value.value()); - size_t const overhead = 13 + fulcrumSize + CodeSize::codeSize(_defaultBody); - return m_runs * 6 * (n - 4) > overhead * evmasm::GasCosts::createDataGas; + uint64_t const overhead = 13 + fulcrumSize + CodeSize::codeSize(_defaultBody); + return m_runs * 6 * (n - 4) > evmasm::GasMeter::dataGas(overhead, m_isCreation, m_evmVersion); } diff --git a/libyul/optimiser/SwitchSplitter.h b/libyul/optimiser/SwitchSplitter.h index 223ef02df1e0..01dd9cc1a040 100644 --- a/libyul/optimiser/SwitchSplitter.h +++ b/libyul/optimiser/SwitchSplitter.h @@ -22,6 +22,7 @@ #include #include +#include #include #include @@ -90,6 +91,8 @@ class SwitchSplitter: public ASTModifier std::optional m_gtHandle; size_t m_runs = 0; + bool m_isCreation = false; + langutil::EVMVersion m_evmVersion; }; } diff --git a/test/libsolidity/semanticTests/externalContracts/prbmath_constructor_switch.sol b/test/libsolidity/semanticTests/externalContracts/prbmath_constructor_switch.sol new file mode 100644 index 000000000000..283232c9bb4f --- /dev/null +++ b/test/libsolidity/semanticTests/externalContracts/prbmath_constructor_switch.sol @@ -0,0 +1,29 @@ +==== ExternalSource: _prbmath/PRBMathCommon.sol ==== +==== ExternalSource: _prbmath/PRBMathUD60x18.sol ==== +==== Source: prbmath.sol ==== +import "_prbmath/PRBMathUD60x18.sol"; + +// Regression test for SwitchSplitter's handling of switches inside creation code: +// PRBMathUD60x18.log10 contains a 39-case switch, only ever called here from the +// constructor, so its dispatch is compiled as part of the creation code rather than +// the runtime code. +contract test { + using PRBMathUD60x18 for uint256; + + uint256 public immutable result; + + constructor(uint256 x) { + result = x.log10(); + } +} +// ---- +// constructor(): 1000000000000000000000 +// gas irOptimized: 108615 +// gas irOptimized code: 18600 +// gas legacy: 118230 +// gas legacy code: 29800 +// gas legacyOptimized: 109471 +// gas legacyOptimized code: 19600 +// gas ssaCFGOptimized: 105833 +// gas ssaCFGOptimized code: 17200 +// result() -> 3000000000000000000 diff --git a/test/libsolidity/semanticTests/externalContracts/switchsplitter_constructor_switch.sol b/test/libsolidity/semanticTests/externalContracts/switchsplitter_constructor_switch.sol new file mode 100644 index 000000000000..6235c62230b3 --- /dev/null +++ b/test/libsolidity/semanticTests/externalContracts/switchsplitter_constructor_switch.sol @@ -0,0 +1,72 @@ +// Regression test: a switch reached only from the constructor, with enough small +// literal cases that SwitchSplitter should split it even in creation code. +contract test { + uint256 public immutable result; + + constructor(uint256 x) { + uint256 r; + assembly { + switch x + case 1 { r := 10 } + case 2 { r := 20 } + case 3 { r := 30 } + case 4 { r := 40 } + case 5 { r := 50 } + case 6 { r := 60 } + case 7 { r := 70 } + case 8 { r := 80 } + case 9 { r := 90 } + case 10 { r := 100 } + case 11 { r := 110 } + case 12 { r := 120 } + case 13 { r := 130 } + case 14 { r := 140 } + case 15 { r := 150 } + case 16 { r := 160 } + case 17 { r := 170 } + case 18 { r := 180 } + case 19 { r := 190 } + case 20 { r := 200 } + case 21 { r := 210 } + case 22 { r := 220 } + case 23 { r := 230 } + case 24 { r := 240 } + case 25 { r := 250 } + case 26 { r := 260 } + case 27 { r := 270 } + case 28 { r := 280 } + case 29 { r := 290 } + case 30 { r := 300 } + case 31 { r := 310 } + case 32 { r := 320 } + case 33 { r := 330 } + case 34 { r := 340 } + case 35 { r := 350 } + case 36 { r := 360 } + case 37 { r := 370 } + case 38 { r := 380 } + case 39 { r := 390 } + case 40 { r := 400 } + case 41 { r := 410 } + case 42 { r := 420 } + case 43 { r := 430 } + case 44 { r := 440 } + case 45 { r := 450 } + case 46 { r := 460 } + case 47 { r := 470 } + case 48 { r := 480 } + } + result = r; + } +} +// ---- +// constructor(): 24 +// gas irOptimized: 70450 +// gas irOptimized code: 18600 +// gas legacy: 72499 +// gas legacy code: 29800 +// gas legacyOptimized: 70165 +// gas legacyOptimized code: 19600 +// gas ssaCFGOptimized: 68966 +// gas ssaCFGOptimized code: 17200 +// result() -> 240 From 3d403211ce305fffdcce0b344d8126a6a412898c Mon Sep 17 00:00:00 2001 From: William Morriss Date: Sun, 2 Aug 2026 23:31:41 -0500 Subject: [PATCH 08/10] docs: add changelog entry for SwitchSplitter optimization Assisted-by: Claude:claude-sonnet-4-6 --- Changelog.md | 1 + 1 file changed, 1 insertion(+) diff --git a/Changelog.md b/Changelog.md index 444091509a0b..9cf66bf35a64 100644 --- a/Changelog.md +++ b/Changelog.md @@ -6,6 +6,7 @@ Compiler Features: * Commandline Interface: `--optimize-runs` now also accepts values from the interval [INT64_MAX, UINT64_MAX]. * General: Speed up SHA-256 hashing (`picosha2`). * General: Remove support for the experimental EOF (EVM Object Format) backend. +* Yul Optimizer: Split large switch statements into a binary search tree of smaller switches, reducing dispatch cost from linear to logarithmic. Bugfixes: * NatSpec: Disallow `@return` tag in event documentation. From 6049337835f0d702016814fe121dac1834b2d955 Mon Sep 17 00:00:00 2001 From: William Morriss Date: Mon, 3 Aug 2026 19:02:35 -0500 Subject: [PATCH 09/10] fix(externalTests): lower zeppelin optimizer runs to fix contract size regression Assisted-by: Claude:claude-sonnet-4-6 --- test/externalTests/zeppelin.sh | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/test/externalTests/zeppelin.sh b/test/externalTests/zeppelin.sh index c26038e0b971..c73348250828 100755 --- a/test/externalTests/zeppelin.sh +++ b/test/externalTests/zeppelin.sh @@ -44,6 +44,7 @@ function zeppelin_test local repo="https://github.com/OpenZeppelin/openzeppelin-contracts.git" local ref="" local config_file="hardhat.config.js" + local extra_optimizer_settings="runs: 175" local compile_only_presets=( #ir-no-optimize # Compilation fails with "Contract initcode size is 49410 bytes and exceeds 49152 bytes (a limit introduced in Shanghai)." @@ -129,13 +130,13 @@ EOF neutralize_package_json_hooks force_hardhat_compiler_binary "$config_file" "$BINARY_TYPE" "$BINARY_PATH" - force_hardhat_compiler_settings "$config_file" "$(first_word "$SELECTED_PRESETS")" + force_hardhat_compiler_settings "$config_file" "$(first_word "$SELECTED_PRESETS")" "" "$CURRENT_EVM_VERSION" "" "$extra_optimizer_settings" npm install npm install hardhat replace_version_pragmas for preset in $SELECTED_PRESETS; do - hardhat_run_test "$config_file" "$preset" "${compile_only_presets[*]}" compile_fn test_fn + hardhat_run_test "$config_file" "$preset" "${compile_only_presets[*]}" compile_fn test_fn "" "" "$extra_optimizer_settings" store_benchmark_report hardhat zeppelin "$repo" "$preset" done } From fce393931885c7685da2f74d457a356f031fc36b Mon Sep 17 00:00:00 2001 From: William Morriss Date: Mon, 3 Aug 2026 19:06:29 -0500 Subject: [PATCH 10/10] test(semanticTests): update gas costs for storage boundary struct array tests Assisted-by: Claude:claude-sonnet-4-6 --- ...rage_boundary_struct_array_mixed_types.sol | 68 +++++++++---------- ...torage_boundary_struct_array_multislot.sol | 20 +++--- 2 files changed, 44 insertions(+), 44 deletions(-) diff --git a/test/libsolidity/semanticTests/storage/storage_boundary_struct_array_mixed_types.sol b/test/libsolidity/semanticTests/storage/storage_boundary_struct_array_mixed_types.sol index 64427e6e3f74..9d3a2c977f2e 100644 --- a/test/libsolidity/semanticTests/storage/storage_boundary_struct_array_mixed_types.sol +++ b/test/libsolidity/semanticTests/storage/storage_boundary_struct_array_mixed_types.sol @@ -98,92 +98,92 @@ contract C { // ---- // canaryValue() -> 0xffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff // boundaryArray() -> 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 -// gas irOptimized: 113169 +// gas irOptimized: 113102 // gas legacy: 120742 // gas legacyOptimized: 112518 -// gas ssaCFGOptimized: 113252 +// gas ssaCFGOptimized: 113190 // destArray() -> 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 -// gas irOptimized: 113078 +// gas irOptimized: 113098 // gas legacy: 120738 // gas legacyOptimized: 112505 -// gas ssaCFGOptimized: 113161 +// gas ssaCFGOptimized: 113186 // fillBoundaryArray() -// gas irOptimized: 912522 +// gas irOptimized: 912455 // gas legacy: 930728 // gas legacyOptimized: 916628 -// gas ssaCFGOptimized: 915317 +// gas ssaCFGOptimized: 915255 // canaryValue() -> 0xffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff // boundaryArray() -> 1, 2, 3, 4, true, 6, 7, 8, 9, true, 11, 12, 13, 14, true, 16, 17, 18, 19, true, 21, 22, 23, 24, true, 26, 27, 28, 29, true, 31, 32, 33, 34, true, 36, 37, 38, 39, true, 41, 42, 43, 44, true, 46, 47, 48, 49, true -// gas irOptimized: 113169 +// gas irOptimized: 113102 // gas legacy: 120742 // gas legacyOptimized: 112518 -// gas ssaCFGOptimized: 113252 +// gas ssaCFGOptimized: 113190 // destArray() -> 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 -// gas irOptimized: 113078 +// gas irOptimized: 113098 // gas legacy: 120738 // gas legacyOptimized: 112505 -// gas ssaCFGOptimized: 113161 +// gas ssaCFGOptimized: 113186 // copyFromBoundary() -// gas irOptimized: 994579 +// gas irOptimized: 994599 // gas legacy: 1023407 // gas legacyOptimized: 994746 -// gas ssaCFGOptimized: 996288 +// gas ssaCFGOptimized: 996313 // canaryValue() -> 0xffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff // boundaryArray() -> 1, 2, 3, 4, true, 6, 7, 8, 9, true, 11, 12, 13, 14, true, 16, 17, 18, 19, true, 21, 22, 23, 24, true, 26, 27, 28, 29, true, 31, 32, 33, 34, true, 36, 37, 38, 39, true, 41, 42, 43, 44, true, 46, 47, 48, 49, true -// gas irOptimized: 113169 +// gas irOptimized: 113102 // gas legacy: 120742 // gas legacyOptimized: 112518 -// gas ssaCFGOptimized: 113252 +// gas ssaCFGOptimized: 113190 // destArray() -> 1, 2, 3, 4, true, 6, 7, 8, 9, true, 11, 12, 13, 14, true, 16, 17, 18, 19, true, 21, 22, 23, 24, true, 26, 27, 28, 29, true, 31, 32, 33, 34, true, 36, 37, 38, 39, true, 41, 42, 43, 44, true, 46, 47, 48, 49, true -// gas irOptimized: 113078 +// gas irOptimized: 113098 // gas legacy: 120738 // gas legacyOptimized: 112505 -// gas ssaCFGOptimized: 113161 +// gas ssaCFGOptimized: 113186 // fillDestArray() -// gas irOptimized: 200426 +// gas irOptimized: 200443 // gas legacy: 218746 // gas legacyOptimized: 204648 -// gas ssaCFGOptimized: 203221 +// gas ssaCFGOptimized: 203193 // canaryValue() -> 0xffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff // boundaryArray() -> 1, 2, 3, 4, true, 6, 7, 8, 9, true, 11, 12, 13, 14, true, 16, 17, 18, 19, true, 21, 22, 23, 24, true, 26, 27, 28, 29, true, 31, 32, 33, 34, true, 36, 37, 38, 39, true, 41, 42, 43, 44, true, 46, 47, 48, 49, true -// gas irOptimized: 113169 +// gas irOptimized: 113102 // gas legacy: 120742 // gas legacyOptimized: 112518 -// gas ssaCFGOptimized: 113252 +// gas ssaCFGOptimized: 113190 // destArray() -> 51, 52, 53, 54, true, 56, 57, 58, 59, true, 61, 62, 63, 64, true, 66, 67, 68, 69, true, 71, 72, 73, 74, true, 76, 77, 78, 79, true, 81, 82, 83, 84, true, 86, 87, 88, 89, true, 91, 92, 93, 94, true, 96, 97, 98, 99, true -// gas irOptimized: 113078 +// gas irOptimized: 113098 // gas legacy: 120738 // gas legacyOptimized: 112505 -// gas ssaCFGOptimized: 113161 +// gas ssaCFGOptimized: 113186 // copyToBoundary() -// gas irOptimized: 282623 +// gas irOptimized: 282556 // gas legacy: 311362 // gas legacyOptimized: 282712 -// gas ssaCFGOptimized: 284332 +// gas ssaCFGOptimized: 284270 // canaryValue() -> 0xffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff // boundaryArray() -> 51, 52, 53, 54, true, 56, 57, 58, 59, true, 61, 62, 63, 64, true, 66, 67, 68, 69, true, 71, 72, 73, 74, true, 76, 77, 78, 79, true, 81, 82, 83, 84, true, 86, 87, 88, 89, true, 91, 92, 93, 94, true, 96, 97, 98, 99, true -// gas irOptimized: 113169 +// gas irOptimized: 113102 // gas legacy: 120742 // gas legacyOptimized: 112518 -// gas ssaCFGOptimized: 113252 +// gas ssaCFGOptimized: 113190 // destArray() -> 51, 52, 53, 54, true, 56, 57, 58, 59, true, 61, 62, 63, 64, true, 66, 67, 68, 69, true, 71, 72, 73, 74, true, 76, 77, 78, 79, true, 81, 82, 83, 84, true, 86, 87, 88, 89, true, 91, 92, 93, 94, true, 96, 97, 98, 99, true -// gas irOptimized: 113078 +// gas irOptimized: 113098 // gas legacy: 120738 // gas legacyOptimized: 112505 -// gas ssaCFGOptimized: 113161 +// gas ssaCFGOptimized: 113186 // deleteBoundaryArray() -// gas irOptimized: 177968 +// gas irOptimized: 177913 // gas legacy: 180995 // gas legacyOptimized: 178182 -// gas ssaCFGOptimized: 177963 +// gas ssaCFGOptimized: 177913 // canaryValue() -> 0xffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff // boundaryArray() -> 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 -// gas irOptimized: 113169 +// gas irOptimized: 113102 // gas legacy: 120742 // gas legacyOptimized: 112518 -// gas ssaCFGOptimized: 113252 +// gas ssaCFGOptimized: 113190 // destArray() -> 51, 52, 53, 54, true, 56, 57, 58, 59, true, 61, 62, 63, 64, true, 66, 67, 68, 69, true, 71, 72, 73, 74, true, 76, 77, 78, 79, true, 81, 82, 83, 84, true, 86, 87, 88, 89, true, 91, 92, 93, 94, true, 96, 97, 98, 99, true -// gas irOptimized: 113078 +// gas irOptimized: 113098 // gas legacy: 120738 // gas legacyOptimized: 112505 -// gas ssaCFGOptimized: 113161 +// gas ssaCFGOptimized: 113186 diff --git a/test/libsolidity/semanticTests/storage/storage_boundary_struct_array_multislot.sol b/test/libsolidity/semanticTests/storage/storage_boundary_struct_array_multislot.sol index 7e12cd6a6a1b..1453e4ef722b 100644 --- a/test/libsolidity/semanticTests/storage/storage_boundary_struct_array_multislot.sol +++ b/test/libsolidity/semanticTests/storage/storage_boundary_struct_array_multislot.sol @@ -92,42 +92,42 @@ contract C { // boundaryArray() -> 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 // destArray() -> 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 // fillBoundaryArray() -// gas irOptimized: 688719 +// gas irOptimized: 688652 // gas legacy: 700075 // gas legacyOptimized: 691605 -// gas ssaCFGOptimized: 688794 +// gas ssaCFGOptimized: 688732 // canaryValue() -> 0xffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff // boundaryArray() -> 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30 // destArray() -> 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 // copyFromBoundary() -// gas irOptimized: 748779 +// gas irOptimized: 748799 // gas legacy: 767297 // gas legacyOptimized: 748756 -// gas ssaCFGOptimized: 748798 +// gas ssaCFGOptimized: 748823 // canaryValue() -> 0xffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff // boundaryArray() -> 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30 // destArray() -> 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30 // fillDestArray() -// gas irOptimized: 175623 +// gas irOptimized: 175640 // gas legacy: 187093 // gas legacyOptimized: 178625 -// gas ssaCFGOptimized: 175698 +// gas ssaCFGOptimized: 175580 // canaryValue() -> 0xffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff // boundaryArray() -> 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30 // destArray() -> 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, 52, 53, 54, 55, 56, 57, 58, 59, 60 // copyToBoundary() -// gas irOptimized: 235823 +// gas irOptimized: 235756 // gas legacy: 254252 // gas legacyOptimized: 235722 -// gas ssaCFGOptimized: 235842 +// gas ssaCFGOptimized: 235780 // canaryValue() -> 0xffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff // boundaryArray() -> 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, 52, 53, 54, 55, 56, 57, 58, 59, 60 // destArray() -> 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, 52, 53, 54, 55, 56, 57, 58, 59, 60 // deleteBoundaryArray() -// gas irOptimized: 137824 +// gas irOptimized: 137769 // gas legacy: 137971 // gas legacyOptimized: 137750 -// gas ssaCFGOptimized: 137819 +// gas ssaCFGOptimized: 137769 // canaryValue() -> 0xffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff // boundaryArray() -> 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 // destArray() -> 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, 52, 53, 54, 55, 56, 57, 58, 59, 60