From 9e242cae193576f49bd1d90faa8e57535280bffa Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Pawe=C5=82=20Bylica?= Date: Thu, 8 Jan 2015 11:55:40 +0100 Subject: [PATCH 1/7] EVM JIT update: latest PoC-8 changes, cleanups and improvments --- evmjit/evmcc/evmcc.cpp | 1 - evmjit/libevmjit-cpp/Env.cpp | 20 ++-- evmjit/libevmjit-cpp/JitVM.cpp | 1 - evmjit/libevmjit/BasicBlock.h | 7 ++ evmjit/libevmjit/Common.h | 2 + evmjit/libevmjit/Compiler.cpp | 152 +++++++++++----------------- evmjit/libevmjit/Compiler.h | 18 ++-- evmjit/libevmjit/CompilerHelper.cpp | 5 + evmjit/libevmjit/CompilerHelper.h | 7 +- evmjit/libevmjit/Ext.cpp | 151 +++++++++++++++------------ evmjit/libevmjit/Ext.h | 50 +++++---- evmjit/libevmjit/GasMeter.cpp | 4 +- evmjit/libevmjit/Instruction.h | 2 +- evmjit/libevmjit/Memory.cpp | 8 +- evmjit/libevmjit/Runtime.cpp | 23 +---- evmjit/libevmjit/Runtime.h | 3 - evmjit/libevmjit/RuntimeData.h | 1 - evmjit/libevmjit/RuntimeManager.cpp | 2 - evmjit/libevmjit/Utils.cpp | 9 +- evmjit/libevmjit/Utils.h | 2 - 20 files changed, 217 insertions(+), 251 deletions(-) diff --git a/evmjit/evmcc/evmcc.cpp b/evmjit/evmcc/evmcc.cpp index 3c43ab78b..e86f948f2 100644 --- a/evmjit/evmcc/evmcc.cpp +++ b/evmjit/evmcc/evmcc.cpp @@ -190,7 +190,6 @@ int main(int argc, char** argv) data.set(RuntimeData::CallValue, 0xabcd); data.set(RuntimeData::CallDataSize, 3); data.set(RuntimeData::GasPrice, 1003); - data.set(RuntimeData::PrevHash, 1003); data.set(RuntimeData::CoinBase, (u160)Address(101010101010101015)); data.set(RuntimeData::TimeStamp, 1005); data.set(RuntimeData::Number, 1006); diff --git a/evmjit/libevmjit-cpp/Env.cpp b/evmjit/libevmjit-cpp/Env.cpp index 1dcd38162..6320aa222 100644 --- a/evmjit/libevmjit-cpp/Env.cpp +++ b/evmjit/libevmjit-cpp/Env.cpp @@ -42,16 +42,15 @@ extern "C" *o_value = eth2llvm(u); } - EXPORT void env_create(ExtVMFace* _env, i256* io_gas, i256* _endowment, byte* _initBeg, uint64_t _initSize, h256* o_address) + EXPORT void env_blockhash(ExtVMFace* _env, i256* _number, h256* o_hash) { - if (_env->depth == 1024) - jit::terminate(jit::ReturnCode::OutOfGas); - - assert(_env->depth < 1024); + *o_hash = _env->blockhash(llvm2eth(*_number)); + } + EXPORT void env_create(ExtVMFace* _env, i256* io_gas, i256* _endowment, byte* _initBeg, uint64_t _initSize, h256* o_address) + { auto endowment = llvm2eth(*_endowment); - - if (_env->balance(_env->myAddress) >= endowment) + if (_env->balance(_env->myAddress) >= endowment && _env->depth < 1024) { _env->subBalance(endowment); auto gas = llvm2eth(*io_gas); @@ -66,13 +65,8 @@ extern "C" EXPORT bool env_call(ExtVMFace* _env, i256* io_gas, h256* _receiveAddress, i256* _value, byte* _inBeg, uint64_t _inSize, byte* _outBeg, uint64_t _outSize, h256* _codeAddress) { - if (_env->depth == 1024) - jit::terminate(jit::ReturnCode::OutOfGas); - - assert(_env->depth < 1024); - auto value = llvm2eth(*_value); - if (_env->balance(_env->myAddress) >= value) + if (_env->balance(_env->myAddress) >= value && _env->depth < 1024) { _env->subBalance(value); auto receiveAddress = right160(*_receiveAddress); diff --git a/evmjit/libevmjit-cpp/JitVM.cpp b/evmjit/libevmjit-cpp/JitVM.cpp index 815aa6332..dda8133a8 100644 --- a/evmjit/libevmjit-cpp/JitVM.cpp +++ b/evmjit/libevmjit-cpp/JitVM.cpp @@ -20,7 +20,6 @@ bytesConstRef JitVM::go(ExtVMFace& _ext, OnOpFunc const&, uint64_t) m_data.set(RuntimeData::CallValue, _ext.value); m_data.set(RuntimeData::CallDataSize, _ext.data.size()); m_data.set(RuntimeData::GasPrice, _ext.gasPrice); - m_data.set(RuntimeData::PrevHash, _ext.previousBlock.hash); m_data.set(RuntimeData::CoinBase, fromAddress(_ext.currentBlock.coinbaseAddress)); m_data.set(RuntimeData::TimeStamp, _ext.currentBlock.timestamp); m_data.set(RuntimeData::Number, _ext.currentBlock.number); diff --git a/evmjit/libevmjit/BasicBlock.h b/evmjit/libevmjit/BasicBlock.h index f0643f342..4a789a8ec 100644 --- a/evmjit/libevmjit/BasicBlock.h +++ b/evmjit/libevmjit/BasicBlock.h @@ -67,6 +67,9 @@ public: ProgramCounter begin() { return m_beginInstIdx; } ProgramCounter end() { return m_endInstIdx; } + bool isJumpDest() const { return m_isJumpDest; } + void markAsJumpDest() { m_isJumpDest = true; } + LocalStack& localStack() { return m_stack; } /// Optimization: propagates values between local stacks in basic blocks @@ -109,6 +112,10 @@ private: /// How many items higher is the current stack than the initial one. /// May be negative. int m_tosOffset = 0; + + /// Is the basic block a valid jump destination. + /// JUMPDEST is the first instruction of the basic block. + bool m_isJumpDest = false; }; } diff --git a/evmjit/libevmjit/Common.h b/evmjit/libevmjit/Common.h index d98cc0acb..436931dcd 100644 --- a/evmjit/libevmjit/Common.h +++ b/evmjit/libevmjit/Common.h @@ -44,6 +44,8 @@ struct i256 }; static_assert(sizeof(i256) == 32, "Wrong i265 size"); +#define UNTESTED assert(false) + } } } diff --git a/evmjit/libevmjit/Compiler.cpp b/evmjit/libevmjit/Compiler.cpp index 48dc50d60..abd725c7f 100644 --- a/evmjit/libevmjit/Compiler.cpp +++ b/evmjit/libevmjit/Compiler.cpp @@ -5,8 +5,6 @@ #include #include -#include - #include #include #include @@ -42,19 +40,17 @@ Compiler::Compiler(Options const& _options): void Compiler::createBasicBlocks(bytes const& _bytecode) { + // FIXME: Simplify this algorithm. All can be done in one pass + std::set splitPoints; // Sorted collections of instruction indices where basic blocks start/end - std::map directJumpTargets; std::vector indirectJumpTargets; - boost::dynamic_bitset<> validJumpTargets(std::max(_bytecode.size(), size_t(1))); splitPoints.insert(0); // First basic block - validJumpTargets[0] = true; for (auto curr = _bytecode.begin(); curr != _bytecode.end(); ++curr) { ProgramCounter currentPC = curr - _bytecode.begin(); - validJumpTargets[currentPC] = true; auto inst = Instruction(*curr); switch (inst) @@ -62,21 +58,7 @@ void Compiler::createBasicBlocks(bytes const& _bytecode) case Instruction::ANY_PUSH: { - auto val = readPushData(curr, _bytecode.end()); - auto next = curr + 1; - if (next == _bytecode.end()) - break; - - auto nextInst = Instruction(*next); - if (nextInst == Instruction::JUMP || nextInst == Instruction::JUMPI) - { - // Create a block for the JUMP target. - ProgramCounter targetPC = val.ult(_bytecode.size()) ? val.getZExtValue() : _bytecode.size(); - splitPoints.insert(targetPC); - - ProgramCounter jumpPC = (next - _bytecode.begin()); - directJumpTargets[jumpPC] = targetPC; - } + readPushData(curr, _bytecode.end()); break; } @@ -105,15 +87,6 @@ void Compiler::createBasicBlocks(bytes const& _bytecode) } } - // Remove split points generated from jumps out of code or into data. - for (auto it = splitPoints.cbegin(); it != splitPoints.cend();) - { - if (*it > _bytecode.size() || !validJumpTargets[*it]) - it = splitPoints.erase(it); - else - ++it; - } - for (auto it = splitPoints.cbegin(); it != splitPoints.cend();) { auto beginInstIdx = *it; @@ -123,33 +96,39 @@ void Compiler::createBasicBlocks(bytes const& _bytecode) } m_stopBB = llvm::BasicBlock::Create(m_mainFunc->getContext(), "Stop", m_mainFunc); - m_badJumpBlock = std::unique_ptr(new BasicBlock("BadJumpBlock", m_mainFunc, m_builder)); - m_jumpTableBlock = std::unique_ptr(new BasicBlock("JumpTableBlock", m_mainFunc, m_builder)); - for (auto it = directJumpTargets.cbegin(); it != directJumpTargets.cend(); ++it) - { - if (it->second >= _bytecode.size()) - { - // Jumping out of code means STOP - m_directJumpTargets[it->first] = m_stopBB; - continue; - } + for (auto it = indirectJumpTargets.cbegin(); it != indirectJumpTargets.cend(); ++it) + basicBlocks.find(*it)->second.markAsJumpDest(); +} - auto blockIter = basicBlocks.find(it->second); - if (blockIter != basicBlocks.end()) - { - m_directJumpTargets[it->first] = blockIter->second.llvm(); - } - else +llvm::BasicBlock* Compiler::getJumpTableBlock() +{ + if (!m_jumpTableBlock) + { + m_jumpTableBlock.reset(new BasicBlock("JumpTable", m_mainFunc, m_builder)); + InsertPointGuard g{m_builder}; + m_builder.SetInsertPoint(m_jumpTableBlock->llvm()); + auto dest = m_jumpTableBlock->localStack().pop(); + auto switchInstr = m_builder.CreateSwitch(dest, getBadJumpBlock()); + for (auto&& p : basicBlocks) { - clog(JIT) << "Bad JUMP at PC " << it->first - << ": " << it->second << " is not a valid PC"; - m_directJumpTargets[it->first] = m_badJumpBlock->llvm(); + if (p.second.isJumpDest()) + switchInstr->addCase(Constant::get(p.first), p.second.llvm()); } } + return m_jumpTableBlock->llvm(); +} - for (auto it = indirectJumpTargets.cbegin(); it != indirectJumpTargets.cend(); ++it) - m_indirectJumpTargets.push_back(&basicBlocks.find(*it)->second); +llvm::BasicBlock* Compiler::getBadJumpBlock() +{ + if (!m_badJumpBlock) + { + m_badJumpBlock.reset(new BasicBlock("BadJump", m_mainFunc, m_builder)); + InsertPointGuard g{m_builder}; + m_builder.SetInsertPoint(m_badJumpBlock->llvm()); + m_builder.CreateRet(Constant::get(ReturnCode::BadJumpDestination)); + } + return m_badJumpBlock->llvm(); } std::unique_ptr Compiler::compile(bytes const& _bytecode, std::string const& _id) @@ -192,25 +171,6 @@ std::unique_ptr Compiler::compile(bytes const& _bytecode, std::str m_builder.SetInsertPoint(m_stopBB); m_builder.CreateRet(Constant::get(ReturnCode::Stop)); - m_builder.SetInsertPoint(m_badJumpBlock->llvm()); - m_builder.CreateRet(Constant::get(ReturnCode::BadJumpDestination)); - - m_builder.SetInsertPoint(m_jumpTableBlock->llvm()); - if (m_indirectJumpTargets.size() > 0) - { - auto dest = m_jumpTableBlock->localStack().pop(); - auto switchInstr = m_builder.CreateSwitch(dest, m_badJumpBlock->llvm(), - m_indirectJumpTargets.size()); - for (auto it = m_indirectJumpTargets.cbegin(); it != m_indirectJumpTargets.cend(); ++it) - { - auto& bb = *it; - auto dest = Constant::get(bb->begin()); - switchInstr->addCase(dest, bb->llvm()); - } - } - else - m_builder.CreateBr(m_badJumpBlock->llvm()); - removeDeadBlocks(); dumpCFGifRequired("blocks-init.dot"); @@ -596,16 +556,21 @@ void Compiler::compileBasicBlock(BasicBlock& _basicBlock, bytes const& _bytecode case Instruction::JUMP: case Instruction::JUMPI: { - // Generate direct jump iff: - // 1. this is not the first instruction in the block - // 2. m_directJumpTargets[currentPC] is defined (meaning that the previous instruction is a PUSH) - // Otherwise generate a indirect jump (a switch). llvm::BasicBlock* targetBlock = nullptr; - if (currentPC != _basicBlock.begin()) + auto target = stack.pop(); + if (auto constant = llvm::dyn_cast(target)) { - auto pairIter = m_directJumpTargets.find(currentPC); - if (pairIter != m_directJumpTargets.end()) - targetBlock = pairIter->second; + auto&& c = constant->getValue(); + if (c.ult(_bytecode.size())) + { + auto v = c.getZExtValue(); + auto it = basicBlocks.find(v); + if (it != basicBlocks.end() && it->second.isJumpDest()) + targetBlock = it->second.llvm(); + } + + if (!targetBlock) + targetBlock = getBadJumpBlock(); } if (inst == Instruction::JUMP) @@ -614,28 +579,30 @@ void Compiler::compileBasicBlock(BasicBlock& _basicBlock, bytes const& _bytecode { // The target address is computed at compile time, // just pop it without looking... - stack.pop(); m_builder.CreateBr(targetBlock); } else - m_builder.CreateBr(m_jumpTableBlock->llvm()); + { + stack.push(target); + m_builder.CreateBr(getJumpTableBlock()); + } } else // JUMPI { - stack.swap(1); auto val = stack.pop(); auto zero = Constant::get(0); auto cond = m_builder.CreateICmpNE(val, zero, "nonzero"); if (targetBlock) { - stack.pop(); m_builder.CreateCondBr(cond, targetBlock, _nextBasicBlock); } else - m_builder.CreateCondBr(cond, m_jumpTableBlock->llvm(), _nextBasicBlock); + { + stack.push(target); + m_builder.CreateCondBr(cond, getJumpTableBlock(), _nextBasicBlock); + } } - break; } @@ -666,7 +633,6 @@ void Compiler::compileBasicBlock(BasicBlock& _basicBlock, bytes const& _bytecode case Instruction::CALLDATASIZE: case Instruction::CODESIZE: case Instruction::GASPRICE: - case Instruction::PREVHASH: case Instruction::COINBASE: case Instruction::TIMESTAMP: case Instruction::NUMBER: @@ -678,6 +644,14 @@ void Compiler::compileBasicBlock(BasicBlock& _basicBlock, bytes const& _bytecode break; } + case Instruction::BLOCKHASH: + { + auto number = stack.pop(); + auto hash = _ext.blockhash(number); + stack.push(hash); + break; + } + case Instruction::BALANCE: { auto address = stack.pop(); @@ -835,6 +809,7 @@ void Compiler::compileBasicBlock(BasicBlock& _basicBlock, bytes const& _bytecode default: // Invalid instruction - runtime exception { + // TODO: Replace with return statement _runtimeManager.raiseException(ReturnCode::BadInstruction); } @@ -871,13 +846,6 @@ void Compiler::removeDeadBlocks() } } while (sthErased); - - // Remove jump table block if no predecessors - if (llvm::pred_begin(m_jumpTableBlock->llvm()) == llvm::pred_end(m_jumpTableBlock->llvm())) - { - m_jumpTableBlock->llvm()->eraseFromParent(); - m_jumpTableBlock.reset(); - } } void Compiler::dumpCFGifRequired(std::string const& _dotfilePath) diff --git a/evmjit/libevmjit/Compiler.h b/evmjit/libevmjit/Compiler.h index 8e3bf357c..1c00dc711 100644 --- a/evmjit/libevmjit/Compiler.h +++ b/evmjit/libevmjit/Compiler.h @@ -47,6 +47,10 @@ private: void compileBasicBlock(BasicBlock& _basicBlock, bytes const& _bytecode, class RuntimeManager& _runtimeManager, class Arith256& _arith, class Memory& _memory, class Ext& _ext, class GasMeter& _gasMeter, llvm::BasicBlock* _nextBasicBlock); + llvm::BasicBlock* getJumpTableBlock(); + + llvm::BasicBlock* getBadJumpBlock(); + void removeDeadBlocks(); /// Dumps basic block graph in graphviz format to a file, if option dumpCFG is enabled. @@ -65,22 +69,16 @@ private: llvm::IRBuilder<> m_builder; /// Maps a program counter pc to a basic block that starts at pc (if any). - std::map basicBlocks = {}; - - /// Maps a pc at which there is a JUMP or JUMPI to the target block of the jump. - std::map m_directJumpTargets = {}; - - /// A list of possible blocks to which there may be indirect jumps. - std::vector m_indirectJumpTargets = {}; + std::map basicBlocks; /// Stop basic block - terminates execution with STOP code (0) llvm::BasicBlock* m_stopBB = nullptr; /// Block with a jump table. - std::unique_ptr m_jumpTableBlock = nullptr; + std::unique_ptr m_jumpTableBlock; - /// Default destination for indirect jumps. - std::unique_ptr m_badJumpBlock = nullptr; + /// Destination for invalid jumps + std::unique_ptr m_badJumpBlock; /// Main program function llvm::Function* m_mainFunc = nullptr; diff --git a/evmjit/libevmjit/CompilerHelper.cpp b/evmjit/libevmjit/CompilerHelper.cpp index badf9d889..9cccecd79 100644 --- a/evmjit/libevmjit/CompilerHelper.cpp +++ b/evmjit/libevmjit/CompilerHelper.cpp @@ -35,6 +35,11 @@ llvm::Function* CompilerHelper::getMainFunction() return nullptr; } +llvm::CallInst* CompilerHelper::createCall(llvm::Function* _func, std::initializer_list const& _args) +{ + return getBuilder().CreateCall(_func, {_args.begin(), _args.size()}); +} + RuntimeHelper::RuntimeHelper(RuntimeManager& _runtimeManager): CompilerHelper(_runtimeManager.getBuilder()), diff --git a/evmjit/libevmjit/CompilerHelper.h b/evmjit/libevmjit/CompilerHelper.h index 19315fe4a..62733ca72 100644 --- a/evmjit/libevmjit/CompilerHelper.h +++ b/evmjit/libevmjit/CompilerHelper.h @@ -31,12 +31,7 @@ protected: llvm::IRBuilder<>& m_builder; llvm::IRBuilder<>& getBuilder() { return m_builder; } - template - llvm::CallInst* createCall(llvm::Function* _func, _Args*... _args) - { - llvm::Value* args[] = {_args...}; - return getBuilder().CreateCall(_func, args); - } + llvm::CallInst* createCall(llvm::Function* _func, std::initializer_list const& _args); friend class RuntimeHelper; }; diff --git a/evmjit/libevmjit/Ext.cpp b/evmjit/libevmjit/Ext.cpp index 38adffd3f..c30b0a1e3 100644 --- a/evmjit/libevmjit/Ext.cpp +++ b/evmjit/libevmjit/Ext.cpp @@ -24,113 +24,132 @@ Ext::Ext(RuntimeManager& _runtimeManager, Memory& _memoryMan): RuntimeHelper(_runtimeManager), m_memoryMan(_memoryMan) { - auto module = getModule(); - - m_args[0] = m_builder.CreateAlloca(Type::Word, nullptr, "ext.index"); - m_args[1] = m_builder.CreateAlloca(Type::Word, nullptr, "ext.value"); - m_arg2 = m_builder.CreateAlloca(Type::Word, nullptr, "ext.arg2"); - m_arg3 = m_builder.CreateAlloca(Type::Word, nullptr, "ext.arg3"); - m_arg4 = m_builder.CreateAlloca(Type::Word, nullptr, "ext.arg4"); - m_arg5 = m_builder.CreateAlloca(Type::Word, nullptr, "ext.arg5"); - m_arg6 = m_builder.CreateAlloca(Type::Word, nullptr, "ext.arg6"); - m_arg7 = m_builder.CreateAlloca(Type::Word, nullptr, "ext.arg7"); - m_arg8 = m_builder.CreateAlloca(Type::Word, nullptr, "ext.arg8"); m_size = m_builder.CreateAlloca(Type::Size, nullptr, "env.size"); +} - using Linkage = llvm::GlobalValue::LinkageTypes; - - llvm::Type* argsTypes[] = {Type::EnvPtr, Type::WordPtr, Type::WordPtr, Type::WordPtr, Type::WordPtr, Type::WordPtr, Type::WordPtr, Type::WordPtr, Type::WordPtr, Type::WordPtr}; - - m_sload = llvm::Function::Create(llvm::FunctionType::get(Type::Void, {argsTypes, 3}, false), Linkage::ExternalLinkage, "env_sload", module); - m_sstore = llvm::Function::Create(llvm::FunctionType::get(Type::Void, {argsTypes, 3}, false), Linkage::ExternalLinkage, "env_sstore", module); - - llvm::Type* sha3ArgsTypes[] = {Type::BytePtr, Type::Size, Type::WordPtr}; - m_sha3 = llvm::Function::Create(llvm::FunctionType::get(Type::Void, sha3ArgsTypes, false), Linkage::ExternalLinkage, "env_sha3", module); - - llvm::Type* createArgsTypes[] = {Type::EnvPtr, Type::WordPtr, Type::WordPtr, Type::BytePtr, Type::Size, Type::WordPtr}; - m_create = llvm::Function::Create(llvm::FunctionType::get(Type::Void, createArgsTypes, false), Linkage::ExternalLinkage, "env_create", module); - llvm::Type* callArgsTypes[] = {Type::EnvPtr, Type::WordPtr, Type::WordPtr, Type::WordPtr, Type::BytePtr, Type::Size, Type::BytePtr, Type::Size, Type::WordPtr}; - m_call = llvm::Function::Create(llvm::FunctionType::get(Type::Bool, callArgsTypes, false), Linkage::ExternalLinkage, "env_call", module); +using FuncDesc = std::tuple; - llvm::Type* logArgsTypes[] = {Type::EnvPtr, Type::BytePtr, Type::Size, Type::WordPtr, Type::WordPtr, Type::WordPtr, Type::WordPtr}; - m_log = llvm::Function::Create(llvm::FunctionType::get(Type::Void, logArgsTypes, false), Linkage::ExternalLinkage, "env_log", module); +llvm::FunctionType* getFunctionType(llvm::Type* _returnType, std::initializer_list const& _argsTypes) +{ + return llvm::FunctionType::get(_returnType, llvm::ArrayRef{_argsTypes.begin(), _argsTypes.size()}, false); +} - llvm::Type* getExtCodeArgsTypes[] = {Type::EnvPtr, Type::WordPtr, Type::Size->getPointerTo()}; - m_getExtCode = llvm::Function::Create(llvm::FunctionType::get(Type::BytePtr, getExtCodeArgsTypes, false), Linkage::ExternalLinkage, "env_getExtCode", module); +std::array::value> const& getEnvFuncDescs() +{ + static std::array::value> descs{{ + FuncDesc{"env_sload", getFunctionType(Type::Void, {Type::EnvPtr, Type::WordPtr, Type::WordPtr})}, + FuncDesc{"env_sstore", getFunctionType(Type::Void, {Type::EnvPtr, Type::WordPtr, Type::WordPtr})}, + FuncDesc{"env_sha3", getFunctionType(Type::Void, {Type::BytePtr, Type::Size, Type::WordPtr})}, + FuncDesc{"env_balance", getFunctionType(Type::Void, {Type::EnvPtr, Type::WordPtr, Type::WordPtr})}, + FuncDesc{"env_create", getFunctionType(Type::Void, {Type::EnvPtr, Type::WordPtr, Type::WordPtr, Type::BytePtr, Type::Size, Type::WordPtr})}, + FuncDesc{"env_call", getFunctionType(Type::Bool, {Type::EnvPtr, Type::WordPtr, Type::WordPtr, Type::WordPtr, Type::BytePtr, Type::Size, Type::BytePtr, Type::Size, Type::WordPtr})}, + FuncDesc{"env_log", getFunctionType(Type::Void, {Type::EnvPtr, Type::BytePtr, Type::Size, Type::WordPtr, Type::WordPtr, Type::WordPtr, Type::WordPtr})}, + FuncDesc{"env_blockhash", getFunctionType(Type::Void, {Type::EnvPtr, Type::WordPtr, Type::WordPtr})}, + FuncDesc{"env_getExtCode", getFunctionType(Type::BytePtr, {Type::EnvPtr, Type::WordPtr, Type::Size->getPointerTo()})}, + FuncDesc{"ext_calldataload", getFunctionType(Type::Void, {Type::RuntimeDataPtr, Type::WordPtr, Type::WordPtr})}, + }}; + + return descs; +} - // Helper function, not client Env interface - llvm::Type* callDataLoadArgsTypes[] = {Type::RuntimeDataPtr, Type::WordPtr, Type::WordPtr}; - m_calldataload = llvm::Function::Create(llvm::FunctionType::get(Type::Void, callDataLoadArgsTypes, false), Linkage::ExternalLinkage, "ext_calldataload", module); +llvm::Function* createFunc(EnvFunc _id, llvm::Module* _module) +{ + auto&& desc = getEnvFuncDescs()[static_cast(_id)]; + return llvm::Function::Create(std::get<1>(desc), llvm::Function::ExternalLinkage, std::get<0>(desc), _module); } -llvm::Function* Ext::getBalanceFunc() +llvm::Value* Ext::getArgAlloca() { - if (!m_balance) + auto& a = m_argAllocas[m_argCounter++]; + if (!a) { - llvm::Type* argsTypes[] = {Type::EnvPtr, Type::WordPtr, Type::WordPtr}; - m_balance = llvm::Function::Create(llvm::FunctionType::get(Type::Void, argsTypes, false), llvm::Function::ExternalLinkage, "env_balance", getModule()); + // FIXME: Improve order and names + InsertPointGuard g{getBuilder()}; + getBuilder().SetInsertPoint(getMainFunction()->front().getFirstNonPHI()); + a = getBuilder().CreateAlloca(Type::Word, nullptr, "arg"); } - return m_balance; + + return a; +} + +llvm::Value* Ext::byPtr(llvm::Value* _value) +{ + auto a = getArgAlloca(); + getBuilder().CreateStore(_value, a); + return a; +} + +llvm::CallInst* Ext::createCall(EnvFunc _funcId, std::initializer_list const& _args) +{ + auto& func = m_funcs[static_cast(_funcId)]; + if (!func) + func = createFunc(_funcId, getModule()); + + m_argCounter = 0; + return getBuilder().CreateCall(func, {_args.begin(), _args.size()}); } llvm::Value* Ext::sload(llvm::Value* _index) { - m_builder.CreateStore(_index, m_args[0]); - m_builder.CreateCall3(m_sload, getRuntimeManager().getEnvPtr(), m_args[0], m_args[1]); // Uses native endianness - return m_builder.CreateLoad(m_args[1]); + auto ret = getArgAlloca(); + createCall(EnvFunc::sload, {getRuntimeManager().getEnvPtr(), byPtr(_index), ret}); // Uses native endianness + return m_builder.CreateLoad(ret); } void Ext::sstore(llvm::Value* _index, llvm::Value* _value) { - m_builder.CreateStore(_index, m_args[0]); - m_builder.CreateStore(_value, m_args[1]); - m_builder.CreateCall3(m_sstore, getRuntimeManager().getEnvPtr(), m_args[0], m_args[1]); // Uses native endianness + createCall(EnvFunc::sstore, {getRuntimeManager().getEnvPtr(), byPtr(_index), byPtr(_value)}); // Uses native endianness } llvm::Value* Ext::calldataload(llvm::Value* _index) { - m_builder.CreateStore(_index, m_args[0]); - createCall(m_calldataload, getRuntimeManager().getDataPtr(), m_args[0], m_args[1]); - auto ret = m_builder.CreateLoad(m_args[1]); + auto ret = getArgAlloca(); + createCall(EnvFunc::calldataload, {getRuntimeManager().getDataPtr(), byPtr(_index), ret}); + ret = m_builder.CreateLoad(ret); return Endianness::toNative(m_builder, ret); } llvm::Value* Ext::balance(llvm::Value* _address) { auto address = Endianness::toBE(m_builder, _address); - m_builder.CreateStore(address, m_args[0]); - createCall(getBalanceFunc(), getRuntimeManager().getEnvPtr(), m_args[0], m_args[1]); - return m_builder.CreateLoad(m_args[1]); + auto ret = getArgAlloca(); + createCall(EnvFunc::balance, {getRuntimeManager().getEnvPtr(), byPtr(address), ret}); + return m_builder.CreateLoad(ret); +} + +llvm::Value* Ext::blockhash(llvm::Value* _number) +{ + auto hash = getArgAlloca(); + createCall(EnvFunc::blockhash, {getRuntimeManager().getEnvPtr(), byPtr(_number), hash}); + hash = m_builder.CreateLoad(hash); + return Endianness::toNative(getBuilder(), hash); } llvm::Value* Ext::create(llvm::Value*& _gas, llvm::Value* _endowment, llvm::Value* _initOff, llvm::Value* _initSize) { - m_builder.CreateStore(_gas, m_args[0]); - m_builder.CreateStore(_endowment, m_arg2); + auto gas = byPtr(_gas); + auto ret = getArgAlloca(); auto begin = m_memoryMan.getBytePtr(_initOff); auto size = m_builder.CreateTrunc(_initSize, Type::Size, "size"); - createCall(m_create, getRuntimeManager().getEnvPtr(), m_args[0], m_arg2, begin, size, m_args[1]); - _gas = m_builder.CreateLoad(m_args[0]); // Return gas - llvm::Value* address = m_builder.CreateLoad(m_args[1]); + createCall(EnvFunc::create, {getRuntimeManager().getEnvPtr(), gas, byPtr(_endowment), begin, size, ret}); + _gas = m_builder.CreateLoad(gas); // Return gas + llvm::Value* address = m_builder.CreateLoad(ret); address = Endianness::toNative(m_builder, address); return address; } llvm::Value* Ext::call(llvm::Value*& _gas, llvm::Value* _receiveAddress, llvm::Value* _value, llvm::Value* _inOff, llvm::Value* _inSize, llvm::Value* _outOff, llvm::Value* _outSize, llvm::Value* _codeAddress) { - m_builder.CreateStore(_gas, m_args[0]); + auto gas = byPtr(_gas); auto receiveAddress = Endianness::toBE(m_builder, _receiveAddress); - m_builder.CreateStore(receiveAddress, m_arg2); - m_builder.CreateStore(_value, m_arg3); auto inBeg = m_memoryMan.getBytePtr(_inOff); auto inSize = m_builder.CreateTrunc(_inSize, Type::Size, "in.size"); auto outBeg = m_memoryMan.getBytePtr(_outOff); auto outSize = m_builder.CreateTrunc(_outSize, Type::Size, "out.size"); auto codeAddress = Endianness::toBE(m_builder, _codeAddress); - m_builder.CreateStore(codeAddress, m_arg8); - auto ret = createCall(m_call, getRuntimeManager().getEnvPtr(), m_args[0], m_arg2, m_arg3, inBeg, inSize, outBeg, outSize, m_arg8); - _gas = m_builder.CreateLoad(m_args[0]); // Return gas + auto ret = createCall(EnvFunc::call, {getRuntimeManager().getEnvPtr(), gas, byPtr(receiveAddress), byPtr(_value), inBeg, inSize, outBeg, outSize, byPtr(codeAddress)}); + _gas = m_builder.CreateLoad(gas); // Return gas return m_builder.CreateZExt(ret, Type::Word, "ret"); } @@ -138,8 +157,9 @@ llvm::Value* Ext::sha3(llvm::Value* _inOff, llvm::Value* _inSize) { auto begin = m_memoryMan.getBytePtr(_inOff); auto size = m_builder.CreateTrunc(_inSize, Type::Size, "size"); - createCall(m_sha3, begin, size, m_args[1]); - llvm::Value* hash = m_builder.CreateLoad(m_args[1]); + auto ret = getArgAlloca(); + createCall(EnvFunc::sha3, {begin, size, ret}); + llvm::Value* hash = m_builder.CreateLoad(ret); hash = Endianness::toNative(m_builder, hash); return hash; } @@ -147,8 +167,7 @@ llvm::Value* Ext::sha3(llvm::Value* _inOff, llvm::Value* _inSize) MemoryRef Ext::getExtCode(llvm::Value* _addr) { auto addr = Endianness::toBE(m_builder, _addr); - m_builder.CreateStore(addr, m_args[0]); - auto code = createCall(m_getExtCode, getRuntimeManager().getEnvPtr(), m_args[0], m_size); + auto code = createCall(EnvFunc::getExtCode, {getRuntimeManager().getEnvPtr(), byPtr(addr), m_size}); auto codeSize = m_builder.CreateLoad(m_size); auto codeSize256 = m_builder.CreateZExt(codeSize, Type::Word); return {code, codeSize256}; @@ -158,7 +177,7 @@ void Ext::log(llvm::Value* _memIdx, llvm::Value* _numBytes, std::array } } diff --git a/evmjit/libevmjit/Ext.h b/evmjit/libevmjit/Ext.h index be71dc1ff..2850be072 100644 --- a/evmjit/libevmjit/Ext.h +++ b/evmjit/libevmjit/Ext.h @@ -18,6 +18,28 @@ struct MemoryRef llvm::Value* size; }; +template +struct sizeOf +{ + static const size_t value = static_cast(_EnumT::_size); +}; + +enum class EnvFunc +{ + sload, + sstore, + sha3, + balance, + create, + call, + log, + blockhash, + getExtCode, + calldataload, // Helper function, not client Env interface + + _size +}; + class Ext : public RuntimeHelper { public: @@ -30,6 +52,7 @@ public: llvm::Value* calldataload(llvm::Value* _index); llvm::Value* create(llvm::Value*& _gas, llvm::Value* _endowment, llvm::Value* _initOff, llvm::Value* _initSize); llvm::Value* call(llvm::Value*& _gas, llvm::Value* _receiveAddress, llvm::Value* _value, llvm::Value* _inOff, llvm::Value* _inSize, llvm::Value* _outOff, llvm::Value* _outSize, llvm::Value* _codeAddress); + llvm::Value* blockhash(llvm::Value* _number); llvm::Value* sha3(llvm::Value* _inOff, llvm::Value* _inSize); MemoryRef getExtCode(llvm::Value* _addr); @@ -39,27 +62,16 @@ public: private: Memory& m_memoryMan; - llvm::Value* m_args[2]; - llvm::Value* m_arg2; - llvm::Value* m_arg3; - llvm::Value* m_arg4; - llvm::Value* m_arg5; - llvm::Value* m_arg6; - llvm::Value* m_arg7; - llvm::Value* m_arg8; llvm::Value* m_size; llvm::Value* m_data = nullptr; - llvm::Function* m_sload; - llvm::Function* m_sstore; - llvm::Function* m_calldataload; - llvm::Function* m_balance = nullptr; - llvm::Function* m_create; - llvm::Function* m_call; - llvm::Function* m_sha3; - llvm::Function* m_getExtCode; - llvm::Function* m_log; - - llvm::Function* getBalanceFunc(); + + std::array::value> m_funcs = {}; + std::array m_argAllocas = {}; + size_t m_argCounter = 0; + + llvm::CallInst* createCall(EnvFunc _funcId, std::initializer_list const& _args); + llvm::Value* getArgAlloca(); + llvm::Value* byPtr(llvm::Value* _value); }; diff --git a/evmjit/libevmjit/GasMeter.cpp b/evmjit/libevmjit/GasMeter.cpp index c31942a45..39f4560e5 100644 --- a/evmjit/libevmjit/GasMeter.cpp +++ b/evmjit/libevmjit/GasMeter.cpp @@ -119,7 +119,7 @@ void GasMeter::count(Instruction _inst) if (!m_checkCall) { // Create gas check call with mocked block cost at begining of current cost-block - m_checkCall = createCall(m_gasCheckFunc, m_runtimeManager.getRuntimePtr(), llvm::UndefValue::get(Type::Word)); + m_checkCall = createCall(m_gasCheckFunc, {m_runtimeManager.getRuntimePtr(), llvm::UndefValue::get(Type::Word)}); } m_blockCost += getStepCost(_inst); @@ -127,7 +127,7 @@ void GasMeter::count(Instruction _inst) void GasMeter::count(llvm::Value* _cost) { - createCall(m_gasCheckFunc, m_runtimeManager.getRuntimePtr(), _cost); + createCall(m_gasCheckFunc, {m_runtimeManager.getRuntimePtr(), _cost}); } void GasMeter::countExp(llvm::Value* _exponent) diff --git a/evmjit/libevmjit/Instruction.h b/evmjit/libevmjit/Instruction.h index 502c4b66e..3f84efa08 100644 --- a/evmjit/libevmjit/Instruction.h +++ b/evmjit/libevmjit/Instruction.h @@ -58,7 +58,7 @@ enum class Instruction: uint8_t EXTCODESIZE, ///< get external code size (from another contract) EXTCODECOPY, ///< copy external code (from another contract) - PREVHASH = 0x40, ///< get hash of most recent complete block + BLOCKHASH = 0x40, ///< get hash of most recent complete block COINBASE, ///< get the block's coinbase address TIMESTAMP, ///< get the block's timestamp NUMBER, ///< get the block's number diff --git a/evmjit/libevmjit/Memory.cpp b/evmjit/libevmjit/Memory.cpp index c60a5e554..9f57c7a4b 100644 --- a/evmjit/libevmjit/Memory.cpp +++ b/evmjit/libevmjit/Memory.cpp @@ -146,18 +146,18 @@ llvm::Function* Memory::createFunc(bool _isStore, llvm::Type* _valueType, GasMet llvm::Value* Memory::loadWord(llvm::Value* _addr) { - return createCall(m_loadWord, getRuntimeManager().getRuntimePtr(), _addr); + return createCall(m_loadWord, {getRuntimeManager().getRuntimePtr(), _addr}); } void Memory::storeWord(llvm::Value* _addr, llvm::Value* _word) { - createCall(m_storeWord, getRuntimeManager().getRuntimePtr(), _addr, _word); + createCall(m_storeWord, {getRuntimeManager().getRuntimePtr(), _addr, _word}); } void Memory::storeByte(llvm::Value* _addr, llvm::Value* _word) { auto byte = m_builder.CreateTrunc(_word, Type::Byte, "byte"); - createCall(m_storeByte, getRuntimeManager().getRuntimePtr(), _addr, byte); + createCall(m_storeByte, {getRuntimeManager().getRuntimePtr(), _addr, byte}); } llvm::Value* Memory::getData() @@ -181,7 +181,7 @@ llvm::Value* Memory::getBytePtr(llvm::Value* _index) void Memory::require(llvm::Value* _offset, llvm::Value* _size) { - createCall(m_require, getRuntimeManager().getRuntimePtr(), _offset, _size); + createCall(m_require, {getRuntimeManager().getRuntimePtr(), _offset, _size}); } void Memory::copyBytes(llvm::Value* _srcPtr, llvm::Value* _srcSize, llvm::Value* _srcIdx, diff --git a/evmjit/libevmjit/Runtime.cpp b/evmjit/libevmjit/Runtime.cpp index 27c81ea86..2522e8ace 100644 --- a/evmjit/libevmjit/Runtime.cpp +++ b/evmjit/libevmjit/Runtime.cpp @@ -11,29 +11,12 @@ namespace eth { namespace jit { -namespace -{ - jmp_buf_ref g_currJmpBuf; -} - -jmp_buf_ref Runtime::getCurrJmpBuf() -{ - return g_currJmpBuf; -} -Runtime::Runtime(RuntimeData* _data, Env* _env): +Runtime::Runtime(RuntimeData* _data, Env* _env) : m_data(*_data), m_env(*_env), - m_currJmpBuf(m_jmpBuf), - m_prevJmpBuf(g_currJmpBuf) -{ - g_currJmpBuf = m_jmpBuf; -} - -Runtime::~Runtime() -{ - g_currJmpBuf = m_prevJmpBuf; -} + m_currJmpBuf(m_jmpBuf) +{} bytes Runtime::getReturnData() const // FIXME: Reconsider returning by copy { diff --git a/evmjit/libevmjit/Runtime.h b/evmjit/libevmjit/Runtime.h index e11dac319..cedfaaf70 100644 --- a/evmjit/libevmjit/Runtime.h +++ b/evmjit/libevmjit/Runtime.h @@ -32,7 +32,6 @@ class Runtime { public: Runtime(RuntimeData* _data, Env* _env); - ~Runtime(); Runtime(const Runtime&) = delete; void operator=(const Runtime&) = delete; @@ -43,7 +42,6 @@ public: bytes getReturnData() const; jmp_buf_ref getJmpBuf() { return m_jmpBuf; } - static jmp_buf_ref getCurrJmpBuf(); private: RuntimeData& m_data; ///< Pointer to data. Expected by compiled contract. @@ -51,7 +49,6 @@ private: jmp_buf_ref m_currJmpBuf; ///< Pointer to jump buffer. Expected by compiled contract. byte* m_memoryData = nullptr; i256 m_memorySize = {}; - jmp_buf_ref m_prevJmpBuf; std::jmp_buf m_jmpBuf; StackImpl m_stack; MemoryImpl m_memory; diff --git a/evmjit/libevmjit/RuntimeData.h b/evmjit/libevmjit/RuntimeData.h index 89987bdeb..bb52f7864 100644 --- a/evmjit/libevmjit/RuntimeData.h +++ b/evmjit/libevmjit/RuntimeData.h @@ -21,7 +21,6 @@ struct RuntimeData CallValue, CallDataSize, GasPrice, - PrevHash, CoinBase, TimeStamp, Number, diff --git a/evmjit/libevmjit/RuntimeManager.cpp b/evmjit/libevmjit/RuntimeManager.cpp index 14280f80f..ea2fe20b5 100644 --- a/evmjit/libevmjit/RuntimeManager.cpp +++ b/evmjit/libevmjit/RuntimeManager.cpp @@ -63,7 +63,6 @@ llvm::Twine getName(RuntimeData::Index _index) case RuntimeData::CallValue: return "callvalue"; case RuntimeData::CallDataSize: return "calldatasize"; case RuntimeData::GasPrice: return "gasprice"; - case RuntimeData::PrevHash: return "prevhash"; case RuntimeData::CoinBase: return "coinbase"; case RuntimeData::TimeStamp: return "timestamp"; case RuntimeData::Number: return "number"; @@ -154,7 +153,6 @@ llvm::Value* RuntimeManager::get(Instruction _inst) case Instruction::CALLVALUE: return get(RuntimeData::CallValue); case Instruction::CALLDATASIZE: return get(RuntimeData::CallDataSize); case Instruction::GASPRICE: return get(RuntimeData::GasPrice); - case Instruction::PREVHASH: return get(RuntimeData::PrevHash); case Instruction::COINBASE: return get(RuntimeData::CoinBase); case Instruction::TIMESTAMP: return get(RuntimeData::TimeStamp); case Instruction::NUMBER: return get(RuntimeData::Number); diff --git a/evmjit/libevmjit/Utils.cpp b/evmjit/libevmjit/Utils.cpp index f1ffbf67f..9d9b3acbb 100644 --- a/evmjit/libevmjit/Utils.cpp +++ b/evmjit/libevmjit/Utils.cpp @@ -1,8 +1,7 @@ -#include +#include #include "Utils.h" #include "Instruction.h" -#include "Runtime.h" namespace dev { @@ -55,12 +54,6 @@ llvm::APInt readPushData(bytes::const_iterator& _curr, bytes::const_iterator _en return value; } -void terminate(ReturnCode _returnCode) -{ - auto jmpBuf = Runtime::getCurrJmpBuf(); - std::longjmp(jmpBuf, static_cast(_returnCode)); -} - } } } diff --git a/evmjit/libevmjit/Utils.h b/evmjit/libevmjit/Utils.h index db0647fdf..f672365c6 100644 --- a/evmjit/libevmjit/Utils.h +++ b/evmjit/libevmjit/Utils.h @@ -17,8 +17,6 @@ struct JIT: public NoteChannel { static const char* name() { return "JIT"; } }; u256 llvm2eth(i256); i256 eth2llvm(u256); -void terminate(ReturnCode _returnCode); - } } } From 5bc24913bf500b2cb1ba547a12c67f7ba3064ecc Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Pawe=C5=82=20Bylica?= Date: Thu, 8 Jan 2015 12:52:59 +0100 Subject: [PATCH 2/7] Fix some GCC initialization warnings --- evmjit/libevmjit/Common.h | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/evmjit/libevmjit/Common.h b/evmjit/libevmjit/Common.h index 436931dcd..1d8451c74 100644 --- a/evmjit/libevmjit/Common.h +++ b/evmjit/libevmjit/Common.h @@ -37,10 +37,10 @@ enum class ReturnCode // TODO: Replace with h256 struct i256 { - uint64_t a; - uint64_t b; - uint64_t c; - uint64_t d; + uint64_t a = 0; + uint64_t b = 0; + uint64_t c = 0; + uint64_t d = 0; }; static_assert(sizeof(i256) == 32, "Wrong i265 size"); From 0a3edb98a36a4c2882f8505aeee30506eac7a0b4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Pawe=C5=82=20Bylica?= Date: Thu, 8 Jan 2015 15:02:31 +0100 Subject: [PATCH 3/7] Add option to use JIT in mining --- eth/main.cpp | 20 ++++++++++++++++++-- 1 file changed, 18 insertions(+), 2 deletions(-) diff --git a/eth/main.cpp b/eth/main.cpp index dae61e114..a0a605193 100644 --- a/eth/main.cpp +++ b/eth/main.cpp @@ -30,6 +30,7 @@ #include #include #include +#include #include #include #if ETH_READLINE @@ -121,7 +122,11 @@ void help() << " -u,--public-ip Force public ip to given (default; auto)." << endl << " -v,--verbosity <0 - 9> Set the log verbosity from 0 to 9 (Default: 8)." << endl << " -x,--peers Attempt to connect to given number of peers (Default: 5)." << endl - << " -V,--version Show the version and exit." << endl; + << " -V,--version Show the version and exit." << endl +#if ETH_EVMJIT + << " --jit Use EVM JIT (default: off)." << endl +#endif + ; exit(0); } @@ -193,6 +198,7 @@ int main(int argc, char** argv) bool upnp = true; bool useLocal = false; bool forceMining = false; + bool jit = false; string clientName; // Init defaults @@ -295,6 +301,15 @@ int main(int argc, char** argv) return -1; } } + else if (arg == "--jit") + { +#if ETH_EVMJIT + jit = true; +#else + cerr << "EVM JIT not enabled" << endl; + return -1; +#endif + } else if (arg == "-h" || arg == "--help") help(); else if (arg == "-V" || arg == "--version") @@ -308,9 +323,10 @@ int main(int argc, char** argv) cout << credits(); + VMFactory::setKind(jit ? VMKind::JIT : VMKind::Interpreter); NetworkPreferences netPrefs(listenPort, publicIP, upnp, useLocal); dev::WebThreeDirect web3( - "Ethereum(++)/" + clientName + "v" + dev::Version + "/" DEV_QUOTED(ETH_BUILD_TYPE) "/" DEV_QUOTED(ETH_BUILD_PLATFORM), + "Ethereum(++)/" + clientName + "v" + dev::Version + "/" DEV_QUOTED(ETH_BUILD_TYPE) "/" DEV_QUOTED(ETH_BUILD_PLATFORM) + (jit ? "/JIT" : ""), dbPath, false, mode == NodeMode::Full ? set{"eth", "shh"} : set(), From 1f50bcd3b5170da23260a11b8c5df09b9890256e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Pawe=C5=82=20Bylica?= Date: Thu, 8 Jan 2015 15:06:12 +0100 Subject: [PATCH 4/7] Fix warnings --- evmjit/libevmjit/BasicBlock.h | 4 ++-- evmjit/libevmjit/Cache.h | 1 - evmjit/libevmjit/ExecutionEngine.cpp | 5 +++++ evmjit/libevmjit/Ext.h | 4 ++-- evmjit/libevmjit/Runtime.h | 2 +- 5 files changed, 10 insertions(+), 6 deletions(-) diff --git a/evmjit/libevmjit/BasicBlock.h b/evmjit/libevmjit/BasicBlock.h index 4a789a8ec..dd099af8a 100644 --- a/evmjit/libevmjit/BasicBlock.h +++ b/evmjit/libevmjit/BasicBlock.h @@ -102,12 +102,12 @@ private: /// 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 = {}; + 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 = {}; + std::vector m_currentStack; /// How many items higher is the current stack than the initial one. /// May be negative. diff --git a/evmjit/libevmjit/Cache.h b/evmjit/libevmjit/Cache.h index 80fe47ade..d1027288e 100644 --- a/evmjit/libevmjit/Cache.h +++ b/evmjit/libevmjit/Cache.h @@ -2,7 +2,6 @@ #include #include -#include #include diff --git a/evmjit/libevmjit/ExecutionEngine.cpp b/evmjit/libevmjit/ExecutionEngine.cpp index 862586575..499312b38 100644 --- a/evmjit/libevmjit/ExecutionEngine.cpp +++ b/evmjit/libevmjit/ExecutionEngine.cpp @@ -4,8 +4,13 @@ #include #include +#pragma warning(push) +#pragma GCC diagnostic push +#pragma GCC diagnostic ignored "-Wunused-parameter" #include #include +#pragma warning(pop) +#pragma GCC diagnostic pop #include #include #include diff --git a/evmjit/libevmjit/Ext.h b/evmjit/libevmjit/Ext.h index 2850be072..e6f1e4667 100644 --- a/evmjit/libevmjit/Ext.h +++ b/evmjit/libevmjit/Ext.h @@ -65,8 +65,8 @@ private: llvm::Value* m_size; llvm::Value* m_data = nullptr; - std::array::value> m_funcs = {}; - std::array m_argAllocas = {}; + std::array::value> m_funcs = {{}}; + std::array m_argAllocas = {{}}; size_t m_argCounter = 0; llvm::CallInst* createCall(EnvFunc _funcId, std::initializer_list const& _args); diff --git a/evmjit/libevmjit/Runtime.h b/evmjit/libevmjit/Runtime.h index cedfaaf70..8cc5b7968 100644 --- a/evmjit/libevmjit/Runtime.h +++ b/evmjit/libevmjit/Runtime.h @@ -48,7 +48,7 @@ private: Env& m_env; ///< Pointer to environment proxy. Expected by compiled contract. jmp_buf_ref m_currJmpBuf; ///< Pointer to jump buffer. Expected by compiled contract. byte* m_memoryData = nullptr; - i256 m_memorySize = {}; + i256 m_memorySize; std::jmp_buf m_jmpBuf; StackImpl m_stack; MemoryImpl m_memory; From b09ebfe2f2d7d4131fa8e926042772ca0ede8eb7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Pawe=C5=82=20Bylica?= Date: Thu, 8 Jan 2015 15:07:54 +0100 Subject: [PATCH 5/7] Add option to use JIT in mining (reverted from commit 0a3edb98a36a4c2882f8505aeee30506eac7a0b4) --- eth/main.cpp | 20 ++------------------ 1 file changed, 2 insertions(+), 18 deletions(-) diff --git a/eth/main.cpp b/eth/main.cpp index a0a605193..dae61e114 100644 --- a/eth/main.cpp +++ b/eth/main.cpp @@ -30,7 +30,6 @@ #include #include #include -#include #include #include #if ETH_READLINE @@ -122,11 +121,7 @@ void help() << " -u,--public-ip Force public ip to given (default; auto)." << endl << " -v,--verbosity <0 - 9> Set the log verbosity from 0 to 9 (Default: 8)." << endl << " -x,--peers Attempt to connect to given number of peers (Default: 5)." << endl - << " -V,--version Show the version and exit." << endl -#if ETH_EVMJIT - << " --jit Use EVM JIT (default: off)." << endl -#endif - ; + << " -V,--version Show the version and exit." << endl; exit(0); } @@ -198,7 +193,6 @@ int main(int argc, char** argv) bool upnp = true; bool useLocal = false; bool forceMining = false; - bool jit = false; string clientName; // Init defaults @@ -301,15 +295,6 @@ int main(int argc, char** argv) return -1; } } - else if (arg == "--jit") - { -#if ETH_EVMJIT - jit = true; -#else - cerr << "EVM JIT not enabled" << endl; - return -1; -#endif - } else if (arg == "-h" || arg == "--help") help(); else if (arg == "-V" || arg == "--version") @@ -323,10 +308,9 @@ int main(int argc, char** argv) cout << credits(); - VMFactory::setKind(jit ? VMKind::JIT : VMKind::Interpreter); NetworkPreferences netPrefs(listenPort, publicIP, upnp, useLocal); dev::WebThreeDirect web3( - "Ethereum(++)/" + clientName + "v" + dev::Version + "/" DEV_QUOTED(ETH_BUILD_TYPE) "/" DEV_QUOTED(ETH_BUILD_PLATFORM) + (jit ? "/JIT" : ""), + "Ethereum(++)/" + clientName + "v" + dev::Version + "/" DEV_QUOTED(ETH_BUILD_TYPE) "/" DEV_QUOTED(ETH_BUILD_PLATFORM), dbPath, false, mode == NodeMode::Full ? set{"eth", "shh"} : set(), From 272a0bd6fc08cb8f51a7e5e3e2b88adac6e75ce4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Pawe=C5=82=20Bylica?= Date: Fri, 9 Jan 2015 16:16:56 +0100 Subject: [PATCH 6/7] EVMJIT: Better object cache and other improvements --- evmjit/CMakeLists.txt | 2 + evmjit/libevmjit/BasicBlock.cpp | 19 ++-- evmjit/libevmjit/BasicBlock.h | 23 +++-- evmjit/libevmjit/Cache.cpp | 40 ++++++--- evmjit/libevmjit/Cache.h | 1 + evmjit/libevmjit/Compiler.cpp | 127 ++++++++++++--------------- evmjit/libevmjit/Compiler.h | 2 +- evmjit/libevmjit/ExecutionEngine.cpp | 11 ++- evmjit/libevmjit/Ext.cpp | 4 +- evmjit/libevmjit/Ext.h | 4 +- evmjit/libevmjit/GasMeter.cpp | 2 +- evmjit/libevmjit/Instruction.cpp | 40 +++++++++ evmjit/libevmjit/Instruction.h | 6 +- evmjit/libevmjit/Utils.cpp | 19 ---- 14 files changed, 168 insertions(+), 132 deletions(-) create mode 100644 evmjit/libevmjit/Instruction.cpp diff --git a/evmjit/CMakeLists.txt b/evmjit/CMakeLists.txt index 26a8010d1..222fa5b72 100644 --- a/evmjit/CMakeLists.txt +++ b/evmjit/CMakeLists.txt @@ -19,6 +19,8 @@ else() set(LLVM_LIBS "-lLLVMBitWriter -lLLVMX86CodeGen -lLLVMSelectionDAG -lLLVMAsmPrinter -lLLVMCodeGen -lLLVMScalarOpts -lLLVMInstCombine -lLLVMTransformUtils -lLLVMipa -lLLVMAnalysis -lLLVMX86AsmParser -lLLVMX86Desc -lLLVMX86Info -lLLVMX86AsmPrinter -lLLVMX86Utils -lLLVMMCJIT -lLLVMTarget -lLLVMRuntimeDyld -lLLVMObject -lLLVMMCParser -lLLVMBitReader -lLLVMExecutionEngine -lLLVMMC -lLLVMCore -lLLVMSupport -lz -lpthread -lffi -ltinfo -ldl -lm") endif() +add_definitions(-D_SCL_SECURE_NO_WARNINGS) # LLVM needs it on Windows + # Boost find_package(Boost REQUIRED) diff --git a/evmjit/libevmjit/BasicBlock.cpp b/evmjit/libevmjit/BasicBlock.cpp index d233ea744..dda0fbc36 100644 --- a/evmjit/libevmjit/BasicBlock.cpp +++ b/evmjit/libevmjit/BasicBlock.cpp @@ -20,20 +20,21 @@ namespace jit const char* BasicBlock::NamePrefix = "Instr."; -BasicBlock::BasicBlock(ProgramCounter _beginInstIdx, ProgramCounter _endInstIdx, llvm::Function* _mainFunc, llvm::IRBuilder<>& _builder) : - m_beginInstIdx(_beginInstIdx), - m_endInstIdx(_endInstIdx), - m_llvmBB(llvm::BasicBlock::Create(_mainFunc->getContext(), {NamePrefix, std::to_string(_beginInstIdx)}, _mainFunc)), +BasicBlock::BasicBlock(bytes::const_iterator _begin, bytes::const_iterator _end, llvm::Function* _mainFunc, llvm::IRBuilder<>& _builder, bool isJumpDest) : + m_begin(_begin), + m_end(_end), + // TODO: Add begin index to name + m_llvmBB(llvm::BasicBlock::Create(_mainFunc->getContext(), NamePrefix, _mainFunc)), m_stack(*this), - m_builder(_builder) + m_builder(_builder), + m_isJumpDest(isJumpDest) {} -BasicBlock::BasicBlock(std::string _name, llvm::Function* _mainFunc, llvm::IRBuilder<>& _builder) : - m_beginInstIdx(0), - m_endInstIdx(0), +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_builder(_builder), + m_isJumpDest(isJumpDest) {} BasicBlock::LocalStack::LocalStack(BasicBlock& _owner) : diff --git a/evmjit/libevmjit/BasicBlock.h b/evmjit/libevmjit/BasicBlock.h index dd099af8a..8d3c31922 100644 --- a/evmjit/libevmjit/BasicBlock.h +++ b/evmjit/libevmjit/BasicBlock.h @@ -1,9 +1,7 @@ #pragma once - #include - #include - +#include "Common.h" #include "Stack.h" namespace dev @@ -52,23 +50,22 @@ public: BasicBlock& m_bblock; }; - /// Basic block name prefix. The rest is beging instruction index. + /// Basic block name prefix. The rest is instruction index. static const char* NamePrefix; - explicit BasicBlock(ProgramCounter _beginInstIdx, ProgramCounter _endInstIdx, llvm::Function* _mainFunc, llvm::IRBuilder<>& _builder); - explicit BasicBlock(std::string _name, llvm::Function* _mainFunc, llvm::IRBuilder<>& _builder); + explicit BasicBlock(bytes::const_iterator _begin, bytes::const_iterator _end, llvm::Function* _mainFunc, llvm::IRBuilder<>& _builder, bool isJumpDest); + explicit BasicBlock(std::string _name, llvm::Function* _mainFunc, llvm::IRBuilder<>& _builder, bool isJumpDest); BasicBlock(const BasicBlock&) = delete; void operator=(const BasicBlock&) = delete; - operator llvm::BasicBlock*() { return m_llvmBB; } + operator llvm::BasicBlock*() { return m_llvmBB; } // TODO: Remove it llvm::BasicBlock* llvm() { return m_llvmBB; } - ProgramCounter begin() { return m_beginInstIdx; } - ProgramCounter end() { return m_endInstIdx; } + bytes::const_iterator begin() { return m_begin; } + bytes::const_iterator end() { return m_end; } bool isJumpDest() const { return m_isJumpDest; } - void markAsJumpDest() { m_isJumpDest = true; } LocalStack& localStack() { return m_stack; } @@ -85,8 +82,8 @@ public: void dump(std::ostream& os, bool _dotOutput = false); private: - ProgramCounter const m_beginInstIdx; - ProgramCounter const m_endInstIdx; + bytes::const_iterator const m_begin; + bytes::const_iterator const m_end; llvm::BasicBlock* const m_llvmBB; @@ -115,7 +112,7 @@ private: /// Is the basic block a valid jump destination. /// JUMPDEST is the first instruction of the basic block. - bool m_isJumpDest = false; + bool const m_isJumpDest = false; }; } diff --git a/evmjit/libevmjit/Cache.cpp b/evmjit/libevmjit/Cache.cpp index a887d91e9..2311c0496 100644 --- a/evmjit/libevmjit/Cache.cpp +++ b/evmjit/libevmjit/Cache.cpp @@ -3,6 +3,7 @@ #include #include #include +#include #include #include #include @@ -23,6 +24,32 @@ ObjectCache* Cache::getObjectCache() return &objectCache; } +namespace +{ + llvm::MemoryBuffer* lastObject; +} + +std::unique_ptr Cache::getObject(std::string const& id) +{ + assert(!lastObject); + llvm::SmallString<256> cachePath; + llvm::sys::path::system_temp_directory(false, cachePath); + llvm::sys::path::append(cachePath, "evm_objs", id); + + if (auto r = llvm::MemoryBuffer::getFile(cachePath.str(), -1, false)) + lastObject = llvm::MemoryBuffer::getMemBufferCopy(r.get()->getBuffer()); + else if (r.getError() != std::make_error_code(std::errc::no_such_file_or_directory)) + std::cerr << r.getError().message(); // TODO: Add log + + if (lastObject) // if object found create fake module + { + auto module = std::unique_ptr(new llvm::Module(id, llvm::getGlobalContext())); + auto mainFuncType = llvm::FunctionType::get(llvm::IntegerType::get(llvm::getGlobalContext(), 32), {}, false); + auto func = llvm::Function::Create(mainFuncType, llvm::Function::ExternalLinkage, id, module.get()); + } + return nullptr; +} + void ObjectCache::notifyObjectCompiled(llvm::Module const* _module, llvm::MemoryBuffer const* _object) { @@ -43,16 +70,9 @@ void ObjectCache::notifyObjectCompiled(llvm::Module const* _module, llvm::Memory llvm::MemoryBuffer* ObjectCache::getObject(llvm::Module const* _module) { - auto&& id = _module->getModuleIdentifier(); - llvm::SmallString<256> cachePath; - llvm::sys::path::system_temp_directory(false, cachePath); - llvm::sys::path::append(cachePath, "evm_objs", id); - - if (auto r = llvm::MemoryBuffer::getFile(cachePath.str(), -1, false)) - return llvm::MemoryBuffer::getMemBufferCopy(r.get()->getBuffer()); - else if (r.getError() != std::make_error_code(std::errc::no_such_file_or_directory)) - std::cerr << r.getError().message(); // TODO: Add log - return nullptr; + auto o = lastObject; + lastObject = nullptr; + return o; } } diff --git a/evmjit/libevmjit/Cache.h b/evmjit/libevmjit/Cache.h index d1027288e..1cad537cd 100644 --- a/evmjit/libevmjit/Cache.h +++ b/evmjit/libevmjit/Cache.h @@ -33,6 +33,7 @@ class Cache { public: static ObjectCache* getObjectCache(); + static std::unique_ptr getObject(std::string const& id); }; } diff --git a/evmjit/libevmjit/Compiler.cpp b/evmjit/libevmjit/Compiler.cpp index abd725c7f..70d40b431 100644 --- a/evmjit/libevmjit/Compiler.cpp +++ b/evmjit/libevmjit/Compiler.cpp @@ -40,77 +40,69 @@ Compiler::Compiler(Options const& _options): void Compiler::createBasicBlocks(bytes const& _bytecode) { - // FIXME: Simplify this algorithm. All can be done in one pass - - std::set splitPoints; // Sorted collections of instruction indices where basic blocks start/end - - std::vector indirectJumpTargets; - - splitPoints.insert(0); // First basic block - - for (auto curr = _bytecode.begin(); curr != _bytecode.end(); ++curr) + /// Helper function that skips push data and finds next iterator (can be the end) + auto skipPushDataAndGetNext = [](bytes::const_iterator _curr, bytes::const_iterator _end) { - ProgramCounter currentPC = curr - _bytecode.begin(); - - auto inst = Instruction(*curr); - switch (inst) - { - - case Instruction::ANY_PUSH: - { - readPushData(curr, _bytecode.end()); - break; - } + static const auto push1 = static_cast(Instruction::PUSH1); + static const auto push32 = static_cast(Instruction::PUSH32); + size_t offset = 1; + if (*_curr >= push1 && *_curr <= push32) + offset += std::min(*_curr - push1 + 1, (_end - _curr) - 1); + return _curr + offset; + }; + + auto begin = _bytecode.begin(); + bool nextJumpDest = false; + for (auto curr = begin, next = begin; curr != _bytecode.end(); curr = next) + { + next = skipPushDataAndGetNext(curr, _bytecode.end()); - case Instruction::JUMPDEST: + bool isEnd = false; + switch (Instruction(*curr)) { - // A basic block starts here. - splitPoints.insert(currentPC); - indirectJumpTargets.push_back(currentPC); - break; - } - case Instruction::JUMP: case Instruction::JUMPI: case Instruction::RETURN: case Instruction::STOP: case Instruction::SUICIDE: - { - // Create a basic block starting at the following instruction. - if (curr + 1 < _bytecode.end()) - splitPoints.insert(currentPC + 1); + isEnd = true; + break; + + case Instruction::JUMPDEST: + nextJumpDest = true; break; - } default: break; } - } - for (auto it = splitPoints.cbegin(); it != splitPoints.cend();) - { - auto beginInstIdx = *it; - ++it; - auto endInstIdx = it != splitPoints.cend() ? *it : _bytecode.size(); - basicBlocks.emplace(std::piecewise_construct, std::forward_as_tuple(beginInstIdx), std::forward_as_tuple(beginInstIdx, endInstIdx, m_mainFunc, m_builder)); + assert(next <= _bytecode.end()); + if (next == _bytecode.end() || Instruction(*next) == Instruction::JUMPDEST) + isEnd = true; + + if (isEnd) + { + auto beginIdx = begin - _bytecode.begin(); + m_basicBlocks.emplace(std::piecewise_construct, std::forward_as_tuple(beginIdx), + std::forward_as_tuple(begin, next, m_mainFunc, m_builder, nextJumpDest)); + nextJumpDest = false; + begin = next; + } } m_stopBB = llvm::BasicBlock::Create(m_mainFunc->getContext(), "Stop", m_mainFunc); - - for (auto it = indirectJumpTargets.cbegin(); it != indirectJumpTargets.cend(); ++it) - basicBlocks.find(*it)->second.markAsJumpDest(); } llvm::BasicBlock* Compiler::getJumpTableBlock() { if (!m_jumpTableBlock) { - m_jumpTableBlock.reset(new BasicBlock("JumpTable", m_mainFunc, m_builder)); + m_jumpTableBlock.reset(new BasicBlock("JumpTable", m_mainFunc, m_builder, true)); InsertPointGuard g{m_builder}; m_builder.SetInsertPoint(m_jumpTableBlock->llvm()); auto dest = m_jumpTableBlock->localStack().pop(); auto switchInstr = m_builder.CreateSwitch(dest, getBadJumpBlock()); - for (auto&& p : basicBlocks) + for (auto&& p : m_basicBlocks) { if (p.second.isJumpDest()) switchInstr->addCase(Constant::get(p.first), p.second.llvm()); @@ -123,7 +115,7 @@ llvm::BasicBlock* Compiler::getBadJumpBlock() { if (!m_badJumpBlock) { - m_badJumpBlock.reset(new BasicBlock("BadJump", m_mainFunc, m_builder)); + m_badJumpBlock.reset(new BasicBlock("BadJump", m_mainFunc, m_builder, true)); InsertPointGuard g{m_builder}; m_builder.SetInsertPoint(m_badJumpBlock->llvm()); m_builder.CreateRet(Constant::get(ReturnCode::BadJumpDestination)); @@ -155,14 +147,14 @@ std::unique_ptr Compiler::compile(bytes const& _bytecode, std::str Stack stack(m_builder, runtimeManager); Arith256 arith(m_builder); - m_builder.CreateBr(basicBlocks.begin()->second); + m_builder.CreateBr(m_basicBlocks.empty() ? m_stopBB : m_basicBlocks.begin()->second); - for (auto basicBlockPairIt = basicBlocks.begin(); basicBlockPairIt != basicBlocks.end(); ++basicBlockPairIt) + for (auto basicBlockPairIt = m_basicBlocks.begin(); basicBlockPairIt != m_basicBlocks.end(); ++basicBlockPairIt) { auto& basicBlock = basicBlockPairIt->second; auto iterCopy = basicBlockPairIt; ++iterCopy; - auto nextBasicBlock = (iterCopy != basicBlocks.end()) ? iterCopy->second.llvm() : nullptr; + auto nextBasicBlock = (iterCopy != m_basicBlocks.end()) ? iterCopy->second.llvm() : nullptr; compileBasicBlock(basicBlock, _bytecode, runtimeManager, arith, memory, ext, gasMeter, nextBasicBlock); } @@ -178,7 +170,7 @@ std::unique_ptr Compiler::compile(bytes const& _bytecode, std::str if (m_options.optimizeStack) { std::vector blockList; - for (auto& entry : basicBlocks) + for (auto& entry : m_basicBlocks) blockList.push_back(&entry.second); if (m_jumpTableBlock) @@ -189,7 +181,7 @@ std::unique_ptr Compiler::compile(bytes const& _bytecode, std::str dumpCFGifRequired("blocks-opt.dot"); } - for (auto& entry : basicBlocks) + for (auto& entry : m_basicBlocks) entry.second.synchronizeLocalStack(stack); if (m_jumpTableBlock) m_jumpTableBlock->synchronizeLocalStack(stack); @@ -219,9 +211,9 @@ void Compiler::compileBasicBlock(BasicBlock& _basicBlock, bytes const& _bytecode m_builder.SetInsertPoint(_basicBlock.llvm()); auto& stack = _basicBlock.localStack(); - for (auto currentPC = _basicBlock.begin(); currentPC != _basicBlock.end(); ++currentPC) + for (auto it = _basicBlock.begin(); it != _basicBlock.end(); ++it) { - auto inst = static_cast(_bytecode[currentPC]); + auto inst = Instruction(*it); _gasMeter.count(inst); @@ -483,10 +475,7 @@ void Compiler::compileBasicBlock(BasicBlock& _basicBlock, bytes const& _bytecode case Instruction::ANY_PUSH: { - auto curr = _bytecode.begin() + currentPC; // TODO: replace currentPC with iterator - auto value = readPushData(curr, _bytecode.end()); - currentPC = curr - _bytecode.begin(); - + auto value = readPushData(it, _basicBlock.end()); stack.push(Constant::get(value)); break; } @@ -561,24 +550,16 @@ void Compiler::compileBasicBlock(BasicBlock& _basicBlock, bytes const& _bytecode if (auto constant = llvm::dyn_cast(target)) { auto&& c = constant->getValue(); - if (c.ult(_bytecode.size())) - { - auto v = c.getZExtValue(); - auto it = basicBlocks.find(v); - if (it != basicBlocks.end() && it->second.isJumpDest()) - targetBlock = it->second.llvm(); - } - - if (!targetBlock) - targetBlock = getBadJumpBlock(); + 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(); } + // TODO: Improve; check for constants if (inst == Instruction::JUMP) { if (targetBlock) { - // The target address is computed at compile time, - // just pop it without looking... m_builder.CreateBr(targetBlock); } else @@ -614,7 +595,7 @@ void Compiler::compileBasicBlock(BasicBlock& _basicBlock, bytes const& _bytecode case Instruction::PC: { - auto value = Constant::get(currentPC); + auto value = Constant::get(it - _bytecode.begin()); stack.push(value); break; } @@ -832,13 +813,13 @@ void Compiler::removeDeadBlocks() do { sthErased = false; - for (auto it = basicBlocks.begin(); it != basicBlocks.end();) + 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(); - basicBlocks.erase(it++); + m_basicBlocks.erase(it++); sthErased = true; } else @@ -866,7 +847,7 @@ void Compiler::dumpCFGtoStream(std::ostream& _out) << " entry [share=record, label=\"entry block\"];\n"; std::vector blocks; - for (auto& pair : basicBlocks) + for (auto& pair : m_basicBlocks) blocks.push_back(&pair.second); if (m_jumpTableBlock) blocks.push_back(m_jumpTableBlock.get()); @@ -905,7 +886,7 @@ void Compiler::dumpCFGtoStream(std::ostream& _out) void Compiler::dump() { - for (auto& entry : basicBlocks) + 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 1c00dc711..640acfa05 100644 --- a/evmjit/libevmjit/Compiler.h +++ b/evmjit/libevmjit/Compiler.h @@ -69,7 +69,7 @@ private: llvm::IRBuilder<> m_builder; /// Maps a program counter pc to a basic block that starts at pc (if any). - std::map basicBlocks; + std::map m_basicBlocks; /// Stop basic block - terminates execution with STOP code (0) llvm::BasicBlock* m_stopBB = nullptr; diff --git a/evmjit/libevmjit/ExecutionEngine.cpp b/evmjit/libevmjit/ExecutionEngine.cpp index 499312b38..7e99932c3 100644 --- a/evmjit/libevmjit/ExecutionEngine.cpp +++ b/evmjit/libevmjit/ExecutionEngine.cpp @@ -74,7 +74,13 @@ ReturnCode ExecutionEngine::run(bytes const& _code, RuntimeData* _data, Env* _en } else { - auto module = Compiler({}).compile(_code, mainFuncName); + bool objectCacheEnabled = true; + auto objectCache = objectCacheEnabled ? Cache::getObjectCache() : nullptr; + std::unique_ptr module; + if (objectCache) + module = Cache::getObject(mainFuncName); + if (!module) + module = Compiler({}).compile(_code, mainFuncName); //module->dump(); if (!ee) { @@ -100,7 +106,8 @@ ReturnCode ExecutionEngine::run(bytes const& _code, RuntimeData* _data, Env* _en module.release(); // Successfully created llvm::ExecutionEngine takes ownership of the module memoryManager.release(); // and memory manager - ee->setObjectCache(Cache::getObjectCache()); + if (objectCache) + ee->setObjectCache(objectCache); entryFuncPtr = (EntryFuncPtr)ee->getFunctionAddress(mainFuncName); } else diff --git a/evmjit/libevmjit/Ext.cpp b/evmjit/libevmjit/Ext.cpp index c30b0a1e3..cd741ad24 100644 --- a/evmjit/libevmjit/Ext.cpp +++ b/evmjit/libevmjit/Ext.cpp @@ -22,7 +22,9 @@ namespace jit Ext::Ext(RuntimeManager& _runtimeManager, Memory& _memoryMan): RuntimeHelper(_runtimeManager), - m_memoryMan(_memoryMan) + m_memoryMan(_memoryMan), + m_funcs{}, + m_argAllocas{} { m_size = m_builder.CreateAlloca(Type::Size, nullptr, "env.size"); } diff --git a/evmjit/libevmjit/Ext.h b/evmjit/libevmjit/Ext.h index e6f1e4667..86a8a6190 100644 --- a/evmjit/libevmjit/Ext.h +++ b/evmjit/libevmjit/Ext.h @@ -65,8 +65,8 @@ private: llvm::Value* m_size; llvm::Value* m_data = nullptr; - std::array::value> m_funcs = {{}}; - std::array m_argAllocas = {{}}; + std::array::value> m_funcs; + std::array m_argAllocas; size_t m_argCounter = 0; llvm::CallInst* createCall(EnvFunc _funcId, std::initializer_list const& _args); diff --git a/evmjit/libevmjit/GasMeter.cpp b/evmjit/libevmjit/GasMeter.cpp index 39f4560e5..84266111e 100644 --- a/evmjit/libevmjit/GasMeter.cpp +++ b/evmjit/libevmjit/GasMeter.cpp @@ -41,7 +41,7 @@ uint64_t const c_logDataGas = 1; uint64_t const c_logTopicGas = 32; uint64_t const c_copyGas = 1; -uint64_t getStepCost(Instruction inst) // TODO: Add this function to FeeSructure (pull request submitted) +uint64_t getStepCost(Instruction inst) { switch (inst) { diff --git a/evmjit/libevmjit/Instruction.cpp b/evmjit/libevmjit/Instruction.cpp new file mode 100644 index 000000000..fdc40d043 --- /dev/null +++ b/evmjit/libevmjit/Instruction.cpp @@ -0,0 +1,40 @@ + +#include "Instruction.h" +#include + +namespace dev +{ +namespace eth +{ +namespace jit +{ + +llvm::APInt readPushData(bytes::const_iterator& _curr, bytes::const_iterator _end) +{ + auto pushInst = *_curr; + assert(Instruction(pushInst) >= Instruction::PUSH1 && Instruction(pushInst) <= Instruction::PUSH32); + auto numBytes = pushInst - static_cast(Instruction::PUSH1) + 1; + llvm::APInt value(256, 0); + ++_curr; // Point the data + for (decltype(numBytes) i = 0; i < numBytes; ++i) + { + byte b = (_curr != _end) ? *_curr++ : 0; + value <<= 8; + value |= b; + } + --_curr; // Point the last real byte read + return value; +} + +void skipPushData(bytes::const_iterator& _curr, bytes::const_iterator _end) +{ + auto pushInst = *_curr; + assert(Instruction(pushInst) >= Instruction::PUSH1 && Instruction(pushInst) <= Instruction::PUSH32); + auto numBytes = pushInst - static_cast(Instruction::PUSH1) + 1; + --_end; + for (decltype(numBytes) i = 0; i < numBytes && _curr < _end; ++i, ++_curr) {} +} + +} +} +} diff --git a/evmjit/libevmjit/Instruction.h b/evmjit/libevmjit/Instruction.h index 3f84efa08..158490dee 100644 --- a/evmjit/libevmjit/Instruction.h +++ b/evmjit/libevmjit/Instruction.h @@ -160,9 +160,13 @@ enum class Instruction: uint8_t /// Reads PUSH data from pointed fragment of bytecode and constructs number out of it /// Reading out of bytecode means reading 0 -/// @param _curr is updates and points the last real byte read +/// @param _curr is updated and points the last real byte read llvm::APInt readPushData(bytes::const_iterator& _curr, bytes::const_iterator _end); +/// Skips PUSH data in pointed fragment of bytecode. +/// @param _curr is updated and points the last real byte skipped +void skipPushData(bytes::const_iterator& _curr, bytes::const_iterator _end); + #define ANY_PUSH PUSH1: \ case Instruction::PUSH2: \ case Instruction::PUSH3: \ diff --git a/evmjit/libevmjit/Utils.cpp b/evmjit/libevmjit/Utils.cpp index 9d9b3acbb..0fd9c0e41 100644 --- a/evmjit/libevmjit/Utils.cpp +++ b/evmjit/libevmjit/Utils.cpp @@ -1,7 +1,5 @@ -#include #include "Utils.h" -#include "Instruction.h" namespace dev { @@ -37,23 +35,6 @@ i256 eth2llvm(u256 _u) return i; } -llvm::APInt readPushData(bytes::const_iterator& _curr, bytes::const_iterator _end) -{ - auto pushInst = *_curr; - assert(Instruction(pushInst) >= Instruction::PUSH1 && Instruction(pushInst) <= Instruction::PUSH32); - auto numBytes = pushInst - static_cast(Instruction::PUSH1) + 1; - llvm::APInt value(256, 0); - ++_curr; // Point the data - for (decltype(numBytes) i = 0; i < numBytes; ++i) - { - byte b = (_curr != _end) ? *_curr++ : 0; - value <<= 8; - value |= b; - } - --_curr; // Point the last real byte read - return value; -} - } } } From 9c32e2592ca57575a3bce0ae5e85d5871c862f30 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Pawe=C5=82=20Bylica?= Date: Fri, 9 Jan 2015 17:01:48 +0100 Subject: [PATCH 7/7] EVMJIT: cleanups --- evmjit/libevmjit/BasicBlock.h | 1 - evmjit/libevmjit/Compiler.cpp | 2 +- evmjit/libevmjit/Compiler.h | 12 +++--------- 3 files changed, 4 insertions(+), 11 deletions(-) diff --git a/evmjit/libevmjit/BasicBlock.h b/evmjit/libevmjit/BasicBlock.h index 8d3c31922..7a5364a4e 100644 --- a/evmjit/libevmjit/BasicBlock.h +++ b/evmjit/libevmjit/BasicBlock.h @@ -59,7 +59,6 @@ public: BasicBlock(const BasicBlock&) = delete; void operator=(const BasicBlock&) = delete; - operator llvm::BasicBlock*() { return m_llvmBB; } // TODO: Remove it llvm::BasicBlock* llvm() { return m_llvmBB; } bytes::const_iterator begin() { return m_begin; } diff --git a/evmjit/libevmjit/Compiler.cpp b/evmjit/libevmjit/Compiler.cpp index 70d40b431..bfcb9cea1 100644 --- a/evmjit/libevmjit/Compiler.cpp +++ b/evmjit/libevmjit/Compiler.cpp @@ -147,7 +147,7 @@ std::unique_ptr Compiler::compile(bytes const& _bytecode, std::str Stack stack(m_builder, runtimeManager); Arith256 arith(m_builder); - m_builder.CreateBr(m_basicBlocks.empty() ? m_stopBB : m_basicBlocks.begin()->second); + m_builder.CreateBr(m_basicBlocks.empty() ? m_stopBB : m_basicBlocks.begin()->second.llvm()); for (auto basicBlockPairIt = m_basicBlocks.begin(); basicBlockPairIt != m_basicBlocks.end(); ++basicBlockPairIt) { diff --git a/evmjit/libevmjit/Compiler.h b/evmjit/libevmjit/Compiler.h index 640acfa05..720a48cf9 100644 --- a/evmjit/libevmjit/Compiler.h +++ b/evmjit/libevmjit/Compiler.h @@ -20,19 +20,13 @@ public: struct Options { /// Optimize stack operations between basic blocks - bool optimizeStack; + bool optimizeStack = true; /// Rewrite switch instructions to sequences of branches - bool rewriteSwitchToBranches; + bool rewriteSwitchToBranches = true; /// Dump CFG as a .dot file for graphviz - bool dumpCFG; - - Options(): - optimizeStack(true), - rewriteSwitchToBranches(true), - dumpCFG(false) - {} + bool dumpCFG = false; }; using ProgramCounter = uint64_t;