diff --git a/evmjit/CMakeLists.txt b/evmjit/CMakeLists.txt index 619477119..49bf7f73d 100644 --- a/evmjit/CMakeLists.txt +++ b/evmjit/CMakeLists.txt @@ -3,13 +3,14 @@ cmake_minimum_required(VERSION 2.8.12) if (${CMAKE_VERSION} VERSION_GREATER 3.0) cmake_policy(SET CMP0042 OLD) # fix MACOSX_RPATH cmake_policy(SET CMP0048 NEW) # allow VERSION argument in project() - project(EVMJIT VERSION 0.9.0 LANGUAGES CXX) + project(EVMJIT VERSION 0.9.0.1 LANGUAGES CXX) else() project(EVMJIT) - set(EVMJIT_VERSION "0.9.0") + set(EVMJIT_VERSION "0.9.0.1") set(EVMJIT_VERSION_MAJOR 0) set(EVMJIT_VERSION_MINOR 9) set(EVMJIT_VERSION_PATCH 0) + set(EVMJIT_VERSION_TWEAK 1) endif() set_property(GLOBAL PROPERTY USE_FOLDERS ON) diff --git a/evmjit/include/evmjit/JIT-c.h b/evmjit/include/evmjit/JIT-c.h index d7c792451..4e0993ab7 100644 --- a/evmjit/include/evmjit/JIT-c.h +++ b/evmjit/include/evmjit/JIT-c.h @@ -47,10 +47,6 @@ typedef enum evmjit_return_code // Standard error codes OutOfGas = -1, - StackUnderflow = -2, - BadJumpDestination = -3, - BadInstruction = -4, - Rejected = -5, ///< Input data (code, gas, block info, etc.) does not meet JIT requirement and execution request has been rejected // Internal error codes LLVMError = -101, diff --git a/evmjit/include/evmjit/JIT.h b/evmjit/include/evmjit/JIT.h index e74534243..285a33218 100644 --- a/evmjit/include/evmjit/JIT.h +++ b/evmjit/include/evmjit/JIT.h @@ -101,10 +101,6 @@ enum class ReturnCode // Standard error codes OutOfGas = -1, - StackUnderflow = -2, - BadJumpDestination = -3, - BadInstruction = -4, - Rejected = -5, ///< Input data (code, gas, block info, etc.) does not meet JIT requirement and execution request has been rejected // Internal error codes LLVMError = -101, diff --git a/evmjit/libevmjit-cpp/JitVM.cpp b/evmjit/libevmjit-cpp/JitVM.cpp index 2bc9043db..6db58b594 100644 --- a/evmjit/libevmjit-cpp/JitVM.cpp +++ b/evmjit/libevmjit-cpp/JitVM.cpp @@ -60,14 +60,8 @@ bytesConstRef JitVM::execImpl(u256& io_gas, ExtVMFace& _ext, OnOpFunc const& _on _ext.suicide(right160(jit2eth(m_data.address))); break; - case evmjit::ReturnCode::BadJumpDestination: - BOOST_THROW_EXCEPTION(BadJumpDestination()); case evmjit::ReturnCode::OutOfGas: BOOST_THROW_EXCEPTION(OutOfGas()); - case evmjit::ReturnCode::StackUnderflow: // FIXME: Remove support for detail errors - BOOST_THROW_EXCEPTION(StackUnderflow()); - case evmjit::ReturnCode::BadInstruction: - BOOST_THROW_EXCEPTION(BadInstruction()); case evmjit::ReturnCode::LinkerWorkaround: // never happens env_sload(); // but forces linker to include env_* JIT callback functions break; diff --git a/evmjit/libevmjit/Array.cpp b/evmjit/libevmjit/Array.cpp index 96329afd5..4f1f47d6e 100644 --- a/evmjit/libevmjit/Array.cpp +++ b/evmjit/libevmjit/Array.cpp @@ -217,11 +217,7 @@ llvm::Type* Array::getType() } Array::Array(llvm::IRBuilder<>& _builder, char const* _name) : - CompilerHelper(_builder), - m_pushFunc([this](){ return createArrayPushFunc(); }), - m_setFunc([this](){ return createArraySetFunc(); }), - m_getFunc([this](){ return createArrayGetFunc(); }), - m_freeFunc([this](){ return createFreeFunc(); }) + CompilerHelper(_builder) { m_array = m_builder.CreateAlloca(getType(), nullptr, _name); m_builder.CreateStore(llvm::ConstantAggregateZero::get(getType()), m_array); @@ -229,11 +225,7 @@ Array::Array(llvm::IRBuilder<>& _builder, char const* _name) : Array::Array(llvm::IRBuilder<>& _builder, llvm::Value* _array) : CompilerHelper(_builder), - m_array(_array), - m_pushFunc([this](){ return createArrayPushFunc(); }), - m_setFunc([this](){ return createArraySetFunc(); }), - m_getFunc([this](){ return createArrayGetFunc(); }), - m_freeFunc([this](){ return createFreeFunc(); }) + m_array(_array) { m_builder.CreateStore(llvm::ConstantAggregateZero::get(getType()), m_array); } diff --git a/evmjit/libevmjit/Array.h b/evmjit/libevmjit/Array.h index 41842f0c9..a2db976b3 100644 --- a/evmjit/libevmjit/Array.h +++ b/evmjit/libevmjit/Array.h @@ -59,10 +59,10 @@ private: llvm::Function* getReallocFunc(); LazyFunction m_pushFunc = {[this](){ return createArrayPushFunc(); }}; // TODO: If works on MSVC, remove form initialization list - LazyFunction m_setFunc; + LazyFunction m_setFunc = {[this](){ return createArraySetFunc(); }}; LazyFunction m_getPtrFunc = {[this](){ return createGetPtrFunc(); }}; - LazyFunction m_getFunc; - LazyFunction m_freeFunc; + LazyFunction m_getFunc = {[this](){ return createArrayGetFunc(); }}; + LazyFunction m_freeFunc = {[this](){ return createFreeFunc(); }}; LazyFunction m_extendFunc = {[this](){ return createExtendFunc(); }}; LazyFunction m_reallocFunc = {[this](){ return getReallocFunc(); }}; }; @@ -70,5 +70,3 @@ private: } } } - - diff --git a/evmjit/libevmjit/BasicBlock.cpp b/evmjit/libevmjit/BasicBlock.cpp index be57a28b8..47b590103 100644 --- a/evmjit/libevmjit/BasicBlock.cpp +++ b/evmjit/libevmjit/BasicBlock.cpp @@ -20,53 +20,42 @@ namespace eth namespace jit { -static const char* jumpDestName = "JmpDst."; -static const char* basicBlockName = "Instr."; - -BasicBlock::BasicBlock(instr_idx _firstInstrIdx, code_iterator _begin, code_iterator _end, llvm::Function* _mainFunc, llvm::IRBuilder<>& _builder, bool isJumpDest) : +BasicBlock::BasicBlock(instr_idx _firstInstrIdx, code_iterator _begin, code_iterator _end, llvm::Function* _mainFunc): m_firstInstrIdx{_firstInstrIdx}, m_begin(_begin), m_end(_end), - m_llvmBB(llvm::BasicBlock::Create(_mainFunc->getContext(), {isJumpDest ? jumpDestName : basicBlockName, std::to_string(_firstInstrIdx)}, _mainFunc)), - m_stack(*this), - m_builder(_builder), - m_isJumpDest(isJumpDest) -{} - -BasicBlock::BasicBlock(std::string _name, llvm::Function* _mainFunc, llvm::IRBuilder<>& _builder, bool isJumpDest) : - m_llvmBB(llvm::BasicBlock::Create(_mainFunc->getContext(), _name, _mainFunc)), - m_stack(*this), - m_builder(_builder), - m_isJumpDest(isJumpDest) + m_llvmBB(llvm::BasicBlock::Create(_mainFunc->getContext(), {"Instr.", std::to_string(_firstInstrIdx)}, _mainFunc)) {} -BasicBlock::LocalStack::LocalStack(BasicBlock& _owner) : - m_bblock(_owner) +LocalStack::LocalStack(Stack& _globalStack): + m_global(_globalStack) {} -void BasicBlock::LocalStack::push(llvm::Value* _value) +void LocalStack::push(llvm::Value* _value) { assert(_value->getType() == Type::Word); - m_bblock.m_currentStack.push_back(_value); - m_bblock.m_tosOffset += 1; - m_maxSize = std::max(m_maxSize, m_bblock.m_tosOffset); + m_local.push_back(_value); + m_maxSize = std::max(m_maxSize, size()); } -llvm::Value* BasicBlock::LocalStack::pop() +llvm::Value* LocalStack::pop() { - auto result = get(0); + auto item = get(0); + assert(!m_local.empty() || !m_input.empty()); - if (m_bblock.m_currentStack.size() > 0) - m_bblock.m_currentStack.pop_back(); + if (m_local.size() > 0) + m_local.pop_back(); + else + ++m_globalPops; - m_bblock.m_tosOffset -= 1; - return result; + m_minSize = std::min(m_minSize, size()); + return item; } /** * Pushes a copy of _index-th element (tos is 0-th elem). */ -void BasicBlock::LocalStack::dup(size_t _index) +void LocalStack::dup(size_t _index) { auto val = get(_index); push(val); @@ -76,7 +65,7 @@ void BasicBlock::LocalStack::dup(size_t _index) * Swaps tos with _index-th element (tos is 0-th elem). * _index must be > 0. */ -void BasicBlock::LocalStack::swap(size_t _index) +void LocalStack::swap(size_t _index) { assert(_index > 0); auto val = get(_index); @@ -85,306 +74,73 @@ void BasicBlock::LocalStack::swap(size_t _index) set(0, val); } -std::vector::iterator BasicBlock::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; - - // Need to map more elements from the EVM stack - auto nNewItems = 1 + _index - currentStack.size(); - currentStack.insert(currentStack.begin(), nNewItems, nullptr); + if (_index < m_local.size()) + return *(m_local.rbegin() + _index); // count from back - return currentStack.end() - _index - 1; -} - -llvm::Value* BasicBlock::LocalStack::get(size_t _index) -{ - auto& initialStack = m_bblock.m_initialStack; - auto itemIter = getItemIterator(_index); + auto idx = _index - m_local.size() + m_globalPops; + if (idx >= m_input.size()) + m_input.resize(idx + 1); + auto& item = m_input[idx]; - if (*itemIter == nullptr) + if (!item) { - // 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); - } - - 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]; + item = m_global.get(idx); // Reach an item from global stack + m_minSize = std::min(m_minSize, -static_cast(idx) - 1); // and remember required stack size } - return *itemIter; + return item; } -void BasicBlock::LocalStack::set(size_t _index, llvm::Value* _word) -{ - auto itemIter = getItemIterator(_index); - *itemIter = _word; -} - - - - - -void BasicBlock::synchronizeLocalStack(Stack& _evmStack) +void LocalStack::set(size_t _index, llvm::Value* _word) { - auto blockTerminator = m_llvmBB->getTerminator(); - assert(blockTerminator != nullptr); - if (blockTerminator->getOpcode() != llvm::Instruction::Ret) - { - // Not needed in case of ret instruction. Ret also 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) - { - assert(static_cast(idx) < m_initialStack.size()); - if (*currIter != m_initialStack[idx]) // value needs update - _evmStack.set(static_cast(idx), *currIter); - } - - // Pop values - if (m_tosOffset < 0) - _evmStack.pop(static_cast(-m_tosOffset)); - - // Push new values - for (; currIter < endIter; ++currIter) - { - assert(*currIter != nullptr); - _evmStack.push(*currIter); - } - } - - // Emit get() for all (used) values from the initial stack - for (size_t idx = 0; idx < m_initialStack.size(); ++idx) + if (_index < m_local.size()) { - 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(); + *(m_local.rbegin() + _index) = _word; + return; } - // Reset the stack - m_initialStack.erase(m_initialStack.begin(), m_initialStack.end()); - m_currentStack.erase(m_currentStack.begin(), m_currentStack.end()); - m_tosOffset = 0; + auto idx = _index - m_local.size() + m_globalPops; + assert(idx < m_input.size()); + m_input[idx] = _word; } -void BasicBlock::linkLocalStacks(std::vector basicBlocks, llvm::IRBuilder<>& _builder) -{ - struct BBInfo - { - BasicBlock& bblock; - std::vector predecessors; - size_t inputItems; - size_t outputItems; - std::vector phisToRewrite; - - BBInfo(BasicBlock& _bblock) : - bblock(_bblock), - predecessors(), - inputItems(0), - outputItems(0) - { - auto& initialStack = bblock.m_initialStack; - for (auto it = initialStack.begin(); - 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; - ++it, ++outputItems); - } - }; - - std::map cfg; - - // Create nodes in cfg - for (auto bb : basicBlocks) - cfg.emplace(bb->llvm(), *bb); - - // Create edges in cfg: for each bb info fill the list - // of predecessor infos. - for (auto& pair : cfg) - { - auto bb = pair.first; - auto& info = pair.second; - - for (auto predIt = llvm::pred_begin(bb); predIt != llvm::pred_end(bb); ++predIt) - { - auto predInfoEntry = cfg.find(*predIt); - if (predInfoEntry != cfg.end()) // FIXME: It is wrong - will skip entry block - info.predecessors.push_back(&predInfoEntry->second); - } - } - // Iteratively compute inputs and outputs of each block, until reaching fixpoint. - bool valuesChanged = true; - while (valuesChanged) +void LocalStack::finalize(llvm::IRBuilder<>& _builder, llvm::BasicBlock& _bb) +{ + auto blockTerminator = _bb.getTerminator(); + assert(blockTerminator); + if (blockTerminator->getOpcode() != llvm::Instruction::Ret) { - for (auto& pair : cfg) - { - DLOG(bb) << pair.second.bblock.llvm()->getName().str() - << ": in " << pair.second.inputItems - << ", out " << pair.second.outputItems - << "\n"; - } + // Not needed in case of ret instruction. Ret invalidates the stack. + _builder.SetInsertPoint(blockTerminator); - valuesChanged = false; - for (auto& pair : cfg) + // Update items fetched from global stack ignoring the poped ones + assert(m_globalPops <= m_input.size()); // pop() always does get() + for (auto i = m_globalPops; i < m_input.size(); ++i) { - auto& info = pair.second; - - if (&info.bblock == basicBlocks.front()) - info.inputItems = 0; // we cannot use phi nodes for first block as it is a successor of entry block - - if (info.predecessors.empty()) - info.inputItems = 0; // no consequences for other blocks, so leave valuesChanged false - - for (auto predInfo : info.predecessors) - { - if (predInfo->outputItems < info.inputItems) - { - info.inputItems = predInfo->outputItems; - valuesChanged = true; - } - else if (predInfo->outputItems > info.inputItems) - { - predInfo->outputItems = info.inputItems; - valuesChanged = true; - } - } + if (m_input[i]) + m_global.set(i, m_input[i]); } - } - - // Propagate values between blocks. - for (auto& entry : cfg) - { - auto& info = entry.second; - auto& bblock = info.bblock; - llvm::BasicBlock::iterator fstNonPhi(bblock.llvm()->getFirstNonPHI()); - auto phiIter = bblock.m_initialStack.begin(); - for (size_t index = 0; index < info.inputItems; ++index, ++phiIter) + // Add new items + auto pops = m_globalPops; // Copy pops counter to keep original value + for (auto& item: m_local) { - assert(llvm::isa(*phiIter)); - auto phi = llvm::cast(*phiIter); - - for (auto predIt : info.predecessors) - { - auto& predExitStack = predIt->bblock.m_currentStack; - auto value = *(predExitStack.end() - 1 - index); - phi->addIncoming(value, predIt->bblock.llvm()); - } - - // Move phi to the front - if (llvm::BasicBlock::iterator(phi) != bblock.llvm()->begin()) - { - phi->removeFromParent(); - _builder.SetInsertPoint(bblock.llvm(), bblock.llvm()->begin()); - _builder.Insert(phi); - } + if (pops) // Override poped global items + m_global.set(--pops, item); // using pops counter as the index + else + m_global.push(item); } - // The items pulled directly from predecessors block must be removed - // from the list of items that has to be popped from the initial stack. - auto& initialStack = bblock.m_initialStack; - initialStack.erase(initialStack.begin(), initialStack.begin() + info.inputItems); - // Initial stack shrinks, so the size difference grows: - bblock.m_tosOffset += (int)info.inputItems; - } - - // We must account for the items that were pushed directly to successor - // blocks and thus should not be on the list of items to be pushed onto - // to EVM stack - for (auto& entry : cfg) - { - auto& info = entry.second; - auto& bblock = info.bblock; - - auto& exitStack = bblock.m_currentStack; - exitStack.erase(exitStack.end() - info.outputItems, exitStack.end()); - bblock.m_tosOffset -= (int)info.outputItems; // FIXME: Fix types - } -} - -void BasicBlock::dump() -{ - dump(std::cerr, false); -} - -void BasicBlock::dump(std::ostream& _out, bool _dotOutput) -{ - llvm::raw_os_ostream out(_out); - - out << (_dotOutput ? "" : "Initial stack:\n"); - for (auto val : m_initialStack) - { - if (val == nullptr) - out << " ?"; - else if (llvm::isa(val)) - out << " " << val->getName(); - else if (llvm::isa(val)) - out << *val; - else - out << " " << *val; - - out << (_dotOutput ? "\\l" : "\n"); - } - - out << (_dotOutput ? "| " : "Instructions:\n"); - for (auto ins = m_llvmBB->begin(); ins != m_llvmBB->end(); ++ins) - out << *ins << (_dotOutput ? "\\l" : "\n"); - - if (! _dotOutput) - out << "Current stack (offset = " << m_tosOffset << "):\n"; - else - out << "|"; - - for (auto val = m_currentStack.rbegin(); val != m_currentStack.rend(); ++val) - { - if (*val == nullptr) - out << " ?"; - else if (llvm::isa(*val)) - out << " " << (*val)->getName(); - else if (llvm::isa(*val)) - out << **val; - else - out << " " << **val; - out << (_dotOutput ? "\\l" : "\n"); + // Pop not overriden items + if (pops) + m_global.pop(pops); } - - if (! _dotOutput) - out << " ...\n----------------------------------------\n"; } - - } } } diff --git a/evmjit/libevmjit/BasicBlock.h b/evmjit/libevmjit/BasicBlock.h index ddf73a04c..a6e420efc 100644 --- a/evmjit/libevmjit/BasicBlock.h +++ b/evmjit/libevmjit/BasicBlock.h @@ -14,52 +14,58 @@ namespace jit using namespace evmjit; using instr_idx = uint64_t; -class BasicBlock +class LocalStack { public: - class LocalStack - { - public: - /// Pushes value on stack - void push(llvm::Value* _value); + explicit LocalStack(Stack& _globalStack); + LocalStack(LocalStack const&) = delete; + void operator=(LocalStack const&) = delete; + + /// Pushes value on stack + void push(llvm::Value* _value); - /// Pops and returns top value - llvm::Value* pop(); + /// Pops and returns top value + llvm::Value* pop(); - /// Duplicates _index'th value on stack - void dup(size_t _index); + /// Duplicates _index'th value on stack + void dup(size_t _index); - /// Swaps _index'th value on stack with a value on stack top. - /// @param _index Index of value to be swaped. Must be > 0. - void swap(size_t _index); + /// Swaps _index'th value on stack with a value on stack top. + /// @param _index Index of value to be swaped. Must be > 0. + void swap(size_t _index); - size_t getMaxSize() const { return m_maxSize; } - int getDiff() const { return m_bblock.m_tosOffset; } + ssize_t size() const { return static_cast(m_local.size()) - static_cast(m_globalPops); } + ssize_t minSize() const { return m_minSize; } + ssize_t maxSize() const { return m_maxSize; } - private: - LocalStack(BasicBlock& _owner); - LocalStack(LocalStack const&) = delete; - void operator=(LocalStack const&) = delete; - friend BasicBlock; + /// Finalize local stack: check the requirements and update of the global stack. + void finalize(llvm::IRBuilder<>& _builder, llvm::BasicBlock& _bb); - /// Gets _index'th value from top (counting from 0) - llvm::Value* get(size_t _index); +private: + /// Gets _index'th value from top (counting from 0) + llvm::Value* get(size_t _index); - /// Sets _index'th value from top (counting from 0) - void set(size_t _index, llvm::Value* _value); + /// Sets _index'th value from top (counting from 0) + void set(size_t _index, llvm::Value* _value); - std::vector::iterator getItemIterator(size_t _index); + /// Items fetched from global stack. First element matches the top of the global stack. + /// Can contain nulls if some items has been skipped. + std::vector m_input; - private: - BasicBlock& m_bblock; - int m_maxSize = 0; ///< Max size reached by the stack. - }; + /// Local stack items that has not been pushed to global stack. First item is just above global stack. + std::vector m_local; - explicit BasicBlock(instr_idx _firstInstrIdx, code_iterator _begin, code_iterator _end, llvm::Function* _mainFunc, llvm::IRBuilder<>& _builder, bool isJumpDest); - explicit BasicBlock(std::string _name, llvm::Function* _mainFunc, llvm::IRBuilder<>& _builder, bool isJumpDest); + Stack& m_global; ///< Reference to global stack. - BasicBlock(const BasicBlock&) = delete; - BasicBlock& operator=(const BasicBlock&) = delete; + size_t m_globalPops = 0; ///< Number of items poped from global stack. In other words: global - local stack overlap. + ssize_t m_minSize = 0; ///< Minimum reached local stack size. Can be negative. + ssize_t m_maxSize = 0; ///< Maximum reached local stack size. +}; + +class BasicBlock +{ +public: + explicit BasicBlock(instr_idx _firstInstrIdx, code_iterator _begin, code_iterator _end, llvm::Function* _mainFunc); llvm::BasicBlock* llvm() { return m_llvmBB; } @@ -67,61 +73,12 @@ public: code_iterator begin() const { return m_begin; } code_iterator end() const { return m_end; } - bool isJumpDest() const { return m_isJumpDest; } - - llvm::Value* getJumpTarget() const { return m_jumpTarget; } - void setJumpTarget(llvm::Value* _jumpTarget) { m_jumpTarget = _jumpTarget; } - - LocalStack& localStack() { return m_stack; } - - /// Optimization: propagates values between local stacks in basic blocks - /// to avoid excessive pushing/popping on the EVM stack. - static void linkLocalStacks(std::vector _basicBlocks, llvm::IRBuilder<>& _builder); - - /// Synchronize current local stack with the EVM stack. - void synchronizeLocalStack(Stack& _evmStack); - - /// Prints local stack and block instructions to stderr. - /// Useful for calling in a debugger session. - void dump(); - void dump(std::ostream& os, bool _dotOutput = false); - private: instr_idx const m_firstInstrIdx = 0; ///< Code index of first instruction in the block - code_iterator const m_begin = {}; ///< Iterator pointing code beginning of the block - code_iterator const m_end = {}; ///< Iterator pointing code end of the block - - llvm::BasicBlock* const m_llvmBB; - - /// Basic black state vector (stack) - current/end values and their positions on stack - /// @internal Must be AFTER m_llvmBB - LocalStack m_stack; - - llvm::IRBuilder<>& m_builder; - - /// This stack contains LLVM values that correspond to items found at - /// the EVM stack when the current basic block starts executing. - /// Location 0 corresponds to the top of the EVM stack, location 1 is - /// the item below the top and so on. The stack grows as the code - /// accesses more items on the EVM stack but once a value is put on - /// the stack, it will never be replaced. - std::vector m_initialStack; - - /// This stack tracks the contents of the EVM stack as the basic block - /// executes. It may grow on both sides, as the code pushes items on - /// top of the stack or changes existing items. - std::vector m_currentStack; - - /// How many items higher is the current stack than the initial one. - /// May be negative. - int m_tosOffset = 0; - - /// Is the basic block a valid jump destination. - /// JUMPDEST is the first instruction of the basic block. - bool const m_isJumpDest = false; + code_iterator const m_begin = {}; ///< Iterator pointing code beginning of the block + code_iterator const m_end = {}; ///< Iterator pointing code end of the block - /// If block finishes with dynamic jump target index is stored here - llvm::Value* m_jumpTarget = nullptr; + llvm::BasicBlock* const m_llvmBB; ///< Reference to the LLVM BasicBlock }; } diff --git a/evmjit/libevmjit/Compiler.cpp b/evmjit/libevmjit/Compiler.cpp index 61aa464a6..182b5fef8 100644 --- a/evmjit/libevmjit/Compiler.cpp +++ b/evmjit/libevmjit/Compiler.cpp @@ -36,7 +36,7 @@ Compiler::Compiler(Options const& _options): Type::init(m_builder.getContext()); } -void Compiler::createBasicBlocks(code_iterator _codeBegin, code_iterator _codeEnd) +std::vector Compiler::createBasicBlocks(code_iterator _codeBegin, code_iterator _codeEnd, llvm::SwitchInst& _jumpTable) { /// Helper function that skips push data and finds next iterator (can be the end) auto skipPushDataAndGetNext = [](code_iterator _curr, code_iterator _end) @@ -54,8 +54,9 @@ void Compiler::createBasicBlocks(code_iterator _codeBegin, code_iterator _codeEn if (*(_codeEnd - 1) != static_cast(Instruction::STOP)) break; + std::vector blocks; + auto begin = _codeBegin; // begin of current block - bool nextJumpDest = false; for (auto curr = begin, next = begin; curr != _codeEnd; curr = next) { next = skipPushDataAndGetNext(curr, _codeEnd); @@ -71,10 +72,6 @@ void Compiler::createBasicBlocks(code_iterator _codeBegin, code_iterator _codeEn isEnd = true; break; - case Instruction::JUMPDEST: - nextJumpDest = true; - break; - default: break; } @@ -86,42 +83,32 @@ void Compiler::createBasicBlocks(code_iterator _codeBegin, code_iterator _codeEn if (isEnd) { auto beginIdx = begin - _codeBegin; - m_basicBlocks.emplace(std::piecewise_construct, std::forward_as_tuple(beginIdx), - std::forward_as_tuple(beginIdx, begin, next, m_mainFunc, m_builder, nextJumpDest)); - nextJumpDest = false; + blocks.emplace_back(beginIdx, begin, next, m_mainFunc); + if (Instruction(*begin) == Instruction::JUMPDEST) + _jumpTable.addCase(Constant::get(beginIdx), blocks.back().llvm()); begin = next; } } + + return blocks; } -llvm::BasicBlock* Compiler::getJumpTableBlock(RuntimeManager& _runtimeManager) +void Compiler::fillJumpTable() { - if (!m_jumpTableBlock) + assert(m_jumpTableBB); + if (llvm::pred_empty(m_jumpTableBB)) { - m_jumpTableBlock.reset(new BasicBlock("JumpTable", m_mainFunc, m_builder, true)); - InsertPointGuard g{m_builder}; - m_builder.SetInsertPoint(m_jumpTableBlock->llvm()); - auto dest = m_builder.CreatePHI(Type::Word, 8, "target"); - auto switchInstr = m_builder.CreateSwitch(dest, getBadJumpBlock(_runtimeManager)); - for (auto&& p : m_basicBlocks) - { - if (p.second.isJumpDest()) - switchInstr->addCase(Constant::get(p.first), p.second.llvm()); - } + m_jumpTableBB->eraseFromParent(); // remove if unused + return; } - return m_jumpTableBlock->llvm(); -} -llvm::BasicBlock* Compiler::getBadJumpBlock(RuntimeManager& _runtimeManager) -{ - if (!m_badJumpBlock) + // TODO: Extend this function as `resolveJumps()` and fill gaps in branch instructions. + auto target = llvm::cast(m_jumpTableBB->begin()); + for (auto pred: llvm::predecessors(m_jumpTableBB)) { - m_badJumpBlock.reset(new BasicBlock("BadJump", m_mainFunc, m_builder, true)); - InsertPointGuard g{m_builder}; - m_builder.SetInsertPoint(m_badJumpBlock->llvm()); - _runtimeManager.exit(ReturnCode::BadJumpDestination); + auto targetMd = llvm::cast(pred->getTerminator()->getMetadata("target")->getOperand(0)); + target->addIncoming(targetMd->getValue(), pred); } - return m_badJumpBlock->llvm(); } std::unique_ptr Compiler::compile(code_iterator _begin, code_iterator _end, std::string const& _id) @@ -135,16 +122,24 @@ std::unique_ptr Compiler::compile(code_iterator _begin, code_itera // Create entry basic block auto entryBlock = llvm::BasicBlock::Create(m_builder.getContext(), {}, m_mainFunc); + + m_stopBB = llvm::BasicBlock::Create(m_mainFunc->getContext(), "Stop", m_mainFunc); + m_abortBB = llvm::BasicBlock::Create(m_mainFunc->getContext(), "Abort", m_mainFunc); + m_jumpTableBB = llvm::BasicBlock::Create(m_mainFunc->getContext(), "JumpTable", m_mainFunc); + m_builder.SetInsertPoint(m_jumpTableBB); + auto target = m_builder.CreatePHI(Type::Word, 16, "target"); + auto& jumpTable = *m_builder.CreateSwitch(target, m_abortBB); + m_builder.SetInsertPoint(entryBlock); - createBasicBlocks(_begin, _end); + auto blocks = createBasicBlocks(_begin, _end, jumpTable); // Init runtime structures. RuntimeManager runtimeManager(m_builder, _begin, _end); GasMeter gasMeter(m_builder, runtimeManager); Memory memory(runtimeManager, gasMeter); Ext ext(runtimeManager, memory); - Stack stack(m_builder, runtimeManager); + Stack stack(m_builder); runtimeManager.setStack(stack); // Runtime Manager will free stack memory Arith256 arith(m_builder); @@ -162,87 +157,39 @@ std::unique_ptr Compiler::compile(code_iterator _begin, code_itera auto normalFlow = m_builder.CreateICmpEQ(r, m_builder.getInt32(0)); runtimeManager.setJmpBuf(jmpBuf); - // TODO: Create Stop basic block on demand - m_stopBB = llvm::BasicBlock::Create(m_mainFunc->getContext(), "Stop", m_mainFunc); - m_abortBB = llvm::BasicBlock::Create(m_mainFunc->getContext(), "Abort", m_mainFunc); - - auto firstBB = m_basicBlocks.empty() ? m_stopBB : m_basicBlocks.begin()->second.llvm(); + auto firstBB = blocks.empty() ? m_stopBB : blocks.front().llvm(); m_builder.CreateCondBr(normalFlow, firstBB, m_abortBB, Type::expectTrue); - for (auto basicBlockPairIt = m_basicBlocks.begin(); basicBlockPairIt != m_basicBlocks.end(); ++basicBlockPairIt) + for (auto it = blocks.begin(); it != blocks.end(); ++it) { - auto& basicBlock = basicBlockPairIt->second; - auto iterCopy = basicBlockPairIt; - ++iterCopy; - auto nextBasicBlock = (iterCopy != m_basicBlocks.end()) ? iterCopy->second.llvm() : nullptr; - compileBasicBlock(basicBlock, runtimeManager, arith, memory, ext, gasMeter, nextBasicBlock); + // TODO: Rewrite + auto nextIt = it + 1; + auto nextBasicBlock = (nextIt != blocks.end()) ? nextIt->llvm() : nullptr; // TODO: What with Stop block? + compileBasicBlock(*it, runtimeManager, arith, memory, ext, gasMeter, nextBasicBlock, stack, jumpTable); } // Code for special blocks: - // TODO: move to separate function. m_builder.SetInsertPoint(m_stopBB); runtimeManager.exit(ReturnCode::Stop); m_builder.SetInsertPoint(m_abortBB); runtimeManager.exit(ReturnCode::OutOfGas); - removeDeadBlocks(); - - // Link jump table target index - if (m_jumpTableBlock) - { - auto phi = llvm::cast(&m_jumpTableBlock->llvm()->getInstList().front()); - for (auto predIt = llvm::pred_begin(m_jumpTableBlock->llvm()); predIt != llvm::pred_end(m_jumpTableBlock->llvm()); ++predIt) - { - BasicBlock* pred = nullptr; - for (auto&& p : m_basicBlocks) - { - if (p.second.llvm() == *predIt) - { - pred = &p.second; - break; - } - } - - phi->addIncoming(pred->getJumpTarget(), pred->llvm()); - } - } - - dumpCFGifRequired("blocks-init.dot"); - - if (m_options.optimizeStack) - { - std::vector blockList; - for (auto& entry : m_basicBlocks) - blockList.push_back(&entry.second); - - if (m_jumpTableBlock) - blockList.push_back(m_jumpTableBlock.get()); - - BasicBlock::linkLocalStacks(blockList, m_builder); - - dumpCFGifRequired("blocks-opt.dot"); - } - - for (auto& entry : m_basicBlocks) - entry.second.synchronizeLocalStack(stack); - if (m_jumpTableBlock) - m_jumpTableBlock->synchronizeLocalStack(stack); - - dumpCFGifRequired("blocks-sync.dot"); + fillJumpTable(); return module; } void Compiler::compileBasicBlock(BasicBlock& _basicBlock, RuntimeManager& _runtimeManager, - Arith256& _arith, Memory& _memory, Ext& _ext, GasMeter& _gasMeter, llvm::BasicBlock* _nextBasicBlock) + Arith256& _arith, Memory& _memory, Ext& _ext, GasMeter& _gasMeter, llvm::BasicBlock* _nextBasicBlock, Stack& _globalStack, + llvm::SwitchInst& jumpTable) { if (!_nextBasicBlock) // this is the last block in the code _nextBasicBlock = m_stopBB; m_builder.SetInsertPoint(_basicBlock.llvm()); - auto& stack = _basicBlock.localStack(); + LocalStack stack{_globalStack}; for (auto it = _basicBlock.begin(); it != _basicBlock.end(); ++it) { @@ -609,44 +556,23 @@ void Compiler::compileBasicBlock(BasicBlock& _basicBlock, RuntimeManager& _runti case Instruction::JUMP: case Instruction::JUMPI: { - llvm::BasicBlock* targetBlock = nullptr; + auto jumpBlock = m_jumpTableBB; auto target = stack.pop(); - if (auto constant = llvm::dyn_cast(target)) - { - auto&& c = constant->getValue(); - auto targetIdx = c.getActiveBits() <= 64 ? c.getZExtValue() : -1; - auto it = m_basicBlocks.find(targetIdx); - targetBlock = (it != m_basicBlocks.end() && it->second.isJumpDest()) ? it->second.llvm() : getBadJumpBlock(_runtimeManager); - } + auto jumpInst = (inst == Instruction::JUMP) ? + m_builder.CreateBr(jumpBlock) : + m_builder.CreateCondBr(m_builder.CreateICmpNE(stack.pop(), Constant::get(0), "jump.check"), jumpBlock, _nextBasicBlock); - // TODO: Improve; check for constants - if (inst == Instruction::JUMP) + if (auto constant = llvm::dyn_cast(target)) { - if (targetBlock) - { - m_builder.CreateBr(targetBlock); - } - else - { - _basicBlock.setJumpTarget(target); - m_builder.CreateBr(getJumpTableBlock(_runtimeManager)); - } + // If target index is a constant do direct jump to the target block. + auto bb = jumpTable.findCaseValue(constant).getCaseSuccessor(); + jumpInst->setSuccessor(0, bb); } - else // JUMPI + else { - auto val = stack.pop(); - auto zero = Constant::get(0); - auto cond = m_builder.CreateICmpNE(val, zero, "nonzero"); - - if (targetBlock) - { - m_builder.CreateCondBr(cond, targetBlock, _nextBasicBlock); - } - else - { - _basicBlock.setJumpTarget(target); - m_builder.CreateCondBr(cond, getJumpTableBlock(_runtimeManager), _nextBasicBlock); - } + // Attach medatada to branch instruction with information about target index. + auto targetMd = llvm::MDNode::get(jumpInst->getContext(), llvm::LocalAsMetadata::get(target)); + jumpInst->setMetadata("target", targetMd); } break; } @@ -868,109 +794,12 @@ void Compiler::compileBasicBlock(BasicBlock& _basicBlock, RuntimeManager& _runti if (!_basicBlock.llvm()->getTerminator()) m_builder.CreateBr(_nextBasicBlock); - m_builder.SetInsertPoint(_basicBlock.llvm()->getFirstNonPHI()); - _runtimeManager.checkStackLimit(_basicBlock.localStack().getMaxSize(), _basicBlock.localStack().getDiff()); -} - - - -void Compiler::removeDeadBlocks() -{ - // Remove dead basic blocks - auto sthErased = false; - do - { - sthErased = false; - for (auto it = m_basicBlocks.begin(); it != m_basicBlocks.end();) - { - auto llvmBB = it->second.llvm(); - if (llvm::pred_begin(llvmBB) == llvm::pred_end(llvmBB)) - { - llvmBB->eraseFromParent(); - m_basicBlocks.erase(it++); - sthErased = true; - } - else - ++it; - } - } - while (sthErased); - - if (m_jumpTableBlock && llvm::pred_begin(m_jumpTableBlock->llvm()) == llvm::pred_end(m_jumpTableBlock->llvm())) - { - m_jumpTableBlock->llvm()->eraseFromParent(); - m_jumpTableBlock.reset(); - } - - if (m_badJumpBlock && llvm::pred_begin(m_badJumpBlock->llvm()) == llvm::pred_end(m_badJumpBlock->llvm())) - { - m_badJumpBlock->llvm()->eraseFromParent(); - m_badJumpBlock.reset(); - } -} + stack.finalize(m_builder, *_basicBlock.llvm()); // TODO: Use references -void Compiler::dumpCFGifRequired(std::string const& _dotfilePath) -{ - if (! m_options.dumpCFG) - return; - - // TODO: handle i/o failures - std::ofstream ofs(_dotfilePath); - dumpCFGtoStream(ofs); - ofs.close(); + m_builder.SetInsertPoint(_basicBlock.llvm()->getFirstNonPHI()); // TODO: Move to LocalStack::finalize + _runtimeManager.checkStackLimit(stack.minSize(), stack.maxSize(), stack.size()); } -void Compiler::dumpCFGtoStream(std::ostream& _out) -{ - _out << "digraph BB {\n" - << " node [shape=record, fontname=Courier, fontsize=10];\n" - << " entry [share=record, label=\"entry block\"];\n"; - - std::vector blocks; - for (auto& pair : m_basicBlocks) - blocks.push_back(&pair.second); - if (m_jumpTableBlock) - blocks.push_back(m_jumpTableBlock.get()); - if (m_badJumpBlock) - blocks.push_back(m_badJumpBlock.get()); - - // std::map phiNodesPerBlock; - - // Output nodes - for (auto bb : blocks) - { - std::string blockName = bb->llvm()->getName(); - - std::ostringstream oss; - bb->dump(oss, true); - - _out << " \"" << blockName << "\" [shape=record, label=\" { " << blockName << "|" << oss.str() << "} \"];\n"; - } - - // Output edges - for (auto bb : blocks) - { - std::string blockName = bb->llvm()->getName(); - - auto end = llvm::pred_end(bb->llvm()); - for (llvm::pred_iterator it = llvm::pred_begin(bb->llvm()); it != end; ++it) - { - _out << " \"" << (*it)->getName().str() << "\" -> \"" << blockName << "\" [" - << ((m_jumpTableBlock.get() && *it == m_jumpTableBlock.get()->llvm()) ? "style = dashed, " : "") - << "];\n"; - } - } - - _out << "}\n"; -} - -void Compiler::dump() -{ - for (auto& entry : m_basicBlocks) - entry.second.dump(); - if (m_jumpTableBlock != nullptr) - m_jumpTableBlock->dump(); -} } } diff --git a/evmjit/libevmjit/Compiler.h b/evmjit/libevmjit/Compiler.h index 0b31ae0f2..af45acedb 100644 --- a/evmjit/libevmjit/Compiler.h +++ b/evmjit/libevmjit/Compiler.h @@ -15,9 +15,6 @@ public: struct Options { - /// Optimize stack operations between basic blocks - bool optimizeStack = true; - /// Rewrite switch instructions to sequences of branches bool rewriteSwitchToBranches = true; @@ -25,32 +22,18 @@ public: bool dumpCFG = false; }; - using ProgramCounter = uint64_t; - Compiler(Options const& _options); std::unique_ptr compile(code_iterator _begin, code_iterator _end, std::string const& _id); private: - void createBasicBlocks(code_iterator _begin, code_iterator _end); - - void compileBasicBlock(BasicBlock& _basicBlock, class RuntimeManager& _runtimeManager, class Arith256& _arith, class Memory& _memory, class Ext& _ext, class GasMeter& _gasMeter, llvm::BasicBlock* _nextBasicBlock); - - llvm::BasicBlock* getJumpTableBlock(RuntimeManager& _runtimeManager); - - llvm::BasicBlock* getBadJumpBlock(RuntimeManager& _runtimeManager); + std::vector createBasicBlocks(code_iterator _begin, code_iterator _end, llvm::SwitchInst& _switchInst); - void removeDeadBlocks(); + void compileBasicBlock(BasicBlock& _basicBlock, class RuntimeManager& _runtimeManager, class Arith256& _arith, class Memory& _memory, class Ext& _ext, class GasMeter& _gasMeter, + llvm::BasicBlock* _nextBasicBlock, class Stack& _globalStack, llvm::SwitchInst& _jumpTable); - /// Dumps basic block graph in graphviz format to a file, if option dumpCFG is enabled. - void dumpCFGifRequired(std::string const& _dotfilePath); - - /// Dumps basic block graph in graphviz format to a stream. - void dumpCFGtoStream(std::ostream& _out); - - /// Dumps all basic blocks to stderr. Useful in a debugging session. - void dump(); + void fillJumpTable(); /// Compiler options Options const& m_options; @@ -58,9 +41,6 @@ private: /// Helper class for generating IR llvm::IRBuilder<> m_builder; - /// Maps a program counter pc to a basic block that starts at pc (if any). - std::map m_basicBlocks; - /// Stop basic block - terminates execution with STOP code (0) llvm::BasicBlock* m_stopBB = nullptr; @@ -68,13 +48,10 @@ private: llvm::BasicBlock* m_abortBB = nullptr; /// Block with a jump table. - std::unique_ptr m_jumpTableBlock; - - /// Destination for invalid jumps - std::unique_ptr m_badJumpBlock; + llvm::BasicBlock* m_jumpTableBB = nullptr; /// Main program function - llvm::Function* m_mainFunc = nullptr; + llvm::Function* m_mainFunc = nullptr; // TODO: Remove }; } diff --git a/evmjit/libevmjit/Optimizer.cpp b/evmjit/libevmjit/Optimizer.cpp index dea3ea2a0..9cc6aa54a 100644 --- a/evmjit/libevmjit/Optimizer.cpp +++ b/evmjit/libevmjit/Optimizer.cpp @@ -114,6 +114,7 @@ bool LowerEVMPass::doFinalization(llvm::Module&) bool prepare(llvm::Module& _module) { auto pm = llvm::legacy::PassManager{}; + pm.add(llvm::createCFGSimplificationPass()); pm.add(llvm::createDeadCodeEliminationPass()); pm.add(new LowerEVMPass{}); return pm.run(_module); diff --git a/evmjit/libevmjit/RuntimeManager.cpp b/evmjit/libevmjit/RuntimeManager.cpp index 971f14abd..bce378b64 100644 --- a/evmjit/libevmjit/RuntimeManager.cpp +++ b/evmjit/libevmjit/RuntimeManager.cpp @@ -110,8 +110,8 @@ RuntimeManager::RuntimeManager(llvm::IRBuilder<>& _builder, code_iterator _codeB m_gasPtr = m_builder.CreateAlloca(Type::Gas, nullptr, "gas.ptr"); m_builder.CreateStore(m_dataElts[RuntimeData::Index::Gas], m_gasPtr); - llvm::Type* checkStackLimitArgs[] = {Type::Size->getPointerTo(), Type::Size, Type::Size, Type::BytePtr}; - m_checkStackLimit = llvm::Function::Create(llvm::FunctionType::get(Type::Void, checkStackLimitArgs, false), llvm::Function::PrivateLinkage, "stack.checkSize", getModule()); + llvm::Type* checkStackLimitArgs[] = {Type::Size->getPointerTo(), Type::Size, Type::Size, Type::Size, Type::BytePtr}; + m_checkStackLimit = llvm::Function::Create(llvm::FunctionType::get(Type::Void, checkStackLimitArgs, false), llvm::Function::PrivateLinkage, "evm.stack.require", getModule()); m_checkStackLimit->setDoesNotThrow(); m_checkStackLimit->setDoesNotCapture(1); @@ -121,7 +121,9 @@ RuntimeManager::RuntimeManager(llvm::IRBuilder<>& _builder, code_iterator _codeB auto currSizePtr = &m_checkStackLimit->getArgumentList().front(); currSizePtr->setName("currSize"); - auto max = currSizePtr->getNextNode(); + auto min = currSizePtr->getNextNode(); + min->setName("min"); + auto max = min->getNextNode(); max->setName("max"); auto diff = max->getNextNode(); diff->setName("diff"); @@ -131,8 +133,11 @@ RuntimeManager::RuntimeManager(llvm::IRBuilder<>& _builder, code_iterator _codeB InsertPointGuard guard{m_builder}; m_builder.SetInsertPoint(checkBB); auto currSize = m_builder.CreateLoad(currSizePtr, "cur"); - auto maxSize = m_builder.CreateNUWAdd(currSize, max, "maxSize"); - auto ok = m_builder.CreateICmpULE(maxSize, m_builder.getInt64(1024), "ok"); + auto minSize = m_builder.CreateAdd(currSize, min, "minSize", false, true); + auto maxSize = m_builder.CreateAdd(currSize, max, "maxSize", true, true); + auto minOk = m_builder.CreateICmpSGE(minSize, m_builder.getInt64(0), "min.ok"); + auto maxOk = m_builder.CreateICmpULE(maxSize, m_builder.getInt64(1024), "max.ok"); + auto ok = m_builder.CreateAnd(minOk, maxOk, "ok"); m_builder.CreateCondBr(ok, updateBB, outOfStackBB, Type::expectTrue); m_builder.SetInsertPoint(updateBB); @@ -145,9 +150,9 @@ RuntimeManager::RuntimeManager(llvm::IRBuilder<>& _builder, code_iterator _codeB m_builder.CreateUnreachable(); } -void RuntimeManager::checkStackLimit(size_t _max, int _diff) +void RuntimeManager::checkStackLimit(ssize_t _min, ssize_t _max, ssize_t _diff) { - createCall(m_checkStackLimit, {m_stackSize, m_builder.getInt64(_max), m_builder.getInt64(_diff), getJmpBuf()}); + createCall(m_checkStackLimit, {m_stackSize, m_builder.getInt64(_min), m_builder.getInt64(_max), m_builder.getInt64(_diff), getJmpBuf()}); } llvm::Value* RuntimeManager::getRuntimePtr() diff --git a/evmjit/libevmjit/RuntimeManager.h b/evmjit/libevmjit/RuntimeManager.h index 8c0728aaf..8511c8898 100644 --- a/evmjit/libevmjit/RuntimeManager.h +++ b/evmjit/libevmjit/RuntimeManager.h @@ -50,7 +50,7 @@ public: static llvm::StructType* getRuntimeType(); static llvm::StructType* getRuntimeDataType(); - void checkStackLimit(size_t _max, int _diff); + void checkStackLimit(ssize_t _min, ssize_t _max, ssize_t _diff); private: llvm::Value* getPtr(RuntimeData::Index _index); diff --git a/evmjit/libevmjit/Stack.cpp b/evmjit/libevmjit/Stack.cpp index b7b8f25b1..273aa3855 100644 --- a/evmjit/libevmjit/Stack.cpp +++ b/evmjit/libevmjit/Stack.cpp @@ -16,52 +16,14 @@ namespace eth namespace jit { -Stack::Stack(llvm::IRBuilder<>& _builder, RuntimeManager& _runtimeManager): +Stack::Stack(llvm::IRBuilder<>& _builder): CompilerHelper(_builder), - m_runtimeManager(_runtimeManager), m_stack(_builder, "stack") {} -llvm::Function* Stack::getGetFunc() -{ - auto& func = m_get; - if (!func) - { - llvm::Type* argTypes[] = {Type::Size, Type::Size, Type::BytePtr}; - func = llvm::Function::Create(llvm::FunctionType::get(Type::Void, argTypes, false), llvm::Function::ExternalLinkage, "stack.require", getModule()); - - auto index = &func->getArgumentList().front(); - index->setName("index"); - auto size = index->getNextNode(); - size->setName("size"); - auto jmpBuf = size->getNextNode(); - jmpBuf->setName("jmpBuf"); - - InsertPointGuard guard{m_builder}; - auto entryBB = llvm::BasicBlock::Create(m_builder.getContext(), {}, func); - auto underflowBB = llvm::BasicBlock::Create(m_builder.getContext(), "Underflow", func); - auto returnBB = llvm::BasicBlock::Create(m_builder.getContext(), "Return", func); - - m_builder.SetInsertPoint(entryBB); - auto underflow = m_builder.CreateICmpUGE(index, size, "underflow"); - m_builder.CreateCondBr(underflow, underflowBB, returnBB); - - m_builder.SetInsertPoint(underflowBB); - m_runtimeManager.abort(jmpBuf); - m_builder.CreateUnreachable(); - - m_builder.SetInsertPoint(returnBB); - m_builder.CreateRetVoid(); - } - return func; -} - llvm::Value* Stack::get(size_t _index) { - createCall(getGetFunc(), {m_builder.getInt64(_index), m_stack.size(), m_runtimeManager.getJmpBuf()}); - auto value = m_stack.get(m_builder.CreateSub(m_stack.size(), m_builder.getInt64(_index + 1))); - //return m_builder.CreateLoad(valuePtr); - return value; + return m_stack.get(m_builder.CreateSub(m_stack.size(), m_builder.getInt64(_index + 1))); } void Stack::set(size_t _index, llvm::Value* _value) @@ -71,8 +33,6 @@ void Stack::set(size_t _index, llvm::Value* _value) void Stack::pop(size_t _count) { - // FIXME: Pop does not check for stack underflow but looks like not needed - // We should place stack.require() check and begining of every BB m_stack.pop(m_builder.getInt64(_count)); } diff --git a/evmjit/libevmjit/Stack.h b/evmjit/libevmjit/Stack.h index ad10dae12..72e3d88d2 100644 --- a/evmjit/libevmjit/Stack.h +++ b/evmjit/libevmjit/Stack.h @@ -1,7 +1,5 @@ #pragma once -#include - #include "Array.h" namespace dev @@ -10,12 +8,11 @@ namespace eth { namespace jit { -class RuntimeManager; -class Stack : public CompilerHelper +class Stack: public CompilerHelper { public: - Stack(llvm::IRBuilder<>& builder, RuntimeManager& runtimeManager); + Stack(llvm::IRBuilder<>& builder); llvm::Value* get(size_t _index); void set(size_t _index, llvm::Value* _value); @@ -24,10 +21,6 @@ public: void free() { m_stack.free(); } private: - llvm::Function* getGetFunc(); - - RuntimeManager& m_runtimeManager; - llvm::Function* m_get = nullptr; Array m_stack; }; @@ -35,5 +28,3 @@ private: } } } - - diff --git a/libevm/SmartVM.cpp b/libevm/SmartVM.cpp index 565e88213..965adf13e 100644 --- a/libevm/SmartVM.cpp +++ b/libevm/SmartVM.cpp @@ -54,7 +54,7 @@ namespace bool isStopSentinel() { - assert((!code.empty() || !codeHash) && "'empty code => empty hash' invariand failed"); + assert((!code.empty() || !codeHash) && "'empty code => empty hash' invariant failed"); return code.empty(); } }; @@ -102,7 +102,7 @@ bytesConstRef SmartVM::execImpl(u256& io_gas, ExtVMFace& _ext, OnOpFunc const& _ clog(JitInfo) << "JIT: " << codeHash; vmKind = VMKind::JIT; } - else + else if (!_ext.code.empty()) // This check is needed for VM tests { static JitWorker s_worker; diff --git a/test/libevm/VMTestsFiller/vmIOandFlowOperationsTestFiller.json b/test/libevm/VMTestsFiller/vmIOandFlowOperationsTestFiller.json index ece519112..4dfa41f6a 100644 --- a/test/libevm/VMTestsFiller/vmIOandFlowOperationsTestFiller.json +++ b/test/libevm/VMTestsFiller/vmIOandFlowOperationsTestFiller.json @@ -2281,6 +2281,130 @@ } }, + "DynamicJump_value1": { + "env" : { + "previousHash" : "5e20a0453cecd065ea59c37ac63e079ee08998b6045136a8ce6635c7912ec0b6", + "currentNumber" : "0", + "currentGasLimit" : "1000000", + "currentDifficulty" : "256", + "currentTimestamp" : "1", + "currentCoinbase" : "2adc25665018aa1fe0e6bc666dac8fc2697ff9ba" + }, + "pre" : { + "0f572e5295c57f15886f9b263e2f6d2d6c7b5ec6" : { + "balance" : "100000000000000000000000", + "nonce" : "0", + "code" : "(asm 1 2 3 CALLVALUE JUMP + JUMPDEST POP POP 0 MSTORE MSIZE 0 RETURN + JUMPDEST POP 0 MSTORE MSIZE 0 RETURN + JUMPDEST 0 MSTORE MSIZE 0 RETURN)", + "storage": {} + } + }, + "exec" : { + "address" : "0f572e5295c57f15886f9b263e2f6d2d6c7b5ec6", + "origin" : "cd1722f3947def4cf144679da39c4c32bdc35681", + "caller" : "cd1722f3947def4cf144679da39c4c32bdc35681", + "value" : "8", + "data" : "", + "gasPrice" : "100000000000000", + "gas" : "100000" + } + }, + + "DynamicJump_value2": { + "env" : { + "previousHash" : "5e20a0453cecd065ea59c37ac63e079ee08998b6045136a8ce6635c7912ec0b6", + "currentNumber" : "0", + "currentGasLimit" : "1000000", + "currentDifficulty" : "256", + "currentTimestamp" : "1", + "currentCoinbase" : "2adc25665018aa1fe0e6bc666dac8fc2697ff9ba" + }, + "pre" : { + "0f572e5295c57f15886f9b263e2f6d2d6c7b5ec6" : { + "balance" : "100000000000000000000000", + "nonce" : "0", + "code" : "(asm 1 2 3 CALLVALUE JUMP + JUMPDEST POP POP 0 MSTORE MSIZE 0 RETURN + JUMPDEST POP 0 MSTORE MSIZE 0 RETURN + JUMPDEST 0 MSTORE MSIZE 0 RETURN)", + "storage": {} + } + }, + "exec" : { + "address" : "0f572e5295c57f15886f9b263e2f6d2d6c7b5ec6", + "origin" : "cd1722f3947def4cf144679da39c4c32bdc35681", + "caller" : "cd1722f3947def4cf144679da39c4c32bdc35681", + "value" : "18", + "data" : "", + "gasPrice" : "100000000000000", + "gas" : "100000" + } + }, + + "DynamicJump_value3": { + "env" : { + "previousHash" : "5e20a0453cecd065ea59c37ac63e079ee08998b6045136a8ce6635c7912ec0b6", + "currentNumber" : "0", + "currentGasLimit" : "1000000", + "currentDifficulty" : "256", + "currentTimestamp" : "1", + "currentCoinbase" : "2adc25665018aa1fe0e6bc666dac8fc2697ff9ba" + }, + "pre" : { + "0f572e5295c57f15886f9b263e2f6d2d6c7b5ec6" : { + "balance" : "100000000000000000000000", + "nonce" : "0", + "code" : "(asm 1 2 3 CALLVALUE JUMP + JUMPDEST POP POP 0 MSTORE MSIZE 0 RETURN + JUMPDEST POP 0 MSTORE MSIZE 0 RETURN + JUMPDEST 0 MSTORE MSIZE 0 RETURN)", + "storage": {} + } + }, + "exec" : { + "address" : "0f572e5295c57f15886f9b263e2f6d2d6c7b5ec6", + "origin" : "cd1722f3947def4cf144679da39c4c32bdc35681", + "caller" : "cd1722f3947def4cf144679da39c4c32bdc35681", + "value" : "27", + "data" : "", + "gasPrice" : "100000000000000", + "gas" : "100000" + } + }, + + "DynamicJump_valueUnderflow": { + "env" : { + "previousHash" : "5e20a0453cecd065ea59c37ac63e079ee08998b6045136a8ce6635c7912ec0b6", + "currentNumber" : "0", + "currentGasLimit" : "1000000", + "currentDifficulty" : "256", + "currentTimestamp" : "1", + "currentCoinbase" : "2adc25665018aa1fe0e6bc666dac8fc2697ff9ba" + }, + "pre" : { + "0f572e5295c57f15886f9b263e2f6d2d6c7b5ec6" : { + "balance" : "100000000000000000000000", + "nonce" : "0", + "code" : "(asm 1 2 3 CALLVALUE JUMP + JUMPDEST POP POP 0 MSTORE MSIZE 0 RETURN + JUMPDEST POP 0 MSTORE MSIZE 0 RETURN + JUMPDEST POP POP POP 0 MSTORE MSIZE 0 RETURN)", + "storage": {} + } + }, + "exec" : { + "address" : "0f572e5295c57f15886f9b263e2f6d2d6c7b5ec6", + "origin" : "cd1722f3947def4cf144679da39c4c32bdc35681", + "caller" : "cd1722f3947def4cf144679da39c4c32bdc35681", + "value" : "27", + "data" : "", + "gasPrice" : "100000000000000", + "gas" : "100000" + } + }, + "BlockNumberDynamicJumpiAfterStop": { "env" : { "previousHash" : "5e20a0453cecd065ea59c37ac63e079ee08998b6045136a8ce6635c7912ec0b6",