diff --git a/evmjit/CMakeLists.txt b/evmjit/CMakeLists.txt index acb113207e..4888de5238 100644 --- a/evmjit/CMakeLists.txt +++ b/evmjit/CMakeLists.txt @@ -4,17 +4,18 @@ project(evmjit) set_property(GLOBAL PROPERTY USE_FOLDERS ON) -if ("${CMAKE_CXX_COMPILER_ID}" STREQUAL "MSVC") -else () - set(CMAKE_CXX_FLAGS "-std=c++11 -Wall -Wno-unknown-pragmas -Wextra -DSHAREDLIB -fPIC") +if(CMAKE_CXX_COMPILER_ID STREQUAL "MSVC") +else() + set(CMAKE_CXX_FLAGS "-std=c++11 -Wall -Wno-unknown-pragmas -Wextra -fPIC") endif() -if(APPLE) - set(CMAKE_SHARED_LINKER_FLAGS "-undefined dynamic_lookup") +if(CMAKE_SYSTEM_NAME STREQUAL "Linux") + # Do not allow unresovled symbols in shared library (default on linux) + set(CMAKE_SHARED_LINKER_FLAGS "-Wl,--no-undefined") endif() # LLVM -if(LLVM_DIR) # local LLVM build +if(LLVM_DIR OR APPLE) # local LLVM build find_package(LLVM REQUIRED CONFIG) message(STATUS "Found LLVM ${LLVM_PACKAGE_VERSION}") message(STATUS "Using LLVMConfig.cmake in: ${LLVM_DIR}") @@ -28,11 +29,9 @@ else() message(STATUS "LLVM include dirs: ${LLVM_INCLUDE_DIRS}") set(LLVM_LIBS "-lLLVMBitWriter -lLLVMX86CodeGen -lLLVMSelectionDAG -lLLVMAsmPrinter -lLLVMCodeGen -lLLVMScalarOpts -lLLVMInstCombine -lLLVMTransformUtils -lLLVMipa -lLLVMAnalysis -lLLVMX86AsmParser -lLLVMX86Desc -lLLVMX86Info -lLLVMX86AsmPrinter -lLLVMX86Utils -lLLVMMCJIT -lLLVMTarget -lLLVMRuntimeDyld -lLLVMObject -lLLVMMCParser -lLLVMBitReader -lLLVMExecutionEngine -lLLVMMC -lLLVMCore -lLLVMSupport -lz -lpthread -lffi -ltinfo -ldl -lm") add_definitions(-D__STDC_CONSTANT_MACROS -D__STDC_FORMAT_MACROS -D__STDC_LIMIT_MACROS) + link_directories(/usr/lib/llvm-3.5/lib) endif() -# Boost -find_package(Boost REQUIRED) - add_subdirectory(libevmjit) if(EVMJIT_CPP) diff --git a/evmjit/libevmjit-cpp/CMakeLists.txt b/evmjit/libevmjit-cpp/CMakeLists.txt index 25be95177e..58375e4ee4 100644 --- a/evmjit/libevmjit-cpp/CMakeLists.txt +++ b/evmjit/libevmjit-cpp/CMakeLists.txt @@ -1,5 +1,8 @@ set(TARGET_NAME evmjit-cpp) +# Boost +find_package(Boost REQUIRED) + set(SOURCES Env.cpp JitVM.cpp JitVM.h diff --git a/evmjit/libevmjit-cpp/Env.cpp b/evmjit/libevmjit-cpp/Env.cpp index 6320aa2228..f898977929 100644 --- a/evmjit/libevmjit-cpp/Env.cpp +++ b/evmjit/libevmjit-cpp/Env.cpp @@ -3,7 +3,7 @@ #include #include -#include +#include "Utils.h" extern "C" { @@ -16,7 +16,6 @@ extern "C" using namespace dev; using namespace dev::eth; using jit::i256; - using jit::eth2llvm; EXPORT void env_sload(ExtVMFace* _env, i256* _index, i256* o_value) { @@ -89,7 +88,7 @@ extern "C" *o_hash = hash; } - EXPORT byte const* env_getExtCode(ExtVMFace* _env, h256* _addr256, uint64_t* o_size) + EXPORT byte const* env_extcode(ExtVMFace* _env, h256* _addr256, uint64_t* o_size) { auto addr = right160(*_addr256); auto& code = _env->codeAt(addr); diff --git a/evmjit/libevmjit-cpp/JitVM.cpp b/evmjit/libevmjit-cpp/JitVM.cpp index dda8133a8d..ededbff527 100644 --- a/evmjit/libevmjit-cpp/JitVM.cpp +++ b/evmjit/libevmjit-cpp/JitVM.cpp @@ -2,7 +2,7 @@ #include "JitVM.h" #include #include -#include +#include "Utils.h" namespace dev { @@ -13,28 +13,28 @@ bytesConstRef JitVM::go(ExtVMFace& _ext, OnOpFunc const&, uint64_t) { using namespace jit; - m_data.set(RuntimeData::Gas, m_gas); - m_data.set(RuntimeData::Address, fromAddress(_ext.myAddress)); - m_data.set(RuntimeData::Caller, fromAddress(_ext.caller)); - m_data.set(RuntimeData::Origin, fromAddress(_ext.origin)); - 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::CoinBase, fromAddress(_ext.currentBlock.coinbaseAddress)); - m_data.set(RuntimeData::TimeStamp, _ext.currentBlock.timestamp); - m_data.set(RuntimeData::Number, _ext.currentBlock.number); - m_data.set(RuntimeData::Difficulty, _ext.currentBlock.difficulty); - m_data.set(RuntimeData::GasLimit, _ext.currentBlock.gasLimit); - m_data.set(RuntimeData::CodeSize, _ext.code.size()); + m_data.elems[RuntimeData::Gas] = eth2llvm(m_gas); + m_data.elems[RuntimeData::Address] = eth2llvm(fromAddress(_ext.myAddress)); + m_data.elems[RuntimeData::Caller] = eth2llvm(fromAddress(_ext.caller)); + m_data.elems[RuntimeData::Origin] = eth2llvm(fromAddress(_ext.origin)); + m_data.elems[RuntimeData::CallValue] = eth2llvm(_ext.value); + m_data.elems[RuntimeData::CallDataSize] = eth2llvm(_ext.data.size()); + m_data.elems[RuntimeData::GasPrice] = eth2llvm(_ext.gasPrice); + m_data.elems[RuntimeData::CoinBase] = eth2llvm(fromAddress(_ext.currentBlock.coinbaseAddress)); + m_data.elems[RuntimeData::TimeStamp] = eth2llvm(_ext.currentBlock.timestamp); + m_data.elems[RuntimeData::Number] = eth2llvm(_ext.currentBlock.number); + m_data.elems[RuntimeData::Difficulty] = eth2llvm(_ext.currentBlock.difficulty); + m_data.elems[RuntimeData::GasLimit] = eth2llvm(_ext.currentBlock.gasLimit); + m_data.elems[RuntimeData::CodeSize] = eth2llvm(_ext.code.size()); m_data.callData = _ext.data.data(); - m_data.code = _ext.code.data(); + m_data.code = _ext.code.data(); auto env = reinterpret_cast(&_ext); auto exitCode = m_engine.run(_ext.code, &m_data, env); switch (exitCode) { case ReturnCode::Suicide: - _ext.suicide(right160(m_data.get(RuntimeData::SuicideDestAddress))); + _ext.suicide(right160(llvm2eth(m_data.elems[RuntimeData::SuicideDestAddress]))); break; case ReturnCode::BadJumpDestination: @@ -50,7 +50,7 @@ bytesConstRef JitVM::go(ExtVMFace& _ext, OnOpFunc const&, uint64_t) } m_gas = llvm2eth(m_data.elems[RuntimeData::Gas]); - return {m_engine.returnData.data(), m_engine.returnData.size()}; // TODO: This all bytesConstRef is problematic, review. + return {std::get<0>(m_engine.returnData), std::get<1>(m_engine.returnData)}; } } diff --git a/evmjit/libevmjit-cpp/Utils.h b/evmjit/libevmjit-cpp/Utils.h new file mode 100644 index 0000000000..ac796920b4 --- /dev/null +++ b/evmjit/libevmjit-cpp/Utils.h @@ -0,0 +1,38 @@ +#pragma once + +#include + +namespace dev +{ +namespace eth +{ + +inline u256 llvm2eth(jit::i256 _i) +{ + u256 u = 0; + u |= _i.d; + u <<= 64; + u |= _i.c; + u <<= 64; + u |= _i.b; + u <<= 64; + u |= _i.a; + return u; +} + +inline jit::i256 eth2llvm(u256 _u) +{ + jit::i256 i; + u256 mask = 0xFFFFFFFFFFFFFFFF; + i.a = static_cast(_u & mask); + _u >>= 64; + i.b = static_cast(_u & mask); + _u >>= 64; + i.c = static_cast(_u & mask); + _u >>= 64; + i.d = static_cast(_u & mask); + return i; +} + +} +} diff --git a/evmjit/libevmjit/Arith256.cpp b/evmjit/libevmjit/Arith256.cpp index b5319bda71..adb6cf9d54 100644 --- a/evmjit/libevmjit/Arith256.cpp +++ b/evmjit/libevmjit/Arith256.cpp @@ -1,8 +1,10 @@ #include "Arith256.h" #include "Runtime.h" #include "Type.h" +#include "Endianness.h" #include +#include namespace dev { @@ -63,6 +65,8 @@ llvm::Value* Arith256::mul(llvm::Value* _arg1, llvm::Value* _arg2) llvm::Value* Arith256::div(llvm::Value* _arg1, llvm::Value* _arg2) { + + //return Endianness::toNative(m_builder, binaryOp(m_div, Endianness::toBE(m_builder, _arg1), Endianness::toBE(m_builder, _arg2))); return binaryOp(m_div, _arg1, _arg2); } @@ -98,25 +102,104 @@ llvm::Value* Arith256::mulmod(llvm::Value* _arg1, llvm::Value* _arg2, llvm::Valu namespace { - using s256 = boost::multiprecision::int256_t; + using uint128 = __uint128_t; - inline s256 u2s(u256 _u) +// uint128 add(uint128 a, uint128 b) { return a + b; } +// uint128 mul(uint128 a, uint128 b) { return a * b; } +// +// uint128 mulq(uint64_t x, uint64_t y) +// { +// return (uint128)x * (uint128)y; +// } +// +// uint128 addc(uint64_t x, uint64_t y) +// { +// return (uint128)x * (uint128)y; +// } + + struct uint256 { - static const bigint c_end = (bigint)1 << 256; - static const u256 c_send = (u256)1 << 255; - if (_u < c_send) - return (s256)_u; - else - return (s256)-(c_end - _u); + uint64_t lo; + uint64_t mid; + uint128 hi; + }; + +// uint256 add(uint256 x, uint256 y) +// { +// auto lo = (uint128) x.lo + y.lo; +// auto mid = (uint128) x.mid + y.mid + (lo >> 64); +// return {lo, mid, x.hi + y.hi + (mid >> 64)}; +// } + + uint256 mul(uint256 x, uint256 y) + { + auto t1 = (uint128) x.lo * y.lo; + auto t2 = (uint128) x.lo * y.mid; + auto t3 = x.lo * y.hi; + auto t4 = (uint128) x.mid * y.lo; + auto t5 = (uint128) x.mid * y.mid; + auto t6 = x.mid * y.hi; + auto t7 = x.hi * y.lo; + auto t8 = x.hi * y.mid; + + auto lo = (uint64_t) t1; + auto m1 = (t1 >> 64) + (uint64_t) t2; + auto m2 = (uint64_t) m1; + auto mid = (uint128) m2 + (uint64_t) t4; + auto hi = (t2 >> 64) + t3 + (t4 >> 64) + t5 + (t6 << 64) + t7 + + (t8 << 64) + (m1 >> 64) + (mid >> 64); + + return {lo, (uint64_t)mid, hi}; } - inline u256 s2u(s256 _u) + bool isZero(i256 const* _n) { - static const bigint c_end = (bigint)1 << 256; - if (_u >= 0) - return (u256)_u; - else - return (u256)(c_end + _u); + return _n->a == 0 && _n->b == 0 && _n->c == 0 && _n->d == 0; + } + + const auto nLimbs = sizeof(i256) / sizeof(mp_limb_t); + + // FIXME: Not thread-safe + static mp_limb_t mod_limbs[] = {0, 0, 0, 0, 1}; + static_assert(sizeof(mod_limbs) / sizeof(mod_limbs[0]) == nLimbs + 1, "mp_limb_t size mismatch"); + static const mpz_t mod{nLimbs + 1, nLimbs + 1, &mod_limbs[0]}; + + static mp_limb_t tmp_limbs[nLimbs + 2]; + static mpz_t tmp{nLimbs + 2, 0, &tmp_limbs[0]}; + + int countLimbs(i256 const* _n) + { + static const auto limbsInWord = sizeof(_n->a) / sizeof(mp_limb_t); + static_assert(limbsInWord == 1, "E?"); + + int l = nLimbs; + if (_n->d != 0) return l; + l -= limbsInWord; + if (_n->c != 0) return l; + l -= limbsInWord; + if (_n->b != 0) return l; + l -= limbsInWord; + if (_n->a != 0) return l; + return 0; + } + + void u2s(mpz_t _u) + { + if (static_cast::type>(_u->_mp_d[nLimbs - 1]) < 0) + { + mpz_sub(tmp, mod, _u); + mpz_set(_u, tmp); + _u->_mp_size = -_u->_mp_size; + } + } + + void s2u(mpz_t _s) + { + if (_s->_mp_size < 0) + { + mpz_add(tmp, mod, _s); + mpz_set(_s, tmp); + } } } @@ -130,69 +213,120 @@ extern "C" using namespace dev::eth::jit; - EXPORT void arith_mul(i256* _arg1, i256* _arg2, i256* o_result) + EXPORT void arith_mul(uint256* _arg1, uint256* _arg2, uint256* o_result) { - auto arg1 = llvm2eth(*_arg1); - auto arg2 = llvm2eth(*_arg2); - *o_result = eth2llvm(arg1 * arg2); + *o_result = mul(*_arg1, *_arg2); } EXPORT void arith_div(i256* _arg1, i256* _arg2, i256* o_result) { - auto arg1 = llvm2eth(*_arg1); - auto arg2 = llvm2eth(*_arg2); - *o_result = eth2llvm(arg2 == 0 ? arg2 : arg1 / arg2); + *o_result = {}; + if (isZero(_arg2)) + return; + + mpz_t x{nLimbs, countLimbs(_arg1), reinterpret_cast(_arg1)}; + mpz_t y{nLimbs, countLimbs(_arg2), reinterpret_cast(_arg2)}; + mpz_t z{nLimbs, 0, reinterpret_cast(o_result)}; + + mpz_tdiv_q(z, x, y); + +// auto arg1 = llvm2eth(*_arg1); +// auto arg2 = llvm2eth(*_arg2); +// auto res = arg2 == 0 ? arg2 : arg1 / arg2; +// std::cout << "DIV " << arg1 << "/" << arg2 << " = " << res << std::endl; +// gmp_printf("GMP %Zd / %Zd = %Zd\n", x, y, z); } EXPORT void arith_mod(i256* _arg1, i256* _arg2, i256* o_result) { - auto arg1 = llvm2eth(*_arg1); - auto arg2 = llvm2eth(*_arg2); - *o_result = eth2llvm(arg2 == 0 ? arg2 : arg1 % arg2); + *o_result = {}; + if (isZero(_arg2)) + return; + + mpz_t x{nLimbs, countLimbs(_arg1), reinterpret_cast(_arg1)}; + mpz_t y{nLimbs, countLimbs(_arg2), reinterpret_cast(_arg2)}; + mpz_t z{nLimbs, 0, reinterpret_cast(o_result)}; + + mpz_tdiv_r(z, x, y); } EXPORT void arith_sdiv(i256* _arg1, i256* _arg2, i256* o_result) { - auto arg1 = llvm2eth(*_arg1); - auto arg2 = llvm2eth(*_arg2); - *o_result = eth2llvm(arg2 == 0 ? arg2 : s2u(u2s(arg1) / u2s(arg2))); + *o_result = {}; + if (isZero(_arg2)) + return; + + mpz_t x{nLimbs, countLimbs(_arg1), reinterpret_cast(_arg1)}; + mpz_t y{nLimbs, countLimbs(_arg2), reinterpret_cast(_arg2)}; + mpz_t z{nLimbs, 0, reinterpret_cast(o_result)}; + u2s(x); + u2s(y); + mpz_tdiv_q(z, x, y); + s2u(z); } EXPORT void arith_smod(i256* _arg1, i256* _arg2, i256* o_result) { - auto arg1 = llvm2eth(*_arg1); - auto arg2 = llvm2eth(*_arg2); - *o_result = eth2llvm(arg2 == 0 ? arg2 : s2u(u2s(arg1) % u2s(arg2))); + *o_result = {}; + if (isZero(_arg2)) + return; + + mpz_t x{nLimbs, countLimbs(_arg1), reinterpret_cast(_arg1)}; + mpz_t y{nLimbs, countLimbs(_arg2), reinterpret_cast(_arg2)}; + mpz_t z{nLimbs, 0, reinterpret_cast(o_result)}; + u2s(x); + u2s(y); + mpz_tdiv_r(z, x, y); + s2u(z); } EXPORT void arith_exp(i256* _arg1, i256* _arg2, i256* o_result) { - bigint left = llvm2eth(*_arg1); - bigint right = llvm2eth(*_arg2); - auto ret = static_cast(boost::multiprecision::powm(left, right, bigint(2) << 256)); - *o_result = eth2llvm(ret); + *o_result = {}; + + static mp_limb_t mod_limbs[nLimbs + 1] = {}; + mod_limbs[nLimbs] = 1; + static const mpz_t mod{nLimbs + 1, nLimbs + 1, &mod_limbs[0]}; + + mpz_t x{nLimbs, countLimbs(_arg1), reinterpret_cast(_arg1)}; + mpz_t y{nLimbs, countLimbs(_arg2), reinterpret_cast(_arg2)}; + mpz_t z{nLimbs, 0, reinterpret_cast(o_result)}; + + mpz_powm(z, x, y, mod); } EXPORT void arith_mulmod(i256* _arg1, i256* _arg2, i256* _arg3, i256* o_result) { - auto arg1 = llvm2eth(*_arg1); - auto arg2 = llvm2eth(*_arg2); - auto arg3 = llvm2eth(*_arg3); - if (arg3 != 0) - *o_result = eth2llvm(u256((bigint(arg1) * bigint(arg2)) % arg3)); - else - *o_result = {}; + *o_result = {}; + if (isZero(_arg3)) + return; + + mpz_t x{nLimbs, countLimbs(_arg1), reinterpret_cast(_arg1)}; + mpz_t y{nLimbs, countLimbs(_arg2), reinterpret_cast(_arg2)}; + mpz_t m{nLimbs, countLimbs(_arg3), reinterpret_cast(_arg3)}; + mpz_t z{nLimbs, 0, reinterpret_cast(o_result)}; + static mp_limb_t p_limbs[nLimbs * 2] = {}; + static mpz_t p{nLimbs * 2, 0, &p_limbs[0]}; + + mpz_mul(p, x, y); + mpz_tdiv_r(z, p, m); } EXPORT void arith_addmod(i256* _arg1, i256* _arg2, i256* _arg3, i256* o_result) { - auto arg1 = llvm2eth(*_arg1); - auto arg2 = llvm2eth(*_arg2); - auto arg3 = llvm2eth(*_arg3); - if (arg3 != 0) - *o_result = eth2llvm(u256((bigint(arg1) + bigint(arg2)) % arg3)); - else - *o_result = {}; + *o_result = {}; + if (isZero(_arg3)) + return; + + mpz_t x{nLimbs, countLimbs(_arg1), reinterpret_cast(_arg1)}; + mpz_t y{nLimbs, countLimbs(_arg2), reinterpret_cast(_arg2)}; + mpz_t m{nLimbs, countLimbs(_arg3), reinterpret_cast(_arg3)}; + mpz_t z{nLimbs, 0, reinterpret_cast(o_result)}; + static mp_limb_t s_limbs[nLimbs + 1] = {}; + static mpz_t s{nLimbs + 1, 0, &s_limbs[0]}; + + mpz_add(s, x, y); + mpz_tdiv_r(z, s, m); } } diff --git a/evmjit/libevmjit/BasicBlock.cpp b/evmjit/libevmjit/BasicBlock.cpp index dda0fbc364..5868c0f431 100644 --- a/evmjit/libevmjit/BasicBlock.cpp +++ b/evmjit/libevmjit/BasicBlock.cpp @@ -168,16 +168,13 @@ void BasicBlock::synchronizeLocalStack(Stack& _evmStack) if (val == nullptr) continue; - assert(llvm::isa(val)); llvm::PHINode* phi = llvm::cast(val); - if (! phi->use_empty()) - { - // Insert call to get() just before the PHI node and replace - // the uses of PHI with the uses of this new instruction. - m_builder.SetInsertPoint(phi); - auto newVal = _evmStack.get(idx); - phi->replaceAllUsesWith(newVal); - } + // Insert call to get() just before the PHI node and replace + // the uses of PHI with the uses of this new instruction. + m_builder.SetInsertPoint(phi); + auto newVal = _evmStack.get(idx); // OPT: Value may be never user but we need to check stack heigth + // It is probably a good idea to keep heigth as a local variable accesible by LLVM directly + phi->replaceAllUsesWith(newVal); phi->eraseFromParent(); } diff --git a/evmjit/libevmjit/BasicBlock.h b/evmjit/libevmjit/BasicBlock.h index 7a5364a4ea..cda4f89bda 100644 --- a/evmjit/libevmjit/BasicBlock.h +++ b/evmjit/libevmjit/BasicBlock.h @@ -57,7 +57,7 @@ public: explicit BasicBlock(std::string _name, llvm::Function* _mainFunc, llvm::IRBuilder<>& _builder, bool isJumpDest); BasicBlock(const BasicBlock&) = delete; - void operator=(const BasicBlock&) = delete; + BasicBlock& operator=(const BasicBlock&) = delete; llvm::BasicBlock* llvm() { return m_llvmBB; } @@ -66,6 +66,9 @@ public: bool isJumpDest() const { return m_isJumpDest; } + llvm::Value* getJumpTarget() const { return m_jumpTarget; } + void setJumpTarget(llvm::Value* _jumpTarget) { m_jumpTarget = _jumpTarget; } + LocalStack& localStack() { return m_stack; } /// Optimization: propagates values between local stacks in basic blocks @@ -112,6 +115,9 @@ private: /// Is the basic block a valid jump destination. /// JUMPDEST is the first instruction of the basic block. bool const m_isJumpDest = false; + + /// If block finishes with dynamic jump target index is stored here + llvm::Value* m_jumpTarget = nullptr; }; } diff --git a/evmjit/libevmjit/CMakeLists.txt b/evmjit/libevmjit/CMakeLists.txt index 28b501ee3a..1990ec3987 100644 --- a/evmjit/libevmjit/CMakeLists.txt +++ b/evmjit/libevmjit/CMakeLists.txt @@ -2,24 +2,24 @@ set(TARGET_NAME evmjit) file(GLOB SOURCES "*.cpp") file(GLOB HEADERS "*.h") +set(INTERFACE_HEADERS interface.h) source_group("" FILES ${HEADERS}) source_group("" FILES ${SOURCES}) if (NOT "${CMAKE_CXX_COMPILER_ID}" STREQUAL "MSVC") # Disable rtti for Cache as LLVM has no rtti set_source_files_properties(Cache.cpp PROPERTIES COMPILE_FLAGS -fno-rtti) -endif () +endif() -link_directories(/usr/lib/llvm-3.5/lib) add_library(${TARGET_NAME} SHARED ${SOURCES} ${HEADERS}) set_property(TARGET ${TARGET_NAME} PROPERTY FOLDER "libs") include_directories(${LLVM_INCLUDE_DIRS}) -include_directories(${Boost_INCLUDE_DIRS}) -target_link_libraries(${TARGET_NAME} ${LLVM_LIBS}) +target_link_libraries(${TARGET_NAME} PRIVATE ${LLVM_LIBS}) +target_link_libraries(${TARGET_NAME} PRIVATE gmp) #set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -fno-rtti") -#install( TARGETS ${EXECUTABLE} ARCHIVE DESTINATION lib LIBRARY DESTINATION lib ) -#install( FILES ${HEADERS} DESTINATION include/${EXECUTABLE} ) +install(TARGETS ${TARGET_NAME} LIBRARY DESTINATION lib) +install(FILES ${INTERFACE_HEADERS} DESTINATION include/${TARGET_NAME}) diff --git a/evmjit/libevmjit/Cache.cpp b/evmjit/libevmjit/Cache.cpp index 0b725bc24d..0e6fb517a1 100644 --- a/evmjit/libevmjit/Cache.cpp +++ b/evmjit/libevmjit/Cache.cpp @@ -36,6 +36,20 @@ std::unique_ptr Cache::getObject(std::string const& id) llvm::sys::path::system_temp_directory(false, cachePath); llvm::sys::path::append(cachePath, "evm_objs", id); +#if defined(__GNUC__) && !defined(NDEBUG) + llvm::sys::fs::file_status st; + auto err = llvm::sys::fs::status(cachePath.str(), st); + if (err) + return nullptr; + auto mtime = st.getLastModificationTime().toEpochTime(); + + std::tm tm; + strptime(__DATE__ __TIME__, " %b %d %Y %H:%M:%S", &tm); + auto btime = (uint64_t)std::mktime(&tm); + if (btime > mtime) + return nullptr; +#endif + if (auto r = llvm::MemoryBuffer::getFile(cachePath.str(), -1, false)) lastObject = llvm::MemoryBuffer::getMemBufferCopy(r.get()->getBuffer()); else if (r.getError() != std::make_error_code(std::errc::no_such_file_or_directory)) diff --git a/evmjit/libevmjit/Common.h b/evmjit/libevmjit/Common.h index 1d8451c74d..d0c5822365 100644 --- a/evmjit/libevmjit/Common.h +++ b/evmjit/libevmjit/Common.h @@ -1,7 +1,7 @@ #pragma once #include -#include +#include namespace dev { @@ -12,8 +12,7 @@ namespace jit using byte = uint8_t; using bytes = std::vector; -using u256 = boost::multiprecision::uint256_t; -using bigint = boost::multiprecision::cpp_int; +using bytes_ref = std::tuple; struct NoteChannel {}; // FIXME: Use some log library? @@ -34,7 +33,6 @@ enum class ReturnCode }; /// Representation of 256-bit value binary compatible with LLVM i256 -// TODO: Replace with h256 struct i256 { uint64_t a = 0; diff --git a/evmjit/libevmjit/Compiler.cpp b/evmjit/libevmjit/Compiler.cpp index bfcb9cea10..6286ef92f2 100644 --- a/evmjit/libevmjit/Compiler.cpp +++ b/evmjit/libevmjit/Compiler.cpp @@ -4,6 +4,7 @@ #include #include #include +#include #include #include @@ -100,7 +101,7 @@ llvm::BasicBlock* Compiler::getJumpTableBlock() m_jumpTableBlock.reset(new BasicBlock("JumpTable", m_mainFunc, m_builder, true)); InsertPointGuard g{m_builder}; m_builder.SetInsertPoint(m_jumpTableBlock->llvm()); - auto dest = m_jumpTableBlock->localStack().pop(); + auto dest = m_builder.CreatePHI(Type::Word, 8, "target"); auto switchInstr = m_builder.CreateSwitch(dest, getBadJumpBlock()); for (auto&& p : m_basicBlocks) { @@ -165,6 +166,26 @@ std::unique_ptr Compiler::compile(bytes const& _bytecode, std::str removeDeadBlocks(); + // Link jump table target index + if (m_jumpTableBlock) + { + auto phi = llvm::cast(&m_jumpTableBlock->llvm()->getInstList().front()); + for (auto predIt = llvm::pred_begin(m_jumpTableBlock->llvm()); predIt != llvm::pred_end(m_jumpTableBlock->llvm()); ++predIt) + { + BasicBlock* pred = nullptr; + for (auto&& p : m_basicBlocks) + { + if (p.second.llvm() == *predIt) + { + pred = &p.second; + break; + } + } + + phi->addIncoming(pred->getJumpTarget(), pred->llvm()); + } + } + dumpCFGifRequired("blocks-init.dot"); if (m_options.optimizeStack) @@ -394,7 +415,8 @@ void Compiler::compileBasicBlock(BasicBlock& _basicBlock, bytes const& _bytecode value = Endianness::toBE(m_builder, value); auto bytes = m_builder.CreateBitCast(value, llvm::VectorType::get(Type::Byte, 32), "bytes"); - auto byte = m_builder.CreateExtractElement(bytes, byteNum, "byte"); + auto safeByteNum = m_builder.CreateZExt(m_builder.CreateTrunc(byteNum, m_builder.getIntNTy(5)), Type::lowPrecision); // Trim index, large values can crash + auto byte = m_builder.CreateExtractElement(bytes, safeByteNum, "byte"); value = m_builder.CreateZExt(byte, Type::Word); auto byteNumValid = m_builder.CreateICmpULT(byteNum, Constant::get(32)); @@ -564,7 +586,7 @@ void Compiler::compileBasicBlock(BasicBlock& _basicBlock, bytes const& _bytecode } else { - stack.push(target); + _basicBlock.setJumpTarget(target); m_builder.CreateBr(getJumpTableBlock()); } } @@ -580,7 +602,7 @@ void Compiler::compileBasicBlock(BasicBlock& _basicBlock, bytes const& _bytecode } else { - stack.push(target); + _basicBlock.setJumpTarget(target); m_builder.CreateCondBr(cond, getJumpTableBlock(), _nextBasicBlock); } } @@ -644,7 +666,7 @@ void Compiler::compileBasicBlock(BasicBlock& _basicBlock, bytes const& _bytecode case Instruction::EXTCODESIZE: { auto addr = stack.pop(); - auto codeRef = _ext.getExtCode(addr); + auto codeRef = _ext.extcode(addr); stack.push(codeRef.size); break; } @@ -682,7 +704,7 @@ void Compiler::compileBasicBlock(BasicBlock& _basicBlock, bytes const& _bytecode auto srcIdx = stack.pop(); auto reqBytes = stack.pop(); - auto codeRef = _ext.getExtCode(addr); + auto codeRef = _ext.extcode(addr); _memory.copyBytes(codeRef.ptr, codeRef.size, srcIdx, destMemIdx, reqBytes); break; diff --git a/evmjit/libevmjit/ExecutionEngine.cpp b/evmjit/libevmjit/ExecutionEngine.cpp index 71142fbeee..ee92a6ba9e 100644 --- a/evmjit/libevmjit/ExecutionEngine.cpp +++ b/evmjit/libevmjit/ExecutionEngine.cpp @@ -19,7 +19,12 @@ #include "Compiler.h" #include "Cache.h" -extern "C" void env_sha3(dev::eth::jit::byte const* _begin, uint64_t _size, std::array* o_hash); +#if defined(NDEBUG) +#define DEBUG_ENV_OPTION(name) false +#else +#include +#define DEBUG_ENV_OPTION(name) (std::getenv(#name) != nullptr) +#endif namespace dev { @@ -49,14 +54,17 @@ ReturnCode runEntryFunc(EntryFuncPtr _mainFunc, Runtime* _runtime) std::string codeHash(bytes const& _code) { - std::array binHash; - env_sha3(_code.data(), _code.size(), &binHash); - - std::ostringstream os; - for (auto i: binHash) - os << std::hex << std::setfill('0') << std::setw(2) << (int)(std::make_unsigned::type)i; - - return os.str(); + uint32_t hash = 0; + for (auto b : _code) + { + hash += b; + hash += (hash << 10); + hash ^= (hash >> 6); + } + hash += (hash << 3); + hash ^= (hash >> 11); + hash += (hash << 15); + return std::to_string(hash); } } @@ -64,6 +72,8 @@ std::string codeHash(bytes const& _code) ReturnCode ExecutionEngine::run(bytes const& _code, 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); EntryFuncPtr entryFuncPtr{}; @@ -74,14 +84,14 @@ ReturnCode ExecutionEngine::run(bytes const& _code, RuntimeData* _data, Env* _en } else { - bool objectCacheEnabled = true; auto objectCache = objectCacheEnabled ? Cache::getObjectCache() : nullptr; std::unique_ptr module; if (objectCache) module = Cache::getObject(mainFuncName); if (!module) module = Compiler({}).compile(_code, mainFuncName); - //module->dump(); + if (debugDumpModule) + module->dump(); if (!ee) { llvm::InitializeNativeTarget(); @@ -92,7 +102,7 @@ ReturnCode ExecutionEngine::run(bytes const& _code, RuntimeData* _data, Env* _en builder.setUseMCJIT(true); std::unique_ptr memoryManager(new llvm::SectionMemoryManager); builder.setMCJITMemoryManager(memoryManager.get()); - builder.setOptLevel(llvm::CodeGenOpt::Default); + builder.setOptLevel(llvm::CodeGenOpt::None); auto triple = llvm::Triple(llvm::sys::getProcessTriple()); if (triple.getOS() == llvm::Triple::OSType::Win32) @@ -126,7 +136,10 @@ ReturnCode ExecutionEngine::run(bytes const& _code, RuntimeData* _data, Env* _en auto returnCode = runEntryFunc(entryFuncPtr, &runtime); if (returnCode == ReturnCode::Return) - this->returnData = runtime.getReturnData(); + { + returnData = runtime.getReturnData(); // Save reference to return data + std::swap(m_memory, runtime.getMemory()); // Take ownership of memory + } auto executionEndTime = std::chrono::high_resolution_clock::now(); clog(JIT) << " + " << std::chrono::duration_cast(executionEndTime - executionStartTime).count() << " ms\n"; diff --git a/evmjit/libevmjit/ExecutionEngine.h b/evmjit/libevmjit/ExecutionEngine.h index 559701bba7..52e3b29fa6 100644 --- a/evmjit/libevmjit/ExecutionEngine.h +++ b/evmjit/libevmjit/ExecutionEngine.h @@ -18,7 +18,13 @@ public: ReturnCode run(bytes const& _code, RuntimeData* _data, Env* _env); - bytes returnData; + /// Reference to returned data (RETURN opcode used) + bytes_ref returnData; + +private: + /// After execution, if RETURN used, memory is moved there + /// to allow client copy the returned data + bytes m_memory; }; } diff --git a/evmjit/libevmjit/Ext.cpp b/evmjit/libevmjit/Ext.cpp index f0767d9e03..a37a6fe995 100644 --- a/evmjit/libevmjit/Ext.cpp +++ b/evmjit/libevmjit/Ext.cpp @@ -5,9 +5,6 @@ #include #include -//#include -//#include - #include "RuntimeManager.h" #include "Memory.h" #include "Type.h" @@ -22,10 +19,10 @@ namespace jit Ext::Ext(RuntimeManager& _runtimeManager, Memory& _memoryMan): RuntimeHelper(_runtimeManager), - m_memoryMan(_memoryMan), - m_funcs({}), // The only std::array initialization that works in both Visual Studio & GCC - m_argAllocas({}) + m_memoryMan(_memoryMan) { + m_funcs = decltype(m_funcs)(); + m_argAllocas = decltype(m_argAllocas)(); m_size = m_builder.CreateAlloca(Type::Size, nullptr, "env.size"); } @@ -48,7 +45,7 @@ std::array::value> const& getEnvFuncDescs() 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{"env_extcode", getFunctionType(Type::BytePtr, {Type::EnvPtr, Type::WordPtr, Type::Size->getPointerTo()})}, FuncDesc{"ext_calldataload", getFunctionType(Type::Void, {Type::RuntimeDataPtr, Type::WordPtr, Type::WordPtr})}, }}; @@ -71,7 +68,6 @@ llvm::Value* Ext::getArgAlloca() getBuilder().SetInsertPoint(getMainFunction()->front().getFirstNonPHI()); a = getBuilder().CreateAlloca(Type::Word, nullptr, "arg"); } - return a; } @@ -166,10 +162,10 @@ llvm::Value* Ext::sha3(llvm::Value* _inOff, llvm::Value* _inSize) return hash; } -MemoryRef Ext::getExtCode(llvm::Value* _addr) +MemoryRef Ext::extcode(llvm::Value* _addr) { auto addr = Endianness::toBE(m_builder, _addr); - auto code = createCall(EnvFunc::getExtCode, {getRuntimeManager().getEnvPtr(), byPtr(addr), m_size}); + auto code = createCall(EnvFunc::extcode, {getRuntimeManager().getEnvPtr(), byPtr(addr), m_size}); auto codeSize = m_builder.CreateLoad(m_size); auto codeSize256 = m_builder.CreateZExt(codeSize, Type::Word); return {code, codeSize256}; diff --git a/evmjit/libevmjit/Ext.h b/evmjit/libevmjit/Ext.h index 86a8a6190d..2601d32abc 100644 --- a/evmjit/libevmjit/Ext.h +++ b/evmjit/libevmjit/Ext.h @@ -34,7 +34,7 @@ enum class EnvFunc call, log, blockhash, - getExtCode, + extcode, calldataload, // Helper function, not client Env interface _size @@ -55,7 +55,7 @@ public: llvm::Value* blockhash(llvm::Value* _number); llvm::Value* sha3(llvm::Value* _inOff, llvm::Value* _inSize); - MemoryRef getExtCode(llvm::Value* _addr); + MemoryRef extcode(llvm::Value* _addr); void log(llvm::Value* _memIdx, llvm::Value* _numBytes, std::array const& _topics); diff --git a/evmjit/libevmjit/Runtime.cpp b/evmjit/libevmjit/Runtime.cpp index 911dc469d4..989c053dcc 100644 --- a/evmjit/libevmjit/Runtime.cpp +++ b/evmjit/libevmjit/Runtime.cpp @@ -18,18 +18,18 @@ Runtime::Runtime(RuntimeData* _data, Env* _env) : m_currJmpBuf(m_jmpBuf) {} -bytes Runtime::getReturnData() const // FIXME: Reconsider returning by copy +bytes_ref Runtime::getReturnData() const { // TODO: Handle large indexes - auto offset = static_cast(llvm2eth(m_data.elems[RuntimeData::ReturnDataOffset])); - auto size = static_cast(llvm2eth(m_data.elems[RuntimeData::ReturnDataSize])); + auto offset = static_cast(m_data.elems[RuntimeData::ReturnDataOffset].a); + auto size = static_cast(m_data.elems[RuntimeData::ReturnDataSize].a); assert(offset + size <= m_memory.size() || size == 0); if (offset + size > m_memory.size()) return {}; - auto dataBeg = m_memory.begin() + offset; - return {dataBeg, dataBeg + size}; + auto dataBeg = m_memory.data() + offset; + return bytes_ref{dataBeg, size}; } } diff --git a/evmjit/libevmjit/Runtime.h b/evmjit/libevmjit/Runtime.h index 8cc5b79686..df1bde7fd7 100644 --- a/evmjit/libevmjit/Runtime.h +++ b/evmjit/libevmjit/Runtime.h @@ -2,15 +2,8 @@ #pragma once #include -#include - -#include "Instruction.h" -#include "CompilerHelper.h" -#include "Utils.h" -#include "Type.h" #include "RuntimeData.h" - #ifdef _MSC_VER #define EXPORT __declspec(dllexport) #else @@ -34,13 +27,13 @@ public: Runtime(RuntimeData* _data, Env* _env); Runtime(const Runtime&) = delete; - void operator=(const Runtime&) = delete; + Runtime& operator=(const Runtime&) = delete; StackImpl& getStack() { return m_stack; } MemoryImpl& getMemory() { return m_memory; } Env* getEnvPtr() { return &m_env; } - bytes getReturnData() const; + bytes_ref getReturnData() const; jmp_buf_ref getJmpBuf() { return m_jmpBuf; } private: diff --git a/evmjit/libevmjit/RuntimeData.h b/evmjit/libevmjit/RuntimeData.h index bb52f78648..a71cd468b9 100644 --- a/evmjit/libevmjit/RuntimeData.h +++ b/evmjit/libevmjit/RuntimeData.h @@ -38,9 +38,6 @@ struct RuntimeData i256 elems[_size] = {}; byte const* callData = nullptr; byte const* code = nullptr; - - void set(Index _index, u256 _value) { elems[_index] = eth2llvm(_value); } - u256 get(Index _index) { return llvm2eth(elems[_index]); } }; /// VM Environment (ExtVM) opaque type diff --git a/evmjit/libevmjit/Utils.cpp b/evmjit/libevmjit/Utils.cpp index 0fd9c0e41c..5f7f75bfd4 100644 --- a/evmjit/libevmjit/Utils.cpp +++ b/evmjit/libevmjit/Utils.cpp @@ -8,32 +8,6 @@ namespace eth namespace jit { -u256 llvm2eth(i256 _i) -{ - u256 u = 0; - u |= _i.d; - u <<= 64; - u |= _i.c; - u <<= 64; - u |= _i.b; - u <<= 64; - u |= _i.a; - return u; -} - -i256 eth2llvm(u256 _u) -{ - i256 i; - u256 mask = 0xFFFFFFFFFFFFFFFF; - i.a = static_cast(_u & mask); - _u >>= 64; - i.b = static_cast(_u & mask); - _u >>= 64; - i.c = static_cast(_u & mask); - _u >>= 64; - i.d = static_cast(_u & mask); - return i; -} } } diff --git a/evmjit/libevmjit/Utils.h b/evmjit/libevmjit/Utils.h index f672365c6b..1e67056676 100644 --- a/evmjit/libevmjit/Utils.h +++ b/evmjit/libevmjit/Utils.h @@ -14,9 +14,6 @@ struct JIT: public NoteChannel { static const char* name() { return "JIT"; } }; //#define clog(CHANNEL) std::cerr #define clog(CHANNEL) std::ostream(nullptr) -u256 llvm2eth(i256); -i256 eth2llvm(u256); - } } } diff --git a/evmjit/libevmjit/interface.cpp b/evmjit/libevmjit/interface.cpp index eef92d00fd..5d2b97c2d2 100644 --- a/evmjit/libevmjit/interface.cpp +++ b/evmjit/libevmjit/interface.cpp @@ -1,5 +1,5 @@ #include "interface.h" -#include +#include #include "ExecutionEngine.h" extern "C" @@ -20,12 +20,12 @@ evmjit_result evmjit_run(void* _data, void* _env) auto returnCode = engine.run(bytecode, data, static_cast(_env)); evmjit_result result = {static_cast(returnCode), 0, nullptr}; - if (returnCode == ReturnCode::Return && !engine.returnData.empty()) + if (returnCode == ReturnCode::Return && std::get<0>(engine.returnData)) { // TODO: Optimized returning data. Allocating memory on client side by callback function might be a good idea - result.returnDataSize = engine.returnData.size(); + result.returnDataSize = std::get<1>(engine.returnData); result.returnData = std::malloc(result.returnDataSize); - std::memcpy(result.returnData, engine.returnData.data(), result.returnDataSize); + std::memcpy(result.returnData, std::get<0>(engine.returnData), result.returnDataSize); } return result;