mirror of
https://github.com/ethereum/solidity
synced 2023-10-03 13:03:40 +00:00
Equal store eliminator.
This commit is contained in:
parent
223395bcad
commit
772e100813
@ -5,6 +5,7 @@ Language Features:
|
||||
|
||||
|
||||
Compiler Features:
|
||||
* Yul Optimizer: Remove ``mstore`` and ``sstore`` operations if the slot already contains the same value.
|
||||
|
||||
|
||||
|
||||
|
@ -44,7 +44,7 @@ struct OptimiserSettings
|
||||
static char constexpr DefaultYulOptimiserSteps[] =
|
||||
"dhfoDgvulfnTUtnIf" // None of these can make stack problems worse
|
||||
"["
|
||||
"xa[r]scLM" // Turn into SSA and simplify
|
||||
"xa[r]EscLM" // Turn into SSA and simplify
|
||||
"cCTUtTOntnfDIul" // Perform structural simplification
|
||||
"Lcul" // Simplify again
|
||||
"Vcul [j]" // Reverse SSA
|
||||
|
@ -120,6 +120,8 @@ add_library(yul
|
||||
optimiser/DeadCodeEliminator.h
|
||||
optimiser/Disambiguator.cpp
|
||||
optimiser/Disambiguator.h
|
||||
optimiser/EqualStoreEliminator.cpp
|
||||
optimiser/EqualStoreEliminator.h
|
||||
optimiser/EquivalentFunctionDetector.cpp
|
||||
optimiser/EquivalentFunctionDetector.h
|
||||
optimiser/EquivalentFunctionCombiner.cpp
|
||||
|
77
libyul/optimiser/EqualStoreEliminator.cpp
Normal file
77
libyul/optimiser/EqualStoreEliminator.cpp
Normal file
@ -0,0 +1,77 @@
|
||||
/*
|
||||
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 <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
// SPDX-License-Identifier: GPL-3.0
|
||||
/**
|
||||
* Optimisation stage that removes mstore and sstore operations if they store the same
|
||||
* value that is already known to be in that slot.
|
||||
*/
|
||||
|
||||
#include <libyul/optimiser/EqualStoreEliminator.h>
|
||||
|
||||
#include <libyul/optimiser/CallGraphGenerator.h>
|
||||
#include <libyul/optimiser/OptimizerUtilities.h>
|
||||
#include <libyul/optimiser/Semantics.h>
|
||||
#include <libyul/optimiser/CallGraphGenerator.h>
|
||||
#include <libyul/SideEffects.h>
|
||||
#include <libyul/AST.h>
|
||||
#include <libyul/Utilities.h>
|
||||
|
||||
#include <libevmasm/GasMeter.h>
|
||||
#include <libsolutil/Keccak256.h>
|
||||
|
||||
#include <limits>
|
||||
|
||||
using namespace std;
|
||||
using namespace solidity;
|
||||
using namespace solidity::util;
|
||||
using namespace solidity::evmasm;
|
||||
using namespace solidity::yul;
|
||||
|
||||
void EqualStoreEliminator::run(OptimiserStepContext const& _context, Block& _ast)
|
||||
{
|
||||
EqualStoreEliminator eliminator{
|
||||
_context.dialect,
|
||||
SideEffectsPropagator::sideEffects(_context.dialect, CallGraphGenerator::callGraph(_ast))
|
||||
};
|
||||
eliminator(_ast);
|
||||
|
||||
StatementRemover remover{eliminator.m_pendingRemovals};
|
||||
remover(_ast);
|
||||
}
|
||||
|
||||
void EqualStoreEliminator::visit(Statement& _statement)
|
||||
{
|
||||
// No need to consider potential changes through complex arguments since
|
||||
// isSimpleStore only returns something if the arguments are identifiers.
|
||||
if (ExpressionStatement const* expression = get_if<ExpressionStatement>(&_statement))
|
||||
{
|
||||
if (auto vars = isSimpleStore(StoreLoadLocation::Storage, *expression))
|
||||
{
|
||||
if (auto const* currentValue = valueOrNullptr(m_storage, vars->first))
|
||||
if (*currentValue == vars->second)
|
||||
m_pendingRemovals.insert(&_statement);
|
||||
}
|
||||
else if (auto vars = isSimpleStore(StoreLoadLocation::Memory, *expression))
|
||||
{
|
||||
if (auto const* currentValue = valueOrNullptr(m_memory, vars->first))
|
||||
if (*currentValue == vars->second)
|
||||
m_pendingRemovals.insert(&_statement);
|
||||
}
|
||||
}
|
||||
|
||||
DataFlowAnalyzer::visit(_statement);
|
||||
}
|
60
libyul/optimiser/EqualStoreEliminator.h
Normal file
60
libyul/optimiser/EqualStoreEliminator.h
Normal file
@ -0,0 +1,60 @@
|
||||
/*
|
||||
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 <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
// SPDX-License-Identifier: GPL-3.0
|
||||
/**
|
||||
* Optimisation stage that removes mstore and sstore operations if they store the same
|
||||
* value that is already known to be in that slot.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <libyul/optimiser/DataFlowAnalyzer.h>
|
||||
#include <libyul/optimiser/OptimiserStep.h>
|
||||
|
||||
namespace solidity::yul
|
||||
{
|
||||
|
||||
/**
|
||||
* Optimisation stage that removes mstore and sstore operations if they store the same
|
||||
* value that is already known to be in that slot.
|
||||
*
|
||||
* Works best if the code is in SSA form - without literal arguments.
|
||||
*
|
||||
* Prerequisite: Disambiguator, ForLoopInitRewriter.
|
||||
*/
|
||||
class EqualStoreEliminator: public DataFlowAnalyzer
|
||||
{
|
||||
public:
|
||||
static constexpr char const* name{"EqualStoreEliminator"};
|
||||
static void run(OptimiserStepContext const&, Block& _ast);
|
||||
|
||||
private:
|
||||
EqualStoreEliminator(
|
||||
Dialect const& _dialect,
|
||||
std::map<YulString, SideEffects> _functionSideEffects
|
||||
):
|
||||
DataFlowAnalyzer(_dialect, std::move(_functionSideEffects))
|
||||
{}
|
||||
|
||||
protected:
|
||||
using ASTModifier::visit;
|
||||
void visit(Statement& _statement) override;
|
||||
|
||||
std::set<Statement const*> m_pendingRemovals;
|
||||
};
|
||||
|
||||
}
|
@ -57,3 +57,18 @@ optional<evmasm::Instruction> yul::toEVMInstruction(Dialect const& _dialect, Yul
|
||||
return builtin->instruction;
|
||||
return nullopt;
|
||||
}
|
||||
|
||||
void StatementRemover::operator()(Block& _block)
|
||||
{
|
||||
util::iterateReplacing(
|
||||
_block.statements,
|
||||
[&](Statement& _statement) -> std::optional<vector<Statement>>
|
||||
{
|
||||
if (m_toRemove.count(&_statement))
|
||||
return {vector<Statement>{}};
|
||||
else
|
||||
return nullopt;
|
||||
}
|
||||
);
|
||||
ASTModifier::operator()(_block);
|
||||
}
|
||||
|
@ -25,6 +25,7 @@
|
||||
#include <libyul/ASTForward.h>
|
||||
#include <libyul/Dialect.h>
|
||||
#include <libyul/YulString.h>
|
||||
#include <libyul/optimiser/ASTWalker.h>
|
||||
|
||||
#include <optional>
|
||||
|
||||
@ -48,4 +49,14 @@ bool isRestrictedIdentifier(Dialect const& _dialect, YulString const& _identifie
|
||||
/// Helper function that returns the instruction, if the `_name` is a BuiltinFunction
|
||||
std::optional<evmasm::Instruction> toEVMInstruction(Dialect const& _dialect, YulString const& _name);
|
||||
|
||||
class StatementRemover: public ASTModifier
|
||||
{
|
||||
public:
|
||||
explicit StatementRemover(std::set<Statement const*> const& _toRemove): m_toRemove(_toRemove) {}
|
||||
|
||||
void operator()(Block& _block) override;
|
||||
private:
|
||||
std::set<Statement const*> const& m_toRemove;
|
||||
};
|
||||
|
||||
}
|
||||
|
@ -32,6 +32,7 @@
|
||||
#include <libyul/optimiser/DeadCodeEliminator.h>
|
||||
#include <libyul/optimiser/FunctionGrouper.h>
|
||||
#include <libyul/optimiser/FunctionHoister.h>
|
||||
#include <libyul/optimiser/EqualStoreEliminator.h>
|
||||
#include <libyul/optimiser/EquivalentFunctionCombiner.h>
|
||||
#include <libyul/optimiser/ExpressionSplitter.h>
|
||||
#include <libyul/optimiser/ExpressionJoiner.h>
|
||||
@ -204,6 +205,7 @@ map<string, unique_ptr<OptimiserStep>> const& OptimiserSuite::allSteps()
|
||||
ConditionalUnsimplifier,
|
||||
ControlFlowSimplifier,
|
||||
DeadCodeEliminator,
|
||||
EqualStoreEliminator,
|
||||
EquivalentFunctionCombiner,
|
||||
ExpressionInliner,
|
||||
ExpressionJoiner,
|
||||
@ -244,6 +246,7 @@ map<string, char> const& OptimiserSuite::stepNameToAbbreviationMap()
|
||||
{ConditionalUnsimplifier::name, 'U'},
|
||||
{ControlFlowSimplifier::name, 'n'},
|
||||
{DeadCodeEliminator::name, 'D'},
|
||||
{EqualStoreEliminator::name, 'E'},
|
||||
{EquivalentFunctionCombiner::name, 'v'},
|
||||
{ExpressionInliner::name, 'e'},
|
||||
{ExpressionJoiner::name, 'j'},
|
||||
|
@ -23,6 +23,7 @@
|
||||
#include <libyul/optimiser/UnusedAssignEliminator.h>
|
||||
|
||||
#include <libyul/optimiser/Semantics.h>
|
||||
#include <libyul/optimiser/OptimizerUtilities.h>
|
||||
#include <libyul/AST.h>
|
||||
|
||||
#include <libsolutil/CommonData.h>
|
||||
|
@ -23,6 +23,7 @@
|
||||
|
||||
#include <libyul/optimiser/Semantics.h>
|
||||
#include <libyul/optimiser/OptimiserStep.h>
|
||||
#include <libyul/optimiser/OptimizerUtilities.h>
|
||||
#include <libyul/AST.h>
|
||||
|
||||
#include <libsolutil/CommonData.h>
|
||||
@ -156,18 +157,3 @@ void UnusedStoreBase::merge(TrackedStores& _target, vector<TrackedStores>&& _sou
|
||||
merge(_target, move(ts));
|
||||
_source.clear();
|
||||
}
|
||||
|
||||
void StatementRemover::operator()(Block& _block)
|
||||
{
|
||||
util::iterateReplacing(
|
||||
_block.statements,
|
||||
[&](Statement& _statement) -> std::optional<vector<Statement>>
|
||||
{
|
||||
if (m_toRemove.count(&_statement))
|
||||
return {vector<Statement>{}};
|
||||
else
|
||||
return nullopt;
|
||||
}
|
||||
);
|
||||
ASTModifier::operator()(_block);
|
||||
}
|
||||
|
@ -105,14 +105,4 @@ protected:
|
||||
size_t m_forLoopNestingDepth = 0;
|
||||
};
|
||||
|
||||
class StatementRemover: public ASTModifier
|
||||
{
|
||||
public:
|
||||
explicit StatementRemover(std::set<Statement const*> const& _toRemove): m_toRemove(_toRemove) {}
|
||||
|
||||
void operator()(Block& _block) override;
|
||||
private:
|
||||
std::set<Statement const*> const& m_toRemove;
|
||||
};
|
||||
|
||||
}
|
||||
|
@ -28,6 +28,7 @@
|
||||
#include <libyul/optimiser/ConditionalUnsimplifier.h>
|
||||
#include <libyul/optimiser/ConditionalSimplifier.h>
|
||||
#include <libyul/optimiser/CommonSubexpressionEliminator.h>
|
||||
#include <libyul/optimiser/EqualStoreEliminator.h>
|
||||
#include <libyul/optimiser/EquivalentFunctionCombiner.h>
|
||||
#include <libyul/optimiser/ExpressionSplitter.h>
|
||||
#include <libyul/optimiser/FunctionGrouper.h>
|
||||
@ -236,6 +237,11 @@ YulOptimizerTestCommon::YulOptimizerTestCommon(
|
||||
ForLoopInitRewriter::run(*m_context, *m_ast);
|
||||
UnusedAssignEliminator::run(*m_context, *m_ast);
|
||||
}},
|
||||
{"equalStoreEliminator", [&]() {
|
||||
disambiguate();
|
||||
ForLoopInitRewriter::run(*m_context, *m_ast);
|
||||
EqualStoreEliminator::run(*m_context, *m_ast);
|
||||
}},
|
||||
{"ssaPlusCleanup", [&]() {
|
||||
disambiguate();
|
||||
ForLoopInitRewriter::run(*m_context, *m_ast);
|
||||
|
@ -0,0 +1,31 @@
|
||||
{
|
||||
let var_k := calldataload(0)
|
||||
let _1 := 0x00
|
||||
let _2 := 0x20
|
||||
mstore(_1, var_k)
|
||||
mstore(_2, _1)
|
||||
sstore(keccak256(_1, 0x40), 0x01)
|
||||
mstore(_1, var_k)
|
||||
mstore(_2, _1)
|
||||
sstore(add(keccak256(_1, 0x40), 0x01), 0x03)
|
||||
mstore(_1, var_k)
|
||||
mstore(_2, _1)
|
||||
sstore(add(keccak256(_1, 0x40), 2), 0x04)
|
||||
mstore(_1, var_k)
|
||||
mstore(_2, _1)
|
||||
sstore(add(keccak256(_1, 0x40), 0x03), 2)
|
||||
}
|
||||
// ----
|
||||
// step: equalStoreEliminator
|
||||
//
|
||||
// {
|
||||
// let var_k := calldataload(0)
|
||||
// let _1 := 0x00
|
||||
// let _2 := 0x20
|
||||
// mstore(_1, var_k)
|
||||
// mstore(_2, _1)
|
||||
// sstore(keccak256(_1, 0x40), 0x01)
|
||||
// sstore(add(keccak256(_1, 0x40), 0x01), 0x03)
|
||||
// sstore(add(keccak256(_1, 0x40), 2), 0x04)
|
||||
// sstore(add(keccak256(_1, 0x40), 0x03), 2)
|
||||
// }
|
@ -138,7 +138,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) == "flcCUnDvejsxIOoighFTLMRmVatrpud");
|
||||
BOOST_TEST(toString(chromosome) == "flcCUnDEvejsxIOoighFTLMRrmVatpud");
|
||||
}
|
||||
|
||||
BOOST_AUTO_TEST_CASE(optimisationSteps_should_translate_chromosomes_genes_to_optimisation_step_names)
|
||||
|
Loading…
Reference in New Issue
Block a user