From 1f40799620b80fae31b3b93d38db08e878ee326a Mon Sep 17 00:00:00 2001 From: Gav Wood Date: Thu, 8 Jan 2015 12:21:34 +0100 Subject: [PATCH] Squashed 'evmjit/' changes from 533531b..035c376 035c376 All jump support code paths tested. Thanks @CJentzsch. 5b4e130 Function rename 30f3e0d Fix evmcc 7b9d495 BLOCKHASH instruction 681adc1 Remove PREVHASH instruction 024639b Create bad jump block on demand c21237f Remove unnecessary jump dest map. Create jump table block on demand. ed614c5 Mandatory JUMPDEST for jumps and new static jumps recognition strategy 86553cd Remove terminate() function 5db00ad New CALL/CREATE depth limit semantics e5d0fb3 Alloc stack elemnent for external function call argument on demand 4f4fc63 Alloc stack elemnent for external function call argument on demand b6248cc Detemplatify createCall helper b77a975 Create helper/external functions on demand 9bf0b75 Create helper/external functions on demand git-subtree-dir: evmjit git-subtree-split: 035c3760e0482bf882800c7b18c87a1cd9cc9648 --- evmcc/evmcc.cpp | 1 - libevmjit-cpp/Env.cpp | 20 ++--- libevmjit-cpp/JitVM.cpp | 1 - libevmjit/BasicBlock.h | 7 ++ libevmjit/Common.h | 2 + libevmjit/Compiler.cpp | 152 ++++++++++++++--------------------- libevmjit/Compiler.h | 18 ++--- libevmjit/CompilerHelper.cpp | 5 ++ libevmjit/CompilerHelper.h | 7 +- libevmjit/Ext.cpp | 151 +++++++++++++++++++--------------- libevmjit/Ext.h | 50 +++++++----- libevmjit/GasMeter.cpp | 4 +- libevmjit/Instruction.h | 2 +- libevmjit/Memory.cpp | 8 +- libevmjit/Runtime.cpp | 23 +----- libevmjit/Runtime.h | 3 - libevmjit/RuntimeData.h | 1 - libevmjit/RuntimeManager.cpp | 2 - libevmjit/Utils.cpp | 9 +-- libevmjit/Utils.h | 2 - 20 files changed, 217 insertions(+), 251 deletions(-) diff --git a/evmcc/evmcc.cpp b/evmcc/evmcc.cpp index 3c43ab78b..e86f948f2 100644 --- a/evmcc/evmcc.cpp +++ b/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/libevmjit-cpp/Env.cpp b/libevmjit-cpp/Env.cpp index 1dcd38162..6320aa222 100644 --- a/libevmjit-cpp/Env.cpp +++ b/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/libevmjit-cpp/JitVM.cpp b/libevmjit-cpp/JitVM.cpp index 815aa6332..dda8133a8 100644 --- a/libevmjit-cpp/JitVM.cpp +++ b/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/libevmjit/BasicBlock.h b/libevmjit/BasicBlock.h index f0643f342..4a789a8ec 100644 --- a/libevmjit/BasicBlock.h +++ b/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/libevmjit/Common.h b/libevmjit/Common.h index d98cc0acb..436931dcd 100644 --- a/libevmjit/Common.h +++ b/libevmjit/Common.h @@ -44,6 +44,8 @@ struct i256 }; static_assert(sizeof(i256) == 32, "Wrong i265 size"); +#define UNTESTED assert(false) + } } } diff --git a/libevmjit/Compiler.cpp b/libevmjit/Compiler.cpp index 48dc50d60..abd725c7f 100644 --- a/libevmjit/Compiler.cpp +++ b/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/libevmjit/Compiler.h b/libevmjit/Compiler.h index 8e3bf357c..1c00dc711 100644 --- a/libevmjit/Compiler.h +++ b/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/libevmjit/CompilerHelper.cpp b/libevmjit/CompilerHelper.cpp index badf9d889..9cccecd79 100644 --- a/libevmjit/CompilerHelper.cpp +++ b/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/libevmjit/CompilerHelper.h b/libevmjit/CompilerHelper.h index 19315fe4a..62733ca72 100644 --- a/libevmjit/CompilerHelper.h +++ b/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/libevmjit/Ext.cpp b/libevmjit/Ext.cpp index 38adffd3f..c30b0a1e3 100644 --- a/libevmjit/Ext.cpp +++ b/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/libevmjit/Ext.h b/libevmjit/Ext.h index be71dc1ff..2850be072 100644 --- a/libevmjit/Ext.h +++ b/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/libevmjit/GasMeter.cpp b/libevmjit/GasMeter.cpp index c31942a45..39f4560e5 100644 --- a/libevmjit/GasMeter.cpp +++ b/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/libevmjit/Instruction.h b/libevmjit/Instruction.h index 502c4b66e..3f84efa08 100644 --- a/libevmjit/Instruction.h +++ b/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/libevmjit/Memory.cpp b/libevmjit/Memory.cpp index c60a5e554..9f57c7a4b 100644 --- a/libevmjit/Memory.cpp +++ b/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/libevmjit/Runtime.cpp b/libevmjit/Runtime.cpp index 27c81ea86..2522e8ace 100644 --- a/libevmjit/Runtime.cpp +++ b/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/libevmjit/Runtime.h b/libevmjit/Runtime.h index e11dac319..cedfaaf70 100644 --- a/libevmjit/Runtime.h +++ b/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/libevmjit/RuntimeData.h b/libevmjit/RuntimeData.h index 89987bdeb..bb52f7864 100644 --- a/libevmjit/RuntimeData.h +++ b/libevmjit/RuntimeData.h @@ -21,7 +21,6 @@ struct RuntimeData CallValue, CallDataSize, GasPrice, - PrevHash, CoinBase, TimeStamp, Number, diff --git a/libevmjit/RuntimeManager.cpp b/libevmjit/RuntimeManager.cpp index 14280f80f..ea2fe20b5 100644 --- a/libevmjit/RuntimeManager.cpp +++ b/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/libevmjit/Utils.cpp b/libevmjit/Utils.cpp index f1ffbf67f..9d9b3acbb 100644 --- a/libevmjit/Utils.cpp +++ b/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/libevmjit/Utils.h b/libevmjit/Utils.h index db0647fdf..f672365c6 100644 --- a/libevmjit/Utils.h +++ b/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); - } } }