Browse Source

Merge pull request #2602 from chfast/evmjit_stack

EVM JIT: stack refactor
cl-refactor
Gav Wood 10 years ago
parent
commit
abc2c97c75
  1. 5
      evmjit/CMakeLists.txt
  2. 4
      evmjit/include/evmjit/JIT-c.h
  3. 4
      evmjit/include/evmjit/JIT.h
  4. 6
      evmjit/libevmjit-cpp/JitVM.cpp
  5. 12
      evmjit/libevmjit/Array.cpp
  6. 8
      evmjit/libevmjit/Array.h
  7. 360
      evmjit/libevmjit/BasicBlock.cpp
  8. 127
      evmjit/libevmjit/BasicBlock.h
  9. 277
      evmjit/libevmjit/Compiler.cpp
  10. 35
      evmjit/libevmjit/Compiler.h
  11. 1
      evmjit/libevmjit/Optimizer.cpp
  12. 19
      evmjit/libevmjit/RuntimeManager.cpp
  13. 2
      evmjit/libevmjit/RuntimeManager.h
  14. 44
      evmjit/libevmjit/Stack.cpp
  15. 13
      evmjit/libevmjit/Stack.h
  16. 4
      libevm/SmartVM.cpp
  17. 124
      test/libevm/VMTestsFiller/vmIOandFlowOperationsTestFiller.json

5
evmjit/CMakeLists.txt

@ -3,13 +3,14 @@ cmake_minimum_required(VERSION 2.8.12)
if (${CMAKE_VERSION} VERSION_GREATER 3.0)
cmake_policy(SET CMP0042 OLD) # fix MACOSX_RPATH
cmake_policy(SET CMP0048 NEW) # allow VERSION argument in project()
project(EVMJIT VERSION 0.9.0 LANGUAGES CXX)
project(EVMJIT VERSION 0.9.0.1 LANGUAGES CXX)
else()
project(EVMJIT)
set(EVMJIT_VERSION "0.9.0")
set(EVMJIT_VERSION "0.9.0.1")
set(EVMJIT_VERSION_MAJOR 0)
set(EVMJIT_VERSION_MINOR 9)
set(EVMJIT_VERSION_PATCH 0)
set(EVMJIT_VERSION_TWEAK 1)
endif()
set_property(GLOBAL PROPERTY USE_FOLDERS ON)

4
evmjit/include/evmjit/JIT-c.h

@ -47,10 +47,6 @@ typedef enum evmjit_return_code
// Standard error codes
OutOfGas = -1,
StackUnderflow = -2,
BadJumpDestination = -3,
BadInstruction = -4,
Rejected = -5, ///< Input data (code, gas, block info, etc.) does not meet JIT requirement and execution request has been rejected
// Internal error codes
LLVMError = -101,

4
evmjit/include/evmjit/JIT.h

@ -101,10 +101,6 @@ enum class ReturnCode
// Standard error codes
OutOfGas = -1,
StackUnderflow = -2,
BadJumpDestination = -3,
BadInstruction = -4,
Rejected = -5, ///< Input data (code, gas, block info, etc.) does not meet JIT requirement and execution request has been rejected
// Internal error codes
LLVMError = -101,

6
evmjit/libevmjit-cpp/JitVM.cpp

@ -60,14 +60,8 @@ bytesConstRef JitVM::execImpl(u256& io_gas, ExtVMFace& _ext, OnOpFunc const& _on
_ext.suicide(right160(jit2eth(m_data.address)));
break;
case evmjit::ReturnCode::BadJumpDestination:
BOOST_THROW_EXCEPTION(BadJumpDestination());
case evmjit::ReturnCode::OutOfGas:
BOOST_THROW_EXCEPTION(OutOfGas());
case evmjit::ReturnCode::StackUnderflow: // FIXME: Remove support for detail errors
BOOST_THROW_EXCEPTION(StackUnderflow());
case evmjit::ReturnCode::BadInstruction:
BOOST_THROW_EXCEPTION(BadInstruction());
case evmjit::ReturnCode::LinkerWorkaround: // never happens
env_sload(); // but forces linker to include env_* JIT callback functions
break;

12
evmjit/libevmjit/Array.cpp

@ -217,11 +217,7 @@ llvm::Type* Array::getType()
}
Array::Array(llvm::IRBuilder<>& _builder, char const* _name) :
CompilerHelper(_builder),
m_pushFunc([this](){ return createArrayPushFunc(); }),
m_setFunc([this](){ return createArraySetFunc(); }),
m_getFunc([this](){ return createArrayGetFunc(); }),
m_freeFunc([this](){ return createFreeFunc(); })
CompilerHelper(_builder)
{
m_array = m_builder.CreateAlloca(getType(), nullptr, _name);
m_builder.CreateStore(llvm::ConstantAggregateZero::get(getType()), m_array);
@ -229,11 +225,7 @@ Array::Array(llvm::IRBuilder<>& _builder, char const* _name) :
Array::Array(llvm::IRBuilder<>& _builder, llvm::Value* _array) :
CompilerHelper(_builder),
m_array(_array),
m_pushFunc([this](){ return createArrayPushFunc(); }),
m_setFunc([this](){ return createArraySetFunc(); }),
m_getFunc([this](){ return createArrayGetFunc(); }),
m_freeFunc([this](){ return createFreeFunc(); })
m_array(_array)
{
m_builder.CreateStore(llvm::ConstantAggregateZero::get(getType()), m_array);
}

8
evmjit/libevmjit/Array.h

@ -59,10 +59,10 @@ private:
llvm::Function* getReallocFunc();
LazyFunction m_pushFunc = {[this](){ return createArrayPushFunc(); }}; // TODO: If works on MSVC, remove form initialization list
LazyFunction m_setFunc;
LazyFunction m_setFunc = {[this](){ return createArraySetFunc(); }};
LazyFunction m_getPtrFunc = {[this](){ return createGetPtrFunc(); }};
LazyFunction m_getFunc;
LazyFunction m_freeFunc;
LazyFunction m_getFunc = {[this](){ return createArrayGetFunc(); }};
LazyFunction m_freeFunc = {[this](){ return createFreeFunc(); }};
LazyFunction m_extendFunc = {[this](){ return createExtendFunc(); }};
LazyFunction m_reallocFunc = {[this](){ return getReallocFunc(); }};
};
@ -70,5 +70,3 @@ private:
}
}
}

360
evmjit/libevmjit/BasicBlock.cpp

@ -20,53 +20,42 @@ namespace eth
namespace jit
{
static const char* jumpDestName = "JmpDst.";
static const char* basicBlockName = "Instr.";
BasicBlock::BasicBlock(instr_idx _firstInstrIdx, 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):
m_firstInstrIdx{_firstInstrIdx},
m_begin(_begin),
m_end(_end),
m_llvmBB(llvm::BasicBlock::Create(_mainFunc->getContext(), {isJumpDest ? jumpDestName : basicBlockName, std::to_string(_firstInstrIdx)}, _mainFunc)),
m_stack(*this),
m_builder(_builder),
m_isJumpDest(isJumpDest)
{}
BasicBlock::BasicBlock(std::string _name, llvm::Function* _mainFunc, llvm::IRBuilder<>& _builder, bool isJumpDest) :
m_llvmBB(llvm::BasicBlock::Create(_mainFunc->getContext(), _name, _mainFunc)),
m_stack(*this),
m_builder(_builder),
m_isJumpDest(isJumpDest)
m_llvmBB(llvm::BasicBlock::Create(_mainFunc->getContext(), {"Instr.", std::to_string(_firstInstrIdx)}, _mainFunc))
{}
BasicBlock::LocalStack::LocalStack(BasicBlock& _owner) :
m_bblock(_owner)
LocalStack::LocalStack(Stack& _globalStack):
m_global(_globalStack)
{}
void BasicBlock::LocalStack::push(llvm::Value* _value)
void LocalStack::push(llvm::Value* _value)
{
assert(_value->getType() == Type::Word);
m_bblock.m_currentStack.push_back(_value);
m_bblock.m_tosOffset += 1;
m_maxSize = std::max(m_maxSize, m_bblock.m_tosOffset);
m_local.push_back(_value);
m_maxSize = std::max(m_maxSize, size());
}
llvm::Value* BasicBlock::LocalStack::pop()
llvm::Value* LocalStack::pop()
{
auto result = get(0);
auto item = get(0);
assert(!m_local.empty() || !m_input.empty());
if (m_bblock.m_currentStack.size() > 0)
m_bblock.m_currentStack.pop_back();
if (m_local.size() > 0)
m_local.pop_back();
else
++m_globalPops;
m_bblock.m_tosOffset -= 1;
return result;
m_minSize = std::min(m_minSize, size());
return item;
}
/**
* Pushes a copy of _index-th element (tos is 0-th elem).
*/
void BasicBlock::LocalStack::dup(size_t _index)
void LocalStack::dup(size_t _index)
{
auto val = get(_index);
push(val);
@ -76,7 +65,7 @@ void BasicBlock::LocalStack::dup(size_t _index)
* Swaps tos with _index-th element (tos is 0-th elem).
* _index must be > 0.
*/
void BasicBlock::LocalStack::swap(size_t _index)
void LocalStack::swap(size_t _index)
{
assert(_index > 0);
auto val = get(_index);
@ -85,306 +74,73 @@ void BasicBlock::LocalStack::swap(size_t _index)
set(0, val);
}
std::vector<llvm::Value*>::iterator BasicBlock::LocalStack::getItemIterator(size_t _index)
llvm::Value* LocalStack::get(size_t _index)
{
auto& currentStack = m_bblock.m_currentStack;
if (_index < currentStack.size())
return currentStack.end() - _index - 1;
// Need to map more elements from the EVM stack
auto nNewItems = 1 + _index - currentStack.size();
currentStack.insert(currentStack.begin(), nNewItems, nullptr);
if (_index < m_local.size())
return *(m_local.rbegin() + _index); // count from back
return currentStack.end() - _index - 1;
}
llvm::Value* BasicBlock::LocalStack::get(size_t _index)
{
auto& initialStack = m_bblock.m_initialStack;
auto itemIter = getItemIterator(_index);
auto idx = _index - m_local.size() + m_globalPops;
if (idx >= m_input.size())
m_input.resize(idx + 1);
auto& item = m_input[idx];
if (*itemIter == nullptr)
if (!item)
{
// Need to fetch a new item from the EVM stack
assert(static_cast<int>(_index) >= m_bblock.m_tosOffset);
size_t initialIdx = _index - m_bblock.m_tosOffset;
if (initialIdx >= initialStack.size())
{
auto nNewItems = 1 + initialIdx - initialStack.size();
initialStack.insert(initialStack.end(), nNewItems, nullptr);
}
assert(initialStack[initialIdx] == nullptr);
// Create a dummy value.
std::string name = "get_" + std::to_string(_index);
initialStack[initialIdx] = m_bblock.m_builder.CreatePHI(Type::Word, 0, std::move(name));
*itemIter = initialStack[initialIdx];
item = m_global.get(idx); // Reach an item from global stack
m_minSize = std::min(m_minSize, -static_cast<ssize_t>(idx) - 1); // and remember required stack size
}
return *itemIter;
return item;
}
void BasicBlock::LocalStack::set(size_t _index, llvm::Value* _word)
{
auto itemIter = getItemIterator(_index);
*itemIter = _word;
}
void BasicBlock::synchronizeLocalStack(Stack& _evmStack)
void LocalStack::set(size_t _index, llvm::Value* _word)
{
auto blockTerminator = m_llvmBB->getTerminator();
assert(blockTerminator != nullptr);
if (blockTerminator->getOpcode() != llvm::Instruction::Ret)
{
// Not needed in case of ret instruction. Ret also invalidates the stack.
m_builder.SetInsertPoint(blockTerminator);
auto currIter = m_currentStack.begin();
auto endIter = m_currentStack.end();
// Update (emit set()) changed values
for (int idx = (int)m_currentStack.size() - 1 - m_tosOffset;
currIter < endIter && idx >= 0;
++currIter, --idx)
{
assert(static_cast<size_t>(idx) < m_initialStack.size());
if (*currIter != m_initialStack[idx]) // value needs update
_evmStack.set(static_cast<size_t>(idx), *currIter);
}
// Pop values
if (m_tosOffset < 0)
_evmStack.pop(static_cast<size_t>(-m_tosOffset));
// Push new values
for (; currIter < endIter; ++currIter)
{
assert(*currIter != nullptr);
_evmStack.push(*currIter);
}
}
// Emit get() for all (used) values from the initial stack
for (size_t idx = 0; idx < m_initialStack.size(); ++idx)
if (_index < m_local.size())
{
auto val = m_initialStack[idx];
if (val == nullptr)
continue;
llvm::PHINode* phi = llvm::cast<llvm::PHINode>(val);
// Insert call to get() just before the PHI node and replace
// the uses of PHI with the uses of this new instruction.
m_builder.SetInsertPoint(phi);
auto newVal = _evmStack.get(idx); // OPT: Value may be never user but we need to check stack heigth
// It is probably a good idea to keep heigth as a local variable accesible by LLVM directly
phi->replaceAllUsesWith(newVal);
phi->eraseFromParent();
*(m_local.rbegin() + _index) = _word;
return;
}
// Reset the stack
m_initialStack.erase(m_initialStack.begin(), m_initialStack.end());
m_currentStack.erase(m_currentStack.begin(), m_currentStack.end());
m_tosOffset = 0;
auto idx = _index - m_local.size() + m_globalPops;
assert(idx < m_input.size());
m_input[idx] = _word;
}
void BasicBlock::linkLocalStacks(std::vector<BasicBlock*> basicBlocks, llvm::IRBuilder<>& _builder)
{
struct BBInfo
{
BasicBlock& bblock;
std::vector<BBInfo*> predecessors;
size_t inputItems;
size_t outputItems;
std::vector<llvm::PHINode*> phisToRewrite;
BBInfo(BasicBlock& _bblock) :
bblock(_bblock),
predecessors(),
inputItems(0),
outputItems(0)
{
auto& initialStack = bblock.m_initialStack;
for (auto it = initialStack.begin();
it != initialStack.end() && *it != nullptr;
++it, ++inputItems);
//if (bblock.localStack().m_tosOffset > 0)
// outputItems = bblock.localStack().m_tosOffset;
auto& exitStack = bblock.m_currentStack;
for (auto it = exitStack.rbegin();
it != exitStack.rend() && *it != nullptr;
++it, ++outputItems);
}
};
std::map<llvm::BasicBlock*, BBInfo> cfg;
// Create nodes in cfg
for (auto bb : basicBlocks)
cfg.emplace(bb->llvm(), *bb);
// Create edges in cfg: for each bb info fill the list
// of predecessor infos.
for (auto& pair : cfg)
{
auto bb = pair.first;
auto& info = pair.second;
for (auto predIt = llvm::pred_begin(bb); predIt != llvm::pred_end(bb); ++predIt)
{
auto predInfoEntry = cfg.find(*predIt);
if (predInfoEntry != cfg.end()) // FIXME: It is wrong - will skip entry block
info.predecessors.push_back(&predInfoEntry->second);
}
}
// Iteratively compute inputs and outputs of each block, until reaching fixpoint.
bool valuesChanged = true;
while (valuesChanged)
void LocalStack::finalize(llvm::IRBuilder<>& _builder, llvm::BasicBlock& _bb)
{
auto blockTerminator = _bb.getTerminator();
assert(blockTerminator);
if (blockTerminator->getOpcode() != llvm::Instruction::Ret)
{
for (auto& pair : cfg)
{
DLOG(bb) << pair.second.bblock.llvm()->getName().str()
<< ": in " << pair.second.inputItems
<< ", out " << pair.second.outputItems
<< "\n";
}
// Not needed in case of ret instruction. Ret invalidates the stack.
_builder.SetInsertPoint(blockTerminator);
valuesChanged = false;
for (auto& pair : cfg)
// Update items fetched from global stack ignoring the poped ones
assert(m_globalPops <= m_input.size()); // pop() always does get()
for (auto i = m_globalPops; i < m_input.size(); ++i)
{
auto& info = pair.second;
if (&info.bblock == basicBlocks.front())
info.inputItems = 0; // we cannot use phi nodes for first block as it is a successor of entry block
if (info.predecessors.empty())
info.inputItems = 0; // no consequences for other blocks, so leave valuesChanged false
for (auto predInfo : info.predecessors)
{
if (predInfo->outputItems < info.inputItems)
{
info.inputItems = predInfo->outputItems;
valuesChanged = true;
}
else if (predInfo->outputItems > info.inputItems)
{
predInfo->outputItems = info.inputItems;
valuesChanged = true;
}
}
if (m_input[i])
m_global.set(i, m_input[i]);
}
}
// Propagate values between blocks.
for (auto& entry : cfg)
{
auto& info = entry.second;
auto& bblock = info.bblock;
llvm::BasicBlock::iterator fstNonPhi(bblock.llvm()->getFirstNonPHI());
auto phiIter = bblock.m_initialStack.begin();
for (size_t index = 0; index < info.inputItems; ++index, ++phiIter)
// Add new items
auto pops = m_globalPops; // Copy pops counter to keep original value
for (auto& item: m_local)
{
assert(llvm::isa<llvm::PHINode>(*phiIter));
auto phi = llvm::cast<llvm::PHINode>(*phiIter);
for (auto predIt : info.predecessors)
{
auto& predExitStack = predIt->bblock.m_currentStack;
auto value = *(predExitStack.end() - 1 - index);
phi->addIncoming(value, predIt->bblock.llvm());
}
// Move phi to the front
if (llvm::BasicBlock::iterator(phi) != bblock.llvm()->begin())
{
phi->removeFromParent();
_builder.SetInsertPoint(bblock.llvm(), bblock.llvm()->begin());
_builder.Insert(phi);
}
if (pops) // Override poped global items
m_global.set(--pops, item); // using pops counter as the index
else
m_global.push(item);
}
// The items pulled directly from predecessors block must be removed
// from the list of items that has to be popped from the initial stack.
auto& initialStack = bblock.m_initialStack;
initialStack.erase(initialStack.begin(), initialStack.begin() + info.inputItems);
// Initial stack shrinks, so the size difference grows:
bblock.m_tosOffset += (int)info.inputItems;
}
// We must account for the items that were pushed directly to successor
// blocks and thus should not be on the list of items to be pushed onto
// to EVM stack
for (auto& entry : cfg)
{
auto& info = entry.second;
auto& bblock = info.bblock;
auto& exitStack = bblock.m_currentStack;
exitStack.erase(exitStack.end() - info.outputItems, exitStack.end());
bblock.m_tosOffset -= (int)info.outputItems; // FIXME: Fix types
}
}
void BasicBlock::dump()
{
dump(std::cerr, false);
}
void BasicBlock::dump(std::ostream& _out, bool _dotOutput)
{
llvm::raw_os_ostream out(_out);
out << (_dotOutput ? "" : "Initial stack:\n");
for (auto val : m_initialStack)
{
if (val == nullptr)
out << " ?";
else if (llvm::isa<llvm::ExtractValueInst>(val))
out << " " << val->getName();
else if (llvm::isa<llvm::Instruction>(val))
out << *val;
else
out << " " << *val;
out << (_dotOutput ? "\\l" : "\n");
}
out << (_dotOutput ? "| " : "Instructions:\n");
for (auto ins = m_llvmBB->begin(); ins != m_llvmBB->end(); ++ins)
out << *ins << (_dotOutput ? "\\l" : "\n");
if (! _dotOutput)
out << "Current stack (offset = " << m_tosOffset << "):\n";
else
out << "|";
for (auto val = m_currentStack.rbegin(); val != m_currentStack.rend(); ++val)
{
if (*val == nullptr)
out << " ?";
else if (llvm::isa<llvm::ExtractValueInst>(*val))
out << " " << (*val)->getName();
else if (llvm::isa<llvm::Instruction>(*val))
out << **val;
else
out << " " << **val;
out << (_dotOutput ? "\\l" : "\n");
// Pop not overriden items
if (pops)
m_global.pop(pops);
}
if (! _dotOutput)
out << " ...\n----------------------------------------\n";
}
}
}
}

127
evmjit/libevmjit/BasicBlock.h

@ -14,52 +14,58 @@ namespace jit
using namespace evmjit;
using instr_idx = uint64_t;
class BasicBlock
class LocalStack
{
public:
class LocalStack
{
public:
/// Pushes value on stack
void push(llvm::Value* _value);
explicit LocalStack(Stack& _globalStack);
LocalStack(LocalStack const&) = delete;
void operator=(LocalStack const&) = delete;
/// Pushes value on stack
void push(llvm::Value* _value);
/// Pops and returns top value
llvm::Value* pop();
/// Pops and returns top value
llvm::Value* pop();
/// Duplicates _index'th value on stack
void dup(size_t _index);
/// Duplicates _index'th value on stack
void dup(size_t _index);
/// Swaps _index'th value on stack with a value on stack top.
/// @param _index Index of value to be swaped. Must be > 0.
void swap(size_t _index);
/// Swaps _index'th value on stack with a value on stack top.
/// @param _index Index of value to be swaped. Must be > 0.
void swap(size_t _index);
size_t getMaxSize() const { return m_maxSize; }
int getDiff() const { return m_bblock.m_tosOffset; }
ssize_t size() const { return static_cast<ssize_t>(m_local.size()) - static_cast<ssize_t>(m_globalPops); }
ssize_t minSize() const { return m_minSize; }
ssize_t maxSize() const { return m_maxSize; }
private:
LocalStack(BasicBlock& _owner);
LocalStack(LocalStack const&) = delete;
void operator=(LocalStack const&) = delete;
friend BasicBlock;
/// Finalize local stack: check the requirements and update of the global stack.
void finalize(llvm::IRBuilder<>& _builder, llvm::BasicBlock& _bb);
/// Gets _index'th value from top (counting from 0)
llvm::Value* get(size_t _index);
private:
/// Gets _index'th value from top (counting from 0)
llvm::Value* get(size_t _index);
/// Sets _index'th value from top (counting from 0)
void set(size_t _index, llvm::Value* _value);
/// Sets _index'th value from top (counting from 0)
void set(size_t _index, llvm::Value* _value);
std::vector<llvm::Value*>::iterator getItemIterator(size_t _index);
/// Items fetched from global stack. First element matches the top of the global stack.
/// Can contain nulls if some items has been skipped.
std::vector<llvm::Value*> m_input;
private:
BasicBlock& m_bblock;
int m_maxSize = 0; ///< Max size reached by the stack.
};
/// Local stack items that has not been pushed to global stack. First item is just above global stack.
std::vector<llvm::Value*> m_local;
explicit BasicBlock(instr_idx _firstInstrIdx, 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);
Stack& m_global; ///< Reference to global stack.
BasicBlock(const BasicBlock&) = delete;
BasicBlock& operator=(const BasicBlock&) = delete;
size_t m_globalPops = 0; ///< Number of items poped from global stack. In other words: global - local stack overlap.
ssize_t m_minSize = 0; ///< Minimum reached local stack size. Can be negative.
ssize_t m_maxSize = 0; ///< Maximum reached local stack size.
};
class BasicBlock
{
public:
explicit BasicBlock(instr_idx _firstInstrIdx, code_iterator _begin, code_iterator _end, llvm::Function* _mainFunc);
llvm::BasicBlock* llvm() { return m_llvmBB; }
@ -67,61 +73,12 @@ public:
code_iterator begin() const { return m_begin; }
code_iterator end() const { return m_end; }
bool isJumpDest() const { return m_isJumpDest; }
llvm::Value* getJumpTarget() const { return m_jumpTarget; }
void setJumpTarget(llvm::Value* _jumpTarget) { m_jumpTarget = _jumpTarget; }
LocalStack& localStack() { return m_stack; }
/// Optimization: propagates values between local stacks in basic blocks
/// to avoid excessive pushing/popping on the EVM stack.
static void linkLocalStacks(std::vector<BasicBlock*> _basicBlocks, llvm::IRBuilder<>& _builder);
/// Synchronize current local stack with the EVM stack.
void synchronizeLocalStack(Stack& _evmStack);
/// Prints local stack and block instructions to stderr.
/// Useful for calling in a debugger session.
void dump();
void dump(std::ostream& os, bool _dotOutput = false);
private:
instr_idx const m_firstInstrIdx = 0; ///< Code index of first instruction in the block
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;
/// Basic black state vector (stack) - current/end values and their positions on stack
/// @internal Must be AFTER m_llvmBB
LocalStack m_stack;
llvm::IRBuilder<>& m_builder;
/// This stack contains LLVM values that correspond to items found at
/// the EVM stack when the current basic block starts executing.
/// Location 0 corresponds to the top of the EVM stack, location 1 is
/// the item below the top and so on. The stack grows as the code
/// accesses more items on the EVM stack but once a value is put on
/// the stack, it will never be replaced.
std::vector<llvm::Value*> m_initialStack;
/// This stack tracks the contents of the EVM stack as the basic block
/// executes. It may grow on both sides, as the code pushes items on
/// top of the stack or changes existing items.
std::vector<llvm::Value*> m_currentStack;
/// 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 const m_isJumpDest = false;
code_iterator const m_begin = {}; ///< Iterator pointing code beginning of the block
code_iterator const m_end = {}; ///< Iterator pointing code end of the block
/// If block finishes with dynamic jump target index is stored here
llvm::Value* m_jumpTarget = nullptr;
llvm::BasicBlock* const m_llvmBB; ///< Reference to the LLVM BasicBlock
};
}

277
evmjit/libevmjit/Compiler.cpp

@ -36,7 +36,7 @@ Compiler::Compiler(Options const& _options):
Type::init(m_builder.getContext());
}
void Compiler::createBasicBlocks(code_iterator _codeBegin, code_iterator _codeEnd)
std::vector<BasicBlock> Compiler::createBasicBlocks(code_iterator _codeBegin, code_iterator _codeEnd, llvm::SwitchInst& _jumpTable)
{
/// Helper function that skips push data and finds next iterator (can be the end)
auto skipPushDataAndGetNext = [](code_iterator _curr, code_iterator _end)
@ -54,8 +54,9 @@ void Compiler::createBasicBlocks(code_iterator _codeBegin, code_iterator _codeEn
if (*(_codeEnd - 1) != static_cast<byte>(Instruction::STOP))
break;
std::vector<BasicBlock> blocks;
auto begin = _codeBegin; // begin of current block
bool nextJumpDest = false;
for (auto curr = begin, next = begin; curr != _codeEnd; curr = next)
{
next = skipPushDataAndGetNext(curr, _codeEnd);
@ -71,10 +72,6 @@ void Compiler::createBasicBlocks(code_iterator _codeBegin, code_iterator _codeEn
isEnd = true;
break;
case Instruction::JUMPDEST:
nextJumpDest = true;
break;
default:
break;
}
@ -86,42 +83,32 @@ void Compiler::createBasicBlocks(code_iterator _codeBegin, code_iterator _codeEn
if (isEnd)
{
auto beginIdx = begin - _codeBegin;
m_basicBlocks.emplace(std::piecewise_construct, std::forward_as_tuple(beginIdx),
std::forward_as_tuple(beginIdx, begin, next, m_mainFunc, m_builder, nextJumpDest));
nextJumpDest = false;
blocks.emplace_back(beginIdx, begin, next, m_mainFunc);
if (Instruction(*begin) == Instruction::JUMPDEST)
_jumpTable.addCase(Constant::get(beginIdx), blocks.back().llvm());
begin = next;
}
}
return blocks;
}
llvm::BasicBlock* Compiler::getJumpTableBlock(RuntimeManager& _runtimeManager)
void Compiler::fillJumpTable()
{
if (!m_jumpTableBlock)
assert(m_jumpTableBB);
if (llvm::pred_empty(m_jumpTableBB))
{
m_jumpTableBlock.reset(new BasicBlock("JumpTable", m_mainFunc, m_builder, true));
InsertPointGuard g{m_builder};
m_builder.SetInsertPoint(m_jumpTableBlock->llvm());
auto dest = m_builder.CreatePHI(Type::Word, 8, "target");
auto switchInstr = m_builder.CreateSwitch(dest, getBadJumpBlock(_runtimeManager));
for (auto&& p : m_basicBlocks)
{
if (p.second.isJumpDest())
switchInstr->addCase(Constant::get(p.first), p.second.llvm());
}
m_jumpTableBB->eraseFromParent(); // remove if unused
return;
}
return m_jumpTableBlock->llvm();
}
llvm::BasicBlock* Compiler::getBadJumpBlock(RuntimeManager& _runtimeManager)
{
if (!m_badJumpBlock)
// TODO: Extend this function as `resolveJumps()` and fill gaps in branch instructions.
auto target = llvm::cast<llvm::PHINode>(m_jumpTableBB->begin());
for (auto pred: llvm::predecessors(m_jumpTableBB))
{
m_badJumpBlock.reset(new BasicBlock("BadJump", m_mainFunc, m_builder, true));
InsertPointGuard g{m_builder};
m_builder.SetInsertPoint(m_badJumpBlock->llvm());
_runtimeManager.exit(ReturnCode::BadJumpDestination);
auto targetMd = llvm::cast<llvm::LocalAsMetadata>(pred->getTerminator()->getMetadata("target")->getOperand(0));
target->addIncoming(targetMd->getValue(), pred);
}
return m_badJumpBlock->llvm();
}
std::unique_ptr<llvm::Module> Compiler::compile(code_iterator _begin, code_iterator _end, std::string const& _id)
@ -135,16 +122,24 @@ std::unique_ptr<llvm::Module> Compiler::compile(code_iterator _begin, code_itera
// Create entry basic block
auto entryBlock = llvm::BasicBlock::Create(m_builder.getContext(), {}, m_mainFunc);
m_stopBB = llvm::BasicBlock::Create(m_mainFunc->getContext(), "Stop", m_mainFunc);
m_abortBB = llvm::BasicBlock::Create(m_mainFunc->getContext(), "Abort", m_mainFunc);
m_jumpTableBB = llvm::BasicBlock::Create(m_mainFunc->getContext(), "JumpTable", m_mainFunc);
m_builder.SetInsertPoint(m_jumpTableBB);
auto target = m_builder.CreatePHI(Type::Word, 16, "target");
auto& jumpTable = *m_builder.CreateSwitch(target, m_abortBB);
m_builder.SetInsertPoint(entryBlock);
createBasicBlocks(_begin, _end);
auto blocks = createBasicBlocks(_begin, _end, jumpTable);
// Init runtime structures.
RuntimeManager runtimeManager(m_builder, _begin, _end);
GasMeter gasMeter(m_builder, runtimeManager);
Memory memory(runtimeManager, gasMeter);
Ext ext(runtimeManager, memory);
Stack stack(m_builder, runtimeManager);
Stack stack(m_builder);
runtimeManager.setStack(stack); // Runtime Manager will free stack memory
Arith256 arith(m_builder);
@ -162,87 +157,39 @@ std::unique_ptr<llvm::Module> Compiler::compile(code_iterator _begin, code_itera
auto normalFlow = m_builder.CreateICmpEQ(r, m_builder.getInt32(0));
runtimeManager.setJmpBuf(jmpBuf);
// TODO: Create Stop basic block on demand
m_stopBB = llvm::BasicBlock::Create(m_mainFunc->getContext(), "Stop", m_mainFunc);
m_abortBB = llvm::BasicBlock::Create(m_mainFunc->getContext(), "Abort", m_mainFunc);
auto firstBB = m_basicBlocks.empty() ? m_stopBB : m_basicBlocks.begin()->second.llvm();
auto firstBB = blocks.empty() ? m_stopBB : blocks.front().llvm();
m_builder.CreateCondBr(normalFlow, firstBB, m_abortBB, Type::expectTrue);
for (auto basicBlockPairIt = m_basicBlocks.begin(); basicBlockPairIt != m_basicBlocks.end(); ++basicBlockPairIt)
for (auto it = blocks.begin(); it != blocks.end(); ++it)
{
auto& basicBlock = basicBlockPairIt->second;
auto iterCopy = basicBlockPairIt;
++iterCopy;
auto nextBasicBlock = (iterCopy != m_basicBlocks.end()) ? iterCopy->second.llvm() : nullptr;
compileBasicBlock(basicBlock, runtimeManager, arith, memory, ext, gasMeter, nextBasicBlock);
// TODO: Rewrite
auto nextIt = it + 1;
auto nextBasicBlock = (nextIt != blocks.end()) ? nextIt->llvm() : nullptr; // TODO: What with Stop block?
compileBasicBlock(*it, runtimeManager, arith, memory, ext, gasMeter, nextBasicBlock, stack, jumpTable);
}
// Code for special blocks:
// TODO: move to separate function.
m_builder.SetInsertPoint(m_stopBB);
runtimeManager.exit(ReturnCode::Stop);
m_builder.SetInsertPoint(m_abortBB);
runtimeManager.exit(ReturnCode::OutOfGas);
removeDeadBlocks();
// Link jump table target index
if (m_jumpTableBlock)
{
auto phi = llvm::cast<llvm::PHINode>(&m_jumpTableBlock->llvm()->getInstList().front());
for (auto predIt = llvm::pred_begin(m_jumpTableBlock->llvm()); predIt != llvm::pred_end(m_jumpTableBlock->llvm()); ++predIt)
{
BasicBlock* pred = nullptr;
for (auto&& p : m_basicBlocks)
{
if (p.second.llvm() == *predIt)
{
pred = &p.second;
break;
}
}
phi->addIncoming(pred->getJumpTarget(), pred->llvm());
}
}
dumpCFGifRequired("blocks-init.dot");
if (m_options.optimizeStack)
{
std::vector<BasicBlock*> blockList;
for (auto& entry : m_basicBlocks)
blockList.push_back(&entry.second);
if (m_jumpTableBlock)
blockList.push_back(m_jumpTableBlock.get());
BasicBlock::linkLocalStacks(blockList, m_builder);
dumpCFGifRequired("blocks-opt.dot");
}
for (auto& entry : m_basicBlocks)
entry.second.synchronizeLocalStack(stack);
if (m_jumpTableBlock)
m_jumpTableBlock->synchronizeLocalStack(stack);
dumpCFGifRequired("blocks-sync.dot");
fillJumpTable();
return module;
}
void Compiler::compileBasicBlock(BasicBlock& _basicBlock, RuntimeManager& _runtimeManager,
Arith256& _arith, Memory& _memory, Ext& _ext, GasMeter& _gasMeter, llvm::BasicBlock* _nextBasicBlock)
Arith256& _arith, Memory& _memory, Ext& _ext, GasMeter& _gasMeter, llvm::BasicBlock* _nextBasicBlock, Stack& _globalStack,
llvm::SwitchInst& jumpTable)
{
if (!_nextBasicBlock) // this is the last block in the code
_nextBasicBlock = m_stopBB;
m_builder.SetInsertPoint(_basicBlock.llvm());
auto& stack = _basicBlock.localStack();
LocalStack stack{_globalStack};
for (auto it = _basicBlock.begin(); it != _basicBlock.end(); ++it)
{
@ -609,44 +556,23 @@ void Compiler::compileBasicBlock(BasicBlock& _basicBlock, RuntimeManager& _runti
case Instruction::JUMP:
case Instruction::JUMPI:
{
llvm::BasicBlock* targetBlock = nullptr;
auto jumpBlock = m_jumpTableBB;
auto target = stack.pop();
if (auto constant = llvm::dyn_cast<llvm::ConstantInt>(target))
{
auto&& c = constant->getValue();
auto targetIdx = c.getActiveBits() <= 64 ? c.getZExtValue() : -1;
auto it = m_basicBlocks.find(targetIdx);
targetBlock = (it != m_basicBlocks.end() && it->second.isJumpDest()) ? it->second.llvm() : getBadJumpBlock(_runtimeManager);
}
auto jumpInst = (inst == Instruction::JUMP) ?
m_builder.CreateBr(jumpBlock) :
m_builder.CreateCondBr(m_builder.CreateICmpNE(stack.pop(), Constant::get(0), "jump.check"), jumpBlock, _nextBasicBlock);
// TODO: Improve; check for constants
if (inst == Instruction::JUMP)
if (auto constant = llvm::dyn_cast<llvm::ConstantInt>(target))
{
if (targetBlock)
{
m_builder.CreateBr(targetBlock);
}
else
{
_basicBlock.setJumpTarget(target);
m_builder.CreateBr(getJumpTableBlock(_runtimeManager));
}
// If target index is a constant do direct jump to the target block.
auto bb = jumpTable.findCaseValue(constant).getCaseSuccessor();
jumpInst->setSuccessor(0, bb);
}
else // JUMPI
else
{
auto val = stack.pop();
auto zero = Constant::get(0);
auto cond = m_builder.CreateICmpNE(val, zero, "nonzero");
if (targetBlock)
{
m_builder.CreateCondBr(cond, targetBlock, _nextBasicBlock);
}
else
{
_basicBlock.setJumpTarget(target);
m_builder.CreateCondBr(cond, getJumpTableBlock(_runtimeManager), _nextBasicBlock);
}
// Attach medatada to branch instruction with information about target index.
auto targetMd = llvm::MDNode::get(jumpInst->getContext(), llvm::LocalAsMetadata::get(target));
jumpInst->setMetadata("target", targetMd);
}
break;
}
@ -868,109 +794,12 @@ void Compiler::compileBasicBlock(BasicBlock& _basicBlock, RuntimeManager& _runti
if (!_basicBlock.llvm()->getTerminator())
m_builder.CreateBr(_nextBasicBlock);
m_builder.SetInsertPoint(_basicBlock.llvm()->getFirstNonPHI());
_runtimeManager.checkStackLimit(_basicBlock.localStack().getMaxSize(), _basicBlock.localStack().getDiff());
}
void Compiler::removeDeadBlocks()
{
// Remove dead basic blocks
auto sthErased = false;
do
{
sthErased = false;
for (auto it = m_basicBlocks.begin(); it != m_basicBlocks.end();)
{
auto llvmBB = it->second.llvm();
if (llvm::pred_begin(llvmBB) == llvm::pred_end(llvmBB))
{
llvmBB->eraseFromParent();
m_basicBlocks.erase(it++);
sthErased = true;
}
else
++it;
}
}
while (sthErased);
if (m_jumpTableBlock && llvm::pred_begin(m_jumpTableBlock->llvm()) == llvm::pred_end(m_jumpTableBlock->llvm()))
{
m_jumpTableBlock->llvm()->eraseFromParent();
m_jumpTableBlock.reset();
}
if (m_badJumpBlock && llvm::pred_begin(m_badJumpBlock->llvm()) == llvm::pred_end(m_badJumpBlock->llvm()))
{
m_badJumpBlock->llvm()->eraseFromParent();
m_badJumpBlock.reset();
}
}
stack.finalize(m_builder, *_basicBlock.llvm()); // TODO: Use references
void Compiler::dumpCFGifRequired(std::string const& _dotfilePath)
{
if (! m_options.dumpCFG)
return;
// TODO: handle i/o failures
std::ofstream ofs(_dotfilePath);
dumpCFGtoStream(ofs);
ofs.close();
m_builder.SetInsertPoint(_basicBlock.llvm()->getFirstNonPHI()); // TODO: Move to LocalStack::finalize
_runtimeManager.checkStackLimit(stack.minSize(), stack.maxSize(), stack.size());
}
void Compiler::dumpCFGtoStream(std::ostream& _out)
{
_out << "digraph BB {\n"
<< " node [shape=record, fontname=Courier, fontsize=10];\n"
<< " entry [share=record, label=\"entry block\"];\n";
std::vector<BasicBlock*> blocks;
for (auto& pair : m_basicBlocks)
blocks.push_back(&pair.second);
if (m_jumpTableBlock)
blocks.push_back(m_jumpTableBlock.get());
if (m_badJumpBlock)
blocks.push_back(m_badJumpBlock.get());
// std::map<BasicBlock*,int> phiNodesPerBlock;
// Output nodes
for (auto bb : blocks)
{
std::string blockName = bb->llvm()->getName();
std::ostringstream oss;
bb->dump(oss, true);
_out << " \"" << blockName << "\" [shape=record, label=\" { " << blockName << "|" << oss.str() << "} \"];\n";
}
// Output edges
for (auto bb : blocks)
{
std::string blockName = bb->llvm()->getName();
auto end = llvm::pred_end(bb->llvm());
for (llvm::pred_iterator it = llvm::pred_begin(bb->llvm()); it != end; ++it)
{
_out << " \"" << (*it)->getName().str() << "\" -> \"" << blockName << "\" ["
<< ((m_jumpTableBlock.get() && *it == m_jumpTableBlock.get()->llvm()) ? "style = dashed, " : "")
<< "];\n";
}
}
_out << "}\n";
}
void Compiler::dump()
{
for (auto& entry : m_basicBlocks)
entry.second.dump();
if (m_jumpTableBlock != nullptr)
m_jumpTableBlock->dump();
}
}
}

35
evmjit/libevmjit/Compiler.h

@ -15,9 +15,6 @@ public:
struct Options
{
/// Optimize stack operations between basic blocks
bool optimizeStack = true;
/// Rewrite switch instructions to sequences of branches
bool rewriteSwitchToBranches = true;
@ -25,32 +22,18 @@ public:
bool dumpCFG = false;
};
using ProgramCounter = uint64_t;
Compiler(Options const& _options);
std::unique_ptr<llvm::Module> compile(code_iterator _begin, code_iterator _end, std::string const& _id);
private:
void createBasicBlocks(code_iterator _begin, code_iterator _end);
void compileBasicBlock(BasicBlock& _basicBlock, class RuntimeManager& _runtimeManager, class Arith256& _arith, class Memory& _memory, class Ext& _ext, class GasMeter& _gasMeter, llvm::BasicBlock* _nextBasicBlock);
llvm::BasicBlock* getJumpTableBlock(RuntimeManager& _runtimeManager);
llvm::BasicBlock* getBadJumpBlock(RuntimeManager& _runtimeManager);
std::vector<BasicBlock> createBasicBlocks(code_iterator _begin, code_iterator _end, llvm::SwitchInst& _switchInst);
void removeDeadBlocks();
void compileBasicBlock(BasicBlock& _basicBlock, class RuntimeManager& _runtimeManager, class Arith256& _arith, class Memory& _memory, class Ext& _ext, class GasMeter& _gasMeter,
llvm::BasicBlock* _nextBasicBlock, class Stack& _globalStack, llvm::SwitchInst& _jumpTable);
/// Dumps basic block graph in graphviz format to a file, if option dumpCFG is enabled.
void dumpCFGifRequired(std::string const& _dotfilePath);
/// Dumps basic block graph in graphviz format to a stream.
void dumpCFGtoStream(std::ostream& _out);
/// Dumps all basic blocks to stderr. Useful in a debugging session.
void dump();
void fillJumpTable();
/// Compiler options
Options const& m_options;
@ -58,9 +41,6 @@ private:
/// Helper class for generating IR
llvm::IRBuilder<> m_builder;
/// Maps a program counter pc to a basic block that starts at pc (if any).
std::map<ProgramCounter, BasicBlock> m_basicBlocks;
/// Stop basic block - terminates execution with STOP code (0)
llvm::BasicBlock* m_stopBB = nullptr;
@ -68,13 +48,10 @@ private:
llvm::BasicBlock* m_abortBB = nullptr;
/// Block with a jump table.
std::unique_ptr<BasicBlock> m_jumpTableBlock;
/// Destination for invalid jumps
std::unique_ptr<BasicBlock> m_badJumpBlock;
llvm::BasicBlock* m_jumpTableBB = nullptr;
/// Main program function
llvm::Function* m_mainFunc = nullptr;
llvm::Function* m_mainFunc = nullptr; // TODO: Remove
};
}

1
evmjit/libevmjit/Optimizer.cpp

@ -114,6 +114,7 @@ bool LowerEVMPass::doFinalization(llvm::Module&)
bool prepare(llvm::Module& _module)
{
auto pm = llvm::legacy::PassManager{};
pm.add(llvm::createCFGSimplificationPass());
pm.add(llvm::createDeadCodeEliminationPass());
pm.add(new LowerEVMPass{});
return pm.run(_module);

19
evmjit/libevmjit/RuntimeManager.cpp

@ -110,8 +110,8 @@ RuntimeManager::RuntimeManager(llvm::IRBuilder<>& _builder, code_iterator _codeB
m_gasPtr = m_builder.CreateAlloca(Type::Gas, nullptr, "gas.ptr");
m_builder.CreateStore(m_dataElts[RuntimeData::Index::Gas], m_gasPtr);
llvm::Type* checkStackLimitArgs[] = {Type::Size->getPointerTo(), Type::Size, Type::Size, Type::BytePtr};
m_checkStackLimit = llvm::Function::Create(llvm::FunctionType::get(Type::Void, checkStackLimitArgs, false), llvm::Function::PrivateLinkage, "stack.checkSize", getModule());
llvm::Type* checkStackLimitArgs[] = {Type::Size->getPointerTo(), Type::Size, Type::Size, Type::Size, Type::BytePtr};
m_checkStackLimit = llvm::Function::Create(llvm::FunctionType::get(Type::Void, checkStackLimitArgs, false), llvm::Function::PrivateLinkage, "evm.stack.require", getModule());
m_checkStackLimit->setDoesNotThrow();
m_checkStackLimit->setDoesNotCapture(1);
@ -121,7 +121,9 @@ RuntimeManager::RuntimeManager(llvm::IRBuilder<>& _builder, code_iterator _codeB
auto currSizePtr = &m_checkStackLimit->getArgumentList().front();
currSizePtr->setName("currSize");
auto max = currSizePtr->getNextNode();
auto min = currSizePtr->getNextNode();
min->setName("min");
auto max = min->getNextNode();
max->setName("max");
auto diff = max->getNextNode();
diff->setName("diff");
@ -131,8 +133,11 @@ RuntimeManager::RuntimeManager(llvm::IRBuilder<>& _builder, code_iterator _codeB
InsertPointGuard guard{m_builder};
m_builder.SetInsertPoint(checkBB);
auto currSize = m_builder.CreateLoad(currSizePtr, "cur");
auto maxSize = m_builder.CreateNUWAdd(currSize, max, "maxSize");
auto ok = m_builder.CreateICmpULE(maxSize, m_builder.getInt64(1024), "ok");
auto minSize = m_builder.CreateAdd(currSize, min, "minSize", false, true);
auto maxSize = m_builder.CreateAdd(currSize, max, "maxSize", true, true);
auto minOk = m_builder.CreateICmpSGE(minSize, m_builder.getInt64(0), "min.ok");
auto maxOk = m_builder.CreateICmpULE(maxSize, m_builder.getInt64(1024), "max.ok");
auto ok = m_builder.CreateAnd(minOk, maxOk, "ok");
m_builder.CreateCondBr(ok, updateBB, outOfStackBB, Type::expectTrue);
m_builder.SetInsertPoint(updateBB);
@ -145,9 +150,9 @@ RuntimeManager::RuntimeManager(llvm::IRBuilder<>& _builder, code_iterator _codeB
m_builder.CreateUnreachable();
}
void RuntimeManager::checkStackLimit(size_t _max, int _diff)
void RuntimeManager::checkStackLimit(ssize_t _min, ssize_t _max, ssize_t _diff)
{
createCall(m_checkStackLimit, {m_stackSize, m_builder.getInt64(_max), m_builder.getInt64(_diff), getJmpBuf()});
createCall(m_checkStackLimit, {m_stackSize, m_builder.getInt64(_min), m_builder.getInt64(_max), m_builder.getInt64(_diff), getJmpBuf()});
}
llvm::Value* RuntimeManager::getRuntimePtr()

2
evmjit/libevmjit/RuntimeManager.h

@ -50,7 +50,7 @@ public:
static llvm::StructType* getRuntimeType();
static llvm::StructType* getRuntimeDataType();
void checkStackLimit(size_t _max, int _diff);
void checkStackLimit(ssize_t _min, ssize_t _max, ssize_t _diff);
private:
llvm::Value* getPtr(RuntimeData::Index _index);

44
evmjit/libevmjit/Stack.cpp

@ -16,52 +16,14 @@ namespace eth
namespace jit
{
Stack::Stack(llvm::IRBuilder<>& _builder, RuntimeManager& _runtimeManager):
Stack::Stack(llvm::IRBuilder<>& _builder):
CompilerHelper(_builder),
m_runtimeManager(_runtimeManager),
m_stack(_builder, "stack")
{}
llvm::Function* Stack::getGetFunc()
{
auto& func = m_get;
if (!func)
{
llvm::Type* argTypes[] = {Type::Size, Type::Size, Type::BytePtr};
func = llvm::Function::Create(llvm::FunctionType::get(Type::Void, argTypes, false), llvm::Function::ExternalLinkage, "stack.require", getModule());
auto index = &func->getArgumentList().front();
index->setName("index");
auto size = index->getNextNode();
size->setName("size");
auto jmpBuf = size->getNextNode();
jmpBuf->setName("jmpBuf");
InsertPointGuard guard{m_builder};
auto entryBB = llvm::BasicBlock::Create(m_builder.getContext(), {}, func);
auto underflowBB = llvm::BasicBlock::Create(m_builder.getContext(), "Underflow", func);
auto returnBB = llvm::BasicBlock::Create(m_builder.getContext(), "Return", func);
m_builder.SetInsertPoint(entryBB);
auto underflow = m_builder.CreateICmpUGE(index, size, "underflow");
m_builder.CreateCondBr(underflow, underflowBB, returnBB);
m_builder.SetInsertPoint(underflowBB);
m_runtimeManager.abort(jmpBuf);
m_builder.CreateUnreachable();
m_builder.SetInsertPoint(returnBB);
m_builder.CreateRetVoid();
}
return func;
}
llvm::Value* Stack::get(size_t _index)
{
createCall(getGetFunc(), {m_builder.getInt64(_index), m_stack.size(), m_runtimeManager.getJmpBuf()});
auto value = m_stack.get(m_builder.CreateSub(m_stack.size(), m_builder.getInt64(_index + 1)));
//return m_builder.CreateLoad(valuePtr);
return value;
return m_stack.get(m_builder.CreateSub(m_stack.size(), m_builder.getInt64(_index + 1)));
}
void Stack::set(size_t _index, llvm::Value* _value)
@ -71,8 +33,6 @@ void Stack::set(size_t _index, llvm::Value* _value)
void Stack::pop(size_t _count)
{
// FIXME: Pop does not check for stack underflow but looks like not needed
// We should place stack.require() check and begining of every BB
m_stack.pop(m_builder.getInt64(_count));
}

13
evmjit/libevmjit/Stack.h

@ -1,7 +1,5 @@
#pragma once
#include <functional>
#include "Array.h"
namespace dev
@ -10,12 +8,11 @@ namespace eth
{
namespace jit
{
class RuntimeManager;
class Stack : public CompilerHelper
class Stack: public CompilerHelper
{
public:
Stack(llvm::IRBuilder<>& builder, RuntimeManager& runtimeManager);
Stack(llvm::IRBuilder<>& builder);
llvm::Value* get(size_t _index);
void set(size_t _index, llvm::Value* _value);
@ -24,10 +21,6 @@ public:
void free() { m_stack.free(); }
private:
llvm::Function* getGetFunc();
RuntimeManager& m_runtimeManager;
llvm::Function* m_get = nullptr;
Array m_stack;
};
@ -35,5 +28,3 @@ private:
}
}
}

4
libevm/SmartVM.cpp

@ -54,7 +54,7 @@ namespace
bool isStopSentinel()
{
assert((!code.empty() || !codeHash) && "'empty code => empty hash' invariand failed");
assert((!code.empty() || !codeHash) && "'empty code => empty hash' invariant failed");
return code.empty();
}
};
@ -102,7 +102,7 @@ bytesConstRef SmartVM::execImpl(u256& io_gas, ExtVMFace& _ext, OnOpFunc const& _
clog(JitInfo) << "JIT: " << codeHash;
vmKind = VMKind::JIT;
}
else
else if (!_ext.code.empty()) // This check is needed for VM tests
{
static JitWorker s_worker;

124
test/libevm/VMTestsFiller/vmIOandFlowOperationsTestFiller.json

@ -2281,6 +2281,130 @@
}
},
"DynamicJump_value1": {
"env" : {
"previousHash" : "5e20a0453cecd065ea59c37ac63e079ee08998b6045136a8ce6635c7912ec0b6",
"currentNumber" : "0",
"currentGasLimit" : "1000000",
"currentDifficulty" : "256",
"currentTimestamp" : "1",
"currentCoinbase" : "2adc25665018aa1fe0e6bc666dac8fc2697ff9ba"
},
"pre" : {
"0f572e5295c57f15886f9b263e2f6d2d6c7b5ec6" : {
"balance" : "100000000000000000000000",
"nonce" : "0",
"code" : "(asm 1 2 3 CALLVALUE JUMP
JUMPDEST POP POP 0 MSTORE MSIZE 0 RETURN
JUMPDEST POP 0 MSTORE MSIZE 0 RETURN
JUMPDEST 0 MSTORE MSIZE 0 RETURN)",
"storage": {}
}
},
"exec" : {
"address" : "0f572e5295c57f15886f9b263e2f6d2d6c7b5ec6",
"origin" : "cd1722f3947def4cf144679da39c4c32bdc35681",
"caller" : "cd1722f3947def4cf144679da39c4c32bdc35681",
"value" : "8",
"data" : "",
"gasPrice" : "100000000000000",
"gas" : "100000"
}
},
"DynamicJump_value2": {
"env" : {
"previousHash" : "5e20a0453cecd065ea59c37ac63e079ee08998b6045136a8ce6635c7912ec0b6",
"currentNumber" : "0",
"currentGasLimit" : "1000000",
"currentDifficulty" : "256",
"currentTimestamp" : "1",
"currentCoinbase" : "2adc25665018aa1fe0e6bc666dac8fc2697ff9ba"
},
"pre" : {
"0f572e5295c57f15886f9b263e2f6d2d6c7b5ec6" : {
"balance" : "100000000000000000000000",
"nonce" : "0",
"code" : "(asm 1 2 3 CALLVALUE JUMP
JUMPDEST POP POP 0 MSTORE MSIZE 0 RETURN
JUMPDEST POP 0 MSTORE MSIZE 0 RETURN
JUMPDEST 0 MSTORE MSIZE 0 RETURN)",
"storage": {}
}
},
"exec" : {
"address" : "0f572e5295c57f15886f9b263e2f6d2d6c7b5ec6",
"origin" : "cd1722f3947def4cf144679da39c4c32bdc35681",
"caller" : "cd1722f3947def4cf144679da39c4c32bdc35681",
"value" : "18",
"data" : "",
"gasPrice" : "100000000000000",
"gas" : "100000"
}
},
"DynamicJump_value3": {
"env" : {
"previousHash" : "5e20a0453cecd065ea59c37ac63e079ee08998b6045136a8ce6635c7912ec0b6",
"currentNumber" : "0",
"currentGasLimit" : "1000000",
"currentDifficulty" : "256",
"currentTimestamp" : "1",
"currentCoinbase" : "2adc25665018aa1fe0e6bc666dac8fc2697ff9ba"
},
"pre" : {
"0f572e5295c57f15886f9b263e2f6d2d6c7b5ec6" : {
"balance" : "100000000000000000000000",
"nonce" : "0",
"code" : "(asm 1 2 3 CALLVALUE JUMP
JUMPDEST POP POP 0 MSTORE MSIZE 0 RETURN
JUMPDEST POP 0 MSTORE MSIZE 0 RETURN
JUMPDEST 0 MSTORE MSIZE 0 RETURN)",
"storage": {}
}
},
"exec" : {
"address" : "0f572e5295c57f15886f9b263e2f6d2d6c7b5ec6",
"origin" : "cd1722f3947def4cf144679da39c4c32bdc35681",
"caller" : "cd1722f3947def4cf144679da39c4c32bdc35681",
"value" : "27",
"data" : "",
"gasPrice" : "100000000000000",
"gas" : "100000"
}
},
"DynamicJump_valueUnderflow": {
"env" : {
"previousHash" : "5e20a0453cecd065ea59c37ac63e079ee08998b6045136a8ce6635c7912ec0b6",
"currentNumber" : "0",
"currentGasLimit" : "1000000",
"currentDifficulty" : "256",
"currentTimestamp" : "1",
"currentCoinbase" : "2adc25665018aa1fe0e6bc666dac8fc2697ff9ba"
},
"pre" : {
"0f572e5295c57f15886f9b263e2f6d2d6c7b5ec6" : {
"balance" : "100000000000000000000000",
"nonce" : "0",
"code" : "(asm 1 2 3 CALLVALUE JUMP
JUMPDEST POP POP 0 MSTORE MSIZE 0 RETURN
JUMPDEST POP 0 MSTORE MSIZE 0 RETURN
JUMPDEST POP POP POP 0 MSTORE MSIZE 0 RETURN)",
"storage": {}
}
},
"exec" : {
"address" : "0f572e5295c57f15886f9b263e2f6d2d6c7b5ec6",
"origin" : "cd1722f3947def4cf144679da39c4c32bdc35681",
"caller" : "cd1722f3947def4cf144679da39c4c32bdc35681",
"value" : "27",
"data" : "",
"gasPrice" : "100000000000000",
"gas" : "100000"
}
},
"BlockNumberDynamicJumpiAfterStop": {
"env" : {
"previousHash" : "5e20a0453cecd065ea59c37ac63e079ee08998b6045136a8ce6635c7912ec0b6",

Loading…
Cancel
Save