diff --git a/libevmcore/Assembly.cpp b/libevmcore/Assembly.cpp index abe932282..0301c9325 100644 --- a/libevmcore/Assembly.cpp +++ b/libevmcore/Assembly.cpp @@ -187,18 +187,7 @@ Assembly& Assembly::optimise(bool _enable) { if (!_enable) return *this; - std::vector>> rules = - { - { { Push, Instruction::POP }, [](AssemblyItemsConstRef) -> AssemblyItems { return {}; } }, - { { PushTag, Instruction::POP }, [](AssemblyItemsConstRef) -> AssemblyItems { return {}; } }, - { { PushString, Instruction::POP }, [](AssemblyItemsConstRef) -> AssemblyItems { return {}; } }, - { { PushSub, Instruction::POP }, [](AssemblyItemsConstRef) -> AssemblyItems { return {}; } }, - { { PushSubSize, Instruction::POP }, [](AssemblyItemsConstRef) -> AssemblyItems { return {}; } }, - { { PushProgramSize, Instruction::POP }, [](AssemblyItemsConstRef) -> AssemblyItems { return {}; } }, - { { Push, PushTag, Instruction::JUMPI }, [](AssemblyItemsConstRef m) -> AssemblyItems { if (m[0].data()) return { m[1], Instruction::JUMP }; else return {}; } }, - { { Instruction::ISZERO, Instruction::ISZERO }, [](AssemblyItemsConstRef) -> AssemblyItems { return {}; } }, - }; - + std::vector>> rules; // jump to next instruction rules.push_back({ { PushTag, Instruction::JUMP, Tag }, [](AssemblyItemsConstRef m) -> AssemblyItems { if (m[0].m_data == m[2].m_data) return {m[2]}; else return m.toVector(); }}); @@ -235,8 +224,6 @@ Assembly& Assembly::optimise(bool _enable) *orig = move(*moveIter); iter = m_items.erase(orig, iter); } - if (iter != m_items.end()) - ++iter; } for (unsigned i = 0; i < m_items.size(); ++i) diff --git a/libevmcore/CommonSubexpressionEliminator.cpp b/libevmcore/CommonSubexpressionEliminator.cpp index 1d2a71836..cc2fdc8bd 100644 --- a/libevmcore/CommonSubexpressionEliminator.cpp +++ b/libevmcore/CommonSubexpressionEliminator.cpp @@ -32,6 +32,8 @@ using namespace dev::eth; vector CommonSubexpressionEliminator::getOptimizedItems() { + optimizeBreakingItem(); + map initialStackContents; map targetStackContents; int minHeight = m_stackHeight + 1; @@ -45,10 +47,13 @@ vector CommonSubexpressionEliminator::getOptimizedItems() // Debug info: //stream(cout, initialStackContents, targetStackContents); - return CSECodeGenerator(m_expressionClasses, m_storeOperations).generateCode( + AssemblyItems items = CSECodeGenerator(m_expressionClasses, m_storeOperations).generateCode( initialStackContents, targetStackContents ); + if (m_breakingItem) + items.push_back(*m_breakingItem); + return items; } ostream& CommonSubexpressionEliminator::stream( @@ -91,12 +96,12 @@ ostream& CommonSubexpressionEliminator::stream( return _out; } -void CommonSubexpressionEliminator::feedItem(AssemblyItem const& _item) +void CommonSubexpressionEliminator::feedItem(AssemblyItem const& _item, bool _copyItem) { if (_item.type() != Operation) { assertThrow(_item.deposit() == 1, InvalidDeposit, ""); - setStackElement(++m_stackHeight, m_expressionClasses.find(_item, {}, false)); + setStackElement(++m_stackHeight, m_expressionClasses.find(_item, {}, _copyItem)); } else { @@ -126,12 +131,38 @@ void CommonSubexpressionEliminator::feedItem(AssemblyItem const& _item) else if (_item.instruction() == Instruction::MLOAD) setStackElement(m_stackHeight + _item.deposit(), loadFromMemory(arguments[0])); else - setStackElement(m_stackHeight + _item.deposit(), m_expressionClasses.find(_item, arguments, false)); + setStackElement(m_stackHeight + _item.deposit(), m_expressionClasses.find(_item, arguments, _copyItem)); } m_stackHeight += _item.deposit(); } } +void CommonSubexpressionEliminator::optimizeBreakingItem() +{ + if (!m_breakingItem || *m_breakingItem != AssemblyItem(Instruction::JUMPI)) + return; + + using Id = ExpressionClasses::Id; + static AssemblyItem s_jump = Instruction::JUMP; + + Id condition = stackElement(m_stackHeight - 1); + Id zero = m_expressionClasses.find(u256(0)); + if (m_expressionClasses.knownToBeDifferent(condition, zero)) + { + feedItem(Instruction::SWAP1, true); + feedItem(Instruction::POP, true); + m_breakingItem = &s_jump; + return; + } + Id negatedCondition = m_expressionClasses.find(Instruction::ISZERO, {condition}); + if (m_expressionClasses.knownToBeDifferent(negatedCondition, zero)) + { + feedItem(Instruction::POP, true); + feedItem(Instruction::POP, true); + m_breakingItem = nullptr; + } +} + void CommonSubexpressionEliminator::setStackElement(int _stackHeight, ExpressionClasses::Id _class) { m_stackElements[_stackHeight] = _class; diff --git a/libevmcore/CommonSubexpressionEliminator.h b/libevmcore/CommonSubexpressionEliminator.h index 03f8cf987..09368f7d2 100644 --- a/libevmcore/CommonSubexpressionEliminator.h +++ b/libevmcore/CommonSubexpressionEliminator.h @@ -88,7 +88,10 @@ public: private: /// Feeds the item into the system for analysis. - void feedItem(AssemblyItem const& _item); + void feedItem(AssemblyItem const& _item, bool _copyItem = false); + + /// Tries to optimize the item that breaks the basic block at the end. + void optimizeBreakingItem(); /// Simplifies the given item using /// Assigns a new equivalence class to the next sequence number of the given stack element. @@ -130,6 +133,10 @@ private: std::vector m_storeOperations; /// Structure containing the classes of equivalent expressions. ExpressionClasses m_expressionClasses; + + /// The item that breaks the basic block, can be nullptr. + /// It is usually appended to the block but can be optimized in some cases. + AssemblyItem const* m_breakingItem = nullptr; }; /** @@ -225,6 +232,8 @@ _AssemblyItemIterator CommonSubexpressionEliminator::feedItems( { for (; _iterator != _end && !SemanticInformation::breaksBasicBlock(*_iterator); ++_iterator) feedItem(*_iterator); + if (_iterator != _end) + m_breakingItem = &(*_iterator++); return _iterator; } diff --git a/test/SolidityOptimizer.cpp b/test/SolidityOptimizer.cpp index cf35536e6..e69d5120e 100644 --- a/test/SolidityOptimizer.cpp +++ b/test/SolidityOptimizer.cpp @@ -536,6 +536,38 @@ BOOST_AUTO_TEST_CASE(cse_deep_stack) }); } +BOOST_AUTO_TEST_CASE(cse_jumpi_no_jump) +{ + AssemblyItems input{ + u256(0), + u256(1), + Instruction::DUP2, + AssemblyItem(PushTag, 1), + Instruction::JUMPI + }; + checkCSE(input, { + u256(0), + u256(1) + }); +} + +BOOST_AUTO_TEST_CASE(cse_jumpi_jump) +{ + AssemblyItems input{ + u256(1), + u256(1), + Instruction::DUP2, + AssemblyItem(PushTag, 1), + Instruction::JUMPI + }; + checkCSE(input, { + u256(1), + Instruction::DUP1, + AssemblyItem(PushTag, 1), + Instruction::JUMP + }); +} + BOOST_AUTO_TEST_SUITE_END() }