diff --git a/evmjit/libevmjit/BasicBlock.cpp b/evmjit/libevmjit/BasicBlock.cpp index b40296f40..bb2673d9c 100644 --- a/evmjit/libevmjit/BasicBlock.cpp +++ b/evmjit/libevmjit/BasicBlock.cpp @@ -53,13 +53,16 @@ void LocalStack::push(llvm::Value* _value) llvm::Value* LocalStack::pop() { - auto result = get(0); + auto item = get(0); + assert(!m_bblock.m_currentStack.empty() || !m_bblock.m_initialStack.empty()); if (m_bblock.m_currentStack.size() > 0) m_bblock.m_currentStack.pop_back(); + else + ++m_bblock.m_globalPops; m_bblock.m_tosOffset -= 1; - return result; + return item; } /** @@ -84,109 +87,71 @@ void LocalStack::swap(size_t _index) set(0, val); } -std::vector::iterator LocalStack::getItemIterator(size_t _index) +llvm::Value* LocalStack::get(size_t _index) { auto& currentStack = m_bblock.m_currentStack; if (_index < currentStack.size()) - return currentStack.end() - _index - 1; + return *(currentStack.rbegin() + _index); // count from back - // Need to map more elements from the EVM stack - auto nNewItems = 1 + _index - currentStack.size(); - currentStack.insert(currentStack.begin(), nNewItems, nullptr); - - return currentStack.end() - _index - 1; -} - -llvm::Value* LocalStack::get(size_t _index) -{ auto& initialStack = m_bblock.m_initialStack; - auto itemIter = getItemIterator(_index); + auto idx = _index - currentStack.size() + m_bblock.m_globalPops; + if (idx >= initialStack.size()) + initialStack.resize(idx + 1); + auto& item = initialStack[idx]; - if (*itemIter == nullptr) - { - // Need to fetch a new item from the EVM stack - assert(static_cast(_index) >= m_bblock.m_tosOffset); - size_t initialIdx = _index - m_bblock.m_tosOffset; - if (initialIdx >= initialStack.size()) - { - auto nNewItems = 1 + initialIdx - initialStack.size(); - initialStack.insert(initialStack.end(), nNewItems, nullptr); - } + if (!item) + item = m_global.get(idx); - assert(initialStack[initialIdx] == nullptr); - // Create a dummy value. - std::string name = "get_" + std::to_string(_index); - initialStack[initialIdx] = m_bblock.m_builder.CreatePHI(Type::Word, 0, std::move(name)); - *itemIter = initialStack[initialIdx]; - } - - return *itemIter; + return item; } void LocalStack::set(size_t _index, llvm::Value* _word) { - auto itemIter = getItemIterator(_index); - *itemIter = _word; -} - - + auto& currentStack = m_bblock.m_currentStack; + if (_index < currentStack.size()) + { + *(currentStack.rbegin() + _index) = _word; + return; + } + auto& initialStack = m_bblock.m_initialStack; + auto idx = _index - currentStack.size() + m_bblock.m_globalPops; + assert(idx < initialStack.size()); + initialStack[idx] = _word; +} void BasicBlock::synchronizeLocalStack(Stack& _evmStack) { auto blockTerminator = m_llvmBB->getTerminator(); - assert(blockTerminator != nullptr); + assert(blockTerminator); if (blockTerminator->getOpcode() != llvm::Instruction::Ret) { - // Not needed in case of ret instruction. Ret also invalidates the stack. + // Not needed in case of ret instruction. Ret invalidates the stack. m_builder.SetInsertPoint(blockTerminator); - auto currIter = m_currentStack.begin(); - auto endIter = m_currentStack.end(); - - // Update (emit set()) changed values - for (int idx = (int)m_currentStack.size() - 1 - m_tosOffset; - currIter < endIter && idx >= 0; - ++currIter, --idx) + // Update items fetched from global stack ignoring the poped ones + assert(m_globalPops <= m_initialStack.size()); // pop() always does get() + for (auto i = m_globalPops; i < m_initialStack.size(); ++i) { - assert(static_cast(idx) < m_initialStack.size()); - if (*currIter != m_initialStack[idx]) // value needs update - _evmStack.set(static_cast(idx), *currIter); + if (m_initialStack[i]) + _evmStack.set(i, m_initialStack[i]); } - // Pop values - if (m_tosOffset < 0) - _evmStack.pop(static_cast(-m_tosOffset)); - - // Push new values - for (; currIter < endIter; ++currIter) + // Add new items + for (auto& item: m_currentStack) { - assert(*currIter != nullptr); - _evmStack.push(*currIter); + if (m_globalPops) // Override poped global items + _evmStack.set(--m_globalPops, item); // using pops counter as the index + else + _evmStack.push(item); } - } - // Emit get() for all (used) values from the initial stack - for (size_t idx = 0; idx < m_initialStack.size(); ++idx) - { - auto val = m_initialStack[idx]; - if (val == nullptr) - continue; - - llvm::PHINode* phi = llvm::cast(val); - // Insert call to get() just before the PHI node and replace - // the uses of PHI with the uses of this new instruction. - m_builder.SetInsertPoint(phi); - auto newVal = _evmStack.get(idx); // OPT: Value may be never user but we need to check stack heigth - // It is probably a good idea to keep heigth as a local variable accesible by LLVM directly - phi->replaceAllUsesWith(newVal); - phi->eraseFromParent(); + // Pop not overriden items + if (m_globalPops) + _evmStack.pop(m_globalPops); } - // Reset the stack - m_initialStack.erase(m_initialStack.begin(), m_initialStack.end()); - m_currentStack.erase(m_currentStack.begin(), m_currentStack.end()); m_tosOffset = 0; } @@ -211,8 +176,6 @@ void BasicBlock::linkLocalStacks(std::vector basicBlocks, llvm::IRB it != initialStack.end() && *it != nullptr; ++it, ++inputItems); - //if (bblock.localStack().m_tosOffset > 0) - // outputItems = bblock.localStack().m_tosOffset; auto& exitStack = bblock.m_currentStack; for (auto it = exitStack.rbegin(); it != exitStack.rend() && *it != nullptr; diff --git a/evmjit/libevmjit/BasicBlock.h b/evmjit/libevmjit/BasicBlock.h index cbac2b048..7a20d4104 100644 --- a/evmjit/libevmjit/BasicBlock.h +++ b/evmjit/libevmjit/BasicBlock.h @@ -45,8 +45,6 @@ private: /// Sets _index'th value from top (counting from 0) void set(size_t _index, llvm::Value* _value); - std::vector::iterator getItemIterator(size_t _index); - private: BasicBlock& m_bblock; Stack& m_global; @@ -114,6 +112,8 @@ private: /// May be negative. int m_tosOffset = 0; + size_t m_globalPops = 0; ///< Number of items poped from global stack. In other words: global - local stack overlap. + /// Is the basic block a valid jump destination. /// JUMPDEST is the first instruction of the basic block. bool const m_isJumpDest = false; diff --git a/evmjit/libevmjit/Compiler.h b/evmjit/libevmjit/Compiler.h index e606044e8..b6b3c3c2d 100644 --- a/evmjit/libevmjit/Compiler.h +++ b/evmjit/libevmjit/Compiler.h @@ -16,7 +16,8 @@ public: struct Options { /// Optimize stack operations between basic blocks - bool optimizeStack = true; + /// TODO: Remove. It must be implemented as a separated pass. + bool optimizeStack = false; /// Rewrite switch instructions to sequences of branches bool rewriteSwitchToBranches = true;