Browse Source

EVM JIT update: latest PoC-8 changes, cleanups and improvments

cl-refactor
Paweł Bylica 10 years ago
parent
commit
9e242cae19
  1. 1
      evmjit/evmcc/evmcc.cpp
  2. 20
      evmjit/libevmjit-cpp/Env.cpp
  3. 1
      evmjit/libevmjit-cpp/JitVM.cpp
  4. 7
      evmjit/libevmjit/BasicBlock.h
  5. 2
      evmjit/libevmjit/Common.h
  6. 152
      evmjit/libevmjit/Compiler.cpp
  7. 18
      evmjit/libevmjit/Compiler.h
  8. 5
      evmjit/libevmjit/CompilerHelper.cpp
  9. 7
      evmjit/libevmjit/CompilerHelper.h
  10. 151
      evmjit/libevmjit/Ext.cpp
  11. 50
      evmjit/libevmjit/Ext.h
  12. 4
      evmjit/libevmjit/GasMeter.cpp
  13. 2
      evmjit/libevmjit/Instruction.h
  14. 8
      evmjit/libevmjit/Memory.cpp
  15. 23
      evmjit/libevmjit/Runtime.cpp
  16. 3
      evmjit/libevmjit/Runtime.h
  17. 1
      evmjit/libevmjit/RuntimeData.h
  18. 2
      evmjit/libevmjit/RuntimeManager.cpp
  19. 9
      evmjit/libevmjit/Utils.cpp
  20. 2
      evmjit/libevmjit/Utils.h

1
evmjit/evmcc/evmcc.cpp

@ -190,7 +190,6 @@ int main(int argc, char** argv)
data.set(RuntimeData::CallValue, 0xabcd);
data.set(RuntimeData::CallDataSize, 3);
data.set(RuntimeData::GasPrice, 1003);
data.set(RuntimeData::PrevHash, 1003);
data.set(RuntimeData::CoinBase, (u160)Address(101010101010101015));
data.set(RuntimeData::TimeStamp, 1005);
data.set(RuntimeData::Number, 1006);

20
evmjit/libevmjit-cpp/Env.cpp

@ -42,16 +42,15 @@ extern "C"
*o_value = eth2llvm(u);
}
EXPORT void env_create(ExtVMFace* _env, i256* io_gas, i256* _endowment, byte* _initBeg, uint64_t _initSize, h256* o_address)
EXPORT void env_blockhash(ExtVMFace* _env, i256* _number, h256* o_hash)
{
if (_env->depth == 1024)
jit::terminate(jit::ReturnCode::OutOfGas);
assert(_env->depth < 1024);
*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)
{
auto endowment = llvm2eth(*_endowment);
if (_env->balance(_env->myAddress) >= endowment)
if (_env->balance(_env->myAddress) >= endowment && _env->depth < 1024)
{
_env->subBalance(endowment);
auto gas = llvm2eth(*io_gas);
@ -66,13 +65,8 @@ extern "C"
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)
{
if (_env->depth == 1024)
jit::terminate(jit::ReturnCode::OutOfGas);
assert(_env->depth < 1024);
auto value = llvm2eth(*_value);
if (_env->balance(_env->myAddress) >= value)
if (_env->balance(_env->myAddress) >= value && _env->depth < 1024)
{
_env->subBalance(value);
auto receiveAddress = right160(*_receiveAddress);

1
evmjit/libevmjit-cpp/JitVM.cpp

@ -20,7 +20,6 @@ bytesConstRef JitVM::go(ExtVMFace& _ext, OnOpFunc const&, uint64_t)
m_data.set(RuntimeData::CallValue, _ext.value);
m_data.set(RuntimeData::CallDataSize, _ext.data.size());
m_data.set(RuntimeData::GasPrice, _ext.gasPrice);
m_data.set(RuntimeData::PrevHash, _ext.previousBlock.hash);
m_data.set(RuntimeData::CoinBase, fromAddress(_ext.currentBlock.coinbaseAddress));
m_data.set(RuntimeData::TimeStamp, _ext.currentBlock.timestamp);
m_data.set(RuntimeData::Number, _ext.currentBlock.number);

7
evmjit/libevmjit/BasicBlock.h

@ -67,6 +67,9 @@ public:
ProgramCounter begin() { return m_beginInstIdx; }
ProgramCounter end() { return m_endInstIdx; }
bool isJumpDest() const { return m_isJumpDest; }
void markAsJumpDest() { m_isJumpDest = true; }
LocalStack& localStack() { return m_stack; }
/// Optimization: propagates values between local stacks in basic blocks
@ -109,6 +112,10 @@ private:
/// How many items higher is the current stack than the initial one.
/// May be negative.
int m_tosOffset = 0;
/// Is the basic block a valid jump destination.
/// JUMPDEST is the first instruction of the basic block.
bool m_isJumpDest = false;
};
}

2
evmjit/libevmjit/Common.h

@ -44,6 +44,8 @@ struct i256
};
static_assert(sizeof(i256) == 32, "Wrong i265 size");
#define UNTESTED assert(false)
}
}
}

152
evmjit/libevmjit/Compiler.cpp

@ -5,8 +5,6 @@
#include <fstream>
#include <chrono>
#include <boost/dynamic_bitset.hpp>
#include <llvm/ADT/PostOrderIterator.h>
#include <llvm/IR/CFG.h>
#include <llvm/IR/Module.h>
@ -42,19 +40,17 @@ Compiler::Compiler(Options const& _options):
void Compiler::createBasicBlocks(bytes const& _bytecode)
{
// FIXME: Simplify this algorithm. All can be done in one pass
std::set<ProgramCounter> splitPoints; // Sorted collections of instruction indices where basic blocks start/end
std::map<ProgramCounter, ProgramCounter> directJumpTargets;
std::vector<ProgramCounter> indirectJumpTargets;
boost::dynamic_bitset<> validJumpTargets(std::max(_bytecode.size(), size_t(1)));
splitPoints.insert(0); // First basic block
validJumpTargets[0] = true;
for (auto curr = _bytecode.begin(); curr != _bytecode.end(); ++curr)
{
ProgramCounter currentPC = curr - _bytecode.begin();
validJumpTargets[currentPC] = true;
auto inst = Instruction(*curr);
switch (inst)
@ -62,21 +58,7 @@ void Compiler::createBasicBlocks(bytes const& _bytecode)
case Instruction::ANY_PUSH:
{
auto val = readPushData(curr, _bytecode.end());
auto next = curr + 1;
if (next == _bytecode.end())
break;
auto nextInst = Instruction(*next);
if (nextInst == Instruction::JUMP || nextInst == Instruction::JUMPI)
{
// Create a block for the JUMP target.
ProgramCounter targetPC = val.ult(_bytecode.size()) ? val.getZExtValue() : _bytecode.size();
splitPoints.insert(targetPC);
ProgramCounter jumpPC = (next - _bytecode.begin());
directJumpTargets[jumpPC] = targetPC;
}
readPushData(curr, _bytecode.end());
break;
}
@ -105,15 +87,6 @@ void Compiler::createBasicBlocks(bytes const& _bytecode)
}
}
// Remove split points generated from jumps out of code or into data.
for (auto it = splitPoints.cbegin(); it != splitPoints.cend();)
{
if (*it > _bytecode.size() || !validJumpTargets[*it])
it = splitPoints.erase(it);
else
++it;
}
for (auto it = splitPoints.cbegin(); it != splitPoints.cend();)
{
auto beginInstIdx = *it;
@ -123,33 +96,39 @@ void Compiler::createBasicBlocks(bytes const& _bytecode)
}
m_stopBB = llvm::BasicBlock::Create(m_mainFunc->getContext(), "Stop", m_mainFunc);
m_badJumpBlock = std::unique_ptr<BasicBlock>(new BasicBlock("BadJumpBlock", m_mainFunc, m_builder));
m_jumpTableBlock = std::unique_ptr<BasicBlock>(new BasicBlock("JumpTableBlock", m_mainFunc, m_builder));
for (auto it = directJumpTargets.cbegin(); it != directJumpTargets.cend(); ++it)
{
if (it->second >= _bytecode.size())
{
// Jumping out of code means STOP
m_directJumpTargets[it->first] = m_stopBB;
continue;
}
for (auto it = indirectJumpTargets.cbegin(); it != indirectJumpTargets.cend(); ++it)
basicBlocks.find(*it)->second.markAsJumpDest();
}
auto blockIter = basicBlocks.find(it->second);
if (blockIter != basicBlocks.end())
{
m_directJumpTargets[it->first] = blockIter->second.llvm();
}
else
llvm::BasicBlock* Compiler::getJumpTableBlock()
{
if (!m_jumpTableBlock)
{
m_jumpTableBlock.reset(new BasicBlock("JumpTable", m_mainFunc, m_builder));
InsertPointGuard g{m_builder};
m_builder.SetInsertPoint(m_jumpTableBlock->llvm());
auto dest = m_jumpTableBlock->localStack().pop();
auto switchInstr = m_builder.CreateSwitch(dest, getBadJumpBlock());
for (auto&& p : basicBlocks)
{
clog(JIT) << "Bad JUMP at PC " << it->first
<< ": " << it->second << " is not a valid PC";
m_directJumpTargets[it->first] = m_badJumpBlock->llvm();
if (p.second.isJumpDest())
switchInstr->addCase(Constant::get(p.first), p.second.llvm());
}
}
return m_jumpTableBlock->llvm();
}
for (auto it = indirectJumpTargets.cbegin(); it != indirectJumpTargets.cend(); ++it)
m_indirectJumpTargets.push_back(&basicBlocks.find(*it)->second);
llvm::BasicBlock* Compiler::getBadJumpBlock()
{
if (!m_badJumpBlock)
{
m_badJumpBlock.reset(new BasicBlock("BadJump", m_mainFunc, m_builder));
InsertPointGuard g{m_builder};
m_builder.SetInsertPoint(m_badJumpBlock->llvm());
m_builder.CreateRet(Constant::get(ReturnCode::BadJumpDestination));
}
return m_badJumpBlock->llvm();
}
std::unique_ptr<llvm::Module> Compiler::compile(bytes const& _bytecode, std::string const& _id)
@ -192,25 +171,6 @@ std::unique_ptr<llvm::Module> Compiler::compile(bytes const& _bytecode, std::str
m_builder.SetInsertPoint(m_stopBB);
m_builder.CreateRet(Constant::get(ReturnCode::Stop));
m_builder.SetInsertPoint(m_badJumpBlock->llvm());
m_builder.CreateRet(Constant::get(ReturnCode::BadJumpDestination));
m_builder.SetInsertPoint(m_jumpTableBlock->llvm());
if (m_indirectJumpTargets.size() > 0)
{
auto dest = m_jumpTableBlock->localStack().pop();
auto switchInstr = m_builder.CreateSwitch(dest, m_badJumpBlock->llvm(),
m_indirectJumpTargets.size());
for (auto it = m_indirectJumpTargets.cbegin(); it != m_indirectJumpTargets.cend(); ++it)
{
auto& bb = *it;
auto dest = Constant::get(bb->begin());
switchInstr->addCase(dest, bb->llvm());
}
}
else
m_builder.CreateBr(m_badJumpBlock->llvm());
removeDeadBlocks();
dumpCFGifRequired("blocks-init.dot");
@ -596,16 +556,21 @@ void Compiler::compileBasicBlock(BasicBlock& _basicBlock, bytes const& _bytecode
case Instruction::JUMP:
case Instruction::JUMPI:
{
// Generate direct jump iff:
// 1. this is not the first instruction in the block
// 2. m_directJumpTargets[currentPC] is defined (meaning that the previous instruction is a PUSH)
// Otherwise generate a indirect jump (a switch).
llvm::BasicBlock* targetBlock = nullptr;
if (currentPC != _basicBlock.begin())
auto target = stack.pop();
if (auto constant = llvm::dyn_cast<llvm::ConstantInt>(target))
{
auto pairIter = m_directJumpTargets.find(currentPC);
if (pairIter != m_directJumpTargets.end())
targetBlock = pairIter->second;
auto&& c = constant->getValue();
if (c.ult(_bytecode.size()))
{
auto v = c.getZExtValue();
auto it = basicBlocks.find(v);
if (it != basicBlocks.end() && it->second.isJumpDest())
targetBlock = it->second.llvm();
}
if (!targetBlock)
targetBlock = getBadJumpBlock();
}
if (inst == Instruction::JUMP)
@ -614,28 +579,30 @@ void Compiler::compileBasicBlock(BasicBlock& _basicBlock, bytes const& _bytecode
{
// The target address is computed at compile time,
// just pop it without looking...
stack.pop();
m_builder.CreateBr(targetBlock);
}
else
m_builder.CreateBr(m_jumpTableBlock->llvm());
{
stack.push(target);
m_builder.CreateBr(getJumpTableBlock());
}
}
else // JUMPI
{
stack.swap(1);
auto val = stack.pop();
auto zero = Constant::get(0);
auto cond = m_builder.CreateICmpNE(val, zero, "nonzero");
if (targetBlock)
{
stack.pop();
m_builder.CreateCondBr(cond, targetBlock, _nextBasicBlock);
}
else
m_builder.CreateCondBr(cond, m_jumpTableBlock->llvm(), _nextBasicBlock);
{
stack.push(target);
m_builder.CreateCondBr(cond, getJumpTableBlock(), _nextBasicBlock);
}
}
break;
}
@ -666,7 +633,6 @@ void Compiler::compileBasicBlock(BasicBlock& _basicBlock, bytes const& _bytecode
case Instruction::CALLDATASIZE:
case Instruction::CODESIZE:
case Instruction::GASPRICE:
case Instruction::PREVHASH:
case Instruction::COINBASE:
case Instruction::TIMESTAMP:
case Instruction::NUMBER:
@ -678,6 +644,14 @@ void Compiler::compileBasicBlock(BasicBlock& _basicBlock, bytes const& _bytecode
break;
}
case Instruction::BLOCKHASH:
{
auto number = stack.pop();
auto hash = _ext.blockhash(number);
stack.push(hash);
break;
}
case Instruction::BALANCE:
{
auto address = stack.pop();
@ -835,6 +809,7 @@ void Compiler::compileBasicBlock(BasicBlock& _basicBlock, bytes const& _bytecode
default: // Invalid instruction - runtime exception
{
// TODO: Replace with return statement
_runtimeManager.raiseException(ReturnCode::BadInstruction);
}
@ -871,13 +846,6 @@ void Compiler::removeDeadBlocks()
}
}
while (sthErased);
// Remove jump table block if no predecessors
if (llvm::pred_begin(m_jumpTableBlock->llvm()) == llvm::pred_end(m_jumpTableBlock->llvm()))
{
m_jumpTableBlock->llvm()->eraseFromParent();
m_jumpTableBlock.reset();
}
}
void Compiler::dumpCFGifRequired(std::string const& _dotfilePath)

18
evmjit/libevmjit/Compiler.h

@ -47,6 +47,10 @@ private:
void compileBasicBlock(BasicBlock& _basicBlock, bytes const& _bytecode, class RuntimeManager& _runtimeManager, class Arith256& _arith, class Memory& _memory, class Ext& _ext, class GasMeter& _gasMeter, llvm::BasicBlock* _nextBasicBlock);
llvm::BasicBlock* getJumpTableBlock();
llvm::BasicBlock* getBadJumpBlock();
void removeDeadBlocks();
/// Dumps basic block graph in graphviz format to a file, if option dumpCFG is enabled.
@ -65,22 +69,16 @@ private:
llvm::IRBuilder<> m_builder;
/// Maps a program counter pc to a basic block that starts at pc (if any).
std::map<ProgramCounter, BasicBlock> basicBlocks = {};
/// Maps a pc at which there is a JUMP or JUMPI to the target block of the jump.
std::map<ProgramCounter, llvm::BasicBlock*> m_directJumpTargets = {};
/// A list of possible blocks to which there may be indirect jumps.
std::vector<BasicBlock*> m_indirectJumpTargets = {};
std::map<ProgramCounter, BasicBlock> basicBlocks;
/// Stop basic block - terminates execution with STOP code (0)
llvm::BasicBlock* m_stopBB = nullptr;
/// Block with a jump table.
std::unique_ptr<BasicBlock> m_jumpTableBlock = nullptr;
std::unique_ptr<BasicBlock> m_jumpTableBlock;
/// Default destination for indirect jumps.
std::unique_ptr<BasicBlock> m_badJumpBlock = nullptr;
/// Destination for invalid jumps
std::unique_ptr<BasicBlock> m_badJumpBlock;
/// Main program function
llvm::Function* m_mainFunc = nullptr;

5
evmjit/libevmjit/CompilerHelper.cpp

@ -35,6 +35,11 @@ llvm::Function* CompilerHelper::getMainFunction()
return nullptr;
}
llvm::CallInst* CompilerHelper::createCall(llvm::Function* _func, std::initializer_list<llvm::Value*> const& _args)
{
return getBuilder().CreateCall(_func, {_args.begin(), _args.size()});
}
RuntimeHelper::RuntimeHelper(RuntimeManager& _runtimeManager):
CompilerHelper(_runtimeManager.getBuilder()),

7
evmjit/libevmjit/CompilerHelper.h

@ -31,12 +31,7 @@ protected:
llvm::IRBuilder<>& m_builder;
llvm::IRBuilder<>& getBuilder() { return m_builder; }
template<typename ..._Args>
llvm::CallInst* createCall(llvm::Function* _func, _Args*... _args)
{
llvm::Value* args[] = {_args...};
return getBuilder().CreateCall(_func, args);
}
llvm::CallInst* createCall(llvm::Function* _func, std::initializer_list<llvm::Value*> const& _args);
friend class RuntimeHelper;
};

151
evmjit/libevmjit/Ext.cpp

@ -24,113 +24,132 @@ Ext::Ext(RuntimeManager& _runtimeManager, Memory& _memoryMan):
RuntimeHelper(_runtimeManager),
m_memoryMan(_memoryMan)
{
auto module = getModule();
m_args[0] = m_builder.CreateAlloca(Type::Word, nullptr, "ext.index");
m_args[1] = m_builder.CreateAlloca(Type::Word, nullptr, "ext.value");
m_arg2 = m_builder.CreateAlloca(Type::Word, nullptr, "ext.arg2");
m_arg3 = m_builder.CreateAlloca(Type::Word, nullptr, "ext.arg3");
m_arg4 = m_builder.CreateAlloca(Type::Word, nullptr, "ext.arg4");
m_arg5 = m_builder.CreateAlloca(Type::Word, nullptr, "ext.arg5");
m_arg6 = m_builder.CreateAlloca(Type::Word, nullptr, "ext.arg6");
m_arg7 = m_builder.CreateAlloca(Type::Word, nullptr, "ext.arg7");
m_arg8 = m_builder.CreateAlloca(Type::Word, nullptr, "ext.arg8");
m_size = m_builder.CreateAlloca(Type::Size, nullptr, "env.size");
}
using Linkage = llvm::GlobalValue::LinkageTypes;
llvm::Type* argsTypes[] = {Type::EnvPtr, Type::WordPtr, Type::WordPtr, Type::WordPtr, Type::WordPtr, Type::WordPtr, Type::WordPtr, Type::WordPtr, Type::WordPtr, Type::WordPtr};
m_sload = llvm::Function::Create(llvm::FunctionType::get(Type::Void, {argsTypes, 3}, false), Linkage::ExternalLinkage, "env_sload", module);
m_sstore = llvm::Function::Create(llvm::FunctionType::get(Type::Void, {argsTypes, 3}, false), Linkage::ExternalLinkage, "env_sstore", module);
llvm::Type* sha3ArgsTypes[] = {Type::BytePtr, Type::Size, Type::WordPtr};
m_sha3 = llvm::Function::Create(llvm::FunctionType::get(Type::Void, sha3ArgsTypes, false), Linkage::ExternalLinkage, "env_sha3", module);
llvm::Type* createArgsTypes[] = {Type::EnvPtr, Type::WordPtr, Type::WordPtr, Type::BytePtr, Type::Size, Type::WordPtr};
m_create = llvm::Function::Create(llvm::FunctionType::get(Type::Void, createArgsTypes, false), Linkage::ExternalLinkage, "env_create", module);
llvm::Type* callArgsTypes[] = {Type::EnvPtr, Type::WordPtr, Type::WordPtr, Type::WordPtr, Type::BytePtr, Type::Size, Type::BytePtr, Type::Size, Type::WordPtr};
m_call = llvm::Function::Create(llvm::FunctionType::get(Type::Bool, callArgsTypes, false), Linkage::ExternalLinkage, "env_call", module);
using FuncDesc = std::tuple<char const*, llvm::FunctionType*>;
llvm::Type* logArgsTypes[] = {Type::EnvPtr, Type::BytePtr, Type::Size, Type::WordPtr, Type::WordPtr, Type::WordPtr, Type::WordPtr};
m_log = llvm::Function::Create(llvm::FunctionType::get(Type::Void, logArgsTypes, false), Linkage::ExternalLinkage, "env_log", module);
llvm::FunctionType* getFunctionType(llvm::Type* _returnType, std::initializer_list<llvm::Type*> const& _argsTypes)
{
return llvm::FunctionType::get(_returnType, llvm::ArrayRef<llvm::Type*>{_argsTypes.begin(), _argsTypes.size()}, false);
}
llvm::Type* getExtCodeArgsTypes[] = {Type::EnvPtr, Type::WordPtr, Type::Size->getPointerTo()};
m_getExtCode = llvm::Function::Create(llvm::FunctionType::get(Type::BytePtr, getExtCodeArgsTypes, false), Linkage::ExternalLinkage, "env_getExtCode", module);
std::array<FuncDesc, sizeOf<EnvFunc>::value> const& getEnvFuncDescs()
{
static std::array<FuncDesc, sizeOf<EnvFunc>::value> descs{{
FuncDesc{"env_sload", getFunctionType(Type::Void, {Type::EnvPtr, Type::WordPtr, Type::WordPtr})},
FuncDesc{"env_sstore", getFunctionType(Type::Void, {Type::EnvPtr, Type::WordPtr, Type::WordPtr})},
FuncDesc{"env_sha3", getFunctionType(Type::Void, {Type::BytePtr, Type::Size, Type::WordPtr})},
FuncDesc{"env_balance", getFunctionType(Type::Void, {Type::EnvPtr, Type::WordPtr, Type::WordPtr})},
FuncDesc{"env_create", getFunctionType(Type::Void, {Type::EnvPtr, Type::WordPtr, 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_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_getExtCode", getFunctionType(Type::BytePtr, {Type::EnvPtr, Type::WordPtr, Type::Size->getPointerTo()})},
FuncDesc{"ext_calldataload", getFunctionType(Type::Void, {Type::RuntimeDataPtr, Type::WordPtr, Type::WordPtr})},
}};
return descs;
}
// Helper function, not client Env interface
llvm::Type* callDataLoadArgsTypes[] = {Type::RuntimeDataPtr, Type::WordPtr, Type::WordPtr};
m_calldataload = llvm::Function::Create(llvm::FunctionType::get(Type::Void, callDataLoadArgsTypes, false), Linkage::ExternalLinkage, "ext_calldataload", module);
llvm::Function* createFunc(EnvFunc _id, llvm::Module* _module)
{
auto&& desc = getEnvFuncDescs()[static_cast<size_t>(_id)];
return llvm::Function::Create(std::get<1>(desc), llvm::Function::ExternalLinkage, std::get<0>(desc), _module);
}
llvm::Function* Ext::getBalanceFunc()
llvm::Value* Ext::getArgAlloca()
{
if (!m_balance)
auto& a = m_argAllocas[m_argCounter++];
if (!a)
{
llvm::Type* argsTypes[] = {Type::EnvPtr, Type::WordPtr, Type::WordPtr};
m_balance = llvm::Function::Create(llvm::FunctionType::get(Type::Void, argsTypes, false), llvm::Function::ExternalLinkage, "env_balance", getModule());
// FIXME: Improve order and names
InsertPointGuard g{getBuilder()};
getBuilder().SetInsertPoint(getMainFunction()->front().getFirstNonPHI());
a = getBuilder().CreateAlloca(Type::Word, nullptr, "arg");
}
return m_balance;
return a;
}
llvm::Value* Ext::byPtr(llvm::Value* _value)
{
auto a = getArgAlloca();
getBuilder().CreateStore(_value, a);
return a;
}
llvm::CallInst* Ext::createCall(EnvFunc _funcId, std::initializer_list<llvm::Value*> const& _args)
{
auto& func = m_funcs[static_cast<size_t>(_funcId)];
if (!func)
func = createFunc(_funcId, getModule());
m_argCounter = 0;
return getBuilder().CreateCall(func, {_args.begin(), _args.size()});
}
llvm::Value* Ext::sload(llvm::Value* _index)
{
m_builder.CreateStore(_index, m_args[0]);
m_builder.CreateCall3(m_sload, getRuntimeManager().getEnvPtr(), m_args[0], m_args[1]); // Uses native endianness
return m_builder.CreateLoad(m_args[1]);
auto ret = getArgAlloca();
createCall(EnvFunc::sload, {getRuntimeManager().getEnvPtr(), byPtr(_index), ret}); // Uses native endianness
return m_builder.CreateLoad(ret);
}
void Ext::sstore(llvm::Value* _index, llvm::Value* _value)
{
m_builder.CreateStore(_index, m_args[0]);
m_builder.CreateStore(_value, m_args[1]);
m_builder.CreateCall3(m_sstore, getRuntimeManager().getEnvPtr(), m_args[0], m_args[1]); // Uses native endianness
createCall(EnvFunc::sstore, {getRuntimeManager().getEnvPtr(), byPtr(_index), byPtr(_value)}); // Uses native endianness
}
llvm::Value* Ext::calldataload(llvm::Value* _index)
{
m_builder.CreateStore(_index, m_args[0]);
createCall(m_calldataload, getRuntimeManager().getDataPtr(), m_args[0], m_args[1]);
auto ret = m_builder.CreateLoad(m_args[1]);
auto ret = getArgAlloca();
createCall(EnvFunc::calldataload, {getRuntimeManager().getDataPtr(), byPtr(_index), ret});
ret = m_builder.CreateLoad(ret);
return Endianness::toNative(m_builder, ret);
}
llvm::Value* Ext::balance(llvm::Value* _address)
{
auto address = Endianness::toBE(m_builder, _address);
m_builder.CreateStore(address, m_args[0]);
createCall(getBalanceFunc(), getRuntimeManager().getEnvPtr(), m_args[0], m_args[1]);
return m_builder.CreateLoad(m_args[1]);
auto ret = getArgAlloca();
createCall(EnvFunc::balance, {getRuntimeManager().getEnvPtr(), byPtr(address), ret});
return m_builder.CreateLoad(ret);
}
llvm::Value* Ext::blockhash(llvm::Value* _number)
{
auto hash = getArgAlloca();
createCall(EnvFunc::blockhash, {getRuntimeManager().getEnvPtr(), byPtr(_number), hash});
hash = m_builder.CreateLoad(hash);
return Endianness::toNative(getBuilder(), hash);
}
llvm::Value* Ext::create(llvm::Value*& _gas, llvm::Value* _endowment, llvm::Value* _initOff, llvm::Value* _initSize)
{
m_builder.CreateStore(_gas, m_args[0]);
m_builder.CreateStore(_endowment, m_arg2);
auto gas = byPtr(_gas);
auto ret = getArgAlloca();
auto begin = m_memoryMan.getBytePtr(_initOff);
auto size = m_builder.CreateTrunc(_initSize, Type::Size, "size");
createCall(m_create, getRuntimeManager().getEnvPtr(), m_args[0], m_arg2, begin, size, m_args[1]);
_gas = m_builder.CreateLoad(m_args[0]); // Return gas
llvm::Value* address = m_builder.CreateLoad(m_args[1]);
createCall(EnvFunc::create, {getRuntimeManager().getEnvPtr(), gas, byPtr(_endowment), begin, size, ret});
_gas = m_builder.CreateLoad(gas); // Return gas
llvm::Value* address = m_builder.CreateLoad(ret);
address = Endianness::toNative(m_builder, 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)
{
m_builder.CreateStore(_gas, m_args[0]);
auto gas = byPtr(_gas);
auto receiveAddress = Endianness::toBE(m_builder, _receiveAddress);
m_builder.CreateStore(receiveAddress, m_arg2);
m_builder.CreateStore(_value, m_arg3);
auto inBeg = m_memoryMan.getBytePtr(_inOff);
auto inSize = m_builder.CreateTrunc(_inSize, Type::Size, "in.size");
auto outBeg = m_memoryMan.getBytePtr(_outOff);
auto outSize = m_builder.CreateTrunc(_outSize, Type::Size, "out.size");
auto codeAddress = Endianness::toBE(m_builder, _codeAddress);
m_builder.CreateStore(codeAddress, m_arg8);
auto ret = createCall(m_call, getRuntimeManager().getEnvPtr(), m_args[0], m_arg2, m_arg3, inBeg, inSize, outBeg, outSize, m_arg8);
_gas = m_builder.CreateLoad(m_args[0]); // Return gas
auto ret = createCall(EnvFunc::call, {getRuntimeManager().getEnvPtr(), gas, 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");
}
@ -138,8 +157,9 @@ llvm::Value* Ext::sha3(llvm::Value* _inOff, llvm::Value* _inSize)
{
auto begin = m_memoryMan.getBytePtr(_inOff);
auto size = m_builder.CreateTrunc(_inSize, Type::Size, "size");
createCall(m_sha3, begin, size, m_args[1]);
llvm::Value* hash = m_builder.CreateLoad(m_args[1]);
auto ret = getArgAlloca();
createCall(EnvFunc::sha3, {begin, size, ret});
llvm::Value* hash = m_builder.CreateLoad(ret);
hash = Endianness::toNative(m_builder, hash);
return hash;
}
@ -147,8 +167,7 @@ llvm::Value* Ext::sha3(llvm::Value* _inOff, llvm::Value* _inSize)
MemoryRef Ext::getExtCode(llvm::Value* _addr)
{
auto addr = Endianness::toBE(m_builder, _addr);
m_builder.CreateStore(addr, m_args[0]);
auto code = createCall(m_getExtCode, getRuntimeManager().getEnvPtr(), m_args[0], m_size);
auto code = createCall(EnvFunc::getExtCode, {getRuntimeManager().getEnvPtr(), byPtr(addr), m_size});
auto codeSize = m_builder.CreateLoad(m_size);
auto codeSize256 = m_builder.CreateZExt(codeSize, Type::Word);
return {code, codeSize256};
@ -158,7 +177,7 @@ void Ext::log(llvm::Value* _memIdx, llvm::Value* _numBytes, std::array<llvm::Val
{
auto begin = m_memoryMan.getBytePtr(_memIdx);
auto size = m_builder.CreateTrunc(_numBytes, Type::Size, "size");
llvm::Value* args[] = {getRuntimeManager().getEnvPtr(), begin, size, m_arg2, m_arg3, m_arg4, m_arg5};
llvm::Value* args[] = {getRuntimeManager().getEnvPtr(), begin, size, getArgAlloca(), getArgAlloca(), getArgAlloca(), getArgAlloca()};
auto topicArgPtr = &args[3];
for (auto&& topic : _topics)
@ -170,7 +189,7 @@ void Ext::log(llvm::Value* _memIdx, llvm::Value* _numBytes, std::array<llvm::Val
++topicArgPtr;
}
m_builder.CreateCall(m_log, args);
createCall(EnvFunc::log, {args[0], args[1], args[2], args[3], args[4], args[5], args[6]}); // TODO: use std::initializer_list<>
}
}

50
evmjit/libevmjit/Ext.h

@ -18,6 +18,28 @@ struct MemoryRef
llvm::Value* size;
};
template<typename _EnumT>
struct sizeOf
{
static const size_t value = static_cast<size_t>(_EnumT::_size);
};
enum class EnvFunc
{
sload,
sstore,
sha3,
balance,
create,
call,
log,
blockhash,
getExtCode,
calldataload, // Helper function, not client Env interface
_size
};
class Ext : public RuntimeHelper
{
public:
@ -30,6 +52,7 @@ public:
llvm::Value* calldataload(llvm::Value* _index);
llvm::Value* create(llvm::Value*& _gas, 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* blockhash(llvm::Value* _number);
llvm::Value* sha3(llvm::Value* _inOff, llvm::Value* _inSize);
MemoryRef getExtCode(llvm::Value* _addr);
@ -39,27 +62,16 @@ public:
private:
Memory& m_memoryMan;
llvm::Value* m_args[2];
llvm::Value* m_arg2;
llvm::Value* m_arg3;
llvm::Value* m_arg4;
llvm::Value* m_arg5;
llvm::Value* m_arg6;
llvm::Value* m_arg7;
llvm::Value* m_arg8;
llvm::Value* m_size;
llvm::Value* m_data = nullptr;
llvm::Function* m_sload;
llvm::Function* m_sstore;
llvm::Function* m_calldataload;
llvm::Function* m_balance = nullptr;
llvm::Function* m_create;
llvm::Function* m_call;
llvm::Function* m_sha3;
llvm::Function* m_getExtCode;
llvm::Function* m_log;
llvm::Function* getBalanceFunc();
std::array<llvm::Function*, sizeOf<EnvFunc>::value> m_funcs = {};
std::array<llvm::Value*, 8> m_argAllocas = {};
size_t m_argCounter = 0;
llvm::CallInst* createCall(EnvFunc _funcId, std::initializer_list<llvm::Value*> const& _args);
llvm::Value* getArgAlloca();
llvm::Value* byPtr(llvm::Value* _value);
};

4
evmjit/libevmjit/GasMeter.cpp

@ -119,7 +119,7 @@ void GasMeter::count(Instruction _inst)
if (!m_checkCall)
{
// Create gas check call with mocked block cost at begining of current cost-block
m_checkCall = createCall(m_gasCheckFunc, m_runtimeManager.getRuntimePtr(), llvm::UndefValue::get(Type::Word));
m_checkCall = createCall(m_gasCheckFunc, {m_runtimeManager.getRuntimePtr(), llvm::UndefValue::get(Type::Word)});
}
m_blockCost += getStepCost(_inst);
@ -127,7 +127,7 @@ void GasMeter::count(Instruction _inst)
void GasMeter::count(llvm::Value* _cost)
{
createCall(m_gasCheckFunc, m_runtimeManager.getRuntimePtr(), _cost);
createCall(m_gasCheckFunc, {m_runtimeManager.getRuntimePtr(), _cost});
}
void GasMeter::countExp(llvm::Value* _exponent)

2
evmjit/libevmjit/Instruction.h

@ -58,7 +58,7 @@ enum class Instruction: uint8_t
EXTCODESIZE, ///< get external code size (from another contract)
EXTCODECOPY, ///< copy external code (from another contract)
PREVHASH = 0x40, ///< get hash of most recent complete block
BLOCKHASH = 0x40, ///< get hash of most recent complete block
COINBASE, ///< get the block's coinbase address
TIMESTAMP, ///< get the block's timestamp
NUMBER, ///< get the block's number

8
evmjit/libevmjit/Memory.cpp

@ -146,18 +146,18 @@ llvm::Function* Memory::createFunc(bool _isStore, llvm::Type* _valueType, GasMet
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)
{
createCall(m_storeWord, getRuntimeManager().getRuntimePtr(), _addr, _word);
createCall(m_storeWord, {getRuntimeManager().getRuntimePtr(), _addr, _word});
}
void Memory::storeByte(llvm::Value* _addr, llvm::Value* _word)
{
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()
@ -181,7 +181,7 @@ llvm::Value* Memory::getBytePtr(llvm::Value* _index)
void Memory::require(llvm::Value* _offset, llvm::Value* _size)
{
createCall(m_require, getRuntimeManager().getRuntimePtr(), _offset, _size);
createCall(m_require, {getRuntimeManager().getRuntimePtr(), _offset, _size});
}
void Memory::copyBytes(llvm::Value* _srcPtr, llvm::Value* _srcSize, llvm::Value* _srcIdx,

23
evmjit/libevmjit/Runtime.cpp

@ -11,29 +11,12 @@ namespace eth
{
namespace jit
{
namespace
{
jmp_buf_ref g_currJmpBuf;
}
jmp_buf_ref Runtime::getCurrJmpBuf()
{
return g_currJmpBuf;
}
Runtime::Runtime(RuntimeData* _data, Env* _env):
Runtime::Runtime(RuntimeData* _data, Env* _env) :
m_data(*_data),
m_env(*_env),
m_currJmpBuf(m_jmpBuf),
m_prevJmpBuf(g_currJmpBuf)
{
g_currJmpBuf = m_jmpBuf;
}
Runtime::~Runtime()
{
g_currJmpBuf = m_prevJmpBuf;
}
m_currJmpBuf(m_jmpBuf)
{}
bytes Runtime::getReturnData() const // FIXME: Reconsider returning by copy
{

3
evmjit/libevmjit/Runtime.h

@ -32,7 +32,6 @@ class Runtime
{
public:
Runtime(RuntimeData* _data, Env* _env);
~Runtime();
Runtime(const Runtime&) = delete;
void operator=(const Runtime&) = delete;
@ -43,7 +42,6 @@ public:
bytes getReturnData() const;
jmp_buf_ref getJmpBuf() { return m_jmpBuf; }
static jmp_buf_ref getCurrJmpBuf();
private:
RuntimeData& m_data; ///< Pointer to data. Expected by compiled contract.
@ -51,7 +49,6 @@ private:
jmp_buf_ref m_currJmpBuf; ///< Pointer to jump buffer. Expected by compiled contract.
byte* m_memoryData = nullptr;
i256 m_memorySize = {};
jmp_buf_ref m_prevJmpBuf;
std::jmp_buf m_jmpBuf;
StackImpl m_stack;
MemoryImpl m_memory;

1
evmjit/libevmjit/RuntimeData.h

@ -21,7 +21,6 @@ struct RuntimeData
CallValue,
CallDataSize,
GasPrice,
PrevHash,
CoinBase,
TimeStamp,
Number,

2
evmjit/libevmjit/RuntimeManager.cpp

@ -63,7 +63,6 @@ llvm::Twine getName(RuntimeData::Index _index)
case RuntimeData::CallValue: return "callvalue";
case RuntimeData::CallDataSize: return "calldatasize";
case RuntimeData::GasPrice: return "gasprice";
case RuntimeData::PrevHash: return "prevhash";
case RuntimeData::CoinBase: return "coinbase";
case RuntimeData::TimeStamp: return "timestamp";
case RuntimeData::Number: return "number";
@ -154,7 +153,6 @@ llvm::Value* RuntimeManager::get(Instruction _inst)
case Instruction::CALLVALUE: return get(RuntimeData::CallValue);
case Instruction::CALLDATASIZE: return get(RuntimeData::CallDataSize);
case Instruction::GASPRICE: return get(RuntimeData::GasPrice);
case Instruction::PREVHASH: return get(RuntimeData::PrevHash);
case Instruction::COINBASE: return get(RuntimeData::CoinBase);
case Instruction::TIMESTAMP: return get(RuntimeData::TimeStamp);
case Instruction::NUMBER: return get(RuntimeData::Number);

9
evmjit/libevmjit/Utils.cpp

@ -1,8 +1,7 @@
#include <csetjmp>
#include <llvm/ADT/APInt.h>
#include "Utils.h"
#include "Instruction.h"
#include "Runtime.h"
namespace dev
{
@ -55,12 +54,6 @@ llvm::APInt readPushData(bytes::const_iterator& _curr, bytes::const_iterator _en
return value;
}
void terminate(ReturnCode _returnCode)
{
auto jmpBuf = Runtime::getCurrJmpBuf();
std::longjmp(jmpBuf, static_cast<int>(_returnCode));
}
}
}
}

2
evmjit/libevmjit/Utils.h

@ -17,8 +17,6 @@ struct JIT: public NoteChannel { static const char* name() { return "JIT"; } };
u256 llvm2eth(i256);
i256 eth2llvm(u256);
void terminate(ReturnCode _returnCode);
}
}
}

Loading…
Cancel
Save