From 579d6f4e32263c7da41931a4b2aec36aee21d683 Mon Sep 17 00:00:00 2001 From: chriseth Date: Mon, 11 May 2015 16:40:28 +0200 Subject: [PATCH] Compute state intersection. --- libevmasm/KnownState.cpp | 47 +++++++++++++++++++------- test/libsolidity/SolidityOptimizer.cpp | 36 ++++++++++++++++++++ 2 files changed, 71 insertions(+), 12 deletions(-) diff --git a/libevmasm/KnownState.cpp b/libevmasm/KnownState.cpp index 41ac4802b..d6fbde2d9 100644 --- a/libevmasm/KnownState.cpp +++ b/libevmasm/KnownState.cpp @@ -160,23 +160,46 @@ KnownState::StoreOperation KnownState::feedItem(AssemblyItem const& _item, bool return op; } -void KnownState::reduceToCommonKnowledge(KnownState const& /*_other*/) +/// Helper function for KnownState::reduceToCommonKnowledge, removes everything from +/// _this which is not in or not equal to the value in _other. +template void intersect( + _Mapping& _this, + _Mapping const& _other, + function<_KeyType(_KeyType)> const& _keyTrans = [](_KeyType _k) { return _k; } +) +{ + for (auto it = _this.begin(); it != _this.end();) + if (_other.count(_keyTrans(it->first)) && _other.at(_keyTrans(it->first)) == it->second) + ++it; + else + it = _this.erase(it); +} + +void KnownState::reduceToCommonKnowledge(KnownState const& _other) { - //@todo - *this = KnownState(m_expressionClasses); + int stackDiff = m_stackHeight - _other.m_stackHeight; + function stackKeyTransform = [=](int _key) -> int { return _key - stackDiff; }; + intersect(m_stackElements, _other.m_stackElements, stackKeyTransform); + // Use the smaller stack height. Essential to terminate in case of loops. + if (m_stackHeight > _other.m_stackHeight) + { + map shiftedStack; + for (auto const& stackElement: m_stackElements) + shiftedStack[stackElement.first - stackDiff] = stackElement.second; + m_stackElements = move(shiftedStack); + m_stackHeight = _other.m_stackHeight; + } + + intersect(m_storageContent, _other.m_storageContent); + intersect(m_memoryContent, _other.m_memoryContent); } bool KnownState::operator==(const KnownState& _other) const { - //@todo - return ( - m_stackElements.empty() && - _other.m_stackElements.empty() && - m_storageContent.empty() && - _other.m_storageContent.empty() && - m_memoryContent.empty() && - _other.m_memoryContent.empty() - ); + return m_storageContent == _other.m_storageContent && + m_memoryContent == _other.m_memoryContent && + m_stackHeight == _other.m_stackHeight && + m_stackElements == _other.m_stackElements; } ExpressionClasses::Id KnownState::stackElement(int _stackHeight, SourceLocation const& _location) diff --git a/test/libsolidity/SolidityOptimizer.cpp b/test/libsolidity/SolidityOptimizer.cpp index 4986b1469..e50469dd6 100644 --- a/test/libsolidity/SolidityOptimizer.cpp +++ b/test/libsolidity/SolidityOptimizer.cpp @@ -272,6 +272,42 @@ BOOST_AUTO_TEST_CASE(storage_write_in_loops) compareVersions("f(uint256)", 36); } +BOOST_AUTO_TEST_CASE(retain_information_in_branches) +{ + // This tests that the optimizer knows that we already have "z == sha3(y)" inside both branches. + char const* sourceCode = R"( + contract c { + bytes32 d; + uint a; + function f(uint x, bytes32 y) returns (uint r_a, bytes32 r_d) { + bytes32 z = sha3(y); + if (x > 8) { + z = sha3(y); + a = x; + } else { + z = sha3(y); + a = x; + } + r_a = a; + r_d = d; + } + } + )"; + compileBothVersions(sourceCode); + compareVersions("f(uint256,bytes32)", 0, "abc"); + compareVersions("f(uint256,bytes32)", 8, "def"); + compareVersions("f(uint256,bytes32)", 10, "ghi"); + + m_optimize = true; + bytes optimizedBytecode = compileAndRun(sourceCode, 0, "c"); + size_t numSHA3s = 0; + eth::eachInstruction(optimizedBytecode, [&](Instruction _instr, u256 const&) { + if (_instr == eth::Instruction::SHA3) + numSHA3s++; + }); + BOOST_CHECK_EQUAL(1, numSHA3s); +} + BOOST_AUTO_TEST_CASE(cse_intermediate_swap) { eth::KnownState state;