diff --git a/evmjit/libevmjit-cpp/JitVM.cpp b/evmjit/libevmjit-cpp/JitVM.cpp index e3bb09099..f92b114df 100644 --- a/evmjit/libevmjit-cpp/JitVM.cpp +++ b/evmjit/libevmjit-cpp/JitVM.cpp @@ -47,7 +47,7 @@ bytesConstRef JitVM::go(ExtVMFace& _ext, OnOpFunc const& _onOp, uint64_t _step) m_data.codeSize = _ext.code.size(); auto env = reinterpret_cast(&_ext); - auto exitCode = m_engine.run(_ext.code, &m_data, env); + auto exitCode = m_engine.run(&m_data, env); switch (exitCode) { case ReturnCode::Suicide: diff --git a/evmjit/libevmjit/BasicBlock.cpp b/evmjit/libevmjit/BasicBlock.cpp index 5868c0f43..c3668c61e 100644 --- a/evmjit/libevmjit/BasicBlock.cpp +++ b/evmjit/libevmjit/BasicBlock.cpp @@ -20,7 +20,7 @@ namespace jit const char* BasicBlock::NamePrefix = "Instr."; -BasicBlock::BasicBlock(bytes::const_iterator _begin, bytes::const_iterator _end, llvm::Function* _mainFunc, llvm::IRBuilder<>& _builder, bool isJumpDest) : +BasicBlock::BasicBlock(code_iterator _begin, code_iterator _end, llvm::Function* _mainFunc, llvm::IRBuilder<>& _builder, bool isJumpDest) : m_begin(_begin), m_end(_end), // TODO: Add begin index to name diff --git a/evmjit/libevmjit/BasicBlock.h b/evmjit/libevmjit/BasicBlock.h index cda4f89bd..26d6914d2 100644 --- a/evmjit/libevmjit/BasicBlock.h +++ b/evmjit/libevmjit/BasicBlock.h @@ -53,7 +53,7 @@ public: /// Basic block name prefix. The rest is instruction index. static const char* NamePrefix; - explicit BasicBlock(bytes::const_iterator _begin, bytes::const_iterator _end, llvm::Function* _mainFunc, llvm::IRBuilder<>& _builder, bool isJumpDest); + explicit BasicBlock(code_iterator _begin, code_iterator _end, llvm::Function* _mainFunc, llvm::IRBuilder<>& _builder, bool isJumpDest); explicit BasicBlock(std::string _name, llvm::Function* _mainFunc, llvm::IRBuilder<>& _builder, bool isJumpDest); BasicBlock(const BasicBlock&) = delete; @@ -61,8 +61,8 @@ public: llvm::BasicBlock* llvm() { return m_llvmBB; } - bytes::const_iterator begin() { return m_begin; } - bytes::const_iterator end() { return m_end; } + code_iterator begin() { return m_begin; } + code_iterator end() { return m_end; } bool isJumpDest() const { return m_isJumpDest; } @@ -84,8 +84,8 @@ public: void dump(std::ostream& os, bool _dotOutput = false); private: - bytes::const_iterator const m_begin; - bytes::const_iterator const m_end; + code_iterator const m_begin = {}; + code_iterator const m_end = {}; llvm::BasicBlock* const m_llvmBB; diff --git a/evmjit/libevmjit/Common.h b/evmjit/libevmjit/Common.h index e485148fb..9c9dfd8a2 100644 --- a/evmjit/libevmjit/Common.h +++ b/evmjit/libevmjit/Common.h @@ -20,6 +20,7 @@ namespace jit using byte = uint8_t; using bytes = std::vector; using bytes_ref = std::tuple; +using code_iterator = byte const*; struct NoteChannel {}; // FIXME: Use some log library? diff --git a/evmjit/libevmjit/Compiler.cpp b/evmjit/libevmjit/Compiler.cpp index 1ce63d8ec..b6c3f10c0 100644 --- a/evmjit/libevmjit/Compiler.cpp +++ b/evmjit/libevmjit/Compiler.cpp @@ -39,10 +39,10 @@ Compiler::Compiler(Options const& _options): Type::init(m_builder.getContext()); } -void Compiler::createBasicBlocks(bytes const& _bytecode) +void Compiler::createBasicBlocks(code_iterator _codeBegin, code_iterator _codeEnd) { /// Helper function that skips push data and finds next iterator (can be the end) - auto skipPushDataAndGetNext = [](bytes::const_iterator _curr, bytes::const_iterator _end) + auto skipPushDataAndGetNext = [](code_iterator _curr, code_iterator _end) { static const auto push1 = static_cast(Instruction::PUSH1); static const auto push32 = static_cast(Instruction::PUSH32); @@ -52,11 +52,11 @@ void Compiler::createBasicBlocks(bytes const& _bytecode) return _curr + offset; }; - auto begin = _bytecode.begin(); + auto begin = _codeBegin; // begin of current block bool nextJumpDest = false; - for (auto curr = begin, next = begin; curr != _bytecode.end(); curr = next) + for (auto curr = begin, next = begin; curr != _codeEnd; curr = next) { - next = skipPushDataAndGetNext(curr, _bytecode.end()); + next = skipPushDataAndGetNext(curr, _codeEnd); bool isEnd = false; switch (Instruction(*curr)) @@ -77,13 +77,13 @@ void Compiler::createBasicBlocks(bytes const& _bytecode) break; } - assert(next <= _bytecode.end()); - if (next == _bytecode.end() || Instruction(*next) == Instruction::JUMPDEST) + assert(next <= _codeEnd); + if (next == _codeEnd || Instruction(*next) == Instruction::JUMPDEST) isEnd = true; if (isEnd) { - auto beginIdx = begin - _bytecode.begin(); + auto beginIdx = begin - _codeBegin; m_basicBlocks.emplace(std::piecewise_construct, std::forward_as_tuple(beginIdx), std::forward_as_tuple(begin, next, m_mainFunc, m_builder, nextJumpDest)); nextJumpDest = false; @@ -124,7 +124,7 @@ llvm::BasicBlock* Compiler::getBadJumpBlock() return m_badJumpBlock->llvm(); } -std::unique_ptr Compiler::compile(bytes const& _bytecode, std::string const& _id) +std::unique_ptr Compiler::compile(code_iterator _begin, code_iterator _end, std::string const& _id) { auto compilationStartTime = std::chrono::high_resolution_clock::now(); auto module = std::unique_ptr(new llvm::Module(_id, m_builder.getContext())); @@ -138,7 +138,8 @@ std::unique_ptr Compiler::compile(bytes const& _bytecode, std::str auto entryBlock = llvm::BasicBlock::Create(m_builder.getContext(), "entry", m_mainFunc); m_builder.SetInsertPoint(entryBlock); - createBasicBlocks(_bytecode); + m_codeBegin = _begin; + createBasicBlocks(_begin, _end); // Init runtime structures. RuntimeManager runtimeManager(m_builder); @@ -156,7 +157,7 @@ std::unique_ptr Compiler::compile(bytes const& _bytecode, std::str auto iterCopy = basicBlockPairIt; ++iterCopy; auto nextBasicBlock = (iterCopy != m_basicBlocks.end()) ? iterCopy->second.llvm() : nullptr; - compileBasicBlock(basicBlock, _bytecode, runtimeManager, arith, memory, ext, gasMeter, nextBasicBlock); + compileBasicBlock(basicBlock, runtimeManager, arith, memory, ext, gasMeter, nextBasicBlock); } // Code for special blocks: @@ -223,7 +224,7 @@ std::unique_ptr Compiler::compile(bytes const& _bytecode, std::str } -void Compiler::compileBasicBlock(BasicBlock& _basicBlock, bytes const& _bytecode, RuntimeManager& _runtimeManager, +void Compiler::compileBasicBlock(BasicBlock& _basicBlock, RuntimeManager& _runtimeManager, Arith256& _arith, Memory& _memory, Ext& _ext, GasMeter& _gasMeter, llvm::BasicBlock* _nextBasicBlock) { if (!_nextBasicBlock) // this is the last block in the code @@ -622,7 +623,7 @@ void Compiler::compileBasicBlock(BasicBlock& _basicBlock, bytes const& _bytecode case Instruction::PC: { - auto value = Constant::get(it - _bytecode.begin()); + auto value = Constant::get(it - m_codeBegin); stack.push(value); break; } diff --git a/evmjit/libevmjit/Compiler.h b/evmjit/libevmjit/Compiler.h index 720a48cf9..3c9bb9c68 100644 --- a/evmjit/libevmjit/Compiler.h +++ b/evmjit/libevmjit/Compiler.h @@ -33,13 +33,13 @@ public: Compiler(Options const& _options); - std::unique_ptr compile(bytes const& _bytecode, std::string const& _id); + std::unique_ptr compile(code_iterator _begin, code_iterator _end, std::string const& _id); private: - void createBasicBlocks(bytes const& _bytecode); + void createBasicBlocks(code_iterator _begin, code_iterator _end); - 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); + void compileBasicBlock(BasicBlock& _basicBlock, class RuntimeManager& _runtimeManager, class Arith256& _arith, class Memory& _memory, class Ext& _ext, class GasMeter& _gasMeter, llvm::BasicBlock* _nextBasicBlock); llvm::BasicBlock* getJumpTableBlock(); @@ -76,6 +76,8 @@ private: /// Main program function llvm::Function* m_mainFunc = nullptr; + + code_iterator m_codeBegin = {}; }; } diff --git a/evmjit/libevmjit/ExecutionEngine.cpp b/evmjit/libevmjit/ExecutionEngine.cpp index ee92a6ba9..e23a625e3 100644 --- a/evmjit/libevmjit/ExecutionEngine.cpp +++ b/evmjit/libevmjit/ExecutionEngine.cpp @@ -52,15 +52,15 @@ ReturnCode runEntryFunc(EntryFuncPtr _mainFunc, Runtime* _runtime) return returnCode; } -std::string codeHash(bytes const& _code) +std::string codeHash(code_iterator _begin, code_iterator _end) { uint32_t hash = 0; - for (auto b : _code) + std::for_each(_begin, _end, [&hash](decltype(*_begin) b) { hash += b; hash += (hash << 10); hash ^= (hash >> 6); - } + }); hash += (hash << 3); hash ^= (hash >> 11); hash += (hash << 15); @@ -69,13 +69,16 @@ std::string codeHash(bytes const& _code) } -ReturnCode ExecutionEngine::run(bytes const& _code, RuntimeData* _data, Env* _env) +ReturnCode ExecutionEngine::run(RuntimeData* _data, Env* _env) { static std::unique_ptr ee; // TODO: Use Managed Objects from LLVM? static auto debugDumpModule = DEBUG_ENV_OPTION(EVMJIT_DUMP_MODULE); static bool objectCacheEnabled = !DEBUG_ENV_OPTION(EVMJIT_CACHE_OFF); - auto mainFuncName = codeHash(_code); + auto codeBegin = _data->code; + auto codeEnd = codeBegin + _data->codeSize; + assert(codeBegin || !codeEnd); //TODO: Is it good idea to execute empty code? + auto mainFuncName = codeHash(codeBegin, codeEnd); EntryFuncPtr entryFuncPtr{}; Runtime runtime(_data, _env); // TODO: I don't know why but it must be created before getFunctionAddress() calls @@ -89,7 +92,7 @@ ReturnCode ExecutionEngine::run(bytes const& _code, RuntimeData* _data, Env* _en if (objectCache) module = Cache::getObject(mainFuncName); if (!module) - module = Compiler({}).compile(_code, mainFuncName); + module = Compiler({}).compile(codeBegin, codeEnd, mainFuncName); if (debugDumpModule) module->dump(); if (!ee) diff --git a/evmjit/libevmjit/ExecutionEngine.h b/evmjit/libevmjit/ExecutionEngine.h index e8d1e1c05..c95bbfb62 100644 --- a/evmjit/libevmjit/ExecutionEngine.h +++ b/evmjit/libevmjit/ExecutionEngine.h @@ -16,7 +16,7 @@ public: ExecutionEngine(ExecutionEngine const&) = delete; void operator=(ExecutionEngine) = delete; - EXPORT ReturnCode run(bytes const& _code, RuntimeData* _data, Env* _env); + EXPORT ReturnCode run(RuntimeData* _data, Env* _env); /// Reference to returned data (RETURN opcode used) bytes_ref returnData; diff --git a/evmjit/libevmjit/Instruction.cpp b/evmjit/libevmjit/Instruction.cpp index fdc40d043..909121607 100644 --- a/evmjit/libevmjit/Instruction.cpp +++ b/evmjit/libevmjit/Instruction.cpp @@ -9,7 +9,7 @@ namespace eth namespace jit { -llvm::APInt readPushData(bytes::const_iterator& _curr, bytes::const_iterator _end) +llvm::APInt readPushData(code_iterator& _curr, code_iterator _end) { auto pushInst = *_curr; assert(Instruction(pushInst) >= Instruction::PUSH1 && Instruction(pushInst) <= Instruction::PUSH32); @@ -26,7 +26,7 @@ llvm::APInt readPushData(bytes::const_iterator& _curr, bytes::const_iterator _en return value; } -void skipPushData(bytes::const_iterator& _curr, bytes::const_iterator _end) +void skipPushData(code_iterator& _curr, code_iterator _end) { auto pushInst = *_curr; assert(Instruction(pushInst) >= Instruction::PUSH1 && Instruction(pushInst) <= Instruction::PUSH32); diff --git a/evmjit/libevmjit/Instruction.h b/evmjit/libevmjit/Instruction.h index 158490dee..6785213d6 100644 --- a/evmjit/libevmjit/Instruction.h +++ b/evmjit/libevmjit/Instruction.h @@ -161,11 +161,11 @@ 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 updated and points the last real byte read -llvm::APInt readPushData(bytes::const_iterator& _curr, bytes::const_iterator _end); +llvm::APInt readPushData(code_iterator& _curr, code_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); +void skipPushData(code_iterator& _curr, code_iterator _end); #define ANY_PUSH PUSH1: \ case Instruction::PUSH2: \ diff --git a/evmjit/libevmjit/interface.cpp b/evmjit/libevmjit/interface.cpp index 6b0992dd4..645f3d150 100644 --- a/evmjit/libevmjit/interface.cpp +++ b/evmjit/libevmjit/interface.cpp @@ -12,6 +12,7 @@ using namespace dev::eth::jit; EXPORT void* evmjit_create() noexcept { + // TODO: Make sure ExecutionEngine constructor does not throw return new(std::nothrow) ExecutionEngine; } @@ -22,14 +23,12 @@ EXPORT void evmjit_destroy(ExecutionEngine* _engine) noexcept EXPORT int evmjit_run(ExecutionEngine* _engine, RuntimeData* _data, Env* _env) noexcept { + if (!_engine || !_data) + return static_cast(ReturnCode::UnexpectedException); + try { - auto codePtr = _data->code; - auto codeSize = _data->codeSize; - bytes bytecode; - bytecode.insert(bytecode.end(), codePtr, codePtr + codeSize); - - auto returnCode = _engine->run(bytecode, _data, _env); + auto returnCode = _engine->run(_data, _env); return static_cast(returnCode); } catch(...)