diff --git a/Changelog.md b/Changelog.md index da977bc28..d27b336c8 100644 --- a/Changelog.md +++ b/Changelog.md @@ -5,6 +5,7 @@ Language Features: Compiler Features: + * Yul Optimizer: Remove ``mstore`` and ``sstore`` operations if the slot already contains the same value. diff --git a/docs/internals/optimizer.rst b/docs/internals/optimizer.rst index 19bca791e..a7eff7346 100644 --- a/docs/internals/optimizer.rst +++ b/docs/internals/optimizer.rst @@ -290,6 +290,7 @@ on the individual steps and their sequence below. - :ref:`conditional-unsimplifier`. - :ref:`control-flow-simplifier`. - :ref:`dead-code-eliminator`. +- :ref:`equal-store-eliminator`. - :ref:`equivalent-function-combiner`. - :ref:`expression-joiner`. - :ref:`expression-simplifier`. @@ -938,6 +939,22 @@ we require ForLoopInitRewriter to run before this step. Prerequisite: ForLoopInitRewriter, Function Hoister, Function Grouper +.. _equal-store-eliminator: + +EqualStoreEliminator +^^^^^^^^^^^^^^^^^^^^ + +This steps removes ``mstore(k, v)`` and ``sstore(k, v)`` calls if +there was a previous call to ``mstore(k, v)`` / ``sstore(k, v)``, +no other store in between and the values of ``k`` and ``v`` did not change. + +This simple step is effective if run after the SSA transform and the +Common Subexpression Eliminator, because SSA will make sure that the variables +will not change and the Common Subexpression Eliminator re-uses exactly the same +variable if the value is known to be the same. + +Prerequisites: Disambiguator, ForLoopInitRewriter + .. _unused-pruner: UnusedPruner diff --git a/libsolidity/interface/OptimiserSettings.h b/libsolidity/interface/OptimiserSettings.h index 5317acee4..e1c35e9ea 100644 --- a/libsolidity/interface/OptimiserSettings.h +++ b/libsolidity/interface/OptimiserSettings.h @@ -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 diff --git a/libyul/CMakeLists.txt b/libyul/CMakeLists.txt index 16b68535a..fde673e3d 100644 --- a/libyul/CMakeLists.txt +++ b/libyul/CMakeLists.txt @@ -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 diff --git a/libyul/optimiser/EqualStoreEliminator.cpp b/libyul/optimiser/EqualStoreEliminator.cpp new file mode 100644 index 000000000..dcba98ce4 --- /dev/null +++ b/libyul/optimiser/EqualStoreEliminator.cpp @@ -0,0 +1,70 @@ +/* + 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 +/** + * Optimisation stage that removes mstore and sstore operations if they store the same + * value that is already known to be in that slot. + */ + +#include + +#include +#include +#include +#include +#include + +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(&_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); +} diff --git a/libyul/optimiser/EqualStoreEliminator.h b/libyul/optimiser/EqualStoreEliminator.h new file mode 100644 index 000000000..796fcc538 --- /dev/null +++ b/libyul/optimiser/EqualStoreEliminator.h @@ -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 . +*/ +// 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 +#include + +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 _functionSideEffects + ): + DataFlowAnalyzer(_dialect, std::move(_functionSideEffects)) + {} + +protected: + using ASTModifier::visit; + void visit(Statement& _statement) override; + + std::set m_pendingRemovals; +}; + +} diff --git a/libyul/optimiser/OptimizerUtilities.cpp b/libyul/optimiser/OptimizerUtilities.cpp index 23596a745..ba06b2180 100644 --- a/libyul/optimiser/OptimizerUtilities.cpp +++ b/libyul/optimiser/OptimizerUtilities.cpp @@ -57,3 +57,18 @@ optional yul::toEVMInstruction(Dialect const& _dialect, Yul return builtin->instruction; return nullopt; } + +void StatementRemover::operator()(Block& _block) +{ + util::iterateReplacing( + _block.statements, + [&](Statement& _statement) -> std::optional> + { + if (m_toRemove.count(&_statement)) + return {vector{}}; + else + return nullopt; + } + ); + ASTModifier::operator()(_block); +} diff --git a/libyul/optimiser/OptimizerUtilities.h b/libyul/optimiser/OptimizerUtilities.h index d80b16316..b491e57e1 100644 --- a/libyul/optimiser/OptimizerUtilities.h +++ b/libyul/optimiser/OptimizerUtilities.h @@ -25,6 +25,7 @@ #include #include #include +#include #include @@ -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 toEVMInstruction(Dialect const& _dialect, YulString const& _name); +class StatementRemover: public ASTModifier +{ +public: + explicit StatementRemover(std::set const& _toRemove): m_toRemove(_toRemove) {} + + void operator()(Block& _block) override; +private: + std::set const& m_toRemove; +}; + } diff --git a/libyul/optimiser/Suite.cpp b/libyul/optimiser/Suite.cpp index 4012fa970..0f2194061 100644 --- a/libyul/optimiser/Suite.cpp +++ b/libyul/optimiser/Suite.cpp @@ -32,6 +32,7 @@ #include #include #include +#include #include #include #include @@ -204,6 +205,7 @@ map> const& OptimiserSuite::allSteps() ConditionalUnsimplifier, ControlFlowSimplifier, DeadCodeEliminator, + EqualStoreEliminator, EquivalentFunctionCombiner, ExpressionInliner, ExpressionJoiner, @@ -244,6 +246,7 @@ map const& OptimiserSuite::stepNameToAbbreviationMap() {ConditionalUnsimplifier::name, 'U'}, {ControlFlowSimplifier::name, 'n'}, {DeadCodeEliminator::name, 'D'}, + {EqualStoreEliminator::name, 'E'}, {EquivalentFunctionCombiner::name, 'v'}, {ExpressionInliner::name, 'e'}, {ExpressionJoiner::name, 'j'}, diff --git a/libyul/optimiser/UnusedAssignEliminator.cpp b/libyul/optimiser/UnusedAssignEliminator.cpp index 74dd599a1..273aa6b79 100644 --- a/libyul/optimiser/UnusedAssignEliminator.cpp +++ b/libyul/optimiser/UnusedAssignEliminator.cpp @@ -23,6 +23,7 @@ #include #include +#include #include #include diff --git a/libyul/optimiser/UnusedStoreBase.cpp b/libyul/optimiser/UnusedStoreBase.cpp index 27700bdf7..8e34d172f 100644 --- a/libyul/optimiser/UnusedStoreBase.cpp +++ b/libyul/optimiser/UnusedStoreBase.cpp @@ -156,18 +156,3 @@ void UnusedStoreBase::merge(TrackedStores& _target, vector&& _sou merge(_target, move(ts)); _source.clear(); } - -void StatementRemover::operator()(Block& _block) -{ - util::iterateReplacing( - _block.statements, - [&](Statement& _statement) -> std::optional> - { - if (m_toRemove.count(&_statement)) - return {vector{}}; - else - return nullopt; - } - ); - ASTModifier::operator()(_block); -} diff --git a/libyul/optimiser/UnusedStoreBase.h b/libyul/optimiser/UnusedStoreBase.h index 3bd4e4297..15dccb04a 100644 --- a/libyul/optimiser/UnusedStoreBase.h +++ b/libyul/optimiser/UnusedStoreBase.h @@ -105,14 +105,4 @@ protected: size_t m_forLoopNestingDepth = 0; }; -class StatementRemover: public ASTModifier -{ -public: - explicit StatementRemover(std::set const& _toRemove): m_toRemove(_toRemove) {} - - void operator()(Block& _block) override; -private: - std::set const& m_toRemove; -}; - } diff --git a/test/libsolidity/semanticTests/externalContracts/deposit_contract.sol b/test/libsolidity/semanticTests/externalContracts/deposit_contract.sol index ed9a07f7f..7f5799805 100644 --- a/test/libsolidity/semanticTests/externalContracts/deposit_contract.sol +++ b/test/libsolidity/semanticTests/externalContracts/deposit_contract.sol @@ -178,7 +178,7 @@ contract DepositContract is IDepositContract, ERC165 { // compileViaYul: also // ---- // constructor() -// gas irOptimized: 1558001 +// gas irOptimized: 1557137 // gas legacy: 2436584 // gas legacyOptimized: 1776483 // supportsInterface(bytes4): 0x0 -> 0 diff --git a/test/libsolidity/semanticTests/functionCall/mapping_array_internal_argument.sol b/test/libsolidity/semanticTests/functionCall/mapping_array_internal_argument.sol index f01a590eb..d3e2ba075 100644 --- a/test/libsolidity/semanticTests/functionCall/mapping_array_internal_argument.sol +++ b/test/libsolidity/semanticTests/functionCall/mapping_array_internal_argument.sol @@ -20,7 +20,7 @@ contract test { // compileViaYul: also // ---- // set(uint8,uint8,uint8,uint8,uint8): 1, 21, 22, 42, 43 -> 0, 0, 0, 0 -// gas irOptimized: 111965 +// gas irOptimized: 111896 // gas legacy: 113806 // gas legacyOptimized: 111781 // get(uint8): 1 -> 21, 22, 42, 43 diff --git a/test/libsolidity/semanticTests/structs/struct_copy.sol b/test/libsolidity/semanticTests/structs/struct_copy.sol index 0bbb6489e..f170dca3f 100644 --- a/test/libsolidity/semanticTests/structs/struct_copy.sol +++ b/test/libsolidity/semanticTests/structs/struct_copy.sol @@ -38,12 +38,12 @@ contract c { // compileViaYul: also // ---- // set(uint256): 7 -> true -// gas irOptimized: 110011 +// gas irOptimized: 110119 // gas legacy: 110616 // gas legacyOptimized: 110006 // retrieve(uint256): 7 -> 1, 3, 4, 2 // copy(uint256,uint256): 7, 8 -> true -// gas irOptimized: 118707 +// gas irOptimized: 118698 // gas legacy: 119166 // gas legacyOptimized: 118622 // retrieve(uint256): 7 -> 1, 3, 4, 2 diff --git a/test/libyul/YulOptimizerTestCommon.cpp b/test/libyul/YulOptimizerTestCommon.cpp index cc315c4b9..bb8458d78 100644 --- a/test/libyul/YulOptimizerTestCommon.cpp +++ b/test/libyul/YulOptimizerTestCommon.cpp @@ -28,6 +28,7 @@ #include #include #include +#include #include #include #include @@ -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); diff --git a/test/libyul/yulOptimizerTests/equalStoreEliminator/branching.yul b/test/libyul/yulOptimizerTests/equalStoreEliminator/branching.yul new file mode 100644 index 000000000..63f0d38e4 --- /dev/null +++ b/test/libyul/yulOptimizerTests/equalStoreEliminator/branching.yul @@ -0,0 +1,25 @@ +{ + let a := calldataload(0) + let b := 20 + sstore(a, b) + if calldataload(32) { + sstore(a, b) + pop(staticcall(0, 0, 0, 0, 0, 0)) + sstore(a, b) + } + sstore(a, b) +} +// ==== +// EVMVersion: >=byzantium +// ---- +// step: equalStoreEliminator +// +// { +// let a := calldataload(0) +// let b := 20 +// sstore(a, b) +// if calldataload(32) +// { +// pop(staticcall(0, 0, 0, 0, 0, 0)) +// } +// } diff --git a/test/libyul/yulOptimizerTests/equalStoreEliminator/forloop.yul b/test/libyul/yulOptimizerTests/equalStoreEliminator/forloop.yul new file mode 100644 index 000000000..ccd7dd40b --- /dev/null +++ b/test/libyul/yulOptimizerTests/equalStoreEliminator/forloop.yul @@ -0,0 +1,20 @@ +{ + let x := calldataload(0) + let y := calldataload(1) + + sstore(x, y) + for {let a := 1} lt(a, 10) {a := add(a, 1) } { + sstore(x, y) + } +} +// ---- +// step: equalStoreEliminator +// +// { +// let x := calldataload(0) +// let y := calldataload(1) +// sstore(x, y) +// let a := 1 +// for { } lt(a, 10) { a := add(a, 1) } +// { sstore(x, y) } +// } diff --git a/test/libyul/yulOptimizerTests/equalStoreEliminator/functionbody.yul b/test/libyul/yulOptimizerTests/equalStoreEliminator/functionbody.yul new file mode 100644 index 000000000..68839721c --- /dev/null +++ b/test/libyul/yulOptimizerTests/equalStoreEliminator/functionbody.yul @@ -0,0 +1,56 @@ +{ + f(calldataload(0), calldataload(32)) + h(calldataload(64), calldataload(96)) + + function f(a, b) { + // gets removed + sstore(a, b) + g() + sstore(a, b) + } + + function g() { + pop(staticcall(0, 0, 0, 0, 0, 0)) + } + + function h(a_, b_) { + // cannot be removed + sstore(a_, b_) + i() + sstore(a_, b_) + } + + function i() { + pop(delegatecall(0, 0, 0, 0, 0, 0)) + } + + +} +// ==== +// EVMVersion: >=byzantium +// ---- +// step: equalStoreEliminator +// +// { +// f(calldataload(0), calldataload(32)) +// h(calldataload(64), calldataload(96)) +// function f(a, b) +// { +// sstore(a, b) +// g() +// } +// function g() +// { +// pop(staticcall(0, 0, 0, 0, 0, 0)) +// } +// function h(a_, b_) +// { +// sstore(a_, b_) +// i() +// sstore(a_, b_) +// } +// function i() +// { +// pop(delegatecall(0, 0, 0, 0, 0, 0)) +// } +// } diff --git a/test/libyul/yulOptimizerTests/equalStoreEliminator/indirect_inferrence.yul b/test/libyul/yulOptimizerTests/equalStoreEliminator/indirect_inferrence.yul new file mode 100644 index 000000000..2b68e6310 --- /dev/null +++ b/test/libyul/yulOptimizerTests/equalStoreEliminator/indirect_inferrence.yul @@ -0,0 +1,24 @@ +{ + let x := calldataload(0) + let y := sload(x) + // both of these can be removed + sstore(x, y) + sstore(x, y) + + let a := x + let b := mload(a) + // both of these can be removed + mstore(a, b) + mstore(a, b) +} +// ==== +// EVMVersion: >=byzantium +// ---- +// step: equalStoreEliminator +// +// { +// let x := calldataload(0) +// let y := sload(x) +// let a := x +// let b := mload(a) +// } diff --git a/test/libyul/yulOptimizerTests/equalStoreEliminator/mstore_with_keccak.yul b/test/libyul/yulOptimizerTests/equalStoreEliminator/mstore_with_keccak.yul new file mode 100644 index 000000000..395fea6d0 --- /dev/null +++ b/test/libyul/yulOptimizerTests/equalStoreEliminator/mstore_with_keccak.yul @@ -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) +// } diff --git a/test/libyul/yulOptimizerTests/equalStoreEliminator/value_change.yul b/test/libyul/yulOptimizerTests/equalStoreEliminator/value_change.yul new file mode 100644 index 000000000..bd1e08bcd --- /dev/null +++ b/test/libyul/yulOptimizerTests/equalStoreEliminator/value_change.yul @@ -0,0 +1,18 @@ +{ + let x := calldataload(0) + let y := calldataload(32) + sstore(x, y) + y := calldataload(64) + // cannot be removed + sstore(x, y) +} +// ---- +// step: equalStoreEliminator +// +// { +// let x := calldataload(0) +// let y := calldataload(32) +// sstore(x, y) +// y := calldataload(64) +// sstore(x, y) +// } diff --git a/test/libyul/yulOptimizerTests/equalStoreEliminator/verbatim.yul b/test/libyul/yulOptimizerTests/equalStoreEliminator/verbatim.yul new file mode 100644 index 000000000..fcaddd121 --- /dev/null +++ b/test/libyul/yulOptimizerTests/equalStoreEliminator/verbatim.yul @@ -0,0 +1,27 @@ +{ + let a := calldataload(0) + let b := 20 + sstore(a, b) + if calldataload(32) { + sstore(a, b) + pop(staticcall(0, 0, 0, 0, 0, 0)) + verbatim_0i_0o("xyz") + } + sstore(a, b) +} +// ==== +// EVMVersion: >=byzantium +// ---- +// step: equalStoreEliminator +// +// { +// let a := calldataload(0) +// let b := 20 +// sstore(a, b) +// if calldataload(32) +// { +// pop(staticcall(0, 0, 0, 0, 0, 0)) +// verbatim_0i_0o("xyz") +// } +// sstore(a, b) +// } diff --git a/test/libyul/yulOptimizerTests/fullSuite/stack_compressor_msize.yul b/test/libyul/yulOptimizerTests/fullSuite/stack_compressor_msize.yul index d670f002f..13683e8e6 100644 --- a/test/libyul/yulOptimizerTests/fullSuite/stack_compressor_msize.yul +++ b/test/libyul/yulOptimizerTests/fullSuite/stack_compressor_msize.yul @@ -55,7 +55,6 @@ // sstore(0, 0) // sstore(2, _1) // extcodecopy(_1, msize(), _1, _1) -// sstore(0, 0) // sstore(3, _1) // } // function gcd(_a, _b) -> out diff --git a/test/libyul/yulOptimizerTests/fullSuite/unusedFunctionParameterPruner_loop.yul b/test/libyul/yulOptimizerTests/fullSuite/unusedFunctionParameterPruner_loop.yul index b531842db..ee648f80f 100644 --- a/test/libyul/yulOptimizerTests/fullSuite/unusedFunctionParameterPruner_loop.yul +++ b/test/libyul/yulOptimizerTests/fullSuite/unusedFunctionParameterPruner_loop.yul @@ -20,9 +20,7 @@ // f() // sstore(0, 1) // f() -// sstore(0, 1) // f() -// sstore(0, 1) // } // function f() // { diff --git a/test/libyul/yulOptimizerTests/fullSuite/unusedFunctionParameterPruner_simple.yul b/test/libyul/yulOptimizerTests/fullSuite/unusedFunctionParameterPruner_simple.yul index 531b1a6ce..68620cc96 100644 --- a/test/libyul/yulOptimizerTests/fullSuite/unusedFunctionParameterPruner_simple.yul +++ b/test/libyul/yulOptimizerTests/fullSuite/unusedFunctionParameterPruner_simple.yul @@ -18,9 +18,7 @@ // f() // sstore(0, 1) // f() -// sstore(0, 1) // f() -// sstore(0, 1) // } // function f() // { diff --git a/test/yulPhaser/Chromosome.cpp b/test/yulPhaser/Chromosome.cpp index 19616cde0..1b1e8bed5 100644 --- a/test/yulPhaser/Chromosome.cpp +++ b/test/yulPhaser/Chromosome.cpp @@ -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) == "flcCUnDEvejsxIOoighFTLMRmVatrpud"); } BOOST_AUTO_TEST_CASE(optimisationSteps_should_translate_chromosomes_genes_to_optimisation_step_names)