diff --git a/libyul/CMakeLists.txt b/libyul/CMakeLists.txt index 8cccb4457..432ddeb14 100644 --- a/libyul/CMakeLists.txt +++ b/libyul/CMakeLists.txt @@ -68,6 +68,8 @@ add_library(yul optimiser/CommonSubexpressionEliminator.h optimiser/ConditionalSimplifier.cpp optimiser/ConditionalSimplifier.h + optimiser/ConditionalUnsimplifier.cpp + optimiser/ConditionalUnsimplifier.h optimiser/ControlFlowSimplifier.cpp optimiser/ControlFlowSimplifier.h optimiser/DataFlowAnalyzer.cpp diff --git a/libyul/optimiser/ConditionalUnsimplifier.cpp b/libyul/optimiser/ConditionalUnsimplifier.cpp new file mode 100644 index 000000000..d1d167aa1 --- /dev/null +++ b/libyul/optimiser/ConditionalUnsimplifier.cpp @@ -0,0 +1,98 @@ +/* + 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 . +*/ +#include +#include +#include +#include +#include +#include +#include + +using namespace std; +using namespace dev; +using namespace yul; + +void ConditionalUnsimplifier::operator()(Switch& _switch) +{ + visit(*_switch.expression); + if (_switch.expression->type() != typeid(Identifier)) + { + ASTModifier::operator()(_switch); + return; + } + YulString expr = boost::get(*_switch.expression).name; + for (auto& _case: _switch.cases) + { + if (_case.value) + { + (*this)(*_case.value); + if ( + !_case.body.statements.empty() && + _case.body.statements.front().type() == typeid(Assignment) + ) + { + Assignment const& assignment = boost::get(_case.body.statements.front()); + if ( + assignment.variableNames.size() == 1 && + assignment.variableNames.front().name == expr && + assignment.value->type() == typeid(Literal) && + valueOfLiteral(boost::get(*assignment.value)) == valueOfLiteral(*_case.value) + ) + _case.body.statements.erase(_case.body.statements.begin()); + } + } + (*this)(_case.body); + } +} + +void ConditionalUnsimplifier::operator()(Block& _block) +{ + iterateReplacingWindow<2>( + _block.statements, + [&](Statement& _stmt1, Statement& _stmt2) -> std::optional> + { + visit(_stmt1); + if (_stmt1.type() == typeid(If)) + { + If& _if = boost::get(_stmt1); + if ( + _if.condition->type() == typeid(Identifier) && + !_if.body.statements.empty() + ) + { + YulString condition = boost::get(*_if.condition).name; + if ( + _stmt2.type() == typeid(Assignment) && + TerminationFinder(m_dialect).controlFlowKind(_if.body.statements.back()) != + TerminationFinder::ControlFlow::FlowOut + ) + { + Assignment const& assignment = boost::get(_stmt2); + if ( + assignment.variableNames.size() == 1 && + assignment.variableNames.front().name == condition && + assignment.value->type() == typeid(Literal) && + valueOfLiteral(boost::get(*assignment.value)) == 0 + ) + return {make_vector(std::move(_stmt1))}; + } + } + } + return {}; + } + ); +} diff --git a/libyul/optimiser/ConditionalUnsimplifier.h b/libyul/optimiser/ConditionalUnsimplifier.h new file mode 100644 index 000000000..16358c9f7 --- /dev/null +++ b/libyul/optimiser/ConditionalUnsimplifier.h @@ -0,0 +1,51 @@ +/* + 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 . +*/ +#pragma once + +#include +#include +#include +#include + +namespace yul +{ + +/** + * Reverse of conditional simplifier. + * + */ +class ConditionalUnsimplifier: public ASTModifier +{ +public: + static constexpr char const* name{"ConditionalUnsimplifier"}; + static void run(OptimiserStepContext& _context, Block& _ast) + { + ConditionalUnsimplifier{_context.dialect}(_ast); + } + + using ASTModifier::operator(); + void operator()(Switch& _switch) override; + void operator()(Block& _block) override; + +private: + explicit ConditionalUnsimplifier(Dialect const& _dialect): + m_dialect(_dialect) + {} + Dialect const& m_dialect; +}; + +} diff --git a/libyul/optimiser/Suite.cpp b/libyul/optimiser/Suite.cpp index d9b689729..b0f84c6bd 100644 --- a/libyul/optimiser/Suite.cpp +++ b/libyul/optimiser/Suite.cpp @@ -26,6 +26,7 @@ #include #include #include +#include #include #include #include @@ -98,9 +99,9 @@ void OptimiserSuite::run( EquivalentFunctionCombiner::name, UnusedPruner::name, BlockFlattener::name, - ConditionalSimplifier::name, ControlFlowSimplifier::name, LiteralRematerialiser::name, + ConditionalUnsimplifier::name, StructuralSimplifier::name, ControlFlowSimplifier::name, ForLoopConditionIntoBody::name, @@ -138,6 +139,7 @@ void OptimiserSuite::run( CommonSubexpressionEliminator::name, ConditionalSimplifier::name, LiteralRematerialiser::name, + ConditionalUnsimplifier::name, StructuralSimplifier::name, ForLoopConditionOutOfBody::name, ControlFlowSimplifier::name, @@ -207,13 +209,14 @@ void OptimiserSuite::run( // SSA plus simplify suite.runSequence({ ConditionalSimplifier::name, + LiteralRematerialiser::name, + ConditionalUnsimplifier::name, CommonSubexpressionEliminator::name, SSATransform::name, RedundantAssignEliminator::name, RedundantAssignEliminator::name, LoadResolver::name, ExpressionSimplifier::name, - LiteralRematerialiser::name, ForLoopConditionOutOfBody::name, StructuralSimplifier::name, BlockFlattener::name, @@ -324,6 +327,7 @@ map> const& OptimiserSuite::allSteps() BlockFlattener, CommonSubexpressionEliminator, ConditionalSimplifier, + ConditionalUnsimplifier, ControlFlowSimplifier, DeadCodeEliminator, EquivalentFunctionCombiner, diff --git a/test/libyul/yulOptimizerTests/fullSuite/aztec.yul b/test/libyul/yulOptimizerTests/fullSuite/aztec.yul index a47172d70..0b82de7e9 100644 --- a/test/libyul/yulOptimizerTests/fullSuite/aztec.yul +++ b/test/libyul/yulOptimizerTests/fullSuite/aztec.yul @@ -233,11 +233,15 @@ // ---- // { // { -// mstore(0x80, 7673901602397024137095011250362199966051872585513276903826533215767972925880) +// let _1 := 0x80 +// mstore(_1, 7673901602397024137095011250362199966051872585513276903826533215767972925880) // mstore(0xa0, 8489654445897228341090914135473290831551238522473825886865492707826370766375) -// let n := calldataload(add(0x04, calldataload(0x04))) -// let _1 := 0x30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f0000001 -// if gt(calldataload(0x24), n) +// let notes := add(0x04, calldataload(0x04)) +// let m := calldataload(0x24) +// let n := calldataload(notes) +// let _2 := 0x30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f0000001 +// let challenge := mod(calldataload(0x44), _2) +// if gt(m, n) // { // mstore(0x00, 404) // revert(0x00, 0x20) @@ -245,95 +249,70 @@ // let kn := calldataload(add(calldatasize(), not(191))) // mstore(0x2a0, caller()) // mstore(0x2c0, kn) -// mstore(0x2e0, calldataload(0x24)) -// kn := mulmod(sub(_1, kn), mod(calldataload(0x44), _1), _1) -// hashCommitments(add(0x04, calldataload(0x04)), n) -// let b := add(0x300, mul(n, 0x80)) +// mstore(0x2e0, m) +// kn := mulmod(sub(_2, kn), challenge, _2) +// hashCommitments(notes, n) +// let b := add(0x300, mul(n, _1)) // let i := 0 // for { } lt(i, n) { i := add(i, 0x01) } // { -// let _2 := add(calldataload(0x04), mul(i, 0xc0)) +// let _3 := add(calldataload(0x04), mul(i, 0xc0)) +// let noteIndex := add(_3, 0x24) // let k := 0 -// let a := calldataload(add(_2, 0x44)) -// let c := mod(calldataload(0x44), _1) -// let _3 := eq(add(i, 0x01), n) -// let _4 := _3 -// let _5 := _3 -// let _6 := _3 -// switch _3 +// let a := calldataload(add(_3, 0x44)) +// let c := challenge +// let _4 := add(i, 0x01) +// switch eq(_4, n) // case 1 { -// _3 := 0x01 -// _4 := _3 -// _5 := _3 -// _6 := _3 // k := kn -// if eq(calldataload(0x24), n) { k := sub(_1, kn) } +// if eq(m, n) { k := sub(_2, kn) } // } -// case 0 { -// _3 := 0 -// _4 := _3 -// _5 := _3 -// _6 := _3 -// k := calldataload(add(_2, 0x24)) -// } -// validateCommitment(add(_2, 0x24), k, a) -// let _7 := gt(add(i, 0x01), calldataload(0x24)) -// let _8 := _7 -// let _9 := _7 -// let _10 := _7 -// switch _7 +// case 0 { k := calldataload(noteIndex) } +// validateCommitment(noteIndex, k, a) +// switch gt(_4, m) // case 1 { -// _7 := 0x01 -// _8 := _7 -// _9 := _7 -// _10 := _7 -// kn := addmod(kn, sub(_1, k), _1) -// let x := mod(mload(0), _1) -// k := mulmod(k, x, _1) -// a := mulmod(a, x, _1) -// c := mulmod(c, x, _1) +// kn := addmod(kn, sub(_2, k), _2) +// let x := mod(mload(0), _2) +// k := mulmod(k, x, _2) +// a := mulmod(a, x, _2) +// c := mulmod(challenge, x, _2) // mstore(0, keccak256(0, 0x20)) // } -// case 0 { -// _7 := 0 -// _8 := _7 -// _9 := _7 -// _10 := _7 -// kn := addmod(kn, k, _1) -// } -// calldatacopy(0xe0, add(_2, 164), 0x40) -// calldatacopy(0x20, add(_2, 100), 0x40) -// mstore(0x120, sub(_1, c)) +// case 0 { kn := addmod(kn, k, _2) } +// let _5 := 0x40 +// calldatacopy(0xe0, add(_3, 164), _5) +// calldatacopy(0x20, add(_3, 100), _5) +// mstore(0x120, sub(_2, c)) // mstore(0x60, k) // mstore(0xc0, a) -// let result := call(gas(), 7, 0, 0xe0, 0x60, 0x1a0, 0x40) -// let result_1 := and(result, call(gas(), 7, 0, 0x20, 0x60, 0x120, 0x40)) -// let result_2 := and(result_1, call(gas(), 7, 0, 0x80, 0x60, 0x160, 0x40)) -// let result_3 := and(result_2, call(gas(), 6, 0, 0x120, 0x80, 0x160, 0x40)) -// result := and(result_3, call(gas(), 6, 0, 0x160, 0x80, b, 0x40)) -// if eq(i, calldataload(0x24)) +// let result := call(gas(), 7, 0, 0xe0, 0x60, 0x1a0, _5) +// let result_1 := and(result, call(gas(), 7, 0, 0x20, 0x60, 0x120, _5)) +// let result_2 := and(result_1, call(gas(), 7, 0, _1, 0x60, 0x160, _5)) +// let result_3 := and(result_2, call(gas(), 6, 0, 0x120, _1, 0x160, _5)) +// result := and(result_3, call(gas(), 6, 0, 0x160, _1, b, _5)) +// if eq(i, m) // { // mstore(0x260, mload(0x20)) -// mstore(0x280, mload(0x40)) +// mstore(0x280, mload(_5)) // mstore(0x1e0, mload(0xe0)) // mstore(0x200, sub(0x30644e72e131a029b85045b68181585d97816a916871ca8d3c208c16d87cfd47, mload(0x100))) // } -// if gt(i, calldataload(0x24)) +// if gt(i, m) // { // mstore(0x60, c) -// let result_4 := and(result, call(gas(), 7, 0, 0x20, 0x60, 0x220, 0x40)) -// let result_5 := and(result_4, call(gas(), 6, 0, 0x220, 0x80, 0x260, 0x40)) -// result := and(result_5, call(gas(), 6, 0, 0x1a0, 0x80, 0x1e0, 0x40)) +// let result_4 := and(result, call(gas(), 7, 0, 0x20, 0x60, 0x220, _5)) +// let result_5 := and(result_4, call(gas(), 6, 0, 0x220, _1, 0x260, _5)) +// result := and(result_5, call(gas(), 6, 0, 0x1a0, _1, 0x1e0, _5)) // } // if iszero(result) // { // mstore(0, 400) // revert(0, 0x20) // } -// b := add(b, 0x40) +// b := add(b, _5) // } -// if lt(calldataload(0x24), n) { validatePairing(0x64) } -// if iszero(eq(mod(keccak256(0x2a0, add(b, not(671))), _1), mod(calldataload(0x44), _1))) +// if lt(m, n) { validatePairing(0x64) } +// if iszero(eq(mod(keccak256(0x2a0, add(b, not(671))), _2), challenge)) // { // mstore(0, 404) // revert(0, 0x20) diff --git a/test/libyul/yulOptimizerTests/fullSuite/clear_after_if_continue.yul b/test/libyul/yulOptimizerTests/fullSuite/clear_after_if_continue.yul index f4147bc17..19816b445 100644 --- a/test/libyul/yulOptimizerTests/fullSuite/clear_after_if_continue.yul +++ b/test/libyul/yulOptimizerTests/fullSuite/clear_after_if_continue.yul @@ -12,16 +12,10 @@ // { // { // let y := mload(0x20) -// for { } -// and(y, 8) -// { -// if y { revert(0, 0) } -// y := 0 -// } +// for { } and(y, 8) { if y { revert(0, 0) } } // { // if y { continue } -// y := 0 -// sstore(1, y) +// sstore(1, 0) // } // if y { revert(0, 0) } // }