Browse Source

Merge remote-tracking branch 'upstream/develop' into evmjit-llvm37

cl-refactor
Paweł Bylica 10 years ago
parent
commit
195f00cf2f
  1. 4
      CMakeLists.txt
  2. 56
      include/evmjit/DataTypes.h
  3. 36
      include/evmjit/JIT.h
  4. 3
      libevmjit-cpp/CMakeLists.txt
  5. 38
      libevmjit-cpp/Env.cpp
  6. 22
      libevmjit-cpp/JitVM.cpp
  7. 6
      libevmjit-cpp/JitVM.h
  8. 14
      libevmjit-cpp/Utils.h
  9. 43
      libevmjit/Array.cpp
  10. 3
      libevmjit/CMakeLists.txt
  11. 27
      libevmjit/Cache.cpp
  12. 12
      libevmjit/Common.h
  13. 24
      libevmjit/ExecutionEngine.cpp
  14. 46
      libevmjit/JIT.cpp
  15. 7
      libevmjit/RuntimeData.h

4
CMakeLists.txt

@ -7,7 +7,7 @@ set(CMAKE_AUTOMOC OFF)
if(${CMAKE_CXX_COMPILER_ID} STREQUAL "MSVC")
else()
set(CMAKE_CXX_FLAGS "-std=c++11 -Wall -Wextra -Wconversion -Wno-sign-conversion -Wno-unknown-pragmas")
set(CMAKE_CXX_FLAGS "-std=c++11 -Wall -Wextra -Wconversion -Wno-sign-conversion -Wno-unknown-pragmas ${CMAKE_CXX_FLAGS}")
endif()
if(${CMAKE_SYSTEM_NAME} STREQUAL "Linux")
@ -33,6 +33,8 @@ else()
link_directories(/usr/lib/llvm-3.7/lib)
endif()
get_filename_component(EVMJIT_INCLUDE_DIR include ABSOLUTE)
add_subdirectory(libevmjit)
if(EVMJIT_CPP)

56
include/evmjit/DataTypes.h

@ -0,0 +1,56 @@
#pragma once
#include <cstdint>
#include <functional>
namespace dev
{
namespace evmjit
{
struct h256
{
uint64_t words[4];
};
inline bool operator==(h256 _h1, h256 _h2)
{
return _h1.words[0] == _h2.words[0] &&
_h1.words[1] == _h2.words[1] &&
_h1.words[2] == _h2.words[2] &&
_h1.words[3] == _h2.words[3];
}
/// Representation of 256-bit value binary compatible with LLVM i256
struct i256
{
uint64_t a = 0;
uint64_t b = 0;
uint64_t c = 0;
uint64_t d = 0;
i256() = default;
i256(h256 _h)
{
a = _h.words[0];
b = _h.words[1];
c = _h.words[2];
d = _h.words[3];
}
};
}
}
namespace std
{
template<> struct hash<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]);
};
};
}

36
include/evmjit/JIT.h

@ -0,0 +1,36 @@
#pragma once
#include "evmjit/DataTypes.h"
namespace dev
{
namespace eth
{
namespace jit
{
class ExecutionEngine;
}
}
namespace evmjit
{
class JIT
{
public:
/// Ask JIT if the EVM code is ready for execution.
/// Returns `true` if the EVM code has been compiled and loaded into memory.
/// In this case the code can be executed without overhead.
/// \param _codeHash The Keccak hash of the EVM code.
static bool isCodeReady(h256 _codeHash);
private:
friend class dev::eth::jit::ExecutionEngine;
static uint64_t getCode(h256 _codeHash);
static void mapCode(h256 _codeHash, uint64_t _funcAddr);
};
}
}

3
libevmjit-cpp/CMakeLists.txt

@ -15,10 +15,11 @@ else()
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -fPIC") # add PIC for archive
endif()
add_library(${TARGET_NAME} ${SOURCES})
add_library(${TARGET_NAME} STATIC ${SOURCES})
set_property(TARGET ${TARGET_NAME} PROPERTY FOLDER "libs")
include_directories(../..)
include_directories(${EVMJIT_INCLUDE_DIR})
include_directories(${LLVM_INCLUDE_DIRS})
include_directories(${Boost_INCLUDE_DIRS})

38
libevmjit-cpp/Env.cpp

@ -1,8 +1,9 @@
#pragma GCC diagnostic ignored "-Wconversion"
#include <libdevcrypto/SHA3.h>
#include <libethcore/Params.h>
#include <libdevcore/SHA3.h>
#include <libevmcore/Params.h>
#include <libevm/ExtVMFace.h>
#include <evmjit/DataTypes.h>
#include "Utils.h"
@ -16,7 +17,7 @@ extern "C"
using namespace dev;
using namespace dev::eth;
using jit::i256;
using evmjit::i256;
EXPORT void env_sload(ExtVMFace* _env, i256* _index, i256* o_value)
{
@ -53,7 +54,7 @@ extern "C"
if (_env->balance(_env->myAddress) >= endowment && _env->depth < 1024)
{
u256 gas = *io_gas;
h256 address(_env->create(endowment, gas, {_initBeg, _initSize}, {}), h256::AlignRight);
h256 address(_env->create(endowment, gas, {_initBeg, (size_t)_initSize}, {}), h256::AlignRight);
*io_gas = static_cast<int64_t>(gas);
*o_address = address;
}
@ -63,19 +64,24 @@ extern "C"
EXPORT bool env_call(ExtVMFace* _env, int64_t* io_gas, int64_t _callGas, h256* _receiveAddress, i256* _value, byte* _inBeg, uint64_t _inSize, byte* _outBeg, uint64_t _outSize, h256* _codeAddress)
{
auto value = llvm2eth(*_value);
auto receiveAddress = right160(*_receiveAddress);
auto codeAddress = right160(*_codeAddress);
const auto isCall = receiveAddress == codeAddress; // OPT: The same address pointer can be used if not CODECALL
CallParameters params;
params.value = llvm2eth(*_value);
params.senderAddress = _env->myAddress;
params.receiveAddress = right160(*_receiveAddress);
params.codeAddress = right160(*_codeAddress);
params.data = {_inBeg, (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(receiveAddress))
if (isCall && !_env->exists(params.receiveAddress))
*io_gas -= static_cast<int64_t>(c_callNewAccountGas); // no underflow, *io_gas non-negative before
if (value > 0) // value transfer
if (params.value > 0) // value transfer
{
/*static*/ assert(c_callValueTransferGas > c_callStipend && "Overflow possible");
*io_gas -= static_cast<int64_t>(c_callValueTransferGas); // no underflow
@ -86,17 +92,17 @@ extern "C"
return false;
auto ret = false;
auto callGas = u256{_callGas};
if (_env->balance(_env->myAddress) >= value && _env->depth < 1024)
ret = _env->call(receiveAddress, value, {_inBeg, _inSize}, callGas, {_outBeg, _outSize}, {}, {}, codeAddress);
params.gas = u256{_callGas};
if (_env->balance(_env->myAddress) >= params.value && _env->depth < 1024)
ret = _env->call(params);
*io_gas += static_cast<int64_t>(callGas); // it is never more than initial _callGas
*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});
auto hash = sha3({_begin, (size_t)_size});
*o_hash = hash;
}
@ -124,7 +130,7 @@ extern "C"
if (_topic4)
topics.push_back(*_topic4);
_env->log(std::move(topics), {_beg, _size});
_env->log(std::move(topics), {_beg, (size_t)_size});
}
}

22
libevmjit-cpp/JitVM.cpp

@ -4,7 +4,7 @@
#include "JitVM.h"
#include <libdevcore/Log.h>
#include <libdevcrypto/SHA3.h>
#include <libdevcore/SHA3.h>
#include <libevm/VM.h>
#include <libevm/VMFactory.h>
#include <evmjit/libevmjit/ExecutionEngine.h>
@ -18,29 +18,25 @@ namespace eth
extern "C" void env_sload(); // fake declaration for linker symbol stripping workaround, see a call below
bytesConstRef JitVM::go(ExtVMFace& _ext, OnOpFunc const& _onOp, uint64_t _step)
bytesConstRef JitVM::execImpl(u256& io_gas, ExtVMFace& _ext, OnOpFunc const& _onOp)
{
using namespace jit;
auto rejected = false;
// TODO: Rejecting transactions with gas limit > 2^63 can be used by attacker to take JIT out of scope
rejected |= m_gas > std::numeric_limits<decltype(m_data.gas)>::max(); // Do not accept requests with gas > 2^63 (int64 max)
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.currentBlock.number > std::numeric_limits<decltype(m_data.number)>::max();
rejected |= _ext.currentBlock.timestamp > std::numeric_limits<decltype(m_data.timestamp)>::max();
if (rejected)
{
cwarn << "Execution rejected by EVM JIT (gas limit: " << m_gas << "), executing with interpreter";
VMFactory::setKind(VMKind::Interpreter);
m_fallbackVM = VMFactory::create(m_gas);
VMFactory::setKind(VMKind::JIT);
auto&& output = m_fallbackVM->go(_ext, _onOp, _step);
m_gas = m_fallbackVM->gas(); // copy remaining gas, Executive expects it
return output;
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)>(m_gas);
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();
@ -55,7 +51,7 @@ bytesConstRef JitVM::go(ExtVMFace& _ext, OnOpFunc const& _onOp, uint64_t _step)
m_data.timestamp = static_cast<decltype(m_data.timestamp)>(_ext.currentBlock.timestamp);
m_data.code = _ext.code.data();
m_data.codeSize = _ext.code.size();
m_data.codeHash = eth2llvm(sha3(_ext.code));
m_data.codeHash = eth2llvm(_ext.codeHash);
auto env = reinterpret_cast<Env*>(&_ext);
auto exitCode = m_engine.run(&m_data, env);
@ -80,7 +76,7 @@ bytesConstRef JitVM::go(ExtVMFace& _ext, OnOpFunc const& _onOp, uint64_t _step)
break;
}
m_gas = m_data.gas; // TODO: Remove m_gas field
io_gas = m_data.gas;
return {std::get<0>(m_engine.returnData), std::get<1>(m_engine.returnData)};
}

6
libevmjit-cpp/JitVM.h

@ -10,12 +10,10 @@ namespace eth
class JitVM: public VMFace
{
virtual bytesConstRef go(ExtVMFace& _ext, OnOpFunc const& _onOp = {}, uint64_t _steps = (uint64_t)-1) override final;
public:
virtual bytesConstRef execImpl(u256& io_gas, ExtVMFace& _ext, OnOpFunc const& _onOp) override final;
private:
friend class VMFactory;
explicit JitVM(u256 _gas = 0) : VMFace(_gas) {}
jit::RuntimeData m_data;
jit::ExecutionEngine m_engine;
std::unique_ptr<VMFace> m_fallbackVM; ///< VM used in case of input data rejected by JIT

14
libevmjit-cpp/Utils.h

@ -1,13 +1,13 @@
#pragma once
#include <evmjit/libevmjit/Common.h>
#include <evmjit/DataTypes.h>
namespace dev
{
namespace eth
{
inline u256 llvm2eth(jit::i256 _i)
inline u256 llvm2eth(evmjit::i256 _i)
{
u256 u = 0;
u |= _i.d;
@ -20,9 +20,9 @@ inline u256 llvm2eth(jit::i256 _i)
return u;
}
inline jit::i256 eth2llvm(u256 _u)
inline evmjit::i256 eth2llvm(u256 _u)
{
jit::i256 i;
evmjit::i256 i;
u256 mask = 0xFFFFFFFFFFFFFFFF;
i.a = static_cast<uint64_t>(_u & mask);
_u >>= 64;
@ -34,5 +34,11 @@ inline jit::i256 eth2llvm(u256 _u)
return i;
}
inline evmjit::h256 eth2llvm(h256 _u)
{
/// Just directly copies memory
return *(evmjit::h256*)&_u;
}
}
}

43
libevmjit/Array.cpp

@ -9,8 +9,6 @@
#include "Runtime.h"
#include "Utils.h"
#include <set> // DEBUG only
namespace dev
{
namespace eth
@ -269,52 +267,15 @@ void Array::extend(llvm::Value* _arrayPtr, llvm::Value* _size)
}
}
namespace
{
struct AllocatedMemoryWatchdog
{
std::set<void*> allocatedMemory;
~AllocatedMemoryWatchdog()
{
if (!allocatedMemory.empty())
{
DLOG(mem) << allocatedMemory.size() << " MEM LEAKS!\n";
for (auto&& leak : allocatedMemory)
DLOG(mem) << "\t" << leak << "\n";
}
}
};
AllocatedMemoryWatchdog watchdog;
}
extern "C"
{
using namespace dev::eth::jit;
EXPORT void* ext_realloc(void* _data, size_t _size) noexcept
{
//std::cerr << "REALLOC: " << _data << " [" << _size << "]" << std::endl;
auto newData = std::realloc(_data, _size);
if (_data != newData)
{
DLOG(mem) << "REALLOC: " << newData << " <- " << _data << " [" << _size << "]\n";
watchdog.allocatedMemory.erase(_data);
watchdog.allocatedMemory.insert(newData);
}
return newData;
return std::realloc(_data, _size);
}
EXPORT void ext_free(void* _data) noexcept
{
std::free(_data);
if (_data)
{
DLOG(mem) << "FREE : " << _data << "\n";
watchdog.allocatedMemory.erase(_data);
}
}
} // extern "C"
}

3
libevmjit/CMakeLists.txt

@ -8,6 +8,7 @@ set(SOURCES
Common.h
Compiler.cpp Compiler.h
CompilerHelper.cpp CompilerHelper.h
${EVMJIT_INCLUDE_DIR}/evmjit/DataTypes.h
Endianness.cpp Endianness.h
ExecStats.cpp ExecStats.h
ExecutionEngine.cpp ExecutionEngine.h
@ -15,6 +16,7 @@ set(SOURCES
GasMeter.cpp GasMeter.h
Instruction.cpp Instruction.h
interface.cpp interface.h
JIT.cpp ${EVMJIT_INCLUDE_DIR}/evmjit/JIT.h
Memory.cpp Memory.h
Optimizer.cpp Optimizer.h
Runtime.cpp Runtime.h
@ -79,6 +81,7 @@ set_target_properties(${TARGET_NAME} PROPERTIES
VERSION ${EVMJIT_VERSION} SOVERSION ${EVMJIT_SOVERSION}
FOLDER "libs")
include_directories(${EVMJIT_INCLUDE_DIR})
include_directories(${LLVM_INCLUDE_DIRS})
include_directories(${CMAKE_CURRENT_BINARY_DIR}/gen)

27
libevmjit/Cache.cpp

@ -1,5 +1,7 @@
#include "Cache.h"
#include <mutex>
#include "preprocessor/llvm_includes_start.h"
#include <llvm/IR/Module.h>
#include <llvm/IR/LLVMContext.h>
@ -23,6 +25,8 @@ namespace jit
namespace
{
using Guard = std::lock_guard<std::mutex>;
std::mutex x_cacheMutex;
CacheMode g_mode;
std::unique_ptr<llvm::MemoryBuffer> g_lastObject;
ExecutionEngineListener* g_listener;
@ -43,6 +47,9 @@ namespace
ObjectCache* Cache::getObjectCache(CacheMode _mode, ExecutionEngineListener* _listener)
{
static ObjectCache objectCache;
Guard g{x_cacheMutex};
g_mode = _mode;
g_listener = _listener;
return &objectCache;
@ -50,6 +57,8 @@ ObjectCache* Cache::getObjectCache(CacheMode _mode, ExecutionEngineListener* _li
void Cache::clear()
{
Guard g{x_cacheMutex};
using namespace llvm::sys;
llvm::SmallString<256> cachePath;
path::system_temp_directory(false, cachePath);
@ -62,6 +71,8 @@ void Cache::clear()
void Cache::preload(llvm::ExecutionEngine& _ee, std::unordered_map<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;
@ -91,11 +102,14 @@ void Cache::preload(llvm::ExecutionEngine& _ee, std::unordered_map<std::string,
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;
if (g_listener)
g_listener->stateChanged(ExecState::CacheLoad);
// TODO: Disabled because is not thread-safe.
//if (g_listener)
// g_listener->stateChanged(ExecState::CacheLoad);
DLOG(cache) << id << ": search\n";
if (!CHECK(!g_lastObject))
@ -135,12 +149,15 @@ std::unique_ptr<llvm::Module> Cache::getObject(std::string const& id)
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;
if (g_listener)
g_listener->stateChanged(ExecState::CacheWrite);
// TODO: Disabled because is not thread-safe.
// if (g_listener)
// g_listener->stateChanged(ExecState::CacheWrite);
auto&& id = _module->getModuleIdentifier();
llvm::SmallString<256> cachePath;
@ -160,6 +177,8 @@ void ObjectCache::notifyObjectCompiled(llvm::Module const* _module, llvm::Memory
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);
}

12
libevmjit/Common.h

@ -31,7 +31,7 @@ enum class ReturnCode
// Standard error codes
OutOfGas = -1,
StackUnderflow = -2,
StackUnderflow = -2,
BadJumpDestination = -3,
BadInstruction = -4,
Rejected = -5, ///< Input data (code, gas, block info, etc.) does not meet JIT requirement and execution request has been rejected
@ -46,16 +46,6 @@ enum class ReturnCode
LinkerWorkaround = -299,
};
/// Representation of 256-bit value binary compatible with LLVM i256
struct i256
{
uint64_t a = 0;
uint64_t b = 0;
uint64_t c = 0;
uint64_t d = 0;
};
static_assert(sizeof(i256) == 32, "Wrong i265 size");
#define UNTESTED assert(false)
}

24
libevmjit/ExecutionEngine.cpp

@ -19,6 +19,7 @@
#include <llvm/Support/ManagedStatic.h>
#include "preprocessor/llvm_includes_end.h"
#include "evmjit/JIT.h"
#include "Runtime.h"
#include "Compiler.h"
#include "Optimizer.h"
@ -33,6 +34,7 @@ namespace eth
{
namespace jit
{
using evmjit::JIT;
namespace
{
@ -106,8 +108,6 @@ ReturnCode ExecutionEngine::run(RuntimeData* _data, Env* _env)
// TODO: Do not pseudo-init the cache every time
auto objectCache = (g_cache != CacheMode::off && g_cache != CacheMode::clear) ? Cache::getObjectCache(g_cache, listener.get()) : nullptr;
static std::unordered_map<std::string, uint64_t> funcCache;
static std::unique_ptr<llvm::ExecutionEngine> ee;
if (!ee)
{
@ -134,8 +134,9 @@ ReturnCode ExecutionEngine::run(RuntimeData* _data, Env* _env)
return ReturnCode::LLVMConfigError;
ee->setObjectCache(objectCache);
if (preloadCache)
Cache::preload(*ee, funcCache);
// FIXME: Disabled during API changes
//if (preloadCache)
// Cache::preload(*ee, funcCache);
}
static StatsCollector statsCollector;
@ -143,11 +144,8 @@ ReturnCode ExecutionEngine::run(RuntimeData* _data, Env* _env)
auto mainFuncName = codeHash(_data->codeHash);
m_runtime.init(_data, _env);
EntryFuncPtr entryFuncPtr = nullptr;
auto it = funcCache.find(mainFuncName);
if (it != funcCache.end())
entryFuncPtr = (EntryFuncPtr) it->second;
// TODO: Remove cast
auto entryFuncPtr = (EntryFuncPtr) JIT::getCode(_data->codeHash);
if (!entryFuncPtr)
{
auto module = objectCache ? Cache::getObject(mainFuncName) : nullptr;
@ -169,12 +167,10 @@ ReturnCode ExecutionEngine::run(RuntimeData* _data, Env* _env)
ee->addModule(std::move(module));
listener->stateChanged(ExecState::CodeGen);
entryFuncPtr = (EntryFuncPtr)ee->getFunctionAddress(mainFuncName);
if (!CHECK(entryFuncPtr))
return ReturnCode::LLVMLinkError;
JIT::mapCode(_data->codeHash, (uint64_t)entryFuncPtr); // FIXME: Remove cast
}
if (!CHECK(entryFuncPtr))
return ReturnCode::LLVMLinkError;
if (it == funcCache.end())
funcCache[mainFuncName] = (uint64_t) entryFuncPtr;
listener->stateChanged(ExecState::Execution);
auto returnCode = entryFuncPtr(&m_runtime);

46
libevmjit/JIT.cpp

@ -0,0 +1,46 @@
#include "evmjit/JIT.h"
#include <unordered_map>
namespace dev
{
namespace evmjit
{
namespace
{
class JITImpl: JIT
{
public:
std::unordered_map<h256, uint64_t> codeMap;
static JITImpl& instance()
{
static JITImpl s_instance;
return s_instance;
}
};
} // anonymous namespace
bool JIT::isCodeReady(h256 _codeHash)
{
return JITImpl::instance().codeMap.count(_codeHash) != 0;
}
uint64_t JIT::getCode(h256 _codeHash)
{
auto& codeMap = JITImpl::instance().codeMap;
auto it = codeMap.find(_codeHash);
if (it != codeMap.end())
return it->second;
return 0;
}
void JIT::mapCode(h256 _codeHash, uint64_t _funcAddr)
{
JITImpl::instance().codeMap.insert(std::make_pair(_codeHash, _funcAddr));
}
}
}

7
libevmjit/RuntimeData.h

@ -1,5 +1,6 @@
#pragma once
#include "evmjit/DataTypes.h"
#include "Common.h"
namespace dev
@ -8,7 +9,9 @@ namespace eth
{
namespace jit
{
using evmjit::i256;
using evmjit::h256;
struct RuntimeData
{
enum Index
@ -49,7 +52,7 @@ struct RuntimeData
int64_t timestamp = 0;
byte const* code = nullptr;
uint64_t codeSize = 0;
i256 codeHash;
h256 codeHash;
};
/// VM Environment (ExtVM) opaque type

Loading…
Cancel
Save