Browse Source

Squashed 'evmjit/' changes from 533531b..035c376

035c376 All jump support code paths tested. Thanks @CJentzsch.
5b4e130 Function rename
30f3e0d Fix evmcc
7b9d495 BLOCKHASH instruction
681adc1 Remove PREVHASH instruction
024639b Create bad jump block on demand
c21237f Remove unnecessary jump dest map. Create jump table block on demand.
ed614c5 Mandatory JUMPDEST for jumps and new static jumps recognition strategy
86553cd Remove terminate() function
5db00ad New CALL/CREATE depth limit semantics
e5d0fb3 Alloc stack elemnent for external function call argument on demand
4f4fc63 Alloc stack elemnent for external function call argument on demand
b6248cc Detemplatify createCall helper
b77a975 Create helper/external functions on demand
9bf0b75 Create helper/external functions on demand

git-subtree-dir: evmjit
git-subtree-split: 035c3760e0
cl-refactor
Gav Wood 10 years ago
parent
commit
1f40799620
  1. 1
      evmcc/evmcc.cpp
  2. 20
      libevmjit-cpp/Env.cpp
  3. 1
      libevmjit-cpp/JitVM.cpp
  4. 7
      libevmjit/BasicBlock.h
  5. 2
      libevmjit/Common.h
  6. 152
      libevmjit/Compiler.cpp
  7. 18
      libevmjit/Compiler.h
  8. 5
      libevmjit/CompilerHelper.cpp
  9. 7
      libevmjit/CompilerHelper.h
  10. 151
      libevmjit/Ext.cpp
  11. 50
      libevmjit/Ext.h
  12. 4
      libevmjit/GasMeter.cpp
  13. 2
      libevmjit/Instruction.h
  14. 8
      libevmjit/Memory.cpp
  15. 23
      libevmjit/Runtime.cpp
  16. 3
      libevmjit/Runtime.h
  17. 1
      libevmjit/RuntimeData.h
  18. 2
      libevmjit/RuntimeManager.cpp
  19. 9
      libevmjit/Utils.cpp
  20. 2
      libevmjit/Utils.h

1
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
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
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
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
libevmjit/Common.h

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

152
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
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
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
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
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
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
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
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
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
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
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
libevmjit/RuntimeData.h

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

2
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
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
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