From 4ef4f7658f090580e7bc6910969aacfe67506ee4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Pawe=C5=82=20Bylica?= Date: Thu, 23 Jul 2015 15:56:54 +0200 Subject: [PATCH 01/24] Fix empty EVM code in SmartVM. --- libevm/SmartVM.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/libevm/SmartVM.cpp b/libevm/SmartVM.cpp index 50a548002..abbe4fddf 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; From 947b9e9664d768c9bf84658524cff78ec1a0cc83 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Pawe=C5=82=20Bylica?= Date: Thu, 23 Jul 2015 16:24:15 +0200 Subject: [PATCH 02/24] Replace "bad jump" exit with "abort" exit. --- evmjit/include/evmjit/JIT-c.h | 1 - evmjit/include/evmjit/JIT.h | 1 - evmjit/libevmjit-cpp/JitVM.cpp | 2 -- evmjit/libevmjit/Compiler.cpp | 30 +++++------------------------- evmjit/libevmjit/Compiler.h | 7 +------ 5 files changed, 6 insertions(+), 35 deletions(-) diff --git a/evmjit/include/evmjit/JIT-c.h b/evmjit/include/evmjit/JIT-c.h index a92b29090..c7b91b48a 100644 --- a/evmjit/include/evmjit/JIT-c.h +++ b/evmjit/include/evmjit/JIT-c.h @@ -40,7 +40,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 diff --git a/evmjit/include/evmjit/JIT.h b/evmjit/include/evmjit/JIT.h index e74534243..91b088dc3 100644 --- a/evmjit/include/evmjit/JIT.h +++ b/evmjit/include/evmjit/JIT.h @@ -102,7 +102,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 diff --git a/evmjit/libevmjit-cpp/JitVM.cpp b/evmjit/libevmjit-cpp/JitVM.cpp index d96da87c1..43565f8bf 100644 --- a/evmjit/libevmjit-cpp/JitVM.cpp +++ b/evmjit/libevmjit-cpp/JitVM.cpp @@ -60,8 +60,6 @@ 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 diff --git a/evmjit/libevmjit/Compiler.cpp b/evmjit/libevmjit/Compiler.cpp index a44e4e890..16a842c89 100644 --- a/evmjit/libevmjit/Compiler.cpp +++ b/evmjit/libevmjit/Compiler.cpp @@ -94,7 +94,7 @@ void Compiler::createBasicBlocks(code_iterator _codeBegin, code_iterator _codeEn } } -llvm::BasicBlock* Compiler::getJumpTableBlock(RuntimeManager& _runtimeManager) +llvm::BasicBlock* Compiler::getJumpTableBlock() { if (!m_jumpTableBlock) { @@ -102,7 +102,7 @@ llvm::BasicBlock* Compiler::getJumpTableBlock(RuntimeManager& _runtimeManager) 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)); + auto switchInstr = m_builder.CreateSwitch(dest, m_abortBB); for (auto&& p : m_basicBlocks) { if (p.second.isJumpDest()) @@ -112,18 +112,6 @@ llvm::BasicBlock* Compiler::getJumpTableBlock(RuntimeManager& _runtimeManager) return m_jumpTableBlock->llvm(); } -llvm::BasicBlock* Compiler::getBadJumpBlock(RuntimeManager& _runtimeManager) -{ - if (!m_badJumpBlock) - { - 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); - } - return m_badJumpBlock->llvm(); -} - std::unique_ptr Compiler::compile(code_iterator _begin, code_iterator _end, std::string const& _id) { auto module = std::unique_ptr(new llvm::Module(_id, m_builder.getContext())); @@ -616,7 +604,7 @@ void Compiler::compileBasicBlock(BasicBlock& _basicBlock, RuntimeManager& _runti 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); + targetBlock = (it != m_basicBlocks.end() && it->second.isJumpDest()) ? it->second.llvm() : m_abortBB; } // TODO: Improve; check for constants @@ -629,7 +617,7 @@ void Compiler::compileBasicBlock(BasicBlock& _basicBlock, RuntimeManager& _runti else { _basicBlock.setJumpTarget(target); - m_builder.CreateBr(getJumpTableBlock(_runtimeManager)); + m_builder.CreateBr(getJumpTableBlock()); } } else // JUMPI @@ -645,7 +633,7 @@ void Compiler::compileBasicBlock(BasicBlock& _basicBlock, RuntimeManager& _runti else { _basicBlock.setJumpTarget(target); - m_builder.CreateCondBr(cond, getJumpTableBlock(_runtimeManager), _nextBasicBlock); + m_builder.CreateCondBr(cond, getJumpTableBlock(), _nextBasicBlock); } } break; @@ -901,12 +889,6 @@ void Compiler::removeDeadBlocks() 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(); - } } void Compiler::dumpCFGifRequired(std::string const& _dotfilePath) @@ -931,8 +913,6 @@ void Compiler::dumpCFGtoStream(std::ostream& _out) 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; diff --git a/evmjit/libevmjit/Compiler.h b/evmjit/libevmjit/Compiler.h index 0b31ae0f2..7c90fbceb 100644 --- a/evmjit/libevmjit/Compiler.h +++ b/evmjit/libevmjit/Compiler.h @@ -37,9 +37,7 @@ private: 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); + llvm::BasicBlock* getJumpTableBlock(); void removeDeadBlocks(); @@ -70,9 +68,6 @@ private: /// Block with a jump table. std::unique_ptr m_jumpTableBlock; - /// Destination for invalid jumps - std::unique_ptr m_badJumpBlock; - /// Main program function llvm::Function* m_mainFunc = nullptr; }; From 0e3629387af93976a9bcc8c5eed999238b555b05 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Pawe=C5=82=20Bylica?= Date: Thu, 23 Jul 2015 16:29:40 +0200 Subject: [PATCH 03/24] Remove unused return codes. --- evmjit/include/evmjit/JIT-c.h | 3 --- evmjit/include/evmjit/JIT.h | 3 --- evmjit/libevmjit-cpp/JitVM.cpp | 4 ---- 3 files changed, 10 deletions(-) diff --git a/evmjit/include/evmjit/JIT-c.h b/evmjit/include/evmjit/JIT-c.h index c7b91b48a..98e8ade33 100644 --- a/evmjit/include/evmjit/JIT-c.h +++ b/evmjit/include/evmjit/JIT-c.h @@ -39,9 +39,6 @@ typedef enum evmjit_return_code // Standard error codes OutOfGas = -1, - StackUnderflow = -2, - 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 91b088dc3..285a33218 100644 --- a/evmjit/include/evmjit/JIT.h +++ b/evmjit/include/evmjit/JIT.h @@ -101,9 +101,6 @@ enum class ReturnCode // Standard error codes OutOfGas = -1, - StackUnderflow = -2, - 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 43565f8bf..d05632955 100644 --- a/evmjit/libevmjit-cpp/JitVM.cpp +++ b/evmjit/libevmjit-cpp/JitVM.cpp @@ -62,10 +62,6 @@ bytesConstRef JitVM::execImpl(u256& io_gas, ExtVMFace& _ext, OnOpFunc const& _on 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; From c80ded8f9793216a27f9b40d95e197447ae8169d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Pawe=C5=82=20Bylica?= Date: Fri, 24 Jul 2015 15:01:58 +0200 Subject: [PATCH 04/24] Move LocalStack out of BasicBlock. --- evmjit/libevmjit/BasicBlock.cpp | 22 +++++------ evmjit/libevmjit/BasicBlock.h | 67 ++++++++++++++++----------------- evmjit/libevmjit/Compiler.cpp | 8 ++-- evmjit/libevmjit/Compiler.h | 2 +- 4 files changed, 47 insertions(+), 52 deletions(-) diff --git a/evmjit/libevmjit/BasicBlock.cpp b/evmjit/libevmjit/BasicBlock.cpp index a41743d0b..b40296f40 100644 --- a/evmjit/libevmjit/BasicBlock.cpp +++ b/evmjit/libevmjit/BasicBlock.cpp @@ -28,23 +28,22 @@ BasicBlock::BasicBlock(instr_idx _firstInstrIdx, code_iterator _begin, code_iter 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) {} -BasicBlock::LocalStack::LocalStack(BasicBlock& _owner) : - m_bblock(_owner) +LocalStack::LocalStack(BasicBlock& _owner, Stack& _globalStack) : + m_bblock(_owner), + 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); @@ -52,7 +51,7 @@ void BasicBlock::LocalStack::push(llvm::Value* _value) m_maxSize = std::max(m_maxSize, m_bblock.m_currentStack.size()); } -llvm::Value* BasicBlock::LocalStack::pop() +llvm::Value* LocalStack::pop() { auto result = get(0); @@ -66,7 +65,7 @@ llvm::Value* BasicBlock::LocalStack::pop() /** * 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 +75,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,7 +84,7 @@ void BasicBlock::LocalStack::swap(size_t _index) set(0, val); } -std::vector::iterator BasicBlock::LocalStack::getItemIterator(size_t _index) +std::vector::iterator LocalStack::getItemIterator(size_t _index) { auto& currentStack = m_bblock.m_currentStack; if (_index < currentStack.size()) @@ -98,7 +97,7 @@ std::vector::iterator BasicBlock::LocalStack::getItemIterator(size return currentStack.end() - _index - 1; } -llvm::Value* BasicBlock::LocalStack::get(size_t _index) +llvm::Value* LocalStack::get(size_t _index) { auto& initialStack = m_bblock.m_initialStack; auto itemIter = getItemIterator(_index); @@ -124,7 +123,7 @@ llvm::Value* BasicBlock::LocalStack::get(size_t _index) return *itemIter; } -void BasicBlock::LocalStack::set(size_t _index, llvm::Value* _word) +void LocalStack::set(size_t _index, llvm::Value* _word) { auto itemIter = getItemIterator(_index); *itemIter = _word; @@ -388,4 +387,3 @@ void BasicBlock::dump(std::ostream& _out, bool _dotOutput) } } } - diff --git a/evmjit/libevmjit/BasicBlock.h b/evmjit/libevmjit/BasicBlock.h index 321499196..cbac2b048 100644 --- a/evmjit/libevmjit/BasicBlock.h +++ b/evmjit/libevmjit/BasicBlock.h @@ -14,47 +14,48 @@ namespace jit using namespace evmjit; using instr_idx = uint64_t; -class BasicBlock +class BasicBlock; + +class LocalStack { public: - class LocalStack - { - public: - /// Pushes value on stack - void push(llvm::Value* _value); + LocalStack(BasicBlock& _owner, Stack& _globalStack); + LocalStack(LocalStack const&) = delete; + void operator=(LocalStack const&) = delete; - /// Pops and returns top value - llvm::Value* pop(); + /// Pushes value on stack + void push(llvm::Value* _value); - /// Duplicates _index'th value on stack - void dup(size_t _index); + /// Pops and returns top value + llvm::Value* pop(); - /// 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); + /// Duplicates _index'th value on stack + void dup(size_t _index); - size_t getMaxSize() const { return m_maxSize; } - int getDiff() const { return m_bblock.m_tosOffset; } + /// 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); - private: - LocalStack(BasicBlock& _owner); - LocalStack(LocalStack const&) = delete; - void operator=(LocalStack const&) = delete; - friend BasicBlock; + size_t getMaxSize() const { return m_maxSize; } - /// 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); + std::vector::iterator getItemIterator(size_t _index); - private: - BasicBlock& m_bblock; - size_t m_maxSize = 0; ///< Max size reached by the stack. - }; +private: + BasicBlock& m_bblock; + Stack& m_global; + size_t m_maxSize = 0; ///< Max size reached by the stack. +}; +class BasicBlock +{ +public: 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); @@ -72,7 +73,7 @@ public: llvm::Value* getJumpTarget() const { return m_jumpTarget; } void setJumpTarget(llvm::Value* _jumpTarget) { m_jumpTarget = _jumpTarget; } - LocalStack& localStack() { return m_stack; } + int getDiff() const { return m_tosOffset; } /// Optimization: propagates values between local stacks in basic blocks /// to avoid excessive pushing/popping on the EVM stack. @@ -93,10 +94,6 @@ private: 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 @@ -111,6 +108,7 @@ private: /// 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; + friend class LocalStack; /// How many items higher is the current stack than the initial one. /// May be negative. @@ -127,4 +125,3 @@ private: } } } - diff --git a/evmjit/libevmjit/Compiler.cpp b/evmjit/libevmjit/Compiler.cpp index 16a842c89..73fc3c0d9 100644 --- a/evmjit/libevmjit/Compiler.cpp +++ b/evmjit/libevmjit/Compiler.cpp @@ -163,7 +163,7 @@ std::unique_ptr Compiler::compile(code_iterator _begin, code_itera auto iterCopy = basicBlockPairIt; ++iterCopy; auto nextBasicBlock = (iterCopy != m_basicBlocks.end()) ? iterCopy->second.llvm() : nullptr; - compileBasicBlock(basicBlock, runtimeManager, arith, memory, ext, gasMeter, nextBasicBlock); + compileBasicBlock(basicBlock, runtimeManager, arith, memory, ext, gasMeter, nextBasicBlock, stack); } // Code for special blocks: @@ -224,13 +224,13 @@ std::unique_ptr Compiler::compile(code_iterator _begin, code_itera 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) { if (!_nextBasicBlock) // this is the last block in the code _nextBasicBlock = m_stopBB; m_builder.SetInsertPoint(_basicBlock.llvm()); - auto& stack = _basicBlock.localStack(); + LocalStack stack{_basicBlock, _globalStack}; for (auto it = _basicBlock.begin(); it != _basicBlock.end(); ++it) { @@ -857,7 +857,7 @@ void Compiler::compileBasicBlock(BasicBlock& _basicBlock, RuntimeManager& _runti m_builder.CreateBr(_nextBasicBlock); m_builder.SetInsertPoint(_basicBlock.llvm()->getFirstNonPHI()); - _runtimeManager.checkStackLimit(_basicBlock.localStack().getMaxSize(), _basicBlock.localStack().getDiff()); + _runtimeManager.checkStackLimit(stack.getMaxSize(), _basicBlock.getDiff()); } diff --git a/evmjit/libevmjit/Compiler.h b/evmjit/libevmjit/Compiler.h index 7c90fbceb..e606044e8 100644 --- a/evmjit/libevmjit/Compiler.h +++ b/evmjit/libevmjit/Compiler.h @@ -35,7 +35,7 @@ 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); + 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::BasicBlock* getJumpTableBlock(); From b604dfefe6d0078f5d3786dd7971642753cad156 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Pawe=C5=82=20Bylica?= Date: Mon, 27 Jul 2015 11:55:49 +0200 Subject: [PATCH 05/24] Reimplementation of local stack handling during basic block compilation. This change removed the intermediate stage when PHI nodes are inserted in place of items fetched from global stack. PHi stage requires information about other basic block therefore must be postponed to the point when all basic blocks has been compiled. In the same time this optimization has not been very effective. --- evmjit/libevmjit/BasicBlock.cpp | 119 +++++++++++--------------------- evmjit/libevmjit/BasicBlock.h | 4 +- evmjit/libevmjit/Compiler.h | 3 +- 3 files changed, 45 insertions(+), 81 deletions(-) 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; From fcacf6e3a2681bf94f4d571925e493e75ad0ce70 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Pawe=C5=82=20Bylica?= Date: Mon, 27 Jul 2015 12:20:12 +0200 Subject: [PATCH 06/24] Remove code doing stack optimization with PHI nodes. --- evmjit/libevmjit/BasicBlock.cpp | 145 +--------------------------- evmjit/libevmjit/BasicBlock.h | 10 +- evmjit/libevmjit/Compiler.cpp | 14 --- evmjit/libevmjit/Compiler.h | 4 - evmjit/libevmjit/RuntimeManager.cpp | 2 +- evmjit/libevmjit/RuntimeManager.h | 2 +- 6 files changed, 4 insertions(+), 173 deletions(-) diff --git a/evmjit/libevmjit/BasicBlock.cpp b/evmjit/libevmjit/BasicBlock.cpp index bb2673d9c..83ef3c7eb 100644 --- a/evmjit/libevmjit/BasicBlock.cpp +++ b/evmjit/libevmjit/BasicBlock.cpp @@ -47,7 +47,6 @@ 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_currentStack.size()); } @@ -61,7 +60,6 @@ llvm::Value* LocalStack::pop() else ++m_bblock.m_globalPops; - m_bblock.m_tosOffset -= 1; return item; } @@ -151,147 +149,6 @@ void BasicBlock::synchronizeLocalStack(Stack& _evmStack) if (m_globalPops) _evmStack.pop(m_globalPops); } - - m_tosOffset = 0; -} - -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); - - 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) - { - for (auto& pair : cfg) - { - DLOG(bb) << pair.second.bblock.llvm()->getName().str() - << ": in " << pair.second.inputItems - << ", out " << pair.second.outputItems - << "\n"; - } - - valuesChanged = false; - for (auto& pair : cfg) - { - 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; - } - } - } - } - - // 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) - { - 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); - } - } - - // 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() @@ -323,7 +180,7 @@ void BasicBlock::dump(std::ostream& _out, bool _dotOutput) out << *ins << (_dotOutput ? "\\l" : "\n"); if (! _dotOutput) - out << "Current stack (offset = " << m_tosOffset << "):\n"; + out << "Current stack:\n"; else out << "|"; diff --git a/evmjit/libevmjit/BasicBlock.h b/evmjit/libevmjit/BasicBlock.h index 7a20d4104..3111eb942 100644 --- a/evmjit/libevmjit/BasicBlock.h +++ b/evmjit/libevmjit/BasicBlock.h @@ -71,11 +71,7 @@ public: llvm::Value* getJumpTarget() const { return m_jumpTarget; } void setJumpTarget(llvm::Value* _jumpTarget) { m_jumpTarget = _jumpTarget; } - int getDiff() const { return m_tosOffset; } - - /// 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); + ssize_t getDiff() const { return static_cast(m_currentStack.size()) - static_cast(m_globalPops); } /// Synchronize current local stack with the EVM stack. void synchronizeLocalStack(Stack& _evmStack); @@ -108,10 +104,6 @@ private: std::vector m_currentStack; friend class LocalStack; - /// How many items higher is the current stack than the initial one. - /// 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. diff --git a/evmjit/libevmjit/Compiler.cpp b/evmjit/libevmjit/Compiler.cpp index 73fc3c0d9..05d5a0fd1 100644 --- a/evmjit/libevmjit/Compiler.cpp +++ b/evmjit/libevmjit/Compiler.cpp @@ -198,20 +198,6 @@ std::unique_ptr Compiler::compile(code_iterator _begin, code_itera 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) diff --git a/evmjit/libevmjit/Compiler.h b/evmjit/libevmjit/Compiler.h index b6b3c3c2d..b9a9aae9b 100644 --- a/evmjit/libevmjit/Compiler.h +++ b/evmjit/libevmjit/Compiler.h @@ -15,10 +15,6 @@ public: struct Options { - /// Optimize stack operations between basic blocks - /// TODO: Remove. It must be implemented as a separated pass. - bool optimizeStack = false; - /// Rewrite switch instructions to sequences of branches bool rewriteSwitchToBranches = true; diff --git a/evmjit/libevmjit/RuntimeManager.cpp b/evmjit/libevmjit/RuntimeManager.cpp index 971f14abd..b55d90a1a 100644 --- a/evmjit/libevmjit/RuntimeManager.cpp +++ b/evmjit/libevmjit/RuntimeManager.cpp @@ -145,7 +145,7 @@ RuntimeManager::RuntimeManager(llvm::IRBuilder<>& _builder, code_iterator _codeB m_builder.CreateUnreachable(); } -void RuntimeManager::checkStackLimit(size_t _max, int _diff) +void RuntimeManager::checkStackLimit(size_t _max, ssize_t _diff) { createCall(m_checkStackLimit, {m_stackSize, m_builder.getInt64(_max), m_builder.getInt64(_diff), getJmpBuf()}); } diff --git a/evmjit/libevmjit/RuntimeManager.h b/evmjit/libevmjit/RuntimeManager.h index 8c0728aaf..aa8f4838b 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(size_t _max, ssize_t _diff); private: llvm::Value* getPtr(RuntimeData::Index _index); From 1e926fe6bc5b82c53d23a0797dcf037e0c64052d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Pawe=C5=82=20Bylica?= Date: Mon, 27 Jul 2015 13:57:48 +0200 Subject: [PATCH 07/24] Move fields from BasicBlock to LocalStack. Remove dump() functions. --- evmjit/libevmjit/BasicBlock.cpp | 115 ++++++++------------------------ evmjit/libevmjit/BasicBlock.h | 55 ++++++--------- evmjit/libevmjit/Compiler.cpp | 82 ++--------------------- evmjit/libevmjit/Compiler.h | 9 --- 4 files changed, 57 insertions(+), 204 deletions(-) diff --git a/evmjit/libevmjit/BasicBlock.cpp b/evmjit/libevmjit/BasicBlock.cpp index 83ef3c7eb..f4f8b4645 100644 --- a/evmjit/libevmjit/BasicBlock.cpp +++ b/evmjit/libevmjit/BasicBlock.cpp @@ -23,42 +23,39 @@ 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, bool isJumpDest): 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_builder(_builder), m_isJumpDest(isJumpDest) {} -BasicBlock::BasicBlock(std::string _name, llvm::Function* _mainFunc, llvm::IRBuilder<>& _builder, bool isJumpDest) : +BasicBlock::BasicBlock(std::string _name, llvm::Function* _mainFunc, bool isJumpDest): m_llvmBB(llvm::BasicBlock::Create(_mainFunc->getContext(), _name, _mainFunc)), - m_builder(_builder), m_isJumpDest(isJumpDest) {} -LocalStack::LocalStack(BasicBlock& _owner, Stack& _globalStack) : - m_bblock(_owner), +LocalStack::LocalStack(Stack& _globalStack): m_global(_globalStack) {} void LocalStack::push(llvm::Value* _value) { assert(_value->getType() == Type::Word); - m_bblock.m_currentStack.push_back(_value); - m_maxSize = std::max(m_maxSize, m_bblock.m_currentStack.size()); + m_currentStack.push_back(_value); + m_maxSize = std::max(m_maxSize, m_currentStack.size()); // FIXME: This is wrong too. + add min size; } llvm::Value* LocalStack::pop() { auto item = get(0); - assert(!m_bblock.m_currentStack.empty() || !m_bblock.m_initialStack.empty()); + assert(!m_currentStack.empty() || !m_initialStack.empty()); - if (m_bblock.m_currentStack.size() > 0) - m_bblock.m_currentStack.pop_back(); + if (m_currentStack.size() > 0) + m_currentStack.pop_back(); else - ++m_bblock.m_globalPops; + ++m_globalPops; return item; } @@ -87,15 +84,13 @@ void LocalStack::swap(size_t _index) llvm::Value* LocalStack::get(size_t _index) { - auto& currentStack = m_bblock.m_currentStack; - if (_index < currentStack.size()) - return *(currentStack.rbegin() + _index); // count from back + if (_index < m_currentStack.size()) + return *(m_currentStack.rbegin() + _index); // count from back - auto& initialStack = m_bblock.m_initialStack; - auto idx = _index - currentStack.size() + m_bblock.m_globalPops; - if (idx >= initialStack.size()) - initialStack.resize(idx + 1); - auto& item = initialStack[idx]; + auto idx = _index - m_currentStack.size() + m_globalPops; + if (idx >= m_initialStack.size()) + m_initialStack.resize(idx + 1); + auto& item = m_initialStack[idx]; if (!item) item = m_global.get(idx); @@ -105,104 +100,50 @@ llvm::Value* LocalStack::get(size_t _index) void LocalStack::set(size_t _index, llvm::Value* _word) { - auto& currentStack = m_bblock.m_currentStack; - if (_index < currentStack.size()) + if (_index < m_currentStack.size()) { - *(currentStack.rbegin() + _index) = _word; + *(m_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; + auto idx = _index - m_currentStack.size() + m_globalPops; + assert(idx < m_initialStack.size()); + m_initialStack[idx] = _word; } -void BasicBlock::synchronizeLocalStack(Stack& _evmStack) +void LocalStack::finalize(llvm::IRBuilder<>& _builder, llvm::BasicBlock& _bb) { - auto blockTerminator = m_llvmBB->getTerminator(); + auto blockTerminator = _bb.getTerminator(); assert(blockTerminator); if (blockTerminator->getOpcode() != llvm::Instruction::Ret) { // Not needed in case of ret instruction. Ret invalidates the stack. - m_builder.SetInsertPoint(blockTerminator); + _builder.SetInsertPoint(blockTerminator); // 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) { if (m_initialStack[i]) - _evmStack.set(i, m_initialStack[i]); + m_global.set(i, m_initialStack[i]); } // Add new items for (auto& item: m_currentStack) { - if (m_globalPops) // Override poped global items - _evmStack.set(--m_globalPops, item); // using pops counter as the index + if (m_globalPops) // Override poped global items + m_global.set(--m_globalPops, item); // using pops counter as the index else - _evmStack.push(item); + m_global.push(item); } // Pop not overriden items if (m_globalPops) - _evmStack.pop(m_globalPops); + m_global.pop(m_globalPops); } } -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:\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"); - } - - if (! _dotOutput) - out << " ...\n----------------------------------------\n"; -} - - - } } diff --git a/evmjit/libevmjit/BasicBlock.h b/evmjit/libevmjit/BasicBlock.h index 3111eb942..75b0efeab 100644 --- a/evmjit/libevmjit/BasicBlock.h +++ b/evmjit/libevmjit/BasicBlock.h @@ -19,7 +19,7 @@ class BasicBlock; class LocalStack { public: - LocalStack(BasicBlock& _owner, Stack& _globalStack); + explicit LocalStack(Stack& _globalStack); LocalStack(LocalStack const&) = delete; void operator=(LocalStack const&) = delete; @@ -36,8 +36,13 @@ public: /// @param _index Index of value to be swaped. Must be > 0. void swap(size_t _index); + ssize_t getDiff() const { return static_cast(m_currentStack.size()) - static_cast(m_globalPops); } size_t getMaxSize() const { return m_maxSize; } + /// TODO: comment + /// TODO: It must be the same builder as in global stack. + void finalize(llvm::IRBuilder<>& _builder, llvm::BasicBlock& _bb); + private: /// Gets _index'th value from top (counting from 0) llvm::Value* get(size_t _index); @@ -45,8 +50,20 @@ private: /// Sets _index'th value from top (counting from 0) void set(size_t _index, llvm::Value* _value); -private: - BasicBlock& m_bblock; + /// 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; + + size_t m_globalPops = 0; ///< Number of items poped from global stack. In other words: global - local stack overlap. Stack& m_global; size_t m_maxSize = 0; ///< Max size reached by the stack. }; @@ -54,8 +71,8 @@ private: class BasicBlock { public: - 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); + explicit BasicBlock(instr_idx _firstInstrIdx, code_iterator _begin, code_iterator _end, llvm::Function* _mainFunc, bool isJumpDest); + explicit BasicBlock(std::string _name, llvm::Function* _mainFunc, bool isJumpDest); BasicBlock(const BasicBlock&) = delete; BasicBlock& operator=(const BasicBlock&) = delete; @@ -71,16 +88,6 @@ public: llvm::Value* getJumpTarget() const { return m_jumpTarget; } void setJumpTarget(llvm::Value* _jumpTarget) { m_jumpTarget = _jumpTarget; } - ssize_t getDiff() const { return static_cast(m_currentStack.size()) - static_cast(m_globalPops); } - - /// 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 @@ -88,24 +95,6 @@ private: llvm::BasicBlock* const m_llvmBB; - 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; - friend class LocalStack; - - 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.cpp b/evmjit/libevmjit/Compiler.cpp index 05d5a0fd1..58d8809d3 100644 --- a/evmjit/libevmjit/Compiler.cpp +++ b/evmjit/libevmjit/Compiler.cpp @@ -87,7 +87,7 @@ void Compiler::createBasicBlocks(code_iterator _codeBegin, code_iterator _codeEn { 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)); + std::forward_as_tuple(beginIdx, begin, next, m_mainFunc, nextJumpDest)); nextJumpDest = false; begin = next; } @@ -98,12 +98,12 @@ llvm::BasicBlock* Compiler::getJumpTableBlock() { if (!m_jumpTableBlock) { - m_jumpTableBlock.reset(new BasicBlock("JumpTable", m_mainFunc, m_builder, true)); + m_jumpTableBlock.reset(new BasicBlock("JumpTable", m_mainFunc, 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, m_abortBB); - for (auto&& p : m_basicBlocks) + for (auto&& p : m_basicBlocks) // FIXME: It mast be done at the end { if (p.second.isJumpDest()) switchInstr->addCase(Constant::get(p.first), p.second.llvm()); @@ -196,15 +196,6 @@ std::unique_ptr Compiler::compile(code_iterator _begin, code_itera } } - dumpCFGifRequired("blocks-init.dot"); - - for (auto& entry : m_basicBlocks) - entry.second.synchronizeLocalStack(stack); - if (m_jumpTableBlock) - m_jumpTableBlock->synchronizeLocalStack(stack); - - dumpCFGifRequired("blocks-sync.dot"); - return module; } @@ -216,7 +207,7 @@ void Compiler::compileBasicBlock(BasicBlock& _basicBlock, RuntimeManager& _runti _nextBasicBlock = m_stopBB; m_builder.SetInsertPoint(_basicBlock.llvm()); - LocalStack stack{_basicBlock, _globalStack}; + LocalStack stack{_globalStack}; for (auto it = _basicBlock.begin(); it != _basicBlock.end(); ++it) { @@ -842,12 +833,13 @@ void Compiler::compileBasicBlock(BasicBlock& _basicBlock, RuntimeManager& _runti if (!_basicBlock.llvm()->getTerminator()) m_builder.CreateBr(_nextBasicBlock); + stack.finalize(m_builder, *_basicBlock.llvm()); // TODO: Use references + m_builder.SetInsertPoint(_basicBlock.llvm()->getFirstNonPHI()); - _runtimeManager.checkStackLimit(stack.getMaxSize(), _basicBlock.getDiff()); + _runtimeManager.checkStackLimit(stack.getMaxSize(), stack.getDiff()); } - void Compiler::removeDeadBlocks() { // Remove dead basic blocks @@ -877,66 +869,6 @@ void Compiler::removeDeadBlocks() } } -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(); -} - -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()); - - // 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 b9a9aae9b..a8cfa30e7 100644 --- a/evmjit/libevmjit/Compiler.h +++ b/evmjit/libevmjit/Compiler.h @@ -38,15 +38,6 @@ private: void removeDeadBlocks(); - /// 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(); - /// Compiler options Options const& m_options; From 3ffdcd9f1399eff22bb71ff2df930b951d86e12f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Pawe=C5=82=20Bylica?= Date: Mon, 27 Jul 2015 14:25:28 +0200 Subject: [PATCH 08/24] Test dynamic jumps. --- evmjit/libevmjit/Compiler.cpp | 2 +- .../vmIOandFlowOperationsTestFiller.json | 99 ++++++++++++++++++- 2 files changed, 97 insertions(+), 4 deletions(-) diff --git a/evmjit/libevmjit/Compiler.cpp b/evmjit/libevmjit/Compiler.cpp index 58d8809d3..bf8a438a2 100644 --- a/evmjit/libevmjit/Compiler.cpp +++ b/evmjit/libevmjit/Compiler.cpp @@ -103,7 +103,7 @@ llvm::BasicBlock* Compiler::getJumpTableBlock() m_builder.SetInsertPoint(m_jumpTableBlock->llvm()); auto dest = m_builder.CreatePHI(Type::Word, 8, "target"); auto switchInstr = m_builder.CreateSwitch(dest, m_abortBB); - for (auto&& p : m_basicBlocks) // FIXME: It mast be done at the end + for (auto&& p : m_basicBlocks) { if (p.second.isJumpDest()) switchInstr->addCase(Constant::get(p.first), p.second.llvm()); diff --git a/test/libevm/VMTestsFiller/vmIOandFlowOperationsTestFiller.json b/test/libevm/VMTestsFiller/vmIOandFlowOperationsTestFiller.json index ee68f0d67..95f2f4edb 100644 --- a/test/libevm/VMTestsFiller/vmIOandFlowOperationsTestFiller.json +++ b/test/libevm/VMTestsFiller/vmIOandFlowOperationsTestFiller.json @@ -140,7 +140,7 @@ "0x01" : "0x17" } } - }, + }, "pre" : { "0f572e5295c57f15886f9b263e2f6d2d6c7b5ec6" : { "balance" : "100000000000000000000000", @@ -2211,6 +2211,99 @@ } }, + "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" + } + }, + "BlockNumberDynamicJumpiAfterStop": { "env" : { "previousHash" : "5e20a0453cecd065ea59c37ac63e079ee08998b6045136a8ce6635c7912ec0b6", @@ -2226,7 +2319,7 @@ "0x03" : "0x02" } } - }, + }, "pre" : { "0f572e5295c57f15886f9b263e2f6d2d6c7b5ec6" : { "balance" : "100000000000000000000000", @@ -3939,7 +4032,7 @@ "gas" : "100000" } }, - + "jumpi_at_the_end" : { "env" : { "currentCoinbase" : "2adc25665018aa1fe0e6bc666dac8fc2697ff9ba", From 7d1e35076f58e198502743082b8fe4c6034ea018 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Pawe=C5=82=20Bylica?= Date: Mon, 27 Jul 2015 14:54:35 +0200 Subject: [PATCH 09/24] Fix local stack max size calculation. --- evmjit/libevmjit/BasicBlock.cpp | 3 ++- evmjit/libevmjit/BasicBlock.h | 16 +++++++++------- evmjit/libevmjit/Compiler.cpp | 4 ++-- 3 files changed, 13 insertions(+), 10 deletions(-) diff --git a/evmjit/libevmjit/BasicBlock.cpp b/evmjit/libevmjit/BasicBlock.cpp index f4f8b4645..d26203807 100644 --- a/evmjit/libevmjit/BasicBlock.cpp +++ b/evmjit/libevmjit/BasicBlock.cpp @@ -44,7 +44,7 @@ void LocalStack::push(llvm::Value* _value) { assert(_value->getType() == Type::Word); m_currentStack.push_back(_value); - m_maxSize = std::max(m_maxSize, m_currentStack.size()); // FIXME: This is wrong too. + add min size; + m_maxSize = std::max(m_maxSize, size()); } llvm::Value* LocalStack::pop() @@ -57,6 +57,7 @@ llvm::Value* LocalStack::pop() else ++m_globalPops; + m_minSize = std::min(m_minSize, size()); return item; } diff --git a/evmjit/libevmjit/BasicBlock.h b/evmjit/libevmjit/BasicBlock.h index 75b0efeab..044e9f687 100644 --- a/evmjit/libevmjit/BasicBlock.h +++ b/evmjit/libevmjit/BasicBlock.h @@ -36,11 +36,11 @@ public: /// @param _index Index of value to be swaped. Must be > 0. void swap(size_t _index); - ssize_t getDiff() const { return static_cast(m_currentStack.size()) - static_cast(m_globalPops); } - size_t getMaxSize() const { return m_maxSize; } + ssize_t size() const { return static_cast(m_currentStack.size()) - static_cast(m_globalPops); } + ssize_t minSize() const { return m_minSize; } + ssize_t maxSize() const { return m_maxSize; } - /// TODO: comment - /// TODO: It must be the same builder as in global stack. + /// Finalize local stack: check the requirements and update of the global stack. void finalize(llvm::IRBuilder<>& _builder, llvm::BasicBlock& _bb); private: @@ -63,9 +63,11 @@ private: /// top of the stack or changes existing items. std::vector m_currentStack; - size_t m_globalPops = 0; ///< Number of items poped from global stack. In other words: global - local stack overlap. - Stack& m_global; - size_t m_maxSize = 0; ///< Max size reached by the stack. + Stack& m_global; ///< Reference to global stack. + + 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 diff --git a/evmjit/libevmjit/Compiler.cpp b/evmjit/libevmjit/Compiler.cpp index bf8a438a2..6002028dc 100644 --- a/evmjit/libevmjit/Compiler.cpp +++ b/evmjit/libevmjit/Compiler.cpp @@ -835,8 +835,8 @@ void Compiler::compileBasicBlock(BasicBlock& _basicBlock, RuntimeManager& _runti stack.finalize(m_builder, *_basicBlock.llvm()); // TODO: Use references - m_builder.SetInsertPoint(_basicBlock.llvm()->getFirstNonPHI()); - _runtimeManager.checkStackLimit(stack.getMaxSize(), stack.getDiff()); + m_builder.SetInsertPoint(_basicBlock.llvm()->getFirstNonPHI()); // TODO: Move to LocalStack::finalize + _runtimeManager.checkStackLimit(stack.maxSize(), stack.size()); } From f4b79cfee76c84141550966307dd0dd96a524eb0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Pawe=C5=82=20Bylica?= Date: Mon, 27 Jul 2015 15:14:32 +0200 Subject: [PATCH 10/24] Do not remove dead blocks during compilation. --- evmjit/libevmjit/Compiler.cpp | 32 -------------------------------- evmjit/libevmjit/Compiler.h | 2 -- evmjit/libevmjit/Optimizer.cpp | 1 + 3 files changed, 1 insertion(+), 34 deletions(-) diff --git a/evmjit/libevmjit/Compiler.cpp b/evmjit/libevmjit/Compiler.cpp index 6002028dc..056b5cabe 100644 --- a/evmjit/libevmjit/Compiler.cpp +++ b/evmjit/libevmjit/Compiler.cpp @@ -174,8 +174,6 @@ std::unique_ptr Compiler::compile(code_iterator _begin, code_itera m_builder.SetInsertPoint(m_abortBB); runtimeManager.exit(ReturnCode::OutOfGas); - removeDeadBlocks(); - // Link jump table target index if (m_jumpTableBlock) { @@ -840,36 +838,6 @@ void Compiler::compileBasicBlock(BasicBlock& _basicBlock, RuntimeManager& _runti } -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(); - } -} - - } } } diff --git a/evmjit/libevmjit/Compiler.h b/evmjit/libevmjit/Compiler.h index a8cfa30e7..c7aea014e 100644 --- a/evmjit/libevmjit/Compiler.h +++ b/evmjit/libevmjit/Compiler.h @@ -36,8 +36,6 @@ private: llvm::BasicBlock* getJumpTableBlock(); - void removeDeadBlocks(); - /// Compiler options Options const& m_options; 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); From f76d63476976cd81ca035ea9ef377e122216ac55 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Pawe=C5=82=20Bylica?= Date: Mon, 27 Jul 2015 16:22:04 +0200 Subject: [PATCH 11/24] Change the way Jump Table is created and filled. --- evmjit/libevmjit/BasicBlock.cpp | 5 --- evmjit/libevmjit/BasicBlock.h | 1 - evmjit/libevmjit/Compiler.cpp | 67 ++++++++++++++++----------------- evmjit/libevmjit/Compiler.h | 6 +-- 4 files changed, 36 insertions(+), 43 deletions(-) diff --git a/evmjit/libevmjit/BasicBlock.cpp b/evmjit/libevmjit/BasicBlock.cpp index d26203807..53bbe8b12 100644 --- a/evmjit/libevmjit/BasicBlock.cpp +++ b/evmjit/libevmjit/BasicBlock.cpp @@ -31,11 +31,6 @@ BasicBlock::BasicBlock(instr_idx _firstInstrIdx, code_iterator _begin, code_iter m_isJumpDest(isJumpDest) {} -BasicBlock::BasicBlock(std::string _name, llvm::Function* _mainFunc, bool isJumpDest): - m_llvmBB(llvm::BasicBlock::Create(_mainFunc->getContext(), _name, _mainFunc)), - m_isJumpDest(isJumpDest) -{} - LocalStack::LocalStack(Stack& _globalStack): m_global(_globalStack) {} diff --git a/evmjit/libevmjit/BasicBlock.h b/evmjit/libevmjit/BasicBlock.h index 044e9f687..ae2a0f584 100644 --- a/evmjit/libevmjit/BasicBlock.h +++ b/evmjit/libevmjit/BasicBlock.h @@ -74,7 +74,6 @@ class BasicBlock { public: explicit BasicBlock(instr_idx _firstInstrIdx, code_iterator _begin, code_iterator _end, llvm::Function* _mainFunc, bool isJumpDest); - explicit BasicBlock(std::string _name, llvm::Function* _mainFunc, bool isJumpDest); BasicBlock(const BasicBlock&) = delete; BasicBlock& operator=(const BasicBlock&) = delete; diff --git a/evmjit/libevmjit/Compiler.cpp b/evmjit/libevmjit/Compiler.cpp index 056b5cabe..de2227521 100644 --- a/evmjit/libevmjit/Compiler.cpp +++ b/evmjit/libevmjit/Compiler.cpp @@ -94,22 +94,40 @@ void Compiler::createBasicBlocks(code_iterator _codeBegin, code_iterator _codeEn } } -llvm::BasicBlock* Compiler::getJumpTableBlock() +void Compiler::fillJumpTable() { - if (!m_jumpTableBlock) + assert(m_jumpTableBB); + + if (llvm::pred_empty(m_jumpTableBB)) { - m_jumpTableBlock.reset(new BasicBlock("JumpTable", m_mainFunc, 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, m_abortBB); - for (auto&& p : m_basicBlocks) + m_jumpTableBB->eraseFromParent(); // remove if unused + return; + } + + m_builder.SetInsertPoint(m_jumpTableBB); + auto target = m_builder.CreatePHI(Type::Word, 16, "target"); + for (auto pred: llvm::predecessors(m_jumpTableBB)) + { + llvm::Value* incomingTarget = nullptr; + for (auto&& p: m_basicBlocks) // FIXME: Fix this stupid search. Use metadata to tag target in the basic block { - if (p.second.isJumpDest()) - switchInstr->addCase(Constant::get(p.first), p.second.llvm()); + if (p.second.llvm() == &*pred) + { + incomingTarget = p.second.getJumpTarget(); + break; + } } + + assert(incomingTarget); + target->addIncoming(incomingTarget, pred); + } + + auto switchInst = m_builder.CreateSwitch(target, m_abortBB); + for (auto& p: m_basicBlocks) + { + if (p.second.isJumpDest()) + switchInst->addCase(Constant::get(p.first), p.second.llvm()); } - return m_jumpTableBlock->llvm(); } std::unique_ptr Compiler::compile(code_iterator _begin, code_iterator _end, std::string const& _id) @@ -150,9 +168,9 @@ 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); + m_jumpTableBB = llvm::BasicBlock::Create(m_mainFunc->getContext(), "JumpTable", m_mainFunc); auto firstBB = m_basicBlocks.empty() ? m_stopBB : m_basicBlocks.begin()->second.llvm(); m_builder.CreateCondBr(normalFlow, firstBB, m_abortBB, Type::expectTrue); @@ -167,32 +185,13 @@ std::unique_ptr Compiler::compile(code_iterator _begin, code_itera } // 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); - // 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()); - } - } + fillJumpTable(); return module; } @@ -592,7 +591,7 @@ void Compiler::compileBasicBlock(BasicBlock& _basicBlock, RuntimeManager& _runti else { _basicBlock.setJumpTarget(target); - m_builder.CreateBr(getJumpTableBlock()); + m_builder.CreateBr(m_jumpTableBB); } } else // JUMPI @@ -608,7 +607,7 @@ void Compiler::compileBasicBlock(BasicBlock& _basicBlock, RuntimeManager& _runti else { _basicBlock.setJumpTarget(target); - m_builder.CreateCondBr(cond, getJumpTableBlock(), _nextBasicBlock); + m_builder.CreateCondBr(cond, m_jumpTableBB, _nextBasicBlock); } } break; diff --git a/evmjit/libevmjit/Compiler.h b/evmjit/libevmjit/Compiler.h index c7aea014e..00a5047c5 100644 --- a/evmjit/libevmjit/Compiler.h +++ b/evmjit/libevmjit/Compiler.h @@ -34,7 +34,7 @@ private: 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::BasicBlock* getJumpTableBlock(); + void fillJumpTable(); /// Compiler options Options const& m_options; @@ -52,10 +52,10 @@ private: llvm::BasicBlock* m_abortBB = nullptr; /// Block with a jump table. - std::unique_ptr m_jumpTableBlock; + llvm::BasicBlock* m_jumpTableBB = nullptr; /// Main program function - llvm::Function* m_mainFunc = nullptr; + llvm::Function* m_mainFunc = nullptr; // TODO: Remove }; } From 406e13dadf001efae1ed91d33d5d75cd9b7b01e0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Pawe=C5=82=20Bylica?= Date: Mon, 27 Jul 2015 18:16:17 +0200 Subject: [PATCH 12/24] Use LLVM metadata for keeping information about jump target index. --- evmjit/libevmjit/BasicBlock.h | 6 ---- evmjit/libevmjit/Compiler.cpp | 55 +++++++++-------------------------- 2 files changed, 13 insertions(+), 48 deletions(-) diff --git a/evmjit/libevmjit/BasicBlock.h b/evmjit/libevmjit/BasicBlock.h index ae2a0f584..c79088ffc 100644 --- a/evmjit/libevmjit/BasicBlock.h +++ b/evmjit/libevmjit/BasicBlock.h @@ -86,9 +86,6 @@ public: bool isJumpDest() const { return m_isJumpDest; } - llvm::Value* getJumpTarget() const { return m_jumpTarget; } - void setJumpTarget(llvm::Value* _jumpTarget) { m_jumpTarget = _jumpTarget; } - 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 @@ -99,9 +96,6 @@ private: /// Is the basic block a valid jump destination. /// JUMPDEST is the first instruction of the basic block. bool const m_isJumpDest = false; - - /// If block finishes with dynamic jump target index is stored here - llvm::Value* m_jumpTarget = nullptr; }; } diff --git a/evmjit/libevmjit/Compiler.cpp b/evmjit/libevmjit/Compiler.cpp index de2227521..a04204812 100644 --- a/evmjit/libevmjit/Compiler.cpp +++ b/evmjit/libevmjit/Compiler.cpp @@ -108,18 +108,8 @@ void Compiler::fillJumpTable() auto target = m_builder.CreatePHI(Type::Word, 16, "target"); for (auto pred: llvm::predecessors(m_jumpTableBB)) { - llvm::Value* incomingTarget = nullptr; - for (auto&& p: m_basicBlocks) // FIXME: Fix this stupid search. Use metadata to tag target in the basic block - { - if (p.second.llvm() == &*pred) - { - incomingTarget = p.second.getJumpTarget(); - break; - } - } - - assert(incomingTarget); - target->addIncoming(incomingTarget, pred); + auto targetMd = llvm::cast(pred->getTerminator()->getMetadata("target")->getOperand(0)); + target->addIncoming(targetMd->getValue(), pred); } auto switchInst = m_builder.CreateSwitch(target, m_abortBB); @@ -571,44 +561,25 @@ 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(); + auto jumpInst = (inst == Instruction::JUMP) ? + m_builder.CreateBr(jumpBlock) : + m_builder.CreateCondBr(m_builder.CreateICmpNE(stack.pop(), Constant::get(0), "jump.check"), jumpBlock, _nextBasicBlock); + if (auto constant = llvm::dyn_cast(target)) { + // If target index is a constant do direct jump to the target block. 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() : m_abortBB; - } - - // TODO: Improve; check for constants - if (inst == Instruction::JUMP) - { - if (targetBlock) - { - m_builder.CreateBr(targetBlock); - } - else - { - _basicBlock.setJumpTarget(target); - m_builder.CreateBr(m_jumpTableBB); - } + jumpInst->setSuccessor(0, (it != m_basicBlocks.end() && it->second.isJumpDest()) ? it->second.llvm() : m_abortBB); } - 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, m_jumpTableBB, _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; } From 1de60c02e685f9b453ee02443c141c3502979df6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Pawe=C5=82=20Bylica?= Date: Mon, 27 Jul 2015 18:48:58 +0200 Subject: [PATCH 13/24] Add test for global stack underflow. --- evmjit/libevmjit/Stack.cpp | 3 +- .../vmIOandFlowOperationsTestFiller.json | 31 +++++++++++++++++++ 2 files changed, 32 insertions(+), 2 deletions(-) diff --git a/evmjit/libevmjit/Stack.cpp b/evmjit/libevmjit/Stack.cpp index b7b8f25b1..b83849a92 100644 --- a/evmjit/libevmjit/Stack.cpp +++ b/evmjit/libevmjit/Stack.cpp @@ -71,8 +71,7 @@ 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 + // TODO: We should place stack.require() check and begining of every BB m_stack.pop(m_builder.getInt64(_count)); } diff --git a/test/libevm/VMTestsFiller/vmIOandFlowOperationsTestFiller.json b/test/libevm/VMTestsFiller/vmIOandFlowOperationsTestFiller.json index 95f2f4edb..0c6801dd0 100644 --- a/test/libevm/VMTestsFiller/vmIOandFlowOperationsTestFiller.json +++ b/test/libevm/VMTestsFiller/vmIOandFlowOperationsTestFiller.json @@ -2304,6 +2304,37 @@ } }, + "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", From 4a68a237b0c79db0a819c0d4d16eb355e6b493aa Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Pawe=C5=82=20Bylica?= Date: Mon, 27 Jul 2015 18:58:24 +0200 Subject: [PATCH 14/24] Rename private members of LocalStack. --- evmjit/libevmjit/BasicBlock.cpp | 40 ++++++++++++++++----------------- evmjit/libevmjit/BasicBlock.h | 22 ++++++------------ 2 files changed, 27 insertions(+), 35 deletions(-) diff --git a/evmjit/libevmjit/BasicBlock.cpp b/evmjit/libevmjit/BasicBlock.cpp index 53bbe8b12..b9534e40f 100644 --- a/evmjit/libevmjit/BasicBlock.cpp +++ b/evmjit/libevmjit/BasicBlock.cpp @@ -38,17 +38,17 @@ LocalStack::LocalStack(Stack& _globalStack): void LocalStack::push(llvm::Value* _value) { assert(_value->getType() == Type::Word); - m_currentStack.push_back(_value); + m_local.push_back(_value); m_maxSize = std::max(m_maxSize, size()); } llvm::Value* LocalStack::pop() { auto item = get(0); - assert(!m_currentStack.empty() || !m_initialStack.empty()); + assert(!m_local.empty() || !m_input.empty()); - if (m_currentStack.size() > 0) - m_currentStack.pop_back(); + if (m_local.size() > 0) + m_local.pop_back(); else ++m_globalPops; @@ -80,13 +80,13 @@ void LocalStack::swap(size_t _index) llvm::Value* LocalStack::get(size_t _index) { - if (_index < m_currentStack.size()) - return *(m_currentStack.rbegin() + _index); // count from back + if (_index < m_local.size()) + return *(m_local.rbegin() + _index); // count from back - auto idx = _index - m_currentStack.size() + m_globalPops; - if (idx >= m_initialStack.size()) - m_initialStack.resize(idx + 1); - auto& item = m_initialStack[idx]; + auto idx = _index - m_local.size() + m_globalPops; + if (idx >= m_input.size()) + m_input.resize(idx + 1); + auto& item = m_input[idx]; if (!item) item = m_global.get(idx); @@ -96,15 +96,15 @@ llvm::Value* LocalStack::get(size_t _index) void LocalStack::set(size_t _index, llvm::Value* _word) { - if (_index < m_currentStack.size()) + if (_index < m_local.size()) { - *(m_currentStack.rbegin() + _index) = _word; + *(m_local.rbegin() + _index) = _word; return; } - auto idx = _index - m_currentStack.size() + m_globalPops; - assert(idx < m_initialStack.size()); - m_initialStack[idx] = _word; + auto idx = _index - m_local.size() + m_globalPops; + assert(idx < m_input.size()); + m_input[idx] = _word; } @@ -118,15 +118,15 @@ void LocalStack::finalize(llvm::IRBuilder<>& _builder, llvm::BasicBlock& _bb) _builder.SetInsertPoint(blockTerminator); // 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(m_globalPops <= m_input.size()); // pop() always does get() + for (auto i = m_globalPops; i < m_input.size(); ++i) { - if (m_initialStack[i]) - m_global.set(i, m_initialStack[i]); + if (m_input[i]) + m_global.set(i, m_input[i]); } // Add new items - for (auto& item: m_currentStack) + for (auto& item: m_local) { if (m_globalPops) // Override poped global items m_global.set(--m_globalPops, item); // using pops counter as the index diff --git a/evmjit/libevmjit/BasicBlock.h b/evmjit/libevmjit/BasicBlock.h index c79088ffc..14c7111f8 100644 --- a/evmjit/libevmjit/BasicBlock.h +++ b/evmjit/libevmjit/BasicBlock.h @@ -14,8 +14,6 @@ namespace jit using namespace evmjit; using instr_idx = uint64_t; -class BasicBlock; - class LocalStack { public: @@ -36,7 +34,7 @@ public: /// @param _index Index of value to be swaped. Must be > 0. void swap(size_t _index); - ssize_t size() const { return static_cast(m_currentStack.size()) - static_cast(m_globalPops); } + 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; } @@ -50,18 +48,12 @@ private: /// Sets _index'th value from top (counting from 0) void set(size_t _index, llvm::Value* _value); - /// 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; + /// 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; + + /// Local stack items that has not been pushed to global stack. First item is just above global stack. + std::vector m_local; Stack& m_global; ///< Reference to global stack. From b44d4846f5799b7d8a3ca6a5cf4ccdb15a2f4823 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Pawe=C5=82=20Bylica?= Date: Mon, 27 Jul 2015 20:48:09 +0200 Subject: [PATCH 15/24] Use llvm::SwitchInstr as a jump dest map. --- evmjit/libevmjit/BasicBlock.cpp | 3 +-- evmjit/libevmjit/BasicBlock.h | 6 ----- evmjit/libevmjit/Compiler.cpp | 43 +++++++++++++++------------------ evmjit/libevmjit/Compiler.h | 5 ++-- 4 files changed, 24 insertions(+), 33 deletions(-) diff --git a/evmjit/libevmjit/BasicBlock.cpp b/evmjit/libevmjit/BasicBlock.cpp index b9534e40f..7ec68813a 100644 --- a/evmjit/libevmjit/BasicBlock.cpp +++ b/evmjit/libevmjit/BasicBlock.cpp @@ -27,8 +27,7 @@ BasicBlock::BasicBlock(instr_idx _firstInstrIdx, code_iterator _begin, code_iter 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_isJumpDest(isJumpDest) + m_llvmBB(llvm::BasicBlock::Create(_mainFunc->getContext(), {isJumpDest ? jumpDestName : basicBlockName, std::to_string(_firstInstrIdx)}, _mainFunc)) {} LocalStack::LocalStack(Stack& _globalStack): diff --git a/evmjit/libevmjit/BasicBlock.h b/evmjit/libevmjit/BasicBlock.h index 14c7111f8..8a23426c1 100644 --- a/evmjit/libevmjit/BasicBlock.h +++ b/evmjit/libevmjit/BasicBlock.h @@ -76,18 +76,12 @@ public: code_iterator begin() const { return m_begin; } code_iterator end() const { return m_end; } - bool isJumpDest() const { return m_isJumpDest; } - 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; - - /// 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.cpp b/evmjit/libevmjit/Compiler.cpp index a04204812..3c79bbdcb 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) +void 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) @@ -86,8 +86,10 @@ 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), + auto r = m_basicBlocks.emplace(std::piecewise_construct, std::forward_as_tuple(beginIdx), std::forward_as_tuple(beginIdx, begin, next, m_mainFunc, nextJumpDest)); + if (nextJumpDest) + _jumpTable.addCase(Constant::get(beginIdx), r.first->second.llvm()); nextJumpDest = false; begin = next; } @@ -97,27 +99,19 @@ void Compiler::createBasicBlocks(code_iterator _codeBegin, code_iterator _codeEn void Compiler::fillJumpTable() { assert(m_jumpTableBB); - if (llvm::pred_empty(m_jumpTableBB)) { m_jumpTableBB->eraseFromParent(); // remove if unused return; } - m_builder.SetInsertPoint(m_jumpTableBB); - auto target = m_builder.CreatePHI(Type::Word, 16, "target"); + // 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)) { auto targetMd = llvm::cast(pred->getTerminator()->getMetadata("target")->getOperand(0)); target->addIncoming(targetMd->getValue(), pred); } - - auto switchInst = m_builder.CreateSwitch(target, m_abortBB); - for (auto& p: m_basicBlocks) - { - if (p.second.isJumpDest()) - switchInst->addCase(Constant::get(p.first), p.second.llvm()); - } } std::unique_ptr Compiler::compile(code_iterator _begin, code_iterator _end, std::string const& _id) @@ -131,9 +125,17 @@ 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); + createBasicBlocks(_begin, _end, jumpTable); // Init runtime structures. RuntimeManager runtimeManager(m_builder, _begin, _end); @@ -158,10 +160,6 @@ std::unique_ptr Compiler::compile(code_iterator _begin, code_itera auto normalFlow = m_builder.CreateICmpEQ(r, m_builder.getInt32(0)); runtimeManager.setJmpBuf(jmpBuf); - 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); - auto firstBB = m_basicBlocks.empty() ? m_stopBB : m_basicBlocks.begin()->second.llvm(); m_builder.CreateCondBr(normalFlow, firstBB, m_abortBB, Type::expectTrue); @@ -171,7 +169,7 @@ std::unique_ptr Compiler::compile(code_iterator _begin, code_itera auto iterCopy = basicBlockPairIt; ++iterCopy; auto nextBasicBlock = (iterCopy != m_basicBlocks.end()) ? iterCopy->second.llvm() : nullptr; - compileBasicBlock(basicBlock, runtimeManager, arith, memory, ext, gasMeter, nextBasicBlock, stack); + compileBasicBlock(basicBlock, runtimeManager, arith, memory, ext, gasMeter, nextBasicBlock, stack, jumpTable); } // Code for special blocks: @@ -188,7 +186,8 @@ std::unique_ptr Compiler::compile(code_iterator _begin, code_itera void Compiler::compileBasicBlock(BasicBlock& _basicBlock, RuntimeManager& _runtimeManager, - Arith256& _arith, Memory& _memory, Ext& _ext, GasMeter& _gasMeter, llvm::BasicBlock* _nextBasicBlock, Stack& _globalStack) + 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; @@ -570,10 +569,8 @@ void Compiler::compileBasicBlock(BasicBlock& _basicBlock, RuntimeManager& _runti if (auto constant = llvm::dyn_cast(target)) { // If target index is a constant do direct jump to the target block. - auto&& c = constant->getValue(); - auto targetIdx = c.getActiveBits() <= 64 ? c.getZExtValue() : -1; - auto it = m_basicBlocks.find(targetIdx); - jumpInst->setSuccessor(0, (it != m_basicBlocks.end() && it->second.isJumpDest()) ? it->second.llvm() : m_abortBB); + auto bb = jumpTable.findCaseValue(constant).getCaseSuccessor(); + jumpInst->setSuccessor(0, bb); } else { diff --git a/evmjit/libevmjit/Compiler.h b/evmjit/libevmjit/Compiler.h index 00a5047c5..b14ef13ae 100644 --- a/evmjit/libevmjit/Compiler.h +++ b/evmjit/libevmjit/Compiler.h @@ -30,9 +30,10 @@ public: private: - void createBasicBlocks(code_iterator _begin, code_iterator _end); + void createBasicBlocks(code_iterator _begin, code_iterator _end, llvm::SwitchInst& _switchInst); - 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); + 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); void fillJumpTable(); From 61233522953235c0e8160375b20bc809b5173ada Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Pawe=C5=82=20Bylica?= Date: Mon, 27 Jul 2015 21:06:28 +0200 Subject: [PATCH 16/24] Replace map of basic blocks with vector. --- evmjit/libevmjit/BasicBlock.h | 4 ++-- evmjit/libevmjit/Compiler.cpp | 12 ++++++------ evmjit/libevmjit/Compiler.h | 4 +--- 3 files changed, 9 insertions(+), 11 deletions(-) diff --git a/evmjit/libevmjit/BasicBlock.h b/evmjit/libevmjit/BasicBlock.h index 8a23426c1..24cd74cbe 100644 --- a/evmjit/libevmjit/BasicBlock.h +++ b/evmjit/libevmjit/BasicBlock.h @@ -67,8 +67,8 @@ class BasicBlock public: explicit BasicBlock(instr_idx _firstInstrIdx, code_iterator _begin, code_iterator _end, llvm::Function* _mainFunc, bool isJumpDest); - BasicBlock(const BasicBlock&) = delete; - BasicBlock& operator=(const BasicBlock&) = delete; + BasicBlock(BasicBlock&&) = default; + BasicBlock& operator=(BasicBlock&&) = default; llvm::BasicBlock* llvm() { return m_llvmBB; } diff --git a/evmjit/libevmjit/Compiler.cpp b/evmjit/libevmjit/Compiler.cpp index 3c79bbdcb..66f347912 100644 --- a/evmjit/libevmjit/Compiler.cpp +++ b/evmjit/libevmjit/Compiler.cpp @@ -86,10 +86,9 @@ void Compiler::createBasicBlocks(code_iterator _codeBegin, code_iterator _codeEn if (isEnd) { auto beginIdx = begin - _codeBegin; - auto r = m_basicBlocks.emplace(std::piecewise_construct, std::forward_as_tuple(beginIdx), - std::forward_as_tuple(beginIdx, begin, next, m_mainFunc, nextJumpDest)); + m_basicBlocks.emplace_back(beginIdx, begin, next, m_mainFunc, nextJumpDest); if (nextJumpDest) - _jumpTable.addCase(Constant::get(beginIdx), r.first->second.llvm()); + _jumpTable.addCase(Constant::get(beginIdx), m_basicBlocks.back().llvm()); nextJumpDest = false; begin = next; } @@ -160,15 +159,16 @@ std::unique_ptr Compiler::compile(code_iterator _begin, code_itera auto normalFlow = m_builder.CreateICmpEQ(r, m_builder.getInt32(0)); runtimeManager.setJmpBuf(jmpBuf); - auto firstBB = m_basicBlocks.empty() ? m_stopBB : m_basicBlocks.begin()->second.llvm(); + auto firstBB = m_basicBlocks.empty() ? m_stopBB : m_basicBlocks.front().llvm(); m_builder.CreateCondBr(normalFlow, firstBB, m_abortBB, Type::expectTrue); for (auto basicBlockPairIt = m_basicBlocks.begin(); basicBlockPairIt != m_basicBlocks.end(); ++basicBlockPairIt) { - auto& basicBlock = basicBlockPairIt->second; + // TODO: Rewrite + auto& basicBlock = *basicBlockPairIt; auto iterCopy = basicBlockPairIt; ++iterCopy; - auto nextBasicBlock = (iterCopy != m_basicBlocks.end()) ? iterCopy->second.llvm() : nullptr; + auto nextBasicBlock = (iterCopy != m_basicBlocks.end()) ? iterCopy->llvm() : nullptr; compileBasicBlock(basicBlock, runtimeManager, arith, memory, ext, gasMeter, nextBasicBlock, stack, jumpTable); } diff --git a/evmjit/libevmjit/Compiler.h b/evmjit/libevmjit/Compiler.h index b14ef13ae..0a6e050d5 100644 --- a/evmjit/libevmjit/Compiler.h +++ b/evmjit/libevmjit/Compiler.h @@ -22,8 +22,6 @@ 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); @@ -44,7 +42,7 @@ private: llvm::IRBuilder<> m_builder; /// Maps a program counter pc to a basic block that starts at pc (if any). - std::map m_basicBlocks; + std::vector m_basicBlocks; /// Stop basic block - terminates execution with STOP code (0) llvm::BasicBlock* m_stopBB = nullptr; From 215ea7dde5d875d8163d85d220ffdbcff24f23e2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Pawe=C5=82=20Bylica?= Date: Mon, 27 Jul 2015 21:19:58 +0200 Subject: [PATCH 17/24] Remove vector of basic blocks from Compiler. --- evmjit/libevmjit/BasicBlock.cpp | 7 ++----- evmjit/libevmjit/BasicBlock.h | 2 +- evmjit/libevmjit/Compiler.cpp | 32 ++++++++++++++------------------ evmjit/libevmjit/Compiler.h | 5 +---- 4 files changed, 18 insertions(+), 28 deletions(-) diff --git a/evmjit/libevmjit/BasicBlock.cpp b/evmjit/libevmjit/BasicBlock.cpp index 7ec68813a..69a7a91d6 100644 --- a/evmjit/libevmjit/BasicBlock.cpp +++ b/evmjit/libevmjit/BasicBlock.cpp @@ -20,14 +20,11 @@ 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, 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_llvmBB(llvm::BasicBlock::Create(_mainFunc->getContext(), {"Instr.", std::to_string(_firstInstrIdx)}, _mainFunc)) {} LocalStack::LocalStack(Stack& _globalStack): diff --git a/evmjit/libevmjit/BasicBlock.h b/evmjit/libevmjit/BasicBlock.h index 24cd74cbe..a8394e7cc 100644 --- a/evmjit/libevmjit/BasicBlock.h +++ b/evmjit/libevmjit/BasicBlock.h @@ -65,7 +65,7 @@ private: class BasicBlock { public: - explicit BasicBlock(instr_idx _firstInstrIdx, code_iterator _begin, code_iterator _end, llvm::Function* _mainFunc, bool isJumpDest); + explicit BasicBlock(instr_idx _firstInstrIdx, code_iterator _begin, code_iterator _end, llvm::Function* _mainFunc); BasicBlock(BasicBlock&&) = default; BasicBlock& operator=(BasicBlock&&) = default; diff --git a/evmjit/libevmjit/Compiler.cpp b/evmjit/libevmjit/Compiler.cpp index 66f347912..0ca01a199 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, llvm::SwitchInst& _jumpTable) +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,13 +83,14 @@ void Compiler::createBasicBlocks(code_iterator _codeBegin, code_iterator _codeEn if (isEnd) { auto beginIdx = begin - _codeBegin; - m_basicBlocks.emplace_back(beginIdx, begin, next, m_mainFunc, nextJumpDest); - if (nextJumpDest) - _jumpTable.addCase(Constant::get(beginIdx), m_basicBlocks.back().llvm()); - 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; } void Compiler::fillJumpTable() @@ -134,7 +132,7 @@ std::unique_ptr Compiler::compile(code_iterator _begin, code_itera m_builder.SetInsertPoint(entryBlock); - createBasicBlocks(_begin, _end, jumpTable); + auto blocks = createBasicBlocks(_begin, _end, jumpTable); // Init runtime structures. RuntimeManager runtimeManager(m_builder, _begin, _end); @@ -159,17 +157,15 @@ std::unique_ptr Compiler::compile(code_iterator _begin, code_itera auto normalFlow = m_builder.CreateICmpEQ(r, m_builder.getInt32(0)); runtimeManager.setJmpBuf(jmpBuf); - auto firstBB = m_basicBlocks.empty() ? m_stopBB : m_basicBlocks.front().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) { // TODO: Rewrite - auto& basicBlock = *basicBlockPairIt; - auto iterCopy = basicBlockPairIt; - ++iterCopy; - auto nextBasicBlock = (iterCopy != m_basicBlocks.end()) ? iterCopy->llvm() : nullptr; - compileBasicBlock(basicBlock, runtimeManager, arith, memory, ext, gasMeter, nextBasicBlock, stack, jumpTable); + 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: diff --git a/evmjit/libevmjit/Compiler.h b/evmjit/libevmjit/Compiler.h index 0a6e050d5..af45acedb 100644 --- a/evmjit/libevmjit/Compiler.h +++ b/evmjit/libevmjit/Compiler.h @@ -28,7 +28,7 @@ public: private: - void createBasicBlocks(code_iterator _begin, code_iterator _end, llvm::SwitchInst& _switchInst); + std::vector createBasicBlocks(code_iterator _begin, code_iterator _end, llvm::SwitchInst& _switchInst); 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); @@ -41,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::vector m_basicBlocks; - /// Stop basic block - terminates execution with STOP code (0) llvm::BasicBlock* m_stopBB = nullptr; From c8846eb01a88630ec8e768805438c71863725dd8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Pawe=C5=82=20Bylica?= Date: Tue, 11 Aug 2015 10:21:07 +0200 Subject: [PATCH 18/24] Do not modify pops counter of LocalStack during finalization. That fixes LocalStack::size() returning incorrect values after LocalStack::finalize() has been called. --- evmjit/libevmjit/BasicBlock.cpp | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/evmjit/libevmjit/BasicBlock.cpp b/evmjit/libevmjit/BasicBlock.cpp index 69a7a91d6..a60d1ba35 100644 --- a/evmjit/libevmjit/BasicBlock.cpp +++ b/evmjit/libevmjit/BasicBlock.cpp @@ -122,17 +122,18 @@ void LocalStack::finalize(llvm::IRBuilder<>& _builder, llvm::BasicBlock& _bb) } // Add new items + auto pops = m_globalPops; // Copy pops counter to keep original value for (auto& item: m_local) { - if (m_globalPops) // Override poped global items - m_global.set(--m_globalPops, item); // using pops counter as the index + if (pops) // Override poped global items + m_global.set(--pops, item); // using pops counter as the index else m_global.push(item); } // Pop not overriden items - if (m_globalPops) - m_global.pop(m_globalPops); + if (pops) + m_global.pop(pops); } } From 94b3c55c7f8765a97863d6bb0843ff6229b76bcf Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Pawe=C5=82=20Bylica?= Date: Tue, 11 Aug 2015 11:01:19 +0200 Subject: [PATCH 19/24] Remove explicit move constructor of BasicBlock. --- evmjit/libevmjit/BasicBlock.h | 9 +++------ 1 file changed, 3 insertions(+), 6 deletions(-) diff --git a/evmjit/libevmjit/BasicBlock.h b/evmjit/libevmjit/BasicBlock.h index baf3ffc34..a6e420efc 100644 --- a/evmjit/libevmjit/BasicBlock.h +++ b/evmjit/libevmjit/BasicBlock.h @@ -67,9 +67,6 @@ class BasicBlock public: explicit BasicBlock(instr_idx _firstInstrIdx, code_iterator _begin, code_iterator _end, llvm::Function* _mainFunc); - BasicBlock(BasicBlock&&) = default; - BasicBlock& operator=(BasicBlock&&) = default; - llvm::BasicBlock* llvm() { return m_llvmBB; } instr_idx firstInstrIdx() const { return m_firstInstrIdx; } @@ -78,10 +75,10 @@ public: 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 + 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; + llvm::BasicBlock* const m_llvmBB; ///< Reference to the LLVM BasicBlock }; } From 864a3540a4e4034f57075dc4ddae8fba1aa2c2f6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Pawe=C5=82=20Bylica?= Date: Wed, 12 Aug 2015 13:39:46 +0200 Subject: [PATCH 20/24] Clean up LazyFunction initialization in Array helper. --- evmjit/libevmjit/Array.cpp | 12 ++---------- evmjit/libevmjit/Array.h | 8 +++----- 2 files changed, 5 insertions(+), 15 deletions(-) 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: } } } - - From 5507cc870a51d513cd9dfb29d845c487f2c1d4a5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Pawe=C5=82=20Bylica?= Date: Wed, 12 Aug 2015 14:37:14 +0200 Subject: [PATCH 21/24] Check also stack underflow in evm.stack.require() function. --- evmjit/libevmjit/Compiler.cpp | 2 +- evmjit/libevmjit/RuntimeManager.cpp | 19 ++++++++++++------- evmjit/libevmjit/RuntimeManager.h | 2 +- 3 files changed, 14 insertions(+), 9 deletions(-) diff --git a/evmjit/libevmjit/Compiler.cpp b/evmjit/libevmjit/Compiler.cpp index 50f9833f4..30437b6a3 100644 --- a/evmjit/libevmjit/Compiler.cpp +++ b/evmjit/libevmjit/Compiler.cpp @@ -797,7 +797,7 @@ void Compiler::compileBasicBlock(BasicBlock& _basicBlock, RuntimeManager& _runti stack.finalize(m_builder, *_basicBlock.llvm()); // TODO: Use references m_builder.SetInsertPoint(_basicBlock.llvm()->getFirstNonPHI()); // TODO: Move to LocalStack::finalize - _runtimeManager.checkStackLimit(stack.maxSize(), stack.size()); + _runtimeManager.checkStackLimit(stack.minSize(), stack.maxSize(), stack.size()); } diff --git a/evmjit/libevmjit/RuntimeManager.cpp b/evmjit/libevmjit/RuntimeManager.cpp index b55d90a1a..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, ssize_t _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 aa8f4838b..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, ssize_t _diff); + void checkStackLimit(ssize_t _min, ssize_t _max, ssize_t _diff); private: llvm::Value* getPtr(RuntimeData::Index _index); From 4904c602b01b307fe46197c53fae1fe993301019 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Pawe=C5=82=20Bylica?= Date: Wed, 12 Aug 2015 14:50:11 +0200 Subject: [PATCH 22/24] "Tweak" version component. Add "tweak" component for evmjit version to control cached objects. Bump version to 0.9.0.1. --- evmjit/CMakeLists.txt | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) 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) From cf681e379cc880ad162f8efc3f7006d9da33301a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Pawe=C5=82=20Bylica?= Date: Wed, 12 Aug 2015 15:55:23 +0200 Subject: [PATCH 23/24] Update min local stack size correctly. --- evmjit/libevmjit/BasicBlock.cpp | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/evmjit/libevmjit/BasicBlock.cpp b/evmjit/libevmjit/BasicBlock.cpp index a60d1ba35..47b590103 100644 --- a/evmjit/libevmjit/BasicBlock.cpp +++ b/evmjit/libevmjit/BasicBlock.cpp @@ -85,7 +85,10 @@ llvm::Value* LocalStack::get(size_t _index) auto& item = m_input[idx]; if (!item) - item = m_global.get(idx); + { + 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 item; } From 403b4c03997f614e232cea023f20985ffb21707d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Pawe=C5=82=20Bylica?= Date: Wed, 12 Aug 2015 16:56:49 +0200 Subject: [PATCH 24/24] Remove helper function for getting an item from global stack. --- evmjit/libevmjit/Compiler.cpp | 2 +- evmjit/libevmjit/Stack.cpp | 43 ++--------------------------------- evmjit/libevmjit/Stack.h | 13 ++--------- 3 files changed, 5 insertions(+), 53 deletions(-) diff --git a/evmjit/libevmjit/Compiler.cpp b/evmjit/libevmjit/Compiler.cpp index 30437b6a3..182b5fef8 100644 --- a/evmjit/libevmjit/Compiler.cpp +++ b/evmjit/libevmjit/Compiler.cpp @@ -139,7 +139,7 @@ std::unique_ptr Compiler::compile(code_iterator _begin, code_itera 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); diff --git a/evmjit/libevmjit/Stack.cpp b/evmjit/libevmjit/Stack.cpp index b83849a92..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,7 +33,6 @@ void Stack::set(size_t _index, llvm::Value* _value) void Stack::pop(size_t _count) { - // TODO: 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: } } } - -