mirror of
https://github.com/ethereum/go-ethereum.git
synced 2026-07-22 04:36:42 +00:00
Merge commit '3dcf30463b961e3fe47b48b2024bbf3d5b5ce675' into evmjit
Conflicts: evmjit/CMakeLists.txt evmjit/libevmjit-cpp/CMakeLists.txt evmjit/libevmjit-cpp/Env.cpp evmjit/libevmjit-cpp/JitVM.cpp evmjit/libevmjit/Arith256.cpp evmjit/libevmjit/BasicBlock.cpp evmjit/libevmjit/BasicBlock.h evmjit/libevmjit/CMakeLists.txt evmjit/libevmjit/Cache.cpp evmjit/libevmjit/Common.h evmjit/libevmjit/Compiler.cpp evmjit/libevmjit/ExecutionEngine.cpp evmjit/libevmjit/ExecutionEngine.h evmjit/libevmjit/Ext.cpp evmjit/libevmjit/Ext.h evmjit/libevmjit/Runtime.cpp evmjit/libevmjit/Runtime.h evmjit/libevmjit/RuntimeData.h evmjit/libevmjit/Utils.cpp evmjit/libevmjit/Utils.h evmjit/libevmjit/interface.cpp
This commit is contained in:
commit
3ef945a431
22 changed files with 365 additions and 179 deletions
|
|
@ -4,17 +4,18 @@ project(evmjit)
|
||||||
|
|
||||||
set_property(GLOBAL PROPERTY USE_FOLDERS ON)
|
set_property(GLOBAL PROPERTY USE_FOLDERS ON)
|
||||||
|
|
||||||
if ("${CMAKE_CXX_COMPILER_ID}" STREQUAL "MSVC")
|
if(CMAKE_CXX_COMPILER_ID STREQUAL "MSVC")
|
||||||
else ()
|
else()
|
||||||
set(CMAKE_CXX_FLAGS "-std=c++11 -Wall -Wno-unknown-pragmas -Wextra -DSHAREDLIB -fPIC")
|
set(CMAKE_CXX_FLAGS "-std=c++11 -Wall -Wno-unknown-pragmas -Wextra -fPIC")
|
||||||
endif()
|
endif()
|
||||||
|
|
||||||
if(APPLE)
|
if(CMAKE_SYSTEM_NAME STREQUAL "Linux")
|
||||||
set(CMAKE_SHARED_LINKER_FLAGS "-undefined dynamic_lookup")
|
# Do not allow unresovled symbols in shared library (default on linux)
|
||||||
|
set(CMAKE_SHARED_LINKER_FLAGS "-Wl,--no-undefined")
|
||||||
endif()
|
endif()
|
||||||
|
|
||||||
# LLVM
|
# LLVM
|
||||||
if(LLVM_DIR) # local LLVM build
|
if(LLVM_DIR OR APPLE) # local LLVM build
|
||||||
find_package(LLVM REQUIRED CONFIG)
|
find_package(LLVM REQUIRED CONFIG)
|
||||||
message(STATUS "Found LLVM ${LLVM_PACKAGE_VERSION}")
|
message(STATUS "Found LLVM ${LLVM_PACKAGE_VERSION}")
|
||||||
message(STATUS "Using LLVMConfig.cmake in: ${LLVM_DIR}")
|
message(STATUS "Using LLVMConfig.cmake in: ${LLVM_DIR}")
|
||||||
|
|
@ -28,11 +29,9 @@ else()
|
||||||
message(STATUS "LLVM include dirs: ${LLVM_INCLUDE_DIRS}")
|
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")
|
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)
|
add_definitions(-D__STDC_CONSTANT_MACROS -D__STDC_FORMAT_MACROS -D__STDC_LIMIT_MACROS)
|
||||||
|
link_directories(/usr/lib/llvm-3.5/lib)
|
||||||
endif()
|
endif()
|
||||||
|
|
||||||
# Boost
|
|
||||||
find_package(Boost REQUIRED)
|
|
||||||
|
|
||||||
add_subdirectory(libevmjit)
|
add_subdirectory(libevmjit)
|
||||||
|
|
||||||
if(EVMJIT_CPP)
|
if(EVMJIT_CPP)
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,8 @@
|
||||||
set(TARGET_NAME evmjit-cpp)
|
set(TARGET_NAME evmjit-cpp)
|
||||||
|
|
||||||
|
# Boost
|
||||||
|
find_package(Boost REQUIRED)
|
||||||
|
|
||||||
set(SOURCES
|
set(SOURCES
|
||||||
Env.cpp
|
Env.cpp
|
||||||
JitVM.cpp JitVM.h
|
JitVM.cpp JitVM.h
|
||||||
|
|
|
||||||
|
|
@ -3,7 +3,7 @@
|
||||||
#include <libevm/FeeStructure.h>
|
#include <libevm/FeeStructure.h>
|
||||||
#include <libevm/ExtVMFace.h>
|
#include <libevm/ExtVMFace.h>
|
||||||
|
|
||||||
#include <evmjit/libevmjit/Utils.h>
|
#include "Utils.h"
|
||||||
|
|
||||||
extern "C"
|
extern "C"
|
||||||
{
|
{
|
||||||
|
|
@ -16,7 +16,6 @@ extern "C"
|
||||||
using namespace dev;
|
using namespace dev;
|
||||||
using namespace dev::eth;
|
using namespace dev::eth;
|
||||||
using jit::i256;
|
using jit::i256;
|
||||||
using jit::eth2llvm;
|
|
||||||
|
|
||||||
EXPORT void env_sload(ExtVMFace* _env, i256* _index, i256* o_value)
|
EXPORT void env_sload(ExtVMFace* _env, i256* _index, i256* o_value)
|
||||||
{
|
{
|
||||||
|
|
@ -89,7 +88,7 @@ extern "C"
|
||||||
*o_hash = hash;
|
*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 addr = right160(*_addr256);
|
||||||
auto& code = _env->codeAt(addr);
|
auto& code = _env->codeAt(addr);
|
||||||
|
|
|
||||||
|
|
@ -2,7 +2,7 @@
|
||||||
#include "JitVM.h"
|
#include "JitVM.h"
|
||||||
#include <libevm/VM.h>
|
#include <libevm/VM.h>
|
||||||
#include <evmjit/libevmjit/ExecutionEngine.h>
|
#include <evmjit/libevmjit/ExecutionEngine.h>
|
||||||
#include <evmjit/libevmjit/Utils.h>
|
#include "Utils.h"
|
||||||
|
|
||||||
namespace dev
|
namespace dev
|
||||||
{
|
{
|
||||||
|
|
@ -13,28 +13,28 @@ bytesConstRef JitVM::go(ExtVMFace& _ext, OnOpFunc const&, uint64_t)
|
||||||
{
|
{
|
||||||
using namespace jit;
|
using namespace jit;
|
||||||
|
|
||||||
m_data.set(RuntimeData::Gas, m_gas);
|
m_data.elems[RuntimeData::Gas] = eth2llvm(m_gas);
|
||||||
m_data.set(RuntimeData::Address, fromAddress(_ext.myAddress));
|
m_data.elems[RuntimeData::Address] = eth2llvm(fromAddress(_ext.myAddress));
|
||||||
m_data.set(RuntimeData::Caller, fromAddress(_ext.caller));
|
m_data.elems[RuntimeData::Caller] = eth2llvm(fromAddress(_ext.caller));
|
||||||
m_data.set(RuntimeData::Origin, fromAddress(_ext.origin));
|
m_data.elems[RuntimeData::Origin] = eth2llvm(fromAddress(_ext.origin));
|
||||||
m_data.set(RuntimeData::CallValue, _ext.value);
|
m_data.elems[RuntimeData::CallValue] = eth2llvm(_ext.value);
|
||||||
m_data.set(RuntimeData::CallDataSize, _ext.data.size());
|
m_data.elems[RuntimeData::CallDataSize] = eth2llvm(_ext.data.size());
|
||||||
m_data.set(RuntimeData::GasPrice, _ext.gasPrice);
|
m_data.elems[RuntimeData::GasPrice] = eth2llvm(_ext.gasPrice);
|
||||||
m_data.set(RuntimeData::CoinBase, fromAddress(_ext.currentBlock.coinbaseAddress));
|
m_data.elems[RuntimeData::CoinBase] = eth2llvm(fromAddress(_ext.currentBlock.coinbaseAddress));
|
||||||
m_data.set(RuntimeData::TimeStamp, _ext.currentBlock.timestamp);
|
m_data.elems[RuntimeData::TimeStamp] = eth2llvm(_ext.currentBlock.timestamp);
|
||||||
m_data.set(RuntimeData::Number, _ext.currentBlock.number);
|
m_data.elems[RuntimeData::Number] = eth2llvm(_ext.currentBlock.number);
|
||||||
m_data.set(RuntimeData::Difficulty, _ext.currentBlock.difficulty);
|
m_data.elems[RuntimeData::Difficulty] = eth2llvm(_ext.currentBlock.difficulty);
|
||||||
m_data.set(RuntimeData::GasLimit, _ext.currentBlock.gasLimit);
|
m_data.elems[RuntimeData::GasLimit] = eth2llvm(_ext.currentBlock.gasLimit);
|
||||||
m_data.set(RuntimeData::CodeSize, _ext.code.size());
|
m_data.elems[RuntimeData::CodeSize] = eth2llvm(_ext.code.size());
|
||||||
m_data.callData = _ext.data.data();
|
m_data.callData = _ext.data.data();
|
||||||
m_data.code = _ext.code.data();
|
m_data.code = _ext.code.data();
|
||||||
|
|
||||||
auto env = reinterpret_cast<Env*>(&_ext);
|
auto env = reinterpret_cast<Env*>(&_ext);
|
||||||
auto exitCode = m_engine.run(_ext.code, &m_data, env);
|
auto exitCode = m_engine.run(_ext.code, &m_data, env);
|
||||||
switch (exitCode)
|
switch (exitCode)
|
||||||
{
|
{
|
||||||
case ReturnCode::Suicide:
|
case ReturnCode::Suicide:
|
||||||
_ext.suicide(right160(m_data.get(RuntimeData::SuicideDestAddress)));
|
_ext.suicide(right160(llvm2eth(m_data.elems[RuntimeData::SuicideDestAddress])));
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case ReturnCode::BadJumpDestination:
|
case ReturnCode::BadJumpDestination:
|
||||||
|
|
@ -50,7 +50,7 @@ bytesConstRef JitVM::go(ExtVMFace& _ext, OnOpFunc const&, uint64_t)
|
||||||
}
|
}
|
||||||
|
|
||||||
m_gas = llvm2eth(m_data.elems[RuntimeData::Gas]);
|
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)};
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
||||||
38
evmjit/libevmjit-cpp/Utils.h
Normal file
38
evmjit/libevmjit-cpp/Utils.h
Normal file
|
|
@ -0,0 +1,38 @@
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <evmjit/libevmjit/Common.h>
|
||||||
|
|
||||||
|
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<uint64_t>(_u & mask);
|
||||||
|
_u >>= 64;
|
||||||
|
i.b = static_cast<uint64_t>(_u & mask);
|
||||||
|
_u >>= 64;
|
||||||
|
i.c = static_cast<uint64_t>(_u & mask);
|
||||||
|
_u >>= 64;
|
||||||
|
i.d = static_cast<uint64_t>(_u & mask);
|
||||||
|
return i;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -1,8 +1,10 @@
|
||||||
#include "Arith256.h"
|
#include "Arith256.h"
|
||||||
#include "Runtime.h"
|
#include "Runtime.h"
|
||||||
#include "Type.h"
|
#include "Type.h"
|
||||||
|
#include "Endianness.h"
|
||||||
|
|
||||||
#include <llvm/IR/Function.h>
|
#include <llvm/IR/Function.h>
|
||||||
|
#include <gmp.h>
|
||||||
|
|
||||||
namespace dev
|
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)
|
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);
|
return binaryOp(m_div, _arg1, _arg2);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -98,25 +102,104 @@ llvm::Value* Arith256::mulmod(llvm::Value* _arg1, llvm::Value* _arg2, llvm::Valu
|
||||||
|
|
||||||
namespace
|
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;
|
uint64_t lo;
|
||||||
static const u256 c_send = (u256)1 << 255;
|
uint64_t mid;
|
||||||
if (_u < c_send)
|
uint128 hi;
|
||||||
return (s256)_u;
|
};
|
||||||
else
|
|
||||||
return (s256)-(c_end - _u);
|
// 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;
|
return _n->a == 0 && _n->b == 0 && _n->c == 0 && _n->d == 0;
|
||||||
if (_u >= 0)
|
}
|
||||||
return (u256)_u;
|
|
||||||
else
|
const auto nLimbs = sizeof(i256) / sizeof(mp_limb_t);
|
||||||
return (u256)(c_end + _u);
|
|
||||||
|
// 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<std::make_signed<mp_limb_t>::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;
|
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);
|
*o_result = mul(*_arg1, *_arg2);
|
||||||
auto arg2 = llvm2eth(*_arg2);
|
|
||||||
*o_result = eth2llvm(arg1 * arg2);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
EXPORT void arith_div(i256* _arg1, i256* _arg2, i256* o_result)
|
EXPORT void arith_div(i256* _arg1, i256* _arg2, i256* o_result)
|
||||||
{
|
{
|
||||||
auto arg1 = llvm2eth(*_arg1);
|
*o_result = {};
|
||||||
auto arg2 = llvm2eth(*_arg2);
|
if (isZero(_arg2))
|
||||||
*o_result = eth2llvm(arg2 == 0 ? arg2 : arg1 / arg2);
|
return;
|
||||||
|
|
||||||
|
mpz_t x{nLimbs, countLimbs(_arg1), reinterpret_cast<mp_limb_t*>(_arg1)};
|
||||||
|
mpz_t y{nLimbs, countLimbs(_arg2), reinterpret_cast<mp_limb_t*>(_arg2)};
|
||||||
|
mpz_t z{nLimbs, 0, reinterpret_cast<mp_limb_t*>(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)
|
EXPORT void arith_mod(i256* _arg1, i256* _arg2, i256* o_result)
|
||||||
{
|
{
|
||||||
auto arg1 = llvm2eth(*_arg1);
|
*o_result = {};
|
||||||
auto arg2 = llvm2eth(*_arg2);
|
if (isZero(_arg2))
|
||||||
*o_result = eth2llvm(arg2 == 0 ? arg2 : arg1 % arg2);
|
return;
|
||||||
|
|
||||||
|
mpz_t x{nLimbs, countLimbs(_arg1), reinterpret_cast<mp_limb_t*>(_arg1)};
|
||||||
|
mpz_t y{nLimbs, countLimbs(_arg2), reinterpret_cast<mp_limb_t*>(_arg2)};
|
||||||
|
mpz_t z{nLimbs, 0, reinterpret_cast<mp_limb_t*>(o_result)};
|
||||||
|
|
||||||
|
mpz_tdiv_r(z, x, y);
|
||||||
}
|
}
|
||||||
|
|
||||||
EXPORT void arith_sdiv(i256* _arg1, i256* _arg2, i256* o_result)
|
EXPORT void arith_sdiv(i256* _arg1, i256* _arg2, i256* o_result)
|
||||||
{
|
{
|
||||||
auto arg1 = llvm2eth(*_arg1);
|
*o_result = {};
|
||||||
auto arg2 = llvm2eth(*_arg2);
|
if (isZero(_arg2))
|
||||||
*o_result = eth2llvm(arg2 == 0 ? arg2 : s2u(u2s(arg1) / u2s(arg2)));
|
return;
|
||||||
|
|
||||||
|
mpz_t x{nLimbs, countLimbs(_arg1), reinterpret_cast<mp_limb_t*>(_arg1)};
|
||||||
|
mpz_t y{nLimbs, countLimbs(_arg2), reinterpret_cast<mp_limb_t*>(_arg2)};
|
||||||
|
mpz_t z{nLimbs, 0, reinterpret_cast<mp_limb_t*>(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)
|
EXPORT void arith_smod(i256* _arg1, i256* _arg2, i256* o_result)
|
||||||
{
|
{
|
||||||
auto arg1 = llvm2eth(*_arg1);
|
*o_result = {};
|
||||||
auto arg2 = llvm2eth(*_arg2);
|
if (isZero(_arg2))
|
||||||
*o_result = eth2llvm(arg2 == 0 ? arg2 : s2u(u2s(arg1) % u2s(arg2)));
|
return;
|
||||||
|
|
||||||
|
mpz_t x{nLimbs, countLimbs(_arg1), reinterpret_cast<mp_limb_t*>(_arg1)};
|
||||||
|
mpz_t y{nLimbs, countLimbs(_arg2), reinterpret_cast<mp_limb_t*>(_arg2)};
|
||||||
|
mpz_t z{nLimbs, 0, reinterpret_cast<mp_limb_t*>(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)
|
EXPORT void arith_exp(i256* _arg1, i256* _arg2, i256* o_result)
|
||||||
{
|
{
|
||||||
bigint left = llvm2eth(*_arg1);
|
*o_result = {};
|
||||||
bigint right = llvm2eth(*_arg2);
|
|
||||||
auto ret = static_cast<u256>(boost::multiprecision::powm(left, right, bigint(2) << 256));
|
static mp_limb_t mod_limbs[nLimbs + 1] = {};
|
||||||
*o_result = eth2llvm(ret);
|
mod_limbs[nLimbs] = 1;
|
||||||
|
static const mpz_t mod{nLimbs + 1, nLimbs + 1, &mod_limbs[0]};
|
||||||
|
|
||||||
|
mpz_t x{nLimbs, countLimbs(_arg1), reinterpret_cast<mp_limb_t*>(_arg1)};
|
||||||
|
mpz_t y{nLimbs, countLimbs(_arg2), reinterpret_cast<mp_limb_t*>(_arg2)};
|
||||||
|
mpz_t z{nLimbs, 0, reinterpret_cast<mp_limb_t*>(o_result)};
|
||||||
|
|
||||||
|
mpz_powm(z, x, y, mod);
|
||||||
}
|
}
|
||||||
|
|
||||||
EXPORT void arith_mulmod(i256* _arg1, i256* _arg2, i256* _arg3, i256* o_result)
|
EXPORT void arith_mulmod(i256* _arg1, i256* _arg2, i256* _arg3, i256* o_result)
|
||||||
{
|
{
|
||||||
auto arg1 = llvm2eth(*_arg1);
|
*o_result = {};
|
||||||
auto arg2 = llvm2eth(*_arg2);
|
if (isZero(_arg3))
|
||||||
auto arg3 = llvm2eth(*_arg3);
|
return;
|
||||||
if (arg3 != 0)
|
|
||||||
*o_result = eth2llvm(u256((bigint(arg1) * bigint(arg2)) % arg3));
|
mpz_t x{nLimbs, countLimbs(_arg1), reinterpret_cast<mp_limb_t*>(_arg1)};
|
||||||
else
|
mpz_t y{nLimbs, countLimbs(_arg2), reinterpret_cast<mp_limb_t*>(_arg2)};
|
||||||
*o_result = {};
|
mpz_t m{nLimbs, countLimbs(_arg3), reinterpret_cast<mp_limb_t*>(_arg3)};
|
||||||
|
mpz_t z{nLimbs, 0, reinterpret_cast<mp_limb_t*>(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)
|
EXPORT void arith_addmod(i256* _arg1, i256* _arg2, i256* _arg3, i256* o_result)
|
||||||
{
|
{
|
||||||
auto arg1 = llvm2eth(*_arg1);
|
*o_result = {};
|
||||||
auto arg2 = llvm2eth(*_arg2);
|
if (isZero(_arg3))
|
||||||
auto arg3 = llvm2eth(*_arg3);
|
return;
|
||||||
if (arg3 != 0)
|
|
||||||
*o_result = eth2llvm(u256((bigint(arg1) + bigint(arg2)) % arg3));
|
mpz_t x{nLimbs, countLimbs(_arg1), reinterpret_cast<mp_limb_t*>(_arg1)};
|
||||||
else
|
mpz_t y{nLimbs, countLimbs(_arg2), reinterpret_cast<mp_limb_t*>(_arg2)};
|
||||||
*o_result = {};
|
mpz_t m{nLimbs, countLimbs(_arg3), reinterpret_cast<mp_limb_t*>(_arg3)};
|
||||||
|
mpz_t z{nLimbs, 0, reinterpret_cast<mp_limb_t*>(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);
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -168,16 +168,13 @@ void BasicBlock::synchronizeLocalStack(Stack& _evmStack)
|
||||||
if (val == nullptr)
|
if (val == nullptr)
|
||||||
continue;
|
continue;
|
||||||
|
|
||||||
assert(llvm::isa<llvm::PHINode>(val));
|
|
||||||
llvm::PHINode* phi = llvm::cast<llvm::PHINode>(val);
|
llvm::PHINode* phi = llvm::cast<llvm::PHINode>(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.
|
||||||
// Insert call to get() just before the PHI node and replace
|
m_builder.SetInsertPoint(phi);
|
||||||
// the uses of PHI with the uses of this new instruction.
|
auto newVal = _evmStack.get(idx); // OPT: Value may be never user but we need to check stack heigth
|
||||||
m_builder.SetInsertPoint(phi);
|
// It is probably a good idea to keep heigth as a local variable accesible by LLVM directly
|
||||||
auto newVal = _evmStack.get(idx);
|
phi->replaceAllUsesWith(newVal);
|
||||||
phi->replaceAllUsesWith(newVal);
|
|
||||||
}
|
|
||||||
phi->eraseFromParent();
|
phi->eraseFromParent();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -57,7 +57,7 @@ public:
|
||||||
explicit BasicBlock(std::string _name, 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;
|
BasicBlock(const BasicBlock&) = delete;
|
||||||
void operator=(const BasicBlock&) = delete;
|
BasicBlock& operator=(const BasicBlock&) = delete;
|
||||||
|
|
||||||
llvm::BasicBlock* llvm() { return m_llvmBB; }
|
llvm::BasicBlock* llvm() { return m_llvmBB; }
|
||||||
|
|
||||||
|
|
@ -66,6 +66,9 @@ public:
|
||||||
|
|
||||||
bool isJumpDest() const { return m_isJumpDest; }
|
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; }
|
LocalStack& localStack() { return m_stack; }
|
||||||
|
|
||||||
/// Optimization: propagates values between local stacks in basic blocks
|
/// Optimization: propagates values between local stacks in basic blocks
|
||||||
|
|
@ -112,6 +115,9 @@ private:
|
||||||
/// Is the basic block a valid jump destination.
|
/// Is the basic block a valid jump destination.
|
||||||
/// JUMPDEST is the first instruction of the basic block.
|
/// JUMPDEST is the first instruction of the basic block.
|
||||||
bool const m_isJumpDest = false;
|
bool const m_isJumpDest = false;
|
||||||
|
|
||||||
|
/// If block finishes with dynamic jump target index is stored here
|
||||||
|
llvm::Value* m_jumpTarget = nullptr;
|
||||||
};
|
};
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -2,24 +2,24 @@ set(TARGET_NAME evmjit)
|
||||||
|
|
||||||
file(GLOB SOURCES "*.cpp")
|
file(GLOB SOURCES "*.cpp")
|
||||||
file(GLOB HEADERS "*.h")
|
file(GLOB HEADERS "*.h")
|
||||||
|
set(INTERFACE_HEADERS interface.h)
|
||||||
source_group("" FILES ${HEADERS})
|
source_group("" FILES ${HEADERS})
|
||||||
source_group("" FILES ${SOURCES})
|
source_group("" FILES ${SOURCES})
|
||||||
|
|
||||||
if (NOT "${CMAKE_CXX_COMPILER_ID}" STREQUAL "MSVC")
|
if (NOT "${CMAKE_CXX_COMPILER_ID}" STREQUAL "MSVC")
|
||||||
# Disable rtti for Cache as LLVM has no rtti
|
# Disable rtti for Cache as LLVM has no rtti
|
||||||
set_source_files_properties(Cache.cpp PROPERTIES COMPILE_FLAGS -fno-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})
|
add_library(${TARGET_NAME} SHARED ${SOURCES} ${HEADERS})
|
||||||
set_property(TARGET ${TARGET_NAME} PROPERTY FOLDER "libs")
|
set_property(TARGET ${TARGET_NAME} PROPERTY FOLDER "libs")
|
||||||
|
|
||||||
include_directories(${LLVM_INCLUDE_DIRS})
|
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")
|
#set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -fno-rtti")
|
||||||
|
|
||||||
#install( TARGETS ${EXECUTABLE} ARCHIVE DESTINATION lib LIBRARY DESTINATION lib )
|
install(TARGETS ${TARGET_NAME} LIBRARY DESTINATION lib)
|
||||||
#install( FILES ${HEADERS} DESTINATION include/${EXECUTABLE} )
|
install(FILES ${INTERFACE_HEADERS} DESTINATION include/${TARGET_NAME})
|
||||||
|
|
|
||||||
|
|
@ -36,6 +36,20 @@ std::unique_ptr<llvm::Module> Cache::getObject(std::string const& id)
|
||||||
llvm::sys::path::system_temp_directory(false, cachePath);
|
llvm::sys::path::system_temp_directory(false, cachePath);
|
||||||
llvm::sys::path::append(cachePath, "evm_objs", id);
|
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))
|
if (auto r = llvm::MemoryBuffer::getFile(cachePath.str(), -1, false))
|
||||||
lastObject = llvm::MemoryBuffer::getMemBufferCopy(r.get()->getBuffer());
|
lastObject = llvm::MemoryBuffer::getMemBufferCopy(r.get()->getBuffer());
|
||||||
else if (r.getError() != std::make_error_code(std::errc::no_such_file_or_directory))
|
else if (r.getError() != std::make_error_code(std::errc::no_such_file_or_directory))
|
||||||
|
|
|
||||||
|
|
@ -1,7 +1,7 @@
|
||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
#include <vector>
|
#include <vector>
|
||||||
#include <boost/multiprecision/cpp_int.hpp>
|
#include <tuple>
|
||||||
|
|
||||||
namespace dev
|
namespace dev
|
||||||
{
|
{
|
||||||
|
|
@ -12,8 +12,7 @@ namespace jit
|
||||||
|
|
||||||
using byte = uint8_t;
|
using byte = uint8_t;
|
||||||
using bytes = std::vector<byte>;
|
using bytes = std::vector<byte>;
|
||||||
using u256 = boost::multiprecision::uint256_t;
|
using bytes_ref = std::tuple<byte const*, size_t>;
|
||||||
using bigint = boost::multiprecision::cpp_int;
|
|
||||||
|
|
||||||
struct NoteChannel {}; // FIXME: Use some log library?
|
struct NoteChannel {}; // FIXME: Use some log library?
|
||||||
|
|
||||||
|
|
@ -34,7 +33,6 @@ enum class ReturnCode
|
||||||
};
|
};
|
||||||
|
|
||||||
/// Representation of 256-bit value binary compatible with LLVM i256
|
/// Representation of 256-bit value binary compatible with LLVM i256
|
||||||
// TODO: Replace with h256
|
|
||||||
struct i256
|
struct i256
|
||||||
{
|
{
|
||||||
uint64_t a = 0;
|
uint64_t a = 0;
|
||||||
|
|
|
||||||
|
|
@ -4,6 +4,7 @@
|
||||||
#include <functional>
|
#include <functional>
|
||||||
#include <fstream>
|
#include <fstream>
|
||||||
#include <chrono>
|
#include <chrono>
|
||||||
|
#include <sstream>
|
||||||
|
|
||||||
#include <llvm/ADT/PostOrderIterator.h>
|
#include <llvm/ADT/PostOrderIterator.h>
|
||||||
#include <llvm/IR/CFG.h>
|
#include <llvm/IR/CFG.h>
|
||||||
|
|
@ -100,7 +101,7 @@ llvm::BasicBlock* Compiler::getJumpTableBlock()
|
||||||
m_jumpTableBlock.reset(new BasicBlock("JumpTable", m_mainFunc, m_builder, true));
|
m_jumpTableBlock.reset(new BasicBlock("JumpTable", m_mainFunc, m_builder, true));
|
||||||
InsertPointGuard g{m_builder};
|
InsertPointGuard g{m_builder};
|
||||||
m_builder.SetInsertPoint(m_jumpTableBlock->llvm());
|
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());
|
auto switchInstr = m_builder.CreateSwitch(dest, getBadJumpBlock());
|
||||||
for (auto&& p : m_basicBlocks)
|
for (auto&& p : m_basicBlocks)
|
||||||
{
|
{
|
||||||
|
|
@ -165,6 +166,26 @@ std::unique_ptr<llvm::Module> Compiler::compile(bytes const& _bytecode, std::str
|
||||||
|
|
||||||
removeDeadBlocks();
|
removeDeadBlocks();
|
||||||
|
|
||||||
|
// Link jump table target index
|
||||||
|
if (m_jumpTableBlock)
|
||||||
|
{
|
||||||
|
auto phi = llvm::cast<llvm::PHINode>(&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");
|
dumpCFGifRequired("blocks-init.dot");
|
||||||
|
|
||||||
if (m_options.optimizeStack)
|
if (m_options.optimizeStack)
|
||||||
|
|
@ -394,7 +415,8 @@ void Compiler::compileBasicBlock(BasicBlock& _basicBlock, bytes const& _bytecode
|
||||||
|
|
||||||
value = Endianness::toBE(m_builder, value);
|
value = Endianness::toBE(m_builder, value);
|
||||||
auto bytes = m_builder.CreateBitCast(value, llvm::VectorType::get(Type::Byte, 32), "bytes");
|
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);
|
value = m_builder.CreateZExt(byte, Type::Word);
|
||||||
|
|
||||||
auto byteNumValid = m_builder.CreateICmpULT(byteNum, Constant::get(32));
|
auto byteNumValid = m_builder.CreateICmpULT(byteNum, Constant::get(32));
|
||||||
|
|
@ -564,7 +586,7 @@ void Compiler::compileBasicBlock(BasicBlock& _basicBlock, bytes const& _bytecode
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
stack.push(target);
|
_basicBlock.setJumpTarget(target);
|
||||||
m_builder.CreateBr(getJumpTableBlock());
|
m_builder.CreateBr(getJumpTableBlock());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -580,7 +602,7 @@ void Compiler::compileBasicBlock(BasicBlock& _basicBlock, bytes const& _bytecode
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
stack.push(target);
|
_basicBlock.setJumpTarget(target);
|
||||||
m_builder.CreateCondBr(cond, getJumpTableBlock(), _nextBasicBlock);
|
m_builder.CreateCondBr(cond, getJumpTableBlock(), _nextBasicBlock);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -644,7 +666,7 @@ void Compiler::compileBasicBlock(BasicBlock& _basicBlock, bytes const& _bytecode
|
||||||
case Instruction::EXTCODESIZE:
|
case Instruction::EXTCODESIZE:
|
||||||
{
|
{
|
||||||
auto addr = stack.pop();
|
auto addr = stack.pop();
|
||||||
auto codeRef = _ext.getExtCode(addr);
|
auto codeRef = _ext.extcode(addr);
|
||||||
stack.push(codeRef.size);
|
stack.push(codeRef.size);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
@ -682,7 +704,7 @@ void Compiler::compileBasicBlock(BasicBlock& _basicBlock, bytes const& _bytecode
|
||||||
auto srcIdx = stack.pop();
|
auto srcIdx = stack.pop();
|
||||||
auto reqBytes = 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);
|
_memory.copyBytes(codeRef.ptr, codeRef.size, srcIdx, destMemIdx, reqBytes);
|
||||||
break;
|
break;
|
||||||
|
|
|
||||||
|
|
@ -19,7 +19,12 @@
|
||||||
#include "Compiler.h"
|
#include "Compiler.h"
|
||||||
#include "Cache.h"
|
#include "Cache.h"
|
||||||
|
|
||||||
extern "C" void env_sha3(dev::eth::jit::byte const* _begin, uint64_t _size, std::array<dev::eth::jit::byte, 32>* o_hash);
|
#if defined(NDEBUG)
|
||||||
|
#define DEBUG_ENV_OPTION(name) false
|
||||||
|
#else
|
||||||
|
#include <cstdlib>
|
||||||
|
#define DEBUG_ENV_OPTION(name) (std::getenv(#name) != nullptr)
|
||||||
|
#endif
|
||||||
|
|
||||||
namespace dev
|
namespace dev
|
||||||
{
|
{
|
||||||
|
|
@ -49,14 +54,17 @@ ReturnCode runEntryFunc(EntryFuncPtr _mainFunc, Runtime* _runtime)
|
||||||
|
|
||||||
std::string codeHash(bytes const& _code)
|
std::string codeHash(bytes const& _code)
|
||||||
{
|
{
|
||||||
std::array<byte, 32> binHash;
|
uint32_t hash = 0;
|
||||||
env_sha3(_code.data(), _code.size(), &binHash);
|
for (auto b : _code)
|
||||||
|
{
|
||||||
std::ostringstream os;
|
hash += b;
|
||||||
for (auto i: binHash)
|
hash += (hash << 10);
|
||||||
os << std::hex << std::setfill('0') << std::setw(2) << (int)(std::make_unsigned<decltype(i)>::type)i;
|
hash ^= (hash >> 6);
|
||||||
|
}
|
||||||
return os.str();
|
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)
|
ReturnCode ExecutionEngine::run(bytes const& _code, RuntimeData* _data, Env* _env)
|
||||||
{
|
{
|
||||||
static std::unique_ptr<llvm::ExecutionEngine> ee; // TODO: Use Managed Objects from LLVM?
|
static std::unique_ptr<llvm::ExecutionEngine> 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 mainFuncName = codeHash(_code);
|
||||||
EntryFuncPtr entryFuncPtr{};
|
EntryFuncPtr entryFuncPtr{};
|
||||||
|
|
@ -74,14 +84,14 @@ ReturnCode ExecutionEngine::run(bytes const& _code, RuntimeData* _data, Env* _en
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
bool objectCacheEnabled = true;
|
|
||||||
auto objectCache = objectCacheEnabled ? Cache::getObjectCache() : nullptr;
|
auto objectCache = objectCacheEnabled ? Cache::getObjectCache() : nullptr;
|
||||||
std::unique_ptr<llvm::Module> module;
|
std::unique_ptr<llvm::Module> module;
|
||||||
if (objectCache)
|
if (objectCache)
|
||||||
module = Cache::getObject(mainFuncName);
|
module = Cache::getObject(mainFuncName);
|
||||||
if (!module)
|
if (!module)
|
||||||
module = Compiler({}).compile(_code, mainFuncName);
|
module = Compiler({}).compile(_code, mainFuncName);
|
||||||
//module->dump();
|
if (debugDumpModule)
|
||||||
|
module->dump();
|
||||||
if (!ee)
|
if (!ee)
|
||||||
{
|
{
|
||||||
llvm::InitializeNativeTarget();
|
llvm::InitializeNativeTarget();
|
||||||
|
|
@ -92,7 +102,7 @@ ReturnCode ExecutionEngine::run(bytes const& _code, RuntimeData* _data, Env* _en
|
||||||
builder.setUseMCJIT(true);
|
builder.setUseMCJIT(true);
|
||||||
std::unique_ptr<llvm::SectionMemoryManager> memoryManager(new llvm::SectionMemoryManager);
|
std::unique_ptr<llvm::SectionMemoryManager> memoryManager(new llvm::SectionMemoryManager);
|
||||||
builder.setMCJITMemoryManager(memoryManager.get());
|
builder.setMCJITMemoryManager(memoryManager.get());
|
||||||
builder.setOptLevel(llvm::CodeGenOpt::Default);
|
builder.setOptLevel(llvm::CodeGenOpt::None);
|
||||||
|
|
||||||
auto triple = llvm::Triple(llvm::sys::getProcessTriple());
|
auto triple = llvm::Triple(llvm::sys::getProcessTriple());
|
||||||
if (triple.getOS() == llvm::Triple::OSType::Win32)
|
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);
|
auto returnCode = runEntryFunc(entryFuncPtr, &runtime);
|
||||||
if (returnCode == ReturnCode::Return)
|
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();
|
auto executionEndTime = std::chrono::high_resolution_clock::now();
|
||||||
clog(JIT) << " + " << std::chrono::duration_cast<std::chrono::milliseconds>(executionEndTime - executionStartTime).count() << " ms\n";
|
clog(JIT) << " + " << std::chrono::duration_cast<std::chrono::milliseconds>(executionEndTime - executionStartTime).count() << " ms\n";
|
||||||
|
|
|
||||||
|
|
@ -18,7 +18,13 @@ public:
|
||||||
|
|
||||||
ReturnCode run(bytes const& _code, RuntimeData* _data, Env* _env);
|
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;
|
||||||
};
|
};
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -5,9 +5,6 @@
|
||||||
#include <llvm/IR/TypeBuilder.h>
|
#include <llvm/IR/TypeBuilder.h>
|
||||||
#include <llvm/IR/IntrinsicInst.h>
|
#include <llvm/IR/IntrinsicInst.h>
|
||||||
|
|
||||||
//#include <libdevcrypto/SHA3.h>
|
|
||||||
//#include <libevm/FeeStructure.h>
|
|
||||||
|
|
||||||
#include "RuntimeManager.h"
|
#include "RuntimeManager.h"
|
||||||
#include "Memory.h"
|
#include "Memory.h"
|
||||||
#include "Type.h"
|
#include "Type.h"
|
||||||
|
|
@ -22,10 +19,10 @@ namespace jit
|
||||||
|
|
||||||
Ext::Ext(RuntimeManager& _runtimeManager, Memory& _memoryMan):
|
Ext::Ext(RuntimeManager& _runtimeManager, Memory& _memoryMan):
|
||||||
RuntimeHelper(_runtimeManager),
|
RuntimeHelper(_runtimeManager),
|
||||||
m_memoryMan(_memoryMan),
|
m_memoryMan(_memoryMan)
|
||||||
m_funcs({}), // The only std::array initialization that works in both Visual Studio & GCC
|
|
||||||
m_argAllocas({})
|
|
||||||
{
|
{
|
||||||
|
m_funcs = decltype(m_funcs)();
|
||||||
|
m_argAllocas = decltype(m_argAllocas)();
|
||||||
m_size = m_builder.CreateAlloca(Type::Size, nullptr, "env.size");
|
m_size = m_builder.CreateAlloca(Type::Size, nullptr, "env.size");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -48,7 +45,7 @@ std::array<FuncDesc, sizeOf<EnvFunc>::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_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_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_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})},
|
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());
|
getBuilder().SetInsertPoint(getMainFunction()->front().getFirstNonPHI());
|
||||||
a = getBuilder().CreateAlloca(Type::Word, nullptr, "arg");
|
a = getBuilder().CreateAlloca(Type::Word, nullptr, "arg");
|
||||||
}
|
}
|
||||||
|
|
||||||
return a;
|
return a;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -166,10 +162,10 @@ llvm::Value* Ext::sha3(llvm::Value* _inOff, llvm::Value* _inSize)
|
||||||
return hash;
|
return hash;
|
||||||
}
|
}
|
||||||
|
|
||||||
MemoryRef Ext::getExtCode(llvm::Value* _addr)
|
MemoryRef Ext::extcode(llvm::Value* _addr)
|
||||||
{
|
{
|
||||||
auto addr = Endianness::toBE(m_builder, _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 codeSize = m_builder.CreateLoad(m_size);
|
||||||
auto codeSize256 = m_builder.CreateZExt(codeSize, Type::Word);
|
auto codeSize256 = m_builder.CreateZExt(codeSize, Type::Word);
|
||||||
return {code, codeSize256};
|
return {code, codeSize256};
|
||||||
|
|
|
||||||
|
|
@ -34,7 +34,7 @@ enum class EnvFunc
|
||||||
call,
|
call,
|
||||||
log,
|
log,
|
||||||
blockhash,
|
blockhash,
|
||||||
getExtCode,
|
extcode,
|
||||||
calldataload, // Helper function, not client Env interface
|
calldataload, // Helper function, not client Env interface
|
||||||
|
|
||||||
_size
|
_size
|
||||||
|
|
@ -55,7 +55,7 @@ public:
|
||||||
llvm::Value* blockhash(llvm::Value* _number);
|
llvm::Value* blockhash(llvm::Value* _number);
|
||||||
|
|
||||||
llvm::Value* sha3(llvm::Value* _inOff, llvm::Value* _inSize);
|
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<llvm::Value*,4> const& _topics);
|
void log(llvm::Value* _memIdx, llvm::Value* _numBytes, std::array<llvm::Value*,4> const& _topics);
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -18,18 +18,18 @@ Runtime::Runtime(RuntimeData* _data, Env* _env) :
|
||||||
m_currJmpBuf(m_jmpBuf)
|
m_currJmpBuf(m_jmpBuf)
|
||||||
{}
|
{}
|
||||||
|
|
||||||
bytes Runtime::getReturnData() const // FIXME: Reconsider returning by copy
|
bytes_ref Runtime::getReturnData() const
|
||||||
{
|
{
|
||||||
// TODO: Handle large indexes
|
// TODO: Handle large indexes
|
||||||
auto offset = static_cast<size_t>(llvm2eth(m_data.elems[RuntimeData::ReturnDataOffset]));
|
auto offset = static_cast<size_t>(m_data.elems[RuntimeData::ReturnDataOffset].a);
|
||||||
auto size = static_cast<size_t>(llvm2eth(m_data.elems[RuntimeData::ReturnDataSize]));
|
auto size = static_cast<size_t>(m_data.elems[RuntimeData::ReturnDataSize].a);
|
||||||
|
|
||||||
assert(offset + size <= m_memory.size() || size == 0);
|
assert(offset + size <= m_memory.size() || size == 0);
|
||||||
if (offset + size > m_memory.size())
|
if (offset + size > m_memory.size())
|
||||||
return {};
|
return {};
|
||||||
|
|
||||||
auto dataBeg = m_memory.begin() + offset;
|
auto dataBeg = m_memory.data() + offset;
|
||||||
return {dataBeg, dataBeg + size};
|
return bytes_ref{dataBeg, size};
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -2,15 +2,8 @@
|
||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
#include <csetjmp>
|
#include <csetjmp>
|
||||||
#include <vector>
|
|
||||||
|
|
||||||
#include "Instruction.h"
|
|
||||||
#include "CompilerHelper.h"
|
|
||||||
#include "Utils.h"
|
|
||||||
#include "Type.h"
|
|
||||||
#include "RuntimeData.h"
|
#include "RuntimeData.h"
|
||||||
|
|
||||||
|
|
||||||
#ifdef _MSC_VER
|
#ifdef _MSC_VER
|
||||||
#define EXPORT __declspec(dllexport)
|
#define EXPORT __declspec(dllexport)
|
||||||
#else
|
#else
|
||||||
|
|
@ -34,13 +27,13 @@ public:
|
||||||
Runtime(RuntimeData* _data, Env* _env);
|
Runtime(RuntimeData* _data, Env* _env);
|
||||||
|
|
||||||
Runtime(const Runtime&) = delete;
|
Runtime(const Runtime&) = delete;
|
||||||
void operator=(const Runtime&) = delete;
|
Runtime& operator=(const Runtime&) = delete;
|
||||||
|
|
||||||
StackImpl& getStack() { return m_stack; }
|
StackImpl& getStack() { return m_stack; }
|
||||||
MemoryImpl& getMemory() { return m_memory; }
|
MemoryImpl& getMemory() { return m_memory; }
|
||||||
Env* getEnvPtr() { return &m_env; }
|
Env* getEnvPtr() { return &m_env; }
|
||||||
|
|
||||||
bytes getReturnData() const;
|
bytes_ref getReturnData() const;
|
||||||
jmp_buf_ref getJmpBuf() { return m_jmpBuf; }
|
jmp_buf_ref getJmpBuf() { return m_jmpBuf; }
|
||||||
|
|
||||||
private:
|
private:
|
||||||
|
|
|
||||||
|
|
@ -38,9 +38,6 @@ struct RuntimeData
|
||||||
i256 elems[_size] = {};
|
i256 elems[_size] = {};
|
||||||
byte const* callData = nullptr;
|
byte const* callData = nullptr;
|
||||||
byte const* code = 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
|
/// VM Environment (ExtVM) opaque type
|
||||||
|
|
|
||||||
|
|
@ -8,32 +8,6 @@ namespace eth
|
||||||
namespace jit
|
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<uint64_t>(_u & mask);
|
|
||||||
_u >>= 64;
|
|
||||||
i.b = static_cast<uint64_t>(_u & mask);
|
|
||||||
_u >>= 64;
|
|
||||||
i.c = static_cast<uint64_t>(_u & mask);
|
|
||||||
_u >>= 64;
|
|
||||||
i.d = static_cast<uint64_t>(_u & mask);
|
|
||||||
return i;
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -14,9 +14,6 @@ struct JIT: public NoteChannel { static const char* name() { return "JIT"; } };
|
||||||
//#define clog(CHANNEL) std::cerr
|
//#define clog(CHANNEL) std::cerr
|
||||||
#define clog(CHANNEL) std::ostream(nullptr)
|
#define clog(CHANNEL) std::ostream(nullptr)
|
||||||
|
|
||||||
u256 llvm2eth(i256);
|
|
||||||
i256 eth2llvm(u256);
|
|
||||||
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,5 @@
|
||||||
#include "interface.h"
|
#include "interface.h"
|
||||||
#include <cstdio>
|
#include <cstring>
|
||||||
#include "ExecutionEngine.h"
|
#include "ExecutionEngine.h"
|
||||||
|
|
||||||
extern "C"
|
extern "C"
|
||||||
|
|
@ -20,12 +20,12 @@ evmjit_result evmjit_run(void* _data, void* _env)
|
||||||
|
|
||||||
auto returnCode = engine.run(bytecode, data, static_cast<Env*>(_env));
|
auto returnCode = engine.run(bytecode, data, static_cast<Env*>(_env));
|
||||||
evmjit_result result = {static_cast<int32_t>(returnCode), 0, nullptr};
|
evmjit_result result = {static_cast<int32_t>(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
|
// 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);
|
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;
|
return result;
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue