diff --git a/libevmasm/PeepholeOptimiser.cpp b/libevmasm/PeepholeOptimiser.cpp index df33e04e5125..40937e73f107 100644 --- a/libevmasm/PeepholeOptimiser.cpp +++ b/libevmasm/PeepholeOptimiser.cpp @@ -198,7 +198,7 @@ struct DoubleSwap: SimplePeepholeOptimizerMethod } }; -struct DoublePush: SimplePeepholeOptimizerMethod +struct DoublePush { static bool apply(OptimiserState& _state) { @@ -498,6 +498,99 @@ struct UnreachableCode } }; +struct DeduplicateNextTagSize3 : SimplePeepholeOptimizerMethod +{ + static bool applySimple( + AssemblyItem const& _precedingItem, + AssemblyItem const& _itemA, + AssemblyItem const& _itemB, + AssemblyItem const& _breakingItem, + AssemblyItem const& _tag, + AssemblyItem const& _itemC, + AssemblyItem const& _itemD, + AssemblyItem const& _breakingItem2, + std::back_insert_iterator _out + ) + { + if ( + _precedingItem.type() != Tag && + _itemA == _itemC && + _itemB == _itemD && + _breakingItem == _breakingItem2 && + _tag.type() == Tag && + SemanticInformation::terminatesControlFlow(_breakingItem) + ) + { + *_out = _precedingItem; + *_out = _tag; + *_out = _itemC; + *_out = _itemD; + *_out = _breakingItem2; + return true; + } + + return false; + } +}; + +struct DeduplicateNextTagSize2 : SimplePeepholeOptimizerMethod +{ + static bool applySimple( + AssemblyItem const& _precedingItem, + AssemblyItem const& _itemA, + AssemblyItem const& _breakingItem, + AssemblyItem const& _tag, + AssemblyItem const& _itemC, + AssemblyItem const& _breakingItem2, + std::back_insert_iterator _out + ) + { + if ( + _precedingItem.type() != Tag && + _itemA == _itemC && + _breakingItem == _breakingItem2 && + _tag.type() == Tag && + SemanticInformation::terminatesControlFlow(_breakingItem) + ) + { + *_out = _precedingItem; + *_out = _tag; + *_out = _itemC; + *_out = _breakingItem2; + return true; + } + + return false; + } +}; + +struct DeduplicateNextTagSize1 : SimplePeepholeOptimizerMethod +{ + static bool applySimple( + AssemblyItem const& _precedingItem, + AssemblyItem const& _breakingItem, + AssemblyItem const& _tag, + AssemblyItem const& _breakingItem2, + std::back_insert_iterator _out + ) + { + if ( + _precedingItem.type() != Tag && + _breakingItem == _breakingItem2 && + _tag.type() == Tag && + SemanticInformation::terminatesControlFlow(_breakingItem) + ) + { + *_out = _precedingItem; + *_out = _tag; + *_out = _breakingItem2; + return true; + } + + return false; + } +}; + void applyMethods(OptimiserState&) { assertThrow(false, OptimizerException, "Peephole optimizer failed to apply identity."); @@ -526,8 +619,8 @@ bool PeepholeOptimiser::optimise() applyMethods( state, PushPop(), OpPop(), OpStop(), OpReturnRevert(), DoublePush(), DoubleSwap(), CommutativeSwap(), SwapComparison(), - DupSwap(), IsZeroIsZeroJumpI(), EqIsZeroJumpI(), DoubleJump(), JumpToNext(), UnreachableCode(), - TagConjunctions(), TruthyAnd(), Identity() + DupSwap(), IsZeroIsZeroJumpI(), EqIsZeroJumpI(), DoubleJump(), JumpToNext(), UnreachableCode(), DeduplicateNextTagSize3(), + DeduplicateNextTagSize2(), DeduplicateNextTagSize1(), TagConjunctions(), TruthyAnd(), Identity() ); if (m_optimisedItems.size() < m_items.size() || ( m_optimisedItems.size() == m_items.size() && ( diff --git a/libevmasm/SemanticInformation.cpp b/libevmasm/SemanticInformation.cpp index 978fb7ce2aef..87d41f6c13a0 100644 --- a/libevmasm/SemanticInformation.cpp +++ b/libevmasm/SemanticInformation.cpp @@ -299,6 +299,13 @@ bool SemanticInformation::altersControlFlow(AssemblyItem const& _item) } } +bool SemanticInformation::terminatesControlFlow(AssemblyItem const& _item) +{ + if (_item.type() != evmasm::Operation) + return false; + return terminatesControlFlow(_item.instruction()); +} + bool SemanticInformation::terminatesControlFlow(Instruction _instruction) { switch (_instruction) diff --git a/libevmasm/SemanticInformation.h b/libevmasm/SemanticInformation.h index c3491cda8bd8..b62832f238af 100644 --- a/libevmasm/SemanticInformation.h +++ b/libevmasm/SemanticInformation.h @@ -84,6 +84,7 @@ struct SemanticInformation static bool isSwapInstruction(AssemblyItem const& _item); static bool isJumpInstruction(AssemblyItem const& _item); static bool altersControlFlow(AssemblyItem const& _item); + static bool terminatesControlFlow(AssemblyItem const& _item); static bool terminatesControlFlow(Instruction _instruction); static bool reverts(Instruction _instruction); /// @returns false if the value put on the stack by _item depends on anything else than diff --git a/test/libevmasm/Optimiser.cpp b/test/libevmasm/Optimiser.cpp index afc3c09fa9df..13fa58bf4bdf 100644 --- a/test/libevmasm/Optimiser.cpp +++ b/test/libevmasm/Optimiser.cpp @@ -1008,6 +1008,81 @@ BOOST_AUTO_TEST_CASE(clear_unreachable_code) ); } +BOOST_AUTO_TEST_CASE(deduplicateNextTagBlockSize3) +{ + AssemblyItems items{ + Instruction::JUMP, + u256(0), + u256(1), + Instruction::REVERT, + AssemblyItem(Tag, 2), + u256(0), + u256(1), + Instruction::REVERT + }; + + AssemblyItems expectation{ + Instruction::JUMP, + AssemblyItem(Tag, 2), + u256(0), + u256(1), + Instruction::REVERT + }; + PeepholeOptimiser peepOpt(items, solidity::test::CommonOptions::get().evmVersion()); + BOOST_REQUIRE(peepOpt.optimise()); + BOOST_CHECK_EQUAL_COLLECTIONS( + items.begin(), items.end(), + expectation.begin(), expectation.end() + ); +} + +BOOST_AUTO_TEST_CASE(deduplicateNextTagBlockSize2) +{ + AssemblyItems items{ + Instruction::JUMP, + u256(0), + Instruction::SELFDESTRUCT, + AssemblyItem(Tag, 2), + u256(0), + Instruction::SELFDESTRUCT + }; + + AssemblyItems expectation{ + Instruction::JUMP, + AssemblyItem(Tag, 2), + u256(0), + Instruction::SELFDESTRUCT + }; + PeepholeOptimiser peepOpt(items, solidity::test::CommonOptions::get().evmVersion()); + BOOST_REQUIRE(peepOpt.optimise()); + BOOST_CHECK_EQUAL_COLLECTIONS( + items.begin(), items.end(), + expectation.begin(), expectation.end() + ); +} + +BOOST_AUTO_TEST_CASE(deduplicateNextTagBlockSize1) +{ + AssemblyItems items{ + Instruction::JUMP, + Instruction::STOP, + AssemblyItem(Tag, 2), + Instruction::STOP + }; + + AssemblyItems expectation{ + Instruction::JUMP, + AssemblyItem(Tag, 2), + Instruction::STOP + }; + PeepholeOptimiser peepOpt(items, solidity::test::CommonOptions::get().evmVersion()); + BOOST_REQUIRE(peepOpt.optimise()); + BOOST_CHECK_EQUAL_COLLECTIONS( + items.begin(), items.end(), + expectation.begin(), expectation.end() + ); +} + BOOST_AUTO_TEST_CASE(peephole_double_push) { AssemblyItems items{