diff options
author | Daniel Kirchner <daniel@ekpyron.org> | 2019-01-16 18:44:45 +0800 |
---|---|---|
committer | Daniel Kirchner <daniel@ekpyron.org> | 2019-01-18 03:37:43 +0800 |
commit | 6de2d92f20d48d38797a628ee35e7615170cd63f (patch) | |
tree | 7d34e8c7655b8558061fd58de1bc26f6e44f1aaa | |
parent | 7c07e9498664c9b9dec6f332490d51c7a920de8b (diff) | |
download | dexon-solidity-6de2d92f20d48d38797a628ee35e7615170cd63f.tar.gz dexon-solidity-6de2d92f20d48d38797a628ee35e7615170cd63f.tar.zst dexon-solidity-6de2d92f20d48d38797a628ee35e7615170cd63f.zip |
Add SSAReverser to the yul optimiser.
-rw-r--r-- | libdevcore/CommonData.h | 53 | ||||
-rw-r--r-- | libyul/CMakeLists.txt | 2 | ||||
-rw-r--r-- | libyul/optimiser/SSAReverser.cpp | 71 | ||||
-rw-r--r-- | libyul/optimiser/SSAReverser.h | 54 | ||||
-rw-r--r-- | libyul/optimiser/Suite.cpp | 13 | ||||
-rw-r--r-- | test/libyul/YulOptimizerTest.cpp | 18 | ||||
-rw-r--r-- | test/libyul/yulOptimizerTests/fullSuite/abi2.yul | 38 | ||||
-rw-r--r-- | test/libyul/yulOptimizerTests/fullSuite/abi_example1.yul | 93 | ||||
-rw-r--r-- | test/libyul/yulOptimizerTests/fullSuite/aztec.yul | 15 | ||||
-rw-r--r-- | test/libyul/yulOptimizerTests/fullSuite/medium.yul | 6 | ||||
-rw-r--r-- | test/libyul/yulOptimizerTests/fullSuite/ssaReverse.yul | 62 | ||||
-rw-r--r-- | test/libyul/yulOptimizerTests/ssaAndBack/simple.yul | 18 | ||||
-rw-r--r-- | test/libyul/yulOptimizerTests/ssaAndBack/ssaReverse.yul | 45 | ||||
-rw-r--r-- | test/libyul/yulOptimizerTests/ssaReverser/abi_example.yul | 44 | ||||
-rw-r--r-- | test/libyul/yulOptimizerTests/ssaReverser/simple.yul | 14 | ||||
-rw-r--r-- | test/tools/yulopti.cpp | 6 |
16 files changed, 470 insertions, 82 deletions
diff --git a/libdevcore/CommonData.h b/libdevcore/CommonData.h index 1d668f26..98331936 100644 --- a/libdevcore/CommonData.h +++ b/libdevcore/CommonData.h @@ -263,6 +263,59 @@ void iterateReplacing(std::vector<T>& _vector, const F& _f) _vector = std::move(modifiedVector); } + +namespace detail +{ +template <typename T, typename F, std::size_t... I> +void iterateReplacingWindow(std::vector<T>& _vector, F const& _f, std::index_sequence<I...>) +{ + // Concept: _f must be Callable, must accept sizeof...(I) parameters of type T&, must return optional<vector<T>> + bool useModified = false; + std::vector<T> modifiedVector; + size_t i = 0; + for (; i + sizeof...(I) <= _vector.size(); ++i) + { + if (boost::optional<std::vector<T>> r = _f(_vector[i + I]...)) + { + if (!useModified) + { + std::move(_vector.begin(), _vector.begin() + i, back_inserter(modifiedVector)); + useModified = true; + } + modifiedVector += std::move(*r); + i += sizeof...(I) - 1; + } + else if (useModified) + modifiedVector.emplace_back(std::move(_vector[i])); + } + if (useModified) + { + for (; i < _vector.size(); ++i) + modifiedVector.emplace_back(std::move(_vector[i])); + _vector = std::move(modifiedVector); + } +} + +} + +/// Function that iterates over the vector @param _vector, +/// calling the function @param _f on sequences of @tparam N of its +/// elements. If @param _f returns a vector, these elements are replaced by +/// the returned vector and the iteration continues with the next @tparam N elements. +/// If the function does not return a vector, the iteration continues with an overlapping +/// sequence of @tparam N elements that starts with the second element of the previous +/// iteration. +/// During the iteration, the original vector is only valid +/// on the current element and after that. The actual replacement takes +/// place at the end, but already visited elements might be invalidated. +/// If nothing is replaced, no copy is performed. +template <std::size_t N, typename T, typename F> +void iterateReplacingWindow(std::vector<T>& _vector, F const& _f) +{ + // Concept: _f must be Callable, must accept N parameters of type T&, must return optional<vector<T>> + detail::iterateReplacingWindow(_vector, _f, std::make_index_sequence<N>{}); +} + /// @returns true iff @a _str passess the hex address checksum test. /// @param _strict if false, hex strings with only uppercase or only lowercase letters /// are considered valid. diff --git a/libyul/CMakeLists.txt b/libyul/CMakeLists.txt index 44af2fc3..259f43f8 100644 --- a/libyul/CMakeLists.txt +++ b/libyul/CMakeLists.txt @@ -80,6 +80,8 @@ add_library(yul optimiser/RedundantAssignEliminator.h optimiser/Rematerialiser.cpp optimiser/Rematerialiser.h + optimiser/SSAReverser.cpp + optimiser/SSAReverser.h optimiser/SSATransform.cpp optimiser/SSATransform.h optimiser/SSAValueTracker.cpp diff --git a/libyul/optimiser/SSAReverser.cpp b/libyul/optimiser/SSAReverser.cpp new file mode 100644 index 00000000..313a677a --- /dev/null +++ b/libyul/optimiser/SSAReverser.cpp @@ -0,0 +1,71 @@ +/* + 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/>. +*/ +#include <libyul/optimiser/SSAReverser.h> +#include <libyul/AsmData.h> +#include <libdevcore/CommonData.h> + +using namespace std; +using namespace dev; +using namespace yul; + +void SSAReverser::operator()(Block& _block) +{ + walkVector(_block.statements); + iterateReplacingWindow<2>( + _block.statements, + [&](Statement& _stmt1, Statement& _stmt2) -> boost::optional<vector<Statement>> + { + // Replaces + // let a_1 := E + // a := a_1 + // with + // a := E + // let a_1 := a + + auto* varDecl = boost::get<VariableDeclaration>(&_stmt1); + auto* assignment = boost::get<Assignment>(&_stmt2); + + if (!varDecl || !assignment) + return {}; + + auto* identifier = boost::get<Identifier>(assignment->value.get()); + + if ( + varDecl->variables.size() == 1 && + varDecl->value && + assignment->variableNames.size() == 1 && + identifier && + identifier->name == varDecl->variables.front().name + ) + { + vector<Statement> result; + result.emplace_back(Assignment{ + std::move(assignment->location), + assignment->variableNames, + std::move(varDecl->value) + }); + result.emplace_back(VariableDeclaration{ + std::move(varDecl->location), + std::move(varDecl->variables), + std::make_unique<Expression>(std::move(assignment->variableNames.front())) + }); + return { std::move(result) }; + } + return {}; + } + ); +} diff --git a/libyul/optimiser/SSAReverser.h b/libyul/optimiser/SSAReverser.h new file mode 100644 index 00000000..a4a11074 --- /dev/null +++ b/libyul/optimiser/SSAReverser.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 <http://www.gnu.org/licenses/>. +*/ +#pragma once + +#include <libyul/optimiser/ASTWalker.h> + +namespace yul +{ + +/** + * Reverses the SSA transformation. + * + * In particular, the SSA transform will rewrite + * + * a := E + * + * to + * + * let a_1 := E + * a := a_1 + * + * To undo this transformation, the SSAReverser changes this back to + * + * a := E + * let a_1 := a + * + * After that the CSE can replace references of a_1 by references to a, + * after which the unused pruner can remove the declaration of a_1. + * + * Prerequisites: None + * + */ +class SSAReverser: public ASTModifier +{ +public: + using ASTModifier::operator(); + void operator()(Block& _block) override; +}; + +} diff --git a/libyul/optimiser/Suite.cpp b/libyul/optimiser/Suite.cpp index 38c0bf49..63931554 100644 --- a/libyul/optimiser/Suite.cpp +++ b/libyul/optimiser/Suite.cpp @@ -35,6 +35,7 @@ #include <libyul/optimiser/UnusedPruner.h> #include <libyul/optimiser/ExpressionSimplifier.h> #include <libyul/optimiser/CommonSubexpressionEliminator.h> +#include <libyul/optimiser/SSAReverser.h> #include <libyul/optimiser/SSATransform.h> #include <libyul/optimiser/StructuralSimplifier.h> #include <libyul/optimiser/RedundantAssignEliminator.h> @@ -88,9 +89,10 @@ void OptimiserSuite::run( UnusedPruner::runUntilStabilised(_dialect, ast, reservedIdentifiers); CommonSubexpressionEliminator{_dialect}(ast); UnusedPruner::runUntilStabilised(_dialect, ast, reservedIdentifiers); - SSATransform::run(ast, dispenser); - RedundantAssignEliminator::run(_dialect, ast); - RedundantAssignEliminator::run(_dialect, ast); + + SSAReverser{}(ast); + CommonSubexpressionEliminator{_dialect}(ast); + UnusedPruner::runUntilStabilised(_dialect, ast, reservedIdentifiers); ExpressionJoiner::run(ast); ExpressionJoiner::run(ast); @@ -127,6 +129,11 @@ void OptimiserSuite::run( UnusedPruner::runUntilStabilised(_dialect, ast); ExpressionJoiner::run(ast); UnusedPruner::runUntilStabilised(_dialect, ast); + + SSAReverser{}(ast); + CommonSubexpressionEliminator{_dialect}(ast); + UnusedPruner::runUntilStabilised(_dialect, ast, reservedIdentifiers); + ExpressionJoiner::run(ast); Rematerialiser::run(_dialect, ast); UnusedPruner::runUntilStabilised(_dialect, ast); diff --git a/test/libyul/YulOptimizerTest.cpp b/test/libyul/YulOptimizerTest.cpp index 59cde4fe..914e0d6a 100644 --- a/test/libyul/YulOptimizerTest.cpp +++ b/test/libyul/YulOptimizerTest.cpp @@ -38,6 +38,7 @@ #include <libyul/optimiser/ExpressionSimplifier.h> #include <libyul/optimiser/UnusedPruner.h> #include <libyul/optimiser/ExpressionJoiner.h> +#include <libyul/optimiser/SSAReverser.h> #include <libyul/optimiser/SSATransform.h> #include <libyul/optimiser/RedundantAssignEliminator.h> #include <libyul/optimiser/StructuralSimplifier.h> @@ -223,6 +224,23 @@ bool YulOptimizerTest::run(ostream& _stream, string const& _linePrefix, bool con disambiguate(); EquivalentFunctionCombiner::run(*m_ast); } + else if (m_optimizerStep == "ssaReverser") + { + disambiguate(); + SSAReverser{}(*m_ast); + } + else if (m_optimizerStep == "ssaAndBack") + { + disambiguate(); + // apply SSA + NameDispenser nameDispenser{*m_dialect, *m_ast}; + SSATransform::run(*m_ast, nameDispenser); + RedundantAssignEliminator::run(*m_dialect, *m_ast); + // reverse SSA + SSAReverser{}(*m_ast); + CommonSubexpressionEliminator{*m_dialect}(*m_ast); + UnusedPruner::runUntilStabilised(*m_dialect, *m_ast); + } else if (m_optimizerStep == "fullSuite") OptimiserSuite::run(*m_dialect, *m_ast, *m_analysisInfo); else diff --git a/test/libyul/yulOptimizerTests/fullSuite/abi2.yul b/test/libyul/yulOptimizerTests/fullSuite/abi2.yul index 887399b6..41a52c67 100644 --- a/test/libyul/yulOptimizerTests/fullSuite/abi2.yul +++ b/test/libyul/yulOptimizerTests/fullSuite/abi2.yul @@ -1073,12 +1073,12 @@ // fullSuite // { // let _2 := mload(1) -// let _172 := mload(0) -// if slt(sub(_2, _172), 64) +// let _153 := mload(0) +// if slt(sub(_2, _153), 64) // { // revert(0, 0) // } -// sstore(0, and(calldataload(_172), 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF)) +// sstore(0, and(calldataload(_153), 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF)) // let x0, x1, x2, x3, x4 := abi_decode_tuple_t_addresst_uint256t_bytes_calldata_ptrt_enum$_Operation_$1949(mload(7), mload(8)) // sstore(x1, x0) // sstore(x3, x2) @@ -1093,40 +1093,40 @@ // value0_57 := and(calldataload(add(headStart_55, value4)), 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF) // value1_58 := calldataload(add(headStart_55, 32)) // let offset_62 := calldataload(add(headStart_55, 64)) -// let _220 := 0xffffffffffffffff -// if gt(offset_62, _220) +// let _201 := 0xffffffffffffffff +// if gt(offset_62, _201) // { // revert(value4, value4) // } -// let _222 := add(headStart_55, offset_62) -// if iszero(slt(add(_222, 0x1f), dataEnd_56)) +// let _203 := add(headStart_55, offset_62) +// if iszero(slt(add(_203, 0x1f), dataEnd_56)) // { // revert(value4, value4) // } -// let abi_decode_length_15_116 := calldataload(_222) -// if gt(abi_decode_length_15_116, _220) +// let abi_decode_length_15_246 := calldataload(_203) +// if gt(abi_decode_length_15_246, _201) // { // revert(value4, value4) // } -// if gt(add(add(_222, abi_decode_length_15_116), 32), dataEnd_56) +// if gt(add(add(_203, abi_decode_length_15_246), 32), dataEnd_56) // { // revert(value4, value4) // } -// value2_59 := add(_222, 32) -// value3 := abi_decode_length_15_116 -// let _225 := calldataload(add(headStart_55, 96)) -// if iszero(lt(_225, 3)) +// value2_59 := add(_203, 32) +// value3 := abi_decode_length_15_246 +// let _206 := calldataload(add(headStart_55, 96)) +// if iszero(lt(_206, 3)) // { // revert(value4, value4) // } -// value4 := _225 +// value4 := _206 // } // function abi_encode_tuple_t_bytes32_t_address_t_uint256_t_bytes32_t_enum$_Operation_$1949_t_uint256_t_uint256_t_uint256_t_address_t_address_t_uint256__to_t_bytes32_t_address_t_uint256_t_bytes32_t_uint8_t_uint256_t_uint256_t_uint256_t_address_t_address_t_uint256_(headStart_252, value10_253, value9_254, value8_255, value7_256, value6_257, value5_258, value4_259, value3_260, value2_261, value1_262, value0_263) -> tail_264 // { // tail_264 := add(headStart_252, 352) // mstore(headStart_252, value0_263) -// let _439 := 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -// mstore(add(headStart_252, 32), and(value1_262, _439)) +// let _413 := 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF +// mstore(add(headStart_252, 32), and(value1_262, _413)) // mstore(add(headStart_252, 64), value2_261) // mstore(add(headStart_252, 96), value3_260) // if iszero(lt(value4_259, 3)) @@ -1137,8 +1137,8 @@ // mstore(add(headStart_252, 160), value5_258) // mstore(add(headStart_252, 192), value6_257) // mstore(add(headStart_252, 224), value7_256) -// mstore(add(headStart_252, 256), and(value8_255, _439)) -// mstore(add(headStart_252, 288), and(value9_254, _439)) +// mstore(add(headStart_252, 256), and(value8_255, _413)) +// mstore(add(headStart_252, 288), and(value9_254, _413)) // mstore(add(headStart_252, 320), value10_253) // } // } diff --git a/test/libyul/yulOptimizerTests/fullSuite/abi_example1.yul b/test/libyul/yulOptimizerTests/fullSuite/abi_example1.yul index a8cac6c6..05a35673 100644 --- a/test/libyul/yulOptimizerTests/fullSuite/abi_example1.yul +++ b/test/libyul/yulOptimizerTests/fullSuite/abi_example1.yul @@ -460,12 +460,12 @@ // { // let _1 := 0x20 // let _2 := 0 -// let _268 := mload(_2) +// let _218 := mload(_2) // let abi_encode_pos := _1 -// let abi_encode_length_68 := mload(_268) +// let abi_encode_length_68 := mload(_218) // mstore(_1, abi_encode_length_68) // abi_encode_pos := 64 -// let abi_encode_srcPtr := add(_268, _1) +// let abi_encode_srcPtr := add(_218, _1) // let abi_encode_i_69 := _2 // for { // } @@ -474,66 +474,65 @@ // abi_encode_i_69 := add(abi_encode_i_69, 1) // } // { -// let _668 := mload(abi_encode_srcPtr) -// let abi_encode_pos_71_760 := abi_encode_pos -// let abi_encode_srcPtr_73_762 := _668 -// let abi_encode_i_74_763 := _2 +// let _580 := mload(abi_encode_srcPtr) +// let abi_encode_pos_71_672 := abi_encode_pos +// let abi_encode_srcPtr_73_674 := _580 +// let abi_encode_i_74_675 := _2 // for { // } -// lt(abi_encode_i_74_763, 0x3) +// lt(abi_encode_i_74_675, 0x3) // { -// abi_encode_i_74_763 := add(abi_encode_i_74_763, 1) +// abi_encode_i_74_675 := add(abi_encode_i_74_675, 1) // } // { -// mstore(abi_encode_pos_71_760, and(mload(abi_encode_srcPtr_73_762), 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF)) -// abi_encode_srcPtr_73_762 := add(abi_encode_srcPtr_73_762, _1) -// abi_encode_pos_71_760 := add(abi_encode_pos_71_760, _1) +// mstore(abi_encode_pos_71_672, and(mload(abi_encode_srcPtr_73_674), 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF)) +// abi_encode_srcPtr_73_674 := add(abi_encode_srcPtr_73_674, _1) +// abi_encode_pos_71_672 := add(abi_encode_pos_71_672, _1) // } // abi_encode_srcPtr := add(abi_encode_srcPtr, _1) // abi_encode_pos := add(abi_encode_pos, 0x60) // } -// let _270 := mload(64) -// let _271 := mload(_1) -// if slt(sub(_270, _271), 128) +// let _220 := mload(64) +// let _221 := mload(_1) +// if slt(sub(_220, _221), 128) // { // revert(_2, _2) // } -// let abi_decode_offset_64 := calldataload(add(_271, 64)) +// let abi_decode_offset_64 := calldataload(add(_221, 64)) // let abi_decode__74 := 0xffffffffffffffff // if gt(abi_decode_offset_64, abi_decode__74) // { // revert(_2, _2) // } -// let abi_decode_value2_367 := abi_decode_t_array$_t_uint256_$dyn_memory_ptr(add(_271, abi_decode_offset_64), _270) -// let abi_decode_offset_65 := calldataload(add(_271, 96)) +// let abi_decode_value2_317 := abi_decode_t_array$_t_uint256_$dyn_memory_ptr(add(_221, abi_decode_offset_64), _220) +// let abi_decode_offset_65 := calldataload(add(_221, 96)) // if gt(abi_decode_offset_65, abi_decode__74) // { // revert(_2, _2) // } -// let abi_decode_value3_368 := abi_decode_t_array$_t_array$_t_uint256_$2_memory_$dyn_memory_ptr(add(_271, abi_decode_offset_65), _270) -// sstore(calldataload(_271), calldataload(add(_271, _1))) -// sstore(abi_decode_value2_367, abi_decode_value3_368) +// let abi_decode_value3_318 := abi_decode_t_array$_t_array$_t_uint256_$2_memory_$dyn_memory_ptr(add(_221, abi_decode_offset_65), _220) +// sstore(calldataload(_221), calldataload(add(_221, _1))) +// sstore(abi_decode_value2_317, abi_decode_value3_318) // sstore(_2, abi_encode_pos) // function abi_decode_t_array$_t_array$_t_uint256_$2_memory_$dyn_memory_ptr(offset_3, end_4) -> array_5 // { // if iszero(slt(add(offset_3, 0x1f), end_4)) // { -// revert(0, 0) +// revert(array_5, array_5) // } // let length_6 := calldataload(offset_3) -// let array_5_115 := allocateMemory(array_allocation_size_t_array$_t_address_$dyn_memory(length_6)) -// array_5 := array_5_115 -// let dst_7 := array_5_115 -// mstore(array_5_115, length_6) +// array_5 := allocateMemory(array_allocation_size_t_array$_t_address_$dyn_memory(length_6)) +// let dst_7 := array_5 +// mstore(array_5, length_6) // let _16 := 0x20 -// dst_7 := add(array_5_115, _16) +// dst_7 := add(array_5, _16) // let src_8 := add(offset_3, _16) // if gt(add(add(offset_3, mul(length_6, 0x40)), _16), end_4) // { // revert(0, 0) // } -// let i_9_346 := 0 -// let i_9 := i_9_346 +// let i_9_296 := 0 +// let i_9 := i_9_296 // for { // } // lt(i_9, length_6) @@ -543,17 +542,17 @@ // { // if iszero(slt(add(src_8, 0x1f), end_4)) // { -// revert(i_9_346, i_9_346) +// revert(i_9_296, i_9_296) // } -// let abi_decode_array_13_124 := allocateMemory(array_allocation_size_t_array$_t_uint256_$2_memory(0x2)) -// let abi_decode_dst_15 := abi_decode_array_13_124 +// let abi_decode_array_13_300 := allocateMemory(array_allocation_size_t_array$_t_uint256_$2_memory(0x2)) +// let abi_decode_dst_15 := abi_decode_array_13_300 // let abi_decode_src_16 := src_8 -// let abi_decode__289 := add(src_8, 0x40) -// if gt(abi_decode__289, end_4) +// let abi_decode__239 := add(src_8, 0x40) +// if gt(abi_decode__239, end_4) // { -// revert(i_9_346, i_9_346) +// revert(i_9_296, i_9_296) // } -// let abi_decode_i_17 := i_9_346 +// let abi_decode_i_17 := i_9_296 // for { // } // lt(abi_decode_i_17, 0x2) @@ -565,24 +564,23 @@ // abi_decode_dst_15 := add(abi_decode_dst_15, _16) // abi_decode_src_16 := add(abi_decode_src_16, _16) // } -// mstore(dst_7, abi_decode_array_13_124) +// mstore(dst_7, abi_decode_array_13_300) // dst_7 := add(dst_7, _16) -// src_8 := abi_decode__289 +// src_8 := abi_decode__239 // } // } // function abi_decode_t_array$_t_uint256_$dyn_memory_ptr(offset_27, end_28) -> array_29 // { // if iszero(slt(add(offset_27, 0x1f), end_28)) // { -// revert(0, 0) +// revert(array_29, array_29) // } // let length_30 := calldataload(offset_27) -// let array_29_131 := allocateMemory(array_allocation_size_t_array$_t_address_$dyn_memory(length_30)) -// array_29 := array_29_131 -// let dst_31 := array_29_131 -// mstore(array_29_131, length_30) +// array_29 := allocateMemory(array_allocation_size_t_array$_t_address_$dyn_memory(length_30)) +// let dst_31 := array_29 +// mstore(array_29, length_30) // let _52 := 0x20 -// dst_31 := add(array_29_131, _52) +// dst_31 := add(array_29, _52) // let src_32 := add(offset_27, _52) // if gt(add(add(offset_27, mul(length_30, _52)), _52), end_28) // { @@ -603,10 +601,9 @@ // } // function allocateMemory(size) -> memPtr // { -// let memPtr_157 := mload(64) -// memPtr := memPtr_157 -// let newFreePtr := add(memPtr_157, size) -// if or(gt(newFreePtr, 0xffffffffffffffff), lt(newFreePtr, memPtr_157)) +// memPtr := mload(64) +// let newFreePtr := add(memPtr, size) +// if or(gt(newFreePtr, 0xffffffffffffffff), lt(newFreePtr, memPtr)) // { // revert(0, 0) // } diff --git a/test/libyul/yulOptimizerTests/fullSuite/aztec.yul b/test/libyul/yulOptimizerTests/fullSuite/aztec.yul index fc09b5d9..e05a3c92 100644 --- a/test/libyul/yulOptimizerTests/fullSuite/aztec.yul +++ b/test/libyul/yulOptimizerTests/fullSuite/aztec.yul @@ -264,10 +264,10 @@ // } // { // let validateJo__34 := 0x20 -// let validateJo__373 := add(validateJo__10, mul(validateJo_i, 0xc0)) -// let validateJo_noteIndex := add(validateJo__373, 0x24) +// let validateJo__351 := add(validateJo__10, mul(validateJo_i, 0xc0)) +// let validateJo_noteIndex := add(validateJo__351, 0x24) // let validateJo_k := validateJo_i_290 -// let validateJo_a_292 := calldataload(add(validateJo__373, 0x44)) +// let validateJo_a_292 := calldataload(add(validateJo__351, 0x44)) // let validateJo_a := validateJo_a_292 // let validateJo_c := validateJo_challenge // let validateJo__39 := add(validateJo_i, 0x01) @@ -296,8 +296,8 @@ // validateJo_kn := addmod(validateJo_kn, validateJo_k, validateJo_gen_order) // } // let validateJo__52 := 0x40 -// calldatacopy(0xe0, add(validateJo__373, 164), validateJo__52) -// calldatacopy(validateJo__34, add(validateJo__373, 100), validateJo__52) +// calldatacopy(0xe0, add(validateJo__351, 164), validateJo__52) +// calldatacopy(validateJo__34, add(validateJo__351, 100), validateJo__52) // let validateJo__61 := 0x120 // mstore(validateJo__61, sub(validateJo_gen_order, validateJo_c)) // let validateJo__62 := 0x60 @@ -310,8 +310,7 @@ // let validateJo__80 := 0x160 // let validateJo_result_304 := and(validateJo_result_303, call(gas(), 7, validateJo_i_290, validateJo__6, validateJo__62, validateJo__80, validateJo__52)) // let validateJo_result_305 := and(validateJo_result_304, call(gas(), 6, validateJo_i_290, validateJo__61, validateJo__6, validateJo__80, validateJo__52)) -// let validateJo_result_306 := and(validateJo_result_305, call(gas(), 6, validateJo_i_290, validateJo__80, validateJo__6, validateJo_b, validateJo__52)) -// validateJo_result := validateJo_result_306 +// validateJo_result := and(validateJo_result_305, call(gas(), 6, validateJo_i_290, validateJo__80, validateJo__6, validateJo_b, validateJo__52)) // if eq(validateJo_i, validateJo_m) // { // mstore(0x260, mload(validateJo__34)) @@ -323,7 +322,7 @@ // { // mstore(validateJo__62, validateJo_c) // let validateJo__120 := 0x220 -// let validateJo_result_307 := and(validateJo_result_306, call(gas(), 7, validateJo_i_290, validateJo__34, validateJo__62, validateJo__120, validateJo__52)) +// let validateJo_result_307 := and(validateJo_result, call(gas(), 7, validateJo_i_290, validateJo__34, validateJo__62, validateJo__120, validateJo__52)) // let validateJo_result_308 := and(validateJo_result_307, call(gas(), 6, validateJo_i_290, validateJo__120, validateJo__6, 0x260, validateJo__52)) // validateJo_result := and(validateJo_result_308, call(gas(), 6, validateJo_i_290, validateJo__65, validateJo__6, 0x1e0, validateJo__52)) // } diff --git a/test/libyul/yulOptimizerTests/fullSuite/medium.yul b/test/libyul/yulOptimizerTests/fullSuite/medium.yul index b10c6c69..1d07cd03 100644 --- a/test/libyul/yulOptimizerTests/fullSuite/medium.yul +++ b/test/libyul/yulOptimizerTests/fullSuite/medium.yul @@ -21,8 +21,8 @@ // { // let allocate__19 := 0x40 // mstore(allocate__19, add(mload(allocate__19), 0x20)) -// let allocate_p_24_41 := mload(allocate__19) -// mstore(allocate__19, add(allocate_p_24_41, allocate__19)) -// mstore(add(allocate_p_24_41, 96), 2) +// let allocate_p_35_39 := mload(allocate__19) +// mstore(allocate__19, add(allocate_p_35_39, allocate__19)) +// mstore(add(allocate_p_35_39, 96), 2) // mstore(allocate__19, 0x20) // } diff --git a/test/libyul/yulOptimizerTests/fullSuite/ssaReverse.yul b/test/libyul/yulOptimizerTests/fullSuite/ssaReverse.yul new file mode 100644 index 00000000..204b444e --- /dev/null +++ b/test/libyul/yulOptimizerTests/fullSuite/ssaReverse.yul @@ -0,0 +1,62 @@ +{ + // This is an abi decode function with the SSA transform applied once. + // This test is supposed to verify that the SSA transform is correctly reversed by the full suite. + function abi_decode_t_bytes_calldata_ptr(offset_12, end_13) -> arrayPos_14, length_15 + { + if iszero(slt(add(offset_12, 0x1f), end_13)) + { + revert(0, 0) + } + let length_15_1 := calldataload(offset_12) + length_15 := length_15_1 + if gt(length_15_1, 0xffffffffffffffff) + { + revert(0, 0) + } + let arrayPos_14_2 := add(offset_12, 0x20) + arrayPos_14 := arrayPos_14_2 + if gt(add(arrayPos_14_2, mul(length_15_1, 0x1)), end_13) + { + revert(0, 0) + } + } + + // prevent inlining + let a,b := abi_decode_t_bytes_calldata_ptr(mload(0),mload(1)) + a,b := abi_decode_t_bytes_calldata_ptr(a,b) + a,b := abi_decode_t_bytes_calldata_ptr(a,b) + a,b := abi_decode_t_bytes_calldata_ptr(a,b) + a,b := abi_decode_t_bytes_calldata_ptr(a,b) + a,b := abi_decode_t_bytes_calldata_ptr(a,b) + a,b := abi_decode_t_bytes_calldata_ptr(a,b) + mstore(a,b) +} +// ---- +// fullSuite +// { +// let a, b := abi_decode_t_bytes_calldata_ptr(mload(0), mload(1)) +// a, b := abi_decode_t_bytes_calldata_ptr(a, b) +// a, b := abi_decode_t_bytes_calldata_ptr(a, b) +// a, b := abi_decode_t_bytes_calldata_ptr(a, b) +// a, b := abi_decode_t_bytes_calldata_ptr(a, b) +// a, b := abi_decode_t_bytes_calldata_ptr(a, b) +// a, b := abi_decode_t_bytes_calldata_ptr(a, b) +// mstore(a, b) +// function abi_decode_t_bytes_calldata_ptr(offset_12, end_13) -> arrayPos_14, length_15 +// { +// if iszero(slt(add(offset_12, 0x1f), end_13)) +// { +// revert(0, 0) +// } +// length_15 := calldataload(offset_12) +// if gt(length_15, 0xffffffffffffffff) +// { +// revert(0, 0) +// } +// arrayPos_14 := add(offset_12, 0x20) +// if gt(add(add(offset_12, length_15), 0x20), end_13) +// { +// revert(0, 0) +// } +// } +// } diff --git a/test/libyul/yulOptimizerTests/ssaAndBack/simple.yul b/test/libyul/yulOptimizerTests/ssaAndBack/simple.yul new file mode 100644 index 00000000..23c433d1 --- /dev/null +++ b/test/libyul/yulOptimizerTests/ssaAndBack/simple.yul @@ -0,0 +1,18 @@ +{ + let a := mload(0) + a := mload(1) + a := mload(2) + a := mload(3) + a := mload(4) + mstore(a, 0) +} +// ---- +// ssaAndBack +// { +// pop(mload(0)) +// pop(mload(1)) +// pop(mload(2)) +// pop(mload(3)) +// let a_5 := mload(4) +// mstore(a_5, 0) +// } diff --git a/test/libyul/yulOptimizerTests/ssaAndBack/ssaReverse.yul b/test/libyul/yulOptimizerTests/ssaAndBack/ssaReverse.yul new file mode 100644 index 00000000..d2ba6471 --- /dev/null +++ b/test/libyul/yulOptimizerTests/ssaAndBack/ssaReverse.yul @@ -0,0 +1,45 @@ +{ + function abi_decode_t_bytes_calldata_ptr(offset_12, end_13) -> arrayPos_14, length_15 + { + if iszero(slt(add(offset_12, 0x1f), end_13)) + { + revert(0, 0) + } + length_15 := calldataload(offset_12) + if gt(length_15, 0xffffffffffffffff) + { + revert(0, 0) + } + arrayPos_14 := add(offset_12, 0x20) + if gt(add(add(offset_12, length_15), 0x20), end_13) + { + revert(0, 0) + } + } + // prevent removal of the function + let a,b := abi_decode_t_bytes_calldata_ptr(mload(0),mload(1)) + mstore(a,b) +} +// ---- +// ssaAndBack +// { +// function abi_decode_t_bytes_calldata_ptr(offset_12, end_13) -> arrayPos_14, length_15 +// { +// if iszero(slt(add(offset_12, 0x1f), end_13)) +// { +// revert(arrayPos_14, arrayPos_14) +// } +// length_15 := calldataload(offset_12) +// if gt(length_15, 0xffffffffffffffff) +// { +// revert(arrayPos_14, arrayPos_14) +// } +// arrayPos_14 := add(offset_12, 0x20) +// if gt(add(add(offset_12, length_15), 0x20), end_13) +// { +// revert(0, 0) +// } +// } +// let a, b := abi_decode_t_bytes_calldata_ptr(mload(0), mload(1)) +// mstore(a, b) +// } diff --git a/test/libyul/yulOptimizerTests/ssaReverser/abi_example.yul b/test/libyul/yulOptimizerTests/ssaReverser/abi_example.yul new file mode 100644 index 00000000..923a42ba --- /dev/null +++ b/test/libyul/yulOptimizerTests/ssaReverser/abi_example.yul @@ -0,0 +1,44 @@ +{ + function abi_decode_t_bytes_calldata_ptr(offset_12, end_13) -> arrayPos_14, length_15 + { + if iszero(slt(add(offset_12, 0x1f), end_13)) + { + revert(0, 0) + } + let length_15_1 := calldataload(offset_12) + length_15 := length_15_1 + if gt(length_15_1, 0xffffffffffffffff) + { + revert(0, 0) + } + let arrayPos_14_2 := add(offset_12, 0x20) + arrayPos_14 := arrayPos_14_2 + if gt(add(arrayPos_14_2, mul(length_15_1, 0x1)), end_13) + { + revert(0, 0) + } + } +} +// ---- +// ssaReverser +// { +// function abi_decode_t_bytes_calldata_ptr(offset_12, end_13) -> arrayPos_14, length_15 +// { +// if iszero(slt(add(offset_12, 0x1f), end_13)) +// { +// revert(0, 0) +// } +// length_15 := calldataload(offset_12) +// let length_15_1 := length_15 +// if gt(length_15_1, 0xffffffffffffffff) +// { +// revert(0, 0) +// } +// arrayPos_14 := add(offset_12, 0x20) +// let arrayPos_14_2 := arrayPos_14 +// if gt(add(arrayPos_14_2, mul(length_15_1, 0x1)), end_13) +// { +// revert(0, 0) +// } +// } +// } diff --git a/test/libyul/yulOptimizerTests/ssaReverser/simple.yul b/test/libyul/yulOptimizerTests/ssaReverser/simple.yul new file mode 100644 index 00000000..eba1f5f1 --- /dev/null +++ b/test/libyul/yulOptimizerTests/ssaReverser/simple.yul @@ -0,0 +1,14 @@ +{ + let a := mload(1) + let a_1 := mload(0) + a := a_1 + mstore(a_1, 0) +} +// ---- +// ssaReverser +// { +// let a := mload(1) +// a := mload(0) +// let a_1 := a +// mstore(a_1, 0) +// } diff --git a/test/tools/yulopti.cpp b/test/tools/yulopti.cpp index efd1ba05..07ebfc7d 100644 --- a/test/tools/yulopti.cpp +++ b/test/tools/yulopti.cpp @@ -46,6 +46,7 @@ #include <libyul/optimiser/UnusedPruner.h> #include <libyul/optimiser/ExpressionJoiner.h> #include <libyul/optimiser/RedundantAssignEliminator.h> +#include <libyul/optimiser/SSAReverser.h> #include <libyul/optimiser/SSATransform.h> #include <libyul/optimiser/StructuralSimplifier.h> #include <libyul/optimiser/VarDeclInitializer.h> @@ -129,7 +130,7 @@ public: cout << "(q)quit/(f)flatten/(c)se/initialize var(d)ecls/(x)plit/(j)oin/(g)rouper/(h)oister/" << endl; cout << " (e)xpr inline/(i)nline/(s)implify/(u)nusedprune/ss(a) transform/" << endl; cout << " (r)edundant assign elim./re(m)aterializer/f(o)r-loop-pre-rewriter/" << endl; - cout << " s(t)ructural simplifier/equi(v)alent function combiner? " << endl; + cout << " s(t)ructural simplifier/equi(v)alent function combiner/ssa re(V)erser? " << endl; cout.flush(); int option = readStandardInputChar(); cout << ' ' << char(option) << endl; @@ -188,6 +189,9 @@ public: case 'v': EquivalentFunctionCombiner::run(*m_ast); break; + case 'V': + SSAReverser{}(*m_ast); + break; default: cout << "Unknown option." << endl; } |