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] 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