diff --git a/libsolidity/interface/OptimiserSettings.h b/libsolidity/interface/OptimiserSettings.h index 3df99325e..990981bc9 100644 --- a/libsolidity/interface/OptimiserSettings.h +++ b/libsolidity/interface/OptimiserSettings.h @@ -43,7 +43,7 @@ struct OptimiserSettings "Tpeul" // Run functional expression inliner "xarulrul" // Prune a bit more in SSA - "xarrcL" // Turn into SSA again and simplify + "xarrcSL" // Turn into SSA again and simplify "gvif" // Run full inliner "CTUcarrLsTFOtfDncarrIulc" // SSA plus simplify "]" diff --git a/libyul/CMakeLists.txt b/libyul/CMakeLists.txt index d8bd02842..2da2a8e54 100644 --- a/libyul/CMakeLists.txt +++ b/libyul/CMakeLists.txt @@ -162,6 +162,8 @@ add_library(yul optimiser/ReasoningBasedSimplifier.h optimiser/RedundantAssignEliminator.cpp optimiser/RedundantAssignEliminator.h + optimiser/RedundantStoreEliminator.cpp + optimiser/RedundantStoreEliminator.h optimiser/Rematerialiser.cpp optimiser/Rematerialiser.h optimiser/SSAReverser.cpp diff --git a/libyul/optimiser/RedundantStoreEliminator.cpp b/libyul/optimiser/RedundantStoreEliminator.cpp new file mode 100644 index 000000000..1c1bb72dc --- /dev/null +++ b/libyul/optimiser/RedundantStoreEliminator.cpp @@ -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 +/** + */ + +#include + +#include +#include + +#include + +#include + +using namespace std; +using namespace solidity; +using namespace solidity::yul; + + +void RedundantStoreEliminator::run(OptimiserStepContext& _context, Block& _ast) +{ + RedundantStoreEliminator rse{_context.dialect}; + rse(_ast); +} + +void RedundantStoreEliminator::operator()(Block& _block) +{ + // TODO This is copied from DataflowAnalyzer + size_t numScopes = m_variableScopes.size(); + pushScope(false); + + map latestStore; + set redundantStores; + + for (size_t i = 0; i < _block.statements.size(); ++i) + { + Statement& statement = _block.statements.at(i); + if (holds_alternative(statement)) + { + ExpressionStatement& exprStatement = get(statement); + // This mechanism relies on the way the data flow analyzer + // handles `m_storage`. + if (auto vars = isSimpleStore(StoreLoadLocation::Storage, exprStatement)) + { + if (m_storage.count(vars->first) && latestStore.count(vars->first)) + redundantStores.insert(latestStore.at(vars->first)); + latestStore[vars->first] = i; + } + } + ASTModifier::visit(statement); + } + + popScope(); + assertThrow(numScopes == m_variableScopes.size(), OptimizerException, ""); + DataFlowAnalyzer::operator()(_block); + if (redundantStores.empty()) + return; + + // The arguments to sstore are identifiers and thus do not have side-effects, + // because otherwise the statement would not have ended up in `redundantStores`. + vector newStatements; + for (size_t i = 0; i < _block.statements.size(); ++i) + { + if (!redundantStores.count(i)) + newStatements.emplace_back(move(_block.statements.at(i))); + } + _block.statements = move(newStatements); +} diff --git a/libyul/optimiser/RedundantStoreEliminator.h b/libyul/optimiser/RedundantStoreEliminator.h new file mode 100644 index 000000000..f4ec097e6 --- /dev/null +++ b/libyul/optimiser/RedundantStoreEliminator.h @@ -0,0 +1,54 @@ +/* + 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 + +namespace solidity::yul +{ +struct Dialect; + +/** + * + */ +class RedundantStoreEliminator: public DataFlowAnalyzer +{ +public: + static constexpr char const* name{"RedundantStoreEliminator"}; + explicit RedundantStoreEliminator( + Dialect const& _dialect + ): DataFlowAnalyzer(_dialect) {} + static void run(OptimiserStepContext&, Block& _ast); + + void operator()(Block& _block); + + +private: +}; + + +} diff --git a/libyul/optimiser/Suite.cpp b/libyul/optimiser/Suite.cpp index 480a542a3..2f74a1f78 100644 --- a/libyul/optimiser/Suite.cpp +++ b/libyul/optimiser/Suite.cpp @@ -56,6 +56,7 @@ #include #include #include +#include #include #include #include @@ -200,6 +201,7 @@ map> const& OptimiserSuite::allSteps() LoadResolver, LoopInvariantCodeMotion, RedundantAssignEliminator, + RedundantStoreEliminator, ReasoningBasedSimplifier, Rematerialiser, SSAReverser, @@ -241,6 +243,7 @@ map const& OptimiserSuite::stepNameToAbbreviationMap() {LoopInvariantCodeMotion::name, 'M'}, {ReasoningBasedSimplifier::name, 'R'}, {RedundantAssignEliminator::name, 'r'}, + {RedundantStoreEliminator::name, 'S'}, {Rematerialiser::name, 'm'}, {SSAReverser::name, 'V'}, {SSATransform::name, 'a'}, diff --git a/test/cmdlineTests/yul_stack_opt/output b/test/cmdlineTests/yul_stack_opt/output index 7c0e8ca66..0fecb3c94 100644 --- a/test/cmdlineTests/yul_stack_opt/output +++ b/test/cmdlineTests/yul_stack_opt/output @@ -6,20 +6,6 @@ object "object" { code { { let _1 := 1 - sstore(_1, _1) - sstore(2, _1) - sstore(3, _1) - sstore(4, _1) - sstore(5, _1) - sstore(6, _1) - sstore(7, _1) - sstore(8, _1) - sstore(9, _1) - sstore(10, _1) - sstore(11, _1) - sstore(12, _1) - sstore(13, _1) - sstore(_1, _1) sstore(2, _1) sstore(3, _1) sstore(4, _1) @@ -39,93 +25,11 @@ object "object" { Binary representation: -6001808155806002558060035580600455806005558060065580600755806008558060095580600a5580600b5580600c5580600d55808155806002558060035580600455806005558060065580600755806008558060095580600a5580600b5580600c5580600d5580815550 +6001806002558060035580600455806005558060065580600755806008558060095580600a5580600b5580600c5580600d5580815550 Text representation: /* "yul_stack_opt/input.yul":98:99 */ 0x01 - dup1 - dup2 - /* "yul_stack_opt/input.yul":129:141 */ - sstore - /* "yul_stack_opt/input.yul":98:99 */ - dup1 - /* "yul_stack_opt/input.yul":151:160 */ - 0x02 - /* "yul_stack_opt/input.yul":144:164 */ - sstore - /* "yul_stack_opt/input.yul":98:99 */ - dup1 - /* "yul_stack_opt/input.yul":174:183 */ - 0x03 - /* "yul_stack_opt/input.yul":167:187 */ - sstore - /* "yul_stack_opt/input.yul":98:99 */ - dup1 - /* "yul_stack_opt/input.yul":197:206 */ - 0x04 - /* "yul_stack_opt/input.yul":190:210 */ - sstore - /* "yul_stack_opt/input.yul":98:99 */ - dup1 - /* "yul_stack_opt/input.yul":220:229 */ - 0x05 - /* "yul_stack_opt/input.yul":213:233 */ - sstore - /* "yul_stack_opt/input.yul":98:99 */ - dup1 - /* "yul_stack_opt/input.yul":243:252 */ - 0x06 - /* "yul_stack_opt/input.yul":236:256 */ - sstore - /* "yul_stack_opt/input.yul":98:99 */ - dup1 - /* "yul_stack_opt/input.yul":266:275 */ - 0x07 - /* "yul_stack_opt/input.yul":259:279 */ - sstore - /* "yul_stack_opt/input.yul":98:99 */ - dup1 - /* "yul_stack_opt/input.yul":289:298 */ - 0x08 - /* "yul_stack_opt/input.yul":282:302 */ - sstore - /* "yul_stack_opt/input.yul":98:99 */ - dup1 - /* "yul_stack_opt/input.yul":312:321 */ - 0x09 - /* "yul_stack_opt/input.yul":305:325 */ - sstore - /* "yul_stack_opt/input.yul":98:99 */ - dup1 - /* "yul_stack_opt/input.yul":335:344 */ - 0x0a - /* "yul_stack_opt/input.yul":328:348 */ - sstore - /* "yul_stack_opt/input.yul":98:99 */ - dup1 - /* "yul_stack_opt/input.yul":358:368 */ - 0x0b - /* "yul_stack_opt/input.yul":351:372 */ - sstore - /* "yul_stack_opt/input.yul":98:99 */ - dup1 - /* "yul_stack_opt/input.yul":382:392 */ - 0x0c - /* "yul_stack_opt/input.yul":375:396 */ - sstore - /* "yul_stack_opt/input.yul":98:99 */ - dup1 - /* "yul_stack_opt/input.yul":406:416 */ - 0x0d - /* "yul_stack_opt/input.yul":399:420 */ - sstore - /* "yul_stack_opt/input.yul":98:99 */ - dup1 - dup2 - /* "yul_stack_opt/input.yul":129:141 */ - sstore - /* "yul_stack_opt/input.yul":98:99 */ dup1 /* "yul_stack_opt/input.yul":151:160 */ 0x02 diff --git a/test/libyul/yulOptimizerTests/fullSuite/stack_compressor_msize.yul b/test/libyul/yulOptimizerTests/fullSuite/stack_compressor_msize.yul index b417991c2..61a929747 100644 --- a/test/libyul/yulOptimizerTests/fullSuite/stack_compressor_msize.yul +++ b/test/libyul/yulOptimizerTests/fullSuite/stack_compressor_msize.yul @@ -47,7 +47,6 @@ // pop(keccak256(gcd(10, 15), or(gt(not(gcd(10, 15)), 1), 1))) // mstore(lt(or(gt(1, or(or(gt(or(or(or(gt(or(gt(_3, _6), 1), _5), _4), _2), 1), 1), _1), 1)), 1), 1), 1) // sstore(not(gcd(10, 15)), 1) -// sstore(0, 0) // sstore(2, 1) // extcodecopy(1, msize(), 1, 1) // sstore(0, 0) diff --git a/test/libyul/yulOptimizerTests/fullSuite/storage.yul b/test/libyul/yulOptimizerTests/fullSuite/storage.yul index 7b3360847..a60b3d348 100644 --- a/test/libyul/yulOptimizerTests/fullSuite/storage.yul +++ b/test/libyul/yulOptimizerTests/fullSuite/storage.yul @@ -8,7 +8,6 @@ // // { // { -// sstore(4, 5) // sstore(4, 3) // sstore(8, 3) // } diff --git a/test/libyul/yulOptimizerTests/fullSuite/unusedFunctionParameterPruner.yul b/test/libyul/yulOptimizerTests/fullSuite/unusedFunctionParameterPruner.yul index b7f0b4afb..6f82ab27a 100644 --- a/test/libyul/yulOptimizerTests/fullSuite/unusedFunctionParameterPruner.yul +++ b/test/libyul/yulOptimizerTests/fullSuite/unusedFunctionParameterPruner.yul @@ -20,7 +20,6 @@ // { // { // let out1, out2 := foo(sload(32)) -// sstore(0, out1) // sstore(0, out2) // let out1_1, out2_1 := foo(sload(8)) // } diff --git a/test/libyul/yulOptimizerTests/fullSuite/unusedFunctionParameterPruner_recursion.yul b/test/libyul/yulOptimizerTests/fullSuite/unusedFunctionParameterPruner_recursion.yul index 69b4517eb..90b096fac 100644 --- a/test/libyul/yulOptimizerTests/fullSuite/unusedFunctionParameterPruner_recursion.yul +++ b/test/libyul/yulOptimizerTests/fullSuite/unusedFunctionParameterPruner_recursion.yul @@ -16,7 +16,6 @@ // { // let x, y, z := f() // sstore(0, x) -// sstore(1, y) // sstore(1, z) // } // function f() -> x, y, z diff --git a/test/libyul/yulOptimizerTests/fullSuite/unusedFunctionParameterPruner_return.yul b/test/libyul/yulOptimizerTests/fullSuite/unusedFunctionParameterPruner_return.yul index 14f0137cb..b066d998b 100644 --- a/test/libyul/yulOptimizerTests/fullSuite/unusedFunctionParameterPruner_return.yul +++ b/test/libyul/yulOptimizerTests/fullSuite/unusedFunctionParameterPruner_return.yul @@ -22,8 +22,6 @@ // { // { // let out1, out2 := foo(sload(32)) -// sstore(0, out1) -// sstore(0, out2) // sstore(0, 0) // let out1_1, out2_1 := foo(sload(8)) // }