Paweł Bylica
9 years ago
53 changed files with 1 additions and 5701 deletions
@ -1 +0,0 @@ |
|||
/build/ |
@ -1,65 +0,0 @@ |
|||
cmake_minimum_required(VERSION 2.8.12) |
|||
|
|||
if (${CMAKE_VERSION} VERSION_GREATER 3.0) |
|||
cmake_policy(SET CMP0042 OLD) # fix MACOSX_RPATH |
|||
cmake_policy(SET CMP0048 NEW) # allow VERSION argument in project() |
|||
project(EVMJIT VERSION 0.9.0.1 LANGUAGES CXX) |
|||
else() |
|||
project(EVMJIT) |
|||
set(EVMJIT_VERSION "0.9.0.1") |
|||
set(EVMJIT_VERSION_MAJOR 0) |
|||
set(EVMJIT_VERSION_MINOR 9) |
|||
set(EVMJIT_VERSION_PATCH 0) |
|||
set(EVMJIT_VERSION_TWEAK 1) |
|||
endif() |
|||
|
|||
set_property(GLOBAL PROPERTY USE_FOLDERS ON) |
|||
set(CMAKE_AUTOMOC OFF) |
|||
|
|||
if(${CMAKE_CXX_COMPILER_ID} STREQUAL "MSVC") |
|||
else() |
|||
set(CMAKE_CXX_FLAGS "-std=c++11 -Wall -Wextra -Wconversion -Wno-sign-conversion -Wno-unknown-pragmas ${CMAKE_CXX_FLAGS}") |
|||
endif() |
|||
|
|||
if(${CMAKE_SYSTEM_NAME} STREQUAL "Linux" AND NOT ${CMAKE_BUILD_TYPE} STREQUAL "DebugSan") |
|||
# Do not allow unresovled symbols in shared library (default on linux) |
|||
set(CMAKE_SHARED_LINKER_FLAGS "-Wl,--no-undefined") |
|||
endif() |
|||
|
|||
# LLVM |
|||
if (${CMAKE_SYSTEM_NAME} STREQUAL "Linux" AND NOT LLVM_DIR) |
|||
# Workaround for Ubuntu broken LLVM package |
|||
find_program(LLVM3_7_CONFIG llvm-config-3.7) |
|||
find_program(LLVM3_8_CONFIG llvm-config-3.8) |
|||
if (LLVM3_7_CONFIG) |
|||
message(STATUS "Using llvm-3.7-dev package from Ubuntu. If does not work, build LLVM and set -DLLVM_DIR=llvm-build/share/llvm/cmake") |
|||
set(LLVM_CONFIG_EXEC llvm-config-3.7) |
|||
set(LLVM_LIB_DIR /usr/lib/llvm-3.7/lib) |
|||
elseif(LLVM3_8_CONFIG) |
|||
message(STATUS "Using llvm-3.8-dev package from Ubuntu. If does not work, build LLVM and set -DLLVM_DIR=llvm-build/share/llvm/cmake") |
|||
set(LLVM_CONFIG_EXEC llvm-config-3.8) |
|||
set(LLVM_LIB_DIR /usr/lib/llvm-3.8/lib) |
|||
else() |
|||
message(FATAL_ERROR "No LLVM package found!") |
|||
endif() |
|||
|
|||
execute_process(COMMAND ${LLVM_CONFIG_EXEC} --includedir OUTPUT_VARIABLE LLVM_INCLUDE_DIRS OUTPUT_STRIP_TRAILING_WHITESPACE) |
|||
message(STATUS "LLVM include dirs: ${LLVM_INCLUDE_DIRS}") |
|||
set(LLVM_LIBS "-lLLVMipo -lLLVMVectorize -lLLVMX86AsmParser -lLLVMX86CodeGen -lLLVMX86Desc -lLLVMX86Info -lLLVMMCDisassembler -lLLVMX86AsmPrinter -lLLVMX86Utils -lLLVMSelectionDAG -lLLVMAsmPrinter -lLLVMCodeGen -lLLVMScalarOpts -lLLVMProfileData -lLLVMInstCombine -lLLVMInstrumentation -lLLVMTransformUtils -lLLVMipa -lLLVMMCJIT -lLLVMExecutionEngine -lLLVMTarget -lLLVMAnalysis -lLLVMRuntimeDyld -lLLVMObject -lLLVMMCParser -lLLVMBitReader -lLLVMMC -lLLVMCore -lLLVMSupport -lz -lpthread -lffi -ltinfo -ldl -lm") |
|||
set(LLVM_DEFINITIONS "-D__STDC_CONSTANT_MACROS -D__STDC_FORMAT_MACROS -D__STDC_LIMIT_MACROS") |
|||
link_directories(${LLVM_LIB_DIR}) |
|||
else() |
|||
find_package(LLVM REQUIRED CONFIG) |
|||
if (${LLVM_VERSION} VERSION_LESS 3.7) |
|||
message(FATAL_ERROR "Incompatible LLVM version ${LLVM_VERSION}") |
|||
endif() |
|||
message(STATUS "Found LLVM ${LLVM_VERSION}") |
|||
message(STATUS "Using LLVMConfig.cmake in: ${LLVM_DIR}") |
|||
llvm_map_components_to_libnames(LLVM_LIBS core support mcjit x86asmparser x86codegen ipo) |
|||
endif() |
|||
|
|||
add_subdirectory(libevmjit) |
|||
|
|||
if(EVMJIT_CPP) |
|||
add_subdirectory(libevmjit-cpp) |
|||
endif() |
@ -1,21 +0,0 @@ |
|||
The MIT License (MIT) |
|||
|
|||
Copyright (c) 2015 Paweł Bylica <chfast@gmail.com> |
|||
|
|||
Permission is hereby granted, free of charge, to any person obtaining a copy |
|||
of this software and associated documentation files (the "Software"), to deal |
|||
in the Software without restriction, including without limitation the rights |
|||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell |
|||
copies of the Software, and to permit persons to whom the Software is |
|||
furnished to do so, subject to the following conditions: |
|||
|
|||
The above copyright notice and this permission notice shall be included in all |
|||
copies or substantial portions of the Software. |
|||
|
|||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR |
|||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, |
|||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE |
|||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER |
|||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, |
|||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE |
|||
SOFTWARE. |
@ -1,36 +0,0 @@ |
|||
# The Ethereum EVM JIT |
|||
|
|||
EVM JIT is a library for just-in-time compilation of Ethereum EVM code. |
|||
It can be used to substitute classic interpreter-like EVM Virtual Machine in Ethereum client. |
|||
|
|||
## Build |
|||
|
|||
### Linux / Ubuntu |
|||
|
|||
1. Install llvm-3.5-dev package |
|||
1. For Ubuntu 14.04 using LLVM deb packages source: http://llvm.org/apt |
|||
2. For Ubuntu 14.10 using Ubuntu packages |
|||
2. Build library with cmake |
|||
1. `mkdir build && cd $_` |
|||
2. `cmake .. && make` |
|||
3. Install library |
|||
1. `sudo make install` |
|||
2. `sudo ldconfig` |
|||
|
|||
### OSX |
|||
|
|||
1. Install llvm35 |
|||
1. `brew install llvm35 --disable-shared --HEAD` |
|||
2. Build library with cmake |
|||
1. `mkdir build && cd $_` |
|||
2. `cmake -DLLVM_DIR=/usr/local/lib/llvm-3.5/share/llvm/cmake .. && make` |
|||
3. Install library |
|||
1. `make install` (with admin rights?) |
|||
|
|||
### Windows |
|||
|
|||
Ask me. |
|||
|
|||
## Options |
|||
|
|||
Options to evmjit library can be passed by environmental variable, e.g. `EVMJIT="-help" testeth --jit`. |
@ -1,70 +0,0 @@ |
|||
|
|||
#include "stdint.h" |
|||
|
|||
#ifdef _MSC_VER |
|||
#define EXPORT __declspec(dllexport) |
|||
#define _ALLOW_KEYWORD_MACROS |
|||
#define noexcept throw() |
|||
#else |
|||
#define EXPORT |
|||
#endif |
|||
|
|||
#ifdef __cplusplus |
|||
extern "C" { |
|||
#endif |
|||
|
|||
typedef struct evmjit_i256 |
|||
{ |
|||
uint64_t words[4]; |
|||
} evmjit_i256; |
|||
|
|||
typedef struct evmjit_runtime_data |
|||
{ |
|||
int64_t gas; |
|||
int64_t gasPrice; |
|||
char const* callData; |
|||
uint64_t callDataSize; |
|||
evmjit_i256 address; |
|||
evmjit_i256 caller; |
|||
evmjit_i256 origin; |
|||
evmjit_i256 callValue; |
|||
evmjit_i256 coinBase; |
|||
evmjit_i256 difficulty; |
|||
evmjit_i256 gasLimit; |
|||
uint64_t number; |
|||
int64_t timestamp; |
|||
char const* code; |
|||
uint64_t codeSize; |
|||
evmjit_i256 codeHash; |
|||
} evmjit_runtime_data; |
|||
|
|||
typedef enum evmjit_return_code |
|||
{ |
|||
// Success codes
|
|||
Stop = 0, |
|||
Return = 1, |
|||
Suicide = 2, |
|||
|
|||
// Standard error codes
|
|||
OutOfGas = -1, |
|||
|
|||
// Internal error codes
|
|||
LLVMError = -101, |
|||
UnexpectedException = -111 |
|||
} evmjit_return_code; |
|||
|
|||
typedef struct evmjit_context evmjit_context; |
|||
|
|||
EXPORT evmjit_context* evmjit_create(evmjit_runtime_data* _data, void* _env); |
|||
|
|||
EXPORT evmjit_return_code evmjit_exec(evmjit_context* _context); |
|||
|
|||
EXPORT void evmjit_destroy(evmjit_context* _context); |
|||
|
|||
|
|||
inline char const* evmjit_get_output(evmjit_runtime_data* _data) { return _data->callData; } |
|||
inline uint64_t evmjit_get_output_size(evmjit_runtime_data* _data) { return _data->callDataSize; } |
|||
|
|||
#ifdef __cplusplus |
|||
} |
|||
#endif |
@ -1,172 +0,0 @@ |
|||
#pragma once |
|||
|
|||
#include <cstdint> |
|||
#include <cstring> |
|||
#include <functional> |
|||
|
|||
#ifdef _MSC_VER |
|||
#define EXPORT __declspec(dllexport) |
|||
#define _ALLOW_KEYWORD_MACROS |
|||
#define noexcept throw() |
|||
#else |
|||
#define EXPORT |
|||
#endif |
|||
|
|||
namespace dev |
|||
{ |
|||
namespace evmjit |
|||
{ |
|||
|
|||
using byte = uint8_t; |
|||
using bytes_ref = std::tuple<byte const*, size_t>; |
|||
|
|||
/// Representation of 256-bit hash value
|
|||
struct h256 |
|||
{ |
|||
uint64_t words[4]; |
|||
}; |
|||
|
|||
inline bool operator==(h256 const& _h1, h256 const& _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 words[4]; |
|||
|
|||
i256() = default; |
|||
i256(h256 const& _h) { std::memcpy(this, &_h, sizeof(*this)); } |
|||
}; |
|||
|
|||
// TODO: Merge with ExecutionContext
|
|||
struct RuntimeData |
|||
{ |
|||
enum Index |
|||
{ |
|||
Gas, |
|||
GasPrice, |
|||
CallData, |
|||
CallDataSize, |
|||
Address, |
|||
Caller, |
|||
Origin, |
|||
CallValue, |
|||
CoinBase, |
|||
Difficulty, |
|||
GasLimit, |
|||
Number, |
|||
Timestamp, |
|||
Code, |
|||
CodeSize, |
|||
|
|||
SuicideDestAddress = Address, ///< Suicide balance destination address
|
|||
ReturnData = CallData, ///< Return data pointer (set only in case of RETURN)
|
|||
ReturnDataSize = CallDataSize, ///< Return data size (set only in case of RETURN)
|
|||
}; |
|||
|
|||
static size_t const numElements = CodeSize + 1; |
|||
|
|||
int64_t gas = 0; |
|||
int64_t gasPrice = 0; |
|||
byte const* callData = nullptr; |
|||
uint64_t callDataSize = 0; |
|||
i256 address; |
|||
i256 caller; |
|||
i256 origin; |
|||
i256 callValue; |
|||
i256 coinBase; |
|||
i256 difficulty; |
|||
i256 gasLimit; |
|||
uint64_t number = 0; |
|||
int64_t timestamp = 0; |
|||
byte const* code = nullptr; |
|||
uint64_t codeSize = 0; |
|||
h256 codeHash; |
|||
}; |
|||
|
|||
/// VM Environment (ExtVM) opaque type
|
|||
struct Env; |
|||
|
|||
enum class ReturnCode |
|||
{ |
|||
// Success codes
|
|||
Stop = 0, |
|||
Return = 1, |
|||
Suicide = 2, |
|||
|
|||
// Standard error codes
|
|||
OutOfGas = -1, |
|||
|
|||
// Internal error codes
|
|||
LLVMError = -101, |
|||
|
|||
UnexpectedException = -111, |
|||
|
|||
LinkerWorkaround = -299, |
|||
}; |
|||
|
|||
class ExecutionContext |
|||
{ |
|||
public: |
|||
ExecutionContext() = default; |
|||
ExecutionContext(RuntimeData& _data, Env* _env) { init(_data, _env); } |
|||
ExecutionContext(ExecutionContext const&) = delete; |
|||
ExecutionContext& operator=(ExecutionContext const&) = delete; |
|||
EXPORT ~ExecutionContext() noexcept; |
|||
|
|||
void init(RuntimeData& _data, Env* _env) { m_data = &_data; m_env = _env; } |
|||
|
|||
byte const* code() const { return m_data->code; } |
|||
uint64_t codeSize() const { return m_data->codeSize; } |
|||
h256 const& codeHash() const { return m_data->codeHash; } |
|||
|
|||
bytes_ref getReturnData() const; |
|||
|
|||
protected: |
|||
RuntimeData* m_data = nullptr; ///< Pointer to data. Expected by compiled contract.
|
|||
Env* m_env = nullptr; ///< Pointer to environment proxy. Expected by compiled contract.
|
|||
byte* m_memData = nullptr; |
|||
uint64_t m_memSize = 0; |
|||
uint64_t m_memCap = 0; |
|||
|
|||
public: |
|||
/// Reference to returned data (RETURN opcode used)
|
|||
bytes_ref returnData; |
|||
}; |
|||
|
|||
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.
|
|||
EXPORT static bool isCodeReady(h256 const& _codeHash); |
|||
|
|||
/// Compile the given EVM code to machine code and make available for execution.
|
|||
EXPORT static void compile(byte const* _code, uint64_t _codeSize, h256 const& _codeHash); |
|||
|
|||
EXPORT static ReturnCode exec(ExecutionContext& _context); |
|||
}; |
|||
|
|||
} |
|||
} |
|||
|
|||
namespace std |
|||
{ |
|||
template<> struct hash<dev::evmjit::h256> |
|||
{ |
|||
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<size_t>(_h.words[0]); |
|||
}; |
|||
}; |
|||
} |
@ -1,24 +0,0 @@ |
|||
set(TARGET_NAME evmjit-cpp) |
|||
|
|||
# Boost |
|||
find_package(Boost REQUIRED) |
|||
|
|||
set(SOURCES |
|||
Env.cpp |
|||
JitVM.cpp JitVM.h |
|||
Utils.h |
|||
) |
|||
source_group("" FILES ${SOURCES}) |
|||
|
|||
if(${CMAKE_CXX_COMPILER_ID} STREQUAL "MSVC") |
|||
else() |
|||
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -fPIC") # add PIC for archive |
|||
endif() |
|||
|
|||
add_library(${TARGET_NAME} STATIC ${SOURCES}) |
|||
set_property(TARGET ${TARGET_NAME} PROPERTY FOLDER "libs") |
|||
|
|||
include_directories(../..) |
|||
include_directories(${Boost_INCLUDE_DIRS}) |
|||
|
|||
target_link_libraries(${TARGET_NAME} evmjit) |
@ -1,135 +0,0 @@ |
|||
|
|||
#pragma GCC diagnostic ignored "-Wconversion" |
|||
#include <libdevcore/SHA3.h> |
|||
#include <libevmcore/Params.h> |
|||
#include <libevm/ExtVMFace.h> |
|||
|
|||
#include "Utils.h" |
|||
|
|||
extern "C" |
|||
{ |
|||
#ifdef _MSC_VER |
|||
#define EXPORT __declspec(dllexport) |
|||
#else |
|||
#define EXPORT |
|||
#endif |
|||
|
|||
using namespace dev; |
|||
using namespace dev::eth; |
|||
using evmjit::i256; |
|||
|
|||
EXPORT void env_sload(ExtVMFace* _env, i256* _index, i256* o_value) |
|||
{ |
|||
auto index = jit2eth(*_index); |
|||
auto value = _env->store(index); // Interface uses native endianness
|
|||
*o_value = eth2jit(value); |
|||
} |
|||
|
|||
EXPORT void env_sstore(ExtVMFace* _env, i256* _index, i256* _value) |
|||
{ |
|||
auto index = jit2eth(*_index); |
|||
auto value = jit2eth(*_value); |
|||
|
|||
if (value == 0 && _env->store(index) != 0) // If delete
|
|||
_env->sub.refunds += c_sstoreRefundGas; // Increase refund counter
|
|||
|
|||
_env->setStore(index, value); // Interface uses native endianness
|
|||
} |
|||
|
|||
EXPORT void env_balance(ExtVMFace* _env, h256* _address, i256* o_value) |
|||
{ |
|||
auto u = _env->balance(right160(*_address)); |
|||
*o_value = eth2jit(u); |
|||
} |
|||
|
|||
EXPORT void env_blockhash(ExtVMFace* _env, i256* _number, h256* o_hash) |
|||
{ |
|||
*o_hash = _env->blockHash(jit2eth(*_number)); |
|||
} |
|||
|
|||
EXPORT void env_create(ExtVMFace* _env, int64_t* io_gas, i256* _endowment, byte* _initBeg, uint64_t _initSize, h256* o_address) |
|||
{ |
|||
auto endowment = jit2eth(*_endowment); |
|||
if (_env->balance(_env->myAddress) >= endowment && _env->depth < 1024) |
|||
{ |
|||
u256 gas = *io_gas; |
|||
h256 address(_env->create(endowment, gas, {_initBeg, (size_t)_initSize}, {}), h256::AlignRight); |
|||
*io_gas = static_cast<int64_t>(gas); |
|||
*o_address = address; |
|||
} |
|||
else |
|||
*o_address = {}; |
|||
} |
|||
|
|||
EXPORT bool env_call(ExtVMFace* _env, int64_t* io_gas, int64_t _callGas, h256* _receiveAddress, i256* _value, byte* _inBeg, uint64_t _inSize, byte* _outBeg, uint64_t _outSize, h256* _codeAddress) |
|||
{ |
|||
CallParameters params; |
|||
params.value = jit2eth(*_value); |
|||
params.senderAddress = _env->myAddress; |
|||
params.receiveAddress = right160(*_receiveAddress); |
|||
params.codeAddress = right160(*_codeAddress); |
|||
params.data = {_inBeg, (size_t)_inSize}; |
|||
params.out = {_outBeg, (size_t)_outSize}; |
|||
params.onOp = {}; |
|||
const auto isCall = params.receiveAddress == params.codeAddress; // OPT: The same address pointer can be used if not CODECALL
|
|||
|
|||
*io_gas -= _callGas; |
|||
if (*io_gas < 0) |
|||
return false; |
|||
|
|||
if (isCall && !_env->exists(params.receiveAddress)) |
|||
*io_gas -= static_cast<int64_t>(c_callNewAccountGas); // no underflow, *io_gas non-negative before
|
|||
|
|||
if (params.value > 0) // value transfer
|
|||
{ |
|||
/*static*/ assert(c_callValueTransferGas > c_callStipend && "Overflow possible"); |
|||
*io_gas -= static_cast<int64_t>(c_callValueTransferGas); // no underflow
|
|||
_callGas += static_cast<int64_t>(c_callStipend); // overflow possibility, but in the same time *io_gas < 0
|
|||
} |
|||
|
|||
if (*io_gas < 0) |
|||
return false; |
|||
|
|||
auto ret = false; |
|||
params.gas = u256{_callGas}; |
|||
if (_env->balance(_env->myAddress) >= params.value && _env->depth < 1024) |
|||
ret = _env->call(params); |
|||
|
|||
*io_gas += static_cast<int64_t>(params.gas); // it is never more than initial _callGas
|
|||
return ret; |
|||
} |
|||
|
|||
EXPORT void env_sha3(byte* _begin, uint64_t _size, h256* o_hash) |
|||
{ |
|||
auto hash = sha3({_begin, (size_t)_size}); |
|||
*o_hash = hash; |
|||
} |
|||
|
|||
EXPORT byte const* env_extcode(ExtVMFace* _env, h256* _addr256, uint64_t* o_size) |
|||
{ |
|||
auto addr = right160(*_addr256); |
|||
auto& code = _env->codeAt(addr); |
|||
*o_size = code.size(); |
|||
return code.data(); |
|||
} |
|||
|
|||
EXPORT void env_log(ExtVMFace* _env, byte* _beg, uint64_t _size, h256* _topic1, h256* _topic2, h256* _topic3, h256* _topic4) |
|||
{ |
|||
dev::h256s topics; |
|||
|
|||
if (_topic1) |
|||
topics.push_back(*_topic1); |
|||
|
|||
if (_topic2) |
|||
topics.push_back(*_topic2); |
|||
|
|||
if (_topic3) |
|||
topics.push_back(*_topic3); |
|||
|
|||
if (_topic4) |
|||
topics.push_back(*_topic4); |
|||
|
|||
_env->log(std::move(topics), {_beg, (size_t)_size}); |
|||
} |
|||
} |
|||
|
@ -1,77 +0,0 @@ |
|||
|
|||
#pragma GCC diagnostic ignored "-Wconversion" |
|||
|
|||
#include "JitVM.h" |
|||
|
|||
#include <libdevcore/Log.h> |
|||
#include <libdevcore/SHA3.h> |
|||
#include <libevm/VM.h> |
|||
#include <libevm/VMFactory.h> |
|||
|
|||
#include "Utils.h" |
|||
|
|||
namespace dev |
|||
{ |
|||
namespace eth |
|||
{ |
|||
|
|||
extern "C" void env_sload(); // fake declaration for linker symbol stripping workaround, see a call below
|
|||
|
|||
bytesConstRef JitVM::execImpl(u256& io_gas, ExtVMFace& _ext, OnOpFunc const& _onOp) |
|||
{ |
|||
auto rejected = false; |
|||
// TODO: Rejecting transactions with gas limit > 2^63 can be used by attacker to take JIT out of scope
|
|||
rejected |= io_gas > std::numeric_limits<decltype(m_data.gas)>::max(); // Do not accept requests with gas > 2^63 (int64 max)
|
|||
rejected |= _ext.gasPrice > std::numeric_limits<decltype(m_data.gasPrice)>::max(); |
|||
rejected |= _ext.envInfo().number() > std::numeric_limits<decltype(m_data.number)>::max(); |
|||
rejected |= _ext.envInfo().timestamp() > std::numeric_limits<decltype(m_data.timestamp)>::max(); |
|||
|
|||
if (rejected) |
|||
{ |
|||
cwarn << "Execution rejected by EVM JIT (gas limit: " << io_gas << "), executing with interpreter"; |
|||
m_fallbackVM = VMFactory::create(VMKind::Interpreter); |
|||
return m_fallbackVM->execImpl(io_gas, _ext, _onOp); |
|||
} |
|||
|
|||
m_data.gas = static_cast<decltype(m_data.gas)>(io_gas); |
|||
m_data.gasPrice = static_cast<decltype(m_data.gasPrice)>(_ext.gasPrice); |
|||
m_data.callData = _ext.data.data(); |
|||
m_data.callDataSize = _ext.data.size(); |
|||
m_data.address = eth2jit(fromAddress(_ext.myAddress)); |
|||
m_data.caller = eth2jit(fromAddress(_ext.caller)); |
|||
m_data.origin = eth2jit(fromAddress(_ext.origin)); |
|||
m_data.callValue = eth2jit(_ext.value); |
|||
m_data.coinBase = eth2jit(fromAddress(_ext.envInfo().beneficiary())); |
|||
m_data.difficulty = eth2jit(_ext.envInfo().difficulty()); |
|||
m_data.gasLimit = eth2jit(_ext.envInfo().gasLimit()); |
|||
m_data.number = static_cast<decltype(m_data.number)>(_ext.envInfo().number()); |
|||
m_data.timestamp = static_cast<decltype(m_data.timestamp)>(_ext.envInfo().timestamp()); |
|||
m_data.code = _ext.code.data(); |
|||
m_data.codeSize = _ext.code.size(); |
|||
m_data.codeHash = eth2jit(_ext.codeHash); |
|||
|
|||
// Pass pointer to ExtVMFace casted to evmjit::Env* opaque type.
|
|||
// JIT will do nothing with the pointer, just pass it to Env callback functions implemented in Env.cpp.
|
|||
m_context.init(m_data, reinterpret_cast<evmjit::Env*>(&_ext)); |
|||
auto exitCode = evmjit::JIT::exec(m_context); |
|||
switch (exitCode) |
|||
{ |
|||
case evmjit::ReturnCode::Suicide: |
|||
_ext.suicide(right160(jit2eth(m_data.address))); |
|||
break; |
|||
|
|||
case evmjit::ReturnCode::OutOfGas: |
|||
BOOST_THROW_EXCEPTION(OutOfGas()); |
|||
case evmjit::ReturnCode::LinkerWorkaround: // never happens
|
|||
env_sload(); // but forces linker to include env_* JIT callback functions
|
|||
break; |
|||
default: |
|||
break; |
|||
} |
|||
|
|||
io_gas = m_data.gas; |
|||
return {std::get<0>(m_context.returnData), std::get<1>(m_context.returnData)}; |
|||
} |
|||
|
|||
} |
|||
} |
@ -1,24 +0,0 @@ |
|||
#pragma once |
|||
|
|||
#include <libevm/VMFace.h> |
|||
#include <evmjit/JIT.h> |
|||
|
|||
namespace dev |
|||
{ |
|||
namespace eth |
|||
{ |
|||
|
|||
class JitVM: public VMFace |
|||
{ |
|||
public: |
|||
virtual bytesConstRef execImpl(u256& io_gas, ExtVMFace& _ext, OnOpFunc const& _onOp) override final; |
|||
|
|||
private: |
|||
evmjit::RuntimeData m_data; |
|||
evmjit::ExecutionContext m_context; |
|||
std::unique_ptr<VMFace> m_fallbackVM; ///< VM used in case of input data rejected by JIT
|
|||
}; |
|||
|
|||
|
|||
} |
|||
} |
@ -1,45 +0,0 @@ |
|||
#pragma once |
|||
|
|||
#include <evmjit/JIT.h> |
|||
|
|||
namespace dev |
|||
{ |
|||
namespace eth |
|||
{ |
|||
|
|||
/// Converts EVM JIT representation of 256-bit integer to eth type dev::u256.
|
|||
inline u256 jit2eth(evmjit::i256 _i) |
|||
{ |
|||
u256 u = _i.words[3]; |
|||
u <<= 64; |
|||
u |= _i.words[2]; |
|||
u <<= 64; |
|||
u |= _i.words[1]; |
|||
u <<= 64; |
|||
u |= _i.words[0]; |
|||
return u; |
|||
} |
|||
|
|||
/// Converts eth type dev::u256 to EVM JIT representation of 256-bit integer.
|
|||
inline evmjit::i256 eth2jit(u256 _u) |
|||
{ |
|||
evmjit::i256 i; |
|||
i.words[0] = static_cast<uint64_t>(_u); |
|||
_u >>= 64; |
|||
i.words[1] = static_cast<uint64_t>(_u); |
|||
_u >>= 64; |
|||
i.words[2] = static_cast<uint64_t>(_u); |
|||
_u >>= 64; |
|||
i.words[3] = static_cast<uint64_t>(_u); |
|||
return i; |
|||
} |
|||
|
|||
/// Converts eth type dev::h256 to EVM JIT representation of 256-bit hash value.
|
|||
inline evmjit::h256 eth2jit(h256 _u) |
|||
{ |
|||
/// Just directly copies memory
|
|||
return *(evmjit::h256*)&_u; |
|||
} |
|||
|
|||
} |
|||
} |
@ -1,508 +0,0 @@ |
|||
#include "Arith256.h" |
|||
|
|||
#include <iostream> |
|||
#include <iomanip> |
|||
|
|||
#include "preprocessor/llvm_includes_start.h" |
|||
#include <llvm/IR/Module.h> |
|||
#include <llvm/IR/IntrinsicInst.h> |
|||
#include "preprocessor/llvm_includes_end.h" |
|||
|
|||
#include "Type.h" |
|||
#include "Endianness.h" |
|||
#include "Utils.h" |
|||
|
|||
namespace dev |
|||
{ |
|||
namespace eth |
|||
{ |
|||
namespace jit |
|||
{ |
|||
|
|||
Arith256::Arith256(llvm::IRBuilder<>& _builder) : |
|||
CompilerHelper(_builder) |
|||
{} |
|||
|
|||
void Arith256::debug(llvm::Value* _value, char _c) |
|||
{ |
|||
if (!m_debug) |
|||
{ |
|||
llvm::Type* argTypes[] = {Type::Word, m_builder.getInt8Ty()}; |
|||
m_debug = llvm::Function::Create(llvm::FunctionType::get(Type::Void, argTypes, false), llvm::Function::ExternalLinkage, "debug", getModule()); |
|||
} |
|||
createCall(m_debug, {m_builder.CreateZExtOrTrunc(_value, Type::Word), m_builder.getInt8(_c)}); |
|||
} |
|||
|
|||
llvm::Function* Arith256::getMulFunc(llvm::Module& _module) |
|||
{ |
|||
static const auto funcName = "evm.mul.i256"; |
|||
if (auto func = _module.getFunction(funcName)) |
|||
return func; |
|||
|
|||
llvm::Type* argTypes[] = {Type::Word, Type::Word}; |
|||
auto func = llvm::Function::Create(llvm::FunctionType::get(Type::Word, argTypes, false), llvm::Function::PrivateLinkage, funcName, &_module); |
|||
func->setDoesNotThrow(); |
|||
func->setDoesNotAccessMemory(); |
|||
|
|||
auto x = &func->getArgumentList().front(); |
|||
x->setName("x"); |
|||
auto y = x->getNextNode(); |
|||
y->setName("y"); |
|||
|
|||
auto bb = llvm::BasicBlock::Create(_module.getContext(), {}, func); |
|||
auto builder = llvm::IRBuilder<>{bb}; |
|||
auto i64 = Type::Size; |
|||
auto i128 = builder.getIntNTy(128); |
|||
auto i256 = Type::Word; |
|||
auto c64 = Constant::get(64); |
|||
auto c128 = Constant::get(128); |
|||
auto c192 = Constant::get(192); |
|||
|
|||
auto x_lo = builder.CreateTrunc(x, i64, "x.lo"); |
|||
auto y_lo = builder.CreateTrunc(y, i64, "y.lo"); |
|||
auto x_mi = builder.CreateTrunc(builder.CreateLShr(x, c64), i64); |
|||
auto y_mi = builder.CreateTrunc(builder.CreateLShr(y, c64), i64); |
|||
auto x_hi = builder.CreateTrunc(builder.CreateLShr(x, c128), i128); |
|||
auto y_hi = builder.CreateTrunc(builder.CreateLShr(y, c128), i128); |
|||
|
|||
auto t1 = builder.CreateMul(builder.CreateZExt(x_lo, i128), builder.CreateZExt(y_lo, i128)); |
|||
auto t2 = builder.CreateMul(builder.CreateZExt(x_lo, i128), builder.CreateZExt(y_mi, i128)); |
|||
auto t3 = builder.CreateMul(builder.CreateZExt(x_lo, i128), y_hi); |
|||
auto t4 = builder.CreateMul(builder.CreateZExt(x_mi, i128), builder.CreateZExt(y_lo, i128)); |
|||
auto t5 = builder.CreateMul(builder.CreateZExt(x_mi, i128), builder.CreateZExt(y_mi, i128)); |
|||
auto t6 = builder.CreateMul(builder.CreateZExt(x_mi, i128), y_hi); |
|||
auto t7 = builder.CreateMul(x_hi, builder.CreateZExt(y_lo, i128)); |
|||
auto t8 = builder.CreateMul(x_hi, builder.CreateZExt(y_mi, i128)); |
|||
|
|||
auto p = builder.CreateZExt(t1, i256); |
|||
p = builder.CreateAdd(p, builder.CreateShl(builder.CreateZExt(t2, i256), c64)); |
|||
p = builder.CreateAdd(p, builder.CreateShl(builder.CreateZExt(t3, i256), c128)); |
|||
p = builder.CreateAdd(p, builder.CreateShl(builder.CreateZExt(t4, i256), c64)); |
|||
p = builder.CreateAdd(p, builder.CreateShl(builder.CreateZExt(t5, i256), c128)); |
|||
p = builder.CreateAdd(p, builder.CreateShl(builder.CreateZExt(t6, i256), c192)); |
|||
p = builder.CreateAdd(p, builder.CreateShl(builder.CreateZExt(t7, i256), c128)); |
|||
p = builder.CreateAdd(p, builder.CreateShl(builder.CreateZExt(t8, i256), c192)); |
|||
builder.CreateRet(p); |
|||
return func; |
|||
} |
|||
|
|||
llvm::Function* Arith256::getMul512Func(llvm::Module& _module) |
|||
{ |
|||
static const auto funcName = "evm.mul.i512"; |
|||
if (auto func = _module.getFunction(funcName)) |
|||
return func; |
|||
|
|||
auto i512Ty = llvm::IntegerType::get(_module.getContext(), 512); |
|||
auto func = llvm::Function::Create(llvm::FunctionType::get(i512Ty, {Type::Word, Type::Word}, false), llvm::Function::PrivateLinkage, funcName, &_module); |
|||
func->setDoesNotThrow(); |
|||
func->setDoesNotAccessMemory(); |
|||
|
|||
auto x = &func->getArgumentList().front(); |
|||
x->setName("x"); |
|||
auto y = x->getNextNode(); |
|||
y->setName("y"); |
|||
|
|||
auto bb = llvm::BasicBlock::Create(_module.getContext(), {}, func); |
|||
auto builder = llvm::IRBuilder<>{bb}; |
|||
|
|||
auto i128 = builder.getIntNTy(128); |
|||
auto i256 = Type::Word; |
|||
auto x_lo = builder.CreateZExt(builder.CreateTrunc(x, i128, "x.lo"), i256); |
|||
auto y_lo = builder.CreateZExt(builder.CreateTrunc(y, i128, "y.lo"), i256); |
|||
auto x_hi = builder.CreateZExt(builder.CreateTrunc(builder.CreateLShr(x, Constant::get(128)), i128, "x.hi"), i256); |
|||
auto y_hi = builder.CreateZExt(builder.CreateTrunc(builder.CreateLShr(y, Constant::get(128)), i128, "y.hi"), i256); |
|||
|
|||
auto mul256Func = getMulFunc(_module); |
|||
auto t1 = builder.CreateCall(mul256Func, {x_lo, y_lo}); |
|||
auto t2 = builder.CreateCall(mul256Func, {x_lo, y_hi}); |
|||
auto t3 = builder.CreateCall(mul256Func, {x_hi, y_lo}); |
|||
auto t4 = builder.CreateCall(mul256Func, {x_hi, y_hi}); |
|||
|
|||
auto p = builder.CreateZExt(t1, i512Ty); |
|||
p = builder.CreateAdd(p, builder.CreateShl(builder.CreateZExt(t2, i512Ty), builder.getIntN(512, 128))); |
|||
p = builder.CreateAdd(p, builder.CreateShl(builder.CreateZExt(t3, i512Ty), builder.getIntN(512, 128))); |
|||
p = builder.CreateAdd(p, builder.CreateShl(builder.CreateZExt(t4, i512Ty), builder.getIntN(512, 256))); |
|||
builder.CreateRet(p); |
|||
|
|||
return func; |
|||
} |
|||
|
|||
namespace |
|||
{ |
|||
llvm::Function* createUDivRemFunc(llvm::Type* _type, llvm::Module& _module, char const* _funcName) |
|||
{ |
|||
// Based of "Improved shift divisor algorithm" from "Software Integer Division" by Microsoft Research
|
|||
// The following algorithm also handles divisor of value 0 returning 0 for both quotient and remainder
|
|||
|
|||
auto retType = llvm::VectorType::get(_type, 2); |
|||
auto func = llvm::Function::Create(llvm::FunctionType::get(retType, {_type, _type}, false), llvm::Function::PrivateLinkage, _funcName, &_module); |
|||
func->setDoesNotThrow(); |
|||
func->setDoesNotAccessMemory(); |
|||
|
|||
auto zero = llvm::ConstantInt::get(_type, 0); |
|||
auto one = llvm::ConstantInt::get(_type, 1); |
|||
|
|||
auto x = &func->getArgumentList().front(); |
|||
x->setName("x"); |
|||
auto yArg = x->getNextNode(); |
|||
yArg->setName("y"); |
|||
|
|||
auto entryBB = llvm::BasicBlock::Create(_module.getContext(), "Entry", func); |
|||
auto mainBB = llvm::BasicBlock::Create(_module.getContext(), "Main", func); |
|||
auto loopBB = llvm::BasicBlock::Create(_module.getContext(), "Loop", func); |
|||
auto continueBB = llvm::BasicBlock::Create(_module.getContext(), "Continue", func); |
|||
auto returnBB = llvm::BasicBlock::Create(_module.getContext(), "Return", func); |
|||
|
|||
auto builder = llvm::IRBuilder<>{entryBB}; |
|||
auto yLEx = builder.CreateICmpULE(yArg, x); |
|||
auto r0 = x; |
|||
builder.CreateCondBr(yLEx, mainBB, returnBB); |
|||
|
|||
builder.SetInsertPoint(mainBB); |
|||
auto ctlzIntr = llvm::Intrinsic::getDeclaration(&_module, llvm::Intrinsic::ctlz, _type); |
|||
// both y and r are non-zero
|
|||
auto yLz = builder.CreateCall(ctlzIntr, {yArg, builder.getInt1(true)}, "y.lz"); |
|||
auto rLz = builder.CreateCall(ctlzIntr, {r0, builder.getInt1(true)}, "r.lz"); |
|||
auto i0 = builder.CreateNUWSub(yLz, rLz, "i0"); |
|||
auto y0 = builder.CreateShl(yArg, i0); |
|||
builder.CreateBr(loopBB); |
|||
|
|||
builder.SetInsertPoint(loopBB); |
|||
auto yPhi = builder.CreatePHI(_type, 2, "y.phi"); |
|||
auto rPhi = builder.CreatePHI(_type, 2, "r.phi"); |
|||
auto iPhi = builder.CreatePHI(_type, 2, "i.phi"); |
|||
auto qPhi = builder.CreatePHI(_type, 2, "q.phi"); |
|||
auto rUpdate = builder.CreateNUWSub(rPhi, yPhi); |
|||
auto qUpdate = builder.CreateOr(qPhi, one); // q += 1, q lowest bit is 0
|
|||
auto rGEy = builder.CreateICmpUGE(rPhi, yPhi); |
|||
auto r1 = builder.CreateSelect(rGEy, rUpdate, rPhi, "r1"); |
|||
auto q1 = builder.CreateSelect(rGEy, qUpdate, qPhi, "q"); |
|||
auto iZero = builder.CreateICmpEQ(iPhi, zero); |
|||
builder.CreateCondBr(iZero, returnBB, continueBB); |
|||
|
|||
builder.SetInsertPoint(continueBB); |
|||
auto i2 = builder.CreateNUWSub(iPhi, one); |
|||
auto q2 = builder.CreateShl(q1, one); |
|||
auto y2 = builder.CreateLShr(yPhi, one); |
|||
builder.CreateBr(loopBB); |
|||
|
|||
yPhi->addIncoming(y0, mainBB); |
|||
yPhi->addIncoming(y2, continueBB); |
|||
rPhi->addIncoming(r0, mainBB); |
|||
rPhi->addIncoming(r1, continueBB); |
|||
iPhi->addIncoming(i0, mainBB); |
|||
iPhi->addIncoming(i2, continueBB); |
|||
qPhi->addIncoming(zero, mainBB); |
|||
qPhi->addIncoming(q2, continueBB); |
|||
|
|||
builder.SetInsertPoint(returnBB); |
|||
auto qRet = builder.CreatePHI(_type, 2, "q.ret"); |
|||
qRet->addIncoming(zero, entryBB); |
|||
qRet->addIncoming(q1, loopBB); |
|||
auto rRet = builder.CreatePHI(_type, 2, "r.ret"); |
|||
rRet->addIncoming(r0, entryBB); |
|||
rRet->addIncoming(r1, loopBB); |
|||
auto ret = builder.CreateInsertElement(llvm::UndefValue::get(retType), qRet, uint64_t(0), "ret0"); |
|||
ret = builder.CreateInsertElement(ret, rRet, 1, "ret"); |
|||
builder.CreateRet(ret); |
|||
|
|||
return func; |
|||
} |
|||
} |
|||
|
|||
llvm::Function* Arith256::getUDivRem256Func(llvm::Module& _module) |
|||
{ |
|||
static const auto funcName = "evm.udivrem.i256"; |
|||
if (auto func = _module.getFunction(funcName)) |
|||
return func; |
|||
|
|||
return createUDivRemFunc(Type::Word, _module, funcName); |
|||
} |
|||
|
|||
llvm::Function* Arith256::getUDivRem512Func(llvm::Module& _module) |
|||
{ |
|||
static const auto funcName = "evm.udivrem.i512"; |
|||
if (auto func = _module.getFunction(funcName)) |
|||
return func; |
|||
|
|||
return createUDivRemFunc(llvm::IntegerType::get(_module.getContext(), 512), _module, funcName); |
|||
} |
|||
|
|||
llvm::Function* Arith256::getUDiv256Func(llvm::Module& _module) |
|||
{ |
|||
static const auto funcName = "evm.udiv.i256"; |
|||
if (auto func = _module.getFunction(funcName)) |
|||
return func; |
|||
|
|||
auto udivremFunc = getUDivRem256Func(_module); |
|||
|
|||
auto func = llvm::Function::Create(llvm::FunctionType::get(Type::Word, {Type::Word, Type::Word}, false), llvm::Function::PrivateLinkage, funcName, &_module); |
|||
func->setDoesNotThrow(); |
|||
func->setDoesNotAccessMemory(); |
|||
|
|||
auto x = &func->getArgumentList().front(); |
|||
x->setName("x"); |
|||
auto y = x->getNextNode(); |
|||
y->setName("y"); |
|||
|
|||
auto bb = llvm::BasicBlock::Create(_module.getContext(), {}, func); |
|||
auto builder = llvm::IRBuilder<>{bb}; |
|||
auto udivrem = builder.CreateCall(udivremFunc, {x, y}); |
|||
auto udiv = builder.CreateExtractElement(udivrem, uint64_t(0)); |
|||
builder.CreateRet(udiv); |
|||
|
|||
return func; |
|||
} |
|||
|
|||
namespace |
|||
{ |
|||
llvm::Function* createURemFunc(llvm::Type* _type, llvm::Module& _module, char const* _funcName) |
|||
{ |
|||
auto udivremFunc = _type == Type::Word ? Arith256::getUDivRem256Func(_module) : Arith256::getUDivRem512Func(_module); |
|||
|
|||
auto func = llvm::Function::Create(llvm::FunctionType::get(_type, {_type, _type}, false), llvm::Function::PrivateLinkage, _funcName, &_module); |
|||
func->setDoesNotThrow(); |
|||
func->setDoesNotAccessMemory(); |
|||
|
|||
auto x = &func->getArgumentList().front(); |
|||
x->setName("x"); |
|||
auto y = x->getNextNode(); |
|||
y->setName("y"); |
|||
|
|||
auto bb = llvm::BasicBlock::Create(_module.getContext(), {}, func); |
|||
auto builder = llvm::IRBuilder<>{bb}; |
|||
auto udivrem = builder.CreateCall(udivremFunc, {x, y}); |
|||
auto r = builder.CreateExtractElement(udivrem, uint64_t(1)); |
|||
builder.CreateRet(r); |
|||
|
|||
return func; |
|||
} |
|||
} |
|||
|
|||
llvm::Function* Arith256::getURem256Func(llvm::Module& _module) |
|||
{ |
|||
static const auto funcName = "evm.urem.i256"; |
|||
if (auto func = _module.getFunction(funcName)) |
|||
return func; |
|||
return createURemFunc(Type::Word, _module, funcName); |
|||
} |
|||
|
|||
llvm::Function* Arith256::getURem512Func(llvm::Module& _module) |
|||
{ |
|||
static const auto funcName = "evm.urem.i512"; |
|||
if (auto func = _module.getFunction(funcName)) |
|||
return func; |
|||
return createURemFunc(llvm::IntegerType::get(_module.getContext(), 512), _module, funcName); |
|||
} |
|||
|
|||
llvm::Function* Arith256::getSDivRem256Func(llvm::Module& _module) |
|||
{ |
|||
static const auto funcName = "evm.sdivrem.i256"; |
|||
if (auto func = _module.getFunction(funcName)) |
|||
return func; |
|||
|
|||
auto udivremFunc = getUDivRem256Func(_module); |
|||
|
|||
auto retType = llvm::VectorType::get(Type::Word, 2); |
|||
auto func = llvm::Function::Create(llvm::FunctionType::get(retType, {Type::Word, Type::Word}, false), llvm::Function::PrivateLinkage, funcName, &_module); |
|||
func->setDoesNotThrow(); |
|||
func->setDoesNotAccessMemory(); |
|||
|
|||
auto x = &func->getArgumentList().front(); |
|||
x->setName("x"); |
|||
auto y = x->getNextNode(); |
|||
y->setName("y"); |
|||
|
|||
auto bb = llvm::BasicBlock::Create(_module.getContext(), "", func); |
|||
auto builder = llvm::IRBuilder<>{bb}; |
|||
auto xIsNeg = builder.CreateICmpSLT(x, Constant::get(0)); |
|||
auto xNeg = builder.CreateSub(Constant::get(0), x); |
|||
auto xAbs = builder.CreateSelect(xIsNeg, xNeg, x); |
|||
|
|||
auto yIsNeg = builder.CreateICmpSLT(y, Constant::get(0)); |
|||
auto yNeg = builder.CreateSub(Constant::get(0), y); |
|||
auto yAbs = builder.CreateSelect(yIsNeg, yNeg, y); |
|||
|
|||
auto res = builder.CreateCall(udivremFunc, {xAbs, yAbs}); |
|||
auto qAbs = builder.CreateExtractElement(res, uint64_t(0)); |
|||
auto rAbs = builder.CreateExtractElement(res, 1); |
|||
|
|||
// the remainder has the same sign as dividend
|
|||
auto rNeg = builder.CreateSub(Constant::get(0), rAbs); |
|||
auto r = builder.CreateSelect(xIsNeg, rNeg, rAbs); |
|||
|
|||
auto qNeg = builder.CreateSub(Constant::get(0), qAbs); |
|||
auto xyOpposite = builder.CreateXor(xIsNeg, yIsNeg); |
|||
auto q = builder.CreateSelect(xyOpposite, qNeg, qAbs); |
|||
|
|||
auto ret = builder.CreateInsertElement(llvm::UndefValue::get(retType), q, uint64_t(0)); |
|||
ret = builder.CreateInsertElement(ret, r, 1); |
|||
builder.CreateRet(ret); |
|||
|
|||
return func; |
|||
} |
|||
|
|||
llvm::Function* Arith256::getSDiv256Func(llvm::Module& _module) |
|||
{ |
|||
static const auto funcName = "evm.sdiv.i256"; |
|||
if (auto func = _module.getFunction(funcName)) |
|||
return func; |
|||
|
|||
auto sdivremFunc = getSDivRem256Func(_module); |
|||
|
|||
auto func = llvm::Function::Create(llvm::FunctionType::get(Type::Word, {Type::Word, Type::Word}, false), llvm::Function::PrivateLinkage, funcName, &_module); |
|||
func->setDoesNotThrow(); |
|||
func->setDoesNotAccessMemory(); |
|||
|
|||
auto x = &func->getArgumentList().front(); |
|||
x->setName("x"); |
|||
auto y = x->getNextNode(); |
|||
y->setName("y"); |
|||
|
|||
auto bb = llvm::BasicBlock::Create(_module.getContext(), {}, func); |
|||
auto builder = llvm::IRBuilder<>{bb}; |
|||
auto sdivrem = builder.CreateCall(sdivremFunc, {x, y}); |
|||
auto q = builder.CreateExtractElement(sdivrem, uint64_t(0)); |
|||
builder.CreateRet(q); |
|||
|
|||
return func; |
|||
} |
|||
|
|||
llvm::Function* Arith256::getSRem256Func(llvm::Module& _module) |
|||
{ |
|||
static const auto funcName = "evm.srem.i256"; |
|||
if (auto func = _module.getFunction(funcName)) |
|||
return func; |
|||
|
|||
auto sdivremFunc = getSDivRem256Func(_module); |
|||
|
|||
auto func = llvm::Function::Create(llvm::FunctionType::get(Type::Word, {Type::Word, Type::Word}, false), llvm::Function::PrivateLinkage, funcName, &_module); |
|||
func->setDoesNotThrow(); |
|||
func->setDoesNotAccessMemory(); |
|||
|
|||
auto x = &func->getArgumentList().front(); |
|||
x->setName("x"); |
|||
auto y = x->getNextNode(); |
|||
y->setName("y"); |
|||
|
|||
auto bb = llvm::BasicBlock::Create(_module.getContext(), {}, func); |
|||
auto builder = llvm::IRBuilder<>{bb}; |
|||
auto sdivrem = builder.CreateCall(sdivremFunc, {x, y}); |
|||
auto r = builder.CreateExtractElement(sdivrem, uint64_t(1)); |
|||
builder.CreateRet(r); |
|||
|
|||
return func; |
|||
} |
|||
|
|||
llvm::Function* Arith256::getExpFunc() |
|||
{ |
|||
if (!m_exp) |
|||
{ |
|||
llvm::Type* argTypes[] = {Type::Word, Type::Word}; |
|||
m_exp = llvm::Function::Create(llvm::FunctionType::get(Type::Word, argTypes, false), llvm::Function::PrivateLinkage, "exp", getModule()); |
|||
m_exp->setDoesNotThrow(); |
|||
m_exp->setDoesNotAccessMemory(); |
|||
|
|||
auto base = &m_exp->getArgumentList().front(); |
|||
base->setName("base"); |
|||
auto exponent = base->getNextNode(); |
|||
exponent->setName("exponent"); |
|||
|
|||
InsertPointGuard guard{m_builder}; |
|||
|
|||
// while (e != 0) {
|
|||
// if (e % 2 == 1)
|
|||
// r *= b;
|
|||
// b *= b;
|
|||
// e /= 2;
|
|||
// }
|
|||
|
|||
auto entryBB = llvm::BasicBlock::Create(m_builder.getContext(), "Entry", m_exp); |
|||
auto headerBB = llvm::BasicBlock::Create(m_builder.getContext(), "LoopHeader", m_exp); |
|||
auto bodyBB = llvm::BasicBlock::Create(m_builder.getContext(), "LoopBody", m_exp); |
|||
auto updateBB = llvm::BasicBlock::Create(m_builder.getContext(), "ResultUpdate", m_exp); |
|||
auto continueBB = llvm::BasicBlock::Create(m_builder.getContext(), "Continue", m_exp); |
|||
auto returnBB = llvm::BasicBlock::Create(m_builder.getContext(), "Return", m_exp); |
|||
|
|||
m_builder.SetInsertPoint(entryBB); |
|||
m_builder.CreateBr(headerBB); |
|||
|
|||
m_builder.SetInsertPoint(headerBB); |
|||
auto r = m_builder.CreatePHI(Type::Word, 2, "r"); |
|||
auto b = m_builder.CreatePHI(Type::Word, 2, "b"); |
|||
auto e = m_builder.CreatePHI(Type::Word, 2, "e"); |
|||
auto eNonZero = m_builder.CreateICmpNE(e, Constant::get(0), "e.nonzero"); |
|||
m_builder.CreateCondBr(eNonZero, bodyBB, returnBB); |
|||
|
|||
m_builder.SetInsertPoint(bodyBB); |
|||
auto eOdd = m_builder.CreateICmpNE(m_builder.CreateAnd(e, Constant::get(1)), Constant::get(0), "e.isodd"); |
|||
m_builder.CreateCondBr(eOdd, updateBB, continueBB); |
|||
|
|||
m_builder.SetInsertPoint(updateBB); |
|||
auto mul256Func = getMulFunc(*getModule()); |
|||
auto r0 = createCall(mul256Func, {r, b}); |
|||
m_builder.CreateBr(continueBB); |
|||
|
|||
m_builder.SetInsertPoint(continueBB); |
|||
auto r1 = m_builder.CreatePHI(Type::Word, 2, "r1"); |
|||
r1->addIncoming(r, bodyBB); |
|||
r1->addIncoming(r0, updateBB); |
|||
auto b1 = createCall(mul256Func, {b, b}); |
|||
auto e1 = m_builder.CreateLShr(e, Constant::get(1), "e1"); |
|||
m_builder.CreateBr(headerBB); |
|||
|
|||
r->addIncoming(Constant::get(1), entryBB); |
|||
r->addIncoming(r1, continueBB); |
|||
b->addIncoming(base, entryBB); |
|||
b->addIncoming(b1, continueBB); |
|||
e->addIncoming(exponent, entryBB); |
|||
e->addIncoming(e1, continueBB); |
|||
|
|||
m_builder.SetInsertPoint(returnBB); |
|||
m_builder.CreateRet(r); |
|||
} |
|||
return m_exp; |
|||
} |
|||
|
|||
llvm::Value* Arith256::exp(llvm::Value* _arg1, llvm::Value* _arg2) |
|||
{ |
|||
// while (e != 0) {
|
|||
// if (e % 2 == 1)
|
|||
// r *= b;
|
|||
// b *= b;
|
|||
// e /= 2;
|
|||
// }
|
|||
|
|||
if (auto c1 = llvm::dyn_cast<llvm::ConstantInt>(_arg1)) |
|||
{ |
|||
if (auto c2 = llvm::dyn_cast<llvm::ConstantInt>(_arg2)) |
|||
{ |
|||
auto b = c1->getValue(); |
|||
auto e = c2->getValue(); |
|||
auto r = llvm::APInt{256, 1}; |
|||
while (e != 0) |
|||
{ |
|||
if (e[0]) |
|||
r *= b; |
|||
b *= b; |
|||
e = e.lshr(1); |
|||
} |
|||
return Constant::get(r); |
|||
} |
|||
} |
|||
|
|||
return createCall(getExpFunc(), {_arg1, _arg2}); |
|||
} |
|||
|
|||
} |
|||
} |
|||
} |
|||
|
|||
extern "C" |
|||
{ |
|||
EXPORT void debug(uint64_t a, uint64_t b, uint64_t c, uint64_t d, char z) |
|||
{ |
|||
DLOG(JIT) << "DEBUG " << std::dec << z << ": " //<< d << c << b << a
|
|||
<< " [" << std::hex << std::setfill('0') << std::setw(16) << d << std::setw(16) << c << std::setw(16) << b << std::setw(16) << a << "]\n"; |
|||
} |
|||
} |
@ -1,42 +0,0 @@ |
|||
#pragma once |
|||
|
|||
#include "CompilerHelper.h" |
|||
|
|||
namespace dev |
|||
{ |
|||
namespace eth |
|||
{ |
|||
namespace jit |
|||
{ |
|||
|
|||
class Arith256 : public CompilerHelper |
|||
{ |
|||
public: |
|||
Arith256(llvm::IRBuilder<>& _builder); |
|||
|
|||
llvm::Value* exp(llvm::Value* _arg1, llvm::Value* _arg2); |
|||
|
|||
void debug(llvm::Value* _value, char _c); |
|||
|
|||
static llvm::Function* getMulFunc(llvm::Module& _module); |
|||
static llvm::Function* getMul512Func(llvm::Module& _module); |
|||
static llvm::Function* getUDiv256Func(llvm::Module& _module); |
|||
static llvm::Function* getURem256Func(llvm::Module& _module); |
|||
static llvm::Function* getURem512Func(llvm::Module& _module); |
|||
static llvm::Function* getUDivRem256Func(llvm::Module& _module); |
|||
static llvm::Function* getSDiv256Func(llvm::Module& _module); |
|||
static llvm::Function* getSRem256Func(llvm::Module& _module); |
|||
static llvm::Function* getSDivRem256Func(llvm::Module& _module); |
|||
static llvm::Function* getUDivRem512Func(llvm::Module& _module); |
|||
|
|||
private: |
|||
llvm::Function* getExpFunc(); |
|||
|
|||
llvm::Function* m_exp = nullptr; |
|||
llvm::Function* m_debug = nullptr; |
|||
}; |
|||
|
|||
|
|||
} |
|||
} |
|||
} |
@ -1,270 +0,0 @@ |
|||
#include "Array.h" |
|||
|
|||
#include "preprocessor/llvm_includes_start.h" |
|||
#include <llvm/IR/Module.h> |
|||
#include <llvm/IR/Function.h> |
|||
#include "preprocessor/llvm_includes_end.h" |
|||
|
|||
#include "RuntimeManager.h" |
|||
#include "Utils.h" |
|||
|
|||
namespace dev |
|||
{ |
|||
namespace eth |
|||
{ |
|||
namespace jit |
|||
{ |
|||
|
|||
static const auto c_reallocStep = 1; |
|||
|
|||
llvm::Value* LazyFunction::call(llvm::IRBuilder<>& _builder, std::initializer_list<llvm::Value*> const& _args, llvm::Twine const& _name) |
|||
{ |
|||
if (!m_func) |
|||
m_func = m_creator(); |
|||
|
|||
return _builder.CreateCall(m_func, {_args.begin(), _args.size()}, _name); |
|||
} |
|||
|
|||
llvm::Function* Array::createArrayPushFunc() |
|||
{ |
|||
llvm::Type* argTypes[] = {m_array->getType(), Type::Word}; |
|||
auto func = llvm::Function::Create(llvm::FunctionType::get(Type::Void, argTypes, false), llvm::Function::PrivateLinkage, "array.push", getModule()); |
|||
func->setDoesNotThrow(); |
|||
func->setDoesNotCapture(1); |
|||
|
|||
auto arrayPtr = &func->getArgumentList().front(); |
|||
arrayPtr->setName("arrayPtr"); |
|||
auto value = arrayPtr->getNextNode(); |
|||
value->setName("value"); |
|||
|
|||
InsertPointGuard guard{m_builder}; |
|||
auto entryBB = llvm::BasicBlock::Create(m_builder.getContext(), "Entry", func); |
|||
auto reallocBB = llvm::BasicBlock::Create(m_builder.getContext(), "Realloc", func); |
|||
auto pushBB = llvm::BasicBlock::Create(m_builder.getContext(), "Push", func); |
|||
|
|||
m_builder.SetInsertPoint(entryBB); |
|||
auto dataPtr = m_builder.CreateStructGEP(getType(), arrayPtr, 0, "dataPtr"); |
|||
auto sizePtr = m_builder.CreateStructGEP(getType(), arrayPtr, 1, "sizePtr"); |
|||
auto capPtr = m_builder.CreateStructGEP(getType(), arrayPtr, 2, "capPtr"); |
|||
auto data = m_builder.CreateLoad(dataPtr, "data"); |
|||
auto size = m_builder.CreateLoad(sizePtr, "size"); |
|||
auto cap = m_builder.CreateLoad(capPtr, "cap"); |
|||
auto reallocReq = m_builder.CreateICmpEQ(cap, size, "reallocReq"); |
|||
m_builder.CreateCondBr(reallocReq, reallocBB, pushBB); |
|||
|
|||
m_builder.SetInsertPoint(reallocBB); |
|||
auto newCap = m_builder.CreateNUWAdd(cap, m_builder.getInt64(c_reallocStep), "newCap"); |
|||
auto reallocSize = m_builder.CreateShl(newCap, 5, "reallocSize"); // size in bytes: newCap * 32
|
|||
auto bytes = m_builder.CreateBitCast(data, Type::BytePtr, "bytes"); |
|||
auto newBytes = m_reallocFunc.call(m_builder, {bytes, reallocSize}, "newBytes"); |
|||
auto newData = m_builder.CreateBitCast(newBytes, Type::WordPtr, "newData"); |
|||
m_builder.CreateStore(newData, dataPtr); |
|||
m_builder.CreateStore(newCap, capPtr); |
|||
m_builder.CreateBr(pushBB); |
|||
|
|||
m_builder.SetInsertPoint(pushBB); |
|||
auto dataPhi = m_builder.CreatePHI(Type::WordPtr, 2, "dataPhi"); |
|||
dataPhi->addIncoming(data, entryBB); |
|||
dataPhi->addIncoming(newData, reallocBB); |
|||
auto newElemPtr = m_builder.CreateGEP(dataPhi, size, "newElemPtr"); |
|||
m_builder.CreateStore(value, newElemPtr); |
|||
auto newSize = m_builder.CreateNUWAdd(size, m_builder.getInt64(1), "newSize"); |
|||
m_builder.CreateStore(newSize, sizePtr); |
|||
m_builder.CreateRetVoid(); |
|||
|
|||
return func; |
|||
} |
|||
|
|||
llvm::Function* Array::createArraySetFunc() |
|||
{ |
|||
llvm::Type* argTypes[] = {m_array->getType(), Type::Size, Type::Word}; |
|||
auto func = llvm::Function::Create(llvm::FunctionType::get(Type::Void, argTypes, false), llvm::Function::PrivateLinkage, "array.set", getModule()); |
|||
func->setDoesNotThrow(); |
|||
func->setDoesNotCapture(1); |
|||
|
|||
auto arrayPtr = &func->getArgumentList().front(); |
|||
arrayPtr->setName("arrayPtr"); |
|||
auto index = arrayPtr->getNextNode(); |
|||
index->setName("index"); |
|||
auto value = index->getNextNode(); |
|||
value->setName("value"); |
|||
|
|||
InsertPointGuard guard{m_builder}; |
|||
m_builder.SetInsertPoint(llvm::BasicBlock::Create(m_builder.getContext(), {}, func)); |
|||
auto dataPtr = m_builder.CreateStructGEP(getType(), arrayPtr, 0, "dataPtr"); |
|||
auto data = m_builder.CreateLoad(dataPtr, "data"); |
|||
auto valuePtr = m_builder.CreateGEP(data, index, "valuePtr"); |
|||
m_builder.CreateStore(value, valuePtr); |
|||
m_builder.CreateRetVoid(); |
|||
return func; |
|||
} |
|||
|
|||
llvm::Function* Array::createArrayGetFunc() |
|||
{ |
|||
llvm::Type* argTypes[] = {m_array->getType(), Type::Size}; |
|||
auto func = llvm::Function::Create(llvm::FunctionType::get(Type::Word, argTypes, false), llvm::Function::PrivateLinkage, "array.get", getModule()); |
|||
func->setDoesNotThrow(); |
|||
func->setDoesNotCapture(1); |
|||
|
|||
auto arrayPtr = &func->getArgumentList().front(); |
|||
arrayPtr->setName("arrayPtr"); |
|||
auto index = arrayPtr->getNextNode(); |
|||
index->setName("index"); |
|||
|
|||
InsertPointGuard guard{m_builder}; |
|||
m_builder.SetInsertPoint(llvm::BasicBlock::Create(m_builder.getContext(), {}, func)); |
|||
auto dataPtr = m_builder.CreateStructGEP(getType(), arrayPtr, 0, "dataPtr"); |
|||
auto data = m_builder.CreateLoad(dataPtr, "data"); |
|||
auto valuePtr = m_builder.CreateGEP(data, index, "valuePtr"); |
|||
auto value = m_builder.CreateLoad(valuePtr, "value"); |
|||
m_builder.CreateRet(value); |
|||
return func; |
|||
} |
|||
|
|||
llvm::Function* Array::createGetPtrFunc() |
|||
{ |
|||
llvm::Type* argTypes[] = {m_array->getType(), Type::Size}; |
|||
auto func = llvm::Function::Create(llvm::FunctionType::get(Type::WordPtr, argTypes, false), llvm::Function::PrivateLinkage, "array.getPtr", getModule()); |
|||
func->setDoesNotThrow(); |
|||
func->setDoesNotCapture(1); |
|||
|
|||
auto arrayPtr = &func->getArgumentList().front(); |
|||
arrayPtr->setName("arrayPtr"); |
|||
auto index = arrayPtr->getNextNode(); |
|||
index->setName("index"); |
|||
|
|||
InsertPointGuard guard{m_builder}; |
|||
m_builder.SetInsertPoint(llvm::BasicBlock::Create(m_builder.getContext(), {}, func)); |
|||
auto dataPtr = m_builder.CreateBitCast(arrayPtr, Type::BytePtr->getPointerTo(), "dataPtr"); |
|||
auto data = m_builder.CreateLoad(dataPtr, "data"); |
|||
auto bytePtr = m_builder.CreateGEP(data, index, "bytePtr"); |
|||
auto wordPtr = m_builder.CreateBitCast(bytePtr, Type::WordPtr, "wordPtr"); |
|||
m_builder.CreateRet(wordPtr); |
|||
return func; |
|||
} |
|||
|
|||
llvm::Function* Array::createFreeFunc() |
|||
{ |
|||
auto func = llvm::Function::Create(llvm::FunctionType::get(Type::Void, m_array->getType(), false), llvm::Function::PrivateLinkage, "array.free", getModule()); |
|||
func->setDoesNotThrow(); |
|||
func->setDoesNotCapture(1); |
|||
|
|||
auto freeFunc = llvm::Function::Create(llvm::FunctionType::get(Type::Void, Type::BytePtr, false), llvm::Function::ExternalLinkage, "ext_free", getModule()); |
|||
freeFunc->setDoesNotThrow(); |
|||
freeFunc->setDoesNotCapture(1); |
|||
|
|||
auto arrayPtr = &func->getArgumentList().front(); |
|||
arrayPtr->setName("arrayPtr"); |
|||
|
|||
InsertPointGuard guard{m_builder}; |
|||
m_builder.SetInsertPoint(llvm::BasicBlock::Create(m_builder.getContext(), {}, func)); |
|||
auto dataPtr = m_builder.CreateStructGEP(getType(), arrayPtr, 0, "dataPtr"); |
|||
auto data = m_builder.CreateLoad(dataPtr, "data"); |
|||
auto mem = m_builder.CreateBitCast(data, Type::BytePtr, "mem"); |
|||
m_builder.CreateCall(freeFunc, mem); |
|||
m_builder.CreateRetVoid(); |
|||
return func; |
|||
} |
|||
|
|||
llvm::Function* Array::getReallocFunc() |
|||
{ |
|||
if (auto func = getModule()->getFunction("ext_realloc")) |
|||
return func; |
|||
|
|||
llvm::Type* reallocArgTypes[] = {Type::BytePtr, Type::Size}; |
|||
auto reallocFunc = llvm::Function::Create(llvm::FunctionType::get(Type::BytePtr, reallocArgTypes, false), llvm::Function::ExternalLinkage, "ext_realloc", getModule()); |
|||
reallocFunc->setDoesNotThrow(); |
|||
reallocFunc->setDoesNotAlias(0); |
|||
reallocFunc->setDoesNotCapture(1); |
|||
return reallocFunc; |
|||
} |
|||
|
|||
llvm::Function* Array::createExtendFunc() |
|||
{ |
|||
llvm::Type* argTypes[] = {m_array->getType(), Type::Size}; |
|||
auto func = llvm::Function::Create(llvm::FunctionType::get(Type::Void, argTypes, false), llvm::Function::PrivateLinkage, "array.extend", getModule()); |
|||
func->setDoesNotThrow(); |
|||
func->setDoesNotCapture(1); |
|||
|
|||
auto arrayPtr = &func->getArgumentList().front(); |
|||
arrayPtr->setName("arrayPtr"); |
|||
auto newSize = arrayPtr->getNextNode(); |
|||
newSize->setName("newSize"); |
|||
|
|||
InsertPointGuard guard{m_builder}; |
|||
m_builder.SetInsertPoint(llvm::BasicBlock::Create(m_builder.getContext(), {}, func)); |
|||
auto dataPtr = m_builder.CreateBitCast(arrayPtr, Type::BytePtr->getPointerTo(), "dataPtr");// TODO: Use byte* in Array
|
|||
auto sizePtr = m_builder.CreateStructGEP(getType(), arrayPtr, 1, "sizePtr"); |
|||
auto capPtr = m_builder.CreateStructGEP(getType(), arrayPtr, 2, "capPtr"); |
|||
auto data = m_builder.CreateLoad(dataPtr, "data"); |
|||
auto size = m_builder.CreateLoad(sizePtr, "size"); |
|||
auto extSize = m_builder.CreateNUWSub(newSize, size, "extSize"); |
|||
auto newData = m_reallocFunc.call(m_builder, {data, newSize}, "newData"); // TODO: Check realloc result for null
|
|||
auto extPtr = m_builder.CreateGEP(newData, size, "extPtr"); |
|||
m_builder.CreateMemSet(extPtr, m_builder.getInt8(0), extSize, 16); |
|||
m_builder.CreateStore(newData, dataPtr); |
|||
m_builder.CreateStore(newSize, sizePtr); |
|||
m_builder.CreateStore(newSize, capPtr); |
|||
m_builder.CreateRetVoid(); |
|||
return func; |
|||
} |
|||
|
|||
llvm::Type* Array::getType() |
|||
{ |
|||
llvm::Type* elementTys[] = {Type::WordPtr, Type::Size, Type::Size}; |
|||
static auto arrayTy = llvm::StructType::create(elementTys, "Array"); |
|||
return arrayTy; |
|||
} |
|||
|
|||
Array::Array(llvm::IRBuilder<>& _builder, char const* _name) : |
|||
CompilerHelper(_builder) |
|||
{ |
|||
m_array = m_builder.CreateAlloca(getType(), nullptr, _name); |
|||
m_builder.CreateStore(llvm::ConstantAggregateZero::get(getType()), m_array); |
|||
} |
|||
|
|||
Array::Array(llvm::IRBuilder<>& _builder, llvm::Value* _array) : |
|||
CompilerHelper(_builder), |
|||
m_array(_array) |
|||
{ |
|||
m_builder.CreateStore(llvm::ConstantAggregateZero::get(getType()), m_array); |
|||
} |
|||
|
|||
|
|||
void Array::pop(llvm::Value* _count) |
|||
{ |
|||
auto sizePtr = m_builder.CreateStructGEP(getType(), m_array, 1, "sizePtr"); |
|||
auto size = m_builder.CreateLoad(sizePtr, "size"); |
|||
auto newSize = m_builder.CreateNUWSub(size, _count, "newSize"); |
|||
m_builder.CreateStore(newSize, sizePtr); |
|||
} |
|||
|
|||
llvm::Value* Array::size(llvm::Value* _array) |
|||
{ |
|||
auto sizePtr = m_builder.CreateStructGEP(getType(), _array ? _array : m_array, 1, "sizePtr"); |
|||
return m_builder.CreateLoad(sizePtr, "array.size"); |
|||
} |
|||
|
|||
void Array::extend(llvm::Value* _arrayPtr, llvm::Value* _size) |
|||
{ |
|||
assert(_arrayPtr->getType() == m_array->getType()); |
|||
assert(_size->getType() == Type::Size); |
|||
m_extendFunc.call(m_builder, {_arrayPtr, _size}); |
|||
} |
|||
|
|||
} |
|||
} |
|||
} |
|||
|
|||
extern "C" |
|||
{ |
|||
EXPORT void* ext_realloc(void* _data, size_t _size) noexcept |
|||
{ |
|||
return std::realloc(_data, _size); |
|||
} |
|||
|
|||
EXPORT void ext_free(void* _data) noexcept |
|||
{ |
|||
std::free(_data); |
|||
} |
|||
} |
@ -1,72 +0,0 @@ |
|||
#pragma once |
|||
|
|||
#include <functional> |
|||
|
|||
#include "CompilerHelper.h" |
|||
|
|||
namespace dev |
|||
{ |
|||
namespace eth |
|||
{ |
|||
namespace jit |
|||
{ |
|||
|
|||
class LazyFunction |
|||
{ |
|||
public: |
|||
using Creator = std::function<llvm::Function*()>; |
|||
|
|||
LazyFunction(Creator _creator) : |
|||
m_creator(_creator) |
|||
{} |
|||
|
|||
llvm::Value* call(llvm::IRBuilder<>& _builder, std::initializer_list<llvm::Value*> const& _args, llvm::Twine const& _name = ""); |
|||
|
|||
private: |
|||
llvm::Function* m_func = nullptr; |
|||
Creator m_creator; |
|||
}; |
|||
|
|||
class Array : public CompilerHelper |
|||
{ |
|||
public: |
|||
Array(llvm::IRBuilder<>& _builder, char const* _name); |
|||
Array(llvm::IRBuilder<>& _builder, llvm::Value* _array); |
|||
|
|||
void push(llvm::Value* _value) { m_pushFunc.call(m_builder, {m_array, _value}); } |
|||
void set(llvm::Value* _index, llvm::Value* _value) { m_setFunc.call(m_builder, {m_array, _index, _value}); } |
|||
llvm::Value* get(llvm::Value* _index) { return m_getFunc.call(m_builder, {m_array, _index}); } |
|||
void pop(llvm::Value* _count); |
|||
llvm::Value* size(llvm::Value* _array = nullptr); |
|||
void free() { m_freeFunc.call(m_builder, {m_array}); } |
|||
|
|||
void extend(llvm::Value* _arrayPtr, llvm::Value* _size); |
|||
llvm::Value* getPtr(llvm::Value* _arrayPtr, llvm::Value* _index) { return m_getPtrFunc.call(m_builder, {_arrayPtr, _index}); } |
|||
|
|||
llvm::Value* getPointerTo() const { return m_array; } |
|||
|
|||
static llvm::Type* getType(); |
|||
|
|||
private: |
|||
llvm::Value* m_array = nullptr; |
|||
|
|||
llvm::Function* createArrayPushFunc(); |
|||
llvm::Function* createArraySetFunc(); |
|||
llvm::Function* createArrayGetFunc(); |
|||
llvm::Function* createGetPtrFunc(); |
|||
llvm::Function* createFreeFunc(); |
|||
llvm::Function* createExtendFunc(); |
|||
llvm::Function* getReallocFunc(); |
|||
|
|||
LazyFunction m_pushFunc = {[this](){ return createArrayPushFunc(); }}; |
|||
LazyFunction m_setFunc = {[this](){ return createArraySetFunc(); }}; |
|||
LazyFunction m_getPtrFunc = {[this](){ return createGetPtrFunc(); }}; |
|||
LazyFunction m_getFunc = {[this](){ return createArrayGetFunc(); }}; |
|||
LazyFunction m_freeFunc = {[this](){ return createFreeFunc(); }}; |
|||
LazyFunction m_extendFunc = {[this](){ return createExtendFunc(); }}; |
|||
LazyFunction m_reallocFunc = {[this](){ return getReallocFunc(); }}; |
|||
}; |
|||
|
|||
} |
|||
} |
|||
} |
@ -1,148 +0,0 @@ |
|||
#include "BasicBlock.h" |
|||
|
|||
#include <iostream> |
|||
|
|||
#include "preprocessor/llvm_includes_start.h" |
|||
#include <llvm/IR/CFG.h> |
|||
#include <llvm/IR/Function.h> |
|||
#include <llvm/IR/Instructions.h> |
|||
#include <llvm/IR/IRBuilder.h> |
|||
#include <llvm/Support/raw_os_ostream.h> |
|||
#include "preprocessor/llvm_includes_end.h" |
|||
|
|||
#include "Type.h" |
|||
#include "Utils.h" |
|||
|
|||
namespace dev |
|||
{ |
|||
namespace eth |
|||
{ |
|||
namespace jit |
|||
{ |
|||
|
|||
BasicBlock::BasicBlock(instr_idx _firstInstrIdx, code_iterator _begin, code_iterator _end, llvm::Function* _mainFunc): |
|||
m_firstInstrIdx{_firstInstrIdx}, |
|||
m_begin(_begin), |
|||
m_end(_end), |
|||
m_llvmBB(llvm::BasicBlock::Create(_mainFunc->getContext(), {"Instr.", std::to_string(_firstInstrIdx)}, _mainFunc)) |
|||
{} |
|||
|
|||
LocalStack::LocalStack(Stack& _globalStack): |
|||
m_global(_globalStack) |
|||
{} |
|||
|
|||
void LocalStack::push(llvm::Value* _value) |
|||
{ |
|||
assert(_value->getType() == Type::Word); |
|||
m_local.push_back(_value); |
|||
m_maxSize = std::max(m_maxSize, size()); |
|||
} |
|||
|
|||
llvm::Value* LocalStack::pop() |
|||
{ |
|||
auto item = get(0); |
|||
assert(!m_local.empty() || !m_input.empty()); |
|||
|
|||
if (m_local.size() > 0) |
|||
m_local.pop_back(); |
|||
else |
|||
++m_globalPops; |
|||
|
|||
m_minSize = std::min(m_minSize, size()); |
|||
return item; |
|||
} |
|||
|
|||
/**
|
|||
* Pushes a copy of _index-th element (tos is 0-th elem). |
|||
*/ |
|||
void LocalStack::dup(size_t _index) |
|||
{ |
|||
auto val = get(_index); |
|||
push(val); |
|||
} |
|||
|
|||
/**
|
|||
* Swaps tos with _index-th element (tos is 0-th elem). |
|||
* _index must be > 0. |
|||
*/ |
|||
void LocalStack::swap(size_t _index) |
|||
{ |
|||
assert(_index > 0); |
|||
auto val = get(_index); |
|||
auto tos = get(0); |
|||
set(_index, tos); |
|||
set(0, val); |
|||
} |
|||
|
|||
llvm::Value* LocalStack::get(size_t _index) |
|||
{ |
|||
if (_index < m_local.size()) |
|||
return *(m_local.rbegin() + _index); // count from back
|
|||
|
|||
auto idx = _index - m_local.size() + m_globalPops; |
|||
if (idx >= m_input.size()) |
|||
m_input.resize(idx + 1); |
|||
auto& item = m_input[idx]; |
|||
|
|||
if (!item) |
|||
{ |
|||
item = m_global.get(idx); // Reach an item from global stack
|
|||
m_minSize = std::min(m_minSize, -static_cast<ssize_t>(idx) - 1); // and remember required stack size
|
|||
} |
|||
|
|||
return item; |
|||
} |
|||
|
|||
void LocalStack::set(size_t _index, llvm::Value* _word) |
|||
{ |
|||
if (_index < m_local.size()) |
|||
{ |
|||
*(m_local.rbegin() + _index) = _word; |
|||
return; |
|||
} |
|||
|
|||
auto idx = _index - m_local.size() + m_globalPops; |
|||
assert(idx < m_input.size()); |
|||
m_input[idx] = _word; |
|||
} |
|||
|
|||
|
|||
void LocalStack::finalize(llvm::IRBuilder<>& _builder, llvm::BasicBlock& _bb) |
|||
{ |
|||
auto blockTerminator = _bb.getTerminator(); |
|||
if (!blockTerminator || blockTerminator->getOpcode() != llvm::Instruction::Ret) |
|||
{ |
|||
// Not needed in case of ret instruction. Ret invalidates the stack.
|
|||
if (blockTerminator) |
|||
_builder.SetInsertPoint(blockTerminator); |
|||
else |
|||
_builder.SetInsertPoint(&_bb); |
|||
|
|||
// Update items fetched from global stack ignoring the poped ones
|
|||
assert(m_globalPops <= m_input.size()); // pop() always does get()
|
|||
for (auto i = m_globalPops; i < m_input.size(); ++i) |
|||
{ |
|||
if (m_input[i]) |
|||
m_global.set(i, m_input[i]); |
|||
} |
|||
|
|||
// Add new items
|
|||
auto pops = m_globalPops; // Copy pops counter to keep original value
|
|||
for (auto& item: m_local) |
|||
{ |
|||
if (pops) // Override poped global items
|
|||
m_global.set(--pops, item); // using pops counter as the index
|
|||
else |
|||
m_global.push(item); |
|||
} |
|||
|
|||
// Pop not overriden items
|
|||
if (pops) |
|||
m_global.pop(pops); |
|||
} |
|||
} |
|||
|
|||
|
|||
} |
|||
} |
|||
} |
@ -1,86 +0,0 @@ |
|||
#pragma once |
|||
|
|||
#include <vector> |
|||
|
|||
#include "Common.h" |
|||
#include "Stack.h" |
|||
|
|||
namespace dev |
|||
{ |
|||
namespace eth |
|||
{ |
|||
namespace jit |
|||
{ |
|||
using namespace evmjit; |
|||
using instr_idx = uint64_t; |
|||
|
|||
class LocalStack |
|||
{ |
|||
public: |
|||
explicit LocalStack(Stack& _globalStack); |
|||
LocalStack(LocalStack const&) = delete; |
|||
void operator=(LocalStack const&) = delete; |
|||
|
|||
/// Pushes value on stack
|
|||
void push(llvm::Value* _value); |
|||
|
|||
/// Pops and returns top value
|
|||
llvm::Value* pop(); |
|||
|
|||
/// Duplicates _index'th value on stack
|
|||
void dup(size_t _index); |
|||
|
|||
/// Swaps _index'th value on stack with a value on stack top.
|
|||
/// @param _index Index of value to be swaped. Must be > 0.
|
|||
void swap(size_t _index); |
|||
|
|||
ssize_t size() const { return static_cast<ssize_t>(m_local.size()) - static_cast<ssize_t>(m_globalPops); } |
|||
ssize_t minSize() const { return m_minSize; } |
|||
ssize_t maxSize() const { return m_maxSize; } |
|||
|
|||
/// Finalize local stack: check the requirements and update of the global stack.
|
|||
void finalize(llvm::IRBuilder<>& _builder, llvm::BasicBlock& _bb); |
|||
|
|||
private: |
|||
/// Gets _index'th value from top (counting from 0)
|
|||
llvm::Value* get(size_t _index); |
|||
|
|||
/// Sets _index'th value from top (counting from 0)
|
|||
void set(size_t _index, llvm::Value* _value); |
|||
|
|||
/// Items fetched from global stack. First element matches the top of the global stack.
|
|||
/// Can contain nulls if some items has been skipped.
|
|||
std::vector<llvm::Value*> m_input; |
|||
|
|||
/// Local stack items that has not been pushed to global stack. First item is just above global stack.
|
|||
std::vector<llvm::Value*> m_local; |
|||
|
|||
Stack& m_global; ///< Reference to global stack.
|
|||
|
|||
size_t m_globalPops = 0; ///< Number of items poped from global stack. In other words: global - local stack overlap.
|
|||
ssize_t m_minSize = 0; ///< Minimum reached local stack size. Can be negative.
|
|||
ssize_t m_maxSize = 0; ///< Maximum reached local stack size.
|
|||
}; |
|||
|
|||
class BasicBlock |
|||
{ |
|||
public: |
|||
explicit BasicBlock(instr_idx _firstInstrIdx, code_iterator _begin, code_iterator _end, llvm::Function* _mainFunc); |
|||
|
|||
llvm::BasicBlock* llvm() { return m_llvmBB; } |
|||
|
|||
instr_idx firstInstrIdx() const { return m_firstInstrIdx; } |
|||
code_iterator begin() const { return m_begin; } |
|||
code_iterator end() const { return m_end; } |
|||
|
|||
private: |
|||
instr_idx const m_firstInstrIdx = 0; ///< Code index of first instruction in the block
|
|||
code_iterator const m_begin = {}; ///< Iterator pointing code beginning of the block
|
|||
code_iterator const m_end = {}; ///< Iterator pointing code end of the block
|
|||
|
|||
llvm::BasicBlock* const m_llvmBB; ///< Reference to the LLVM BasicBlock
|
|||
}; |
|||
|
|||
} |
|||
} |
|||
} |
@ -1,9 +0,0 @@ |
|||
|
|||
#define EVMJIT_VERSION "${EVMJIT_VERSION}" |
|||
#define EVMJIT_VERSION_MAJOR ${EVMJIT_VERSION_MAJOR} |
|||
#define EVMJIT_VERSION_MINOR ${EVMJIT_VERSION_MINOR} |
|||
#define EVMJIT_VERSION_PATCH ${EVMJIT_VERSION_PATCH} |
|||
|
|||
#define LLVM_VERSION "${LLVM_PACKAGE_VERSION}" |
|||
#define LLVM_ASSERTIONS "${LLVM_ENABLE_ASSERTIONS}" |
|||
#define LLVM_DEBUG ${LLVM_DEBUG} |
@ -1,58 +0,0 @@ |
|||
set(TARGET_NAME evmjit) |
|||
|
|||
get_filename_component(EVMJIT_INCLUDE_DIR ../include ABSOLUTE) |
|||
|
|||
set(SOURCES |
|||
JIT.cpp ${EVMJIT_INCLUDE_DIR}/evmjit/JIT.h |
|||
JIT-c.cpp ${EVMJIT_INCLUDE_DIR}/evmjit/JIT-c.h |
|||
Arith256.cpp Arith256.h |
|||
Array.cpp Array.h |
|||
BasicBlock.cpp BasicBlock.h |
|||
Cache.cpp Cache.h |
|||
Common.h |
|||
Compiler.cpp Compiler.h |
|||
CompilerHelper.cpp CompilerHelper.h |
|||
Endianness.cpp Endianness.h |
|||
ExecStats.cpp ExecStats.h |
|||
Ext.cpp Ext.h |
|||
GasMeter.cpp GasMeter.h |
|||
Instruction.cpp Instruction.h |
|||
Memory.cpp Memory.h |
|||
Optimizer.cpp Optimizer.h |
|||
RuntimeManager.cpp RuntimeManager.h |
|||
Stack.cpp Stack.h |
|||
Type.cpp Type.h |
|||
Utils.cpp Utils.h |
|||
) |
|||
source_group("" FILES ${SOURCES}) |
|||
|
|||
if(${CMAKE_CXX_COMPILER_ID} STREQUAL "MSVC") |
|||
else() |
|||
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -fno-rtti") |
|||
endif() |
|||
|
|||
if(${EVMJIT_VERSION_MAJOR} EQUAL 0) |
|||
set(EVMJIT_SOVERSION "0.${EVMJIT_VERSION_MINOR}") |
|||
else() |
|||
set(EVMJIT_SOVERSION ${EVMJIT_VERSION_MAJOR}) |
|||
endif() |
|||
|
|||
|
|||
string(COMPARE EQUAL "${LLVM_ENABLE_ASSERTIONS}" "ON" LLVM_DEBUG) |
|||
configure_file(BuildInfo.h.in ${CMAKE_CURRENT_BINARY_DIR}/gen/BuildInfo.gen.h) |
|||
|
|||
message(STATUS "EVM JIT version: ${EVMJIT_VERSION_MAJOR}.${EVMJIT_VERSION_MINOR}.${EVMJIT_VERSION_PATCH}") |
|||
|
|||
add_library(${TARGET_NAME} SHARED ${SOURCES} gen/BuildInfo.gen.h) |
|||
set_target_properties(${TARGET_NAME} PROPERTIES |
|||
VERSION ${EVMJIT_VERSION} SOVERSION ${EVMJIT_SOVERSION} |
|||
FOLDER "libs") |
|||
|
|||
target_include_directories(${TARGET_NAME} PUBLIC ${EVMJIT_INCLUDE_DIR}) |
|||
target_include_directories(${TARGET_NAME} PRIVATE ${LLVM_INCLUDE_DIRS}) |
|||
target_include_directories(${TARGET_NAME} PRIVATE ${CMAKE_CURRENT_BINARY_DIR}/gen) |
|||
target_compile_definitions(${TARGET_NAME} PRIVATE ${LLVM_DEFINITIONS}) |
|||
target_link_libraries(${TARGET_NAME} PRIVATE ${LLVM_LIBS}) |
|||
|
|||
install(TARGETS ${TARGET_NAME} LIBRARY DESTINATION lib ARCHIVE DESTINATION lib RUNTIME DESTINATION bin) |
|||
install(DIRECTORY ${EVMJIT_INCLUDE_DIR} DESTINATION include) |
@ -1,190 +0,0 @@ |
|||
#include "Cache.h" |
|||
|
|||
#include <mutex> |
|||
|
|||
#include "preprocessor/llvm_includes_start.h" |
|||
#include <llvm/IR/Module.h> |
|||
#include <llvm/IR/LLVMContext.h> |
|||
#include <llvm/IR/Instructions.h> |
|||
#include <llvm/ExecutionEngine/ExecutionEngine.h> |
|||
#include <llvm/Support/Path.h> |
|||
#include <llvm/Support/FileSystem.h> |
|||
#include <llvm/Support/raw_os_ostream.h> |
|||
#include "preprocessor/llvm_includes_end.h" |
|||
|
|||
#include "ExecStats.h" |
|||
#include "Utils.h" |
|||
#include "BuildInfo.gen.h" |
|||
|
|||
namespace dev |
|||
{ |
|||
namespace evmjit |
|||
{ |
|||
|
|||
namespace |
|||
{ |
|||
using Guard = std::lock_guard<std::mutex>; |
|||
std::mutex x_cacheMutex; |
|||
CacheMode g_mode; |
|||
std::unique_ptr<llvm::MemoryBuffer> g_lastObject; |
|||
JITListener* g_listener; |
|||
|
|||
llvm::StringRef getLibVersionStamp() |
|||
{ |
|||
return EVMJIT_VERSION; |
|||
} |
|||
} |
|||
|
|||
ObjectCache* Cache::init(CacheMode _mode, JITListener* _listener) |
|||
{ |
|||
Guard g{x_cacheMutex}; |
|||
|
|||
g_mode = _mode; |
|||
g_listener = _listener; |
|||
|
|||
if (g_mode == CacheMode::clear) |
|||
{ |
|||
Cache::clear(); |
|||
g_mode = CacheMode::off; |
|||
} |
|||
|
|||
if (g_mode != CacheMode::off) |
|||
{ |
|||
static ObjectCache objectCache; |
|||
return &objectCache; |
|||
} |
|||
return nullptr; |
|||
} |
|||
|
|||
void Cache::clear() |
|||
{ |
|||
Guard g{x_cacheMutex}; |
|||
|
|||
using namespace llvm::sys; |
|||
llvm::SmallString<256> cachePath; |
|||
path::system_temp_directory(false, cachePath); |
|||
path::append(cachePath, "evm_objs"); |
|||
|
|||
std::error_code err; |
|||
for (auto it = fs::directory_iterator{cachePath.str(), err}; it != fs::directory_iterator{}; it.increment(err)) |
|||
fs::remove(it->path()); |
|||
} |
|||
|
|||
void Cache::preload(llvm::ExecutionEngine& _ee, std::unordered_map<std::string, uint64_t>& _funcCache) |
|||
{ |
|||
Guard g{x_cacheMutex}; |
|||
|
|||
// TODO: Cache dir should be in one place
|
|||
using namespace llvm::sys; |
|||
llvm::SmallString<256> cachePath; |
|||
path::system_temp_directory(false, cachePath); |
|||
path::append(cachePath, "evm_objs"); |
|||
|
|||
// Disable listener
|
|||
auto listener = g_listener; |
|||
g_listener = nullptr; |
|||
|
|||
std::error_code err; |
|||
for (auto it = fs::directory_iterator{cachePath.str(), err}; it != fs::directory_iterator{}; it.increment(err)) |
|||
{ |
|||
auto name = it->path().substr(cachePath.size() + 1); |
|||
if (auto module = getObject(name)) |
|||
{ |
|||
DLOG(cache) << "Preload: " << name << "\n"; |
|||
_ee.addModule(std::move(module)); |
|||
auto addr = _ee.getFunctionAddress(name); |
|||
assert(addr); |
|||
_funcCache[std::move(name)] = addr; |
|||
} |
|||
} |
|||
|
|||
g_listener = listener; |
|||
} |
|||
|
|||
std::unique_ptr<llvm::Module> Cache::getObject(std::string const& id) |
|||
{ |
|||
Guard g{x_cacheMutex}; |
|||
|
|||
if (g_mode != CacheMode::on && g_mode != CacheMode::read) |
|||
return nullptr; |
|||
|
|||
// TODO: Disabled because is not thread-safe.
|
|||
//if (g_listener)
|
|||
// g_listener->stateChanged(ExecState::CacheLoad);
|
|||
|
|||
DLOG(cache) << id << ": search\n"; |
|||
if (!CHECK(!g_lastObject)) |
|||
g_lastObject = nullptr; |
|||
|
|||
llvm::SmallString<256> cachePath; |
|||
llvm::sys::path::system_temp_directory(false, cachePath); |
|||
llvm::sys::path::append(cachePath, "evm_objs", id); |
|||
|
|||
if (auto r = llvm::MemoryBuffer::getFile(cachePath.str(), -1, false)) |
|||
{ |
|||
auto& buf = r.get(); |
|||
auto expectedStamp = getLibVersionStamp(); |
|||
auto stampSize = expectedStamp.size(); |
|||
auto objStamp = buf->getBufferSize() >= stampSize ? llvm::StringRef{buf->getBufferEnd() - stampSize, stampSize} : llvm::StringRef{}; |
|||
if (objStamp == expectedStamp) |
|||
g_lastObject = llvm::MemoryBuffer::getMemBufferCopy(r.get()->getBuffer()); |
|||
else |
|||
DLOG(cache) << "Unmatched version: " << objStamp.str() << ", expected " << expectedStamp.str() << "\n"; |
|||
} |
|||
else if (r.getError() != std::make_error_code(std::errc::no_such_file_or_directory)) |
|||
DLOG(cache) << r.getError().message(); // TODO: Add warning log
|
|||
|
|||
if (g_lastObject) // if object found create fake module
|
|||
{ |
|||
DLOG(cache) << id << ": found\n"; |
|||
auto&& context = llvm::getGlobalContext(); |
|||
auto module = std::unique_ptr<llvm::Module>(new llvm::Module(id, context)); |
|||
auto mainFuncType = llvm::FunctionType::get(llvm::Type::getVoidTy(context), {}, false); |
|||
auto mainFunc = llvm::Function::Create(mainFuncType, llvm::Function::ExternalLinkage, id, module.get()); |
|||
auto bb = llvm::BasicBlock::Create(context, {}, mainFunc); |
|||
bb->getInstList().push_back(new llvm::UnreachableInst{context}); |
|||
return module; |
|||
} |
|||
DLOG(cache) << id << ": not found\n"; |
|||
return nullptr; |
|||
} |
|||
|
|||
|
|||
void ObjectCache::notifyObjectCompiled(llvm::Module const* _module, llvm::MemoryBufferRef _object) |
|||
{ |
|||
Guard g{x_cacheMutex}; |
|||
|
|||
// Only in "on" and "write" mode
|
|||
if (g_mode != CacheMode::on && g_mode != CacheMode::write) |
|||
return; |
|||
|
|||
// TODO: Disabled because is not thread-safe.
|
|||
// if (g_listener)
|
|||
// g_listener->stateChanged(ExecState::CacheWrite);
|
|||
|
|||
auto&& id = _module->getModuleIdentifier(); |
|||
llvm::SmallString<256> cachePath; |
|||
llvm::sys::path::system_temp_directory(false, cachePath); |
|||
llvm::sys::path::append(cachePath, "evm_objs"); |
|||
|
|||
if (llvm::sys::fs::create_directory(cachePath.str())) |
|||
DLOG(cache) << "Cannot create cache dir " << cachePath.str().str() << "\n"; |
|||
|
|||
llvm::sys::path::append(cachePath, id); |
|||
|
|||
DLOG(cache) << id << ": write\n"; |
|||
std::error_code error; |
|||
llvm::raw_fd_ostream cacheFile(cachePath.c_str(), error, llvm::sys::fs::F_None); |
|||
cacheFile << _object.getBuffer() << getLibVersionStamp(); |
|||
} |
|||
|
|||
std::unique_ptr<llvm::MemoryBuffer> ObjectCache::getObject(llvm::Module const* _module) |
|||
{ |
|||
Guard g{x_cacheMutex}; |
|||
|
|||
DLOG(cache) << _module->getModuleIdentifier() << ": use\n"; |
|||
return std::move(g_lastObject); |
|||
} |
|||
|
|||
} |
|||
} |
@ -1,59 +0,0 @@ |
|||
#pragma once |
|||
|
|||
#include <memory> |
|||
#include <unordered_map> |
|||
|
|||
#include "preprocessor/llvm_includes_start.h" |
|||
#include <llvm/ExecutionEngine/ObjectCache.h> |
|||
#include "preprocessor/llvm_includes_end.h" |
|||
|
|||
namespace llvm |
|||
{ |
|||
class ExecutionEngine; |
|||
} |
|||
|
|||
namespace dev |
|||
{ |
|||
namespace evmjit |
|||
{ |
|||
class JITListener; |
|||
|
|||
enum class CacheMode |
|||
{ |
|||
on, |
|||
off, |
|||
read, |
|||
write, |
|||
clear, |
|||
preload |
|||
}; |
|||
|
|||
class ObjectCache : public llvm::ObjectCache |
|||
{ |
|||
public: |
|||
/// notifyObjectCompiled - Provides a pointer to compiled code for Module M.
|
|||
virtual void notifyObjectCompiled(llvm::Module const* _module, llvm::MemoryBufferRef _object) final override; |
|||
|
|||
/// getObjectCopy - Returns a pointer to a newly allocated MemoryBuffer that
|
|||
/// contains the object which corresponds with Module M, or 0 if an object is
|
|||
/// not available. The caller owns both the MemoryBuffer returned by this
|
|||
/// and the memory it references.
|
|||
virtual std::unique_ptr<llvm::MemoryBuffer> getObject(llvm::Module const* _module) final override; |
|||
}; |
|||
|
|||
|
|||
class Cache |
|||
{ |
|||
public: |
|||
static ObjectCache* init(CacheMode _mode, JITListener* _listener); |
|||
static std::unique_ptr<llvm::Module> getObject(std::string const& id); |
|||
|
|||
/// Clears cache storage
|
|||
static void clear(); |
|||
|
|||
/// Loads all available cached objects to ExecutionEngine
|
|||
static void preload(llvm::ExecutionEngine& _ee, std::unordered_map<std::string, uint64_t>& _funcCache); |
|||
}; |
|||
|
|||
} |
|||
} |
@ -1,16 +0,0 @@ |
|||
#pragma once |
|||
|
|||
#include <cstdint> |
|||
|
|||
namespace dev |
|||
{ |
|||
namespace evmjit |
|||
{ |
|||
|
|||
using byte = uint8_t; |
|||
using code_iterator = byte const*; |
|||
|
|||
#define UNTESTED assert(false) |
|||
|
|||
} |
|||
} |
@ -1,800 +0,0 @@ |
|||
#include "Compiler.h" |
|||
|
|||
#include <functional> |
|||
#include <fstream> |
|||
#include <chrono> |
|||
#include <sstream> |
|||
|
|||
#include "preprocessor/llvm_includes_start.h" |
|||
#include <llvm/IR/CFG.h> |
|||
#include <llvm/IR/Module.h> |
|||
#include <llvm/IR/IntrinsicInst.h> |
|||
#include "preprocessor/llvm_includes_end.h" |
|||
|
|||
#include "Instruction.h" |
|||
#include "Type.h" |
|||
#include "Memory.h" |
|||
#include "Stack.h" |
|||
#include "Ext.h" |
|||
#include "GasMeter.h" |
|||
#include "Utils.h" |
|||
#include "Endianness.h" |
|||
#include "Arith256.h" |
|||
#include "RuntimeManager.h" |
|||
|
|||
namespace dev |
|||
{ |
|||
namespace eth |
|||
{ |
|||
namespace jit |
|||
{ |
|||
|
|||
static const auto c_destIdxLabel = "destIdx"; |
|||
|
|||
Compiler::Compiler(Options const& _options): |
|||
m_options(_options), |
|||
m_builder(llvm::getGlobalContext()) |
|||
{ |
|||
Type::init(m_builder.getContext()); |
|||
} |
|||
|
|||
std::vector<BasicBlock> Compiler::createBasicBlocks(code_iterator _codeBegin, code_iterator _codeEnd, llvm::SwitchInst& _jumpTable) |
|||
{ |
|||
/// Helper function that skips push data and finds next iterator (can be the end)
|
|||
auto skipPushDataAndGetNext = [](code_iterator _curr, code_iterator _end) |
|||
{ |
|||
static const auto push1 = static_cast<size_t>(Instruction::PUSH1); |
|||
static const auto push32 = static_cast<size_t>(Instruction::PUSH32); |
|||
size_t offset = 1; |
|||
if (*_curr >= push1 && *_curr <= push32) |
|||
offset += std::min<size_t>(*_curr - push1 + 1, (_end - _curr) - 1); |
|||
return _curr + offset; |
|||
}; |
|||
|
|||
// Skip all STOPs in the end
|
|||
for (; _codeEnd != _codeBegin; --_codeEnd) |
|||
if (*(_codeEnd - 1) != static_cast<byte>(Instruction::STOP)) |
|||
break; |
|||
|
|||
std::vector<BasicBlock> blocks; |
|||
|
|||
auto begin = _codeBegin; // begin of current block
|
|||
for (auto curr = begin, next = begin; curr != _codeEnd; curr = next) |
|||
{ |
|||
next = skipPushDataAndGetNext(curr, _codeEnd); |
|||
|
|||
bool isEnd = false; |
|||
switch (Instruction(*curr)) |
|||
{ |
|||
case Instruction::JUMP: |
|||
case Instruction::JUMPI: |
|||
case Instruction::RETURN: |
|||
case Instruction::STOP: |
|||
case Instruction::SUICIDE: |
|||
isEnd = true; |
|||
break; |
|||
|
|||
default: |
|||
break; |
|||
} |
|||
|
|||
assert(next <= _codeEnd); |
|||
if (next == _codeEnd || Instruction(*next) == Instruction::JUMPDEST) |
|||
isEnd = true; |
|||
|
|||
if (isEnd) |
|||
{ |
|||
auto beginIdx = begin - _codeBegin; |
|||
blocks.emplace_back(beginIdx, begin, next, m_mainFunc); |
|||
if (Instruction(*begin) == Instruction::JUMPDEST) |
|||
_jumpTable.addCase(Constant::get(beginIdx), blocks.back().llvm()); |
|||
begin = next; |
|||
} |
|||
} |
|||
|
|||
return blocks; |
|||
} |
|||
|
|||
void Compiler::resolveJumps() |
|||
{ |
|||
// Iterate through all EVM instructions blocks (skip first 4 - special blocks).
|
|||
for (auto it = std::next(m_mainFunc->begin(), 4); it != m_mainFunc->end(); ++it) |
|||
{ |
|||
auto jumpTable = llvm::cast<llvm::SwitchInst>(m_jumpTableBB->getTerminator()); |
|||
auto jumpTableInput = llvm::cast<llvm::PHINode>(m_jumpTableBB->begin()); |
|||
auto nextBlock = it->getNextNode() != m_mainFunc->end() ? it->getNextNode() : m_stopBB; |
|||
auto term = it->getTerminator(); |
|||
|
|||
if (!term) // Block may have no terminator if the next instruction is a jump destination.
|
|||
llvm::IRBuilder<>{it}.CreateBr(nextBlock); |
|||
else if (auto jump = llvm::dyn_cast<llvm::BranchInst>(term)) |
|||
if (jump->getSuccessor(0) == m_jumpTableBB) |
|||
{ |
|||
auto destIdx = llvm::cast<llvm::ValueAsMetadata>(jump->getMetadata(c_destIdxLabel)->getOperand(0))->getValue(); |
|||
if (auto constant = llvm::dyn_cast<llvm::ConstantInt>(destIdx)) |
|||
{ |
|||
// If destination index is a constant do direct jump to the destination block.
|
|||
auto bb = jumpTable->findCaseValue(constant).getCaseSuccessor(); |
|||
jump->setSuccessor(0, bb); |
|||
} |
|||
else |
|||
jumpTableInput->addIncoming(destIdx, it); // Fill up PHI node
|
|||
|
|||
if (jump->isConditional()) |
|||
jump->setSuccessor(1, nextBlock); // Set next block for conditional jumps
|
|||
} |
|||
} |
|||
} |
|||
|
|||
std::unique_ptr<llvm::Module> Compiler::compile(code_iterator _begin, code_iterator _end, std::string const& _id) |
|||
{ |
|||
auto module = std::unique_ptr<llvm::Module>(new llvm::Module(_id, m_builder.getContext())); |
|||
|
|||
// Create main function
|
|||
auto mainFuncType = llvm::FunctionType::get(Type::MainReturn, Type::RuntimePtr, false); |
|||
m_mainFunc = llvm::Function::Create(mainFuncType, llvm::Function::ExternalLinkage, _id, module.get()); |
|||
m_mainFunc->getArgumentList().front().setName("rt"); |
|||
|
|||
// Create entry basic block
|
|||
auto entryBlock = llvm::BasicBlock::Create(m_builder.getContext(), {}, m_mainFunc); |
|||
|
|||
m_stopBB = llvm::BasicBlock::Create(m_mainFunc->getContext(), "Stop", m_mainFunc); |
|||
m_abortBB = llvm::BasicBlock::Create(m_mainFunc->getContext(), "Abort", m_mainFunc); |
|||
m_jumpTableBB = llvm::BasicBlock::Create(m_mainFunc->getContext(), "JumpTable", m_mainFunc); |
|||
m_builder.SetInsertPoint(m_jumpTableBB); |
|||
auto target = m_builder.CreatePHI(Type::Word, 16, "target"); |
|||
auto& jumpTable = *m_builder.CreateSwitch(target, m_abortBB); |
|||
|
|||
m_builder.SetInsertPoint(entryBlock); |
|||
|
|||
auto blocks = createBasicBlocks(_begin, _end, jumpTable); |
|||
|
|||
// Init runtime structures.
|
|||
RuntimeManager runtimeManager(m_builder, _begin, _end); |
|||
GasMeter gasMeter(m_builder, runtimeManager); |
|||
Memory memory(runtimeManager, gasMeter); |
|||
Ext ext(runtimeManager, memory); |
|||
Stack stack(m_builder); |
|||
runtimeManager.setStack(stack); // Runtime Manager will free stack memory
|
|||
Arith256 arith(m_builder); |
|||
|
|||
auto jmpBufWords = m_builder.CreateAlloca(Type::BytePtr, m_builder.getInt64(3), "jmpBuf.words"); |
|||
auto frameaddress = llvm::Intrinsic::getDeclaration(module.get(), llvm::Intrinsic::frameaddress); |
|||
auto fp = m_builder.CreateCall(frameaddress, m_builder.getInt32(0), "fp"); |
|||
m_builder.CreateStore(fp, jmpBufWords); |
|||
auto stacksave = llvm::Intrinsic::getDeclaration(module.get(), llvm::Intrinsic::stacksave); |
|||
auto sp = m_builder.CreateCall(stacksave, {}, "sp"); |
|||
auto jmpBufSp = m_builder.CreateConstInBoundsGEP1_64(jmpBufWords, 2, "jmpBuf.sp"); |
|||
m_builder.CreateStore(sp, jmpBufSp); |
|||
auto setjmp = llvm::Intrinsic::getDeclaration(module.get(), llvm::Intrinsic::eh_sjlj_setjmp); |
|||
auto jmpBuf = m_builder.CreateBitCast(jmpBufWords, Type::BytePtr, "jmpBuf"); |
|||
auto r = m_builder.CreateCall(setjmp, jmpBuf); |
|||
auto normalFlow = m_builder.CreateICmpEQ(r, m_builder.getInt32(0)); |
|||
runtimeManager.setJmpBuf(jmpBuf); |
|||
|
|||
auto firstBB = blocks.empty() ? m_stopBB : blocks.front().llvm(); |
|||
m_builder.CreateCondBr(normalFlow, firstBB, m_abortBB, Type::expectTrue); |
|||
|
|||
for (auto& block: blocks) |
|||
compileBasicBlock(block, runtimeManager, arith, memory, ext, gasMeter, stack); |
|||
|
|||
// Code for special blocks:
|
|||
m_builder.SetInsertPoint(m_stopBB); |
|||
runtimeManager.exit(ReturnCode::Stop); |
|||
|
|||
m_builder.SetInsertPoint(m_abortBB); |
|||
runtimeManager.exit(ReturnCode::OutOfGas); |
|||
|
|||
resolveJumps(); |
|||
|
|||
return module; |
|||
} |
|||
|
|||
|
|||
void Compiler::compileBasicBlock(BasicBlock& _basicBlock, RuntimeManager& _runtimeManager, |
|||
Arith256& _arith, Memory& _memory, Ext& _ext, GasMeter& _gasMeter, Stack& _globalStack) |
|||
{ |
|||
m_builder.SetInsertPoint(_basicBlock.llvm()); |
|||
LocalStack stack{_globalStack}; |
|||
|
|||
for (auto it = _basicBlock.begin(); it != _basicBlock.end(); ++it) |
|||
{ |
|||
auto inst = Instruction(*it); |
|||
|
|||
_gasMeter.count(inst); |
|||
|
|||
switch (inst) |
|||
{ |
|||
|
|||
case Instruction::ADD: |
|||
{ |
|||
auto lhs = stack.pop(); |
|||
auto rhs = stack.pop(); |
|||
auto result = m_builder.CreateAdd(lhs, rhs); |
|||
stack.push(result); |
|||
break; |
|||
} |
|||
|
|||
case Instruction::SUB: |
|||
{ |
|||
auto lhs = stack.pop(); |
|||
auto rhs = stack.pop(); |
|||
auto result = m_builder.CreateSub(lhs, rhs); |
|||
stack.push(result); |
|||
break; |
|||
} |
|||
|
|||
case Instruction::MUL: |
|||
{ |
|||
auto lhs = stack.pop(); |
|||
auto rhs = stack.pop(); |
|||
auto res = m_builder.CreateMul(lhs, rhs); |
|||
stack.push(res); |
|||
break; |
|||
} |
|||
|
|||
case Instruction::DIV: |
|||
{ |
|||
auto d = stack.pop(); |
|||
auto n = stack.pop(); |
|||
auto divByZero = m_builder.CreateICmpEQ(n, Constant::get(0)); |
|||
n = m_builder.CreateSelect(divByZero, Constant::get(1), n); // protect against hardware signal
|
|||
auto r = m_builder.CreateUDiv(d, n); |
|||
r = m_builder.CreateSelect(divByZero, Constant::get(0), r); |
|||
stack.push(r); |
|||
break; |
|||
} |
|||
|
|||
case Instruction::SDIV: |
|||
{ |
|||
auto d = stack.pop(); |
|||
auto n = stack.pop(); |
|||
auto divByZero = m_builder.CreateICmpEQ(n, Constant::get(0)); |
|||
auto divByMinusOne = m_builder.CreateICmpEQ(n, Constant::get(-1)); |
|||
n = m_builder.CreateSelect(divByZero, Constant::get(1), n); // protect against hardware signal
|
|||
auto r = m_builder.CreateSDiv(d, n); |
|||
r = m_builder.CreateSelect(divByZero, Constant::get(0), r); |
|||
auto dNeg = m_builder.CreateSub(Constant::get(0), d); |
|||
r = m_builder.CreateSelect(divByMinusOne, dNeg, r); // protect against undef i256.min / -1
|
|||
stack.push(r); |
|||
break; |
|||
} |
|||
|
|||
case Instruction::MOD: |
|||
{ |
|||
auto d = stack.pop(); |
|||
auto n = stack.pop(); |
|||
auto divByZero = m_builder.CreateICmpEQ(n, Constant::get(0)); |
|||
n = m_builder.CreateSelect(divByZero, Constant::get(1), n); // protect against hardware signal
|
|||
auto r = m_builder.CreateURem(d, n); |
|||
r = m_builder.CreateSelect(divByZero, Constant::get(0), r); |
|||
stack.push(r); |
|||
break; |
|||
} |
|||
|
|||
case Instruction::SMOD: |
|||
{ |
|||
auto d = stack.pop(); |
|||
auto n = stack.pop(); |
|||
auto divByZero = m_builder.CreateICmpEQ(n, Constant::get(0)); |
|||
auto divByMinusOne = m_builder.CreateICmpEQ(n, Constant::get(-1)); |
|||
n = m_builder.CreateSelect(divByZero, Constant::get(1), n); // protect against hardware signal
|
|||
auto r = m_builder.CreateSRem(d, n); |
|||
r = m_builder.CreateSelect(divByZero, Constant::get(0), r); |
|||
r = m_builder.CreateSelect(divByMinusOne, Constant::get(0), r); // protect against undef i256.min / -1
|
|||
stack.push(r); |
|||
break; |
|||
} |
|||
|
|||
case Instruction::ADDMOD: |
|||
{ |
|||
auto i512Ty = m_builder.getIntNTy(512); |
|||
auto a = stack.pop(); |
|||
auto b = stack.pop(); |
|||
auto m = stack.pop(); |
|||
auto divByZero = m_builder.CreateICmpEQ(m, Constant::get(0)); |
|||
a = m_builder.CreateZExt(a, i512Ty); |
|||
b = m_builder.CreateZExt(b, i512Ty); |
|||
m = m_builder.CreateZExt(m, i512Ty); |
|||
auto s = m_builder.CreateNUWAdd(a, b); |
|||
s = m_builder.CreateURem(s, m); |
|||
s = m_builder.CreateTrunc(s, Type::Word); |
|||
s = m_builder.CreateSelect(divByZero, Constant::get(0), s); |
|||
stack.push(s); |
|||
break; |
|||
} |
|||
|
|||
case Instruction::MULMOD: |
|||
{ |
|||
auto i512Ty = m_builder.getIntNTy(512); |
|||
auto a = stack.pop(); |
|||
auto b = stack.pop(); |
|||
auto m = stack.pop(); |
|||
auto divByZero = m_builder.CreateICmpEQ(m, Constant::get(0)); |
|||
m = m_builder.CreateZExt(m, i512Ty); |
|||
// TODO: Add support for i256 x i256 -> i512 in LowerEVM pass
|
|||
llvm::Value* p = m_builder.CreateCall(Arith256::getMul512Func(*_basicBlock.llvm()->getParent()->getParent()), {a, b}); |
|||
p = m_builder.CreateURem(p, m); |
|||
p = m_builder.CreateTrunc(p, Type::Word); |
|||
p = m_builder.CreateSelect(divByZero, Constant::get(0), p); |
|||
stack.push(p); |
|||
break; |
|||
} |
|||
|
|||
case Instruction::EXP: |
|||
{ |
|||
auto base = stack.pop(); |
|||
auto exponent = stack.pop(); |
|||
_gasMeter.countExp(exponent); |
|||
auto ret = _arith.exp(base, exponent); |
|||
stack.push(ret); |
|||
break; |
|||
} |
|||
|
|||
case Instruction::NOT: |
|||
{ |
|||
auto value = stack.pop(); |
|||
auto ret = m_builder.CreateXor(value, Constant::get(-1), "bnot"); |
|||
stack.push(ret); |
|||
break; |
|||
} |
|||
|
|||
case Instruction::LT: |
|||
{ |
|||
auto lhs = stack.pop(); |
|||
auto rhs = stack.pop(); |
|||
auto res1 = m_builder.CreateICmpULT(lhs, rhs); |
|||
auto res256 = m_builder.CreateZExt(res1, Type::Word); |
|||
stack.push(res256); |
|||
break; |
|||
} |
|||
|
|||
case Instruction::GT: |
|||
{ |
|||
auto lhs = stack.pop(); |
|||
auto rhs = stack.pop(); |
|||
auto res1 = m_builder.CreateICmpUGT(lhs, rhs); |
|||
auto res256 = m_builder.CreateZExt(res1, Type::Word); |
|||
stack.push(res256); |
|||
break; |
|||
} |
|||
|
|||
case Instruction::SLT: |
|||
{ |
|||
auto lhs = stack.pop(); |
|||
auto rhs = stack.pop(); |
|||
auto res1 = m_builder.CreateICmpSLT(lhs, rhs); |
|||
auto res256 = m_builder.CreateZExt(res1, Type::Word); |
|||
stack.push(res256); |
|||
break; |
|||
} |
|||
|
|||
case Instruction::SGT: |
|||
{ |
|||
auto lhs = stack.pop(); |
|||
auto rhs = stack.pop(); |
|||
auto res1 = m_builder.CreateICmpSGT(lhs, rhs); |
|||
auto res256 = m_builder.CreateZExt(res1, Type::Word); |
|||
stack.push(res256); |
|||
break; |
|||
} |
|||
|
|||
case Instruction::EQ: |
|||
{ |
|||
auto lhs = stack.pop(); |
|||
auto rhs = stack.pop(); |
|||
auto res1 = m_builder.CreateICmpEQ(lhs, rhs); |
|||
auto res256 = m_builder.CreateZExt(res1, Type::Word); |
|||
stack.push(res256); |
|||
break; |
|||
} |
|||
|
|||
case Instruction::ISZERO: |
|||
{ |
|||
auto top = stack.pop(); |
|||
auto iszero = m_builder.CreateICmpEQ(top, Constant::get(0), "iszero"); |
|||
auto result = m_builder.CreateZExt(iszero, Type::Word); |
|||
stack.push(result); |
|||
break; |
|||
} |
|||
|
|||
case Instruction::AND: |
|||
{ |
|||
auto lhs = stack.pop(); |
|||
auto rhs = stack.pop(); |
|||
auto res = m_builder.CreateAnd(lhs, rhs); |
|||
stack.push(res); |
|||
break; |
|||
} |
|||
|
|||
case Instruction::OR: |
|||
{ |
|||
auto lhs = stack.pop(); |
|||
auto rhs = stack.pop(); |
|||
auto res = m_builder.CreateOr(lhs, rhs); |
|||
stack.push(res); |
|||
break; |
|||
} |
|||
|
|||
case Instruction::XOR: |
|||
{ |
|||
auto lhs = stack.pop(); |
|||
auto rhs = stack.pop(); |
|||
auto res = m_builder.CreateXor(lhs, rhs); |
|||
stack.push(res); |
|||
break; |
|||
} |
|||
|
|||
case Instruction::BYTE: |
|||
{ |
|||
const auto idx = stack.pop(); |
|||
auto value = Endianness::toBE(m_builder, stack.pop()); |
|||
|
|||
auto idxValid = m_builder.CreateICmpULT(idx, Constant::get(32), "idxValid"); |
|||
auto bytes = m_builder.CreateBitCast(value, llvm::VectorType::get(Type::Byte, 32), "bytes"); |
|||
// TODO: Workaround for LLVM bug. Using big value of index causes invalid memory access.
|
|||
auto safeIdx = m_builder.CreateTrunc(idx, m_builder.getIntNTy(5)); |
|||
// TODO: Workaround for LLVM bug. DAG Builder used sext on index instead of zext
|
|||
safeIdx = m_builder.CreateZExt(safeIdx, Type::Size); |
|||
auto byte = m_builder.CreateExtractElement(bytes, safeIdx, "byte"); |
|||
value = m_builder.CreateZExt(byte, Type::Word); |
|||
value = m_builder.CreateSelect(idxValid, value, Constant::get(0)); |
|||
stack.push(value); |
|||
break; |
|||
} |
|||
|
|||
case Instruction::SIGNEXTEND: |
|||
{ |
|||
auto idx = stack.pop(); |
|||
auto word = stack.pop(); |
|||
|
|||
auto k32_ = m_builder.CreateTrunc(idx, m_builder.getIntNTy(5), "k_32"); |
|||
auto k32 = m_builder.CreateZExt(k32_, Type::Size); |
|||
auto k32x8 = m_builder.CreateMul(k32, m_builder.getInt64(8), "kx8"); |
|||
|
|||
// test for word >> (k * 8 + 7)
|
|||
auto bitpos = m_builder.CreateAdd(k32x8, m_builder.getInt64(7), "bitpos"); |
|||
auto bitposEx = m_builder.CreateZExt(bitpos, Type::Word); |
|||
auto bitval = m_builder.CreateLShr(word, bitposEx, "bitval"); |
|||
auto bittest = m_builder.CreateTrunc(bitval, Type::Bool, "bittest"); |
|||
|
|||
auto mask_ = m_builder.CreateShl(Constant::get(1), bitposEx); |
|||
auto mask = m_builder.CreateSub(mask_, Constant::get(1), "mask"); |
|||
|
|||
auto negmask = m_builder.CreateXor(mask, llvm::ConstantInt::getAllOnesValue(Type::Word), "negmask"); |
|||
auto val1 = m_builder.CreateOr(word, negmask); |
|||
auto val0 = m_builder.CreateAnd(word, mask); |
|||
|
|||
auto kInRange = m_builder.CreateICmpULE(idx, llvm::ConstantInt::get(Type::Word, 30)); |
|||
auto result = m_builder.CreateSelect(kInRange, |
|||
m_builder.CreateSelect(bittest, val1, val0), |
|||
word); |
|||
stack.push(result); |
|||
break; |
|||
} |
|||
|
|||
case Instruction::SHA3: |
|||
{ |
|||
auto inOff = stack.pop(); |
|||
auto inSize = stack.pop(); |
|||
_memory.require(inOff, inSize); |
|||
_gasMeter.countSha3Data(inSize); |
|||
auto hash = _ext.sha3(inOff, inSize); |
|||
stack.push(hash); |
|||
break; |
|||
} |
|||
|
|||
case Instruction::POP: |
|||
{ |
|||
stack.pop(); |
|||
break; |
|||
} |
|||
|
|||
case Instruction::ANY_PUSH: |
|||
{ |
|||
auto value = readPushData(it, _basicBlock.end()); |
|||
stack.push(Constant::get(value)); |
|||
break; |
|||
} |
|||
|
|||
case Instruction::ANY_DUP: |
|||
{ |
|||
auto index = static_cast<size_t>(inst) - static_cast<size_t>(Instruction::DUP1); |
|||
stack.dup(index); |
|||
break; |
|||
} |
|||
|
|||
case Instruction::ANY_SWAP: |
|||
{ |
|||
auto index = static_cast<size_t>(inst) - static_cast<size_t>(Instruction::SWAP1) + 1; |
|||
stack.swap(index); |
|||
break; |
|||
} |
|||
|
|||
case Instruction::MLOAD: |
|||
{ |
|||
auto addr = stack.pop(); |
|||
auto word = _memory.loadWord(addr); |
|||
stack.push(word); |
|||
break; |
|||
} |
|||
|
|||
case Instruction::MSTORE: |
|||
{ |
|||
auto addr = stack.pop(); |
|||
auto word = stack.pop(); |
|||
_memory.storeWord(addr, word); |
|||
break; |
|||
} |
|||
|
|||
case Instruction::MSTORE8: |
|||
{ |
|||
auto addr = stack.pop(); |
|||
auto word = stack.pop(); |
|||
_memory.storeByte(addr, word); |
|||
break; |
|||
} |
|||
|
|||
case Instruction::MSIZE: |
|||
{ |
|||
auto word = _memory.getSize(); |
|||
stack.push(word); |
|||
break; |
|||
} |
|||
|
|||
case Instruction::SLOAD: |
|||
{ |
|||
auto index = stack.pop(); |
|||
auto value = _ext.sload(index); |
|||
stack.push(value); |
|||
break; |
|||
} |
|||
|
|||
case Instruction::SSTORE: |
|||
{ |
|||
auto index = stack.pop(); |
|||
auto value = stack.pop(); |
|||
_gasMeter.countSStore(_ext, index, value); |
|||
_ext.sstore(index, value); |
|||
break; |
|||
} |
|||
|
|||
case Instruction::JUMP: |
|||
case Instruction::JUMPI: |
|||
{ |
|||
auto destIdx = llvm::MDNode::get(m_builder.getContext(), llvm::ValueAsMetadata::get(stack.pop())); |
|||
|
|||
// Create branch instruction, initially to jump table.
|
|||
// Destination will be optimized with direct jump during jump resolving if destination index is a constant.
|
|||
auto jumpInst = (inst == Instruction::JUMP) ? |
|||
m_builder.CreateBr(m_jumpTableBB) : |
|||
m_builder.CreateCondBr(m_builder.CreateICmpNE(stack.pop(), Constant::get(0), "jump.check"), m_jumpTableBB, nullptr); |
|||
|
|||
// Attach medatada to branch instruction with information about destination index.
|
|||
jumpInst->setMetadata(c_destIdxLabel, destIdx); |
|||
break; |
|||
} |
|||
|
|||
case Instruction::JUMPDEST: |
|||
{ |
|||
// Nothing to do
|
|||
break; |
|||
} |
|||
|
|||
case Instruction::PC: |
|||
{ |
|||
auto value = Constant::get(it - _basicBlock.begin() + _basicBlock.firstInstrIdx()); |
|||
stack.push(value); |
|||
break; |
|||
} |
|||
|
|||
case Instruction::GAS: |
|||
{ |
|||
_gasMeter.commitCostBlock(); |
|||
stack.push(m_builder.CreateZExt(_runtimeManager.getGas(), Type::Word)); |
|||
break; |
|||
} |
|||
|
|||
case Instruction::ADDRESS: |
|||
case Instruction::CALLER: |
|||
case Instruction::ORIGIN: |
|||
case Instruction::CALLVALUE: |
|||
case Instruction::GASPRICE: |
|||
case Instruction::COINBASE: |
|||
case Instruction::DIFFICULTY: |
|||
case Instruction::GASLIMIT: |
|||
case Instruction::NUMBER: |
|||
case Instruction::TIMESTAMP: |
|||
{ |
|||
// Pushes an element of runtime data on stack
|
|||
auto value = _runtimeManager.get(inst); |
|||
value = m_builder.CreateZExt(value, Type::Word); |
|||
stack.push(value); |
|||
break; |
|||
} |
|||
|
|||
case Instruction::CODESIZE: |
|||
stack.push(_runtimeManager.getCodeSize()); |
|||
break; |
|||
|
|||
case Instruction::CALLDATASIZE: |
|||
stack.push(_runtimeManager.getCallDataSize()); |
|||
break; |
|||
|
|||
case Instruction::BLOCKHASH: |
|||
{ |
|||
auto number = stack.pop(); |
|||
auto hash = _ext.blockHash(number); |
|||
stack.push(hash); |
|||
break; |
|||
} |
|||
|
|||
case Instruction::BALANCE: |
|||
{ |
|||
auto address = stack.pop(); |
|||
auto value = _ext.balance(address); |
|||
stack.push(value); |
|||
break; |
|||
} |
|||
|
|||
case Instruction::EXTCODESIZE: |
|||
{ |
|||
auto addr = stack.pop(); |
|||
auto codeRef = _ext.extcode(addr); |
|||
stack.push(codeRef.size); |
|||
break; |
|||
} |
|||
|
|||
case Instruction::CALLDATACOPY: |
|||
{ |
|||
auto destMemIdx = stack.pop(); |
|||
auto srcIdx = stack.pop(); |
|||
auto reqBytes = stack.pop(); |
|||
|
|||
auto srcPtr = _runtimeManager.getCallData(); |
|||
auto srcSize = _runtimeManager.getCallDataSize(); |
|||
|
|||
_memory.copyBytes(srcPtr, srcSize, srcIdx, destMemIdx, reqBytes); |
|||
break; |
|||
} |
|||
|
|||
case Instruction::CODECOPY: |
|||
{ |
|||
auto destMemIdx = stack.pop(); |
|||
auto srcIdx = stack.pop(); |
|||
auto reqBytes = stack.pop(); |
|||
|
|||
auto srcPtr = _runtimeManager.getCode(); // TODO: Code & its size are constants, feature #80814234
|
|||
auto srcSize = _runtimeManager.getCodeSize(); |
|||
|
|||
_memory.copyBytes(srcPtr, srcSize, srcIdx, destMemIdx, reqBytes); |
|||
break; |
|||
} |
|||
|
|||
case Instruction::EXTCODECOPY: |
|||
{ |
|||
auto addr = stack.pop(); |
|||
auto destMemIdx = stack.pop(); |
|||
auto srcIdx = stack.pop(); |
|||
auto reqBytes = stack.pop(); |
|||
|
|||
auto codeRef = _ext.extcode(addr); |
|||
|
|||
_memory.copyBytes(codeRef.ptr, codeRef.size, srcIdx, destMemIdx, reqBytes); |
|||
break; |
|||
} |
|||
|
|||
case Instruction::CALLDATALOAD: |
|||
{ |
|||
auto idx = stack.pop(); |
|||
auto value = _ext.calldataload(idx); |
|||
stack.push(value); |
|||
break; |
|||
} |
|||
|
|||
case Instruction::CREATE: |
|||
{ |
|||
auto endowment = stack.pop(); |
|||
auto initOff = stack.pop(); |
|||
auto initSize = stack.pop(); |
|||
_memory.require(initOff, initSize); |
|||
|
|||
_gasMeter.commitCostBlock(); |
|||
auto address = _ext.create(endowment, initOff, initSize); |
|||
stack.push(address); |
|||
break; |
|||
} |
|||
|
|||
case Instruction::CALL: |
|||
case Instruction::CALLCODE: |
|||
{ |
|||
auto callGas = stack.pop(); |
|||
auto codeAddress = stack.pop(); |
|||
auto value = stack.pop(); |
|||
auto inOff = stack.pop(); |
|||
auto inSize = stack.pop(); |
|||
auto outOff = stack.pop(); |
|||
auto outSize = stack.pop(); |
|||
|
|||
_gasMeter.commitCostBlock(); |
|||
|
|||
// Require memory for in and out buffers
|
|||
_memory.require(outOff, outSize); // Out buffer first as we guess it will be after the in one
|
|||
_memory.require(inOff, inSize); |
|||
|
|||
auto receiveAddress = codeAddress; |
|||
if (inst == Instruction::CALLCODE) |
|||
receiveAddress = _runtimeManager.get(RuntimeData::Address); |
|||
|
|||
auto ret = _ext.call(callGas, receiveAddress, value, inOff, inSize, outOff, outSize, codeAddress); |
|||
_gasMeter.count(m_builder.getInt64(0), _runtimeManager.getJmpBuf(), _runtimeManager.getGasPtr()); |
|||
stack.push(ret); |
|||
break; |
|||
} |
|||
|
|||
case Instruction::RETURN: |
|||
{ |
|||
auto index = stack.pop(); |
|||
auto size = stack.pop(); |
|||
|
|||
_memory.require(index, size); |
|||
_runtimeManager.registerReturnData(index, size); |
|||
|
|||
_runtimeManager.exit(ReturnCode::Return); |
|||
break; |
|||
} |
|||
|
|||
case Instruction::SUICIDE: |
|||
{ |
|||
_runtimeManager.registerSuicide(stack.pop()); |
|||
_runtimeManager.exit(ReturnCode::Suicide); |
|||
break; |
|||
} |
|||
|
|||
|
|||
case Instruction::STOP: |
|||
{ |
|||
m_builder.CreateBr(m_stopBB); |
|||
break; |
|||
} |
|||
|
|||
case Instruction::LOG0: |
|||
case Instruction::LOG1: |
|||
case Instruction::LOG2: |
|||
case Instruction::LOG3: |
|||
case Instruction::LOG4: |
|||
{ |
|||
auto beginIdx = stack.pop(); |
|||
auto numBytes = stack.pop(); |
|||
_memory.require(beginIdx, numBytes); |
|||
|
|||
// This will commit the current cost block
|
|||
_gasMeter.countLogData(numBytes); |
|||
|
|||
std::array<llvm::Value*, 4> topics{{}}; |
|||
auto numTopics = static_cast<size_t>(inst) - static_cast<size_t>(Instruction::LOG0); |
|||
for (size_t i = 0; i < numTopics; ++i) |
|||
topics[i] = stack.pop(); |
|||
|
|||
_ext.log(beginIdx, numBytes, topics); |
|||
break; |
|||
} |
|||
|
|||
default: // Invalid instruction - abort
|
|||
m_builder.CreateBr(m_abortBB); |
|||
it = _basicBlock.end() - 1; // finish block compilation
|
|||
} |
|||
} |
|||
|
|||
_gasMeter.commitCostBlock(); |
|||
|
|||
stack.finalize(m_builder, *_basicBlock.llvm()); // TODO: Use references
|
|||
|
|||
m_builder.SetInsertPoint(_basicBlock.llvm()->getFirstNonPHI()); // TODO: Move to LocalStack::finalize
|
|||
_runtimeManager.checkStackLimit(stack.minSize(), stack.maxSize(), stack.size()); |
|||
} |
|||
|
|||
|
|||
} |
|||
} |
|||
} |
@ -1,59 +0,0 @@ |
|||
#pragma once |
|||
|
|||
#include "BasicBlock.h" |
|||
|
|||
namespace dev |
|||
{ |
|||
namespace eth |
|||
{ |
|||
namespace jit |
|||
{ |
|||
|
|||
class Compiler |
|||
{ |
|||
public: |
|||
|
|||
struct Options |
|||
{ |
|||
/// Rewrite switch instructions to sequences of branches
|
|||
bool rewriteSwitchToBranches = true; |
|||
|
|||
/// Dump CFG as a .dot file for graphviz
|
|||
bool dumpCFG = false; |
|||
}; |
|||
|
|||
Compiler(Options const& _options); |
|||
|
|||
std::unique_ptr<llvm::Module> compile(code_iterator _begin, code_iterator _end, std::string const& _id); |
|||
|
|||
private: |
|||
|
|||
std::vector<BasicBlock> createBasicBlocks(code_iterator _begin, code_iterator _end, llvm::SwitchInst& _switchInst); |
|||
|
|||
void compileBasicBlock(BasicBlock& _basicBlock, class RuntimeManager& _runtimeManager, class Arith256& _arith, class Memory& _memory, class Ext& _ext, class GasMeter& _gasMeter, |
|||
class Stack& _globalStack); |
|||
|
|||
void resolveJumps(); |
|||
|
|||
/// Compiler options
|
|||
Options const& m_options; |
|||
|
|||
/// Helper class for generating IR
|
|||
llvm::IRBuilder<> m_builder; |
|||
|
|||
/// Stop basic block - terminates execution with STOP code (0)
|
|||
llvm::BasicBlock* m_stopBB = nullptr; |
|||
|
|||
/// Abort basic block - terminates execution with OOG-like state
|
|||
llvm::BasicBlock* m_abortBB = nullptr; |
|||
|
|||
/// Block with a jump table.
|
|||
llvm::BasicBlock* m_jumpTableBB = nullptr; |
|||
|
|||
/// Main program function
|
|||
llvm::Function* m_mainFunc = nullptr; // TODO: Remove
|
|||
}; |
|||
|
|||
} |
|||
} |
|||
} |
@ -1,51 +0,0 @@ |
|||
#include "CompilerHelper.h" |
|||
|
|||
#include "preprocessor/llvm_includes_start.h" |
|||
#include <llvm/IR/Module.h> |
|||
#include "preprocessor/llvm_includes_end.h" |
|||
|
|||
#include "RuntimeManager.h" |
|||
|
|||
namespace dev |
|||
{ |
|||
namespace eth |
|||
{ |
|||
namespace jit |
|||
{ |
|||
|
|||
CompilerHelper::CompilerHelper(llvm::IRBuilder<>& _builder) : |
|||
m_builder(_builder) |
|||
{} |
|||
|
|||
llvm::Module* CompilerHelper::getModule() |
|||
{ |
|||
assert(m_builder.GetInsertBlock()); |
|||
assert(m_builder.GetInsertBlock()->getParent()); // BB must be in a function
|
|||
return m_builder.GetInsertBlock()->getParent()->getParent(); |
|||
} |
|||
|
|||
llvm::Function* CompilerHelper::getMainFunction() |
|||
{ |
|||
// TODO: Rename or change semantics of getMainFunction() function
|
|||
assert(m_builder.GetInsertBlock()); |
|||
auto mainFunc = m_builder.GetInsertBlock()->getParent(); |
|||
assert(mainFunc); |
|||
if (mainFunc == &mainFunc->getParent()->getFunctionList().front()) // Main function is the first one in module
|
|||
return mainFunc; |
|||
return nullptr; |
|||
} |
|||
|
|||
llvm::CallInst* CompilerHelper::createCall(llvm::Function* _func, std::initializer_list<llvm::Value*> const& _args) |
|||
{ |
|||
return getBuilder().CreateCall(_func, {_args.begin(), _args.size()}); |
|||
} |
|||
|
|||
|
|||
RuntimeHelper::RuntimeHelper(RuntimeManager& _runtimeManager): |
|||
CompilerHelper(_runtimeManager.getBuilder()), |
|||
m_runtimeManager(_runtimeManager) |
|||
{} |
|||
|
|||
} |
|||
} |
|||
} |
@ -1,64 +0,0 @@ |
|||
#pragma once |
|||
|
|||
#include "preprocessor/llvm_includes_start.h" |
|||
#include <llvm/IR/IRBuilder.h> |
|||
#include "preprocessor/llvm_includes_end.h" |
|||
|
|||
|
|||
namespace dev |
|||
{ |
|||
namespace eth |
|||
{ |
|||
namespace jit |
|||
{ |
|||
class RuntimeManager; |
|||
|
|||
/// Base class for compiler helpers like Memory, GasMeter, etc.
|
|||
class CompilerHelper |
|||
{ |
|||
protected: |
|||
CompilerHelper(llvm::IRBuilder<>& _builder); |
|||
|
|||
CompilerHelper(const CompilerHelper&) = delete; |
|||
CompilerHelper& operator=(CompilerHelper) = delete; |
|||
|
|||
/// Reference to the IR module being compiled
|
|||
llvm::Module* getModule(); |
|||
|
|||
/// Reference to the main module function
|
|||
llvm::Function* getMainFunction(); |
|||
|
|||
/// Reference to parent compiler IR builder
|
|||
llvm::IRBuilder<>& m_builder; |
|||
llvm::IRBuilder<>& getBuilder() { return m_builder; } |
|||
|
|||
llvm::CallInst* createCall(llvm::Function* _func, std::initializer_list<llvm::Value*> const& _args); |
|||
|
|||
friend class RuntimeHelper; |
|||
}; |
|||
|
|||
/// Compiler helper that depends on runtime data
|
|||
class RuntimeHelper : public CompilerHelper |
|||
{ |
|||
protected: |
|||
RuntimeHelper(RuntimeManager& _runtimeManager); |
|||
|
|||
RuntimeManager& getRuntimeManager() { return m_runtimeManager; } |
|||
|
|||
private: |
|||
RuntimeManager& m_runtimeManager; |
|||
}; |
|||
|
|||
struct InsertPointGuard |
|||
{ |
|||
explicit InsertPointGuard(llvm::IRBuilderBase& _builder): m_builder(_builder), m_insertPoint(_builder.saveIP()) {} |
|||
~InsertPointGuard() { m_builder.restoreIP(m_insertPoint); } |
|||
|
|||
private: |
|||
llvm::IRBuilderBase& m_builder; |
|||
llvm::IRBuilderBase::InsertPoint m_insertPoint; |
|||
}; |
|||
|
|||
} |
|||
} |
|||
} |
@ -1,33 +0,0 @@ |
|||
#include "Endianness.h" |
|||
|
|||
#include "preprocessor/llvm_includes_start.h" |
|||
#include <llvm/IR/IntrinsicInst.h> |
|||
#include <llvm/Support/Host.h> |
|||
#include "preprocessor/llvm_includes_end.h" |
|||
|
|||
#include "Type.h" |
|||
|
|||
namespace dev |
|||
{ |
|||
namespace eth |
|||
{ |
|||
namespace jit |
|||
{ |
|||
|
|||
llvm::Value* Endianness::bswapIfLE(llvm::IRBuilder<>& _builder, llvm::Value* _word) |
|||
{ |
|||
if (llvm::sys::IsLittleEndianHost) |
|||
{ |
|||
if (auto constant = llvm::dyn_cast<llvm::ConstantInt>(_word)) |
|||
return _builder.getInt(constant->getValue().byteSwap()); |
|||
|
|||
// OPT: Cache func declaration?
|
|||
auto bswapFunc = llvm::Intrinsic::getDeclaration(_builder.GetInsertBlock()->getParent()->getParent(), llvm::Intrinsic::bswap, Type::Word); |
|||
return _builder.CreateCall(bswapFunc, _word); |
|||
} |
|||
return _word; |
|||
} |
|||
|
|||
} |
|||
} |
|||
} |
@ -1,25 +0,0 @@ |
|||
#pragma once |
|||
|
|||
#include "preprocessor/llvm_includes_start.h" |
|||
#include <llvm/IR/IRBuilder.h> |
|||
#include "preprocessor/llvm_includes_end.h" |
|||
|
|||
namespace dev |
|||
{ |
|||
namespace eth |
|||
{ |
|||
namespace jit |
|||
{ |
|||
|
|||
struct Endianness |
|||
{ |
|||
static llvm::Value* toBE(llvm::IRBuilder<>& _builder, llvm::Value* _word) { return bswapIfLE(_builder, _word); } |
|||
static llvm::Value* toNative(llvm::IRBuilder<>& _builder, llvm::Value* _word) { return bswapIfLE(_builder, _word); } |
|||
|
|||
private: |
|||
static llvm::Value* bswapIfLE(llvm::IRBuilder<>& _builder, llvm::Value* _word); |
|||
}; |
|||
|
|||
} |
|||
} |
|||
} |
@ -1,95 +0,0 @@ |
|||
#include "ExecStats.h" |
|||
|
|||
#include <iostream> |
|||
#include <iomanip> |
|||
#include <cassert> |
|||
|
|||
#include "Utils.h" |
|||
|
|||
namespace dev |
|||
{ |
|||
namespace evmjit |
|||
{ |
|||
|
|||
void ExecStats::stateChanged(ExecState _state) |
|||
{ |
|||
if (!CHECK(m_state != ExecState::Finished)) |
|||
return; |
|||
|
|||
auto now = clock::now(); |
|||
if (_state != ExecState::Started) |
|||
{ |
|||
assert(time[(int)m_state] == ExecStats::duration::zero()); |
|||
time[(int)m_state] = now - m_tp; |
|||
} |
|||
m_state = _state; |
|||
m_tp = now; |
|||
} |
|||
|
|||
namespace |
|||
{ |
|||
struct StatsAgg |
|||
{ |
|||
using unit = std::chrono::microseconds; |
|||
ExecStats::duration tot = ExecStats::duration::zero(); |
|||
ExecStats::duration min = ExecStats::duration::max(); |
|||
ExecStats::duration max = ExecStats::duration::zero(); |
|||
size_t count = 0; |
|||
|
|||
void update(ExecStats::duration _d) |
|||
{ |
|||
++count; |
|||
tot += _d; |
|||
min = _d < min ? _d : min; |
|||
max = _d > max ? _d : max; |
|||
} |
|||
|
|||
void output(char const* _name, std::ostream& _os) |
|||
{ |
|||
auto avg = tot / count; |
|||
_os << std::setfill(' ') |
|||
<< std::setw(12) << std::left << _name |
|||
<< std::setw(10) << std::right << std::chrono::duration_cast<unit>(tot).count() |
|||
<< std::setw(10) << std::right << std::chrono::duration_cast<unit>(avg).count() |
|||
<< std::setw(10) << std::right << std::chrono::duration_cast<unit>(min).count() |
|||
<< std::setw(10) << std::right << std::chrono::duration_cast<unit>(max).count() |
|||
<< std::endl; |
|||
} |
|||
}; |
|||
|
|||
char const* getExecStateName(ExecState _state) |
|||
{ |
|||
switch (_state) |
|||
{ |
|||
case ExecState::Started: return "Start"; |
|||
case ExecState::CacheLoad: return "CacheLoad"; |
|||
case ExecState::CacheWrite: return "CacheWrite"; |
|||
case ExecState::Compilation: return "Compilation"; |
|||
case ExecState::Optimization: return "Optimization"; |
|||
case ExecState::CodeGen: return "CodeGen"; |
|||
case ExecState::Execution: return "Execution"; |
|||
case ExecState::Return: return "Return"; |
|||
case ExecState::Finished: return "Finish"; |
|||
} |
|||
return nullptr; |
|||
} |
|||
} |
|||
|
|||
StatsCollector::~StatsCollector() |
|||
{ |
|||
if (stats.empty()) |
|||
return; |
|||
|
|||
std::cout << " [us] total avg min max\n"; |
|||
for (int i = 0; i < (int)ExecState::Finished; ++i) |
|||
{ |
|||
StatsAgg agg; |
|||
for (auto&& s : stats) |
|||
agg.update(s->time[i]); |
|||
|
|||
agg.output(getExecStateName(ExecState(i)), std::cout); |
|||
} |
|||
} |
|||
|
|||
} |
|||
} |
@ -1,68 +0,0 @@ |
|||
#pragma once |
|||
|
|||
#include <memory> |
|||
#include <vector> |
|||
#include <string> |
|||
#include <chrono> |
|||
|
|||
namespace dev |
|||
{ |
|||
namespace evmjit |
|||
{ |
|||
|
|||
enum class ExecState |
|||
{ |
|||
Started, |
|||
CacheLoad, |
|||
CacheWrite, |
|||
Compilation, |
|||
Optimization, |
|||
CodeGen, |
|||
Execution, |
|||
Return, |
|||
Finished |
|||
}; |
|||
|
|||
class JITListener |
|||
{ |
|||
public: |
|||
JITListener() = default; |
|||
JITListener(JITListener const&) = delete; |
|||
JITListener& operator=(JITListener) = delete; |
|||
virtual ~JITListener() {} |
|||
|
|||
virtual void executionStarted() {} |
|||
virtual void executionEnded() {} |
|||
|
|||
virtual void stateChanged(ExecState) {} |
|||
}; |
|||
|
|||
class ExecStats : public JITListener |
|||
{ |
|||
public: |
|||
using clock = std::chrono::high_resolution_clock; |
|||
using duration = clock::duration; |
|||
using time_point = clock::time_point; |
|||
|
|||
std::string id; |
|||
duration time[(int)ExecState::Finished] = {}; |
|||
|
|||
void stateChanged(ExecState _state) override; |
|||
|
|||
private: |
|||
ExecState m_state = {}; |
|||
time_point m_tp = {}; |
|||
|
|||
}; |
|||
|
|||
|
|||
class StatsCollector |
|||
{ |
|||
public: |
|||
std::vector<std::unique_ptr<ExecStats>> stats; |
|||
|
|||
~StatsCollector(); |
|||
}; |
|||
|
|||
} |
|||
} |
@ -1,210 +0,0 @@ |
|||
#include "Ext.h" |
|||
|
|||
#include "preprocessor/llvm_includes_start.h" |
|||
#include <llvm/IR/IntrinsicInst.h> |
|||
#include "preprocessor/llvm_includes_end.h" |
|||
|
|||
#include "RuntimeManager.h" |
|||
#include "Memory.h" |
|||
#include "Type.h" |
|||
#include "Endianness.h" |
|||
|
|||
namespace dev |
|||
{ |
|||
namespace eth |
|||
{ |
|||
namespace jit |
|||
{ |
|||
|
|||
Ext::Ext(RuntimeManager& _runtimeManager, Memory& _memoryMan): |
|||
RuntimeHelper(_runtimeManager), |
|||
m_memoryMan(_memoryMan) |
|||
{ |
|||
m_funcs = decltype(m_funcs)(); |
|||
m_argAllocas = decltype(m_argAllocas)(); |
|||
m_size = m_builder.CreateAlloca(Type::Size, nullptr, "env.size"); |
|||
} |
|||
|
|||
|
|||
using FuncDesc = std::tuple<char const*, llvm::FunctionType*>; |
|||
|
|||
llvm::FunctionType* getFunctionType(llvm::Type* _returnType, std::initializer_list<llvm::Type*> const& _argsTypes) |
|||
{ |
|||
return llvm::FunctionType::get(_returnType, llvm::ArrayRef<llvm::Type*>{_argsTypes.begin(), _argsTypes.size()}, false); |
|||
} |
|||
|
|||
std::array<FuncDesc, sizeOf<EnvFunc>::value> const& getEnvFuncDescs() |
|||
{ |
|||
static std::array<FuncDesc, sizeOf<EnvFunc>::value> descs{{ |
|||
FuncDesc{"env_sload", getFunctionType(Type::Void, {Type::EnvPtr, Type::WordPtr, Type::WordPtr})}, |
|||
FuncDesc{"env_sstore", getFunctionType(Type::Void, {Type::EnvPtr, Type::WordPtr, Type::WordPtr})}, |
|||
FuncDesc{"env_sha3", getFunctionType(Type::Void, {Type::BytePtr, Type::Size, Type::WordPtr})}, |
|||
FuncDesc{"env_balance", getFunctionType(Type::Void, {Type::EnvPtr, Type::WordPtr, Type::WordPtr})}, |
|||
FuncDesc{"env_create", getFunctionType(Type::Void, {Type::EnvPtr, Type::GasPtr, Type::WordPtr, Type::BytePtr, Type::Size, Type::WordPtr})}, |
|||
FuncDesc{"env_call", getFunctionType(Type::Bool, {Type::EnvPtr, Type::GasPtr, Type::Gas, 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_extcode", getFunctionType(Type::BytePtr, {Type::EnvPtr, Type::WordPtr, Type::Size->getPointerTo()})}, |
|||
}}; |
|||
|
|||
return descs; |
|||
} |
|||
|
|||
llvm::Function* createFunc(EnvFunc _id, llvm::Module* _module) |
|||
{ |
|||
auto&& desc = getEnvFuncDescs()[static_cast<size_t>(_id)]; |
|||
return llvm::Function::Create(std::get<1>(desc), llvm::Function::ExternalLinkage, std::get<0>(desc), _module); |
|||
} |
|||
|
|||
llvm::Value* Ext::getArgAlloca() |
|||
{ |
|||
auto& a = m_argAllocas[m_argCounter]; |
|||
if (!a) |
|||
{ |
|||
InsertPointGuard g{getBuilder()}; |
|||
auto allocaIt = getMainFunction()->front().begin(); |
|||
std::advance(allocaIt, m_argCounter); // Skip already created allocas
|
|||
getBuilder().SetInsertPoint(allocaIt); |
|||
a = getBuilder().CreateAlloca(Type::Word, nullptr, {"a.", std::to_string(m_argCounter)}); |
|||
} |
|||
++m_argCounter; |
|||
return a; |
|||
} |
|||
|
|||
llvm::Value* Ext::byPtr(llvm::Value* _value) |
|||
{ |
|||
auto a = getArgAlloca(); |
|||
getBuilder().CreateStore(_value, a); |
|||
return a; |
|||
} |
|||
|
|||
llvm::CallInst* Ext::createCall(EnvFunc _funcId, std::initializer_list<llvm::Value*> const& _args) |
|||
{ |
|||
auto& func = m_funcs[static_cast<size_t>(_funcId)]; |
|||
if (!func) |
|||
func = createFunc(_funcId, getModule()); |
|||
|
|||
m_argCounter = 0; |
|||
return getBuilder().CreateCall(func, {_args.begin(), _args.size()}); |
|||
} |
|||
|
|||
llvm::Value* Ext::sload(llvm::Value* _index) |
|||
{ |
|||
auto ret = getArgAlloca(); |
|||
createCall(EnvFunc::sload, {getRuntimeManager().getEnvPtr(), byPtr(_index), ret}); // Uses native endianness
|
|||
return m_builder.CreateLoad(ret); |
|||
} |
|||
|
|||
void Ext::sstore(llvm::Value* _index, llvm::Value* _value) |
|||
{ |
|||
createCall(EnvFunc::sstore, {getRuntimeManager().getEnvPtr(), byPtr(_index), byPtr(_value)}); // Uses native endianness
|
|||
} |
|||
|
|||
llvm::Value* Ext::calldataload(llvm::Value* _idx) |
|||
{ |
|||
auto ret = getArgAlloca(); |
|||
auto result = m_builder.CreateBitCast(ret, Type::BytePtr); |
|||
|
|||
auto callDataSize = getRuntimeManager().getCallDataSize(); |
|||
auto callDataSize64 = m_builder.CreateTrunc(callDataSize, Type::Size); |
|||
auto idxValid = m_builder.CreateICmpULT(_idx, callDataSize); |
|||
auto idx = m_builder.CreateTrunc(m_builder.CreateSelect(idxValid, _idx, callDataSize), Type::Size, "idx"); |
|||
|
|||
auto end = m_builder.CreateNUWAdd(idx, m_builder.getInt64(32)); |
|||
end = m_builder.CreateSelect(m_builder.CreateICmpULE(end, callDataSize64), end, callDataSize64); |
|||
auto copySize = m_builder.CreateNUWSub(end, idx); |
|||
auto padSize = m_builder.CreateNUWSub(m_builder.getInt64(32), copySize); |
|||
auto dataBegin = m_builder.CreateGEP(Type::Byte, getRuntimeManager().getCallData(), idx); |
|||
m_builder.CreateMemCpy(result, dataBegin, copySize, 1); |
|||
auto pad = m_builder.CreateGEP(Type::Byte, result, copySize); |
|||
m_builder.CreateMemSet(pad, m_builder.getInt8(0), padSize, 1); |
|||
|
|||
m_argCounter = 0; // Release args allocas. TODO: This is a bad design
|
|||
return Endianness::toNative(m_builder, m_builder.CreateLoad(ret)); |
|||
} |
|||
|
|||
llvm::Value* Ext::balance(llvm::Value* _address) |
|||
{ |
|||
auto address = Endianness::toBE(m_builder, _address); |
|||
auto ret = getArgAlloca(); |
|||
createCall(EnvFunc::balance, {getRuntimeManager().getEnvPtr(), byPtr(address), ret}); |
|||
return m_builder.CreateLoad(ret); |
|||
} |
|||
|
|||
llvm::Value* Ext::blockHash(llvm::Value* _number) |
|||
{ |
|||
auto hash = getArgAlloca(); |
|||
createCall(EnvFunc::blockhash, {getRuntimeManager().getEnvPtr(), byPtr(_number), hash}); |
|||
hash = m_builder.CreateLoad(hash); |
|||
return Endianness::toNative(getBuilder(), hash); |
|||
} |
|||
|
|||
llvm::Value* Ext::create(llvm::Value* _endowment, llvm::Value* _initOff, llvm::Value* _initSize) |
|||
{ |
|||
auto ret = getArgAlloca(); |
|||
auto begin = m_memoryMan.getBytePtr(_initOff); |
|||
auto size = m_builder.CreateTrunc(_initSize, Type::Size, "size"); |
|||
createCall(EnvFunc::create, {getRuntimeManager().getEnvPtr(), getRuntimeManager().getGasPtr(), byPtr(_endowment), begin, size, ret}); |
|||
llvm::Value* address = m_builder.CreateLoad(ret); |
|||
address = Endianness::toNative(m_builder, address); |
|||
return address; |
|||
} |
|||
|
|||
llvm::Value* Ext::call(llvm::Value* _callGas, llvm::Value* _receiveAddress, llvm::Value* _value, llvm::Value* _inOff, llvm::Value* _inSize, llvm::Value* _outOff, llvm::Value* _outSize, llvm::Value* _codeAddress) |
|||
{ |
|||
auto receiveAddress = Endianness::toBE(m_builder, _receiveAddress); |
|||
auto inBeg = m_memoryMan.getBytePtr(_inOff); |
|||
auto inSize = m_builder.CreateTrunc(_inSize, Type::Size, "in.size"); |
|||
auto outBeg = m_memoryMan.getBytePtr(_outOff); |
|||
auto outSize = m_builder.CreateTrunc(_outSize, Type::Size, "out.size"); |
|||
auto codeAddress = Endianness::toBE(m_builder, _codeAddress); |
|||
auto callGas = m_builder.CreateSelect( |
|||
m_builder.CreateICmpULE(_callGas, m_builder.CreateZExt(Constant::gasMax, Type::Word)), |
|||
m_builder.CreateTrunc(_callGas, Type::Gas), |
|||
Constant::gasMax); |
|||
auto ret = createCall(EnvFunc::call, {getRuntimeManager().getEnvPtr(), getRuntimeManager().getGasPtr(), callGas, byPtr(receiveAddress), byPtr(_value), inBeg, inSize, outBeg, outSize, byPtr(codeAddress)}); |
|||
return m_builder.CreateZExt(ret, Type::Word, "ret"); |
|||
} |
|||
|
|||
llvm::Value* Ext::sha3(llvm::Value* _inOff, llvm::Value* _inSize) |
|||
{ |
|||
auto begin = m_memoryMan.getBytePtr(_inOff); |
|||
auto size = m_builder.CreateTrunc(_inSize, Type::Size, "size"); |
|||
auto ret = getArgAlloca(); |
|||
createCall(EnvFunc::sha3, {begin, size, ret}); |
|||
llvm::Value* hash = m_builder.CreateLoad(ret); |
|||
hash = Endianness::toNative(m_builder, hash); |
|||
return hash; |
|||
} |
|||
|
|||
MemoryRef Ext::extcode(llvm::Value* _addr) |
|||
{ |
|||
auto addr = Endianness::toBE(m_builder, _addr); |
|||
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}; |
|||
} |
|||
|
|||
void Ext::log(llvm::Value* _memIdx, llvm::Value* _numBytes, std::array<llvm::Value*,4> const& _topics) |
|||
{ |
|||
auto begin = m_memoryMan.getBytePtr(_memIdx); |
|||
auto size = m_builder.CreateTrunc(_numBytes, Type::Size, "size"); |
|||
llvm::Value* args[] = {getRuntimeManager().getEnvPtr(), begin, size, getArgAlloca(), getArgAlloca(), getArgAlloca(), getArgAlloca()}; |
|||
|
|||
auto topicArgPtr = &args[3]; |
|||
for (auto&& topic : _topics) |
|||
{ |
|||
if (topic) |
|||
m_builder.CreateStore(Endianness::toBE(m_builder, topic), *topicArgPtr); |
|||
else |
|||
*topicArgPtr = llvm::ConstantPointerNull::get(Type::WordPtr); |
|||
++topicArgPtr; |
|||
} |
|||
|
|||
createCall(EnvFunc::log, {args[0], args[1], args[2], args[3], args[4], args[5], args[6]}); // TODO: use std::initializer_list<>
|
|||
} |
|||
|
|||
} |
|||
} |
|||
} |
@ -1,79 +0,0 @@ |
|||
#pragma once |
|||
|
|||
#include <array> |
|||
|
|||
#include "CompilerHelper.h" |
|||
|
|||
namespace dev |
|||
{ |
|||
namespace eth |
|||
{ |
|||
namespace jit |
|||
{ |
|||
class Memory; |
|||
|
|||
struct MemoryRef |
|||
{ |
|||
llvm::Value* ptr; |
|||
llvm::Value* size; |
|||
}; |
|||
|
|||
template<typename _EnumT> |
|||
struct sizeOf |
|||
{ |
|||
static const size_t value = static_cast<size_t>(_EnumT::_size); |
|||
}; |
|||
|
|||
enum class EnvFunc |
|||
{ |
|||
sload, |
|||
sstore, |
|||
sha3, |
|||
balance, |
|||
create, |
|||
call, |
|||
log, |
|||
blockhash, |
|||
extcode, |
|||
|
|||
_size |
|||
}; |
|||
|
|||
class Ext : public RuntimeHelper |
|||
{ |
|||
public: |
|||
Ext(RuntimeManager& _runtimeManager, Memory& _memoryMan); |
|||
|
|||
llvm::Value* sload(llvm::Value* _index); |
|||
void sstore(llvm::Value* _index, llvm::Value* _value); |
|||
|
|||
llvm::Value* balance(llvm::Value* _address); |
|||
llvm::Value* calldataload(llvm::Value* _index); |
|||
llvm::Value* create(llvm::Value* _endowment, llvm::Value* _initOff, llvm::Value* _initSize); |
|||
llvm::Value* call(llvm::Value* _callGas, llvm::Value* _receiveAddress, llvm::Value* _value, llvm::Value* _inOff, llvm::Value* _inSize, llvm::Value* _outOff, llvm::Value* _outSize, llvm::Value* _codeAddress); |
|||
llvm::Value* blockHash(llvm::Value* _number); |
|||
|
|||
llvm::Value* sha3(llvm::Value* _inOff, llvm::Value* _inSize); |
|||
MemoryRef extcode(llvm::Value* _addr); |
|||
|
|||
void log(llvm::Value* _memIdx, llvm::Value* _numBytes, std::array<llvm::Value*,4> const& _topics); |
|||
|
|||
private: |
|||
Memory& m_memoryMan; |
|||
|
|||
llvm::Value* m_size; |
|||
|
|||
std::array<llvm::Function*, sizeOf<EnvFunc>::value> m_funcs; |
|||
std::array<llvm::Value*, 8> m_argAllocas; |
|||
size_t m_argCounter = 0; |
|||
|
|||
llvm::CallInst* createCall(EnvFunc _funcId, std::initializer_list<llvm::Value*> const& _args); |
|||
llvm::Value* getArgAlloca(); |
|||
llvm::Value* byPtr(llvm::Value* _value); |
|||
}; |
|||
|
|||
|
|||
} |
|||
} |
|||
} |
|||
|
@ -1,299 +0,0 @@ |
|||
#include "GasMeter.h" |
|||
|
|||
#include "preprocessor/llvm_includes_start.h" |
|||
#include <llvm/IR/IntrinsicInst.h> |
|||
#include "preprocessor/llvm_includes_end.h" |
|||
|
|||
#include "Ext.h" |
|||
#include "RuntimeManager.h" |
|||
|
|||
namespace dev |
|||
{ |
|||
namespace eth |
|||
{ |
|||
namespace jit |
|||
{ |
|||
|
|||
namespace // Helper functions
|
|||
{ |
|||
|
|||
int64_t const c_stepGas[] = {0, 2, 3, 5, 8, 10, 20}; |
|||
int64_t const c_expByteGas = 10; |
|||
int64_t const c_sha3Gas = 30; |
|||
int64_t const c_sha3WordGas = 6; |
|||
int64_t const c_sloadGas = 50; |
|||
int64_t const c_sstoreSetGas = 20000; |
|||
int64_t const c_sstoreResetGas = 5000; |
|||
int64_t const c_sstoreClearGas = 5000; |
|||
int64_t const c_jumpdestGas = 1; |
|||
int64_t const c_logGas = 375; |
|||
int64_t const c_logTopicGas = 375; |
|||
int64_t const c_logDataGas = 8; |
|||
int64_t const c_callGas = 40; |
|||
int64_t const c_createGas = 32000; |
|||
int64_t const c_memoryGas = 3; |
|||
int64_t const c_copyGas = 3; |
|||
|
|||
int64_t getStepCost(Instruction inst) |
|||
{ |
|||
switch (inst) |
|||
{ |
|||
// Tier 0
|
|||
case Instruction::STOP: |
|||
case Instruction::RETURN: |
|||
case Instruction::SUICIDE: |
|||
case Instruction::SSTORE: // Handle cost of SSTORE separately in GasMeter::countSStore()
|
|||
return c_stepGas[0]; |
|||
|
|||
// Tier 1
|
|||
case Instruction::ADDRESS: |
|||
case Instruction::ORIGIN: |
|||
case Instruction::CALLER: |
|||
case Instruction::CALLVALUE: |
|||
case Instruction::CALLDATASIZE: |
|||
case Instruction::CODESIZE: |
|||
case Instruction::GASPRICE: |
|||
case Instruction::COINBASE: |
|||
case Instruction::TIMESTAMP: |
|||
case Instruction::NUMBER: |
|||
case Instruction::DIFFICULTY: |
|||
case Instruction::GASLIMIT: |
|||
case Instruction::POP: |
|||
case Instruction::PC: |
|||
case Instruction::MSIZE: |
|||
case Instruction::GAS: |
|||
return c_stepGas[1]; |
|||
|
|||
// Tier 2
|
|||
case Instruction::ADD: |
|||
case Instruction::SUB: |
|||
case Instruction::LT: |
|||
case Instruction::GT: |
|||
case Instruction::SLT: |
|||
case Instruction::SGT: |
|||
case Instruction::EQ: |
|||
case Instruction::ISZERO: |
|||
case Instruction::AND: |
|||
case Instruction::OR: |
|||
case Instruction::XOR: |
|||
case Instruction::NOT: |
|||
case Instruction::BYTE: |
|||
case Instruction::CALLDATALOAD: |
|||
case Instruction::CALLDATACOPY: |
|||
case Instruction::CODECOPY: |
|||
case Instruction::MLOAD: |
|||
case Instruction::MSTORE: |
|||
case Instruction::MSTORE8: |
|||
case Instruction::ANY_PUSH: |
|||
case Instruction::ANY_DUP: |
|||
case Instruction::ANY_SWAP: |
|||
return c_stepGas[2]; |
|||
|
|||
// Tier 3
|
|||
case Instruction::MUL: |
|||
case Instruction::DIV: |
|||
case Instruction::SDIV: |
|||
case Instruction::MOD: |
|||
case Instruction::SMOD: |
|||
case Instruction::SIGNEXTEND: |
|||
return c_stepGas[3]; |
|||
|
|||
// Tier 4
|
|||
case Instruction::ADDMOD: |
|||
case Instruction::MULMOD: |
|||
case Instruction::JUMP: |
|||
return c_stepGas[4]; |
|||
|
|||
// Tier 5
|
|||
case Instruction::EXP: |
|||
case Instruction::JUMPI: |
|||
return c_stepGas[5]; |
|||
|
|||
// Tier 6
|
|||
case Instruction::BALANCE: |
|||
case Instruction::EXTCODESIZE: |
|||
case Instruction::EXTCODECOPY: |
|||
case Instruction::BLOCKHASH: |
|||
return c_stepGas[6]; |
|||
|
|||
case Instruction::SHA3: |
|||
return c_sha3Gas; |
|||
|
|||
case Instruction::SLOAD: |
|||
return c_sloadGas; |
|||
|
|||
case Instruction::JUMPDEST: |
|||
return c_jumpdestGas; |
|||
|
|||
case Instruction::LOG0: |
|||
case Instruction::LOG1: |
|||
case Instruction::LOG2: |
|||
case Instruction::LOG3: |
|||
case Instruction::LOG4: |
|||
{ |
|||
auto numTopics = static_cast<int64_t>(inst) - static_cast<int64_t>(Instruction::LOG0); |
|||
return c_logGas + numTopics * c_logTopicGas; |
|||
} |
|||
|
|||
case Instruction::CALL: |
|||
case Instruction::CALLCODE: |
|||
return c_callGas; |
|||
|
|||
case Instruction::CREATE: |
|||
return c_createGas; |
|||
} |
|||
|
|||
return 0; // TODO: Add UNREACHABLE macro
|
|||
} |
|||
|
|||
} |
|||
|
|||
GasMeter::GasMeter(llvm::IRBuilder<>& _builder, RuntimeManager& _runtimeManager) : |
|||
CompilerHelper(_builder), |
|||
m_runtimeManager(_runtimeManager) |
|||
{ |
|||
llvm::Type* gasCheckArgs[] = {Type::Gas->getPointerTo(), Type::Gas, Type::BytePtr}; |
|||
m_gasCheckFunc = llvm::Function::Create(llvm::FunctionType::get(Type::Void, gasCheckArgs, false), llvm::Function::PrivateLinkage, "gas.check", getModule()); |
|||
m_gasCheckFunc->setDoesNotThrow(); |
|||
m_gasCheckFunc->setDoesNotCapture(1); |
|||
|
|||
auto checkBB = llvm::BasicBlock::Create(_builder.getContext(), "Check", m_gasCheckFunc); |
|||
auto updateBB = llvm::BasicBlock::Create(_builder.getContext(), "Update", m_gasCheckFunc); |
|||
auto outOfGasBB = llvm::BasicBlock::Create(_builder.getContext(), "OutOfGas", m_gasCheckFunc); |
|||
|
|||
auto gasPtr = &m_gasCheckFunc->getArgumentList().front(); |
|||
gasPtr->setName("gasPtr"); |
|||
auto cost = gasPtr->getNextNode(); |
|||
cost->setName("cost"); |
|||
auto jmpBuf = cost->getNextNode(); |
|||
jmpBuf->setName("jmpBuf"); |
|||
|
|||
InsertPointGuard guard(m_builder); |
|||
m_builder.SetInsertPoint(checkBB); |
|||
auto gas = m_builder.CreateLoad(gasPtr, "gas"); |
|||
auto gasUpdated = m_builder.CreateNSWSub(gas, cost, "gasUpdated"); |
|||
auto gasOk = m_builder.CreateICmpSGE(gasUpdated, m_builder.getInt64(0), "gasOk"); // gas >= 0, with gas == 0 we can still do 0 cost instructions
|
|||
m_builder.CreateCondBr(gasOk, updateBB, outOfGasBB, Type::expectTrue); |
|||
|
|||
m_builder.SetInsertPoint(updateBB); |
|||
m_builder.CreateStore(gasUpdated, gasPtr); |
|||
m_builder.CreateRetVoid(); |
|||
|
|||
m_builder.SetInsertPoint(outOfGasBB); |
|||
m_runtimeManager.abort(jmpBuf); |
|||
m_builder.CreateUnreachable(); |
|||
} |
|||
|
|||
void GasMeter::count(Instruction _inst) |
|||
{ |
|||
if (!m_checkCall) |
|||
{ |
|||
// Create gas check call with mocked block cost at begining of current cost-block
|
|||
m_checkCall = createCall(m_gasCheckFunc, {m_runtimeManager.getGasPtr(), llvm::UndefValue::get(Type::Gas), m_runtimeManager.getJmpBuf()}); |
|||
} |
|||
|
|||
m_blockCost += getStepCost(_inst); |
|||
} |
|||
|
|||
void GasMeter::count(llvm::Value* _cost, llvm::Value* _jmpBuf, llvm::Value* _gasPtr) |
|||
{ |
|||
if (_cost->getType() == Type::Word) |
|||
{ |
|||
auto gasMax256 = m_builder.CreateZExt(Constant::gasMax, Type::Word); |
|||
auto tooHigh = m_builder.CreateICmpUGT(_cost, gasMax256, "costTooHigh"); |
|||
auto cost64 = m_builder.CreateTrunc(_cost, Type::Gas); |
|||
_cost = m_builder.CreateSelect(tooHigh, Constant::gasMax, cost64, "cost"); |
|||
} |
|||
|
|||
assert(_cost->getType() == Type::Gas); |
|||
createCall(m_gasCheckFunc, {_gasPtr ? _gasPtr : m_runtimeManager.getGasPtr(), _cost, _jmpBuf ? _jmpBuf : m_runtimeManager.getJmpBuf()}); |
|||
} |
|||
|
|||
void GasMeter::countExp(llvm::Value* _exponent) |
|||
{ |
|||
// Additional cost is 1 per significant byte of exponent
|
|||
// lz - leading zeros
|
|||
// cost = ((256 - lz) + 7) / 8
|
|||
|
|||
// OPT: Can gas update be done in exp algorithm?
|
|||
auto ctlz = llvm::Intrinsic::getDeclaration(getModule(), llvm::Intrinsic::ctlz, Type::Word); |
|||
auto lz256 = m_builder.CreateCall(ctlz, {_exponent, m_builder.getInt1(false)}); |
|||
auto lz = m_builder.CreateTrunc(lz256, Type::Gas, "lz"); |
|||
auto sigBits = m_builder.CreateSub(m_builder.getInt64(256), lz, "sigBits"); |
|||
auto sigBytes = m_builder.CreateUDiv(m_builder.CreateAdd(sigBits, m_builder.getInt64(7)), m_builder.getInt64(8)); |
|||
count(m_builder.CreateNUWMul(sigBytes, m_builder.getInt64(c_expByteGas))); |
|||
} |
|||
|
|||
void GasMeter::countSStore(Ext& _ext, llvm::Value* _index, llvm::Value* _newValue) |
|||
{ |
|||
auto oldValue = _ext.sload(_index); |
|||
auto oldValueIsZero = m_builder.CreateICmpEQ(oldValue, Constant::get(0), "oldValueIsZero"); |
|||
auto newValueIsntZero = m_builder.CreateICmpNE(_newValue, Constant::get(0), "newValueIsntZero"); |
|||
auto isInsert = m_builder.CreateAnd(oldValueIsZero, newValueIsntZero, "isInsert"); |
|||
static_assert(c_sstoreResetGas == c_sstoreClearGas, "Update SSTORE gas cost"); |
|||
auto cost = m_builder.CreateSelect(isInsert, m_builder.getInt64(c_sstoreSetGas), m_builder.getInt64(c_sstoreResetGas), "cost"); |
|||
count(cost); |
|||
} |
|||
|
|||
void GasMeter::countLogData(llvm::Value* _dataLength) |
|||
{ |
|||
assert(m_checkCall); |
|||
assert(m_blockCost > 0); // LOGn instruction is already counted
|
|||
static_assert(c_logDataGas != 1, "Log data gas cost has changed. Update GasMeter."); |
|||
count(m_builder.CreateNUWMul(_dataLength, Constant::get(c_logDataGas))); // TODO: Use i64
|
|||
} |
|||
|
|||
void GasMeter::countSha3Data(llvm::Value* _dataLength) |
|||
{ |
|||
assert(m_checkCall); |
|||
assert(m_blockCost > 0); // SHA3 instruction is already counted
|
|||
|
|||
// TODO: This round ups to 32 happens in many places
|
|||
static_assert(c_sha3WordGas != 1, "SHA3 data cost has changed. Update GasMeter"); |
|||
auto dataLength64 = getBuilder().CreateTrunc(_dataLength, Type::Gas); |
|||
auto words64 = m_builder.CreateUDiv(m_builder.CreateNUWAdd(dataLength64, getBuilder().getInt64(31)), getBuilder().getInt64(32)); |
|||
auto cost64 = m_builder.CreateNUWMul(getBuilder().getInt64(c_sha3WordGas), words64); |
|||
count(cost64); |
|||
} |
|||
|
|||
void GasMeter::giveBack(llvm::Value* _gas) |
|||
{ |
|||
assert(_gas->getType() == Type::Gas); |
|||
m_runtimeManager.setGas(m_builder.CreateAdd(m_runtimeManager.getGas(), _gas)); |
|||
} |
|||
|
|||
void GasMeter::commitCostBlock() |
|||
{ |
|||
// If any uncommited block
|
|||
if (m_checkCall) |
|||
{ |
|||
if (m_blockCost == 0) // Do not check 0
|
|||
{ |
|||
m_checkCall->eraseFromParent(); // Remove the gas check call
|
|||
m_checkCall = nullptr; |
|||
return; |
|||
} |
|||
|
|||
m_checkCall->setArgOperand(1, m_builder.getInt64(m_blockCost)); // Update block cost in gas check call
|
|||
m_checkCall = nullptr; // End cost-block
|
|||
m_blockCost = 0; |
|||
} |
|||
assert(m_blockCost == 0); |
|||
} |
|||
|
|||
void GasMeter::countMemory(llvm::Value* _additionalMemoryInWords, llvm::Value* _jmpBuf, llvm::Value* _gasPtr) |
|||
{ |
|||
static_assert(c_memoryGas != 1, "Memory gas cost has changed. Update GasMeter."); |
|||
count(_additionalMemoryInWords, _jmpBuf, _gasPtr); |
|||
} |
|||
|
|||
void GasMeter::countCopy(llvm::Value* _copyWords) |
|||
{ |
|||
static_assert(c_copyGas != 1, "Copy gas cost has changed. Update GasMeter."); |
|||
count(m_builder.CreateNUWMul(_copyWords, m_builder.getInt64(c_copyGas))); |
|||
} |
|||
|
|||
} |
|||
} |
|||
} |
|||
|
@ -1,64 +0,0 @@ |
|||
#pragma once |
|||
|
|||
#include "CompilerHelper.h" |
|||
#include "Instruction.h" |
|||
|
|||
namespace dev |
|||
{ |
|||
namespace eth |
|||
{ |
|||
namespace jit |
|||
{ |
|||
class RuntimeManager; |
|||
using namespace evmjit; |
|||
|
|||
class GasMeter : public CompilerHelper // TODO: Use RuntimeHelper
|
|||
{ |
|||
public: |
|||
GasMeter(llvm::IRBuilder<>& _builder, RuntimeManager& _runtimeManager); |
|||
|
|||
/// Count step cost of instruction
|
|||
void count(Instruction _inst); |
|||
|
|||
/// Count additional cost
|
|||
void count(llvm::Value* _cost, llvm::Value* _jmpBuf = nullptr, llvm::Value* _gasPtr = nullptr); |
|||
|
|||
/// Calculate & count gas cost for SSTORE instruction
|
|||
void countSStore(class Ext& _ext, llvm::Value* _index, llvm::Value* _newValue); |
|||
|
|||
/// Calculate & count additional gas cost for EXP instruction
|
|||
void countExp(llvm::Value* _exponent); |
|||
|
|||
/// Count gas cost of LOG data
|
|||
void countLogData(llvm::Value* _dataLength); |
|||
|
|||
/// Count gas cost of SHA3 data
|
|||
void countSha3Data(llvm::Value* _dataLength); |
|||
|
|||
/// Finalize cost-block by checking gas needed for the block before the block
|
|||
void commitCostBlock(); |
|||
|
|||
/// Give back an amount of gas not used by a call
|
|||
void giveBack(llvm::Value* _gas); |
|||
|
|||
/// Generate code that checks the cost of additional memory used by program
|
|||
void countMemory(llvm::Value* _additionalMemoryInWords, llvm::Value* _jmpBuf, llvm::Value* _gasPtr); |
|||
|
|||
/// Count addional gas cost for memory copy
|
|||
void countCopy(llvm::Value* _copyWords); |
|||
|
|||
private: |
|||
/// Cumulative gas cost of a block of instructions
|
|||
/// @TODO Handle overflow
|
|||
int64_t m_blockCost = 0; |
|||
|
|||
llvm::CallInst* m_checkCall = nullptr; |
|||
llvm::Function* m_gasCheckFunc = nullptr; |
|||
|
|||
RuntimeManager& m_runtimeManager; |
|||
}; |
|||
|
|||
} |
|||
} |
|||
} |
|||
|
@ -1,39 +0,0 @@ |
|||
#include "Instruction.h" |
|||
|
|||
#include "preprocessor/llvm_includes_start.h" |
|||
#include <llvm/ADT/APInt.h> |
|||
#include "preprocessor/llvm_includes_end.h" |
|||
|
|||
namespace dev |
|||
{ |
|||
namespace evmjit |
|||
{ |
|||
|
|||
llvm::APInt readPushData(code_iterator& _curr, code_iterator _end) |
|||
{ |
|||
auto pushInst = *_curr; |
|||
assert(Instruction(pushInst) >= Instruction::PUSH1 && Instruction(pushInst) <= Instruction::PUSH32); |
|||
auto numBytes = pushInst - static_cast<size_t>(Instruction::PUSH1) + 1; |
|||
llvm::APInt value(256, 0); |
|||
++_curr; // Point the data
|
|||
for (decltype(numBytes) i = 0; i < numBytes; ++i) |
|||
{ |
|||
byte b = (_curr != _end) ? *_curr++ : 0; |
|||
value <<= 8; |
|||
value |= b; |
|||
} |
|||
--_curr; // Point the last real byte read
|
|||
return value; |
|||
} |
|||
|
|||
void skipPushData(code_iterator& _curr, code_iterator _end) |
|||
{ |
|||
auto pushInst = *_curr; |
|||
assert(Instruction(pushInst) >= Instruction::PUSH1 && Instruction(pushInst) <= Instruction::PUSH32); |
|||
auto numBytes = pushInst - static_cast<size_t>(Instruction::PUSH1) + 1; |
|||
--_end; |
|||
for (decltype(numBytes) i = 0; i < numBytes && _curr < _end; ++i, ++_curr) {} |
|||
} |
|||
|
|||
} |
|||
} |
@ -1,236 +0,0 @@ |
|||
#pragma once |
|||
|
|||
#include "Common.h" |
|||
|
|||
namespace llvm |
|||
{ |
|||
class APInt; |
|||
} |
|||
|
|||
namespace dev |
|||
{ |
|||
namespace evmjit |
|||
{ |
|||
|
|||
/// Virtual machine bytecode instruction.
|
|||
enum class Instruction: uint8_t |
|||
{ |
|||
STOP = 0x00, ///< halts execution
|
|||
ADD, ///< addition operation
|
|||
MUL, ///< mulitplication operation
|
|||
SUB, ///< subtraction operation
|
|||
DIV, ///< integer division operation
|
|||
SDIV, ///< signed integer division operation
|
|||
MOD, ///< modulo remainder operation
|
|||
SMOD, ///< signed modulo remainder operation
|
|||
ADDMOD, ///< unsigned modular addition
|
|||
MULMOD, ///< unsigned modular multiplication
|
|||
EXP, ///< exponential operation
|
|||
SIGNEXTEND, ///< extend length of signed integer
|
|||
|
|||
LT = 0x10, ///< less-than comparision
|
|||
GT, ///< greater-than comparision
|
|||
SLT, ///< signed less-than comparision
|
|||
SGT, ///< signed greater-than comparision
|
|||
EQ, ///< equality comparision
|
|||
ISZERO, ///< simple not operator
|
|||
AND, ///< bitwise AND operation
|
|||
OR, ///< bitwise OR operation
|
|||
XOR, ///< bitwise XOR operation
|
|||
NOT, ///< bitwise NOT opertation
|
|||
BYTE, ///< retrieve single byte from word
|
|||
|
|||
SHA3 = 0x20, ///< compute SHA3-256 hash
|
|||
|
|||
ADDRESS = 0x30, ///< get address of currently executing account
|
|||
BALANCE, ///< get balance of the given account
|
|||
ORIGIN, ///< get execution origination address
|
|||
CALLER, ///< get caller address
|
|||
CALLVALUE, ///< get deposited value by the instruction/transaction responsible for this execution
|
|||
CALLDATALOAD, ///< get input data of current environment
|
|||
CALLDATASIZE, ///< get size of input data in current environment
|
|||
CALLDATACOPY, ///< copy input data in current environment to memory
|
|||
CODESIZE, ///< get size of code running in current environment
|
|||
CODECOPY, ///< copy code running in current environment to memory
|
|||
GASPRICE, ///< get price of gas in current environment
|
|||
EXTCODESIZE, ///< get external code size (from another contract)
|
|||
EXTCODECOPY, ///< copy external code (from another contract)
|
|||
|
|||
BLOCKHASH = 0x40, ///< get hash of most recent complete block
|
|||
COINBASE, ///< get the block's coinbase address
|
|||
TIMESTAMP, ///< get the block's timestamp
|
|||
NUMBER, ///< get the block's number
|
|||
DIFFICULTY, ///< get the block's difficulty
|
|||
GASLIMIT, ///< get the block's gas limit
|
|||
|
|||
POP = 0x50, ///< remove item from stack
|
|||
MLOAD, ///< load word from memory
|
|||
MSTORE, ///< save word to memory
|
|||
MSTORE8, ///< save byte to memory
|
|||
SLOAD, ///< load word from storage
|
|||
SSTORE, ///< save word to storage
|
|||
JUMP, ///< alter the program counter
|
|||
JUMPI, ///< conditionally alter the program counter
|
|||
PC, ///< get the program counter
|
|||
MSIZE, ///< get the size of active memory
|
|||
GAS, ///< get the amount of available gas
|
|||
JUMPDEST, ///< set a potential jump destination
|
|||
|
|||
PUSH1 = 0x60, ///< place 1 byte item on stack
|
|||
PUSH2, ///< place 2 byte item on stack
|
|||
PUSH3, ///< place 3 byte item on stack
|
|||
PUSH4, ///< place 4 byte item on stack
|
|||
PUSH5, ///< place 5 byte item on stack
|
|||
PUSH6, ///< place 6 byte item on stack
|
|||
PUSH7, ///< place 7 byte item on stack
|
|||
PUSH8, ///< place 8 byte item on stack
|
|||
PUSH9, ///< place 9 byte item on stack
|
|||
PUSH10, ///< place 10 byte item on stack
|
|||
PUSH11, ///< place 11 byte item on stack
|
|||
PUSH12, ///< place 12 byte item on stack
|
|||
PUSH13, ///< place 13 byte item on stack
|
|||
PUSH14, ///< place 14 byte item on stack
|
|||
PUSH15, ///< place 15 byte item on stack
|
|||
PUSH16, ///< place 16 byte item on stack
|
|||
PUSH17, ///< place 17 byte item on stack
|
|||
PUSH18, ///< place 18 byte item on stack
|
|||
PUSH19, ///< place 19 byte item on stack
|
|||
PUSH20, ///< place 20 byte item on stack
|
|||
PUSH21, ///< place 21 byte item on stack
|
|||
PUSH22, ///< place 22 byte item on stack
|
|||
PUSH23, ///< place 23 byte item on stack
|
|||
PUSH24, ///< place 24 byte item on stack
|
|||
PUSH25, ///< place 25 byte item on stack
|
|||
PUSH26, ///< place 26 byte item on stack
|
|||
PUSH27, ///< place 27 byte item on stack
|
|||
PUSH28, ///< place 28 byte item on stack
|
|||
PUSH29, ///< place 29 byte item on stack
|
|||
PUSH30, ///< place 30 byte item on stack
|
|||
PUSH31, ///< place 31 byte item on stack
|
|||
PUSH32, ///< place 32 byte item on stack
|
|||
|
|||
DUP1 = 0x80, ///< copies the highest item in the stack to the top of the stack
|
|||
DUP2, ///< copies the second highest item in the stack to the top of the stack
|
|||
DUP3, ///< copies the third highest item in the stack to the top of the stack
|
|||
DUP4, ///< copies the 4th highest item in the stack to the top of the stack
|
|||
DUP5, ///< copies the 5th highest item in the stack to the top of the stack
|
|||
DUP6, ///< copies the 6th highest item in the stack to the top of the stack
|
|||
DUP7, ///< copies the 7th highest item in the stack to the top of the stack
|
|||
DUP8, ///< copies the 8th highest item in the stack to the top of the stack
|
|||
DUP9, ///< copies the 9th highest item in the stack to the top of the stack
|
|||
DUP10, ///< copies the 10th highest item in the stack to the top of the stack
|
|||
DUP11, ///< copies the 11th highest item in the stack to the top of the stack
|
|||
DUP12, ///< copies the 12th highest item in the stack to the top of the stack
|
|||
DUP13, ///< copies the 13th highest item in the stack to the top of the stack
|
|||
DUP14, ///< copies the 14th highest item in the stack to the top of the stack
|
|||
DUP15, ///< copies the 15th highest item in the stack to the top of the stack
|
|||
DUP16, ///< copies the 16th highest item in the stack to the top of the stack
|
|||
|
|||
SWAP1 = 0x90, ///< swaps the highest and second highest value on the stack
|
|||
SWAP2, ///< swaps the highest and third highest value on the stack
|
|||
SWAP3, ///< swaps the highest and 4th highest value on the stack
|
|||
SWAP4, ///< swaps the highest and 5th highest value on the stack
|
|||
SWAP5, ///< swaps the highest and 6th highest value on the stack
|
|||
SWAP6, ///< swaps the highest and 7th highest value on the stack
|
|||
SWAP7, ///< swaps the highest and 8th highest value on the stack
|
|||
SWAP8, ///< swaps the highest and 9th highest value on the stack
|
|||
SWAP9, ///< swaps the highest and 10th highest value on the stack
|
|||
SWAP10, ///< swaps the highest and 11th highest value on the stack
|
|||
SWAP11, ///< swaps the highest and 12th highest value on the stack
|
|||
SWAP12, ///< swaps the highest and 13th highest value on the stack
|
|||
SWAP13, ///< swaps the highest and 14th highest value on the stack
|
|||
SWAP14, ///< swaps the highest and 15th highest value on the stack
|
|||
SWAP15, ///< swaps the highest and 16th highest value on the stack
|
|||
SWAP16, ///< swaps the highest and 17th highest value on the stack
|
|||
|
|||
LOG0 = 0xa0, ///< Makes a log entry; no topics.
|
|||
LOG1, ///< Makes a log entry; 1 topic.
|
|||
LOG2, ///< Makes a log entry; 2 topics.
|
|||
LOG3, ///< Makes a log entry; 3 topics.
|
|||
LOG4, ///< Makes a log entry; 4 topics.
|
|||
|
|||
CREATE = 0xf0, ///< create a new account with associated code
|
|||
CALL, ///< message-call into an account
|
|||
CALLCODE, ///< message-call with another account's code only
|
|||
RETURN, ///< halt execution returning output data
|
|||
SUICIDE = 0xff ///< halt execution and register account for later deletion
|
|||
}; |
|||
|
|||
/// Reads PUSH data from pointed fragment of bytecode and constructs number out of it
|
|||
/// Reading out of bytecode means reading 0
|
|||
/// @param _curr is updated and points the last real byte read
|
|||
llvm::APInt readPushData(code_iterator& _curr, code_iterator _end); |
|||
|
|||
/// Skips PUSH data in pointed fragment of bytecode.
|
|||
/// @param _curr is updated and points the last real byte skipped
|
|||
void skipPushData(code_iterator& _curr, code_iterator _end); |
|||
|
|||
#define ANY_PUSH PUSH1: \ |
|||
case Instruction::PUSH2: \ |
|||
case Instruction::PUSH3: \ |
|||
case Instruction::PUSH4: \ |
|||
case Instruction::PUSH5: \ |
|||
case Instruction::PUSH6: \ |
|||
case Instruction::PUSH7: \ |
|||
case Instruction::PUSH8: \ |
|||
case Instruction::PUSH9: \ |
|||
case Instruction::PUSH10: \ |
|||
case Instruction::PUSH11: \ |
|||
case Instruction::PUSH12: \ |
|||
case Instruction::PUSH13: \ |
|||
case Instruction::PUSH14: \ |
|||
case Instruction::PUSH15: \ |
|||
case Instruction::PUSH16: \ |
|||
case Instruction::PUSH17: \ |
|||
case Instruction::PUSH18: \ |
|||
case Instruction::PUSH19: \ |
|||
case Instruction::PUSH20: \ |
|||
case Instruction::PUSH21: \ |
|||
case Instruction::PUSH22: \ |
|||
case Instruction::PUSH23: \ |
|||
case Instruction::PUSH24: \ |
|||
case Instruction::PUSH25: \ |
|||
case Instruction::PUSH26: \ |
|||
case Instruction::PUSH27: \ |
|||
case Instruction::PUSH28: \ |
|||
case Instruction::PUSH29: \ |
|||
case Instruction::PUSH30: \ |
|||
case Instruction::PUSH31: \ |
|||
case Instruction::PUSH32 |
|||
|
|||
#define ANY_DUP DUP1: \ |
|||
case Instruction::DUP2: \ |
|||
case Instruction::DUP3: \ |
|||
case Instruction::DUP4: \ |
|||
case Instruction::DUP5: \ |
|||
case Instruction::DUP6: \ |
|||
case Instruction::DUP7: \ |
|||
case Instruction::DUP8: \ |
|||
case Instruction::DUP9: \ |
|||
case Instruction::DUP10: \ |
|||
case Instruction::DUP11: \ |
|||
case Instruction::DUP12: \ |
|||
case Instruction::DUP13: \ |
|||
case Instruction::DUP14: \ |
|||
case Instruction::DUP15: \ |
|||
case Instruction::DUP16 |
|||
|
|||
#define ANY_SWAP SWAP1: \ |
|||
case Instruction::SWAP2: \ |
|||
case Instruction::SWAP3: \ |
|||
case Instruction::SWAP4: \ |
|||
case Instruction::SWAP5: \ |
|||
case Instruction::SWAP6: \ |
|||
case Instruction::SWAP7: \ |
|||
case Instruction::SWAP8: \ |
|||
case Instruction::SWAP9: \ |
|||
case Instruction::SWAP10: \ |
|||
case Instruction::SWAP11: \ |
|||
case Instruction::SWAP12: \ |
|||
case Instruction::SWAP13: \ |
|||
case Instruction::SWAP14: \ |
|||
case Instruction::SWAP15: \ |
|||
case Instruction::SWAP16 |
|||
|
|||
} |
|||
} |
@ -1,48 +0,0 @@ |
|||
#include <evmjit/JIT-c.h> |
|||
#include <cassert> |
|||
#include <evmjit/JIT.h> |
|||
|
|||
extern "C" |
|||
{ |
|||
using namespace dev::evmjit; |
|||
|
|||
evmjit_context* evmjit_create(evmjit_runtime_data* _data, void* _env) |
|||
{ |
|||
auto data = reinterpret_cast<RuntimeData*>(_data); |
|||
auto env = reinterpret_cast<Env*>(_env); |
|||
|
|||
assert(!data && "Pointer to runtime data must not be null"); |
|||
if (!data) |
|||
return nullptr; |
|||
|
|||
// TODO: Make sure ExecutionEngine constructor does not throw + make JIT/ExecutionEngine interface all nothrow
|
|||
auto context = new(std::nothrow) ExecutionContext{*data, env}; |
|||
return reinterpret_cast<evmjit_context*>(context); |
|||
} |
|||
|
|||
void evmjit_destroy(evmjit_context* _context) |
|||
{ |
|||
auto context = reinterpret_cast<ExecutionContext*>(_context); |
|||
delete context; |
|||
} |
|||
|
|||
evmjit_return_code evmjit_exec(evmjit_context* _context) |
|||
{ |
|||
auto context = reinterpret_cast<ExecutionContext*>(_context); |
|||
|
|||
assert(!context && "Invalid context"); |
|||
if (!context) |
|||
return UnexpectedException; |
|||
|
|||
try |
|||
{ |
|||
auto returnCode = JIT::exec(*context); |
|||
return static_cast<evmjit_return_code>(returnCode); |
|||
} |
|||
catch(...) |
|||
{ |
|||
return UnexpectedException; |
|||
} |
|||
} |
|||
|
|||
} |
@ -1,252 +0,0 @@ |
|||
#include "evmjit/JIT.h" |
|||
|
|||
#include <array> |
|||
#include <mutex> |
|||
|
|||
#include "preprocessor/llvm_includes_start.h" |
|||
#include <llvm/IR/Module.h> |
|||
#include <llvm/ADT/Triple.h> |
|||
#include <llvm/ExecutionEngine/MCJIT.h> |
|||
#include <llvm/Support/TargetSelect.h> |
|||
#include <llvm/Support/Host.h> |
|||
#include <llvm/Support/CommandLine.h> |
|||
#include <llvm/Support/ManagedStatic.h> |
|||
#include "preprocessor/llvm_includes_end.h" |
|||
|
|||
#include "Compiler.h" |
|||
#include "Optimizer.h" |
|||
#include "Cache.h" |
|||
#include "ExecStats.h" |
|||
#include "Utils.h" |
|||
#include "BuildInfo.gen.h" |
|||
|
|||
namespace dev |
|||
{ |
|||
namespace evmjit |
|||
{ |
|||
using namespace eth::jit; |
|||
|
|||
namespace |
|||
{ |
|||
using ExecFunc = ReturnCode(*)(ExecutionContext*); |
|||
|
|||
std::string hash2str(i256 const& _hash) |
|||
{ |
|||
static const auto size = sizeof(_hash); |
|||
static const auto hexChars = "0123456789abcdef"; |
|||
std::string str; |
|||
str.resize(size * 2); |
|||
auto outIt = str.rbegin(); // reverse for BE
|
|||
auto& arr = *(std::array<byte, size>*)&_hash; |
|||
for (auto b : arr) |
|||
{ |
|||
*(outIt++) = hexChars[b & 0xf]; |
|||
*(outIt++) = hexChars[b >> 4]; |
|||
} |
|||
return str; |
|||
} |
|||
|
|||
void printVersion() |
|||
{ |
|||
std::cout << "Ethereum EVM JIT Compiler (http://github.com/ethereum/evmjit):\n" |
|||
<< " EVMJIT version " << EVMJIT_VERSION << "\n" |
|||
#ifdef NDEBUG |
|||
<< " Optimized build, " |
|||
#else |
|||
<< " DEBUG build, " |
|||
#endif |
|||
<< __DATE__ << " (" << __TIME__ << ")\n" |
|||
<< std::endl; |
|||
} |
|||
|
|||
namespace cl = llvm::cl; |
|||
cl::opt<bool> g_optimize{"O", cl::desc{"Optimize"}}; |
|||
cl::opt<CacheMode> g_cache{"cache", cl::desc{"Cache compiled EVM code on disk"}, |
|||
cl::values( |
|||
clEnumValN(CacheMode::on, "1", "Enabled"), |
|||
clEnumValN(CacheMode::off, "0", "Disabled"), |
|||
clEnumValN(CacheMode::read, "r", "Read only. No new objects are added to cache."), |
|||
clEnumValN(CacheMode::write, "w", "Write only. No objects are loaded from cache."), |
|||
clEnumValN(CacheMode::clear, "c", "Clear the cache storage. Cache is disabled."), |
|||
clEnumValN(CacheMode::preload, "p", "Preload all cached objects."), |
|||
clEnumValEnd)}; |
|||
cl::opt<bool> g_stats{"st", cl::desc{"Statistics"}}; |
|||
cl::opt<bool> g_dump{"dump", cl::desc{"Dump LLVM IR module"}}; |
|||
|
|||
void parseOptions() |
|||
{ |
|||
static llvm::llvm_shutdown_obj shutdownObj{}; |
|||
cl::AddExtraVersionPrinter(printVersion); |
|||
cl::ParseEnvironmentOptions("evmjit", "EVMJIT", "Ethereum EVM JIT Compiler"); |
|||
} |
|||
|
|||
class JITImpl |
|||
{ |
|||
std::unique_ptr<llvm::ExecutionEngine> m_engine; |
|||
mutable std::mutex x_codeMap; |
|||
std::unordered_map<h256, ExecFunc> m_codeMap; |
|||
|
|||
public: |
|||
static JITImpl& instance() |
|||
{ |
|||
static JITImpl s_instance; |
|||
return s_instance; |
|||
} |
|||
|
|||
JITImpl(); |
|||
|
|||
llvm::ExecutionEngine& engine() { return *m_engine; } |
|||
|
|||
ExecFunc getExecFunc(h256 const& _codeHash) const; |
|||
void mapExecFunc(h256 _codeHash, ExecFunc _funcAddr); |
|||
|
|||
ExecFunc compile(byte const* _code, uint64_t _codeSize, h256 const& _codeHash); |
|||
}; |
|||
|
|||
JITImpl::JITImpl() |
|||
{ |
|||
parseOptions(); |
|||
|
|||
bool preloadCache = g_cache == CacheMode::preload; |
|||
if (preloadCache) |
|||
g_cache = CacheMode::on; |
|||
|
|||
llvm::InitializeNativeTarget(); |
|||
llvm::InitializeNativeTargetAsmPrinter(); |
|||
|
|||
auto module = std::unique_ptr<llvm::Module>(new llvm::Module({}, llvm::getGlobalContext())); |
|||
|
|||
// FIXME: LLVM 3.7: test on Windows
|
|||
auto triple = llvm::Triple(llvm::sys::getProcessTriple()); |
|||
if (triple.getOS() == llvm::Triple::OSType::Win32) |
|||
triple.setObjectFormat(llvm::Triple::ObjectFormatType::ELF); // MCJIT does not support COFF format
|
|||
module->setTargetTriple(triple.str()); |
|||
|
|||
llvm::EngineBuilder builder(std::move(module)); |
|||
builder.setEngineKind(llvm::EngineKind::JIT); |
|||
builder.setOptLevel(g_optimize ? llvm::CodeGenOpt::Default : llvm::CodeGenOpt::None); |
|||
|
|||
m_engine.reset(builder.create()); |
|||
|
|||
// TODO: Update cache listener
|
|||
m_engine->setObjectCache(Cache::init(g_cache, nullptr)); |
|||
|
|||
// FIXME: Disabled during API changes
|
|||
//if (preloadCache)
|
|||
// Cache::preload(*m_engine, funcCache);
|
|||
} |
|||
|
|||
ExecFunc JITImpl::getExecFunc(h256 const& _codeHash) const |
|||
{ |
|||
std::lock_guard<std::mutex> lock{x_codeMap}; |
|||
auto it = m_codeMap.find(_codeHash); |
|||
if (it != m_codeMap.end()) |
|||
return it->second; |
|||
return nullptr; |
|||
} |
|||
|
|||
void JITImpl::mapExecFunc(h256 _codeHash, ExecFunc _funcAddr) |
|||
{ |
|||
std::lock_guard<std::mutex> lock{x_codeMap}; |
|||
m_codeMap.emplace(std::move(_codeHash), _funcAddr); |
|||
} |
|||
|
|||
ExecFunc JITImpl::compile(byte const* _code, uint64_t _codeSize, h256 const& _codeHash) |
|||
{ |
|||
auto name = hash2str(_codeHash); |
|||
auto module = Cache::getObject(name); |
|||
if (!module) |
|||
{ |
|||
// TODO: Listener support must be redesigned. These should be a feature of JITImpl
|
|||
//listener->stateChanged(ExecState::Compilation);
|
|||
assert(_code || !_codeSize); //TODO: Is it good idea to execute empty code?
|
|||
module = Compiler{{}}.compile(_code, _code + _codeSize, name); |
|||
|
|||
if (g_optimize) |
|||
{ |
|||
//listener->stateChanged(ExecState::Optimization);
|
|||
optimize(*module); |
|||
} |
|||
|
|||
prepare(*module); |
|||
} |
|||
if (g_dump) |
|||
module->dump(); |
|||
|
|||
m_engine->addModule(std::move(module)); |
|||
//listener->stateChanged(ExecState::CodeGen);
|
|||
return (ExecFunc)m_engine->getFunctionAddress(name); |
|||
} |
|||
|
|||
} // anonymous namespace
|
|||
|
|||
bool JIT::isCodeReady(h256 const& _codeHash) |
|||
{ |
|||
return JITImpl::instance().getExecFunc(_codeHash) != nullptr; |
|||
} |
|||
|
|||
void JIT::compile(byte const* _code, uint64_t _codeSize, h256 const& _codeHash) |
|||
{ |
|||
auto& jit = JITImpl::instance(); |
|||
auto execFunc = jit.compile(_code, _codeSize, _codeHash); |
|||
if (execFunc) // FIXME: What with error?
|
|||
jit.mapExecFunc(_codeHash, execFunc); |
|||
} |
|||
|
|||
ReturnCode JIT::exec(ExecutionContext& _context) |
|||
{ |
|||
//std::unique_ptr<ExecStats> listener{new ExecStats};
|
|||
//listener->stateChanged(ExecState::Started);
|
|||
//static StatsCollector statsCollector;
|
|||
|
|||
auto& jit = JITImpl::instance(); |
|||
auto codeHash = _context.codeHash(); |
|||
auto execFunc = jit.getExecFunc(codeHash); |
|||
if (!execFunc) |
|||
{ |
|||
execFunc = jit.compile(_context.code(), _context.codeSize(), codeHash); |
|||
if (!execFunc) |
|||
return ReturnCode::LLVMError; |
|||
jit.mapExecFunc(codeHash, execFunc); |
|||
} |
|||
|
|||
//listener->stateChanged(ExecState::Execution);
|
|||
auto returnCode = execFunc(&_context); |
|||
//listener->stateChanged(ExecState::Return);
|
|||
|
|||
if (returnCode == ReturnCode::Return) |
|||
_context.returnData = _context.getReturnData(); // Save reference to return data
|
|||
|
|||
//listener->stateChanged(ExecState::Finished);
|
|||
// if (g_stats)
|
|||
// statsCollector.stats.push_back(std::move(listener));
|
|||
|
|||
return returnCode; |
|||
} |
|||
|
|||
|
|||
extern "C" void ext_free(void* _data) noexcept; |
|||
|
|||
ExecutionContext::~ExecutionContext() noexcept |
|||
{ |
|||
if (m_memData) |
|||
ext_free(m_memData); // Use helper free to check memory leaks
|
|||
} |
|||
|
|||
bytes_ref ExecutionContext::getReturnData() const |
|||
{ |
|||
auto data = m_data->callData; |
|||
auto size = static_cast<size_t>(m_data->callDataSize); |
|||
|
|||
if (data < m_memData || data >= m_memData + m_memSize || size == 0) |
|||
{ |
|||
assert(size == 0); // data can be an invalid pointer only if size is 0
|
|||
m_data->callData = nullptr; |
|||
return {}; |
|||
} |
|||
|
|||
return bytes_ref{data, size}; |
|||
} |
|||
|
|||
} |
|||
} |
@ -1,246 +0,0 @@ |
|||
#include "Memory.h" |
|||
|
|||
#include "preprocessor/llvm_includes_start.h" |
|||
#include <llvm/IR/IntrinsicInst.h> |
|||
#include "preprocessor/llvm_includes_end.h" |
|||
|
|||
#include "Type.h" |
|||
#include "GasMeter.h" |
|||
#include "Endianness.h" |
|||
#include "RuntimeManager.h" |
|||
|
|||
namespace dev |
|||
{ |
|||
namespace eth |
|||
{ |
|||
namespace jit |
|||
{ |
|||
|
|||
Memory::Memory(RuntimeManager& _runtimeManager, GasMeter& _gasMeter): |
|||
RuntimeHelper(_runtimeManager), // TODO: RuntimeHelper not needed
|
|||
m_memory{getBuilder(), _runtimeManager.getMem()}, |
|||
m_gasMeter(_gasMeter) |
|||
{} |
|||
|
|||
llvm::Function* Memory::getRequireFunc() |
|||
{ |
|||
auto& func = m_require; |
|||
if (!func) |
|||
{ |
|||
llvm::Type* argTypes[] = {Array::getType()->getPointerTo(), Type::Word, Type::Word, Type::BytePtr, Type::GasPtr}; |
|||
func = llvm::Function::Create(llvm::FunctionType::get(Type::Void, argTypes, false), llvm::Function::PrivateLinkage, "mem.require", getModule()); |
|||
func->setDoesNotThrow(); |
|||
|
|||
auto mem = &func->getArgumentList().front(); |
|||
mem->setName("mem"); |
|||
auto blkOffset = mem->getNextNode(); |
|||
blkOffset->setName("blkOffset"); |
|||
auto blkSize = blkOffset->getNextNode(); |
|||
blkSize->setName("blkSize"); |
|||
auto jmpBuf = blkSize->getNextNode(); |
|||
jmpBuf->setName("jmpBuf"); |
|||
auto gas = jmpBuf->getNextNode(); |
|||
gas->setName("gas"); |
|||
|
|||
auto preBB = llvm::BasicBlock::Create(func->getContext(), "Pre", func); |
|||
auto checkBB = llvm::BasicBlock::Create(func->getContext(), "Check", func); |
|||
auto resizeBB = llvm::BasicBlock::Create(func->getContext(), "Resize", func); |
|||
auto returnBB = llvm::BasicBlock::Create(func->getContext(), "Return", func); |
|||
|
|||
InsertPointGuard guard(m_builder); // Restores insert point at function exit
|
|||
|
|||
// BB "Pre": Ignore checks with size 0
|
|||
m_builder.SetInsertPoint(preBB); |
|||
m_builder.CreateCondBr(m_builder.CreateICmpNE(blkSize, Constant::get(0)), checkBB, returnBB, Type::expectTrue); |
|||
|
|||
// BB "Check"
|
|||
m_builder.SetInsertPoint(checkBB); |
|||
static const auto c_inputMax = uint64_t(1) << 33; // max value of blkSize and blkOffset that will not result in integer overflow in calculations below
|
|||
auto blkOffsetOk = m_builder.CreateICmpULE(blkOffset, Constant::get(c_inputMax), "blkOffsetOk"); |
|||
auto blkO = m_builder.CreateSelect(blkOffsetOk, m_builder.CreateTrunc(blkOffset, Type::Size), m_builder.getInt64(c_inputMax), "bklO"); |
|||
auto blkSizeOk = m_builder.CreateICmpULE(blkSize, Constant::get(c_inputMax), "blkSizeOk"); |
|||
auto blkS = m_builder.CreateSelect(blkSizeOk, m_builder.CreateTrunc(blkSize, Type::Size), m_builder.getInt64(c_inputMax), "bklS"); |
|||
|
|||
auto sizeReq0 = m_builder.CreateNUWAdd(blkO, blkS, "sizeReq0"); |
|||
auto sizeReq = m_builder.CreateAnd(m_builder.CreateNUWAdd(sizeReq0, m_builder.getInt64(31)), uint64_t(-1) << 5, "sizeReq"); // s' = ((s0 + 31) / 32) * 32
|
|||
auto sizeCur = m_memory.size(mem); |
|||
auto sizeOk = m_builder.CreateICmpULE(sizeReq, sizeCur, "sizeOk"); |
|||
|
|||
m_builder.CreateCondBr(sizeOk, returnBB, resizeBB, Type::expectTrue); |
|||
|
|||
// BB "Resize"
|
|||
m_builder.SetInsertPoint(resizeBB); |
|||
// Check gas first
|
|||
auto w1 = m_builder.CreateLShr(sizeReq, 5); |
|||
auto w1s = m_builder.CreateNUWMul(w1, w1); |
|||
auto c1 = m_builder.CreateAdd(m_builder.CreateNUWMul(w1, m_builder.getInt64(3)), m_builder.CreateLShr(w1s, 9)); |
|||
auto w0 = m_builder.CreateLShr(sizeCur, 5); |
|||
auto w0s = m_builder.CreateNUWMul(w0, w0); |
|||
auto c0 = m_builder.CreateAdd(m_builder.CreateNUWMul(w0, m_builder.getInt64(3)), m_builder.CreateLShr(w0s, 9)); |
|||
auto cc = m_builder.CreateNUWSub(c1, c0); |
|||
auto costOk = m_builder.CreateAnd(blkOffsetOk, blkSizeOk, "costOk"); |
|||
auto c = m_builder.CreateSelect(costOk, cc, m_builder.getInt64(std::numeric_limits<int64_t>::max()), "c"); |
|||
m_gasMeter.count(c, jmpBuf, gas); |
|||
// Resize
|
|||
m_memory.extend(mem, sizeReq); |
|||
m_builder.CreateBr(returnBB); |
|||
|
|||
// BB "Return"
|
|||
m_builder.SetInsertPoint(returnBB); |
|||
m_builder.CreateRetVoid(); |
|||
} |
|||
return func; |
|||
} |
|||
|
|||
llvm::Function* Memory::createFunc(bool _isStore, llvm::Type* _valueType) |
|||
{ |
|||
auto isWord = _valueType == Type::Word; |
|||
|
|||
llvm::Type* storeArgs[] = {Array::getType()->getPointerTo(), Type::Word, _valueType}; |
|||
llvm::Type* loadArgs[] = {Array::getType()->getPointerTo(), Type::Word}; |
|||
auto name = _isStore ? isWord ? "mstore" : "mstore8" : "mload"; |
|||
auto funcType = _isStore ? llvm::FunctionType::get(Type::Void, storeArgs, false) : llvm::FunctionType::get(Type::Word, loadArgs, false); |
|||
auto func = llvm::Function::Create(funcType, llvm::Function::PrivateLinkage, name, getModule()); |
|||
|
|||
InsertPointGuard guard(m_builder); // Restores insert point at function exit
|
|||
|
|||
m_builder.SetInsertPoint(llvm::BasicBlock::Create(func->getContext(), {}, func)); |
|||
auto mem = &func->getArgumentList().front(); |
|||
mem->setName("mem"); |
|||
auto index = mem->getNextNode(); |
|||
index->setName("index"); |
|||
|
|||
if (_isStore) |
|||
{ |
|||
auto valueArg = index->getNextNode(); |
|||
valueArg->setName("value"); |
|||
auto value = isWord ? Endianness::toBE(m_builder, valueArg) : valueArg; |
|||
auto memPtr = m_memory.getPtr(mem, m_builder.CreateTrunc(index, Type::Size)); |
|||
auto valuePtr = m_builder.CreateBitCast(memPtr, _valueType->getPointerTo(), "valuePtr"); |
|||
m_builder.CreateStore(value, valuePtr); |
|||
m_builder.CreateRetVoid(); |
|||
} |
|||
else |
|||
{ |
|||
auto memPtr = m_memory.getPtr(mem, m_builder.CreateTrunc(index, Type::Size)); |
|||
llvm::Value* ret = m_builder.CreateLoad(memPtr); |
|||
ret = Endianness::toNative(m_builder, ret); |
|||
m_builder.CreateRet(ret); |
|||
} |
|||
|
|||
return func; |
|||
} |
|||
|
|||
llvm::Function* Memory::getLoadWordFunc() |
|||
{ |
|||
auto& func = m_loadWord; |
|||
if (!func) |
|||
func = createFunc(false, Type::Word); |
|||
return func; |
|||
} |
|||
|
|||
llvm::Function* Memory::getStoreWordFunc() |
|||
{ |
|||
auto& func = m_storeWord; |
|||
if (!func) |
|||
func = createFunc(true, Type::Word); |
|||
return func; |
|||
} |
|||
|
|||
llvm::Function* Memory::getStoreByteFunc() |
|||
{ |
|||
auto& func = m_storeByte; |
|||
if (!func) |
|||
func = createFunc(true, Type::Byte); |
|||
return func; |
|||
} |
|||
|
|||
|
|||
llvm::Value* Memory::loadWord(llvm::Value* _addr) |
|||
{ |
|||
require(_addr, Constant::get(Type::Word->getPrimitiveSizeInBits() / 8)); |
|||
return createCall(getLoadWordFunc(), {getRuntimeManager().getMem(), _addr}); |
|||
} |
|||
|
|||
void Memory::storeWord(llvm::Value* _addr, llvm::Value* _word) |
|||
{ |
|||
require(_addr, Constant::get(Type::Word->getPrimitiveSizeInBits() / 8)); |
|||
createCall(getStoreWordFunc(), {getRuntimeManager().getMem(), _addr, _word}); |
|||
} |
|||
|
|||
void Memory::storeByte(llvm::Value* _addr, llvm::Value* _word) |
|||
{ |
|||
require(_addr, Constant::get(Type::Byte->getPrimitiveSizeInBits() / 8)); |
|||
auto byte = m_builder.CreateTrunc(_word, Type::Byte, "byte"); |
|||
createCall(getStoreByteFunc(), {getRuntimeManager().getMem(), _addr, byte}); |
|||
} |
|||
|
|||
llvm::Value* Memory::getData() |
|||
{ |
|||
auto memPtr = m_builder.CreateBitCast(getRuntimeManager().getMem(), Type::BytePtr->getPointerTo()); |
|||
auto data = m_builder.CreateLoad(memPtr, "data"); |
|||
assert(data->getType() == Type::BytePtr); |
|||
return data; |
|||
} |
|||
|
|||
llvm::Value* Memory::getSize() |
|||
{ |
|||
return m_builder.CreateZExt(m_memory.size(), Type::Word, "msize"); // TODO: Allow placing i64 on stack
|
|||
} |
|||
|
|||
llvm::Value* Memory::getBytePtr(llvm::Value* _index) |
|||
{ |
|||
return m_builder.CreateGEP(getData(), _index, "ptr"); |
|||
} |
|||
|
|||
void Memory::require(llvm::Value* _offset, llvm::Value* _size) |
|||
{ |
|||
if (auto constant = llvm::dyn_cast<llvm::ConstantInt>(_size)) |
|||
{ |
|||
if (!constant->getValue()) |
|||
return; |
|||
} |
|||
createCall(getRequireFunc(), {getRuntimeManager().getMem(), _offset, _size, getRuntimeManager().getJmpBuf(), getRuntimeManager().getGasPtr()}); |
|||
} |
|||
|
|||
void Memory::copyBytes(llvm::Value* _srcPtr, llvm::Value* _srcSize, llvm::Value* _srcIdx, |
|||
llvm::Value* _destMemIdx, llvm::Value* _reqBytes) |
|||
{ |
|||
require(_destMemIdx, _reqBytes); |
|||
|
|||
// Additional copy cost
|
|||
// TODO: This round ups to 32 happens in many places
|
|||
auto reqBytes = m_builder.CreateTrunc(_reqBytes, Type::Gas); |
|||
auto copyWords = m_builder.CreateUDiv(m_builder.CreateNUWAdd(reqBytes, m_builder.getInt64(31)), m_builder.getInt64(32)); |
|||
m_gasMeter.countCopy(copyWords); |
|||
|
|||
// Algorithm:
|
|||
// isOutsideData = idx256 >= size256
|
|||
// idx64 = trunc idx256
|
|||
// size64 = trunc size256
|
|||
// dataLeftSize = size64 - idx64 // safe if not isOutsideData
|
|||
// reqBytes64 = trunc _reqBytes // require() handles large values
|
|||
// bytesToCopy0 = select(reqBytes64 > dataLeftSize, dataSizeLeft, reqBytes64) // min
|
|||
// bytesToCopy = select(isOutsideData, 0, bytesToCopy0)
|
|||
|
|||
auto isOutsideData = m_builder.CreateICmpUGE(_srcIdx, _srcSize); |
|||
auto idx64 = m_builder.CreateTrunc(_srcIdx, Type::Size); |
|||
auto size64 = m_builder.CreateTrunc(_srcSize, Type::Size); |
|||
auto dataLeftSize = m_builder.CreateNUWSub(size64, idx64); |
|||
auto outOfBound = m_builder.CreateICmpUGT(reqBytes, dataLeftSize); |
|||
auto bytesToCopyInner = m_builder.CreateSelect(outOfBound, dataLeftSize, reqBytes); |
|||
auto bytesToCopy = m_builder.CreateSelect(isOutsideData, m_builder.getInt64(0), bytesToCopyInner, "bytesToCopy"); |
|||
auto bytesToZero = m_builder.CreateNUWSub(reqBytes, bytesToCopy, "bytesToZero"); |
|||
|
|||
auto src = m_builder.CreateGEP(_srcPtr, idx64, "src"); |
|||
auto dstIdx = m_builder.CreateTrunc(_destMemIdx, Type::Size, "dstIdx"); |
|||
auto padIdx = m_builder.CreateNUWAdd(dstIdx, bytesToCopy, "padIdx"); |
|||
auto dst = m_memory.getPtr(getRuntimeManager().getMem(), dstIdx); |
|||
auto pad = m_memory.getPtr(getRuntimeManager().getMem(), padIdx); |
|||
m_builder.CreateMemCpy(dst, src, bytesToCopy, 0); |
|||
m_builder.CreateMemSet(pad, m_builder.getInt8(0), bytesToZero, 0); |
|||
} |
|||
|
|||
} |
|||
} |
|||
} |
@ -1,51 +0,0 @@ |
|||
#pragma once |
|||
|
|||
#include "Array.h" |
|||
|
|||
namespace dev |
|||
{ |
|||
namespace eth |
|||
{ |
|||
namespace jit |
|||
{ |
|||
class GasMeter; |
|||
|
|||
class Memory : public RuntimeHelper |
|||
{ |
|||
public: |
|||
Memory(RuntimeManager& _runtimeManager, GasMeter& _gasMeter); |
|||
|
|||
llvm::Value* loadWord(llvm::Value* _addr); |
|||
void storeWord(llvm::Value* _addr, llvm::Value* _word); |
|||
void storeByte(llvm::Value* _addr, llvm::Value* _byte); |
|||
llvm::Value* getData(); |
|||
llvm::Value* getSize(); |
|||
llvm::Value* getBytePtr(llvm::Value* _index); |
|||
void copyBytes(llvm::Value* _srcPtr, llvm::Value* _srcSize, llvm::Value* _srcIndex, |
|||
llvm::Value* _destMemIdx, llvm::Value* _byteCount); |
|||
|
|||
/// Requires the amount of memory to for data defined by offset and size. And counts gas fee for that memory.
|
|||
void require(llvm::Value* _offset, llvm::Value* _size); |
|||
|
|||
private: |
|||
Array m_memory; |
|||
|
|||
GasMeter& m_gasMeter; |
|||
|
|||
llvm::Function* createFunc(bool _isStore, llvm::Type* _type); |
|||
|
|||
llvm::Function* getRequireFunc(); |
|||
llvm::Function* getLoadWordFunc(); |
|||
llvm::Function* getStoreWordFunc(); |
|||
llvm::Function* getStoreByteFunc(); |
|||
|
|||
llvm::Function* m_require = nullptr; |
|||
llvm::Function* m_loadWord = nullptr; |
|||
llvm::Function* m_storeWord = nullptr; |
|||
llvm::Function* m_storeByte = nullptr; |
|||
}; |
|||
|
|||
} |
|||
} |
|||
} |
|||
|
@ -1,125 +0,0 @@ |
|||
#include "Optimizer.h" |
|||
|
|||
#include "preprocessor/llvm_includes_start.h" |
|||
#include <llvm/IR/BasicBlock.h> |
|||
#include <llvm/IR/Function.h> |
|||
#include <llvm/IR/LegacyPassManager.h> |
|||
#include <llvm/Transforms/Scalar.h> |
|||
#include <llvm/Transforms/IPO.h> |
|||
#include "preprocessor/llvm_includes_end.h" |
|||
|
|||
#include "Arith256.h" |
|||
#include "Type.h" |
|||
|
|||
namespace dev |
|||
{ |
|||
namespace eth |
|||
{ |
|||
namespace jit |
|||
{ |
|||
|
|||
bool optimize(llvm::Module& _module) |
|||
{ |
|||
auto pm = llvm::legacy::PassManager{}; |
|||
pm.add(llvm::createFunctionInliningPass(2, 2)); |
|||
pm.add(llvm::createCFGSimplificationPass()); |
|||
pm.add(llvm::createInstructionCombiningPass()); |
|||
pm.add(llvm::createAggressiveDCEPass()); |
|||
pm.add(llvm::createLowerSwitchPass()); |
|||
return pm.run(_module); |
|||
} |
|||
|
|||
namespace |
|||
{ |
|||
|
|||
class LowerEVMPass: public llvm::BasicBlockPass |
|||
{ |
|||
static char ID; |
|||
|
|||
public: |
|||
LowerEVMPass(): |
|||
llvm::BasicBlockPass(ID) |
|||
{} |
|||
|
|||
virtual bool runOnBasicBlock(llvm::BasicBlock& _bb) override; |
|||
|
|||
using llvm::BasicBlockPass::doFinalization; |
|||
virtual bool doFinalization(llvm::Module& _module) override; |
|||
}; |
|||
|
|||
char LowerEVMPass::ID = 0; |
|||
|
|||
bool LowerEVMPass::runOnBasicBlock(llvm::BasicBlock& _bb) |
|||
{ |
|||
auto modified = false; |
|||
auto module = _bb.getParent()->getParent(); |
|||
auto i512Ty = llvm::IntegerType::get(_bb.getContext(), 512); |
|||
for (auto it = _bb.begin(); it != _bb.end(); ) |
|||
{ |
|||
auto& inst = *it++; |
|||
llvm::Function* func = nullptr; |
|||
if (inst.getType() == Type::Word) |
|||
{ |
|||
switch (inst.getOpcode()) |
|||
{ |
|||
case llvm::Instruction::Mul: |
|||
func = Arith256::getMulFunc(*module); |
|||
break; |
|||
|
|||
case llvm::Instruction::UDiv: |
|||
func = Arith256::getUDiv256Func(*module); |
|||
break; |
|||
|
|||
case llvm::Instruction::URem: |
|||
func = Arith256::getURem256Func(*module); |
|||
break; |
|||
|
|||
case llvm::Instruction::SDiv: |
|||
func = Arith256::getSDiv256Func(*module); |
|||
break; |
|||
|
|||
case llvm::Instruction::SRem: |
|||
func = Arith256::getSRem256Func(*module); |
|||
break; |
|||
} |
|||
} |
|||
else if (inst.getType() == i512Ty) |
|||
{ |
|||
switch (inst.getOpcode()) |
|||
{ |
|||
case llvm::Instruction::URem: |
|||
func = Arith256::getURem512Func(*module); |
|||
break; |
|||
} |
|||
} |
|||
|
|||
if (func) |
|||
{ |
|||
auto call = llvm::CallInst::Create(func, {inst.getOperand(0), inst.getOperand(1)}, "", &inst); |
|||
inst.replaceAllUsesWith(call); |
|||
inst.eraseFromParent(); |
|||
modified = true; |
|||
} |
|||
} |
|||
return modified; |
|||
} |
|||
|
|||
bool LowerEVMPass::doFinalization(llvm::Module&) |
|||
{ |
|||
return false; |
|||
} |
|||
|
|||
} |
|||
|
|||
bool prepare(llvm::Module& _module) |
|||
{ |
|||
auto pm = llvm::legacy::PassManager{}; |
|||
pm.add(llvm::createCFGSimplificationPass()); |
|||
pm.add(llvm::createDeadCodeEliminationPass()); |
|||
pm.add(new LowerEVMPass{}); |
|||
return pm.run(_module); |
|||
} |
|||
|
|||
} |
|||
} |
|||
} |
@ -1,21 +0,0 @@ |
|||
#pragma once |
|||
|
|||
namespace llvm |
|||
{ |
|||
class Module; |
|||
} |
|||
|
|||
namespace dev |
|||
{ |
|||
namespace eth |
|||
{ |
|||
namespace jit |
|||
{ |
|||
|
|||
bool optimize(llvm::Module& _module); |
|||
|
|||
bool prepare(llvm::Module& _module); |
|||
|
|||
} |
|||
} |
|||
} |
@ -1,302 +0,0 @@ |
|||
#include "RuntimeManager.h" |
|||
|
|||
#include "preprocessor/llvm_includes_start.h" |
|||
#include <llvm/IR/IntrinsicInst.h> |
|||
#include "preprocessor/llvm_includes_end.h" |
|||
|
|||
#include "Stack.h" |
|||
#include "Utils.h" |
|||
|
|||
namespace dev |
|||
{ |
|||
namespace eth |
|||
{ |
|||
namespace jit |
|||
{ |
|||
|
|||
llvm::StructType* RuntimeManager::getRuntimeDataType() |
|||
{ |
|||
static llvm::StructType* type = nullptr; |
|||
if (!type) |
|||
{ |
|||
llvm::Type* elems[] = |
|||
{ |
|||
Type::Size, // gas
|
|||
Type::Size, // gasPrice
|
|||
Type::BytePtr, // callData
|
|||
Type::Size, // callDataSize
|
|||
Type::Word, // address
|
|||
Type::Word, // caller
|
|||
Type::Word, // origin
|
|||
Type::Word, // callValue
|
|||
Type::Word, // coinBase
|
|||
Type::Word, // difficulty
|
|||
Type::Word, // gasLimit
|
|||
Type::Size, // blockNumber
|
|||
Type::Size, // blockTimestamp
|
|||
Type::BytePtr, // code
|
|||
Type::Size, // codeSize
|
|||
}; |
|||
type = llvm::StructType::create(elems, "RuntimeData"); |
|||
} |
|||
return type; |
|||
} |
|||
|
|||
llvm::StructType* RuntimeManager::getRuntimeType() |
|||
{ |
|||
static llvm::StructType* type = nullptr; |
|||
if (!type) |
|||
{ |
|||
llvm::Type* elems[] = |
|||
{ |
|||
Type::RuntimeDataPtr, // data
|
|||
Type::EnvPtr, // Env*
|
|||
Array::getType() // memory
|
|||
}; |
|||
type = llvm::StructType::create(elems, "Runtime"); |
|||
} |
|||
return type; |
|||
} |
|||
|
|||
namespace |
|||
{ |
|||
llvm::Twine getName(RuntimeData::Index _index) |
|||
{ |
|||
switch (_index) |
|||
{ |
|||
default: return ""; |
|||
case RuntimeData::Gas: return "msg.gas"; |
|||
case RuntimeData::GasPrice: return "tx.gasprice"; |
|||
case RuntimeData::CallData: return "msg.data.ptr"; |
|||
case RuntimeData::CallDataSize: return "msg.data.size"; |
|||
case RuntimeData::Address: return "this.address"; |
|||
case RuntimeData::Caller: return "msg.caller"; |
|||
case RuntimeData::Origin: return "tx.origin"; |
|||
case RuntimeData::CallValue: return "msg.value"; |
|||
case RuntimeData::CoinBase: return "block.coinbase"; |
|||
case RuntimeData::Difficulty: return "block.difficulty"; |
|||
case RuntimeData::GasLimit: return "block.gaslimit"; |
|||
case RuntimeData::Number: return "block.number"; |
|||
case RuntimeData::Timestamp: return "block.timestamp"; |
|||
case RuntimeData::Code: return "code.ptr"; |
|||
case RuntimeData::CodeSize: return "code.size"; |
|||
} |
|||
} |
|||
} |
|||
|
|||
RuntimeManager::RuntimeManager(llvm::IRBuilder<>& _builder, code_iterator _codeBegin, code_iterator _codeEnd): |
|||
CompilerHelper(_builder), |
|||
m_codeBegin(_codeBegin), |
|||
m_codeEnd(_codeEnd) |
|||
{ |
|||
m_longjmp = llvm::Intrinsic::getDeclaration(getModule(), llvm::Intrinsic::eh_sjlj_longjmp); |
|||
|
|||
// Unpack data
|
|||
auto rtPtr = getRuntimePtr(); |
|||
m_dataPtr = m_builder.CreateLoad(m_builder.CreateStructGEP(getRuntimeType(), rtPtr, 0), "dataPtr"); |
|||
assert(m_dataPtr->getType() == Type::RuntimeDataPtr); |
|||
m_memPtr = m_builder.CreateStructGEP(getRuntimeType(), rtPtr, 2, "mem"); |
|||
assert(m_memPtr->getType() == Array::getType()->getPointerTo()); |
|||
m_envPtr = m_builder.CreateLoad(m_builder.CreateStructGEP(getRuntimeType(), rtPtr, 1), "env"); |
|||
assert(m_envPtr->getType() == Type::EnvPtr); |
|||
|
|||
m_stackSize = m_builder.CreateAlloca(Type::Size, nullptr, "stackSize"); |
|||
m_builder.CreateStore(m_builder.getInt64(0), m_stackSize); |
|||
|
|||
auto data = m_builder.CreateLoad(m_dataPtr, "data"); |
|||
for (unsigned i = 0; i < m_dataElts.size(); ++i) |
|||
m_dataElts[i] = m_builder.CreateExtractValue(data, i, getName(RuntimeData::Index(i))); |
|||
|
|||
m_gasPtr = m_builder.CreateAlloca(Type::Gas, nullptr, "gas.ptr"); |
|||
m_builder.CreateStore(m_dataElts[RuntimeData::Index::Gas], m_gasPtr); |
|||
|
|||
llvm::Type* checkStackLimitArgs[] = {Type::Size->getPointerTo(), Type::Size, Type::Size, Type::Size, Type::BytePtr}; |
|||
m_checkStackLimit = llvm::Function::Create(llvm::FunctionType::get(Type::Void, checkStackLimitArgs, false), llvm::Function::PrivateLinkage, "evm.stack.require", getModule()); |
|||
m_checkStackLimit->setDoesNotThrow(); |
|||
m_checkStackLimit->setDoesNotCapture(1); |
|||
|
|||
auto checkBB = llvm::BasicBlock::Create(_builder.getContext(), "Check", m_checkStackLimit); |
|||
auto updateBB = llvm::BasicBlock::Create(_builder.getContext(), "Update", m_checkStackLimit); |
|||
auto outOfStackBB = llvm::BasicBlock::Create(_builder.getContext(), "OutOfStack", m_checkStackLimit); |
|||
|
|||
auto currSizePtr = &m_checkStackLimit->getArgumentList().front(); |
|||
currSizePtr->setName("currSize"); |
|||
auto min = currSizePtr->getNextNode(); |
|||
min->setName("min"); |
|||
auto max = min->getNextNode(); |
|||
max->setName("max"); |
|||
auto diff = max->getNextNode(); |
|||
diff->setName("diff"); |
|||
auto jmpBuf = diff->getNextNode(); |
|||
jmpBuf->setName("jmpBuf"); |
|||
|
|||
InsertPointGuard guard{m_builder}; |
|||
m_builder.SetInsertPoint(checkBB); |
|||
auto currSize = m_builder.CreateLoad(currSizePtr, "cur"); |
|||
auto minSize = m_builder.CreateAdd(currSize, min, "minSize", false, true); |
|||
auto maxSize = m_builder.CreateAdd(currSize, max, "maxSize", true, true); |
|||
auto minOk = m_builder.CreateICmpSGE(minSize, m_builder.getInt64(0), "min.ok"); |
|||
auto maxOk = m_builder.CreateICmpULE(maxSize, m_builder.getInt64(1024), "max.ok"); |
|||
auto ok = m_builder.CreateAnd(minOk, maxOk, "ok"); |
|||
m_builder.CreateCondBr(ok, updateBB, outOfStackBB, Type::expectTrue); |
|||
|
|||
m_builder.SetInsertPoint(updateBB); |
|||
auto newSize = m_builder.CreateNSWAdd(currSize, diff); |
|||
m_builder.CreateStore(newSize, currSizePtr); |
|||
m_builder.CreateRetVoid(); |
|||
|
|||
m_builder.SetInsertPoint(outOfStackBB); |
|||
abort(jmpBuf); |
|||
m_builder.CreateUnreachable(); |
|||
} |
|||
|
|||
void RuntimeManager::checkStackLimit(ssize_t _min, ssize_t _max, ssize_t _diff) |
|||
{ |
|||
createCall(m_checkStackLimit, {m_stackSize, m_builder.getInt64(_min), m_builder.getInt64(_max), m_builder.getInt64(_diff), getJmpBuf()}); |
|||
} |
|||
|
|||
llvm::Value* RuntimeManager::getRuntimePtr() |
|||
{ |
|||
// Expect first argument of a function to be a pointer to Runtime
|
|||
auto func = m_builder.GetInsertBlock()->getParent(); |
|||
auto rtPtr = &func->getArgumentList().front(); |
|||
assert(rtPtr->getType() == Type::RuntimePtr); |
|||
return rtPtr; |
|||
} |
|||
|
|||
llvm::Value* RuntimeManager::getDataPtr() |
|||
{ |
|||
if (getMainFunction()) |
|||
return m_dataPtr; |
|||
|
|||
auto rtPtr = getRuntimePtr(); |
|||
auto dataPtr = m_builder.CreateLoad(m_builder.CreateStructGEP(getRuntimeType(), rtPtr, 0), "data"); |
|||
assert(dataPtr->getType() == getRuntimeDataType()->getPointerTo()); |
|||
return dataPtr; |
|||
} |
|||
|
|||
llvm::Value* RuntimeManager::getEnvPtr() |
|||
{ |
|||
assert(getMainFunction()); // Available only in main function
|
|||
return m_envPtr; |
|||
} |
|||
|
|||
llvm::Value* RuntimeManager::getPtr(RuntimeData::Index _index) |
|||
{ |
|||
auto ptr = getBuilder().CreateStructGEP(getRuntimeDataType(), getDataPtr(), _index); |
|||
assert(getRuntimeDataType()->getElementType(_index)->getPointerTo() == ptr->getType()); |
|||
return ptr; |
|||
} |
|||
|
|||
llvm::Value* RuntimeManager::get(RuntimeData::Index _index) |
|||
{ |
|||
return m_dataElts[_index]; |
|||
} |
|||
|
|||
void RuntimeManager::set(RuntimeData::Index _index, llvm::Value* _value) |
|||
{ |
|||
auto ptr = getPtr(_index); |
|||
assert(ptr->getType() == _value->getType()->getPointerTo()); |
|||
getBuilder().CreateStore(_value, ptr); |
|||
} |
|||
|
|||
void RuntimeManager::registerReturnData(llvm::Value* _offset, llvm::Value* _size) |
|||
{ |
|||
auto memPtr = m_builder.CreateBitCast(getMem(), Type::BytePtr->getPointerTo()); |
|||
auto mem = getBuilder().CreateLoad(memPtr, "memory"); |
|||
auto returnDataPtr = getBuilder().CreateGEP(mem, _offset); |
|||
set(RuntimeData::ReturnData, returnDataPtr); |
|||
|
|||
auto size64 = getBuilder().CreateTrunc(_size, Type::Size); |
|||
set(RuntimeData::ReturnDataSize, size64); |
|||
} |
|||
|
|||
void RuntimeManager::registerSuicide(llvm::Value* _balanceAddress) |
|||
{ |
|||
set(RuntimeData::SuicideDestAddress, _balanceAddress); |
|||
} |
|||
|
|||
void RuntimeManager::exit(ReturnCode _returnCode) |
|||
{ |
|||
if (m_stack) |
|||
m_stack->free(); |
|||
|
|||
auto extGasPtr = m_builder.CreateStructGEP(getRuntimeDataType(), getDataPtr(), RuntimeData::Index::Gas, "msg.gas.ptr"); |
|||
m_builder.CreateStore(getGas(), extGasPtr); |
|||
m_builder.CreateRet(Constant::get(_returnCode)); |
|||
} |
|||
|
|||
void RuntimeManager::abort(llvm::Value* _jmpBuf) |
|||
{ |
|||
auto longjmp = llvm::Intrinsic::getDeclaration(getModule(), llvm::Intrinsic::eh_sjlj_longjmp); |
|||
createCall(longjmp, {_jmpBuf}); |
|||
} |
|||
|
|||
llvm::Value* RuntimeManager::get(Instruction _inst) |
|||
{ |
|||
switch (_inst) |
|||
{ |
|||
default: assert(false); return nullptr; |
|||
case Instruction::ADDRESS: return get(RuntimeData::Address); |
|||
case Instruction::CALLER: return get(RuntimeData::Caller); |
|||
case Instruction::ORIGIN: return get(RuntimeData::Origin); |
|||
case Instruction::CALLVALUE: return get(RuntimeData::CallValue); |
|||
case Instruction::GASPRICE: return get(RuntimeData::GasPrice); |
|||
case Instruction::COINBASE: return get(RuntimeData::CoinBase); |
|||
case Instruction::DIFFICULTY: return get(RuntimeData::Difficulty); |
|||
case Instruction::GASLIMIT: return get(RuntimeData::GasLimit); |
|||
case Instruction::NUMBER: return get(RuntimeData::Number); |
|||
case Instruction::TIMESTAMP: return get(RuntimeData::Timestamp); |
|||
} |
|||
} |
|||
|
|||
llvm::Value* RuntimeManager::getCallData() |
|||
{ |
|||
return get(RuntimeData::CallData); |
|||
} |
|||
|
|||
llvm::Value* RuntimeManager::getCode() |
|||
{ |
|||
// OPT Check what is faster
|
|||
//return get(RuntimeData::Code);
|
|||
return m_builder.CreateGlobalStringPtr({reinterpret_cast<char const*>(m_codeBegin), static_cast<size_t>(m_codeEnd - m_codeBegin)}, "code"); |
|||
} |
|||
|
|||
llvm::Value* RuntimeManager::getCodeSize() |
|||
{ |
|||
return Constant::get(m_codeEnd - m_codeBegin); |
|||
} |
|||
|
|||
llvm::Value* RuntimeManager::getCallDataSize() |
|||
{ |
|||
auto value = get(RuntimeData::CallDataSize); |
|||
assert(value->getType() == Type::Size); |
|||
return getBuilder().CreateZExt(value, Type::Word); |
|||
} |
|||
|
|||
llvm::Value* RuntimeManager::getGas() |
|||
{ |
|||
return getBuilder().CreateLoad(getGasPtr(), "gas"); |
|||
} |
|||
|
|||
llvm::Value* RuntimeManager::getGasPtr() |
|||
{ |
|||
assert(getMainFunction()); |
|||
return m_gasPtr; |
|||
} |
|||
|
|||
llvm::Value* RuntimeManager::getMem() |
|||
{ |
|||
assert(getMainFunction()); |
|||
return m_memPtr; |
|||
} |
|||
|
|||
void RuntimeManager::setGas(llvm::Value* _gas) |
|||
{ |
|||
assert(_gas->getType() == Type::Gas); |
|||
getBuilder().CreateStore(_gas, getGasPtr()); |
|||
} |
|||
|
|||
} |
|||
} |
|||
} |
@ -1,79 +0,0 @@ |
|||
#pragma once |
|||
|
|||
#include <array> |
|||
|
|||
#include "CompilerHelper.h" |
|||
#include "Type.h" |
|||
#include "Instruction.h" |
|||
|
|||
namespace dev |
|||
{ |
|||
namespace eth |
|||
{ |
|||
namespace jit |
|||
{ |
|||
using namespace evmjit; |
|||
class Stack; |
|||
|
|||
class RuntimeManager: public CompilerHelper |
|||
{ |
|||
public: |
|||
RuntimeManager(llvm::IRBuilder<>& _builder, code_iterator _codeBegin, code_iterator _codeEnd); |
|||
|
|||
llvm::Value* getRuntimePtr(); |
|||
llvm::Value* getDataPtr(); |
|||
llvm::Value* getEnvPtr(); |
|||
|
|||
llvm::Value* get(RuntimeData::Index _index); |
|||
llvm::Value* get(Instruction _inst); |
|||
llvm::Value* getGas(); |
|||
llvm::Value* getGasPtr(); |
|||
llvm::Value* getCallData(); |
|||
llvm::Value* getCode(); |
|||
llvm::Value* getCodeSize(); |
|||
llvm::Value* getCallDataSize(); |
|||
llvm::Value* getJmpBuf() { return m_jmpBuf; } |
|||
void setGas(llvm::Value* _gas); |
|||
|
|||
llvm::Value* getMem(); |
|||
|
|||
void registerReturnData(llvm::Value* _index, llvm::Value* _size); // TODO: Move to Memory.
|
|||
void registerSuicide(llvm::Value* _balanceAddress); |
|||
|
|||
void exit(ReturnCode _returnCode); |
|||
|
|||
void abort(llvm::Value* _jmpBuf); |
|||
|
|||
void setStack(Stack& _stack) { m_stack = &_stack; } |
|||
void setJmpBuf(llvm::Value* _jmpBuf) { m_jmpBuf = _jmpBuf; } |
|||
|
|||
static llvm::StructType* getRuntimeType(); |
|||
static llvm::StructType* getRuntimeDataType(); |
|||
|
|||
void checkStackLimit(ssize_t _min, ssize_t _max, ssize_t _diff); |
|||
|
|||
private: |
|||
llvm::Value* getPtr(RuntimeData::Index _index); |
|||
void set(RuntimeData::Index _index, llvm::Value* _value); |
|||
|
|||
llvm::Function* m_longjmp = nullptr; |
|||
llvm::Value* m_jmpBuf = nullptr; |
|||
llvm::Value* m_dataPtr = nullptr; |
|||
llvm::Value* m_gasPtr = nullptr; |
|||
llvm::Value* m_memPtr = nullptr; |
|||
llvm::Value* m_envPtr = nullptr; |
|||
|
|||
std::array<llvm::Value*, RuntimeData::numElements> m_dataElts; |
|||
|
|||
llvm::Value* m_stackSize = nullptr; |
|||
llvm::Function* m_checkStackLimit = nullptr; |
|||
|
|||
code_iterator m_codeBegin = {}; |
|||
code_iterator m_codeEnd = {}; |
|||
|
|||
Stack* m_stack = nullptr; |
|||
}; |
|||
|
|||
} |
|||
} |
|||
} |
@ -1,44 +0,0 @@ |
|||
#include "Stack.h" |
|||
|
|||
#include "preprocessor/llvm_includes_start.h" |
|||
#include <llvm/IR/Function.h> |
|||
#include "preprocessor/llvm_includes_end.h" |
|||
|
|||
#include "RuntimeManager.h" |
|||
#include "Utils.h" |
|||
|
|||
namespace dev |
|||
{ |
|||
namespace eth |
|||
{ |
|||
namespace jit |
|||
{ |
|||
|
|||
Stack::Stack(llvm::IRBuilder<>& _builder): |
|||
CompilerHelper(_builder), |
|||
m_stack(_builder, "stack") |
|||
{} |
|||
|
|||
llvm::Value* Stack::get(size_t _index) |
|||
{ |
|||
return m_stack.get(m_builder.CreateSub(m_stack.size(), m_builder.getInt64(_index + 1))); |
|||
} |
|||
|
|||
void Stack::set(size_t _index, llvm::Value* _value) |
|||
{ |
|||
m_stack.set(m_builder.CreateSub(m_stack.size(), m_builder.getInt64(_index + 1)), _value); |
|||
} |
|||
|
|||
void Stack::pop(size_t _count) |
|||
{ |
|||
m_stack.pop(m_builder.getInt64(_count)); |
|||
} |
|||
|
|||
void Stack::push(llvm::Value* _value) |
|||
{ |
|||
m_stack.push(_value); |
|||
} |
|||
|
|||
} |
|||
} |
|||
} |
@ -1,30 +0,0 @@ |
|||
#pragma once |
|||
|
|||
#include "Array.h" |
|||
|
|||
namespace dev |
|||
{ |
|||
namespace eth |
|||
{ |
|||
namespace jit |
|||
{ |
|||
|
|||
class Stack: public CompilerHelper |
|||
{ |
|||
public: |
|||
Stack(llvm::IRBuilder<>& builder); |
|||
|
|||
llvm::Value* get(size_t _index); |
|||
void set(size_t _index, llvm::Value* _value); |
|||
void pop(size_t _count); |
|||
void push(llvm::Value* _value); |
|||
void free() { m_stack.free(); } |
|||
|
|||
private: |
|||
Array m_stack; |
|||
}; |
|||
|
|||
|
|||
} |
|||
} |
|||
} |
@ -1,73 +0,0 @@ |
|||
#include "Type.h" |
|||
|
|||
#include <llvm/IR/MDBuilder.h> |
|||
|
|||
#include "RuntimeManager.h" |
|||
|
|||
namespace dev |
|||
{ |
|||
namespace eth |
|||
{ |
|||
namespace jit |
|||
{ |
|||
|
|||
llvm::IntegerType* Type::Word; |
|||
llvm::PointerType* Type::WordPtr; |
|||
llvm::IntegerType* Type::Bool; |
|||
llvm::IntegerType* Type::Size; |
|||
llvm::IntegerType* Type::Gas; |
|||
llvm::PointerType* Type::GasPtr; |
|||
llvm::IntegerType* Type::Byte; |
|||
llvm::PointerType* Type::BytePtr; |
|||
llvm::Type* Type::Void; |
|||
llvm::IntegerType* Type::MainReturn; |
|||
llvm::PointerType* Type::EnvPtr; |
|||
llvm::PointerType* Type::RuntimeDataPtr; |
|||
llvm::PointerType* Type::RuntimePtr; |
|||
llvm::ConstantInt* Constant::gasMax; |
|||
llvm::MDNode* Type::expectTrue; |
|||
|
|||
void Type::init(llvm::LLVMContext& _context) |
|||
{ |
|||
if (!Word) // Do init only once
|
|||
{ |
|||
Word = llvm::Type::getIntNTy(_context, 256); |
|||
WordPtr = Word->getPointerTo(); |
|||
Bool = llvm::Type::getInt1Ty(_context); |
|||
Size = llvm::Type::getInt64Ty(_context); |
|||
Gas = Size; |
|||
GasPtr = Gas->getPointerTo(); |
|||
Byte = llvm::Type::getInt8Ty(_context); |
|||
BytePtr = Byte->getPointerTo(); |
|||
Void = llvm::Type::getVoidTy(_context); |
|||
MainReturn = llvm::Type::getInt32Ty(_context); |
|||
|
|||
EnvPtr = llvm::StructType::create(_context, "Env")->getPointerTo(); |
|||
RuntimeDataPtr = RuntimeManager::getRuntimeDataType()->getPointerTo(); |
|||
RuntimePtr = RuntimeManager::getRuntimeType()->getPointerTo(); |
|||
|
|||
Constant::gasMax = llvm::ConstantInt::getSigned(Type::Gas, std::numeric_limits<int64_t>::max()); |
|||
|
|||
expectTrue = llvm::MDBuilder{_context}.createBranchWeights(1, 0); |
|||
} |
|||
} |
|||
|
|||
llvm::ConstantInt* Constant::get(int64_t _n) |
|||
{ |
|||
return llvm::ConstantInt::getSigned(Type::Word, _n); |
|||
} |
|||
|
|||
llvm::ConstantInt* Constant::get(llvm::APInt const& _n) |
|||
{ |
|||
return llvm::ConstantInt::get(Type::Word->getContext(), _n); |
|||
} |
|||
|
|||
llvm::ConstantInt* Constant::get(ReturnCode _returnCode) |
|||
{ |
|||
return llvm::ConstantInt::get(Type::MainReturn, static_cast<uint64_t>(_returnCode)); |
|||
} |
|||
|
|||
} |
|||
} |
|||
} |
|||
|
@ -1,61 +0,0 @@ |
|||
#pragma once |
|||
|
|||
#include "preprocessor/llvm_includes_start.h" |
|||
#include <llvm/IR/Type.h> |
|||
#include <llvm/IR/Constants.h> |
|||
#include <llvm/IR/Metadata.h> |
|||
#include "preprocessor/llvm_includes_end.h" |
|||
|
|||
#include "evmjit/JIT.h" // ReturnCode |
|||
|
|||
namespace dev |
|||
{ |
|||
namespace eth |
|||
{ |
|||
namespace jit |
|||
{ |
|||
using namespace evmjit; |
|||
|
|||
struct Type |
|||
{ |
|||
static llvm::IntegerType* Word; |
|||
static llvm::PointerType* WordPtr; |
|||
|
|||
static llvm::IntegerType* Bool; |
|||
static llvm::IntegerType* Size; |
|||
static llvm::IntegerType* Gas; |
|||
static llvm::PointerType* GasPtr; |
|||
|
|||
static llvm::IntegerType* Byte; |
|||
static llvm::PointerType* BytePtr; |
|||
|
|||
static llvm::Type* Void; |
|||
|
|||
/// Main function return type
|
|||
static llvm::IntegerType* MainReturn; |
|||
|
|||
static llvm::PointerType* EnvPtr; |
|||
static llvm::PointerType* RuntimeDataPtr; |
|||
static llvm::PointerType* RuntimePtr; |
|||
|
|||
// TODO: Redesign static LLVM objects
|
|||
static llvm::MDNode* expectTrue; |
|||
|
|||
static void init(llvm::LLVMContext& _context); |
|||
}; |
|||
|
|||
struct Constant |
|||
{ |
|||
static llvm::ConstantInt* gasMax; |
|||
|
|||
/// Returns word-size constant
|
|||
static llvm::ConstantInt* get(int64_t _n); |
|||
static llvm::ConstantInt* get(llvm::APInt const& _n); |
|||
|
|||
static llvm::ConstantInt* get(ReturnCode _returnCode); |
|||
}; |
|||
|
|||
} |
|||
} |
|||
} |
|||
|
@ -1,27 +0,0 @@ |
|||
#include "Utils.h" |
|||
|
|||
#include <llvm/Support/Debug.h> |
|||
|
|||
#include "BuildInfo.gen.h" |
|||
|
|||
#if !defined(NDEBUG) // Debug
|
|||
|
|||
namespace dev |
|||
{ |
|||
namespace evmjit |
|||
{ |
|||
|
|||
std::ostream& getLogStream(char const* _channel) |
|||
{ |
|||
static std::ostream nullStream{nullptr}; |
|||
#if LLVM_DEBUG |
|||
return (llvm::DebugFlag && llvm::isCurrentDebugType(_channel)) ? std::cerr : nullStream; |
|||
#else |
|||
return (void)_channel, nullStream; |
|||
#endif |
|||
} |
|||
|
|||
} |
|||
} |
|||
|
|||
#endif |
@ -1,40 +0,0 @@ |
|||
#pragma once |
|||
|
|||
#include <iostream> |
|||
|
|||
// The same as assert, but expression is always evaluated and result returned
|
|||
#define CHECK(expr) (assert(expr), expr) |
|||
|
|||
#if !defined(NDEBUG) // Debug
|
|||
|
|||
namespace dev |
|||
{ |
|||
namespace evmjit |
|||
{ |
|||
|
|||
std::ostream& getLogStream(char const* _channel); |
|||
|
|||
} |
|||
} |
|||
|
|||
#define DLOG(CHANNEL) ::dev::evmjit::getLogStream(#CHANNEL) |
|||
|
|||
#else // Release
|
|||
|
|||
namespace dev |
|||
{ |
|||
namespace evmjit |
|||
{ |
|||
|
|||
struct Voider |
|||
{ |
|||
void operator=(std::ostream const&) {} |
|||
}; |
|||
|
|||
} |
|||
} |
|||
|
|||
|
|||
#define DLOG(CHANNEL) true ? (void)0 : ::dev::evmjit::Voider{} = std::cerr |
|||
|
|||
#endif |
@ -1,7 +0,0 @@ |
|||
#if defined(_MSC_VER) |
|||
#pragma warning(pop) |
|||
#elif defined(__clang__) |
|||
#pragma clang diagnostic pop |
|||
#else |
|||
#pragma GCC diagnostic pop |
|||
#endif |
@ -1,12 +0,0 @@ |
|||
#if defined(_MSC_VER) |
|||
#pragma warning(push) |
|||
#pragma warning(disable: 4267 4244 4800 4624) |
|||
#elif defined(__clang__) |
|||
#pragma clang diagnostic push |
|||
#pragma clang diagnostic ignored "-Wunused-parameter" |
|||
#pragma clang diagnostic ignored "-Wconversion" |
|||
#else |
|||
#pragma GCC diagnostic push |
|||
#pragma GCC diagnostic ignored "-Wunused-parameter" |
|||
#pragma GCC diagnostic ignored "-Wconversion" |
|||
#endif |
Loading…
Reference in new issue