From d6f4eb1eef52ad698a4a9d51844d86fc6080a133 Mon Sep 17 00:00:00 2001 From: chriseth Date: Fri, 24 Apr 2015 17:51:25 +0200 Subject: [PATCH 01/17] Statically compile libevmjit-cpp. --- libevmjit-cpp/CMakeLists.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/libevmjit-cpp/CMakeLists.txt b/libevmjit-cpp/CMakeLists.txt index add132f2a..5b4dbb9c8 100644 --- a/libevmjit-cpp/CMakeLists.txt +++ b/libevmjit-cpp/CMakeLists.txt @@ -15,7 +15,7 @@ else() set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -fPIC") # add PIC for archive endif() -add_library(${TARGET_NAME} ${SOURCES}) +add_library(${TARGET_NAME} STATIC ${SOURCES}) set_property(TARGET ${TARGET_NAME} PROPERTY FOLDER "libs") include_directories(../..) From c3fe9ad112a69c9ae42a25914eda8315d2102338 Mon Sep 17 00:00:00 2001 From: chriseth Date: Fri, 24 Apr 2015 17:35:16 +0200 Subject: [PATCH 02/17] Move assembly related files to libevmasm and Params.h/.cpp to libevmcore. --- libevmjit-cpp/Env.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/libevmjit-cpp/Env.cpp b/libevmjit-cpp/Env.cpp index b89aca726..2c37412fc 100644 --- a/libevmjit-cpp/Env.cpp +++ b/libevmjit-cpp/Env.cpp @@ -1,7 +1,7 @@ #pragma GCC diagnostic ignored "-Wconversion" #include -#include +#include #include #include "Utils.h" From 9ca47fe0cf795271cb6e86e5df612bd4c5b3b49f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Pawe=C5=82=20Bylica?= Date: Tue, 5 May 2015 17:52:27 +0200 Subject: [PATCH 03/17] JIT class: the EVM JIT facade. The JIT class added, future public EVM JIT library interface. Currently it supports queries about EVM code status. --- CMakeLists.txt | 2 ++ include/evmjit/DataTypes.h | 56 +++++++++++++++++++++++++++++++++++ include/evmjit/JIT.h | 36 ++++++++++++++++++++++ libevmjit-cpp/CMakeLists.txt | 1 + libevmjit-cpp/Env.cpp | 3 +- libevmjit-cpp/Utils.h | 14 ++++++--- libevmjit/CMakeLists.txt | 3 ++ libevmjit/Common.h | 12 +------- libevmjit/ExecutionEngine.cpp | 24 +++++++-------- libevmjit/JIT.cpp | 46 ++++++++++++++++++++++++++++ libevmjit/RuntimeData.h | 7 +++-- 11 files changed, 172 insertions(+), 32 deletions(-) create mode 100644 include/evmjit/DataTypes.h create mode 100644 include/evmjit/JIT.h create mode 100644 libevmjit/JIT.cpp diff --git a/CMakeLists.txt b/CMakeLists.txt index 5ab394a80..a0e9c1f46 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -33,6 +33,8 @@ else() link_directories(/usr/lib/llvm-3.5/lib) endif() +get_filename_component(EVMJIT_INCLUDE_DIR include ABSOLUTE) + add_subdirectory(libevmjit) if(EVMJIT_CPP) diff --git a/include/evmjit/DataTypes.h b/include/evmjit/DataTypes.h new file mode 100644 index 000000000..179d9a372 --- /dev/null +++ b/include/evmjit/DataTypes.h @@ -0,0 +1,56 @@ +#pragma once + +#include +#include + +namespace dev +{ +namespace evmjit +{ + +struct h256 +{ + uint64_t words[4]; +}; + +inline bool operator==(h256 _h1, h256 _h2) +{ + return _h1.words[0] == _h2.words[0] && + _h1.words[1] == _h2.words[1] && + _h1.words[2] == _h2.words[2] && + _h1.words[3] == _h2.words[3]; +} + +/// Representation of 256-bit value binary compatible with LLVM i256 +struct i256 +{ + uint64_t a = 0; + uint64_t b = 0; + uint64_t c = 0; + uint64_t d = 0; + + i256() = default; + i256(h256 _h) + { + a = _h.words[0]; + b = _h.words[1]; + c = _h.words[2]; + d = _h.words[3]; + } +}; + +} +} + +namespace std +{ +template<> struct hash +{ + size_t operator()(dev::evmjit::h256 const& _h) const + { + /// This implementation expects the argument to be a full 256-bit Keccak hash. + /// It does nothing more than returning a slice of the input hash. + return static_cast(_h.words[0]); + }; +}; +} diff --git a/include/evmjit/JIT.h b/include/evmjit/JIT.h new file mode 100644 index 000000000..446dd9e56 --- /dev/null +++ b/include/evmjit/JIT.h @@ -0,0 +1,36 @@ +#pragma once + +#include "evmjit/DataTypes.h" + +namespace dev +{ +namespace eth +{ +namespace jit +{ + class ExecutionEngine; +} +} + +namespace evmjit +{ + +class JIT +{ +public: + + /// Ask JIT if the EVM code is ready for execution. + /// Returns `true` if the EVM code has been compiled and loaded into memory. + /// In this case the code can be executed without overhead. + /// \param _codeHash The Keccak hash of the EVM code. + static bool isCodeReady(h256 _codeHash); + +private: + friend class dev::eth::jit::ExecutionEngine; + + static void* getCode(h256 _codeHash); + static void mapCode(h256 _codeHash, void* _funcAddr); +}; + +} +} diff --git a/libevmjit-cpp/CMakeLists.txt b/libevmjit-cpp/CMakeLists.txt index 5b4dbb9c8..5b2a35b00 100644 --- a/libevmjit-cpp/CMakeLists.txt +++ b/libevmjit-cpp/CMakeLists.txt @@ -19,6 +19,7 @@ add_library(${TARGET_NAME} STATIC ${SOURCES}) set_property(TARGET ${TARGET_NAME} PROPERTY FOLDER "libs") include_directories(../..) +include_directories(${EVMJIT_INCLUDE_DIR}) include_directories(${LLVM_INCLUDE_DIRS}) include_directories(${Boost_INCLUDE_DIRS}) diff --git a/libevmjit-cpp/Env.cpp b/libevmjit-cpp/Env.cpp index 2c37412fc..a5a60f48c 100644 --- a/libevmjit-cpp/Env.cpp +++ b/libevmjit-cpp/Env.cpp @@ -3,6 +3,7 @@ #include #include #include +#include #include "Utils.h" @@ -16,7 +17,7 @@ extern "C" using namespace dev; using namespace dev::eth; - using jit::i256; + using evmjit::i256; EXPORT void env_sload(ExtVMFace* _env, i256* _index, i256* o_value) { diff --git a/libevmjit-cpp/Utils.h b/libevmjit-cpp/Utils.h index ac796920b..f9b9b2ef4 100644 --- a/libevmjit-cpp/Utils.h +++ b/libevmjit-cpp/Utils.h @@ -1,13 +1,13 @@ #pragma once -#include +#include namespace dev { namespace eth { -inline u256 llvm2eth(jit::i256 _i) +inline u256 llvm2eth(evmjit::i256 _i) { u256 u = 0; u |= _i.d; @@ -20,9 +20,9 @@ inline u256 llvm2eth(jit::i256 _i) return u; } -inline jit::i256 eth2llvm(u256 _u) +inline evmjit::i256 eth2llvm(u256 _u) { - jit::i256 i; + evmjit::i256 i; u256 mask = 0xFFFFFFFFFFFFFFFF; i.a = static_cast(_u & mask); _u >>= 64; @@ -34,5 +34,11 @@ inline jit::i256 eth2llvm(u256 _u) return i; } +inline evmjit::h256 eth2llvm(h256 _u) +{ + /// Just directly copies memory + return *(evmjit::h256*)&_u; +} + } } diff --git a/libevmjit/CMakeLists.txt b/libevmjit/CMakeLists.txt index 7f4e763d7..4b15d52ac 100644 --- a/libevmjit/CMakeLists.txt +++ b/libevmjit/CMakeLists.txt @@ -8,6 +8,7 @@ set(SOURCES Common.h Compiler.cpp Compiler.h CompilerHelper.cpp CompilerHelper.h + ${EVMJIT_INCLUDE_DIR}/evmjit/DataTypes.h Endianness.cpp Endianness.h ExecStats.cpp ExecStats.h ExecutionEngine.cpp ExecutionEngine.h @@ -15,6 +16,7 @@ set(SOURCES GasMeter.cpp GasMeter.h Instruction.cpp Instruction.h interface.cpp interface.h + JIT.cpp ${EVMJIT_INCLUDE_DIR}/evmjit/JIT.h Memory.cpp Memory.h Optimizer.cpp Optimizer.h Runtime.cpp Runtime.h @@ -79,6 +81,7 @@ set_target_properties(${TARGET_NAME} PROPERTIES VERSION ${EVMJIT_VERSION} SOVERSION ${EVMJIT_SOVERSION} FOLDER "libs") +include_directories(${EVMJIT_INCLUDE_DIR}) include_directories(${LLVM_INCLUDE_DIRS}) include_directories(${CMAKE_CURRENT_BINARY_DIR}/gen) diff --git a/libevmjit/Common.h b/libevmjit/Common.h index 028f0b3c5..b519614fe 100644 --- a/libevmjit/Common.h +++ b/libevmjit/Common.h @@ -31,7 +31,7 @@ enum class ReturnCode // Standard error codes OutOfGas = -1, - StackUnderflow = -2, + StackUnderflow = -2, BadJumpDestination = -3, BadInstruction = -4, Rejected = -5, ///< Input data (code, gas, block info, etc.) does not meet JIT requirement and execution request has been rejected @@ -46,16 +46,6 @@ enum class ReturnCode LinkerWorkaround = -299, }; -/// Representation of 256-bit value binary compatible with LLVM i256 -struct i256 -{ - uint64_t a = 0; - uint64_t b = 0; - uint64_t c = 0; - uint64_t d = 0; -}; -static_assert(sizeof(i256) == 32, "Wrong i265 size"); - #define UNTESTED assert(false) } diff --git a/libevmjit/ExecutionEngine.cpp b/libevmjit/ExecutionEngine.cpp index e15dad969..08ca403b5 100644 --- a/libevmjit/ExecutionEngine.cpp +++ b/libevmjit/ExecutionEngine.cpp @@ -19,6 +19,7 @@ #include #include "preprocessor/llvm_includes_end.h" +#include "evmjit/JIT.h" #include "Runtime.h" #include "Compiler.h" #include "Optimizer.h" @@ -33,6 +34,7 @@ namespace eth { namespace jit { +using evmjit::JIT; namespace { @@ -119,8 +121,6 @@ ReturnCode ExecutionEngine::run(RuntimeData* _data, Env* _env) // TODO: Do not pseudo-init the cache every time auto objectCache = (g_cache != CacheMode::off && g_cache != CacheMode::clear) ? Cache::getObjectCache(g_cache, listener.get()) : nullptr; - static std::unordered_map funcCache; - static std::unique_ptr ee; if (!ee) { @@ -147,8 +147,9 @@ ReturnCode ExecutionEngine::run(RuntimeData* _data, Env* _env) module.release(); // Successfully created llvm::ExecutionEngine takes ownership of the module ee->setObjectCache(objectCache); - if (preloadCache) - Cache::preload(*ee, funcCache); + // FIXME: Disabled during API changes + //if (preloadCache) + // Cache::preload(*ee, funcCache); } static StatsCollector statsCollector; @@ -156,11 +157,8 @@ ReturnCode ExecutionEngine::run(RuntimeData* _data, Env* _env) auto mainFuncName = codeHash(_data->codeHash); m_runtime.init(_data, _env); - EntryFuncPtr entryFuncPtr = nullptr; - auto it = funcCache.find(mainFuncName); - if (it != funcCache.end()) - entryFuncPtr = (EntryFuncPtr) it->second; - + // TODO: Remove cast + auto entryFuncPtr = (EntryFuncPtr) JIT::getCode(_data->codeHash); if (!entryFuncPtr) { auto module = objectCache ? Cache::getObject(mainFuncName) : nullptr; @@ -183,12 +181,10 @@ ReturnCode ExecutionEngine::run(RuntimeData* _data, Env* _env) module.release(); listener->stateChanged(ExecState::CodeGen); entryFuncPtr = (EntryFuncPtr)ee->getFunctionAddress(mainFuncName); + if (!CHECK(entryFuncPtr)) + return ReturnCode::LLVMLinkError; + JIT::mapCode(_data->codeHash, (void*)entryFuncPtr); // FIXME: Remove cast } - if (!CHECK(entryFuncPtr)) - return ReturnCode::LLVMLinkError; - - if (it == funcCache.end()) - funcCache[mainFuncName] = (uint64_t) entryFuncPtr; listener->stateChanged(ExecState::Execution); auto returnCode = entryFuncPtr(&m_runtime); diff --git a/libevmjit/JIT.cpp b/libevmjit/JIT.cpp new file mode 100644 index 000000000..9774c7396 --- /dev/null +++ b/libevmjit/JIT.cpp @@ -0,0 +1,46 @@ +#include "evmjit/JIT.h" + +#include + +namespace dev +{ +namespace evmjit +{ +namespace +{ + +class JITImpl: JIT +{ +public: + std::unordered_map codeMap; + + static JITImpl& instance() + { + static JITImpl s_instance; + return s_instance; + } +}; + +} // anonymous namespace + +bool JIT::isCodeReady(h256 _codeHash) +{ + return JITImpl::instance().codeMap.count(_codeHash) != 0; +} + +void* JIT::getCode(h256 _codeHash) +{ + auto& codeMap = JITImpl::instance().codeMap; + auto it = codeMap.find(_codeHash); + if (it != codeMap.end()) + return it->second; + return nullptr; +} + +void JIT::mapCode(h256 _codeHash, void* _funcAddr) +{ + JITImpl::instance().codeMap.insert(std::make_pair(_codeHash, _funcAddr)); +} + +} +} diff --git a/libevmjit/RuntimeData.h b/libevmjit/RuntimeData.h index cc081cc58..6a5cd0d14 100644 --- a/libevmjit/RuntimeData.h +++ b/libevmjit/RuntimeData.h @@ -1,5 +1,6 @@ #pragma once +#include "evmjit/DataTypes.h" #include "Common.h" namespace dev @@ -8,7 +9,9 @@ namespace eth { namespace jit { - +using evmjit::i256; +using evmjit::h256; + struct RuntimeData { enum Index @@ -49,7 +52,7 @@ struct RuntimeData int64_t timestamp = 0; byte const* code = nullptr; uint64_t codeSize = 0; - i256 codeHash; + h256 codeHash; }; /// VM Environment (ExtVM) opaque type From 9c97f20674e73128541902dff1a444067a9c1abb Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Pawe=C5=82=20Bylica?= Date: Wed, 6 May 2015 10:29:36 +0200 Subject: [PATCH 04/17] Allow selecting VM kind manually --- libevmjit-cpp/JitVM.cpp | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/libevmjit-cpp/JitVM.cpp b/libevmjit-cpp/JitVM.cpp index 84cc41dd1..7acbec5c1 100644 --- a/libevmjit-cpp/JitVM.cpp +++ b/libevmjit-cpp/JitVM.cpp @@ -32,9 +32,7 @@ bytesConstRef JitVM::go(ExtVMFace& _ext, OnOpFunc const& _onOp, uint64_t _step) if (rejected) { cwarn << "Execution rejected by EVM JIT (gas limit: " << m_gas << "), executing with interpreter"; - VMFactory::setKind(VMKind::Interpreter); - m_fallbackVM = VMFactory::create(m_gas); - VMFactory::setKind(VMKind::JIT); + m_fallbackVM = VMFactory::create(VMKind::Interpreter, m_gas); auto&& output = m_fallbackVM->go(_ext, _onOp, _step); m_gas = m_fallbackVM->gas(); // copy remaining gas, Executive expects it return output; From dd3f1fe2540c4905b5581d32ee7e264a1c0d1207 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Pawe=C5=82=20Bylica?= Date: Mon, 11 May 2015 14:07:27 +0200 Subject: [PATCH 05/17] Remove gas counter from VM interface (VMFace) --- libevmjit-cpp/JitVM.cpp | 16 +++++++--------- libevmjit-cpp/JitVM.h | 3 +-- 2 files changed, 8 insertions(+), 11 deletions(-) diff --git a/libevmjit-cpp/JitVM.cpp b/libevmjit-cpp/JitVM.cpp index 84cc41dd1..717d70958 100644 --- a/libevmjit-cpp/JitVM.cpp +++ b/libevmjit-cpp/JitVM.cpp @@ -18,29 +18,27 @@ namespace eth extern "C" void env_sload(); // fake declaration for linker symbol stripping workaround, see a call below -bytesConstRef JitVM::go(ExtVMFace& _ext, OnOpFunc const& _onOp, uint64_t _step) +bytesConstRef JitVM::go(u256& io_gas, ExtVMFace& _ext, OnOpFunc const& _onOp, uint64_t _step) { using namespace jit; auto rejected = false; // TODO: Rejecting transactions with gas limit > 2^63 can be used by attacker to take JIT out of scope - rejected |= m_gas > std::numeric_limits::max(); // Do not accept requests with gas > 2^63 (int64 max) + rejected |= io_gas > std::numeric_limits::max(); // Do not accept requests with gas > 2^63 (int64 max) rejected |= _ext.gasPrice > std::numeric_limits::max(); rejected |= _ext.currentBlock.number > std::numeric_limits::max(); rejected |= _ext.currentBlock.timestamp > std::numeric_limits::max(); if (rejected) { - cwarn << "Execution rejected by EVM JIT (gas limit: " << m_gas << "), executing with interpreter"; + cwarn << "Execution rejected by EVM JIT (gas limit: " << io_gas << "), executing with interpreter"; VMFactory::setKind(VMKind::Interpreter); - m_fallbackVM = VMFactory::create(m_gas); + m_fallbackVM = VMFactory::create(); VMFactory::setKind(VMKind::JIT); - auto&& output = m_fallbackVM->go(_ext, _onOp, _step); - m_gas = m_fallbackVM->gas(); // copy remaining gas, Executive expects it - return output; + return m_fallbackVM->go(io_gas, _ext, _onOp, _step); } - m_data.gas = static_cast(m_gas); + m_data.gas = static_cast(io_gas); m_data.gasPrice = static_cast(_ext.gasPrice); m_data.callData = _ext.data.data(); m_data.callDataSize = _ext.data.size(); @@ -80,7 +78,7 @@ bytesConstRef JitVM::go(ExtVMFace& _ext, OnOpFunc const& _onOp, uint64_t _step) break; } - m_gas = m_data.gas; // TODO: Remove m_gas field + io_gas = m_data.gas; return {std::get<0>(m_engine.returnData), std::get<1>(m_engine.returnData)}; } diff --git a/libevmjit-cpp/JitVM.h b/libevmjit-cpp/JitVM.h index 58caa3648..797fe7e1c 100644 --- a/libevmjit-cpp/JitVM.h +++ b/libevmjit-cpp/JitVM.h @@ -10,11 +10,10 @@ namespace eth class JitVM: public VMFace { - virtual bytesConstRef go(ExtVMFace& _ext, OnOpFunc const& _onOp = {}, uint64_t _steps = (uint64_t)-1) override final; + virtual bytesConstRef go(u256& io_gas, ExtVMFace& _ext, OnOpFunc const& _onOp = {}, uint64_t _steps = (uint64_t)-1) override final; private: friend class VMFactory; - explicit JitVM(u256 _gas = 0) : VMFace(_gas) {} jit::RuntimeData m_data; jit::ExecutionEngine m_engine; From d92198969655a7a156f1008a6a7761f4dcd4778d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Pawe=C5=82=20Bylica?= Date: Tue, 12 May 2015 14:29:06 +0200 Subject: [PATCH 06/17] VM cleanups --- libevmjit-cpp/JitVM.h | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/libevmjit-cpp/JitVM.h b/libevmjit-cpp/JitVM.h index 797fe7e1c..e6864f885 100644 --- a/libevmjit-cpp/JitVM.h +++ b/libevmjit-cpp/JitVM.h @@ -10,11 +10,10 @@ namespace eth class JitVM: public VMFace { +public: virtual bytesConstRef go(u256& io_gas, ExtVMFace& _ext, OnOpFunc const& _onOp = {}, uint64_t _steps = (uint64_t)-1) override final; private: - friend class VMFactory; - jit::RuntimeData m_data; jit::ExecutionEngine m_engine; std::unique_ptr m_fallbackVM; ///< VM used in case of input data rejected by JIT From 3b943f850a1c09564206eaa2405175ceb2ed4e40 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Pawe=C5=82=20Bylica?= Date: Tue, 19 May 2015 10:19:14 +0200 Subject: [PATCH 07/17] Do not override CMAKE_CXX_FLAGS in evmjit. --- CMakeLists.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index a0e9c1f46..280fec212 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -7,7 +7,7 @@ set(CMAKE_AUTOMOC OFF) if(${CMAKE_CXX_COMPILER_ID} STREQUAL "MSVC") else() - set(CMAKE_CXX_FLAGS "-std=c++11 -Wall -Wextra -Wconversion -Wno-sign-conversion -Wno-unknown-pragmas") + set(CMAKE_CXX_FLAGS "-std=c++11 -Wall -Wextra -Wconversion -Wno-sign-conversion -Wno-unknown-pragmas ${CMAKE_CXX_FLAGS}") endif() if(${CMAKE_SYSTEM_NAME} STREQUAL "Linux") From a6ef3f40d1249d390f5c26e3fe2bad74594e1145 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Pawe=C5=82=20Bylica?= Date: Tue, 19 May 2015 17:44:07 +0200 Subject: [PATCH 08/17] Avoid forbidden function pointer cast. --- include/evmjit/JIT.h | 4 ++-- libevmjit/ExecutionEngine.cpp | 2 +- libevmjit/JIT.cpp | 8 ++++---- 3 files changed, 7 insertions(+), 7 deletions(-) diff --git a/include/evmjit/JIT.h b/include/evmjit/JIT.h index 446dd9e56..c9ddde705 100644 --- a/include/evmjit/JIT.h +++ b/include/evmjit/JIT.h @@ -28,8 +28,8 @@ public: private: friend class dev::eth::jit::ExecutionEngine; - static void* getCode(h256 _codeHash); - static void mapCode(h256 _codeHash, void* _funcAddr); + static uint64_t getCode(h256 _codeHash); + static void mapCode(h256 _codeHash, uint64_t _funcAddr); }; } diff --git a/libevmjit/ExecutionEngine.cpp b/libevmjit/ExecutionEngine.cpp index 08ca403b5..e5abb36b3 100644 --- a/libevmjit/ExecutionEngine.cpp +++ b/libevmjit/ExecutionEngine.cpp @@ -183,7 +183,7 @@ ReturnCode ExecutionEngine::run(RuntimeData* _data, Env* _env) entryFuncPtr = (EntryFuncPtr)ee->getFunctionAddress(mainFuncName); if (!CHECK(entryFuncPtr)) return ReturnCode::LLVMLinkError; - JIT::mapCode(_data->codeHash, (void*)entryFuncPtr); // FIXME: Remove cast + JIT::mapCode(_data->codeHash, (uint64_t)entryFuncPtr); // FIXME: Remove cast } listener->stateChanged(ExecState::Execution); diff --git a/libevmjit/JIT.cpp b/libevmjit/JIT.cpp index 9774c7396..8617fab6d 100644 --- a/libevmjit/JIT.cpp +++ b/libevmjit/JIT.cpp @@ -12,7 +12,7 @@ namespace class JITImpl: JIT { public: - std::unordered_map codeMap; + std::unordered_map codeMap; static JITImpl& instance() { @@ -28,16 +28,16 @@ bool JIT::isCodeReady(h256 _codeHash) return JITImpl::instance().codeMap.count(_codeHash) != 0; } -void* JIT::getCode(h256 _codeHash) +uint64_t JIT::getCode(h256 _codeHash) { auto& codeMap = JITImpl::instance().codeMap; auto it = codeMap.find(_codeHash); if (it != codeMap.end()) return it->second; - return nullptr; + return 0; } -void JIT::mapCode(h256 _codeHash, void* _funcAddr) +void JIT::mapCode(h256 _codeHash, uint64_t _funcAddr) { JITImpl::instance().codeMap.insert(std::make_pair(_codeHash, _funcAddr)); } From c8c2e18dc75b9f100d013d9511a2750ea920e3a0 Mon Sep 17 00:00:00 2001 From: Gav Wood Date: Tue, 19 May 2015 19:51:38 +0200 Subject: [PATCH 09/17] Move non-cryptopp dependent stuff into devcore. --- libevmjit-cpp/Env.cpp | 2 +- libevmjit-cpp/JitVM.cpp | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/libevmjit-cpp/Env.cpp b/libevmjit-cpp/Env.cpp index a5a60f48c..d1f239f9f 100644 --- a/libevmjit-cpp/Env.cpp +++ b/libevmjit-cpp/Env.cpp @@ -1,6 +1,6 @@ #pragma GCC diagnostic ignored "-Wconversion" -#include +#include #include #include #include diff --git a/libevmjit-cpp/JitVM.cpp b/libevmjit-cpp/JitVM.cpp index 7acbec5c1..6efd4c31a 100644 --- a/libevmjit-cpp/JitVM.cpp +++ b/libevmjit-cpp/JitVM.cpp @@ -4,7 +4,7 @@ #include "JitVM.h" #include -#include +#include #include #include #include From 5d4fbd4c44aa373082263c2419ebc3065eea39b9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Pawe=C5=82=20Bylica?= Date: Thu, 21 May 2015 13:11:13 +0200 Subject: [PATCH 10/17] Apply recent VM interface changes to EVM JIT. --- libevmjit-cpp/Env.cpp | 25 +++++++++++++++---------- libevmjit-cpp/JitVM.h | 6 +++++- 2 files changed, 20 insertions(+), 11 deletions(-) diff --git a/libevmjit-cpp/Env.cpp b/libevmjit-cpp/Env.cpp index d1f239f9f..86a65dbab 100644 --- a/libevmjit-cpp/Env.cpp +++ b/libevmjit-cpp/Env.cpp @@ -64,19 +64,24 @@ extern "C" EXPORT bool env_call(ExtVMFace* _env, int64_t* io_gas, int64_t _callGas, h256* _receiveAddress, i256* _value, byte* _inBeg, uint64_t _inSize, byte* _outBeg, uint64_t _outSize, h256* _codeAddress) { - auto value = llvm2eth(*_value); - auto receiveAddress = right160(*_receiveAddress); - auto codeAddress = right160(*_codeAddress); - const auto isCall = receiveAddress == codeAddress; // OPT: The same address pointer can be used if not CODECALL + CallParameters params; + params.value = llvm2eth(*_value); + params.senderAddress = _env->myAddress; + params.receiveAddress = right160(*_receiveAddress); + params.codeAddress = right160(*_codeAddress); + params.data = {_inBeg, _inSize}; + params.out = {_outBeg, _outSize}; + params.onOp = {}; + const auto isCall = params.receiveAddress == params.codeAddress; // OPT: The same address pointer can be used if not CODECALL *io_gas -= _callGas; if (*io_gas < 0) return false; - if (isCall && !_env->exists(receiveAddress)) + if (isCall && !_env->exists(params.receiveAddress)) *io_gas -= static_cast(c_callNewAccountGas); // no underflow, *io_gas non-negative before - if (value > 0) // value transfer + if (params.value > 0) // value transfer { /*static*/ assert(c_callValueTransferGas > c_callStipend && "Overflow possible"); *io_gas -= static_cast(c_callValueTransferGas); // no underflow @@ -87,11 +92,11 @@ extern "C" return false; auto ret = false; - auto callGas = u256{_callGas}; - if (_env->balance(_env->myAddress) >= value && _env->depth < 1024) - ret = _env->call(receiveAddress, value, {_inBeg, _inSize}, callGas, {_outBeg, _outSize}, {}, {}, codeAddress); + params.gas = u256{_callGas}; + if (_env->balance(_env->myAddress) >= params.value && _env->depth < 1024) + ret = _env->call(params); - *io_gas += static_cast(callGas); // it is never more than initial _callGas + *io_gas += static_cast(params.gas); // it is never more than initial _callGas return ret; } diff --git a/libevmjit-cpp/JitVM.h b/libevmjit-cpp/JitVM.h index 58caa3648..38ef9ff61 100644 --- a/libevmjit-cpp/JitVM.h +++ b/libevmjit-cpp/JitVM.h @@ -12,10 +12,14 @@ class JitVM: public VMFace { virtual bytesConstRef go(ExtVMFace& _ext, OnOpFunc const& _onOp = {}, uint64_t _steps = (uint64_t)-1) override final; + virtual u256 gas() const noexcept { return m_gas; } + virtual void reset(u256 const& _gas = 0) noexcept { m_gas = _gas; } + private: friend class VMFactory; - explicit JitVM(u256 _gas = 0) : VMFace(_gas) {} + explicit JitVM(u256 _gas = 0): m_gas(_gas) {} + u256 m_gas; jit::RuntimeData m_data; jit::ExecutionEngine m_engine; std::unique_ptr m_fallbackVM; ///< VM used in case of input data rejected by JIT From 04a123e9a1c27adad19a00c949c19bea3df88dbb Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Pawe=C5=82=20Bylica?= Date: Wed, 27 May 2015 13:23:00 +0200 Subject: [PATCH 11/17] Change VM interface to return a copy of output. --- libevmjit-cpp/JitVM.cpp | 4 ++-- libevmjit-cpp/JitVM.h | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/libevmjit-cpp/JitVM.cpp b/libevmjit-cpp/JitVM.cpp index 5193168a4..ac8df545f 100644 --- a/libevmjit-cpp/JitVM.cpp +++ b/libevmjit-cpp/JitVM.cpp @@ -18,7 +18,7 @@ namespace eth extern "C" void env_sload(); // fake declaration for linker symbol stripping workaround, see a call below -bytesConstRef JitVM::go(u256& io_gas, ExtVMFace& _ext, OnOpFunc const& _onOp, uint64_t _step) +bytesConstRef JitVM::execImpl(u256& io_gas, ExtVMFace& _ext, OnOpFunc const& _onOp, uint64_t _step) { using namespace jit; @@ -33,7 +33,7 @@ bytesConstRef JitVM::go(u256& io_gas, ExtVMFace& _ext, OnOpFunc const& _onOp, ui { cwarn << "Execution rejected by EVM JIT (gas limit: " << io_gas << "), executing with interpreter"; m_fallbackVM = VMFactory::create(VMKind::Interpreter); - return m_fallbackVM->go(io_gas, _ext, _onOp, _step); + return m_fallbackVM->execImpl(io_gas, _ext, _onOp, _step); } m_data.gas = static_cast(io_gas); diff --git a/libevmjit-cpp/JitVM.h b/libevmjit-cpp/JitVM.h index e6864f885..7c8b4ceb8 100644 --- a/libevmjit-cpp/JitVM.h +++ b/libevmjit-cpp/JitVM.h @@ -11,7 +11,7 @@ namespace eth class JitVM: public VMFace { public: - virtual bytesConstRef go(u256& io_gas, ExtVMFace& _ext, OnOpFunc const& _onOp = {}, uint64_t _steps = (uint64_t)-1) override final; + virtual bytesConstRef execImpl(u256& io_gas, ExtVMFace& _ext, OnOpFunc const& _onOp = {}, uint64_t _steps = (uint64_t)-1) override final; private: jit::RuntimeData m_data; From 565744c9e3a2e91119c7ebb3d6663e0569ed94e7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Pawe=C5=82=20Bylica?= Date: Thu, 28 May 2015 08:56:21 +0200 Subject: [PATCH 12/17] Change the way execution results are collected. Changes handling ExecutionResult by Executive. From now execution results are collected on if a storage for results (ExecutionResult) is provided to an Executiove instance up front. This change allow better output management for calls - VM interface improved. --- libevmjit-cpp/JitVM.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/libevmjit-cpp/JitVM.h b/libevmjit-cpp/JitVM.h index 7c8b4ceb8..03c3c8880 100644 --- a/libevmjit-cpp/JitVM.h +++ b/libevmjit-cpp/JitVM.h @@ -11,7 +11,7 @@ namespace eth class JitVM: public VMFace { public: - virtual bytesConstRef execImpl(u256& io_gas, ExtVMFace& _ext, OnOpFunc const& _onOp = {}, uint64_t _steps = (uint64_t)-1) override final; + virtual bytesConstRef execImpl(u256& io_gas, ExtVMFace& _ext, OnOpFunc const& _onOp, uint64_t _steps) override final; private: jit::RuntimeData m_data; From 17d7d715543719cd1c1dc5262b1af2ebc82a7e15 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Pawe=C5=82=20Bylica?= Date: Thu, 28 May 2015 10:29:16 +0200 Subject: [PATCH 13/17] Kill steps limit option in VM. --- libevmjit-cpp/JitVM.cpp | 4 ++-- libevmjit-cpp/JitVM.h | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/libevmjit-cpp/JitVM.cpp b/libevmjit-cpp/JitVM.cpp index ac8df545f..0d6a6e00a 100644 --- a/libevmjit-cpp/JitVM.cpp +++ b/libevmjit-cpp/JitVM.cpp @@ -18,7 +18,7 @@ namespace eth extern "C" void env_sload(); // fake declaration for linker symbol stripping workaround, see a call below -bytesConstRef JitVM::execImpl(u256& io_gas, ExtVMFace& _ext, OnOpFunc const& _onOp, uint64_t _step) +bytesConstRef JitVM::execImpl(u256& io_gas, ExtVMFace& _ext, OnOpFunc const& _onOp) { using namespace jit; @@ -33,7 +33,7 @@ bytesConstRef JitVM::execImpl(u256& io_gas, ExtVMFace& _ext, OnOpFunc const& _on { cwarn << "Execution rejected by EVM JIT (gas limit: " << io_gas << "), executing with interpreter"; m_fallbackVM = VMFactory::create(VMKind::Interpreter); - return m_fallbackVM->execImpl(io_gas, _ext, _onOp, _step); + return m_fallbackVM->execImpl(io_gas, _ext, _onOp); } m_data.gas = static_cast(io_gas); diff --git a/libevmjit-cpp/JitVM.h b/libevmjit-cpp/JitVM.h index 03c3c8880..e97abd83b 100644 --- a/libevmjit-cpp/JitVM.h +++ b/libevmjit-cpp/JitVM.h @@ -11,7 +11,7 @@ namespace eth class JitVM: public VMFace { public: - virtual bytesConstRef execImpl(u256& io_gas, ExtVMFace& _ext, OnOpFunc const& _onOp, uint64_t _steps) override final; + virtual bytesConstRef execImpl(u256& io_gas, ExtVMFace& _ext, OnOpFunc const& _onOp) override final; private: jit::RuntimeData m_data; From 0871668ebf570f9f290a4714c6f17f63c4d0d67d Mon Sep 17 00:00:00 2001 From: winsvega Date: Fri, 22 May 2015 16:21:34 +0300 Subject: [PATCH 14/17] evmJit warnings fix --- libevmjit-cpp/Env.cpp | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/libevmjit-cpp/Env.cpp b/libevmjit-cpp/Env.cpp index 86a65dbab..4d7865bd0 100644 --- a/libevmjit-cpp/Env.cpp +++ b/libevmjit-cpp/Env.cpp @@ -54,7 +54,7 @@ extern "C" if (_env->balance(_env->myAddress) >= endowment && _env->depth < 1024) { u256 gas = *io_gas; - h256 address(_env->create(endowment, gas, {_initBeg, _initSize}, {}), h256::AlignRight); + h256 address(_env->create(endowment, gas, {_initBeg, (size_t)_initSize}, {}), h256::AlignRight); *io_gas = static_cast(gas); *o_address = address; } @@ -69,8 +69,8 @@ extern "C" params.senderAddress = _env->myAddress; params.receiveAddress = right160(*_receiveAddress); params.codeAddress = right160(*_codeAddress); - params.data = {_inBeg, _inSize}; - params.out = {_outBeg, _outSize}; + params.data = {_inBeg, (size_t)_inSize}; + params.out = {_outBeg, (size_t)_outSize}; params.onOp = {}; const auto isCall = params.receiveAddress == params.codeAddress; // OPT: The same address pointer can be used if not CODECALL @@ -102,7 +102,7 @@ extern "C" EXPORT void env_sha3(byte* _begin, uint64_t _size, h256* o_hash) { - auto hash = sha3({_begin, _size}); + auto hash = sha3({_begin, (size_t)_size}); *o_hash = hash; } @@ -130,7 +130,7 @@ extern "C" if (_topic4) topics.push_back(*_topic4); - _env->log(std::move(topics), {_beg, _size}); + _env->log(std::move(topics), {_beg, (size_t)_size}); } } From 8aa722658d7c3325eb24fd79073f3b8d44e6d3a1 Mon Sep 17 00:00:00 2001 From: arkpar Date: Mon, 8 Jun 2015 10:26:07 +0200 Subject: [PATCH 15/17] codeHash in ExtVM --- libevmjit-cpp/JitVM.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/libevmjit-cpp/JitVM.cpp b/libevmjit-cpp/JitVM.cpp index 5193168a4..6dafa3f39 100644 --- a/libevmjit-cpp/JitVM.cpp +++ b/libevmjit-cpp/JitVM.cpp @@ -51,7 +51,7 @@ bytesConstRef JitVM::go(u256& io_gas, ExtVMFace& _ext, OnOpFunc const& _onOp, ui m_data.timestamp = static_cast(_ext.currentBlock.timestamp); m_data.code = _ext.code.data(); m_data.codeSize = _ext.code.size(); - m_data.codeHash = eth2llvm(sha3(_ext.code)); + m_data.codeHash = eth2llvm(_ext.codeHash); auto env = reinterpret_cast(&_ext); auto exitCode = m_engine.run(&m_data, env); From 9359223e694f28c1aa30717694a243d8fb057f07 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Pawe=C5=82=20Bylica?= Date: Wed, 10 Jun 2015 13:04:19 +0200 Subject: [PATCH 16/17] Remove memory leak detector. Is it not thread-safe. --- libevmjit/Array.cpp | 43 ++----------------------------------------- 1 file changed, 2 insertions(+), 41 deletions(-) diff --git a/libevmjit/Array.cpp b/libevmjit/Array.cpp index 3266038db..0b511a058 100644 --- a/libevmjit/Array.cpp +++ b/libevmjit/Array.cpp @@ -9,8 +9,6 @@ #include "Runtime.h" #include "Utils.h" -#include // DEBUG only - namespace dev { namespace eth @@ -269,52 +267,15 @@ void Array::extend(llvm::Value* _arrayPtr, llvm::Value* _size) } } -namespace -{ - struct AllocatedMemoryWatchdog - { - std::set allocatedMemory; - - ~AllocatedMemoryWatchdog() - { - if (!allocatedMemory.empty()) - { - DLOG(mem) << allocatedMemory.size() << " MEM LEAKS!\n"; - for (auto&& leak : allocatedMemory) - DLOG(mem) << "\t" << leak << "\n"; - } - } - }; - - AllocatedMemoryWatchdog watchdog; -} - extern "C" { - using namespace dev::eth::jit; - EXPORT void* ext_realloc(void* _data, size_t _size) noexcept { - //std::cerr << "REALLOC: " << _data << " [" << _size << "]" << std::endl; - auto newData = std::realloc(_data, _size); - if (_data != newData) - { - DLOG(mem) << "REALLOC: " << newData << " <- " << _data << " [" << _size << "]\n"; - watchdog.allocatedMemory.erase(_data); - watchdog.allocatedMemory.insert(newData); - } - return newData; + return std::realloc(_data, _size); } EXPORT void ext_free(void* _data) noexcept { std::free(_data); - if (_data) - { - DLOG(mem) << "FREE : " << _data << "\n"; - watchdog.allocatedMemory.erase(_data); - } } - -} // extern "C" - +} From 787df33341d084c2d69e1e2dabe9a009744f8dcb Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Pawe=C5=82=20Bylica?= Date: Wed, 10 Jun 2015 14:22:44 +0200 Subject: [PATCH 17/17] Protect EVM JIT cache with mutex. Fixes ethereum/cpp-ethereum#2086. --- libevmjit/Cache.cpp | 27 +++++++++++++++++++++++---- 1 file changed, 23 insertions(+), 4 deletions(-) diff --git a/libevmjit/Cache.cpp b/libevmjit/Cache.cpp index 47a6386e9..42ccf44ac 100644 --- a/libevmjit/Cache.cpp +++ b/libevmjit/Cache.cpp @@ -1,5 +1,7 @@ #include "Cache.h" +#include + #include "preprocessor/llvm_includes_start.h" #include #include @@ -23,6 +25,8 @@ namespace jit namespace { + using Guard = std::lock_guard; + std::mutex x_cacheMutex; CacheMode g_mode; llvm::MemoryBuffer* g_lastObject; ExecutionEngineListener* g_listener; @@ -43,6 +47,9 @@ namespace ObjectCache* Cache::getObjectCache(CacheMode _mode, ExecutionEngineListener* _listener) { static ObjectCache objectCache; + + Guard g{x_cacheMutex}; + g_mode = _mode; g_listener = _listener; return &objectCache; @@ -50,6 +57,8 @@ ObjectCache* Cache::getObjectCache(CacheMode _mode, ExecutionEngineListener* _li void Cache::clear() { + Guard g{x_cacheMutex}; + using namespace llvm::sys; llvm::SmallString<256> cachePath; path::system_temp_directory(false, cachePath); @@ -62,6 +71,8 @@ void Cache::clear() void Cache::preload(llvm::ExecutionEngine& _ee, std::unordered_map& _funcCache) { + Guard g{x_cacheMutex}; + // TODO: Cache dir should be in one place using namespace llvm::sys; llvm::SmallString<256> cachePath; @@ -92,11 +103,14 @@ void Cache::preload(llvm::ExecutionEngine& _ee, std::unordered_map Cache::getObject(std::string const& id) { + Guard g{x_cacheMutex}; + if (g_mode != CacheMode::on && g_mode != CacheMode::read) return nullptr; - if (g_listener) - g_listener->stateChanged(ExecState::CacheLoad); + // TODO: Disabled because is not thread-safe. + //if (g_listener) + // g_listener->stateChanged(ExecState::CacheLoad); DLOG(cache) << id << ": search\n"; if (!CHECK(!g_lastObject)) @@ -136,12 +150,15 @@ std::unique_ptr Cache::getObject(std::string const& id) void ObjectCache::notifyObjectCompiled(llvm::Module const* _module, llvm::MemoryBuffer const* _object) { + Guard g{x_cacheMutex}; + // Only in "on" and "write" mode if (g_mode != CacheMode::on && g_mode != CacheMode::write) return; - if (g_listener) - g_listener->stateChanged(ExecState::CacheWrite); + // TODO: Disabled because is not thread-safe. + // if (g_listener) + // g_listener->stateChanged(ExecState::CacheWrite); auto&& id = _module->getModuleIdentifier(); llvm::SmallString<256> cachePath; @@ -161,6 +178,8 @@ void ObjectCache::notifyObjectCompiled(llvm::Module const* _module, llvm::Memory llvm::MemoryBuffer* ObjectCache::getObject(llvm::Module const* _module) { + Guard g{x_cacheMutex}; + DLOG(cache) << _module->getModuleIdentifier() << ": use\n"; auto o = g_lastObject; g_lastObject = nullptr;