Browse Source

Merge remote-tracking branch 'origin/develop-cpp' into develop

cl-refactor
Paweł Bylica 10 years ago
parent
commit
c80851725d
  1. 1
      CMakeLists.txt
  2. 18
      libevmjit-cpp/Env.cpp
  3. 17
      libevmjit-cpp/JitVM.cpp
  4. 10
      libevmjit/BasicBlock.cpp
  5. 17
      libevmjit/BasicBlock.h
  6. 2
      libevmjit/Common.h
  7. 24
      libevmjit/Compiler.cpp
  8. 2
      libevmjit/Compiler.h
  9. 26
      libevmjit/Ext.cpp
  10. 4
      libevmjit/Ext.h
  11. 99
      libevmjit/GasMeter.cpp
  12. 2
      libevmjit/GasMeter.h
  13. 482
      libevmjit/Memory.cpp
  14. 15
      libevmjit/RuntimeManager.cpp
  15. 3
      libevmjit/RuntimeManager.h
  16. 7
      libevmjit/Type.cpp
  17. 4
      libevmjit/Type.h

1
CMakeLists.txt

@ -3,6 +3,7 @@ cmake_minimum_required(VERSION 2.8.12)
project(evmjit) project(evmjit)
set_property(GLOBAL PROPERTY USE_FOLDERS ON) set_property(GLOBAL PROPERTY USE_FOLDERS ON)
set(CMAKE_AUTOMOC OFF)
if(${CMAKE_CXX_COMPILER_ID} STREQUAL "MSVC") if(${CMAKE_CXX_COMPILER_ID} STREQUAL "MSVC")
else() else()

18
libevmjit-cpp/Env.cpp

@ -46,23 +46,22 @@ extern "C"
*o_hash = _env->blockhash(llvm2eth(*_number)); *o_hash = _env->blockhash(llvm2eth(*_number));
} }
EXPORT void env_create(ExtVMFace* _env, i256* io_gas, i256* _endowment, byte* _initBeg, uint64_t _initSize, h256* o_address) EXPORT void env_create(ExtVMFace* _env, int64_t* io_gas, i256* _endowment, byte* _initBeg, uint64_t _initSize, h256* o_address)
{ {
auto endowment = llvm2eth(*_endowment); auto endowment = llvm2eth(*_endowment);
if (_env->balance(_env->myAddress) >= endowment && _env->depth < 1024) if (_env->balance(_env->myAddress) >= endowment && _env->depth < 1024)
{ {
_env->subBalance(endowment); _env->subBalance(endowment);
auto gas = llvm2eth(*io_gas); u256 gas = *io_gas;
OnOpFunc onOp {}; // TODO: Handle that thing h256 address(_env->create(endowment, gas, {_initBeg, _initSize}, {}), h256::AlignRight);
h256 address(_env->create(endowment, gas, {_initBeg, _initSize}, onOp), h256::AlignRight); *io_gas = static_cast<int64_t>(gas);
*io_gas = eth2llvm(gas);
*o_address = address; *o_address = address;
} }
else else
*o_address = {}; *o_address = {};
} }
EXPORT bool env_call(ExtVMFace* _env, i256* 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, h256* _receiveAddress, i256* _value, byte* _inBeg, uint64_t _inSize, byte* _outBeg, uint64_t _outSize, h256* _codeAddress)
{ {
auto value = llvm2eth(*_value); auto value = llvm2eth(*_value);
if (_env->balance(_env->myAddress) >= value && _env->depth < 1024) if (_env->balance(_env->myAddress) >= value && _env->depth < 1024)
@ -71,11 +70,10 @@ extern "C"
auto receiveAddress = right160(*_receiveAddress); auto receiveAddress = right160(*_receiveAddress);
auto inRef = bytesConstRef{_inBeg, _inSize}; auto inRef = bytesConstRef{_inBeg, _inSize};
auto outRef = bytesConstRef{_outBeg, _outSize}; auto outRef = bytesConstRef{_outBeg, _outSize};
OnOpFunc onOp {}; // TODO: Handle that thing
auto codeAddress = right160(*_codeAddress); auto codeAddress = right160(*_codeAddress);
auto gas = llvm2eth(*io_gas); u256 gas = *io_gas;
auto ret = _env->call(receiveAddress, value, inRef, gas, outRef, onOp, {}, codeAddress); auto ret = _env->call(receiveAddress, value, inRef, gas, outRef, {}, {}, codeAddress);
*io_gas = eth2llvm(gas); *io_gas = static_cast<int64_t>(gas);
return ret; return ret;
} }

17
libevmjit-cpp/JitVM.cpp

@ -11,11 +11,14 @@ namespace dev
namespace eth namespace eth
{ {
extern "C" void env_sload(); // fake declaration for linker symbol stripping workaround, see a call below
bytesConstRef JitVM::go(ExtVMFace& _ext, OnOpFunc const& _onOp, uint64_t _step) bytesConstRef JitVM::go(ExtVMFace& _ext, OnOpFunc const& _onOp, uint64_t _step)
{ {
using namespace jit; using namespace jit;
auto rejected = false; auto rejected = false;
// TODO: Rejecting transactions with gas limit > 2^63 can be used by attacker to take JIT out of scope
rejected |= m_gas > std::numeric_limits<decltype(m_data.gas)>::max(); // Do not accept requests with gas > 2^63 (int64 max) rejected |= m_gas > std::numeric_limits<decltype(m_data.gas)>::max(); // Do not accept requests with gas > 2^63 (int64 max)
rejected |= _ext.gasPrice > std::numeric_limits<decltype(m_data.gasPrice)>::max(); rejected |= _ext.gasPrice > std::numeric_limits<decltype(m_data.gasPrice)>::max();
rejected |= _ext.currentBlock.number > std::numeric_limits<decltype(m_data.number)>::max(); rejected |= _ext.currentBlock.number > std::numeric_limits<decltype(m_data.number)>::max();
@ -64,6 +67,9 @@ bytesConstRef JitVM::go(ExtVMFace& _ext, OnOpFunc const& _onOp, uint64_t _step)
BOOST_THROW_EXCEPTION(StackTooSmall()); BOOST_THROW_EXCEPTION(StackTooSmall());
case ReturnCode::BadInstruction: case ReturnCode::BadInstruction:
BOOST_THROW_EXCEPTION(BadInstruction()); BOOST_THROW_EXCEPTION(BadInstruction());
case ReturnCode::LinkerWorkaround: // never happens
env_sload(); // but forces linker to include env_* JIT callback functions
break;
default: default:
break; break;
} }
@ -74,14 +80,3 @@ bytesConstRef JitVM::go(ExtVMFace& _ext, OnOpFunc const& _onOp, uint64_t _step)
} }
} }
namespace
{
// MSVS linker ignores export symbols in Env.cpp if nothing points at least one of them
extern "C" void env_sload();
void linkerWorkaround()
{
env_sload();
(void)&linkerWorkaround; // suppress unused function warning from GCC
}
}

10
libevmjit/BasicBlock.cpp

@ -18,13 +18,14 @@ namespace eth
namespace jit namespace jit
{ {
const char* BasicBlock::NamePrefix = "Instr."; static const char* jumpDestName = "JmpDst.";
static const char* basicBlockName = "Instr.";
BasicBlock::BasicBlock(code_iterator _begin, code_iterator _end, llvm::Function* _mainFunc, llvm::IRBuilder<>& _builder, bool isJumpDest) : BasicBlock::BasicBlock(instr_idx _firstInstrIdx, code_iterator _begin, code_iterator _end, llvm::Function* _mainFunc, llvm::IRBuilder<>& _builder, bool isJumpDest) :
m_firstInstrIdx{_firstInstrIdx},
m_begin(_begin), m_begin(_begin),
m_end(_end), m_end(_end),
// TODO: Add begin index to name m_llvmBB(llvm::BasicBlock::Create(_mainFunc->getContext(), {isJumpDest ? jumpDestName : basicBlockName, std::to_string(_firstInstrIdx)}, _mainFunc)),
m_llvmBB(llvm::BasicBlock::Create(_mainFunc->getContext(), NamePrefix, _mainFunc)),
m_stack(*this), m_stack(*this),
m_builder(_builder), m_builder(_builder),
m_isJumpDest(isJumpDest) m_isJumpDest(isJumpDest)
@ -43,6 +44,7 @@ BasicBlock::LocalStack::LocalStack(BasicBlock& _owner) :
void BasicBlock::LocalStack::push(llvm::Value* _value) void BasicBlock::LocalStack::push(llvm::Value* _value)
{ {
assert(_value->getType() == Type::Word);
m_bblock.m_currentStack.push_back(_value); m_bblock.m_currentStack.push_back(_value);
m_bblock.m_tosOffset += 1; m_bblock.m_tosOffset += 1;
} }

17
libevmjit/BasicBlock.h

@ -11,7 +11,7 @@ namespace eth
namespace jit namespace jit
{ {
using ProgramCounter = uint64_t; // TODO: Rename using instr_idx = uint64_t;
class BasicBlock class BasicBlock
{ {
@ -50,10 +50,7 @@ public:
BasicBlock& m_bblock; BasicBlock& m_bblock;
}; };
/// Basic block name prefix. The rest is instruction index. explicit BasicBlock(instr_idx _firstInstrIdx, code_iterator _begin, code_iterator _end, llvm::Function* _mainFunc, llvm::IRBuilder<>& _builder, bool isJumpDest);
static const char* NamePrefix;
explicit BasicBlock(code_iterator _begin, code_iterator _end, llvm::Function* _mainFunc, llvm::IRBuilder<>& _builder, bool isJumpDest);
explicit BasicBlock(std::string _name, llvm::Function* _mainFunc, llvm::IRBuilder<>& _builder, bool isJumpDest); explicit BasicBlock(std::string _name, llvm::Function* _mainFunc, llvm::IRBuilder<>& _builder, bool isJumpDest);
BasicBlock(const BasicBlock&) = delete; BasicBlock(const BasicBlock&) = delete;
@ -61,8 +58,9 @@ public:
llvm::BasicBlock* llvm() { return m_llvmBB; } llvm::BasicBlock* llvm() { return m_llvmBB; }
code_iterator begin() { return m_begin; } instr_idx firstInstrIdx() const { return m_firstInstrIdx; }
code_iterator end() { return m_end; } code_iterator begin() const { return m_begin; }
code_iterator end() const { return m_end; }
bool isJumpDest() const { return m_isJumpDest; } bool isJumpDest() const { return m_isJumpDest; }
@ -84,8 +82,9 @@ public:
void dump(std::ostream& os, bool _dotOutput = false); void dump(std::ostream& os, bool _dotOutput = false);
private: private:
code_iterator const m_begin = {}; instr_idx const m_firstInstrIdx = 0; ///< Code index of first instruction in the block
code_iterator const m_end = {}; code_iterator const m_begin = {}; ///< Iterator pointing code beginning of the block
code_iterator const m_end = {}; ///< Iterator pointing code end of the block
llvm::BasicBlock* const m_llvmBB; llvm::BasicBlock* const m_llvmBB;

2
libevmjit/Common.h

@ -44,6 +44,8 @@ enum class ReturnCode
LLVMLinkError = -103, LLVMLinkError = -103,
UnexpectedException = -111, UnexpectedException = -111,
LinkerWorkaround = -299,
}; };
/// Representation of 256-bit value binary compatible with LLVM i256 /// Representation of 256-bit value binary compatible with LLVM i256

24
libevmjit/Compiler.cpp

@ -85,7 +85,7 @@ void Compiler::createBasicBlocks(code_iterator _codeBegin, code_iterator _codeEn
{ {
auto beginIdx = begin - _codeBegin; auto beginIdx = begin - _codeBegin;
m_basicBlocks.emplace(std::piecewise_construct, std::forward_as_tuple(beginIdx), m_basicBlocks.emplace(std::piecewise_construct, std::forward_as_tuple(beginIdx),
std::forward_as_tuple(begin, next, m_mainFunc, m_builder, nextJumpDest)); std::forward_as_tuple(beginIdx, begin, next, m_mainFunc, m_builder, nextJumpDest));
nextJumpDest = false; nextJumpDest = false;
begin = next; begin = next;
} }
@ -138,7 +138,6 @@ std::unique_ptr<llvm::Module> Compiler::compile(code_iterator _begin, code_itera
auto entryBlock = llvm::BasicBlock::Create(m_builder.getContext(), "entry", m_mainFunc); auto entryBlock = llvm::BasicBlock::Create(m_builder.getContext(), "entry", m_mainFunc);
m_builder.SetInsertPoint(entryBlock); m_builder.SetInsertPoint(entryBlock);
m_codeBegin = _begin;
createBasicBlocks(_begin, _end); createBasicBlocks(_begin, _end);
// Init runtime structures. // Init runtime structures.
@ -623,7 +622,7 @@ void Compiler::compileBasicBlock(BasicBlock& _basicBlock, RuntimeManager& _runti
case Instruction::PC: case Instruction::PC:
{ {
auto value = Constant::get(it - m_codeBegin); auto value = Constant::get(it - _basicBlock.begin() + _basicBlock.firstInstrIdx());
stack.push(value); stack.push(value);
break; break;
} }
@ -631,7 +630,7 @@ void Compiler::compileBasicBlock(BasicBlock& _basicBlock, RuntimeManager& _runti
case Instruction::GAS: case Instruction::GAS:
{ {
_gasMeter.commitCostBlock(); _gasMeter.commitCostBlock();
stack.push(_runtimeManager.getGas()); stack.push(m_builder.CreateZExt(_runtimeManager.getGas(), Type::Word));
break; break;
} }
@ -741,10 +740,7 @@ void Compiler::compileBasicBlock(BasicBlock& _basicBlock, RuntimeManager& _runti
_memory.require(initOff, initSize); _memory.require(initOff, initSize);
_gasMeter.commitCostBlock(); _gasMeter.commitCostBlock();
auto address = _ext.create(endowment, initOff, initSize);
auto gas = _runtimeManager.getGas();
auto address = _ext.create(gas, endowment, initOff, initSize);
_runtimeManager.setGas(gas);
stack.push(address); stack.push(address);
break; break;
} }
@ -752,7 +748,7 @@ void Compiler::compileBasicBlock(BasicBlock& _basicBlock, RuntimeManager& _runti
case Instruction::CALL: case Instruction::CALL:
case Instruction::CALLCODE: case Instruction::CALLCODE:
{ {
auto gas = stack.pop(); auto callGas256 = 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();
@ -770,9 +766,13 @@ 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);
_gasMeter.count(gas); auto gas = _runtimeManager.getGas();
auto ret = _ext.call(gas, receiveAddress, value, inOff, inSize, outOff, outSize, codeAddress); _gasMeter.count(callGas256);
_gasMeter.giveBack(gas); 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;
} }

2
libevmjit/Compiler.h

@ -76,8 +76,6 @@ private:
/// Main program function /// Main program function
llvm::Function* m_mainFunc = nullptr; llvm::Function* m_mainFunc = nullptr;
code_iterator m_codeBegin = {};
}; };
} }

26
libevmjit/Ext.cpp

@ -41,8 +41,8 @@ std::array<FuncDesc, sizeOf<EnvFunc>::value> const& getEnvFuncDescs()
FuncDesc{"env_sstore", getFunctionType(Type::Void, {Type::EnvPtr, Type::WordPtr, Type::WordPtr})}, FuncDesc{"env_sstore", getFunctionType(Type::Void, {Type::EnvPtr, Type::WordPtr, Type::WordPtr})},
FuncDesc{"env_sha3", getFunctionType(Type::Void, {Type::BytePtr, Type::Size, Type::WordPtr})}, FuncDesc{"env_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::WordPtr, 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::WordPtr, 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::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()})},
@ -60,14 +60,16 @@ llvm::Function* createFunc(EnvFunc _id, llvm::Module* _module)
llvm::Value* Ext::getArgAlloca() llvm::Value* Ext::getArgAlloca()
{ {
auto& a = m_argAllocas[m_argCounter++]; auto& a = m_argAllocas[m_argCounter];
if (!a) if (!a)
{ {
// FIXME: Improve order and names
InsertPointGuard g{getBuilder()}; InsertPointGuard g{getBuilder()};
getBuilder().SetInsertPoint(getMainFunction()->front().getFirstNonPHI()); auto allocaIt = getMainFunction()->front().begin();
a = getBuilder().CreateAlloca(Type::Word, nullptr, "arg"); std::advance(allocaIt, m_argCounter); // Skip already created allocas
getBuilder().SetInsertPoint(allocaIt);
a = getBuilder().CreateAlloca(Type::Word, nullptr, {"a.", std::to_string(m_argCounter)});
} }
++m_argCounter;
return a; return a;
} }
@ -124,30 +126,26 @@ llvm::Value* Ext::blockhash(llvm::Value* _number)
return Endianness::toNative(getBuilder(), hash); return Endianness::toNative(getBuilder(), hash);
} }
llvm::Value* Ext::create(llvm::Value*& _gas, llvm::Value* _endowment, llvm::Value* _initOff, llvm::Value* _initSize) llvm::Value* Ext::create(llvm::Value* _endowment, llvm::Value* _initOff, llvm::Value* _initSize)
{ {
auto gas = byPtr(_gas);
auto ret = getArgAlloca(); auto ret = getArgAlloca();
auto begin = m_memoryMan.getBytePtr(_initOff); auto begin = m_memoryMan.getBytePtr(_initOff);
auto size = m_builder.CreateTrunc(_initSize, Type::Size, "size"); auto size = m_builder.CreateTrunc(_initSize, Type::Size, "size");
createCall(EnvFunc::create, {getRuntimeManager().getEnvPtr(), gas, byPtr(_endowment), begin, size, ret}); createCall(EnvFunc::create, {getRuntimeManager().getEnvPtr(), getRuntimeManager().getGasPtr(), byPtr(_endowment), begin, size, ret});
_gas = m_builder.CreateLoad(gas); // Return gas
llvm::Value* address = m_builder.CreateLoad(ret); llvm::Value* address = m_builder.CreateLoad(ret);
address = Endianness::toNative(m_builder, address); address = Endianness::toNative(m_builder, address);
return address; return address;
} }
llvm::Value* Ext::call(llvm::Value*& _gas, 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* _receiveAddress, llvm::Value* _value, llvm::Value* _inOff, llvm::Value* _inSize, llvm::Value* _outOff, llvm::Value* _outSize, llvm::Value* _codeAddress)
{ {
auto gas = byPtr(_gas);
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);
auto inSize = m_builder.CreateTrunc(_inSize, Type::Size, "in.size"); auto inSize = m_builder.CreateTrunc(_inSize, Type::Size, "in.size");
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(), gas, byPtr(receiveAddress), byPtr(_value), inBeg, inSize, outBeg, outSize, byPtr(codeAddress)}); auto ret = createCall(EnvFunc::call, {getRuntimeManager().getEnvPtr(), getRuntimeManager().getGasPtr(), byPtr(receiveAddress), byPtr(_value), inBeg, inSize, outBeg, outSize, byPtr(codeAddress)});
_gas = m_builder.CreateLoad(gas); // Return gas
return m_builder.CreateZExt(ret, Type::Word, "ret"); return m_builder.CreateZExt(ret, Type::Word, "ret");
} }

4
libevmjit/Ext.h

@ -50,8 +50,8 @@ 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*& _gas, 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*& _gas, 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* _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);

99
libevmjit/GasMeter.cpp

@ -19,29 +19,29 @@ namespace jit
namespace // Helper functions namespace // Helper functions
{ {
uint64_t const c_stepGas = 1; int64_t const c_stepGas = 1;
uint64_t const c_balanceGas = 20; int64_t const c_balanceGas = 20;
uint64_t const c_sha3Gas = 10; int64_t const c_sha3Gas = 10;
uint64_t const c_sha3WordGas = 10; int64_t const c_sha3WordGas = 10;
uint64_t const c_sloadGas = 20; int64_t const c_sloadGas = 20;
uint64_t const c_sstoreSetGas = 300; int64_t const c_sstoreSetGas = 300;
uint64_t const c_sstoreResetGas = 100; int64_t const c_sstoreResetGas = 100;
uint64_t const c_sstoreRefundGas = 100; int64_t const c_sstoreRefundGas = 100;
uint64_t const c_createGas = 100; int64_t const c_createGas = 100;
uint64_t const c_createDataGas = 5; int64_t const c_createDataGas = 5;
uint64_t const c_callGas = 20; int64_t const c_callGas = 20;
uint64_t const c_expGas = 1; int64_t const c_expGas = 1;
uint64_t const c_expByteGas = 1; int64_t const c_expByteGas = 1;
uint64_t const c_memoryGas = 1; int64_t const c_memoryGas = 1;
uint64_t const c_txDataZeroGas = 1; int64_t const c_txDataZeroGas = 1;
uint64_t const c_txDataNonZeroGas = 5; int64_t const c_txDataNonZeroGas = 5;
uint64_t const c_txGas = 500; int64_t const c_txGas = 500;
uint64_t const c_logGas = 32; int64_t const c_logGas = 32;
uint64_t const c_logDataGas = 1; int64_t const c_logDataGas = 1;
uint64_t const c_logTopicGas = 32; int64_t const c_logTopicGas = 32;
uint64_t const c_copyGas = 1; int64_t const c_copyGas = 1;
uint64_t getStepCost(Instruction inst) int64_t getStepCost(Instruction inst)
{ {
switch (inst) switch (inst)
{ {
@ -72,7 +72,7 @@ uint64_t getStepCost(Instruction inst)
case Instruction::LOG3: case Instruction::LOG3:
case Instruction::LOG4: case Instruction::LOG4:
{ {
auto numTopics = static_cast<uint64_t>(inst) - static_cast<uint64_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;
} }
} }
@ -86,7 +86,7 @@ GasMeter::GasMeter(llvm::IRBuilder<>& _builder, RuntimeManager& _runtimeManager)
{ {
auto module = getModule(); auto module = getModule();
llvm::Type* gasCheckArgs[] = {Type::RuntimePtr, Type::Word}; llvm::Type* gasCheckArgs[] = {Type::RuntimePtr, Type::Gas};
m_gasCheckFunc = llvm::Function::Create(llvm::FunctionType::get(Type::Void, gasCheckArgs, false), llvm::Function::PrivateLinkage, "gas.check", module); m_gasCheckFunc = llvm::Function::Create(llvm::FunctionType::get(Type::Void, gasCheckArgs, false), llvm::Function::PrivateLinkage, "gas.check", module);
InsertPointGuard guard(m_builder); InsertPointGuard guard(m_builder);
@ -94,14 +94,15 @@ GasMeter::GasMeter(llvm::IRBuilder<>& _builder, RuntimeManager& _runtimeManager)
auto outOfGasBB = llvm::BasicBlock::Create(_builder.getContext(), "OutOfGas", 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 rt = &m_gasCheckFunc->getArgumentList().front();
rt->setName("rt");
auto cost = rt->getNextNode();
cost->setName("cost");
m_builder.SetInsertPoint(checkBB); m_builder.SetInsertPoint(checkBB);
auto arg = m_gasCheckFunc->arg_begin();
arg->setName("rt");
++arg;
arg->setName("cost");
auto cost = arg;
auto gas = m_runtimeManager.getGas(); auto gas = m_runtimeManager.getGas();
auto isOutOfGas = m_builder.CreateICmpUGT(cost, gas, "isOutOfGas"); gas = 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
m_builder.CreateCondBr(isOutOfGas, outOfGasBB, updateBB); m_builder.CreateCondBr(isOutOfGas, outOfGasBB, updateBB);
m_builder.SetInsertPoint(outOfGasBB); m_builder.SetInsertPoint(outOfGasBB);
@ -109,7 +110,6 @@ GasMeter::GasMeter(llvm::IRBuilder<>& _builder, RuntimeManager& _runtimeManager)
m_builder.CreateUnreachable(); m_builder.CreateUnreachable();
m_builder.SetInsertPoint(updateBB); m_builder.SetInsertPoint(updateBB);
gas = m_builder.CreateSub(gas, cost);
m_runtimeManager.setGas(gas); m_runtimeManager.setGas(gas);
m_builder.CreateRetVoid(); m_builder.CreateRetVoid();
} }
@ -119,7 +119,7 @@ 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::Word)}); m_checkCall = createCall(m_gasCheckFunc, {m_runtimeManager.getRuntimePtr(), llvm::UndefValue::get(Type::Gas)});
} }
m_blockCost += getStepCost(_inst); m_blockCost += getStepCost(_inst);
@ -127,6 +127,15 @@ void GasMeter::count(Instruction _inst)
void GasMeter::count(llvm::Value* _cost) void GasMeter::count(llvm::Value* _cost)
{ {
if (_cost->getType() == Type::Word)
{
auto gasMax256 = m_builder.CreateZExt(Constant::gasMax, Type::Word);
auto tooHigh = m_builder.CreateICmpUGT(_cost, gasMax256, "costTooHigh");
auto cost64 = m_builder.CreateTrunc(_cost, Type::Gas);
_cost = m_builder.CreateSelect(tooHigh, Constant::gasMax, cost64, "cost");
}
assert(_cost->getType() == Type::Gas);
createCall(m_gasCheckFunc, {m_runtimeManager.getRuntimePtr(), _cost}); createCall(m_gasCheckFunc, {m_runtimeManager.getRuntimePtr(), _cost});
} }
@ -136,12 +145,13 @@ void GasMeter::countExp(llvm::Value* _exponent)
// lz - leading zeros // lz - leading zeros
// cost = ((256 - lz) + 7) / 8 // cost = ((256 - lz) + 7) / 8
// OPT: All calculations can be done on 32/64 bits // OPT: Can gas update be done in exp algorithm?
auto ctlz = llvm::Intrinsic::getDeclaration(getModule(), llvm::Intrinsic::ctlz, Type::Word); auto ctlz = llvm::Intrinsic::getDeclaration(getModule(), llvm::Intrinsic::ctlz, Type::Word);
auto lz = m_builder.CreateCall2(ctlz, _exponent, m_builder.getInt1(false)); auto lz256 = m_builder.CreateCall2(ctlz, _exponent, m_builder.getInt1(false));
auto sigBits = m_builder.CreateSub(Constant::get(256), lz); auto lz = m_builder.CreateTrunc(lz256, Type::Gas, "lz");
auto sigBytes = m_builder.CreateUDiv(m_builder.CreateAdd(sigBits, Constant::get(7)), Constant::get(8)); 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(sigBytes); count(sigBytes);
} }
@ -154,8 +164,8 @@ void GasMeter::countSStore(Ext& _ext, llvm::Value* _index, llvm::Value* _newValu
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"); auto isDelete = m_builder.CreateAnd(oldValueIsntZero, newValueIsZero, "isDelete");
auto cost = m_builder.CreateSelect(isInsert, Constant::get(c_sstoreSetGas), Constant::get(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, Constant::get(0), cost, "cost"); cost = m_builder.CreateSelect(isDelete, m_builder.getInt64(0), cost, "cost");
count(cost); count(cost);
} }
@ -173,17 +183,16 @@ void GasMeter::countSha3Data(llvm::Value* _dataLength)
assert(m_blockCost > 0); // SHA3 instruction is already counted assert(m_blockCost > 0); // SHA3 instruction is already counted
// TODO: This round ups to 32 happens in many places // TODO: This round ups to 32 happens in many places
// FIXME: 64-bit arith used, but not verified
static_assert(c_sha3WordGas != 1, "SHA3 data cost has changed. Update GasMeter"); static_assert(c_sha3WordGas != 1, "SHA3 data cost has changed. Update GasMeter");
auto dataLength64 = getBuilder().CreateTrunc(_dataLength, Type::lowPrecision); auto dataLength64 = getBuilder().CreateTrunc(_dataLength, Type::Gas);
auto words64 = m_builder.CreateUDiv(m_builder.CreateAdd(dataLength64, getBuilder().getInt64(31)), getBuilder().getInt64(32)); auto words64 = m_builder.CreateUDiv(m_builder.CreateNUWAdd(dataLength64, getBuilder().getInt64(31)), getBuilder().getInt64(32));
auto cost64 = m_builder.CreateNUWMul(getBuilder().getInt64(c_sha3WordGas), words64); auto cost64 = m_builder.CreateNUWMul(getBuilder().getInt64(c_sha3WordGas), words64);
auto cost = getBuilder().CreateZExt(cost64, Type::Word); count(cost64);
count(cost);
} }
void GasMeter::giveBack(llvm::Value* _gas) void GasMeter::giveBack(llvm::Value* _gas)
{ {
assert(_gas->getType() == Type::Gas);
m_runtimeManager.setGas(m_builder.CreateAdd(m_runtimeManager.getGas(), _gas)); m_runtimeManager.setGas(m_builder.CreateAdd(m_runtimeManager.getGas(), _gas));
} }
@ -199,7 +208,7 @@ void GasMeter::commitCostBlock()
return; return;
} }
m_checkCall->setArgOperand(1, Constant::get(m_blockCost)); // Update block cost in gas check call m_checkCall->setArgOperand(1, m_builder.getInt64(m_blockCost)); // Update block cost in gas check call
m_checkCall = nullptr; // End cost-block m_checkCall = nullptr; // End cost-block
m_blockCost = 0; m_blockCost = 0;
} }

2
libevmjit/GasMeter.h

@ -50,7 +50,7 @@ public:
private: private:
/// Cumulative gas cost of a block of instructions /// Cumulative gas cost of a block of instructions
/// @TODO Handle overflow /// @TODO Handle overflow
uint64_t m_blockCost = 0; int64_t m_blockCost = 0;
llvm::CallInst* m_checkCall = nullptr; llvm::CallInst* m_checkCall = nullptr;
llvm::Function* m_gasCheckFunc = nullptr; llvm::Function* m_gasCheckFunc = nullptr;

482
libevmjit/Memory.cpp

@ -1,239 +1,243 @@
#include "Memory.h" #include "Memory.h"
#include <vector> #include <vector>
#include <iostream> #include <iostream>
#include <iomanip> #include <iomanip>
#include <cstdint> #include <cstdint>
#include <cassert> #include <cassert>
#include <llvm/IR/GlobalVariable.h> #include <llvm/IR/GlobalVariable.h>
#include <llvm/IR/Function.h> #include <llvm/IR/Function.h>
#include <llvm/IR/IntrinsicInst.h> #include <llvm/IR/IntrinsicInst.h>
#include "Type.h" #include "Type.h"
#include "Runtime.h" #include "Runtime.h"
#include "GasMeter.h" #include "GasMeter.h"
#include "Endianness.h" #include "Endianness.h"
#include "RuntimeManager.h" #include "RuntimeManager.h"
namespace dev namespace dev
{ {
namespace eth namespace eth
{ {
namespace jit 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_gasMeter(_gasMeter) m_gasMeter(_gasMeter)
{ {
llvm::Type* resizeArgs[] = {Type::RuntimePtr, Type::WordPtr}; llvm::Type* resizeArgs[] = {Type::RuntimePtr, Type::WordPtr};
m_resize = llvm::Function::Create(llvm::FunctionType::get(Type::BytePtr, resizeArgs, false), llvm::Function::ExternalLinkage, "mem_resize", getModule()); m_resize = llvm::Function::Create(llvm::FunctionType::get(Type::BytePtr, resizeArgs, false), llvm::Function::ExternalLinkage, "mem_resize", getModule());
llvm::AttrBuilder attrBuilder; llvm::AttrBuilder attrBuilder;
attrBuilder.addAttribute(llvm::Attribute::NoAlias).addAttribute(llvm::Attribute::NoCapture).addAttribute(llvm::Attribute::NonNull).addAttribute(llvm::Attribute::ReadOnly); attrBuilder.addAttribute(llvm::Attribute::NoAlias).addAttribute(llvm::Attribute::NoCapture).addAttribute(llvm::Attribute::NonNull).addAttribute(llvm::Attribute::ReadOnly);
m_resize->setAttributes(llvm::AttributeSet::get(m_resize->getContext(), 1, attrBuilder)); m_resize->setAttributes(llvm::AttributeSet::get(m_resize->getContext(), 1, attrBuilder));
m_require = createRequireFunc(_gasMeter); m_require = createRequireFunc(_gasMeter);
m_loadWord = createFunc(false, Type::Word, _gasMeter); m_loadWord = createFunc(false, Type::Word, _gasMeter);
m_storeWord = createFunc(true, Type::Word, _gasMeter); m_storeWord = createFunc(true, Type::Word, _gasMeter);
m_storeByte = createFunc(true, Type::Byte, _gasMeter); m_storeByte = createFunc(true, Type::Byte, _gasMeter);
} }
llvm::Function* Memory::createRequireFunc(GasMeter& _gasMeter) llvm::Function* Memory::createRequireFunc(GasMeter& _gasMeter)
{ {
llvm::Type* argTypes[] = {Type::RuntimePtr, Type::Word, Type::Word}; llvm::Type* argTypes[] = {Type::RuntimePtr, Type::Word, Type::Word};
auto func = llvm::Function::Create(llvm::FunctionType::get(Type::Void, argTypes, false), llvm::Function::PrivateLinkage, "mem.require", getModule()); auto func = llvm::Function::Create(llvm::FunctionType::get(Type::Void, argTypes, false), llvm::Function::PrivateLinkage, "mem.require", getModule());
auto rt = func->arg_begin(); auto rt = func->arg_begin();
rt->setName("rt"); rt->setName("rt");
auto offset = rt->getNextNode(); auto offset = rt->getNextNode();
offset->setName("offset"); offset->setName("offset");
auto size = offset->getNextNode(); auto size = offset->getNextNode();
size->setName("size"); size->setName("size");
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);
auto resizeBB = llvm::BasicBlock::Create(func->getContext(), "Resize", func); auto resizeBB = llvm::BasicBlock::Create(func->getContext(), "Resize", func);
auto returnBB = llvm::BasicBlock::Create(func->getContext(), "Return", func); auto returnBB = llvm::BasicBlock::Create(func->getContext(), "Return", func);
InsertPointGuard guard(m_builder); // Restores insert point at function exit InsertPointGuard guard(m_builder); // Restores insert point at function exit
// 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)); auto sizeIsZero = m_builder.CreateICmpEQ(size, Constant::get(0));
m_builder.CreateCondBr(sizeIsZero, returnBB, checkBB); 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); auto uaddWO = llvm::Intrinsic::getDeclaration(getModule(), llvm::Intrinsic::uadd_with_overflow, Type::Word);
auto uaddRes = m_builder.CreateCall2(uaddWO, offset, size, "res"); auto uaddRes = m_builder.CreateCall2(uaddWO, offset, size, "res");
auto sizeRequired = m_builder.CreateExtractValue(uaddRes, 0, "sizeReq"); auto sizeRequired = m_builder.CreateExtractValue(uaddRes, 0, "sizeReq");
auto overflow1 = m_builder.CreateExtractValue(uaddRes, 1, "overflow1"); auto overflow1 = m_builder.CreateExtractValue(uaddRes, 1, "overflow1");
auto rtPtr = getRuntimeManager().getRuntimePtr(); auto rtPtr = getRuntimeManager().getRuntimePtr();
auto sizePtr = m_builder.CreateStructGEP(rtPtr, 4); auto sizePtr = m_builder.CreateStructGEP(rtPtr, 4);
auto currSize = m_builder.CreateLoad(sizePtr, "currSize"); auto currSize = m_builder.CreateLoad(sizePtr, "currSize");
auto tooSmall = m_builder.CreateICmpULE(currSize, sizeRequired, "tooSmall"); auto tooSmall = m_builder.CreateICmpULE(currSize, sizeRequired, "tooSmall");
auto resizeNeeded = m_builder.CreateOr(tooSmall, overflow1, "resizeNeeded"); auto resizeNeeded = m_builder.CreateOr(tooSmall, overflow1, "resizeNeeded");
m_builder.CreateCondBr(resizeNeeded, resizeBB, returnBB); // OPT branch weights? m_builder.CreateCondBr(resizeNeeded, resizeBB, returnBB); // OPT branch weights?
// 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"); uaddRes = m_builder.CreateCall2(uaddWO, sizeRequired, Constant::get(31), "res");
auto wordsRequired = m_builder.CreateExtractValue(uaddRes, 0); auto wordsRequired = m_builder.CreateExtractValue(uaddRes, 0);
auto overflow2 = m_builder.CreateExtractValue(uaddRes, 1, "overflow2"); auto overflow2 = m_builder.CreateExtractValue(uaddRes, 1, "overflow2");
auto overflow = m_builder.CreateOr(overflow1, overflow2, "overflow"); auto overflow = m_builder.CreateOr(overflow1, overflow2, "overflow");
wordsRequired = m_builder.CreateSelect(overflow, Constant::get(-1), wordsRequired); wordsRequired = m_builder.CreateSelect(overflow, Constant::get(-1), wordsRequired);
wordsRequired = m_builder.CreateUDiv(wordsRequired, Constant::get(32), "wordsReq"); wordsRequired = m_builder.CreateUDiv(wordsRequired, Constant::get(32), "wordsReq");
sizeRequired = m_builder.CreateMul(wordsRequired, Constant::get(32), "roundedSizeReq"); sizeRequired = m_builder.CreateMul(wordsRequired, Constant::get(32), "roundedSizeReq");
auto words = m_builder.CreateUDiv(currSize, Constant::get(32), "words"); // size is always 32*k auto words = m_builder.CreateUDiv(currSize, Constant::get(32), "words"); // size is always 32*k
auto newWords = m_builder.CreateSub(wordsRequired, words, "addtionalWords"); auto newWords = m_builder.CreateSub(wordsRequired, words, "addtionalWords");
_gasMeter.countMemory(newWords); _gasMeter.countMemory(newWords);
// Resize // Resize
m_builder.CreateStore(sizeRequired, sizePtr); m_builder.CreateStore(sizeRequired, sizePtr);
auto newData = m_builder.CreateCall2(m_resize, rt, sizePtr, "newData"); auto newData = m_builder.CreateCall2(m_resize, rt, sizePtr, "newData");
auto dataPtr = m_builder.CreateStructGEP(rtPtr, 3); auto dataPtr = m_builder.CreateStructGEP(rtPtr, 3);
m_builder.CreateStore(newData, dataPtr); m_builder.CreateStore(newData, dataPtr);
m_builder.CreateBr(returnBB); m_builder.CreateBr(returnBB);
// BB "Return" // BB "Return"
m_builder.SetInsertPoint(returnBB); m_builder.SetInsertPoint(returnBB);
m_builder.CreateRetVoid(); m_builder.CreateRetVoid();
return func; return func;
} }
llvm::Function* Memory::createFunc(bool _isStore, llvm::Type* _valueType, GasMeter&) llvm::Function* Memory::createFunc(bool _isStore, llvm::Type* _valueType, GasMeter&)
{ {
auto isWord = _valueType == Type::Word; auto isWord = _valueType == Type::Word;
llvm::Type* storeArgs[] = {Type::RuntimePtr, Type::Word, _valueType}; llvm::Type* storeArgs[] = {Type::RuntimePtr, Type::Word, _valueType};
llvm::Type* loadArgs[] = {Type::RuntimePtr, Type::Word}; llvm::Type* loadArgs[] = {Type::RuntimePtr, 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());
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 rt = func->arg_begin();
rt->setName("rt"); rt->setName("rt");
auto index = rt->getNextNode(); auto index = rt->getNextNode();
index->setName("index"); index->setName("index");
auto valueSize = _valueType->getPrimitiveSizeInBits() / 8; auto valueSize = _valueType->getPrimitiveSizeInBits() / 8;
this->require(index, Constant::get(valueSize)); this->require(index, Constant::get(valueSize));
auto ptr = getBytePtr(index); auto ptr = getBytePtr(index);
if (isWord) if (isWord)
ptr = m_builder.CreateBitCast(ptr, Type::WordPtr, "wordPtr"); ptr = m_builder.CreateBitCast(ptr, Type::WordPtr, "wordPtr");
if (_isStore) if (_isStore)
{ {
llvm::Value* value = index->getNextNode(); llvm::Value* value = index->getNextNode();
value->setName("value"); value->setName("value");
if (isWord) if (isWord)
value = Endianness::toBE(m_builder, value); value = Endianness::toBE(m_builder, value);
m_builder.CreateStore(value, ptr); m_builder.CreateStore(value, ptr);
m_builder.CreateRetVoid(); m_builder.CreateRetVoid();
} }
else else
{ {
llvm::Value* ret = m_builder.CreateLoad(ptr); llvm::Value* ret = m_builder.CreateLoad(ptr);
ret = Endianness::toNative(m_builder, ret); ret = Endianness::toNative(m_builder, ret);
m_builder.CreateRet(ret); m_builder.CreateRet(ret);
} }
return func; return func;
} }
llvm::Value* Memory::loadWord(llvm::Value* _addr) llvm::Value* Memory::loadWord(llvm::Value* _addr)
{ {
return createCall(m_loadWord, {getRuntimeManager().getRuntimePtr(), _addr}); return createCall(m_loadWord, {getRuntimeManager().getRuntimePtr(), _addr});
} }
void Memory::storeWord(llvm::Value* _addr, llvm::Value* _word) void Memory::storeWord(llvm::Value* _addr, llvm::Value* _word)
{ {
createCall(m_storeWord, {getRuntimeManager().getRuntimePtr(), _addr, _word}); createCall(m_storeWord, {getRuntimeManager().getRuntimePtr(), _addr, _word});
} }
void Memory::storeByte(llvm::Value* _addr, llvm::Value* _word) void Memory::storeByte(llvm::Value* _addr, llvm::Value* _word)
{ {
auto byte = m_builder.CreateTrunc(_word, Type::Byte, "byte"); auto byte = m_builder.CreateTrunc(_word, Type::Byte, "byte");
createCall(m_storeByte, {getRuntimeManager().getRuntimePtr(), _addr, byte}); createCall(m_storeByte, {getRuntimeManager().getRuntimePtr(), _addr, byte});
} }
llvm::Value* Memory::getData() llvm::Value* Memory::getData()
{ {
auto rtPtr = getRuntimeManager().getRuntimePtr(); auto rtPtr = getRuntimeManager().getRuntimePtr();
auto dataPtr = m_builder.CreateStructGEP(rtPtr, 3); auto dataPtr = m_builder.CreateStructGEP(rtPtr, 3);
return m_builder.CreateLoad(dataPtr, "data"); return m_builder.CreateLoad(dataPtr, "data");
} }
llvm::Value* Memory::getSize() llvm::Value* Memory::getSize()
{ {
auto rtPtr = getRuntimeManager().getRuntimePtr(); auto rtPtr = getRuntimeManager().getRuntimePtr();
auto sizePtr = m_builder.CreateStructGEP(rtPtr, 4); auto sizePtr = m_builder.CreateStructGEP(rtPtr, 4);
return m_builder.CreateLoad(sizePtr, "size"); return m_builder.CreateLoad(sizePtr, "size");
} }
llvm::Value* Memory::getBytePtr(llvm::Value* _index) llvm::Value* Memory::getBytePtr(llvm::Value* _index)
{ {
auto idx = m_builder.CreateTrunc(_index, Type::Size, "idx"); // Never allow memory index be a type bigger than i64 auto idx = m_builder.CreateTrunc(_index, Type::Size, "idx"); // Never allow memory index be a type bigger than i64
return m_builder.CreateGEP(getData(), idx, "ptr"); return m_builder.CreateGEP(getData(), idx, "ptr");
} }
void Memory::require(llvm::Value* _offset, llvm::Value* _size) void Memory::require(llvm::Value* _offset, llvm::Value* _size)
{ {
createCall(m_require, {getRuntimeManager().getRuntimePtr(), _offset, _size}); if (auto constant = llvm::dyn_cast<llvm::ConstantInt>(_size))
} {
if (!constant->getValue())
void Memory::copyBytes(llvm::Value* _srcPtr, llvm::Value* _srcSize, llvm::Value* _srcIdx, return;
llvm::Value* _destMemIdx, llvm::Value* _reqBytes) }
{ createCall(m_require, {getRuntimeManager().getRuntimePtr(), _offset, _size});
require(_destMemIdx, _reqBytes); }
// Additional copy cost void Memory::copyBytes(llvm::Value* _srcPtr, llvm::Value* _srcSize, llvm::Value* _srcIdx,
// TODO: This round ups to 32 happens in many places llvm::Value* _destMemIdx, llvm::Value* _reqBytes)
auto copyWords = m_builder.CreateUDiv(m_builder.CreateAdd(_reqBytes, Constant::get(31)), Constant::get(32)); {
m_gasMeter.countCopy(copyWords); require(_destMemIdx, _reqBytes);
// Algorithm: // Additional copy cost
// isOutsideData = idx256 >= size256 // TODO: This round ups to 32 happens in many places
// idx64 = trunc idx256 auto reqBytes = m_builder.CreateTrunc(_reqBytes, Type::Gas);
// size64 = trunc size256 auto copyWords = m_builder.CreateUDiv(m_builder.CreateNUWAdd(reqBytes, m_builder.getInt64(31)), m_builder.getInt64(32));
// dataLeftSize = size64 - idx64 // safe if not isOutsideData m_gasMeter.countCopy(copyWords);
// reqBytes64 = trunc _reqBytes // require() handles large values
// bytesToCopy0 = select(reqBytes64 > dataLeftSize, dataSizeLeft, reqBytes64) // min // Algorithm:
// bytesToCopy = select(isOutsideData, 0, bytesToCopy0) // isOutsideData = idx256 >= size256
// idx64 = trunc idx256
auto isOutsideData = m_builder.CreateICmpUGE(_srcIdx, _srcSize); // size64 = trunc size256
auto idx64 = m_builder.CreateTrunc(_srcIdx, Type::lowPrecision); // dataLeftSize = size64 - idx64 // safe if not isOutsideData
auto size64 = m_builder.CreateTrunc(_srcSize, Type::lowPrecision); // reqBytes64 = trunc _reqBytes // require() handles large values
auto dataLeftSize = m_builder.CreateNUWSub(size64, idx64); // bytesToCopy0 = select(reqBytes64 > dataLeftSize, dataSizeLeft, reqBytes64) // min
auto reqBytes64 = m_builder.CreateTrunc(_reqBytes, Type::lowPrecision); // bytesToCopy = select(isOutsideData, 0, bytesToCopy0)
auto outOfBound = m_builder.CreateICmpUGT(reqBytes64, dataLeftSize);
auto bytesToCopyInner = m_builder.CreateSelect(outOfBound, dataLeftSize, reqBytes64); auto isOutsideData = m_builder.CreateICmpUGE(_srcIdx, _srcSize);
auto zero64 = llvm::ConstantInt::get(Type::lowPrecision, 0); // TODO: Cache common constants auto idx64 = m_builder.CreateTrunc(_srcIdx, Type::Size);
auto bytesToCopy = m_builder.CreateSelect(isOutsideData, zero64, bytesToCopyInner); auto size64 = m_builder.CreateTrunc(_srcSize, Type::Size);
auto dataLeftSize = m_builder.CreateNUWSub(size64, idx64);
auto src = m_builder.CreateGEP(_srcPtr, idx64, "src"); auto outOfBound = m_builder.CreateICmpUGT(reqBytes, dataLeftSize);
auto dstIdx = m_builder.CreateTrunc(_destMemIdx, Type::Size, "dstIdx"); // Never allow memory index be a type bigger than i64 auto bytesToCopyInner = m_builder.CreateSelect(outOfBound, dataLeftSize, reqBytes);
auto dst = m_builder.CreateGEP(getData(), dstIdx, "dst"); auto bytesToCopy = m_builder.CreateSelect(isOutsideData, m_builder.getInt64(0), bytesToCopyInner);
m_builder.CreateMemCpy(dst, src, bytesToCopy, 0);
} 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 dst = m_builder.CreateGEP(getData(), dstIdx, "dst");
} m_builder.CreateMemCpy(dst, src, bytesToCopy, 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 extern "C"
{ {
auto size = _size->a; // Trunc to 64-bit using namespace dev::eth::jit;
auto& memory = _rt->getMemory();
memory.resize(size); EXPORT byte* mem_resize(Runtime* _rt, i256* _size) // TODO: Use uint64 as size OR use realloc in LLVM IR
return memory.data(); {
} auto size = _size->a; // Trunc to 64-bit
} auto& memory = _rt->getMemory();
memory.resize(size);
return memory.data();
}
}

15
libevmjit/RuntimeManager.cpp

@ -216,15 +216,20 @@ llvm::Value* RuntimeManager::getJmpBuf()
llvm::Value* RuntimeManager::getGas() llvm::Value* RuntimeManager::getGas()
{ {
auto value = get(RuntimeData::Gas); auto gas = get(RuntimeData::Gas);
assert(value->getType() == Type::Size); assert(gas->getType() == Type::Gas);
return getBuilder().CreateZExt(value, Type::Word); return gas;
}
llvm::Value* RuntimeManager::getGasPtr()
{
return getPtr(RuntimeData::Gas);
} }
void RuntimeManager::setGas(llvm::Value* _gas) void RuntimeManager::setGas(llvm::Value* _gas)
{ {
auto newGas = getBuilder().CreateTrunc(_gas, Type::Size); assert(_gas->getType() == Type::Gas);
set(RuntimeData::Gas, newGas); set(RuntimeData::Gas, _gas);
} }
} }

3
libevmjit/RuntimeManager.h

@ -23,7 +23,8 @@ public:
llvm::Value* get(RuntimeData::Index _index); llvm::Value* get(RuntimeData::Index _index);
llvm::Value* get(Instruction _inst); llvm::Value* get(Instruction _inst);
llvm::Value* getGas(); // TODO: Remove llvm::Value* getGas();
llvm::Value* getGasPtr();
llvm::Value* getCallData(); llvm::Value* getCallData();
llvm::Value* getCode(); llvm::Value* getCode();
llvm::Value* getCodeSize(); llvm::Value* getCodeSize();

7
libevmjit/Type.cpp

@ -17,6 +17,8 @@ llvm::PointerType* Type::WordPtr;
llvm::IntegerType* Type::lowPrecision; llvm::IntegerType* Type::lowPrecision;
llvm::IntegerType* Type::Bool; llvm::IntegerType* Type::Bool;
llvm::IntegerType* Type::Size; llvm::IntegerType* Type::Size;
llvm::IntegerType* Type::Gas;
llvm::PointerType* Type::GasPtr;
llvm::IntegerType* Type::Byte; llvm::IntegerType* Type::Byte;
llvm::PointerType* Type::BytePtr; llvm::PointerType* Type::BytePtr;
llvm::Type* Type::Void; llvm::Type* Type::Void;
@ -24,6 +26,7 @@ llvm::IntegerType* Type::MainReturn;
llvm::PointerType* Type::EnvPtr; llvm::PointerType* Type::EnvPtr;
llvm::PointerType* Type::RuntimeDataPtr; llvm::PointerType* Type::RuntimeDataPtr;
llvm::PointerType* Type::RuntimePtr; llvm::PointerType* Type::RuntimePtr;
llvm::ConstantInt* Constant::gasMax;
void Type::init(llvm::LLVMContext& _context) void Type::init(llvm::LLVMContext& _context)
{ {
@ -35,6 +38,8 @@ void Type::init(llvm::LLVMContext& _context)
// TODO: Size should be architecture-dependent // TODO: Size should be architecture-dependent
Bool = llvm::Type::getInt1Ty(_context); Bool = llvm::Type::getInt1Ty(_context);
Size = llvm::Type::getInt64Ty(_context); Size = llvm::Type::getInt64Ty(_context);
Gas = Size;
GasPtr = Gas->getPointerTo();
Byte = llvm::Type::getInt8Ty(_context); Byte = llvm::Type::getInt8Ty(_context);
BytePtr = Byte->getPointerTo(); BytePtr = Byte->getPointerTo();
Void = llvm::Type::getVoidTy(_context); Void = llvm::Type::getVoidTy(_context);
@ -43,6 +48,8 @@ void Type::init(llvm::LLVMContext& _context)
EnvPtr = llvm::StructType::create(_context, "Env")->getPointerTo(); EnvPtr = llvm::StructType::create(_context, "Env")->getPointerTo();
RuntimeDataPtr = RuntimeManager::getRuntimeDataType()->getPointerTo(); RuntimeDataPtr = RuntimeManager::getRuntimeDataType()->getPointerTo();
RuntimePtr = RuntimeManager::getRuntimeType()->getPointerTo(); RuntimePtr = RuntimeManager::getRuntimeType()->getPointerTo();
Constant::gasMax = llvm::ConstantInt::getSigned(Type::Gas, std::numeric_limits<int64_t>::max());
} }
} }

4
libevmjit/Type.h

@ -23,6 +23,8 @@ struct Type
static llvm::IntegerType* Bool; static llvm::IntegerType* Bool;
static llvm::IntegerType* Size; static llvm::IntegerType* Size;
static llvm::IntegerType* Gas;
static llvm::PointerType* GasPtr;
static llvm::IntegerType* Byte; static llvm::IntegerType* Byte;
static llvm::PointerType* BytePtr; static llvm::PointerType* BytePtr;
@ -41,6 +43,8 @@ struct Type
struct Constant struct Constant
{ {
static llvm::ConstantInt* gasMax;
/// Returns word-size constant /// Returns word-size constant
static llvm::ConstantInt* get(int64_t _n); static llvm::ConstantInt* get(int64_t _n);
static llvm::ConstantInt* get(llvm::APInt const& _n); static llvm::ConstantInt* get(llvm::APInt const& _n);

Loading…
Cancel
Save