Browse Source

Merge pull request #1178 from imapp-pl/pr/evmjit-v0.3

EVM JIT 0.3 (PoC-9)
cl-refactor
Gav Wood 10 years ago
parent
commit
c9a6beebe2
  1. 4
      evmjit/CMakeLists.txt
  2. 3
      evmjit/libevmjit-cpp/CMakeLists.txt
  3. 37
      evmjit/libevmjit-cpp/Env.cpp
  4. 133
      evmjit/libevmjit/Arith256.cpp
  5. 320
      evmjit/libevmjit/Array.cpp
  6. 74
      evmjit/libevmjit/Array.h
  7. 65
      evmjit/libevmjit/BasicBlock.cpp
  8. 1
      evmjit/libevmjit/BuildInfo.h.in
  9. 35
      evmjit/libevmjit/CMakeLists.txt
  10. 81
      evmjit/libevmjit/Cache.cpp
  11. 14
      evmjit/libevmjit/Cache.h
  12. 8
      evmjit/libevmjit/Common.h
  13. 73
      evmjit/libevmjit/Compiler.cpp
  14. 4
      evmjit/libevmjit/Compiler.h
  15. 9
      evmjit/libevmjit/Endianness.cpp
  16. 1
      evmjit/libevmjit/ExecStats.cpp
  17. 1
      evmjit/libevmjit/ExecStats.h
  18. 91
      evmjit/libevmjit/ExecutionEngine.cpp
  19. 7
      evmjit/libevmjit/ExecutionEngine.h
  20. 10
      evmjit/libevmjit/Ext.cpp
  21. 2
      evmjit/libevmjit/Ext.h
  22. 230
      evmjit/libevmjit/GasMeter.cpp
  23. 4
      evmjit/libevmjit/GasMeter.h
  24. 158
      evmjit/libevmjit/Memory.cpp
  25. 6
      evmjit/libevmjit/Memory.h
  26. 29
      evmjit/libevmjit/Optimizer.cpp
  27. 19
      evmjit/libevmjit/Optimizer.h
  28. 25
      evmjit/libevmjit/Runtime.cpp
  29. 24
      evmjit/libevmjit/Runtime.h
  30. 43
      evmjit/libevmjit/RuntimeManager.cpp
  31. 20
      evmjit/libevmjit/RuntimeManager.h
  32. 138
      evmjit/libevmjit/Stack.cpp
  33. 15
      evmjit/libevmjit/Stack.h
  34. 6
      evmjit/libevmjit/Type.cpp
  35. 3
      evmjit/libevmjit/Type.h
  36. 22
      evmjit/libevmjit/Utils.cpp
  37. 36
      evmjit/libevmjit/Utils.h
  38. 5
      evmjit/libevmjit/interface.cpp

4
evmjit/CMakeLists.txt

@ -7,7 +7,7 @@ set(CMAKE_AUTOMOC OFF)
if(${CMAKE_CXX_COMPILER_ID} STREQUAL "MSVC") if(${CMAKE_CXX_COMPILER_ID} STREQUAL "MSVC")
else() else()
set(CMAKE_CXX_FLAGS "-std=c++11 -Wall -Wextra -Wconversion -Wno-unknown-pragmas") set(CMAKE_CXX_FLAGS "-std=c++11 -Wall -Wextra -Wconversion -Wno-sign-conversion -Wno-unknown-pragmas")
endif() endif()
if(${CMAKE_SYSTEM_NAME} STREQUAL "Linux") if(${CMAKE_SYSTEM_NAME} STREQUAL "Linux")
@ -22,7 +22,7 @@ if(LLVM_DIR OR APPLE) # local LLVM build
message(STATUS "Using LLVMConfig.cmake in: ${LLVM_DIR}") message(STATUS "Using LLVMConfig.cmake in: ${LLVM_DIR}")
add_definitions(${LLVM_DEFINITIONS}) add_definitions(${LLVM_DEFINITIONS})
# TODO: bitwriter is needed only for evmcc # TODO: bitwriter is needed only for evmcc
llvm_map_components_to_libnames(LLVM_LIBS core support mcjit x86asmparser x86codegen bitwriter) llvm_map_components_to_libnames(LLVM_LIBS core support mcjit x86asmparser x86codegen bitwriter ipo)
else() else()
# Workaround for Ubuntu broken LLVM package # Workaround for Ubuntu broken LLVM package
message(STATUS "Using llvm-3.5-dev package from Ubuntu. If does not work, build LLVM and set -DLLVM_DIR=llvm-build/share/llvm/cmake") message(STATUS "Using llvm-3.5-dev package from Ubuntu. If does not work, build LLVM and set -DLLVM_DIR=llvm-build/share/llvm/cmake")

3
evmjit/libevmjit-cpp/CMakeLists.txt

@ -4,8 +4,9 @@ set(TARGET_NAME evmjit-cpp)
find_package(Boost REQUIRED) find_package(Boost REQUIRED)
set(SOURCES set(SOURCES
Env.cpp Env.cpp
JitVM.cpp JitVM.h JitVM.cpp JitVM.h
Utils.h
) )
source_group("" FILES ${SOURCES}) source_group("" FILES ${SOURCES})

37
evmjit/libevmjit-cpp/Env.cpp

@ -62,23 +62,40 @@ extern "C"
*o_address = {}; *o_address = {};
} }
EXPORT bool env_call(ExtVMFace* _env, int64_t* io_gas, h256* _receiveAddress, i256* _value, byte* _inBeg, uint64_t _inSize, byte* _outBeg, uint64_t _outSize, h256* _codeAddress) 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 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
*io_gas -= _callGas;
if (*io_gas < 0)
return false;
if (isCall && !_env->exists(receiveAddress))
*io_gas -= static_cast<int64_t>(c_callNewAccountGas); // no underflow, *io_gas non-negative before
if (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;
auto callGas = u256{_callGas};
if (_env->balance(_env->myAddress) >= value && _env->depth < 1024) if (_env->balance(_env->myAddress) >= value && _env->depth < 1024)
{ {
_env->subBalance(value); _env->subBalance(value);
auto receiveAddress = right160(*_receiveAddress); ret = _env->call(receiveAddress, value, {_inBeg, _inSize}, callGas, {_outBeg, _outSize}, {}, {}, codeAddress);
auto inRef = bytesConstRef{_inBeg, _inSize};
auto outRef = bytesRef{_outBeg, _outSize};
auto codeAddress = right160(*_codeAddress);
u256 gas = *io_gas;
auto ret = _env->call(receiveAddress, value, inRef, gas, outRef, {}, {}, codeAddress);
*io_gas = static_cast<int64_t>(gas);
return ret;
} }
return false; *io_gas += static_cast<int64_t>(callGas); // it is never more than initial _callGas
return ret;
} }
EXPORT void env_sha3(byte* _begin, uint64_t _size, h256* o_hash) EXPORT void env_sha3(byte* _begin, uint64_t _size, h256* o_hash)

133
evmjit/libevmjit/Arith256.cpp

@ -9,6 +9,7 @@
#include "Type.h" #include "Type.h"
#include "Endianness.h" #include "Endianness.h"
#include "Utils.h"
namespace dev namespace dev
{ {
@ -38,6 +39,8 @@ llvm::Function* Arith256::getMulFunc()
{ {
llvm::Type* argTypes[] = {Type::Word, Type::Word}; llvm::Type* argTypes[] = {Type::Word, Type::Word};
func = llvm::Function::Create(llvm::FunctionType::get(Type::Word, argTypes, false), llvm::Function::PrivateLinkage, "mul", getModule()); func = llvm::Function::Create(llvm::FunctionType::get(Type::Word, argTypes, false), llvm::Function::PrivateLinkage, "mul", getModule());
func->setDoesNotThrow();
func->setDoesNotAccessMemory();
auto x = &func->getArgumentList().front(); auto x = &func->getArgumentList().front();
x->setName("x"); x->setName("x");
@ -50,12 +53,16 @@ llvm::Function* Arith256::getMulFunc()
auto i64 = Type::Size; auto i64 = Type::Size;
auto i128 = m_builder.getIntNTy(128); auto i128 = m_builder.getIntNTy(128);
auto i256 = Type::Word; auto i256 = Type::Word;
auto c64 = Constant::get(64);
auto c128 = Constant::get(128);
auto c192 = Constant::get(192);
auto x_lo = m_builder.CreateTrunc(x, i64, "x.lo"); auto x_lo = m_builder.CreateTrunc(x, i64, "x.lo");
auto y_lo = m_builder.CreateTrunc(y, i64, "y.lo"); auto y_lo = m_builder.CreateTrunc(y, i64, "y.lo");
auto x_mi = m_builder.CreateTrunc(m_builder.CreateLShr(x, Constant::get(64)), i64); auto x_mi = m_builder.CreateTrunc(m_builder.CreateLShr(x, c64), i64);
auto y_mi = m_builder.CreateTrunc(m_builder.CreateLShr(y, Constant::get(64)), i64); auto y_mi = m_builder.CreateTrunc(m_builder.CreateLShr(y, c64), i64);
auto x_hi = m_builder.CreateTrunc(m_builder.CreateLShr(x, Constant::get(128)), i128); auto x_hi = m_builder.CreateTrunc(m_builder.CreateLShr(x, c128), i128);
auto y_hi = m_builder.CreateTrunc(m_builder.CreateLShr(y, Constant::get(128)), i128); auto y_hi = m_builder.CreateTrunc(m_builder.CreateLShr(y, c128), i128);
auto t1 = m_builder.CreateMul(m_builder.CreateZExt(x_lo, i128), m_builder.CreateZExt(y_lo, i128)); auto t1 = m_builder.CreateMul(m_builder.CreateZExt(x_lo, i128), m_builder.CreateZExt(y_lo, i128));
auto t2 = m_builder.CreateMul(m_builder.CreateZExt(x_lo, i128), m_builder.CreateZExt(y_mi, i128)); auto t2 = m_builder.CreateMul(m_builder.CreateZExt(x_lo, i128), m_builder.CreateZExt(y_mi, i128));
@ -67,13 +74,13 @@ llvm::Function* Arith256::getMulFunc()
auto t8 = m_builder.CreateMul(x_hi, m_builder.CreateZExt(y_mi, i128)); auto t8 = m_builder.CreateMul(x_hi, m_builder.CreateZExt(y_mi, i128));
auto p = m_builder.CreateZExt(t1, i256); auto p = m_builder.CreateZExt(t1, i256);
p = m_builder.CreateAdd(p, m_builder.CreateShl(m_builder.CreateZExt(t2, i256), Constant::get(64))); p = m_builder.CreateAdd(p, m_builder.CreateShl(m_builder.CreateZExt(t2, i256), c64));
p = m_builder.CreateAdd(p, m_builder.CreateShl(m_builder.CreateZExt(t3, i256), Constant::get(128))); p = m_builder.CreateAdd(p, m_builder.CreateShl(m_builder.CreateZExt(t3, i256), c128));
p = m_builder.CreateAdd(p, m_builder.CreateShl(m_builder.CreateZExt(t4, i256), Constant::get(64))); p = m_builder.CreateAdd(p, m_builder.CreateShl(m_builder.CreateZExt(t4, i256), c64));
p = m_builder.CreateAdd(p, m_builder.CreateShl(m_builder.CreateZExt(t5, i256), Constant::get(128))); p = m_builder.CreateAdd(p, m_builder.CreateShl(m_builder.CreateZExt(t5, i256), c128));
p = m_builder.CreateAdd(p, m_builder.CreateShl(m_builder.CreateZExt(t6, i256), Constant::get(192))); p = m_builder.CreateAdd(p, m_builder.CreateShl(m_builder.CreateZExt(t6, i256), c192));
p = m_builder.CreateAdd(p, m_builder.CreateShl(m_builder.CreateZExt(t7, i256), Constant::get(128))); p = m_builder.CreateAdd(p, m_builder.CreateShl(m_builder.CreateZExt(t7, i256), c128));
p = m_builder.CreateAdd(p, m_builder.CreateShl(m_builder.CreateZExt(t8, i256), Constant::get(192))); p = m_builder.CreateAdd(p, m_builder.CreateShl(m_builder.CreateZExt(t8, i256), c192));
m_builder.CreateRet(p); m_builder.CreateRet(p);
} }
return func; return func;
@ -87,6 +94,8 @@ llvm::Function* Arith256::getMul512Func()
auto i512 = m_builder.getIntNTy(512); auto i512 = m_builder.getIntNTy(512);
llvm::Type* argTypes[] = {Type::Word, Type::Word}; llvm::Type* argTypes[] = {Type::Word, Type::Word};
func = llvm::Function::Create(llvm::FunctionType::get(i512, argTypes, false), llvm::Function::PrivateLinkage, "mul512", getModule()); func = llvm::Function::Create(llvm::FunctionType::get(i512, argTypes, false), llvm::Function::PrivateLinkage, "mul512", getModule());
func->setDoesNotThrow();
func->setDoesNotAccessMemory();
auto x = &func->getArgumentList().front(); auto x = &func->getArgumentList().front();
x->setName("x"); x->setName("x");
@ -130,6 +139,8 @@ llvm::Function* Arith256::getDivFunc(llvm::Type* _type)
auto retType = llvm::StructType::get(m_builder.getContext(), llvm::ArrayRef<llvm::Type*>{argTypes}); auto retType = llvm::StructType::get(m_builder.getContext(), llvm::ArrayRef<llvm::Type*>{argTypes});
auto funcName = _type == Type::Word ? "div" : "div512"; auto funcName = _type == Type::Word ? "div" : "div512";
func = llvm::Function::Create(llvm::FunctionType::get(retType, argTypes, false), llvm::Function::PrivateLinkage, funcName, getModule()); func = llvm::Function::Create(llvm::FunctionType::get(retType, argTypes, false), llvm::Function::PrivateLinkage, funcName, getModule());
func->setDoesNotThrow();
func->setDoesNotAccessMemory();
auto zero = llvm::ConstantInt::get(_type, 0); auto zero = llvm::ConstantInt::get(_type, 0);
auto one = llvm::ConstantInt::get(_type, 1); auto one = llvm::ConstantInt::get(_type, 1);
@ -221,6 +232,8 @@ llvm::Function* Arith256::getExpFunc()
{ {
llvm::Type* argTypes[] = {Type::Word, Type::Word}; 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 = 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(); auto base = &m_exp->getArgumentList().front();
base->setName("base"); base->setName("base");
@ -289,6 +302,8 @@ llvm::Function* Arith256::getAddModFunc()
auto i512Ty = m_builder.getIntNTy(512); auto i512Ty = m_builder.getIntNTy(512);
llvm::Type* argTypes[] = {Type::Word, Type::Word, Type::Word}; llvm::Type* argTypes[] = {Type::Word, Type::Word, Type::Word};
m_addmod = llvm::Function::Create(llvm::FunctionType::get(Type::Word, argTypes, false), llvm::Function::PrivateLinkage, "addmod", getModule()); m_addmod = llvm::Function::Create(llvm::FunctionType::get(Type::Word, argTypes, false), llvm::Function::PrivateLinkage, "addmod", getModule());
m_addmod->setDoesNotThrow();
m_addmod->setDoesNotAccessMemory();
auto x = &m_addmod->getArgumentList().front(); auto x = &m_addmod->getArgumentList().front();
x->setName("x"); x->setName("x");
@ -318,6 +333,8 @@ llvm::Function* Arith256::getMulModFunc()
{ {
llvm::Type* argTypes[] = {Type::Word, Type::Word, Type::Word}; llvm::Type* argTypes[] = {Type::Word, Type::Word, Type::Word};
m_mulmod = llvm::Function::Create(llvm::FunctionType::get(Type::Word, argTypes, false), llvm::Function::PrivateLinkage, "mulmod", getModule()); m_mulmod = llvm::Function::Create(llvm::FunctionType::get(Type::Word, argTypes, false), llvm::Function::PrivateLinkage, "mulmod", getModule());
m_mulmod->setDoesNotThrow();
m_mulmod->setDoesNotAccessMemory();
auto i512Ty = m_builder.getIntNTy(512); auto i512Ty = m_builder.getIntNTy(512);
auto x = &m_mulmod->getArgumentList().front(); auto x = &m_mulmod->getArgumentList().front();
@ -343,18 +360,49 @@ llvm::Function* Arith256::getMulModFunc()
llvm::Value* Arith256::mul(llvm::Value* _arg1, llvm::Value* _arg2) llvm::Value* Arith256::mul(llvm::Value* _arg1, llvm::Value* _arg2)
{ {
if (auto c1 = llvm::dyn_cast<llvm::ConstantInt>(_arg1))
{
if (auto c2 = llvm::dyn_cast<llvm::ConstantInt>(_arg2))
return Constant::get(c1->getValue() * c2->getValue());
}
return createCall(getMulFunc(), {_arg1, _arg2}); return createCall(getMulFunc(), {_arg1, _arg2});
} }
std::pair<llvm::Value*, llvm::Value*> Arith256::div(llvm::Value* _arg1, llvm::Value* _arg2) std::pair<llvm::Value*, llvm::Value*> Arith256::div(llvm::Value* _arg1, llvm::Value* _arg2)
{ {
auto div = m_builder.CreateExtractValue(createCall(getDivFunc(Type::Word), {_arg1, _arg2}), 0, "div"); if (auto c1 = llvm::dyn_cast<llvm::ConstantInt>(_arg1))
auto mod = m_builder.CreateExtractValue(createCall(getDivFunc(Type::Word), {_arg1, _arg2}), 1, "mod"); {
if (auto c2 = llvm::dyn_cast<llvm::ConstantInt>(_arg2))
{
if (!c2->getValue())
return std::make_pair(Constant::get(0), Constant::get(0));
auto div = Constant::get(c1->getValue().udiv(c2->getValue()));
auto mod = Constant::get(c1->getValue().urem(c2->getValue()));
return std::make_pair(div, mod);
}
}
auto r = createCall(getDivFunc(Type::Word), {_arg1, _arg2});
auto div = m_builder.CreateExtractValue(r, 0, "div");
auto mod = m_builder.CreateExtractValue(r, 1, "mod");
return std::make_pair(div, mod); return std::make_pair(div, mod);
} }
std::pair<llvm::Value*, llvm::Value*> Arith256::sdiv(llvm::Value* _x, llvm::Value* _y) std::pair<llvm::Value*, llvm::Value*> Arith256::sdiv(llvm::Value* _x, llvm::Value* _y)
{ {
if (auto c1 = llvm::dyn_cast<llvm::ConstantInt>(_x))
{
if (auto c2 = llvm::dyn_cast<llvm::ConstantInt>(_y))
{
if (!c2->getValue())
return std::make_pair(Constant::get(0), Constant::get(0));
auto div = Constant::get(c1->getValue().sdiv(c2->getValue()));
auto mod = Constant::get(c1->getValue().srem(c2->getValue()));
return std::make_pair(div, mod);
}
}
auto xIsNeg = m_builder.CreateICmpSLT(_x, Constant::get(0)); auto xIsNeg = m_builder.CreateICmpSLT(_x, Constant::get(0));
auto xNeg = m_builder.CreateSub(Constant::get(0), _x); auto xNeg = m_builder.CreateSub(Constant::get(0), _x);
auto xAbs = m_builder.CreateSelect(xIsNeg, xNeg, _x); auto xAbs = m_builder.CreateSelect(xIsNeg, xNeg, _x);
@ -378,16 +426,73 @@ std::pair<llvm::Value*, llvm::Value*> Arith256::sdiv(llvm::Value* _x, llvm::Valu
llvm::Value* Arith256::exp(llvm::Value* _arg1, llvm::Value* _arg2) 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}); return createCall(getExpFunc(), {_arg1, _arg2});
} }
llvm::Value* Arith256::addmod(llvm::Value* _arg1, llvm::Value* _arg2, llvm::Value* _arg3) llvm::Value* Arith256::addmod(llvm::Value* _arg1, llvm::Value* _arg2, llvm::Value* _arg3)
{ {
// FIXME: Disabled because of llvm::APInt::urem bug
// if (auto c1 = llvm::dyn_cast<llvm::ConstantInt>(_arg1))
// {
// if (auto c2 = llvm::dyn_cast<llvm::ConstantInt>(_arg2))
// {
// if (auto c3 = llvm::dyn_cast<llvm::ConstantInt>(_arg3))
// {
// if (!c3->getValue())
// return Constant::get(0);
// auto s = c1->getValue().zext(256+64) + c2->getValue().zext(256+64);
// auto r = s.urem(c3->getValue().zext(256+64)).trunc(256);
// return Constant::get(r);
// }
// }
// }
return createCall(getAddModFunc(), {_arg1, _arg2, _arg3}); return createCall(getAddModFunc(), {_arg1, _arg2, _arg3});
} }
llvm::Value* Arith256::mulmod(llvm::Value* _arg1, llvm::Value* _arg2, llvm::Value* _arg3) llvm::Value* Arith256::mulmod(llvm::Value* _arg1, llvm::Value* _arg2, llvm::Value* _arg3)
{ {
// FIXME: Disabled because of llvm::APInt::urem bug
// if (auto c1 = llvm::dyn_cast<llvm::ConstantInt>(_arg1))
// {
// if (auto c2 = llvm::dyn_cast<llvm::ConstantInt>(_arg2))
// {
// if (auto c3 = llvm::dyn_cast<llvm::ConstantInt>(_arg3))
// {
// if (!c3->getValue())
// return Constant::get(0);
// auto p = c1->getValue().zext(512) * c2->getValue().zext(512);
// auto r = p.urem(c3->getValue().zext(512)).trunc(256);
// return Constant::get(r);
// }
// }
// }
return createCall(getMulModFunc(), {_arg1, _arg2, _arg3}); return createCall(getMulModFunc(), {_arg1, _arg2, _arg3});
} }
@ -400,7 +505,7 @@ extern "C"
{ {
EXPORT void debug(uint64_t a, uint64_t b, uint64_t c, uint64_t d, char z) EXPORT void debug(uint64_t a, uint64_t b, uint64_t c, uint64_t d, char z)
{ {
std::cerr << "DEBUG " << std::dec << z << ": " //<< d << c << b << a 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"; << " [" << std::hex << std::setfill('0') << std::setw(16) << d << std::setw(16) << c << std::setw(16) << b << std::setw(16) << a << "]\n";
} }
} }

320
evmjit/libevmjit/Array.cpp

@ -0,0 +1,320 @@
#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 "Runtime.h"
#include "Utils.h"
#include <set> // DEBUG only
namespace dev
{
namespace eth
{
namespace jit
{
static const auto c_reallocStep = 1;
static const auto c_reallocMultipier = 2;
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(arrayPtr, 0, "dataPtr");
auto sizePtr = m_builder.CreateStructGEP(arrayPtr, 1, "sizePtr");
auto capPtr = m_builder.CreateStructGEP(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");
//newCap = m_builder.CreateNUWMul(newCap, m_builder.getInt64(c_reallocMultipier));
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(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(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(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(arrayPtr, 1, "sizePtr");
auto capPtr = m_builder.CreateStructGEP(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_pushFunc([this](){ return createArrayPushFunc(); }),
m_setFunc([this](){ return createArraySetFunc(); }),
m_getFunc([this](){ return createArrayGetFunc(); }),
m_freeFunc([this](){ return createFreeFunc(); })
{
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_pushFunc([this](){ return createArrayPushFunc(); }),
m_setFunc([this](){ return createArraySetFunc(); }),
m_getFunc([this](){ return createArrayGetFunc(); }),
m_freeFunc([this](){ return createFreeFunc(); })
{
m_builder.CreateStore(llvm::ConstantAggregateZero::get(getType()), m_array);
}
void Array::pop(llvm::Value* _count)
{
auto sizePtr = m_builder.CreateStructGEP(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(_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});
}
}
}
}
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;
}
EXPORT void ext_free(void* _data) noexcept
{
std::free(_data);
if (_data)
{
DLOG(mem) << "FREE : " << _data << "\n";
watchdog.allocatedMemory.erase(_data);
}
}
} // extern "C"

74
evmjit/libevmjit/Array.h

@ -0,0 +1,74 @@
#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(); }}; // TODO: If works on MSVC, remove form initialization list
LazyFunction m_setFunc;
LazyFunction m_getPtrFunc = {[this](){ return createGetPtrFunc(); }};
LazyFunction m_getFunc;
LazyFunction m_freeFunc;
LazyFunction m_extendFunc = {[this](){ return createExtendFunc(); }};
LazyFunction m_reallocFunc = {[this](){ return getReallocFunc(); }};
};
}
}
}

65
evmjit/libevmjit/BasicBlock.cpp

@ -11,6 +11,7 @@
#include "preprocessor/llvm_includes_end.h" #include "preprocessor/llvm_includes_end.h"
#include "Type.h" #include "Type.h"
#include "Utils.h"
namespace dev namespace dev
{ {
@ -136,32 +137,34 @@ void BasicBlock::synchronizeLocalStack(Stack& _evmStack)
{ {
auto blockTerminator = m_llvmBB->getTerminator(); auto blockTerminator = m_llvmBB->getTerminator();
assert(blockTerminator != nullptr); assert(blockTerminator != nullptr);
m_builder.SetInsertPoint(blockTerminator); if (blockTerminator->getOpcode() != llvm::Instruction::Ret)
{
// Not needed in case of ret instruction. Ret also invalidates the stack.
m_builder.SetInsertPoint(blockTerminator);
auto currIter = m_currentStack.begin(); auto currIter = m_currentStack.begin();
auto endIter = m_currentStack.end(); auto endIter = m_currentStack.end();
// Update (emit set()) changed values // Update (emit set()) changed values
for (int idx = (int)m_currentStack.size() - 1 - m_tosOffset; for (int idx = (int)m_currentStack.size() - 1 - m_tosOffset;
currIter < endIter && idx >= 0; currIter < endIter && idx >= 0;
++currIter, --idx) ++currIter, --idx)
{ {
assert(static_cast<size_t>(idx) < m_initialStack.size()); assert(static_cast<size_t>(idx) < m_initialStack.size());
if (*currIter != m_initialStack[idx]) // value needs update if (*currIter != m_initialStack[idx]) // value needs update
_evmStack.set(static_cast<size_t>(idx), *currIter); _evmStack.set(static_cast<size_t>(idx), *currIter);
} }
if (m_tosOffset < 0)
{
// Pop values // Pop values
_evmStack.pop(static_cast<size_t>(-m_tosOffset)); if (m_tosOffset < 0)
} _evmStack.pop(static_cast<size_t>(-m_tosOffset));
// Push new values // Push new values
for (; currIter < endIter; ++currIter) for (; currIter < endIter; ++currIter)
{ {
assert(*currIter != nullptr); assert(*currIter != nullptr);
_evmStack.push(*currIter); _evmStack.push(*currIter);
}
} }
// Emit get() for all (used) values from the initial stack // Emit get() for all (used) values from the initial stack
@ -233,7 +236,7 @@ void BasicBlock::linkLocalStacks(std::vector<BasicBlock*> basicBlocks, llvm::IRB
for (auto predIt = llvm::pred_begin(bb); predIt != llvm::pred_end(bb); ++predIt) for (auto predIt = llvm::pred_begin(bb); predIt != llvm::pred_end(bb); ++predIt)
{ {
auto predInfoEntry = cfg.find(*predIt); auto predInfoEntry = cfg.find(*predIt);
if (predInfoEntry != cfg.end()) if (predInfoEntry != cfg.end()) // FIXME: It is wrong - will skip entry block
info.predecessors.push_back(&predInfoEntry->second); info.predecessors.push_back(&predInfoEntry->second);
} }
} }
@ -242,13 +245,12 @@ void BasicBlock::linkLocalStacks(std::vector<BasicBlock*> basicBlocks, llvm::IRB
bool valuesChanged = true; bool valuesChanged = true;
while (valuesChanged) while (valuesChanged)
{ {
if (getenv("EVMCC_DEBUG_BLOCKS")) for (auto& pair : cfg)
{ {
for (auto& pair : cfg) DLOG(bb) << pair.second.bblock.llvm()->getName().str()
std::cerr << pair.second.bblock.llvm()->getName().str() << ": in " << pair.second.inputItems
<< ": in " << pair.second.inputItems << ", out " << pair.second.outputItems
<< ", out " << pair.second.outputItems << "\n";
<< "\n";
} }
valuesChanged = false; valuesChanged = false;
@ -256,6 +258,9 @@ void BasicBlock::linkLocalStacks(std::vector<BasicBlock*> basicBlocks, llvm::IRB
{ {
auto& info = pair.second; auto& info = pair.second;
if (&info.bblock == basicBlocks.front())
info.inputItems = 0; // we cannot use phi nodes for first block as it is a successor of entry block
if (info.predecessors.empty()) if (info.predecessors.empty())
info.inputItems = 0; // no consequences for other blocks, so leave valuesChanged false info.inputItems = 0; // no consequences for other blocks, so leave valuesChanged false
@ -340,6 +345,8 @@ void BasicBlock::dump(std::ostream& _out, bool _dotOutput)
{ {
if (val == nullptr) if (val == nullptr)
out << " ?"; out << " ?";
else if (llvm::isa<llvm::ExtractValueInst>(val))
out << " " << val->getName();
else if (llvm::isa<llvm::Instruction>(val)) else if (llvm::isa<llvm::Instruction>(val))
out << *val; out << *val;
else else
@ -361,6 +368,8 @@ void BasicBlock::dump(std::ostream& _out, bool _dotOutput)
{ {
if (*val == nullptr) if (*val == nullptr)
out << " ?"; out << " ?";
else if (llvm::isa<llvm::ExtractValueInst>(*val))
out << " " << (*val)->getName();
else if (llvm::isa<llvm::Instruction>(*val)) else if (llvm::isa<llvm::Instruction>(*val))
out << **val; out << **val;
else else

1
evmjit/libevmjit/BuildInfo.h.in

@ -8,3 +8,4 @@
#define LLVM_VERSION "${LLVM_PACKAGE_VERSION}" #define LLVM_VERSION "${LLVM_PACKAGE_VERSION}"
#define LLVM_ASSERTIONS "${LLVM_ENABLE_ASSERTIONS}" #define LLVM_ASSERTIONS "${LLVM_ENABLE_ASSERTIONS}"
#define LLVM_DEBUG ${LLVM_DEBUG}

35
evmjit/libevmjit/CMakeLists.txt

@ -1,9 +1,29 @@
set(TARGET_NAME evmjit) set(TARGET_NAME evmjit)
file(GLOB SOURCES "*.cpp") set(SOURCES
file(GLOB HEADERS "*.h") Arith256.cpp Arith256.h
set(INTERFACE_HEADERS interface.h) Array.cpp Array.h
source_group("" FILES ${HEADERS}) 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
ExecutionEngine.cpp ExecutionEngine.h
Ext.cpp Ext.h
GasMeter.cpp GasMeter.h
Instruction.cpp Instruction.h
interface.cpp interface.h
Memory.cpp Memory.h
Optimizer.cpp Optimizer.h
Runtime.cpp Runtime.h
RuntimeData.h
RuntimeManager.cpp RuntimeManager.h
Stack.cpp Stack.h
Type.cpp Type.h
Utils.cpp Utils.h
)
source_group("" FILES ${SOURCES}) source_group("" FILES ${SOURCES})
if(${CMAKE_CXX_COMPILER_ID} STREQUAL "MSVC") if(${CMAKE_CXX_COMPILER_ID} STREQUAL "MSVC")
@ -48,11 +68,13 @@ else()
set(EVMJIT_SOVERSION ${EVMJIT_VERSION_MAJOR}) set(EVMJIT_SOVERSION ${EVMJIT_VERSION_MAJOR})
endif() endif()
string(COMPARE EQUAL "${LLVM_ENABLE_ASSERTIONS}" "ON" LLVM_DEBUG)
configure_file(BuildInfo.h.in ${CMAKE_CURRENT_BINARY_DIR}/gen/BuildInfo.gen.h) 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} ${EVMJIT_VERSION_PRERELEASE} (${EVMJIT_VERSION_FULL})") message(STATUS "EVM JIT version: ${EVMJIT_VERSION_MAJOR}.${EVMJIT_VERSION_MINOR}.${EVMJIT_VERSION_PATCH} ${EVMJIT_VERSION_PRERELEASE} (${EVMJIT_VERSION_FULL})")
add_library(${TARGET_NAME} SHARED ${SOURCES} ${HEADERS} gen/BuildInfo.gen.h) add_library(${TARGET_NAME} SHARED ${SOURCES} gen/BuildInfo.gen.h)
set_target_properties(${TARGET_NAME} PROPERTIES set_target_properties(${TARGET_NAME} PROPERTIES
VERSION ${EVMJIT_VERSION} SOVERSION ${EVMJIT_SOVERSION} VERSION ${EVMJIT_VERSION} SOVERSION ${EVMJIT_SOVERSION}
FOLDER "libs") FOLDER "libs")
@ -62,7 +84,4 @@ include_directories(${CMAKE_CURRENT_BINARY_DIR}/gen)
target_link_libraries(${TARGET_NAME} PRIVATE ${LLVM_LIBS}) target_link_libraries(${TARGET_NAME} PRIVATE ${LLVM_LIBS})
#set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -fno-rtti")
install(TARGETS ${TARGET_NAME} LIBRARY DESTINATION lib ARCHIVE DESTINATION lib RUNTIME DESTINATION bin) install(TARGETS ${TARGET_NAME} LIBRARY DESTINATION lib ARCHIVE DESTINATION lib RUNTIME DESTINATION bin)
#install(FILES ${INTERFACE_HEADERS} DESTINATION include/${TARGET_NAME})

81
evmjit/libevmjit/Cache.cpp

@ -1,8 +1,5 @@
#include "Cache.h" #include "Cache.h"
#include <iostream>
#include <cassert>
#include "preprocessor/llvm_includes_start.h" #include "preprocessor/llvm_includes_start.h"
#include <llvm/IR/Module.h> #include <llvm/IR/Module.h>
#include <llvm/IR/LLVMContext.h> #include <llvm/IR/LLVMContext.h>
@ -14,6 +11,7 @@
#include "ExecutionEngine.h" #include "ExecutionEngine.h"
#include "Utils.h" #include "Utils.h"
#include "BuildInfo.gen.h"
namespace dev namespace dev
{ {
@ -22,28 +20,54 @@ namespace eth
namespace jit namespace jit
{ {
//#define CACHE_LOG std::cerr << "CACHE "
#define CACHE_LOG std::ostream(nullptr)
namespace namespace
{ {
CacheMode g_mode;
llvm::MemoryBuffer* g_lastObject; llvm::MemoryBuffer* g_lastObject;
ExecutionEngineListener* g_listener; ExecutionEngineListener* g_listener;
static const size_t c_versionStampLength = 32;
llvm::StringRef getLibVersionStamp()
{
static auto version = llvm::SmallString<c_versionStampLength>{};
if (version.empty())
{
version = EVMJIT_VERSION_FULL;
version.resize(c_versionStampLength);
}
return version;
}
} }
ObjectCache* Cache::getObjectCache(ExecutionEngineListener* _listener) ObjectCache* Cache::getObjectCache(CacheMode _mode, ExecutionEngineListener* _listener)
{ {
static ObjectCache objectCache; static ObjectCache objectCache;
g_mode = _mode;
g_listener = _listener; g_listener = _listener;
return &objectCache; return &objectCache;
} }
void Cache::clear()
{
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());
}
std::unique_ptr<llvm::Module> Cache::getObject(std::string const& id) std::unique_ptr<llvm::Module> Cache::getObject(std::string const& id)
{ {
if (g_mode != CacheMode::on && g_mode != CacheMode::read)
return nullptr;
if (g_listener) if (g_listener)
g_listener->stateChanged(ExecState::CacheLoad); g_listener->stateChanged(ExecState::CacheLoad);
CACHE_LOG << id << ": search\n"; DLOG(cache) << id << ": search\n";
if (!CHECK(!g_lastObject)) if (!CHECK(!g_lastObject))
g_lastObject = nullptr; g_lastObject = nullptr;
@ -51,28 +75,21 @@ std::unique_ptr<llvm::Module> Cache::getObject(std::string const& id)
llvm::sys::path::system_temp_directory(false, cachePath); llvm::sys::path::system_temp_directory(false, cachePath);
llvm::sys::path::append(cachePath, "evm_objs", id); llvm::sys::path::append(cachePath, "evm_objs", id);
#if defined(__GNUC__) && !defined(NDEBUG)
llvm::sys::fs::file_status st;
auto err = llvm::sys::fs::status(cachePath.str(), st);
if (err)
return nullptr;
auto mtime = st.getLastModificationTime().toEpochTime();
std::tm tm;
strptime(__DATE__ __TIME__, " %b %d %Y %H:%M:%S", &tm);
auto btime = (uint64_t)std::mktime(&tm);
if (btime > mtime)
return nullptr;
#endif
if (auto r = llvm::MemoryBuffer::getFile(cachePath.str(), -1, false)) if (auto r = llvm::MemoryBuffer::getFile(cachePath.str(), -1, false))
g_lastObject = llvm::MemoryBuffer::getMemBufferCopy(r.get()->getBuffer()); {
auto& buf = r.get();
auto objVersionStamp = buf->getBufferSize() >= c_versionStampLength ? llvm::StringRef{buf->getBufferEnd() - c_versionStampLength, c_versionStampLength} : llvm::StringRef{};
if (objVersionStamp == getLibVersionStamp())
g_lastObject = llvm::MemoryBuffer::getMemBufferCopy(r.get()->getBuffer());
else
DLOG(cache) << "Unmatched version: " << objVersionStamp.str() << ", expected " << getLibVersionStamp().str() << "\n";
}
else if (r.getError() != std::make_error_code(std::errc::no_such_file_or_directory)) else if (r.getError() != std::make_error_code(std::errc::no_such_file_or_directory))
std::cerr << r.getError().message(); // TODO: Add log DLOG(cache) << r.getError().message(); // TODO: Add warning log
if (g_lastObject) // if object found create fake module if (g_lastObject) // if object found create fake module
{ {
CACHE_LOG << id << ": found\n"; DLOG(cache) << id << ": found\n";
auto&& context = llvm::getGlobalContext(); auto&& context = llvm::getGlobalContext();
auto module = std::unique_ptr<llvm::Module>(new llvm::Module(id, context)); auto module = std::unique_ptr<llvm::Module>(new llvm::Module(id, context));
auto mainFuncType = llvm::FunctionType::get(llvm::Type::getVoidTy(context), {}, false); auto mainFuncType = llvm::FunctionType::get(llvm::Type::getVoidTy(context), {}, false);
@ -81,13 +98,17 @@ std::unique_ptr<llvm::Module> Cache::getObject(std::string const& id)
bb->getInstList().push_back(new llvm::UnreachableInst{context}); bb->getInstList().push_back(new llvm::UnreachableInst{context});
return module; return module;
} }
CACHE_LOG << id << ": not found\n"; DLOG(cache) << id << ": not found\n";
return nullptr; return nullptr;
} }
void ObjectCache::notifyObjectCompiled(llvm::Module const* _module, llvm::MemoryBuffer const* _object) void ObjectCache::notifyObjectCompiled(llvm::Module const* _module, llvm::MemoryBuffer const* _object)
{ {
// Only in "on" and "write" mode
if (g_mode != CacheMode::on && g_mode != CacheMode::write)
return;
if (g_listener) if (g_listener)
g_listener->stateChanged(ExecState::CacheWrite); g_listener->stateChanged(ExecState::CacheWrite);
@ -97,19 +118,19 @@ void ObjectCache::notifyObjectCompiled(llvm::Module const* _module, llvm::Memory
llvm::sys::path::append(cachePath, "evm_objs"); llvm::sys::path::append(cachePath, "evm_objs");
if (llvm::sys::fs::create_directory(cachePath.str())) if (llvm::sys::fs::create_directory(cachePath.str()))
return; // TODO: Add log DLOG(cache) << "Cannot create cache dir " << cachePath.str().str() << "\n";
llvm::sys::path::append(cachePath, id); llvm::sys::path::append(cachePath, id);
CACHE_LOG << id << ": write\n"; DLOG(cache) << id << ": write\n";
std::string error; std::string error;
llvm::raw_fd_ostream cacheFile(cachePath.c_str(), error, llvm::sys::fs::F_None); llvm::raw_fd_ostream cacheFile(cachePath.c_str(), error, llvm::sys::fs::F_None);
cacheFile << _object->getBuffer(); cacheFile << _object->getBuffer() << getLibVersionStamp();
} }
llvm::MemoryBuffer* ObjectCache::getObject(llvm::Module const* _module) llvm::MemoryBuffer* ObjectCache::getObject(llvm::Module const* _module)
{ {
CACHE_LOG << _module->getModuleIdentifier() << ": use\n"; DLOG(cache) << _module->getModuleIdentifier() << ": use\n";
auto o = g_lastObject; auto o = g_lastObject;
g_lastObject = nullptr; g_lastObject = nullptr;
return o; return o;

14
evmjit/libevmjit/Cache.h

@ -12,6 +12,15 @@ namespace jit
{ {
class ExecutionEngineListener; class ExecutionEngineListener;
enum class CacheMode
{
on,
off,
read,
write,
clear
};
class ObjectCache : public llvm::ObjectCache class ObjectCache : public llvm::ObjectCache
{ {
public: public:
@ -29,8 +38,11 @@ public:
class Cache class Cache
{ {
public: public:
static ObjectCache* getObjectCache(ExecutionEngineListener* _listener); static ObjectCache* getObjectCache(CacheMode _mode, ExecutionEngineListener* _listener);
static std::unique_ptr<llvm::Module> getObject(std::string const& id); static std::unique_ptr<llvm::Module> getObject(std::string const& id);
/// Clears cache storage
static void clear();
}; };
} }

8
evmjit/libevmjit/Common.h

@ -1,11 +1,12 @@
#pragma once #pragma once
#include <vector>
#include <tuple> #include <tuple>
#include <cstdint> #include <cstdint>
#ifdef _MSC_VER #ifdef _MSC_VER
#define EXPORT __declspec(dllexport) #define EXPORT __declspec(dllexport)
#define _ALLOW_KEYWORD_MACROS
#define noexcept throw()
#else #else
#define EXPORT #define EXPORT
#endif #endif
@ -18,12 +19,9 @@ namespace jit
{ {
using byte = uint8_t; using byte = uint8_t;
using bytes = std::vector<byte>;
using bytes_ref = std::tuple<byte const*, size_t>; using bytes_ref = std::tuple<byte const*, size_t>;
using code_iterator = byte const*; using code_iterator = byte const*;
struct NoteChannel {}; // FIXME: Use some log library?
enum class ReturnCode enum class ReturnCode
{ {
// Success codes // Success codes

73
evmjit/libevmjit/Compiler.cpp

@ -6,13 +6,9 @@
#include <sstream> #include <sstream>
#include "preprocessor/llvm_includes_start.h" #include "preprocessor/llvm_includes_start.h"
#include <llvm/ADT/PostOrderIterator.h>
#include <llvm/IR/CFG.h> #include <llvm/IR/CFG.h>
#include <llvm/IR/Module.h> #include <llvm/IR/Module.h>
#include <llvm/IR/IntrinsicInst.h> #include <llvm/IR/IntrinsicInst.h>
#include <llvm/IR/MDBuilder.h>
#include <llvm/PassManager.h>
#include <llvm/Transforms/Scalar.h>
#include "preprocessor/llvm_includes_end.h" #include "preprocessor/llvm_includes_end.h"
#include "Instruction.h" #include "Instruction.h"
@ -93,7 +89,7 @@ void Compiler::createBasicBlocks(code_iterator _codeBegin, code_iterator _codeEn
} }
} }
llvm::BasicBlock* Compiler::getJumpTableBlock() llvm::BasicBlock* Compiler::getJumpTableBlock(RuntimeManager& _runtimeManager)
{ {
if (!m_jumpTableBlock) if (!m_jumpTableBlock)
{ {
@ -101,7 +97,7 @@ llvm::BasicBlock* Compiler::getJumpTableBlock()
InsertPointGuard g{m_builder}; InsertPointGuard g{m_builder};
m_builder.SetInsertPoint(m_jumpTableBlock->llvm()); m_builder.SetInsertPoint(m_jumpTableBlock->llvm());
auto dest = m_builder.CreatePHI(Type::Word, 8, "target"); auto dest = m_builder.CreatePHI(Type::Word, 8, "target");
auto switchInstr = m_builder.CreateSwitch(dest, getBadJumpBlock()); auto switchInstr = m_builder.CreateSwitch(dest, getBadJumpBlock(_runtimeManager));
for (auto&& p : m_basicBlocks) for (auto&& p : m_basicBlocks)
{ {
if (p.second.isJumpDest()) if (p.second.isJumpDest())
@ -111,21 +107,20 @@ llvm::BasicBlock* Compiler::getJumpTableBlock()
return m_jumpTableBlock->llvm(); return m_jumpTableBlock->llvm();
} }
llvm::BasicBlock* Compiler::getBadJumpBlock() llvm::BasicBlock* Compiler::getBadJumpBlock(RuntimeManager& _runtimeManager)
{ {
if (!m_badJumpBlock) if (!m_badJumpBlock)
{ {
m_badJumpBlock.reset(new BasicBlock("BadJump", m_mainFunc, m_builder, true)); m_badJumpBlock.reset(new BasicBlock("BadJump", m_mainFunc, m_builder, true));
InsertPointGuard g{m_builder}; InsertPointGuard g{m_builder};
m_builder.SetInsertPoint(m_badJumpBlock->llvm()); m_builder.SetInsertPoint(m_badJumpBlock->llvm());
m_builder.CreateRet(Constant::get(ReturnCode::BadJumpDestination)); _runtimeManager.exit(ReturnCode::BadJumpDestination);
} }
return m_badJumpBlock->llvm(); return m_badJumpBlock->llvm();
} }
std::unique_ptr<llvm::Module> Compiler::compile(code_iterator _begin, code_iterator _end, std::string const& _id) std::unique_ptr<llvm::Module> Compiler::compile(code_iterator _begin, code_iterator _end, std::string const& _id)
{ {
auto compilationStartTime = std::chrono::high_resolution_clock::now();
auto module = std::unique_ptr<llvm::Module>(new llvm::Module(_id, m_builder.getContext())); auto module = std::unique_ptr<llvm::Module>(new llvm::Module(_id, m_builder.getContext()));
// Create main function // Create main function
@ -137,6 +132,17 @@ std::unique_ptr<llvm::Module> Compiler::compile(code_iterator _begin, code_itera
auto entryBlock = llvm::BasicBlock::Create(m_builder.getContext(), {}, m_mainFunc); auto entryBlock = llvm::BasicBlock::Create(m_builder.getContext(), {}, m_mainFunc);
m_builder.SetInsertPoint(entryBlock); m_builder.SetInsertPoint(entryBlock);
createBasicBlocks(_begin, _end);
// 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);
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 jmpBufWords = m_builder.CreateAlloca(Type::BytePtr, m_builder.getInt64(3), "jmpBuf.words");
auto frameaddress = llvm::Intrinsic::getDeclaration(module.get(), llvm::Intrinsic::frameaddress); auto frameaddress = llvm::Intrinsic::getDeclaration(module.get(), llvm::Intrinsic::frameaddress);
auto fp = m_builder.CreateCall(frameaddress, m_builder.getInt32(0), "fp"); auto fp = m_builder.CreateCall(frameaddress, m_builder.getInt32(0), "fp");
@ -149,24 +155,14 @@ std::unique_ptr<llvm::Module> Compiler::compile(code_iterator _begin, code_itera
auto jmpBuf = m_builder.CreateBitCast(jmpBufWords, Type::BytePtr, "jmpBuf"); auto jmpBuf = m_builder.CreateBitCast(jmpBufWords, Type::BytePtr, "jmpBuf");
auto r = m_builder.CreateCall(setjmp, jmpBuf); auto r = m_builder.CreateCall(setjmp, jmpBuf);
auto normalFlow = m_builder.CreateICmpEQ(r, m_builder.getInt32(0)); auto normalFlow = m_builder.CreateICmpEQ(r, m_builder.getInt32(0));
runtimeManager.setJmpBuf(jmpBuf);
createBasicBlocks(_begin, _end);
// Init runtime structures.
RuntimeManager runtimeManager(m_builder, jmpBuf, _begin, _end);
GasMeter gasMeter(m_builder, runtimeManager);
Memory memory(runtimeManager, gasMeter);
Ext ext(runtimeManager, memory);
Stack stack(m_builder, runtimeManager);
Arith256 arith(m_builder);
// TODO: Create Stop basic block on demand // TODO: Create Stop basic block on demand
m_stopBB = llvm::BasicBlock::Create(m_mainFunc->getContext(), "Stop", m_mainFunc); m_stopBB = llvm::BasicBlock::Create(m_mainFunc->getContext(), "Stop", m_mainFunc);
auto abortBB = llvm::BasicBlock::Create(m_mainFunc->getContext(), "Abort", m_mainFunc); auto abortBB = llvm::BasicBlock::Create(m_mainFunc->getContext(), "Abort", m_mainFunc);
auto firstBB = m_basicBlocks.empty() ? m_stopBB : m_basicBlocks.begin()->second.llvm(); auto firstBB = m_basicBlocks.empty() ? m_stopBB : m_basicBlocks.begin()->second.llvm();
auto expectTrue = llvm::MDBuilder{m_builder.getContext()}.createBranchWeights(1, 0); m_builder.CreateCondBr(normalFlow, firstBB, abortBB, Type::expectTrue);
m_builder.CreateCondBr(normalFlow, firstBB, abortBB, expectTrue);
for (auto basicBlockPairIt = m_basicBlocks.begin(); basicBlockPairIt != m_basicBlocks.end(); ++basicBlockPairIt) for (auto basicBlockPairIt = m_basicBlocks.begin(); basicBlockPairIt != m_basicBlocks.end(); ++basicBlockPairIt)
{ {
@ -180,10 +176,10 @@ std::unique_ptr<llvm::Module> Compiler::compile(code_iterator _begin, code_itera
// Code for special blocks: // Code for special blocks:
// TODO: move to separate function. // TODO: move to separate function.
m_builder.SetInsertPoint(m_stopBB); m_builder.SetInsertPoint(m_stopBB);
m_builder.CreateRet(Constant::get(ReturnCode::Stop)); runtimeManager.exit(ReturnCode::Stop);
m_builder.SetInsertPoint(abortBB); m_builder.SetInsertPoint(abortBB);
m_builder.CreateRet(Constant::get(ReturnCode::OutOfGas)); runtimeManager.exit(ReturnCode::OutOfGas);
removeDeadBlocks(); removeDeadBlocks();
@ -230,16 +226,6 @@ std::unique_ptr<llvm::Module> Compiler::compile(code_iterator _begin, code_itera
dumpCFGifRequired("blocks-sync.dot"); dumpCFGifRequired("blocks-sync.dot");
if (m_jumpTableBlock && m_options.rewriteSwitchToBranches)
{
llvm::FunctionPassManager fpManager(module.get());
fpManager.add(llvm::createLowerSwitchPass());
fpManager.doInitialization();
fpManager.run(*m_mainFunc);
}
auto compilationEndTime = std::chrono::high_resolution_clock::now();
clog(JIT) << "JIT: " << std::chrono::duration_cast<std::chrono::milliseconds>(compilationEndTime - compilationStartTime).count();
return module; return module;
} }
@ -600,7 +586,7 @@ void Compiler::compileBasicBlock(BasicBlock& _basicBlock, RuntimeManager& _runti
auto&& c = constant->getValue(); auto&& c = constant->getValue();
auto targetIdx = c.getActiveBits() <= 64 ? c.getZExtValue() : -1; auto targetIdx = c.getActiveBits() <= 64 ? c.getZExtValue() : -1;
auto it = m_basicBlocks.find(targetIdx); auto it = m_basicBlocks.find(targetIdx);
targetBlock = (it != m_basicBlocks.end() && it->second.isJumpDest()) ? it->second.llvm() : getBadJumpBlock(); targetBlock = (it != m_basicBlocks.end() && it->second.isJumpDest()) ? it->second.llvm() : getBadJumpBlock(_runtimeManager);
} }
// TODO: Improve; check for constants // TODO: Improve; check for constants
@ -613,7 +599,7 @@ void Compiler::compileBasicBlock(BasicBlock& _basicBlock, RuntimeManager& _runti
else else
{ {
_basicBlock.setJumpTarget(target); _basicBlock.setJumpTarget(target);
m_builder.CreateBr(getJumpTableBlock()); m_builder.CreateBr(getJumpTableBlock(_runtimeManager));
} }
} }
else // JUMPI else // JUMPI
@ -629,7 +615,7 @@ void Compiler::compileBasicBlock(BasicBlock& _basicBlock, RuntimeManager& _runti
else else
{ {
_basicBlock.setJumpTarget(target); _basicBlock.setJumpTarget(target);
m_builder.CreateCondBr(cond, getJumpTableBlock(), _nextBasicBlock); m_builder.CreateCondBr(cond, getJumpTableBlock(_runtimeManager), _nextBasicBlock);
} }
} }
break; break;
@ -769,7 +755,7 @@ void Compiler::compileBasicBlock(BasicBlock& _basicBlock, RuntimeManager& _runti
case Instruction::CALL: case Instruction::CALL:
case Instruction::CALLCODE: case Instruction::CALLCODE:
{ {
auto callGas256 = stack.pop(); auto callGas = stack.pop();
auto codeAddress = stack.pop(); auto codeAddress = stack.pop();
auto value = stack.pop(); auto value = stack.pop();
auto inOff = stack.pop(); auto inOff = stack.pop();
@ -787,13 +773,8 @@ void Compiler::compileBasicBlock(BasicBlock& _basicBlock, RuntimeManager& _runti
if (inst == Instruction::CALLCODE) if (inst == Instruction::CALLCODE)
receiveAddress = _runtimeManager.get(RuntimeData::Address); receiveAddress = _runtimeManager.get(RuntimeData::Address);
auto gas = _runtimeManager.getGas(); auto ret = _ext.call(callGas, receiveAddress, value, inOff, inSize, outOff, outSize, codeAddress);
_gasMeter.count(callGas256); _gasMeter.count(m_builder.getInt64(0), _runtimeManager.getJmpBuf(), _runtimeManager.getGasPtr());
auto callGas = m_builder.CreateTrunc(callGas256, Type::Gas);
auto gasLeft = m_builder.CreateNSWSub(gas, callGas);
_runtimeManager.setGas(callGas);
auto ret = _ext.call(receiveAddress, value, inOff, inSize, outOff, outSize, codeAddress);
_gasMeter.giveBack(gasLeft);
stack.push(ret); stack.push(ret);
break; break;
} }
@ -806,14 +787,14 @@ void Compiler::compileBasicBlock(BasicBlock& _basicBlock, RuntimeManager& _runti
_memory.require(index, size); _memory.require(index, size);
_runtimeManager.registerReturnData(index, size); _runtimeManager.registerReturnData(index, size);
m_builder.CreateRet(Constant::get(ReturnCode::Return)); _runtimeManager.exit(ReturnCode::Return);
break; break;
} }
case Instruction::SUICIDE: case Instruction::SUICIDE:
{ {
_runtimeManager.registerSuicide(stack.pop()); _runtimeManager.registerSuicide(stack.pop());
m_builder.CreateRet(Constant::get(ReturnCode::Suicide)); _runtimeManager.exit(ReturnCode::Suicide);
break; break;
} }

4
evmjit/libevmjit/Compiler.h

@ -38,9 +38,9 @@ private:
void compileBasicBlock(BasicBlock& _basicBlock, class RuntimeManager& _runtimeManager, class Arith256& _arith, class Memory& _memory, class Ext& _ext, class GasMeter& _gasMeter, llvm::BasicBlock* _nextBasicBlock); void compileBasicBlock(BasicBlock& _basicBlock, class RuntimeManager& _runtimeManager, class Arith256& _arith, class Memory& _memory, class Ext& _ext, class GasMeter& _gasMeter, llvm::BasicBlock* _nextBasicBlock);
llvm::BasicBlock* getJumpTableBlock(); llvm::BasicBlock* getJumpTableBlock(RuntimeManager& _runtimeManager);
llvm::BasicBlock* getBadJumpBlock(); llvm::BasicBlock* getBadJumpBlock(RuntimeManager& _runtimeManager);
void removeDeadBlocks(); void removeDeadBlocks();

9
evmjit/libevmjit/Endianness.cpp

@ -2,6 +2,7 @@
#include "preprocessor/llvm_includes_start.h" #include "preprocessor/llvm_includes_start.h"
#include <llvm/IR/IntrinsicInst.h> #include <llvm/IR/IntrinsicInst.h>
#include <llvm/Support/Host.h>
#include "preprocessor/llvm_includes_end.h" #include "preprocessor/llvm_includes_end.h"
#include "Type.h" #include "Type.h"
@ -15,13 +16,7 @@ namespace jit
llvm::Value* Endianness::bswapIfLE(llvm::IRBuilder<>& _builder, llvm::Value* _word) llvm::Value* Endianness::bswapIfLE(llvm::IRBuilder<>& _builder, llvm::Value* _word)
{ {
union tester if (llvm::sys::IsLittleEndianHost)
{
unsigned int x;
unsigned char isLE;
};
if (tester{1}.isLE)
{ {
// FIXME: Disabled because of problems with BYTE // FIXME: Disabled because of problems with BYTE
//if (auto constant = llvm::dyn_cast<llvm::ConstantInt>(_word)) //if (auto constant = llvm::dyn_cast<llvm::ConstantInt>(_word))

1
evmjit/libevmjit/ExecStats.cpp

@ -67,6 +67,7 @@ char const* getExecStateName(ExecState _state)
case ExecState::CacheLoad: return "CacheLoad"; case ExecState::CacheLoad: return "CacheLoad";
case ExecState::CacheWrite: return "CacheWrite"; case ExecState::CacheWrite: return "CacheWrite";
case ExecState::Compilation: return "Compilation"; case ExecState::Compilation: return "Compilation";
case ExecState::Optimization: return "Optimization";
case ExecState::CodeGen: return "CodeGen"; case ExecState::CodeGen: return "CodeGen";
case ExecState::Execution: return "Execution"; case ExecState::Execution: return "Execution";
case ExecState::Return: return "Return"; case ExecState::Return: return "Return";

1
evmjit/libevmjit/ExecStats.h

@ -1,5 +1,6 @@
#pragma once #pragma once
#include <vector>
#include <string> #include <string>
#include <chrono> #include <chrono>

91
evmjit/libevmjit/ExecutionEngine.cpp

@ -1,8 +1,9 @@
#include "ExecutionEngine.h" #include "ExecutionEngine.h"
#include <array> #include <array>
#include <cstdlib> // env options #include <mutex>
#include <iostream> #include <iostream>
#include <unordered_map>
#include "preprocessor/llvm_includes_start.h" #include "preprocessor/llvm_includes_start.h"
#include <llvm/IR/Module.h> #include <llvm/IR/Module.h>
@ -12,10 +13,13 @@
#include <llvm/ExecutionEngine/MCJIT.h> #include <llvm/ExecutionEngine/MCJIT.h>
#include <llvm/Support/TargetSelect.h> #include <llvm/Support/TargetSelect.h>
#include <llvm/Support/Host.h> #include <llvm/Support/Host.h>
#include <llvm/Support/CommandLine.h>
#include <llvm/Support/ManagedStatic.h>
#include "preprocessor/llvm_includes_end.h" #include "preprocessor/llvm_includes_end.h"
#include "Runtime.h" #include "Runtime.h"
#include "Compiler.h" #include "Compiler.h"
#include "Optimizer.h"
#include "Cache.h" #include "Cache.h"
#include "ExecStats.h" #include "ExecStats.h"
#include "Utils.h" #include "Utils.h"
@ -48,42 +52,58 @@ std::string codeHash(i256 const& _hash)
return str; return str;
} }
bool getEnvOption(char const* _name, bool _default) void printVersion()
{ {
auto var = std::getenv(_name); std::cout << "Ethereum EVM JIT Compiler (http://github.com/ethereum/evmjit):\n"
if (!var) << " EVMJIT version " << EVMJIT_VERSION << "\n"
return _default; #ifdef NDEBUG
return std::strtol(var, nullptr, 10) != 0; << " Optimized build, " EVMJIT_VERSION_FULL "\n"
#else
<< " DEBUG build, " EVMJIT_VERSION_FULL "\n"
#endif
<< " Built " << __DATE__ << " (" << __TIME__ << ")\n"
<< std::endl;
} }
bool showInfo() 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."),
clEnumValEnd)};
cl::opt<bool> g_stats{"st", cl::desc{"Statistics"}};
cl::opt<bool> g_dump{"dump", cl::desc{"Dump LLVM IR module"}};
void parseOptions()
{ {
auto show = getEnvOption("EVMJIT_INFO", false); static llvm::llvm_shutdown_obj shutdownObj{};
if (show) cl::AddExtraVersionPrinter(printVersion);
{ cl::ParseEnvironmentOptions("evmjit", "EVMJIT", "Ethereum EVM JIT Compiler");
std::cout << "The Ethereum EVM JIT " EVMJIT_VERSION_FULL " LLVM " LLVM_VERSION << std::endl;
}
return show;
} }
} }
ReturnCode ExecutionEngine::run(RuntimeData* _data, Env* _env) ReturnCode ExecutionEngine::run(RuntimeData* _data, Env* _env)
{ {
static auto debugDumpModule = getEnvOption("EVMJIT_DUMP", false); static std::once_flag flag;
static auto objectCacheEnabled = getEnvOption("EVMJIT_CACHE", true); std::call_once(flag, parseOptions);
static auto statsCollectingEnabled = getEnvOption("EVMJIT_STATS", false);
static auto infoShown = showInfo();
(void) infoShown;
std::unique_ptr<ExecStats> listener{new ExecStats}; std::unique_ptr<ExecStats> listener{new ExecStats};
listener->stateChanged(ExecState::Started); listener->stateChanged(ExecState::Started);
auto objectCache = objectCacheEnabled ? Cache::getObjectCache(listener.get()) : nullptr; auto objectCache = (g_cache != CacheMode::off && g_cache != CacheMode::clear) ? Cache::getObjectCache(g_cache, listener.get()) : nullptr;
static std::unique_ptr<llvm::ExecutionEngine> ee; static std::unique_ptr<llvm::ExecutionEngine> ee;
if (!ee) if (!ee)
{ {
if (g_cache == CacheMode::clear)
Cache::clear();
llvm::InitializeNativeTarget(); llvm::InitializeNativeTarget();
llvm::InitializeNativeTargetAsmPrinter(); llvm::InitializeNativeTargetAsmPrinter();
@ -91,7 +111,7 @@ ReturnCode ExecutionEngine::run(RuntimeData* _data, Env* _env)
llvm::EngineBuilder builder(module.get()); llvm::EngineBuilder builder(module.get());
builder.setEngineKind(llvm::EngineKind::JIT); builder.setEngineKind(llvm::EngineKind::JIT);
builder.setUseMCJIT(true); builder.setUseMCJIT(true);
builder.setOptLevel(llvm::CodeGenOpt::None); builder.setOptLevel(g_optimize ? llvm::CodeGenOpt::Default : llvm::CodeGenOpt::None);
auto triple = llvm::Triple(llvm::sys::getProcessTriple()); auto triple = llvm::Triple(llvm::sys::getProcessTriple());
if (triple.getOS() == llvm::Triple::OSType::Win32) if (triple.getOS() == llvm::Triple::OSType::Win32)
@ -108,9 +128,14 @@ ReturnCode ExecutionEngine::run(RuntimeData* _data, Env* _env)
static StatsCollector statsCollector; static StatsCollector statsCollector;
auto mainFuncName = codeHash(_data->codeHash); auto mainFuncName = codeHash(_data->codeHash);
Runtime runtime(_data, _env); // TODO: I don't know why but it must be created before getFunctionAddress() calls m_runtime.init(_data, _env);
EntryFuncPtr entryFuncPtr = nullptr;
static std::unordered_map<std::string, EntryFuncPtr> funcCache;
auto it = funcCache.find(mainFuncName);
if (it != funcCache.end())
entryFuncPtr = it->second;
auto entryFuncPtr = (EntryFuncPtr)ee->getFunctionAddress(mainFuncName);
if (!entryFuncPtr) if (!entryFuncPtr)
{ {
auto module = objectCache ? Cache::getObject(mainFuncName) : nullptr; auto module = objectCache ? Cache::getObject(mainFuncName) : nullptr;
@ -118,9 +143,15 @@ ReturnCode ExecutionEngine::run(RuntimeData* _data, Env* _env)
{ {
listener->stateChanged(ExecState::Compilation); listener->stateChanged(ExecState::Compilation);
assert(_data->code || !_data->codeSize); //TODO: Is it good idea to execute empty code? assert(_data->code || !_data->codeSize); //TODO: Is it good idea to execute empty code?
module = Compiler({}).compile(_data->code, _data->code + _data->codeSize, mainFuncName); module = Compiler{{}}.compile(_data->code, _data->code + _data->codeSize, mainFuncName);
if (g_optimize)
{
listener->stateChanged(ExecState::Optimization);
optimize(*module);
}
} }
if (debugDumpModule) if (g_dump)
module->dump(); module->dump();
ee->addModule(module.get()); ee->addModule(module.get());
@ -131,18 +162,18 @@ ReturnCode ExecutionEngine::run(RuntimeData* _data, Env* _env)
if (!CHECK(entryFuncPtr)) if (!CHECK(entryFuncPtr))
return ReturnCode::LLVMLinkError; return ReturnCode::LLVMLinkError;
funcCache[mainFuncName] = entryFuncPtr;
listener->stateChanged(ExecState::Execution); listener->stateChanged(ExecState::Execution);
auto returnCode = entryFuncPtr(&runtime); auto returnCode = entryFuncPtr(&m_runtime);
listener->stateChanged(ExecState::Return); listener->stateChanged(ExecState::Return);
if (returnCode == ReturnCode::Return) if (returnCode == ReturnCode::Return)
{ returnData = m_runtime.getReturnData(); // Save reference to return data
returnData = runtime.getReturnData(); // Save reference to return data
std::swap(m_memory, runtime.getMemory()); // Take ownership of memory
}
listener->stateChanged(ExecState::Finished); listener->stateChanged(ExecState::Finished);
if (statsCollectingEnabled) if (g_stats)
statsCollector.stats.push_back(std::move(listener)); statsCollector.stats.push_back(std::move(listener));
return returnCode; return returnCode;

7
evmjit/libevmjit/ExecutionEngine.h

@ -2,7 +2,7 @@
#include <memory> #include <memory>
#include "RuntimeData.h" #include "Runtime.h"
namespace dev namespace dev
{ {
@ -17,6 +17,7 @@ enum class ExecState
CacheLoad, CacheLoad,
CacheWrite, CacheWrite,
Compilation, Compilation,
Optimization,
CodeGen, CodeGen,
Execution, Execution,
Return, Return,
@ -50,9 +51,7 @@ public:
bytes_ref returnData; bytes_ref returnData;
private: private:
/// After execution, if RETURN used, memory is moved there Runtime m_runtime;
/// to allow client copy the returned data
bytes m_memory;
}; };
} }

10
evmjit/libevmjit/Ext.cpp

@ -41,7 +41,7 @@ std::array<FuncDesc, sizeOf<EnvFunc>::value> const& getEnvFuncDescs()
FuncDesc{"env_sha3", getFunctionType(Type::Void, {Type::BytePtr, Type::Size, 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_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_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::WordPtr, Type::WordPtr, Type::BytePtr, Type::Size, 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_log", getFunctionType(Type::Void, {Type::EnvPtr, Type::BytePtr, Type::Size, Type::WordPtr, Type::WordPtr, Type::WordPtr, Type::WordPtr})},
FuncDesc{"env_blockhash", getFunctionType(Type::Void, {Type::EnvPtr, Type::WordPtr, Type::WordPtr})}, FuncDesc{"env_blockhash", getFunctionType(Type::Void, {Type::EnvPtr, Type::WordPtr, Type::WordPtr})},
FuncDesc{"env_extcode", getFunctionType(Type::BytePtr, {Type::EnvPtr, Type::WordPtr, Type::Size->getPointerTo()})}, FuncDesc{"env_extcode", getFunctionType(Type::BytePtr, {Type::EnvPtr, Type::WordPtr, Type::Size->getPointerTo()})},
@ -136,7 +136,7 @@ llvm::Value* Ext::create(llvm::Value* _endowment, llvm::Value* _initOff, llvm::V
return address; return address;
} }
llvm::Value* Ext::call(llvm::Value* _receiveAddress, llvm::Value* _value, llvm::Value* _inOff, llvm::Value* _inSize, llvm::Value* _outOff, llvm::Value* _outSize, llvm::Value* _codeAddress) 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 receiveAddress = Endianness::toBE(m_builder, _receiveAddress);
auto inBeg = m_memoryMan.getBytePtr(_inOff); auto inBeg = m_memoryMan.getBytePtr(_inOff);
@ -144,7 +144,11 @@ llvm::Value* Ext::call(llvm::Value* _receiveAddress, llvm::Value* _value, llvm::
auto outBeg = m_memoryMan.getBytePtr(_outOff); auto outBeg = m_memoryMan.getBytePtr(_outOff);
auto outSize = m_builder.CreateTrunc(_outSize, Type::Size, "out.size"); auto outSize = m_builder.CreateTrunc(_outSize, Type::Size, "out.size");
auto codeAddress = Endianness::toBE(m_builder, _codeAddress); auto codeAddress = Endianness::toBE(m_builder, _codeAddress);
auto ret = createCall(EnvFunc::call, {getRuntimeManager().getEnvPtr(), getRuntimeManager().getGasPtr(), byPtr(receiveAddress), byPtr(_value), inBeg, inSize, outBeg, outSize, byPtr(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"); return m_builder.CreateZExt(ret, Type::Word, "ret");
} }

2
evmjit/libevmjit/Ext.h

@ -51,7 +51,7 @@ public:
llvm::Value* balance(llvm::Value* _address); llvm::Value* balance(llvm::Value* _address);
llvm::Value* calldataload(llvm::Value* _index); llvm::Value* calldataload(llvm::Value* _index);
llvm::Value* create(llvm::Value* _endowment, llvm::Value* _initOff, llvm::Value* _initSize); llvm::Value* create(llvm::Value* _endowment, llvm::Value* _initOff, llvm::Value* _initSize);
llvm::Value* call(llvm::Value* _receiveAddress, llvm::Value* _value, llvm::Value* _inOff, llvm::Value* _inSize, llvm::Value* _outOff, llvm::Value* _outSize, llvm::Value* _codeAddress); 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* blockhash(llvm::Value* _number);
llvm::Value* sha3(llvm::Value* _inOff, llvm::Value* _inSize); llvm::Value* sha3(llvm::Value* _inOff, llvm::Value* _inSize);

230
evmjit/libevmjit/GasMeter.cpp

@ -17,52 +17,113 @@ namespace jit
namespace // Helper functions namespace // Helper functions
{ {
int64_t const c_stepGas = 1; int64_t const c_stepGas[] = {0, 2, 3, 5, 8, 10, 20};
int64_t const c_balanceGas = 20; int64_t const c_expByteGas = 10;
int64_t const c_sha3Gas = 10; int64_t const c_sha3Gas = 30;
int64_t const c_sha3WordGas = 10; int64_t const c_sha3WordGas = 6;
int64_t const c_sloadGas = 20; int64_t const c_sloadGas = 50;
int64_t const c_sstoreSetGas = 300; int64_t const c_sstoreSetGas = 20000;
int64_t const c_sstoreResetGas = 100; int64_t const c_sstoreResetGas = 5000;
int64_t const c_sstoreRefundGas = 100; int64_t const c_sstoreClearGas = 5000;
int64_t const c_createGas = 100; int64_t const c_jumpdestGas = 1;
int64_t const c_createDataGas = 5; int64_t const c_logGas = 375;
int64_t const c_callGas = 20; int64_t const c_logTopicGas = 375;
int64_t const c_expGas = 1; int64_t const c_logDataGas = 8;
int64_t const c_expByteGas = 1; int64_t const c_callGas = 40;
int64_t const c_memoryGas = 1; int64_t const c_createGas = 32000;
int64_t const c_txDataZeroGas = 1; int64_t const c_memoryGas = 3;
int64_t const c_txDataNonZeroGas = 5; int64_t const c_copyGas = 3;
int64_t const c_txGas = 500;
int64_t const c_logGas = 32;
int64_t const c_logDataGas = 1;
int64_t const c_logTopicGas = 32;
int64_t const c_copyGas = 1;
int64_t getStepCost(Instruction inst) int64_t getStepCost(Instruction inst)
{ {
switch (inst) switch (inst)
{ {
default: // Assumes instruction code is valid // Tier 0
return c_stepGas;
case Instruction::STOP: case Instruction::STOP:
case Instruction::RETURN:
case Instruction::SUICIDE: case Instruction::SUICIDE:
case Instruction::SSTORE: // Handle cost of SSTORE separately in GasMeter::countSStore() case Instruction::SSTORE: // Handle cost of SSTORE separately in GasMeter::countSStore()
return 0; return c_stepGas[0];
case Instruction::EXP: return c_expGas; // Tier 1
case Instruction::ADDRESS:
case Instruction::SLOAD: return c_sloadGas; case Instruction::ORIGIN:
case Instruction::CALLER:
case Instruction::SHA3: return c_sha3Gas; case Instruction::CALLVALUE:
case Instruction::CALLDATASIZE:
case Instruction::BALANCE: return c_balanceGas; case Instruction::CODESIZE:
case Instruction::GASPRICE:
case Instruction::CALL: case Instruction::COINBASE:
case Instruction::CALLCODE: return c_callGas; case Instruction::TIMESTAMP:
case Instruction::NUMBER:
case Instruction::CREATE: return c_createGas; 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::LOG0:
case Instruction::LOG1: case Instruction::LOG1:
@ -73,7 +134,16 @@ int64_t getStepCost(Instruction inst)
auto numTopics = static_cast<int64_t>(inst) - static_cast<int64_t>(Instruction::LOG0); auto numTopics = static_cast<int64_t>(inst) - static_cast<int64_t>(Instruction::LOG0);
return c_logGas + numTopics * c_logTopicGas; 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
} }
} }
@ -82,34 +152,36 @@ GasMeter::GasMeter(llvm::IRBuilder<>& _builder, RuntimeManager& _runtimeManager)
CompilerHelper(_builder), CompilerHelper(_builder),
m_runtimeManager(_runtimeManager) m_runtimeManager(_runtimeManager)
{ {
auto module = getModule(); 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());
llvm::Type* gasCheckArgs[] = {Type::RuntimePtr, Type::Gas}; m_gasCheckFunc->setDoesNotThrow();
m_gasCheckFunc = llvm::Function::Create(llvm::FunctionType::get(Type::Void, gasCheckArgs, false), llvm::Function::PrivateLinkage, "gas.check", module); m_gasCheckFunc->setDoesNotCapture(1);
InsertPointGuard guard(m_builder);
auto checkBB = llvm::BasicBlock::Create(_builder.getContext(), "Check", m_gasCheckFunc); auto checkBB = llvm::BasicBlock::Create(_builder.getContext(), "Check", m_gasCheckFunc);
auto outOfGasBB = llvm::BasicBlock::Create(_builder.getContext(), "OutOfGas", m_gasCheckFunc);
auto updateBB = llvm::BasicBlock::Create(_builder.getContext(), "Update", m_gasCheckFunc); auto updateBB = llvm::BasicBlock::Create(_builder.getContext(), "Update", m_gasCheckFunc);
auto outOfGasBB = llvm::BasicBlock::Create(_builder.getContext(), "OutOfGas", m_gasCheckFunc);
auto rt = &m_gasCheckFunc->getArgumentList().front(); auto gasPtr = &m_gasCheckFunc->getArgumentList().front();
rt->setName("rt"); gasPtr->setName("gasPtr");
auto cost = rt->getNextNode(); auto cost = gasPtr->getNextNode();
cost->setName("cost"); cost->setName("cost");
auto jmpBuf = cost->getNextNode();
jmpBuf->setName("jmpBuf");
InsertPointGuard guard(m_builder);
m_builder.SetInsertPoint(checkBB); m_builder.SetInsertPoint(checkBB);
auto gas = m_runtimeManager.getGas(); auto gas = m_builder.CreateLoad(gasPtr, "gas");
gas = m_builder.CreateNSWSub(gas, cost, "gasUpdated"); auto gasUpdated = m_builder.CreateNSWSub(gas, cost, "gasUpdated");
auto isOutOfGas = m_builder.CreateICmpSLT(gas, m_builder.getInt64(0), "isOutOfGas"); // gas < 0, with gas == 0 we can still do 0 cost instructions 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(isOutOfGas, outOfGasBB, updateBB); m_builder.CreateCondBr(gasOk, updateBB, outOfGasBB, Type::expectTrue);
m_builder.SetInsertPoint(outOfGasBB);
m_runtimeManager.abort();
m_builder.CreateUnreachable();
m_builder.SetInsertPoint(updateBB); m_builder.SetInsertPoint(updateBB);
m_runtimeManager.setGas(gas); m_builder.CreateStore(gasUpdated, gasPtr);
m_builder.CreateRetVoid(); m_builder.CreateRetVoid();
m_builder.SetInsertPoint(outOfGasBB);
m_runtimeManager.abort(jmpBuf);
m_builder.CreateUnreachable();
} }
void GasMeter::count(Instruction _inst) void GasMeter::count(Instruction _inst)
@ -117,13 +189,13 @@ void GasMeter::count(Instruction _inst)
if (!m_checkCall) if (!m_checkCall)
{ {
// Create gas check call with mocked block cost at begining of current cost-block // Create gas check call with mocked block cost at begining of current cost-block
m_checkCall = createCall(m_gasCheckFunc, {m_runtimeManager.getRuntimePtr(), llvm::UndefValue::get(Type::Gas)}); m_checkCall = createCall(m_gasCheckFunc, {m_runtimeManager.getGasPtr(), llvm::UndefValue::get(Type::Gas), m_runtimeManager.getJmpBuf()});
} }
m_blockCost += getStepCost(_inst); m_blockCost += getStepCost(_inst);
} }
void GasMeter::count(llvm::Value* _cost) void GasMeter::count(llvm::Value* _cost, llvm::Value* _jmpBuf, llvm::Value* _gasPtr)
{ {
if (_cost->getType() == Type::Word) if (_cost->getType() == Type::Word)
{ {
@ -134,7 +206,7 @@ void GasMeter::count(llvm::Value* _cost)
} }
assert(_cost->getType() == Type::Gas); assert(_cost->getType() == Type::Gas);
createCall(m_gasCheckFunc, {m_runtimeManager.getRuntimePtr(), _cost}); createCall(m_gasCheckFunc, {_gasPtr ? _gasPtr : m_runtimeManager.getGasPtr(), _cost, _jmpBuf ? _jmpBuf : m_runtimeManager.getJmpBuf()});
} }
void GasMeter::countExp(llvm::Value* _exponent) void GasMeter::countExp(llvm::Value* _exponent)
@ -145,25 +217,31 @@ void GasMeter::countExp(llvm::Value* _exponent)
// OPT: Can gas update be done in exp algorithm? // OPT: Can gas update be done in exp algorithm?
auto ctlz = llvm::Intrinsic::getDeclaration(getModule(), llvm::Intrinsic::ctlz, Type::Word); auto t = llvm::APInt{256, 1};
auto lz256 = m_builder.CreateCall2(ctlz, _exponent, m_builder.getInt1(false)); auto c = m_builder.CreateSelect(m_builder.CreateICmpUGE(_exponent, Constant::get(t)), m_builder.getInt64(1), m_builder.getInt64(0));
auto lz = m_builder.CreateTrunc(lz256, Type::Gas, "lz"); for (auto i = 2; i <= 32; ++i)
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)); t <<= 8;
count(sigBytes); c = m_builder.CreateSelect(m_builder.CreateICmpUGE(_exponent, Constant::get(t)), m_builder.getInt64(i), c);
}
// FIXME: Does not work because of LLVM bug: https://llvm.org/bugs/show_bug.cgi?id=22304
// auto ctlz = llvm::Intrinsic::getDeclaration(getModule(), llvm::Intrinsic::ctlz, Type::Word);
// auto lz256 = m_builder.CreateCall2(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(c, m_builder.getInt64(c_expByteGas)));
} }
void GasMeter::countSStore(Ext& _ext, llvm::Value* _index, llvm::Value* _newValue) void GasMeter::countSStore(Ext& _ext, llvm::Value* _index, llvm::Value* _newValue)
{ {
auto oldValue = _ext.sload(_index); auto oldValue = _ext.sload(_index);
auto oldValueIsZero = m_builder.CreateICmpEQ(oldValue, Constant::get(0), "oldValueIsZero"); auto oldValueIsZero = m_builder.CreateICmpEQ(oldValue, Constant::get(0), "oldValueIsZero");
auto newValueIsZero = m_builder.CreateICmpEQ(_newValue, Constant::get(0), "newValueIsZero");
auto oldValueIsntZero = m_builder.CreateICmpNE(oldValue, Constant::get(0), "oldValueIsntZero");
auto newValueIsntZero = m_builder.CreateICmpNE(_newValue, Constant::get(0), "newValueIsntZero"); auto newValueIsntZero = m_builder.CreateICmpNE(_newValue, Constant::get(0), "newValueIsntZero");
auto isInsert = m_builder.CreateAnd(oldValueIsZero, newValueIsntZero, "isInsert"); auto isInsert = m_builder.CreateAnd(oldValueIsZero, newValueIsntZero, "isInsert");
auto isDelete = m_builder.CreateAnd(oldValueIsntZero, newValueIsZero, "isDelete"); 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"); auto cost = m_builder.CreateSelect(isInsert, m_builder.getInt64(c_sstoreSetGas), m_builder.getInt64(c_sstoreResetGas), "cost");
cost = m_builder.CreateSelect(isDelete, m_builder.getInt64(0), cost, "cost");
count(cost); count(cost);
} }
@ -171,8 +249,8 @@ void GasMeter::countLogData(llvm::Value* _dataLength)
{ {
assert(m_checkCall); assert(m_checkCall);
assert(m_blockCost > 0); // LOGn instruction is already counted assert(m_blockCost > 0); // LOGn instruction is already counted
static_assert(c_logDataGas == 1, "Log data gas cost has changed. Update GasMeter."); static_assert(c_logDataGas != 1, "Log data gas cost has changed. Update GasMeter.");
count(_dataLength); count(m_builder.CreateNUWMul(_dataLength, Constant::get(c_logDataGas))); // TODO: Use i64
} }
void GasMeter::countSha3Data(llvm::Value* _dataLength) void GasMeter::countSha3Data(llvm::Value* _dataLength)
@ -213,16 +291,16 @@ void GasMeter::commitCostBlock()
assert(m_blockCost == 0); assert(m_blockCost == 0);
} }
void GasMeter::countMemory(llvm::Value* _additionalMemoryInWords) void GasMeter::countMemory(llvm::Value* _additionalMemoryInWords, llvm::Value* _jmpBuf, llvm::Value* _gasPtr)
{ {
static_assert(c_memoryGas == 1, "Memory gas cost has changed. Update GasMeter."); static_assert(c_memoryGas != 1, "Memory gas cost has changed. Update GasMeter.");
count(_additionalMemoryInWords); count(_additionalMemoryInWords, _jmpBuf, _gasPtr);
} }
void GasMeter::countCopy(llvm::Value* _copyWords) void GasMeter::countCopy(llvm::Value* _copyWords)
{ {
static_assert(c_copyGas == 1, "Copy gas cost has changed. Update GasMeter."); static_assert(c_copyGas != 1, "Copy gas cost has changed. Update GasMeter.");
count(_copyWords); count(m_builder.CreateNUWMul(_copyWords, m_builder.getInt64(c_copyGas)));
} }
} }

4
evmjit/libevmjit/GasMeter.h

@ -20,7 +20,7 @@ public:
void count(Instruction _inst); void count(Instruction _inst);
/// Count additional cost /// Count additional cost
void count(llvm::Value* _cost); void count(llvm::Value* _cost, llvm::Value* _jmpBuf = nullptr, llvm::Value* _gasPtr = nullptr);
/// Calculate & count gas cost for SSTORE instruction /// Calculate & count gas cost for SSTORE instruction
void countSStore(class Ext& _ext, llvm::Value* _index, llvm::Value* _newValue); void countSStore(class Ext& _ext, llvm::Value* _index, llvm::Value* _newValue);
@ -41,7 +41,7 @@ public:
void giveBack(llvm::Value* _gas); void giveBack(llvm::Value* _gas);
/// Generate code that checks the cost of additional memory used by program /// Generate code that checks the cost of additional memory used by program
void countMemory(llvm::Value* _additionalMemoryInWords); void countMemory(llvm::Value* _additionalMemoryInWords, llvm::Value* _jmpBuf, llvm::Value* _gasPtr);
/// Count addional gas cost for memory copy /// Count addional gas cost for memory copy
void countCopy(llvm::Value* _copyWords); void countCopy(llvm::Value* _copyWords);

158
evmjit/libevmjit/Memory.cpp

@ -19,6 +19,7 @@ namespace jit
Memory::Memory(RuntimeManager& _runtimeManager, GasMeter& _gasMeter): Memory::Memory(RuntimeManager& _runtimeManager, GasMeter& _gasMeter):
RuntimeHelper(_runtimeManager), // TODO: RuntimeHelper not needed RuntimeHelper(_runtimeManager), // TODO: RuntimeHelper not needed
m_memory{getBuilder(), _runtimeManager.getMem()},
m_gasMeter(_gasMeter) m_gasMeter(_gasMeter)
{} {}
@ -27,20 +28,20 @@ llvm::Function* Memory::getRequireFunc()
auto& func = m_require; auto& func = m_require;
if (!func) if (!func)
{ {
llvm::Type* argTypes[] = {Type::RuntimePtr, Type::Word, Type::Word}; 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 = llvm::Function::Create(llvm::FunctionType::get(Type::Void, argTypes, false), llvm::Function::PrivateLinkage, "mem.require", getModule());
auto rt = func->arg_begin(); func->setDoesNotThrow();
rt->setName("rt");
auto offset = rt->getNextNode(); auto mem = &func->getArgumentList().front();
offset->setName("offset"); mem->setName("mem");
auto size = offset->getNextNode(); auto blkOffset = mem->getNextNode();
size->setName("size"); blkOffset->setName("blkOffset");
auto blkSize = blkOffset->getNextNode();
llvm::Type* resizeArgs[] = {Type::RuntimePtr, Type::WordPtr}; blkSize->setName("blkSize");
auto resize = llvm::Function::Create(llvm::FunctionType::get(Type::BytePtr, resizeArgs, false), llvm::Function::ExternalLinkage, "mem_resize", getModule()); auto jmpBuf = blkSize->getNextNode();
llvm::AttrBuilder attrBuilder; jmpBuf->setName("jmpBuf");
attrBuilder.addAttribute(llvm::Attribute::NoAlias).addAttribute(llvm::Attribute::NoCapture).addAttribute(llvm::Attribute::NonNull).addAttribute(llvm::Attribute::ReadOnly); auto gas = jmpBuf->getNextNode();
resize->setAttributes(llvm::AttributeSet::get(resize->getContext(), 1, attrBuilder)); gas->setName("gas");
auto preBB = llvm::BasicBlock::Create(func->getContext(), "Pre", func); auto preBB = llvm::BasicBlock::Create(func->getContext(), "Pre", func);
auto checkBB = llvm::BasicBlock::Create(func->getContext(), "Check", func); auto checkBB = llvm::BasicBlock::Create(func->getContext(), "Check", func);
@ -51,40 +52,38 @@ llvm::Function* Memory::getRequireFunc()
// BB "Pre": Ignore checks with size 0 // BB "Pre": Ignore checks with size 0
m_builder.SetInsertPoint(preBB); m_builder.SetInsertPoint(preBB);
auto sizeIsZero = m_builder.CreateICmpEQ(size, Constant::get(0)); m_builder.CreateCondBr(m_builder.CreateICmpNE(blkSize, Constant::get(0)), checkBB, returnBB, Type::expectTrue);
m_builder.CreateCondBr(sizeIsZero, returnBB, checkBB);
// BB "Check" // BB "Check"
m_builder.SetInsertPoint(checkBB); m_builder.SetInsertPoint(checkBB);
auto uaddWO = llvm::Intrinsic::getDeclaration(getModule(), llvm::Intrinsic::uadd_with_overflow, Type::Word); 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 uaddRes = m_builder.CreateCall2(uaddWO, offset, size, "res"); auto blkOffsetOk = m_builder.CreateICmpULE(blkOffset, Constant::get(c_inputMax), "blkOffsetOk");
auto sizeRequired = m_builder.CreateExtractValue(uaddRes, 0, "sizeReq"); auto blkO = m_builder.CreateSelect(blkOffsetOk, m_builder.CreateTrunc(blkOffset, Type::Size), m_builder.getInt64(c_inputMax), "bklO");
auto overflow1 = m_builder.CreateExtractValue(uaddRes, 1, "overflow1"); auto blkSizeOk = m_builder.CreateICmpULE(blkSize, Constant::get(c_inputMax), "blkSizeOk");
auto rtPtr = getRuntimeManager().getRuntimePtr(); auto blkS = m_builder.CreateSelect(blkSizeOk, m_builder.CreateTrunc(blkSize, Type::Size), m_builder.getInt64(c_inputMax), "bklS");
auto sizePtr = m_builder.CreateStructGEP(rtPtr, 4);
auto currSize = m_builder.CreateLoad(sizePtr, "currSize"); auto sizeReq0 = m_builder.CreateNUWAdd(blkO, blkS, "sizeReq0");
auto tooSmall = m_builder.CreateICmpULE(currSize, sizeRequired, "tooSmall"); auto sizeReq = m_builder.CreateAnd(m_builder.CreateNUWAdd(sizeReq0, m_builder.getInt64(31)), uint64_t(-1) << 5, "sizeReq"); // s' = ((s0 + 31) / 32) * 32
auto resizeNeeded = m_builder.CreateOr(tooSmall, overflow1, "resizeNeeded"); auto sizeCur = m_memory.size(mem);
m_builder.CreateCondBr(resizeNeeded, resizeBB, returnBB); // OPT branch weights? auto sizeOk = m_builder.CreateICmpULE(sizeReq, sizeCur, "sizeOk");
m_builder.CreateCondBr(sizeOk, returnBB, resizeBB, Type::expectTrue);
// BB "Resize" // BB "Resize"
m_builder.SetInsertPoint(resizeBB); m_builder.SetInsertPoint(resizeBB);
// Check gas first // Check gas first
uaddRes = m_builder.CreateCall2(uaddWO, sizeRequired, Constant::get(31), "res"); auto w1 = m_builder.CreateLShr(sizeReq, 5);
auto wordsRequired = m_builder.CreateExtractValue(uaddRes, 0); auto w1s = m_builder.CreateNUWMul(w1, w1);
auto overflow2 = m_builder.CreateExtractValue(uaddRes, 1, "overflow2"); auto c1 = m_builder.CreateAdd(m_builder.CreateNUWMul(w1, m_builder.getInt64(3)), m_builder.CreateLShr(w1s, 9));
auto overflow = m_builder.CreateOr(overflow1, overflow2, "overflow"); auto w0 = m_builder.CreateLShr(sizeCur, 5);
wordsRequired = m_builder.CreateSelect(overflow, Constant::get(-1), wordsRequired); auto w0s = m_builder.CreateNUWMul(w0, w0);
wordsRequired = m_builder.CreateUDiv(wordsRequired, Constant::get(32), "wordsReq"); auto c0 = m_builder.CreateAdd(m_builder.CreateNUWMul(w0, m_builder.getInt64(3)), m_builder.CreateLShr(w0s, 9));
sizeRequired = m_builder.CreateMul(wordsRequired, Constant::get(32), "roundedSizeReq"); auto cc = m_builder.CreateNUWSub(c1, c0);
auto words = m_builder.CreateUDiv(currSize, Constant::get(32), "words"); // size is always 32*k auto costOk = m_builder.CreateAnd(blkOffsetOk, blkSizeOk, "costOk");
auto newWords = m_builder.CreateSub(wordsRequired, words, "addtionalWords"); auto c = m_builder.CreateSelect(costOk, cc, m_builder.getInt64(std::numeric_limits<int64_t>::max()), "c");
m_gasMeter.countMemory(newWords); m_gasMeter.count(c, jmpBuf, gas);
// Resize // Resize
m_builder.CreateStore(sizeRequired, sizePtr); m_memory.extend(mem, sizeReq);
auto newData = m_builder.CreateCall2(resize, rt, sizePtr, "newData");
auto dataPtr = m_builder.CreateStructGEP(rtPtr, 3);
m_builder.CreateStore(newData, dataPtr);
m_builder.CreateBr(returnBB); m_builder.CreateBr(returnBB);
// BB "Return" // BB "Return"
@ -94,12 +93,12 @@ llvm::Function* Memory::getRequireFunc()
return func; return func;
} }
llvm::Function* Memory::createFunc(bool _isStore, llvm::Type* _valueType, GasMeter&) llvm::Function* Memory::createFunc(bool _isStore, llvm::Type* _valueType)
{ {
auto isWord = _valueType == Type::Word; auto isWord = _valueType == Type::Word;
llvm::Type* storeArgs[] = {Type::RuntimePtr, Type::Word, _valueType}; llvm::Type* storeArgs[] = {Array::getType()->getPointerTo(), Type::Word, _valueType};
llvm::Type* loadArgs[] = {Type::RuntimePtr, Type::Word}; llvm::Type* loadArgs[] = {Array::getType()->getPointerTo(), Type::Word};
auto name = _isStore ? isWord ? "mstore" : "mstore8" : "mload"; 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 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()); auto func = llvm::Function::Create(funcType, llvm::Function::PrivateLinkage, name, getModule());
@ -107,28 +106,25 @@ llvm::Function* Memory::createFunc(bool _isStore, llvm::Type* _valueType, GasMet
InsertPointGuard guard(m_builder); // Restores insert point at function exit InsertPointGuard guard(m_builder); // Restores insert point at function exit
m_builder.SetInsertPoint(llvm::BasicBlock::Create(func->getContext(), {}, func)); m_builder.SetInsertPoint(llvm::BasicBlock::Create(func->getContext(), {}, func));
auto rt = func->arg_begin(); auto mem = &func->getArgumentList().front();
rt->setName("rt"); mem->setName("mem");
auto index = rt->getNextNode(); auto index = mem->getNextNode();
index->setName("index"); index->setName("index");
auto valueSize = _valueType->getPrimitiveSizeInBits() / 8;
this->require(index, Constant::get(valueSize));
auto ptr = getBytePtr(index);
if (isWord)
ptr = m_builder.CreateBitCast(ptr, Type::WordPtr, "wordPtr");
if (_isStore) if (_isStore)
{ {
llvm::Value* value = index->getNextNode(); auto valueArg = index->getNextNode();
value->setName("value"); valueArg->setName("value");
if (isWord) auto value = isWord ? Endianness::toBE(m_builder, valueArg) : valueArg;
value = Endianness::toBE(m_builder, value); auto memPtr = m_memory.getPtr(mem, m_builder.CreateTrunc(index, Type::Size));
m_builder.CreateStore(value, ptr); auto valuePtr = m_builder.CreateBitCast(memPtr, _valueType->getPointerTo(), "valuePtr");
m_builder.CreateStore(value, valuePtr);
m_builder.CreateRetVoid(); m_builder.CreateRetVoid();
} }
else else
{ {
llvm::Value* ret = m_builder.CreateLoad(ptr); 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); ret = Endianness::toNative(m_builder, ret);
m_builder.CreateRet(ret); m_builder.CreateRet(ret);
} }
@ -140,7 +136,7 @@ llvm::Function* Memory::getLoadWordFunc()
{ {
auto& func = m_loadWord; auto& func = m_loadWord;
if (!func) if (!func)
func = createFunc(false, Type::Word, m_gasMeter); func = createFunc(false, Type::Word);
return func; return func;
} }
@ -148,7 +144,7 @@ llvm::Function* Memory::getStoreWordFunc()
{ {
auto& func = m_storeWord; auto& func = m_storeWord;
if (!func) if (!func)
func = createFunc(true, Type::Word, m_gasMeter); func = createFunc(true, Type::Word);
return func; return func;
} }
@ -156,39 +152,41 @@ llvm::Function* Memory::getStoreByteFunc()
{ {
auto& func = m_storeByte; auto& func = m_storeByte;
if (!func) if (!func)
func = createFunc(true, Type::Byte, m_gasMeter); func = createFunc(true, Type::Byte);
return func; return func;
} }
llvm::Value* Memory::loadWord(llvm::Value* _addr) llvm::Value* Memory::loadWord(llvm::Value* _addr)
{ {
return createCall(getLoadWordFunc(), {getRuntimeManager().getRuntimePtr(), _addr}); require(_addr, Constant::get(Type::Word->getPrimitiveSizeInBits() / 8));
return createCall(getLoadWordFunc(), {getRuntimeManager().getMem(), _addr});
} }
void Memory::storeWord(llvm::Value* _addr, llvm::Value* _word) void Memory::storeWord(llvm::Value* _addr, llvm::Value* _word)
{ {
createCall(getStoreWordFunc(), {getRuntimeManager().getRuntimePtr(), _addr, _word}); require(_addr, Constant::get(Type::Word->getPrimitiveSizeInBits() / 8));
createCall(getStoreWordFunc(), {getRuntimeManager().getMem(), _addr, _word});
} }
void Memory::storeByte(llvm::Value* _addr, llvm::Value* _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"); auto byte = m_builder.CreateTrunc(_word, Type::Byte, "byte");
createCall(getStoreByteFunc(), {getRuntimeManager().getRuntimePtr(), _addr, byte}); createCall(getStoreByteFunc(), {getRuntimeManager().getMem(), _addr, byte});
} }
llvm::Value* Memory::getData() llvm::Value* Memory::getData()
{ {
auto rtPtr = getRuntimeManager().getRuntimePtr(); auto memPtr = m_builder.CreateBitCast(getRuntimeManager().getMem(), Type::BytePtr->getPointerTo());
auto dataPtr = m_builder.CreateStructGEP(rtPtr, 3); auto data = m_builder.CreateLoad(memPtr, "data");
return m_builder.CreateLoad(dataPtr, "data"); assert(data->getType() == Type::BytePtr);
return data;
} }
llvm::Value* Memory::getSize() llvm::Value* Memory::getSize()
{ {
auto rtPtr = getRuntimeManager().getRuntimePtr(); return m_builder.CreateZExt(m_memory.size(), Type::Word, "msize"); // TODO: Allow placing i64 on stack
auto sizePtr = m_builder.CreateStructGEP(rtPtr, 4);
return m_builder.CreateLoad(sizePtr, "size");
} }
llvm::Value* Memory::getBytePtr(llvm::Value* _index) llvm::Value* Memory::getBytePtr(llvm::Value* _index)
@ -204,7 +202,7 @@ void Memory::require(llvm::Value* _offset, llvm::Value* _size)
if (!constant->getValue()) if (!constant->getValue())
return; return;
} }
createCall(getRequireFunc(), {getRuntimeManager().getRuntimePtr(), _offset, _size}); createCall(getRequireFunc(), {getRuntimeManager().getMem(), _offset, _size, getRuntimeManager().getJmpBuf(), getRuntimeManager().getGasPtr()});
} }
void Memory::copyBytes(llvm::Value* _srcPtr, llvm::Value* _srcSize, llvm::Value* _srcIdx, void Memory::copyBytes(llvm::Value* _srcPtr, llvm::Value* _srcSize, llvm::Value* _srcIdx,
@ -233,28 +231,18 @@ void Memory::copyBytes(llvm::Value* _srcPtr, llvm::Value* _srcSize, llvm::Value*
auto dataLeftSize = m_builder.CreateNUWSub(size64, idx64); auto dataLeftSize = m_builder.CreateNUWSub(size64, idx64);
auto outOfBound = m_builder.CreateICmpUGT(reqBytes, dataLeftSize); auto outOfBound = m_builder.CreateICmpUGT(reqBytes, dataLeftSize);
auto bytesToCopyInner = m_builder.CreateSelect(outOfBound, dataLeftSize, reqBytes); auto bytesToCopyInner = m_builder.CreateSelect(outOfBound, dataLeftSize, reqBytes);
auto bytesToCopy = m_builder.CreateSelect(isOutsideData, m_builder.getInt64(0), bytesToCopyInner); 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 src = m_builder.CreateGEP(_srcPtr, idx64, "src");
auto dstIdx = m_builder.CreateTrunc(_destMemIdx, Type::Size, "dstIdx"); // Never allow memory index be a type bigger than i64 auto dstIdx = m_builder.CreateTrunc(_destMemIdx, Type::Size, "dstIdx"); // Never allow memory index be a type bigger than i64
auto dst = m_builder.CreateGEP(getData(), dstIdx, "dst"); 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.CreateMemCpy(dst, src, bytesToCopy, 0);
m_builder.CreateMemSet(pad, m_builder.getInt8(0), bytesToZero, 0);
} }
} }
} }
} }
extern "C"
{
using namespace dev::eth::jit;
EXPORT byte* mem_resize(Runtime* _rt, i256* _size) // TODO: Use uint64 as size OR use realloc in LLVM IR
{
auto size = _size->a; // Trunc to 64-bit
auto& memory = _rt->getMemory();
memory.resize(size);
return memory.data();
}
}

6
evmjit/libevmjit/Memory.h

@ -1,6 +1,6 @@
#pragma once #pragma once
#include "CompilerHelper.h" #include "Array.h"
namespace dev namespace dev
{ {
@ -28,9 +28,11 @@ public:
void require(llvm::Value* _offset, llvm::Value* _size); void require(llvm::Value* _offset, llvm::Value* _size);
private: private:
Array m_memory;
GasMeter& m_gasMeter; GasMeter& m_gasMeter;
llvm::Function* createFunc(bool _isStore, llvm::Type* _type, GasMeter& _gasMeter); llvm::Function* createFunc(bool _isStore, llvm::Type* _type);
llvm::Function* getRequireFunc(); llvm::Function* getRequireFunc();
llvm::Function* getLoadWordFunc(); llvm::Function* getLoadWordFunc();

29
evmjit/libevmjit/Optimizer.cpp

@ -0,0 +1,29 @@
#include "Optimizer.h"
#include "preprocessor/llvm_includes_start.h"
#include <llvm/PassManager.h>
#include <llvm/Transforms/Scalar.h>
#include <llvm/Transforms/IPO.h>
#include "preprocessor/llvm_includes_end.h"
namespace dev
{
namespace eth
{
namespace jit
{
bool optimize(llvm::Module& _module)
{
auto pm = llvm::PassManager{};
//pm.add(llvm::createFunctionInliningPass(2, 2)); // Produces invalid IR
pm.add(llvm::createCFGSimplificationPass());
//pm.add(llvm::createInstructionCombiningPass()); // Produces invalid runtime results
pm.add(llvm::createAggressiveDCEPass());
pm.add(llvm::createLowerSwitchPass());
return pm.run(_module);
}
}
}
}

19
evmjit/libevmjit/Optimizer.h

@ -0,0 +1,19 @@
#pragma once
namespace llvm
{
class Module;
}
namespace dev
{
namespace eth
{
namespace jit
{
bool optimize(llvm::Module& _module);
}
}
}

25
evmjit/libevmjit/Runtime.cpp

@ -9,20 +9,29 @@ namespace eth
namespace jit namespace jit
{ {
Runtime::Runtime(RuntimeData* _data, Env* _env) : void Runtime::init(RuntimeData* _data, Env* _env)
m_data(*_data), {
m_env(*_env) m_data = _data;
{} m_env = _env;
}
extern "C" void ext_free(void* _data) noexcept;
Runtime::~Runtime()
{
if (m_memData)
ext_free(m_memData); // Use helper free to check memory leaks
}
bytes_ref Runtime::getReturnData() const bytes_ref Runtime::getReturnData() const
{ {
auto data = m_data.callData; auto data = m_data->callData;
auto size = static_cast<size_t>(m_data.callDataSize); auto size = static_cast<size_t>(m_data->callDataSize);
if (data < m_memory.data() || data >= m_memory.data() + m_memory.size() || size == 0) 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 assert(size == 0); // data can be an invalid pointer only if size is 0
m_data.callData = nullptr; m_data->callData = nullptr;
return {}; return {};
} }

24
evmjit/libevmjit/Runtime.h

@ -9,30 +9,20 @@ namespace eth
namespace jit namespace jit
{ {
using StackImpl = std::vector<i256>;
using MemoryImpl = bytes;
class Runtime class Runtime
{ {
public: public:
Runtime(RuntimeData* _data, Env* _env); void init(RuntimeData* _data, Env* _env);
EXPORT ~Runtime();
Runtime(const Runtime&) = delete;
Runtime& operator=(const Runtime&) = delete;
StackImpl& getStack() { return m_stack; }
MemoryImpl& getMemory() { return m_memory; }
bytes_ref getReturnData() const; bytes_ref getReturnData() const;
private: private:
RuntimeData& m_data; ///< Pointer to data. Expected by compiled contract. RuntimeData* m_data = nullptr; ///< Pointer to data. Expected by compiled contract.
Env& m_env; ///< Pointer to environment proxy. Expected by compiled contract. Env* m_env = nullptr; ///< Pointer to environment proxy. Expected by compiled contract.
void* m_currJmpBuf = nullptr; ///< Pointer to jump buffer. Expected by compiled contract. byte* m_memData = nullptr;
byte* m_memoryData = nullptr; uint64_t m_memSize = 0;
i256 m_memorySize; uint64_t m_memCap = 0;
StackImpl m_stack;
MemoryImpl m_memory;
}; };
} }

43
evmjit/libevmjit/RuntimeManager.cpp

@ -4,6 +4,9 @@
#include <llvm/IR/IntrinsicInst.h> #include <llvm/IR/IntrinsicInst.h>
#include "preprocessor/llvm_includes_end.h" #include "preprocessor/llvm_includes_end.h"
#include "Stack.h"
#include "Utils.h"
namespace dev namespace dev
{ {
namespace eth namespace eth
@ -48,9 +51,7 @@ llvm::StructType* RuntimeManager::getRuntimeType()
{ {
Type::RuntimeDataPtr, // data Type::RuntimeDataPtr, // data
Type::EnvPtr, // Env* Type::EnvPtr, // Env*
Type::BytePtr, // jmpbuf Array::getType() // memory
Type::BytePtr, // memory data
Type::Word, // memory size
}; };
type = llvm::StructType::create(elems, "Runtime"); type = llvm::StructType::create(elems, "Runtime");
} }
@ -83,22 +84,21 @@ llvm::Twine getName(RuntimeData::Index _index)
} }
} }
RuntimeManager::RuntimeManager(llvm::IRBuilder<>& _builder, llvm::Value* _jmpBuf, code_iterator _codeBegin, code_iterator _codeEnd): RuntimeManager::RuntimeManager(llvm::IRBuilder<>& _builder, code_iterator _codeBegin, code_iterator _codeEnd):
CompilerHelper(_builder), CompilerHelper(_builder),
m_jmpBuf(_jmpBuf),
m_codeBegin(_codeBegin), m_codeBegin(_codeBegin),
m_codeEnd(_codeEnd) m_codeEnd(_codeEnd)
{ {
m_longjmp = llvm::Intrinsic::getDeclaration(getModule(), llvm::Intrinsic::eh_sjlj_longjmp); m_longjmp = llvm::Intrinsic::getDeclaration(getModule(), llvm::Intrinsic::eh_sjlj_longjmp);
// save jmpBuf to be used in helper functions
auto ptr = m_builder.CreateStructGEP(getRuntimePtr(), 2);
m_builder.CreateStore(m_jmpBuf, ptr, "jmpBufExt");
// Unpack data // Unpack data
auto rtPtr = getRuntimePtr(); auto rtPtr = getRuntimePtr();
m_dataPtr = m_builder.CreateLoad(m_builder.CreateStructGEP(rtPtr, 0), "data"); m_dataPtr = m_builder.CreateLoad(m_builder.CreateStructGEP(rtPtr, 0), "data");
assert(m_dataPtr->getType() == Type::RuntimeDataPtr); assert(m_dataPtr->getType() == Type::RuntimeDataPtr);
m_gasPtr = m_builder.CreateStructGEP(m_dataPtr, 0, "gas");
assert(m_gasPtr->getType() == Type::Gas->getPointerTo());
m_memPtr = m_builder.CreateStructGEP(rtPtr, 2, "mem");
assert(m_memPtr->getType() == Array::getType()->getPointerTo());
m_envPtr = m_builder.CreateLoad(m_builder.CreateStructGEP(rtPtr, 1), "env"); m_envPtr = m_builder.CreateLoad(m_builder.CreateStructGEP(rtPtr, 1), "env");
assert(m_envPtr->getType() == Type::EnvPtr); assert(m_envPtr->getType() == Type::EnvPtr);
} }
@ -150,7 +150,7 @@ void RuntimeManager::set(RuntimeData::Index _index, llvm::Value* _value)
void RuntimeManager::registerReturnData(llvm::Value* _offset, llvm::Value* _size) void RuntimeManager::registerReturnData(llvm::Value* _offset, llvm::Value* _size)
{ {
auto memPtr = getBuilder().CreateStructGEP(getRuntimePtr(), 3); auto memPtr = m_builder.CreateBitCast(getMem(), Type::BytePtr->getPointerTo());
auto mem = getBuilder().CreateLoad(memPtr, "memory"); auto mem = getBuilder().CreateLoad(memPtr, "memory");
auto idx = m_builder.CreateTrunc(_offset, Type::Size, "idx"); // Never allow memory index be a type bigger than i64 // TODO: Report bug & fix to LLVM auto idx = m_builder.CreateTrunc(_offset, Type::Size, "idx"); // Never allow memory index be a type bigger than i64 // TODO: Report bug & fix to LLVM
auto returnDataPtr = getBuilder().CreateGEP(mem, idx); auto returnDataPtr = getBuilder().CreateGEP(mem, idx);
@ -165,6 +165,14 @@ void RuntimeManager::registerSuicide(llvm::Value* _balanceAddress)
set(RuntimeData::SuicideDestAddress, _balanceAddress); set(RuntimeData::SuicideDestAddress, _balanceAddress);
} }
void RuntimeManager::exit(ReturnCode _returnCode)
{
if (m_stack)
m_stack->free();
m_builder.CreateRet(Constant::get(_returnCode));
}
void RuntimeManager::abort(llvm::Value* _jmpBuf) void RuntimeManager::abort(llvm::Value* _jmpBuf)
{ {
auto longjmp = llvm::Intrinsic::getDeclaration(getModule(), llvm::Intrinsic::eh_sjlj_longjmp); auto longjmp = llvm::Intrinsic::getDeclaration(getModule(), llvm::Intrinsic::eh_sjlj_longjmp);
@ -213,12 +221,6 @@ llvm::Value* RuntimeManager::getCallDataSize()
return getBuilder().CreateZExt(value, Type::Word); return getBuilder().CreateZExt(value, Type::Word);
} }
llvm::Value* RuntimeManager::getJmpBufExt()
{
auto ptr = getBuilder().CreateStructGEP(getRuntimePtr(), 2);
return getBuilder().CreateLoad(ptr, "jmpBufExt");
}
llvm::Value* RuntimeManager::getGas() llvm::Value* RuntimeManager::getGas()
{ {
auto gas = get(RuntimeData::Gas); auto gas = get(RuntimeData::Gas);
@ -228,7 +230,14 @@ llvm::Value* RuntimeManager::getGas()
llvm::Value* RuntimeManager::getGasPtr() llvm::Value* RuntimeManager::getGasPtr()
{ {
return getPtr(RuntimeData::Gas); assert(getMainFunction());
return m_gasPtr;
}
llvm::Value* RuntimeManager::getMem()
{
assert(getMainFunction());
return m_memPtr;
} }
void RuntimeManager::setGas(llvm::Value* _gas) void RuntimeManager::setGas(llvm::Value* _gas)

20
evmjit/libevmjit/RuntimeManager.h

@ -11,11 +11,12 @@ namespace eth
{ {
namespace jit namespace jit
{ {
class Stack;
class RuntimeManager: public CompilerHelper class RuntimeManager: public CompilerHelper
{ {
public: public:
RuntimeManager(llvm::IRBuilder<>& _builder, llvm::Value* _jmpBuf, code_iterator _codeBegin, code_iterator _codeEnd); RuntimeManager(llvm::IRBuilder<>& _builder, code_iterator _codeBegin, code_iterator _codeEnd);
llvm::Value* getRuntimePtr(); llvm::Value* getRuntimePtr();
llvm::Value* getDataPtr(); llvm::Value* getDataPtr();
@ -32,11 +33,17 @@ public:
llvm::Value* getJmpBuf() { return m_jmpBuf; } llvm::Value* getJmpBuf() { return m_jmpBuf; }
void setGas(llvm::Value* _gas); void setGas(llvm::Value* _gas);
void registerReturnData(llvm::Value* _index, llvm::Value* _size); llvm::Value* getMem();
void registerReturnData(llvm::Value* _index, llvm::Value* _size); // TODO: Move to Memory.
void registerSuicide(llvm::Value* _balanceAddress); void registerSuicide(llvm::Value* _balanceAddress);
void exit(ReturnCode _returnCode);
void abort(llvm::Value* _jmpBuf); void abort(llvm::Value* _jmpBuf);
void abort() { abort(getJmpBufExt()); }
void setStack(Stack& _stack) { m_stack = &_stack; }
void setJmpBuf(llvm::Value* _jmpBuf) { m_jmpBuf = _jmpBuf; }
static llvm::StructType* getRuntimeType(); static llvm::StructType* getRuntimeType();
static llvm::StructType* getRuntimeDataType(); static llvm::StructType* getRuntimeDataType();
@ -44,15 +51,18 @@ public:
private: private:
llvm::Value* getPtr(RuntimeData::Index _index); llvm::Value* getPtr(RuntimeData::Index _index);
void set(RuntimeData::Index _index, llvm::Value* _value); void set(RuntimeData::Index _index, llvm::Value* _value);
llvm::Value* getJmpBufExt();
llvm::Function* m_longjmp = nullptr; llvm::Function* m_longjmp = nullptr;
llvm::Value* const m_jmpBuf; llvm::Value* m_jmpBuf = nullptr;
llvm::Value* m_dataPtr = nullptr; llvm::Value* m_dataPtr = nullptr;
llvm::Value* m_gasPtr = nullptr;
llvm::Value* m_memPtr = nullptr;
llvm::Value* m_envPtr = nullptr; llvm::Value* m_envPtr = nullptr;
code_iterator m_codeBegin = {}; code_iterator m_codeBegin = {};
code_iterator m_codeEnd = {}; code_iterator m_codeEnd = {};
Stack* m_stack = nullptr;
}; };
} }

138
evmjit/libevmjit/Stack.cpp

@ -6,6 +6,9 @@
#include "RuntimeManager.h" #include "RuntimeManager.h"
#include "Runtime.h" #include "Runtime.h"
#include "Utils.h"
#include <set> // DEBUG only
namespace dev namespace dev
{ {
@ -16,20 +19,62 @@ namespace jit
Stack::Stack(llvm::IRBuilder<>& _builder, RuntimeManager& _runtimeManager): Stack::Stack(llvm::IRBuilder<>& _builder, RuntimeManager& _runtimeManager):
CompilerHelper(_builder), CompilerHelper(_builder),
m_runtimeManager(_runtimeManager) m_runtimeManager(_runtimeManager),
m_stack(_builder, "stack")
{}
llvm::Function* Stack::getPushFunc()
{ {
m_arg = m_builder.CreateAlloca(Type::Word, nullptr, "stack.arg"); auto& func = m_push;
if (!func)
{
llvm::Type* argTypes[] = {Type::RuntimePtr, Type::Word};
func = llvm::Function::Create(llvm::FunctionType::get(Type::Void, argTypes, false), llvm::Function::ExternalLinkage, "stack.push", getModule());
llvm::Type* extArgTypes[] = {Type::RuntimePtr, Type::WordPtr};
auto extPushFunc = llvm::Function::Create(llvm::FunctionType::get(Type::Void, extArgTypes, false), llvm::Function::ExternalLinkage, "stack_push", getModule());
auto rt = &func->getArgumentList().front();
rt->setName("rt");
auto value = rt->getNextNode();
value->setName("value");
using namespace llvm; InsertPointGuard guard{m_builder};
using Linkage = GlobalValue::LinkageTypes; auto entryBB = llvm::BasicBlock::Create(m_builder.getContext(), {}, func);
m_builder.SetInsertPoint(entryBB);
auto a = m_builder.CreateAlloca(Type::Word);
m_builder.CreateStore(value, a);
createCall(extPushFunc, {rt, a});
m_builder.CreateRetVoid();
}
return func;
}
auto module = getModule(); llvm::Function* Stack::getSetFunc()
{
auto& func = m_set;
if (!func)
{
llvm::Type* argTypes[] = {Type::RuntimePtr, Type::Size, Type::Word};
func = llvm::Function::Create(llvm::FunctionType::get(Type::Void, argTypes, false), llvm::Function::ExternalLinkage, "stack.set", getModule());
llvm::Type* extArgTypes[] = {Type::RuntimePtr, Type::Size, Type::WordPtr};
auto extSetFunc = llvm::Function::Create(llvm::FunctionType::get(Type::Void, extArgTypes, false), llvm::Function::ExternalLinkage, "stack_set", getModule());
llvm::Type* pushArgTypes[] = {Type::RuntimePtr, Type::WordPtr}; auto rt = &func->getArgumentList().front();
m_push = Function::Create(FunctionType::get(Type::Void, pushArgTypes, false), Linkage::ExternalLinkage, "stack_push", module); rt->setName("rt");
auto index = rt->getNextNode();
index->setName("index");
auto value = index->getNextNode();
value->setName("value");
llvm::Type* getSetArgTypes[] = {Type::RuntimePtr, Type::Size, Type::WordPtr}; InsertPointGuard guard{m_builder};
m_set = Function::Create(FunctionType::get(Type::Void, getSetArgTypes, false), Linkage::ExternalLinkage, "stack_set", module); auto entryBB = llvm::BasicBlock::Create(m_builder.getContext(), {}, func);
m_builder.SetInsertPoint(entryBB);
auto a = m_builder.CreateAlloca(Type::Word);
m_builder.CreateStore(value, a);
createCall(extSetFunc, {rt, index, a});
m_builder.CreateRetVoid();
}
return func;
} }
llvm::Function* Stack::getPopFunc() llvm::Function* Stack::getPopFunc()
@ -73,16 +118,14 @@ llvm::Function* Stack::getGetFunc()
auto& func = m_get; auto& func = m_get;
if (!func) if (!func)
{ {
llvm::Type* argTypes[] = {Type::RuntimePtr, Type::Size, Type::BytePtr}; llvm::Type* argTypes[] = {Type::Size, Type::Size, Type::BytePtr};
func = llvm::Function::Create(llvm::FunctionType::get(Type::WordPtr, argTypes, false), llvm::Function::ExternalLinkage, "stack.get", getModule()); func = llvm::Function::Create(llvm::FunctionType::get(Type::Void, argTypes, false), llvm::Function::ExternalLinkage, "stack.require", getModule());
llvm::Type* extArgTypes[] = {Type::RuntimePtr, Type::Size};
auto extGetFunc = llvm::Function::Create(llvm::FunctionType::get(Type::WordPtr, extArgTypes, false), llvm::Function::ExternalLinkage, "stack_get", getModule());
auto rt = &func->getArgumentList().front(); auto index = &func->getArgumentList().front();
rt->setName("rt");
auto index = rt->getNextNode();
index->setName("index"); index->setName("index");
auto jmpBuf = index->getNextNode(); auto size = index->getNextNode();
size->setName("size");
auto jmpBuf = size->getNextNode();
jmpBuf->setName("jmpBuf"); jmpBuf->setName("jmpBuf");
InsertPointGuard guard{m_builder}; InsertPointGuard guard{m_builder};
@ -91,46 +134,44 @@ llvm::Function* Stack::getGetFunc()
auto returnBB = llvm::BasicBlock::Create(m_builder.getContext(), "Return", func); auto returnBB = llvm::BasicBlock::Create(m_builder.getContext(), "Return", func);
m_builder.SetInsertPoint(entryBB); m_builder.SetInsertPoint(entryBB);
auto valuePtr = createCall(extGetFunc, {rt, index}); auto underflow = m_builder.CreateICmpUGE(index, size, "underflow");
auto ok = m_builder.CreateICmpNE(valuePtr, llvm::ConstantPointerNull::get(Type::WordPtr)); m_builder.CreateCondBr(underflow, underflowBB, returnBB);
m_builder.CreateCondBr(ok, returnBB, underflowBB); //TODO: Add branch weight
m_builder.SetInsertPoint(underflowBB); m_builder.SetInsertPoint(underflowBB);
m_runtimeManager.abort(jmpBuf); m_runtimeManager.abort(jmpBuf);
m_builder.CreateUnreachable(); m_builder.CreateUnreachable();
m_builder.SetInsertPoint(returnBB); m_builder.SetInsertPoint(returnBB);
m_builder.CreateRet(valuePtr); m_builder.CreateRetVoid();
} }
return func; return func;
} }
llvm::Value* Stack::get(size_t _index) llvm::Value* Stack::get(size_t _index)
{ {
auto valuePtr = createCall(getGetFunc(), {m_runtimeManager.getRuntimePtr(), m_builder.getInt64(_index), m_runtimeManager.getJmpBuf()}); createCall(getGetFunc(), {m_builder.getInt64(_index), m_stack.size(), m_runtimeManager.getJmpBuf()});
return m_builder.CreateLoad(valuePtr); auto value = m_stack.get(m_builder.CreateSub(m_stack.size(), m_builder.getInt64(_index + 1)));
//return m_builder.CreateLoad(valuePtr);
return value;
} }
void Stack::set(size_t _index, llvm::Value* _value) void Stack::set(size_t _index, llvm::Value* _value)
{ {
m_builder.CreateStore(_value, m_arg); m_stack.set(m_builder.CreateSub(m_stack.size(), m_builder.getInt64(_index + 1)), _value);
m_builder.CreateCall3(m_set, m_runtimeManager.getRuntimePtr(), llvm::ConstantInt::get(Type::Size, _index, false), m_arg);
} }
void Stack::pop(size_t _count) void Stack::pop(size_t _count)
{ {
createCall(getPopFunc(), {m_runtimeManager.getRuntimePtr(), m_builder.getInt64(_count), m_runtimeManager.getJmpBuf()}); // FIXME: Pop does not check for stack underflow but looks like not needed
// We should place stack.require() check and begining of every BB
m_stack.pop(m_builder.getInt64(_count));
} }
void Stack::push(llvm::Value* _value) void Stack::push(llvm::Value* _value)
{ {
m_builder.CreateStore(_value, m_arg); m_stack.push(_value);
m_builder.CreateCall2(m_push, m_runtimeManager.getRuntimePtr(), m_arg);
} }
size_t Stack::maxStackSize = 0;
} }
} }
} }
@ -139,41 +180,6 @@ extern "C"
{ {
using namespace dev::eth::jit; using namespace dev::eth::jit;
EXPORT bool stack_pop(Runtime* _rt, uint64_t _count)
{
auto& stack = _rt->getStack();
if (stack.size() < _count)
return false;
stack.erase(stack.end() - _count, stack.end());
return true;
}
EXPORT void stack_push(Runtime* _rt, i256 const* _word)
{
auto& stack = _rt->getStack();
stack.push_back(*_word);
if (stack.size() > Stack::maxStackSize)
Stack::maxStackSize = stack.size();
}
EXPORT i256* stack_get(Runtime* _rt, uint64_t _index)
{
auto& stack = _rt->getStack();
return _index < stack.size() ? &*(stack.rbegin() + _index) : nullptr;
}
EXPORT void stack_set(Runtime* _rt, uint64_t _index, i256 const* _word)
{
auto& stack = _rt->getStack();
assert(_index < stack.size());
if (_index >= stack.size())
return;
*(stack.rbegin() + _index) = *_word;
}
EXPORT void ext_calldataload(RuntimeData* _rtData, i256* _index, byte* o_value) EXPORT void ext_calldataload(RuntimeData* _rtData, i256* _index, byte* o_value)
{ {
// It asumes all indexes are less than 2^64 // It asumes all indexes are less than 2^64

15
evmjit/libevmjit/Stack.h

@ -1,6 +1,8 @@
#pragma once #pragma once
#include "CompilerHelper.h" #include <functional>
#include "Array.h"
namespace dev namespace dev
{ {
@ -19,21 +21,22 @@ public:
void set(size_t _index, llvm::Value* _value); void set(size_t _index, llvm::Value* _value);
void pop(size_t _count); void pop(size_t _count);
void push(llvm::Value* _value); void push(llvm::Value* _value);
void free() { m_stack.free(); }
static size_t maxStackSize;
private: private:
llvm::Function* getPopFunc(); llvm::Function* getPopFunc();
llvm::Function* getPushFunc();
llvm::Function* getGetFunc(); llvm::Function* getGetFunc();
llvm::Function* getSetFunc();
RuntimeManager& m_runtimeManager; RuntimeManager& m_runtimeManager;
llvm::Function* m_pop = nullptr; llvm::Function* m_pop = nullptr;
llvm::Function* m_push; llvm::Function* m_push = nullptr;
llvm::Function* m_get = nullptr; llvm::Function* m_get = nullptr;
llvm::Function* m_set; llvm::Function* m_set = nullptr;
llvm::Value* m_arg; Array m_stack;
}; };

6
evmjit/libevmjit/Type.cpp

@ -1,4 +1,7 @@
#include "Type.h" #include "Type.h"
#include <llvm/IR/MDBuilder.h>
#include "RuntimeManager.h" #include "RuntimeManager.h"
namespace dev namespace dev
@ -23,6 +26,7 @@ llvm::PointerType* Type::EnvPtr;
llvm::PointerType* Type::RuntimeDataPtr; llvm::PointerType* Type::RuntimeDataPtr;
llvm::PointerType* Type::RuntimePtr; llvm::PointerType* Type::RuntimePtr;
llvm::ConstantInt* Constant::gasMax; llvm::ConstantInt* Constant::gasMax;
llvm::MDNode* Type::expectTrue;
void Type::init(llvm::LLVMContext& _context) void Type::init(llvm::LLVMContext& _context)
{ {
@ -46,6 +50,8 @@ void Type::init(llvm::LLVMContext& _context)
RuntimePtr = RuntimeManager::getRuntimeType()->getPointerTo(); RuntimePtr = RuntimeManager::getRuntimeType()->getPointerTo();
Constant::gasMax = llvm::ConstantInt::getSigned(Type::Gas, std::numeric_limits<int64_t>::max()); Constant::gasMax = llvm::ConstantInt::getSigned(Type::Gas, std::numeric_limits<int64_t>::max());
expectTrue = llvm::MDBuilder{_context}.createBranchWeights(1, 0);
} }
} }

3
evmjit/libevmjit/Type.h

@ -40,6 +40,9 @@ struct Type
static llvm::PointerType* RuntimeDataPtr; static llvm::PointerType* RuntimeDataPtr;
static llvm::PointerType* RuntimePtr; static llvm::PointerType* RuntimePtr;
// TODO: Redesign static LLVM objects
static llvm::MDNode* expectTrue;
static void init(llvm::LLVMContext& _context); static void init(llvm::LLVMContext& _context);
}; };

22
evmjit/libevmjit/Utils.cpp

@ -1,13 +1,27 @@
#include "Utils.h" #include "Utils.h"
#include <llvm/Support/Debug.h>
#include "BuildInfo.gen.h"
#if !defined(NDEBUG) // Debug
namespace dev namespace dev
{ {
namespace eth namespace evmjit
{
namespace jit
{ {
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

36
evmjit/libevmjit/Utils.h

@ -2,23 +2,39 @@
#include <iostream> #include <iostream>
#include "Common.h" // 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 dev
{ {
namespace eth namespace evmjit
{
namespace jit
{ {
struct JIT: public NoteChannel { static const char* name() { return "JIT"; } }; std::ostream& getLogStream(char const* _channel);
}
}
//#define clog(CHANNEL) std::cerr #define DLOG(CHANNEL) ::dev::evmjit::getLogStream(#CHANNEL)
#define clog(CHANNEL) std::ostream(nullptr)
// The same as assert, but expression is always evaluated and result returned #else // Release
#define CHECK(expr) (assert(expr), expr)
namespace dev
{
namespace evmjit
{
struct Voider
{
void operator=(std::ostream const&) {}
};
} }
} }
}
#define DLOG(CHANNEL) true ? (void)0 : ::dev::evmjit::Voider{} = std::cerr
#endif

5
evmjit/libevmjit/interface.cpp

@ -5,11 +5,6 @@ extern "C"
using namespace dev::eth::jit; using namespace dev::eth::jit;
#ifdef _MSC_VER
#define _ALLOW_KEYWORD_MACROS
#define noexcept throw()
#endif
EXPORT void* evmjit_create() noexcept EXPORT void* evmjit_create() noexcept
{ {
// TODO: Make sure ExecutionEngine constructor does not throw // TODO: Make sure ExecutionEngine constructor does not throw

Loading…
Cancel
Save