mirror of
https://github.com/ethereum/go-ethereum.git
synced 2026-07-21 20:26:41 +00:00
parent
d41828fee1
commit
d28139677b
9 changed files with 342 additions and 84 deletions
|
|
@ -1,8 +1,11 @@
|
||||||
|
|
||||||
#include "BasicBlock.h"
|
#include "BasicBlock.h"
|
||||||
|
|
||||||
|
#include <boost/lexical_cast.hpp>
|
||||||
|
|
||||||
#include <llvm/IR/Function.h>
|
#include <llvm/IR/Function.h>
|
||||||
#include <llvm/IR/Instructions.h>
|
#include <llvm/IR/Instructions.h>
|
||||||
|
#include <llvm/IR/IRBuilder.h>
|
||||||
|
|
||||||
#include "Type.h"
|
#include "Type.h"
|
||||||
|
|
||||||
|
|
@ -15,65 +18,211 @@ namespace jit
|
||||||
|
|
||||||
const char* BasicBlock::NamePrefix = "Instr.";
|
const char* BasicBlock::NamePrefix = "Instr.";
|
||||||
|
|
||||||
BasicBlock::BasicBlock(ProgramCounter _beginInstIdx, ProgramCounter _endInstIdx, llvm::Function* _mainFunc) :
|
BasicBlock::BasicBlock(ProgramCounter _beginInstIdx, ProgramCounter _endInstIdx, llvm::Function* _mainFunc, llvm::IRBuilder<>& _builder) :
|
||||||
m_beginInstIdx(_beginInstIdx),
|
m_beginInstIdx(_beginInstIdx),
|
||||||
m_endInstIdx(_endInstIdx),
|
m_endInstIdx(_endInstIdx),
|
||||||
m_llvmBB(llvm::BasicBlock::Create(_mainFunc->getContext(), {NamePrefix, std::to_string(_beginInstIdx)}, _mainFunc)),
|
m_llvmBB(llvm::BasicBlock::Create(_mainFunc->getContext(), {NamePrefix, std::to_string(_beginInstIdx)}, _mainFunc)),
|
||||||
m_stack(m_llvmBB)
|
m_stack(_builder)
|
||||||
{}
|
{}
|
||||||
|
|
||||||
BasicBlock::BasicBlock(std::string _name, llvm::Function* _mainFunc) :
|
BasicBlock::BasicBlock(std::string _name, llvm::Function* _mainFunc, llvm::IRBuilder<>& _builder) :
|
||||||
m_beginInstIdx(0),
|
m_beginInstIdx(0),
|
||||||
m_endInstIdx(0),
|
m_endInstIdx(0),
|
||||||
m_llvmBB(llvm::BasicBlock::Create(_mainFunc->getContext(), _name, _mainFunc)),
|
m_llvmBB(llvm::BasicBlock::Create(_mainFunc->getContext(), _name, _mainFunc)),
|
||||||
m_stack(m_llvmBB)
|
m_stack(_builder)
|
||||||
{}
|
{}
|
||||||
|
|
||||||
|
BasicBlock::LocalStack::LocalStack(llvm::IRBuilder<>& _builder) :
|
||||||
|
m_builder(_builder),
|
||||||
|
m_initialStack(),
|
||||||
|
m_currentStack(),
|
||||||
|
m_tosOffset(0)
|
||||||
|
{}
|
||||||
|
|
||||||
void BasicBlock::LocalStack::push(llvm::Value* _value)
|
void BasicBlock::LocalStack::push(llvm::Value* _value)
|
||||||
{
|
{
|
||||||
assert(_value->getType() == Type::i256);
|
m_currentStack.push_back(_value);
|
||||||
m_backend.push_back(_value);
|
m_tosOffset += 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
llvm::Value* BasicBlock::LocalStack::pop()
|
llvm::Value* BasicBlock::LocalStack::pop()
|
||||||
{
|
{
|
||||||
auto top = get(0);
|
auto result = get(0);
|
||||||
m_backend.pop_back();
|
|
||||||
return top;
|
if (m_currentStack.size() > 0)
|
||||||
|
m_currentStack.pop_back();
|
||||||
|
|
||||||
|
m_tosOffset -= 1;
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Pushes a copy of _index-th element (tos is 0-th elem).
|
||||||
|
*/
|
||||||
|
void BasicBlock::LocalStack::dup(size_t _index)
|
||||||
|
{
|
||||||
|
auto val = get(_index);
|
||||||
|
push(val);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Swaps tos with _index-th element (tos is 0-th elem).
|
||||||
|
* _index must be > 0.
|
||||||
|
*/
|
||||||
|
void BasicBlock::LocalStack::swap(size_t _index)
|
||||||
|
{
|
||||||
|
assert(_index > 0);
|
||||||
|
auto val = get(_index);
|
||||||
|
auto tos = get(0);
|
||||||
|
set(_index, tos);
|
||||||
|
set(0, val);
|
||||||
|
}
|
||||||
|
|
||||||
|
void BasicBlock::LocalStack::synchronize(Stack& _evmStack)
|
||||||
|
{
|
||||||
|
auto blockTerminator = m_builder.GetInsertBlock()->getTerminator();
|
||||||
|
assert(blockTerminator != nullptr);
|
||||||
|
m_builder.SetInsertPoint(blockTerminator);
|
||||||
|
|
||||||
|
auto currIter = m_currentStack.begin();
|
||||||
|
auto endIter = m_currentStack.end();
|
||||||
|
|
||||||
|
// Update (emit set()) changed values
|
||||||
|
for (int idx = 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);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (m_tosOffset < 0)
|
||||||
|
{
|
||||||
|
// Pop values
|
||||||
|
_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)
|
||||||
|
{
|
||||||
|
auto val = m_initialStack[idx];
|
||||||
|
if (val == nullptr)
|
||||||
|
continue;
|
||||||
|
|
||||||
|
assert(llvm::isa<llvm::PHINode>(val));
|
||||||
|
llvm::PHINode* phi = llvm::cast<llvm::PHINode>(val);
|
||||||
|
if (! phi->use_empty())
|
||||||
|
{
|
||||||
|
// 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);
|
||||||
|
phi->replaceAllUsesWith(newVal);
|
||||||
|
}
|
||||||
|
phi->eraseFromParent();
|
||||||
|
}
|
||||||
|
|
||||||
|
// Reset the stack
|
||||||
|
m_initialStack.erase(m_initialStack.begin(), m_initialStack.end());
|
||||||
|
m_currentStack.erase(m_currentStack.begin(), m_currentStack.end());
|
||||||
|
m_tosOffset = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::vector<llvm::Value*>::iterator BasicBlock::LocalStack::getItemIterator(size_t _index)
|
||||||
|
{
|
||||||
|
if (_index < m_currentStack.size())
|
||||||
|
return m_currentStack.end() - _index - 1;
|
||||||
|
|
||||||
|
// Need to map more elements from the EVM stack
|
||||||
|
auto nNewItems = 1 + _index - m_currentStack.size();
|
||||||
|
m_currentStack.insert(m_currentStack.begin(), nNewItems, nullptr);
|
||||||
|
|
||||||
|
return m_currentStack.end() - _index - 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
llvm::Value* BasicBlock::LocalStack::get(size_t _index)
|
llvm::Value* BasicBlock::LocalStack::get(size_t _index)
|
||||||
{
|
{
|
||||||
if (_index >= m_backend.size())
|
auto itemIter = getItemIterator(_index);
|
||||||
|
|
||||||
|
if (*itemIter == nullptr)
|
||||||
{
|
{
|
||||||
// Create PHI node for missing values
|
// Need to fetch a new item from the EVM stack
|
||||||
auto nMissingVals = _index - m_backend.size() + 1;
|
assert(static_cast<int>(_index) >= m_tosOffset);
|
||||||
m_backend.insert(m_backend.begin(), nMissingVals, nullptr);
|
size_t initialIdx = _index - m_tosOffset;
|
||||||
for (decltype(nMissingVals) i = 0; i < nMissingVals; ++i)
|
if (initialIdx >= m_initialStack.size())
|
||||||
{
|
{
|
||||||
m_backend[nMissingVals - 1 - i] = m_llvmBB->empty() ?
|
auto nNewItems = 1 + initialIdx - m_initialStack.size();
|
||||||
llvm::PHINode::Create(Type::i256, 0, {}, m_llvmBB) :
|
m_initialStack.insert(m_initialStack.end(), nNewItems, nullptr);
|
||||||
llvm::PHINode::Create(Type::i256, 0, {}, m_llvmBB->getFirstNonPHI());
|
|
||||||
m_numRequiredStackItems += 1;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
assert(m_initialStack[initialIdx] == nullptr);
|
||||||
|
// Create a dummy value.
|
||||||
|
std::string name = "get_" + boost::lexical_cast<std::string>(_index);
|
||||||
|
m_initialStack[initialIdx] = m_builder.CreatePHI(Type::i256, 0, name);
|
||||||
|
*itemIter = m_initialStack[initialIdx];
|
||||||
}
|
}
|
||||||
|
|
||||||
return *(m_backend.rbegin() + _index);
|
return *itemIter;
|
||||||
}
|
}
|
||||||
|
|
||||||
void BasicBlock::LocalStack::dup(size_t _index)
|
void BasicBlock::LocalStack::set(size_t _index, llvm::Value* _word)
|
||||||
{
|
{
|
||||||
m_backend.push_back(get(_index));
|
auto itemIter = getItemIterator(_index);
|
||||||
|
*itemIter = _word;
|
||||||
}
|
}
|
||||||
|
|
||||||
void BasicBlock::LocalStack::swap(size_t _index)
|
|
||||||
|
void BasicBlock::dump()
|
||||||
{
|
{
|
||||||
assert(_index != 0);
|
std::cerr << "Initial stack:\n";
|
||||||
get(_index); // Create PHI nodes
|
for (auto val : m_stack.m_initialStack)
|
||||||
std::swap(*m_backend.rbegin(), *(m_backend.rbegin() + _index));
|
{
|
||||||
|
if (val == nullptr)
|
||||||
|
std::cerr << " ?\n";
|
||||||
|
else if (llvm::isa<llvm::Instruction>(val))
|
||||||
|
val->dump();
|
||||||
|
else
|
||||||
|
{
|
||||||
|
std::cerr << " ";
|
||||||
|
val->dump();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
std::cerr << " ...\n";
|
||||||
|
|
||||||
|
std::cerr << "Instructions:\n";
|
||||||
|
for (auto ins = m_llvmBB->begin(); ins != m_llvmBB->end(); ++ins)
|
||||||
|
ins->dump();
|
||||||
|
|
||||||
|
std::cerr << "Current stack (offset = "
|
||||||
|
<< m_stack.m_tosOffset << "):\n";
|
||||||
|
|
||||||
|
for (auto val = m_stack.m_currentStack.rbegin(); val != m_stack.m_currentStack.rend(); ++val)
|
||||||
|
{
|
||||||
|
if (*val == nullptr)
|
||||||
|
std::cerr << " ?\n";
|
||||||
|
else if (llvm::isa<llvm::Instruction>(*val))
|
||||||
|
(*val)->dump();
|
||||||
|
else
|
||||||
|
{
|
||||||
|
std::cerr << " ";
|
||||||
|
(*val)->dump();
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
std::cerr << " ...\n----------------------------------------\n";
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,8 +1,11 @@
|
||||||
|
#pragma once
|
||||||
|
|
||||||
#include <vector>
|
#include <vector>
|
||||||
|
|
||||||
#include <llvm/IR/BasicBlock.h>
|
#include <llvm/IR/BasicBlock.h>
|
||||||
|
|
||||||
|
#include "Stack.h"
|
||||||
|
|
||||||
namespace dev
|
namespace dev
|
||||||
{
|
{
|
||||||
namespace eth
|
namespace eth
|
||||||
|
|
@ -18,49 +21,72 @@ public:
|
||||||
class LocalStack
|
class LocalStack
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
|
|
||||||
/// Pushes value on stack
|
/// Pushes value on stack
|
||||||
void push(llvm::Value* _value);
|
void push(llvm::Value* _value);
|
||||||
|
|
||||||
/// Pops and returns top value
|
/// Pops and returns top value
|
||||||
llvm::Value* pop();
|
llvm::Value* pop();
|
||||||
|
|
||||||
/// Gets _index'th value from top (counting from 0)
|
/// Duplicates _index'th value on stack
|
||||||
llvm::Value* get(size_t _index);
|
|
||||||
|
|
||||||
/// Duplicates _index'th value on stack.
|
|
||||||
void dup(size_t _index);
|
void dup(size_t _index);
|
||||||
|
|
||||||
/// Swaps _index'th value on stack with a value on stack top.
|
/// Swaps _index'th value on stack with a value on stack top.
|
||||||
/// @param _index Index of value to be swaped. Cannot be 0.
|
/// @param _index Index of value to be swaped. Must be > 0.
|
||||||
void swap(size_t _index);
|
void swap(size_t _index);
|
||||||
|
|
||||||
/// Size of the stack
|
/// Synchronize current local stack with the EVM stack.
|
||||||
size_t size() const { return m_backend.size(); }
|
void synchronize(Stack& _evmStack);
|
||||||
|
|
||||||
size_t initialSize() const { return m_numRequiredStackItems; }
|
|
||||||
|
|
||||||
private:
|
private:
|
||||||
// LocalStack(llvm::BasicBlock* _llvmBB) : m_llvmBB(_llvmBB) {}
|
|
||||||
LocalStack(llvm::BasicBlock* _llvmBB) : m_llvmBB(_llvmBB), m_numRequiredStackItems(0) {}
|
LocalStack(llvm::IRBuilder<>& _builder);
|
||||||
LocalStack(LocalStack const&) = delete;
|
LocalStack(LocalStack const&) = delete;
|
||||||
void operator=(LocalStack const&) = delete;
|
void operator=(LocalStack const&) = delete;
|
||||||
friend BasicBlock;
|
friend BasicBlock;
|
||||||
|
|
||||||
|
/// 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);
|
||||||
|
|
||||||
|
std::vector<llvm::Value*>::iterator getItemIterator(size_t _index);
|
||||||
|
|
||||||
private:
|
private:
|
||||||
std::vector<llvm::Value*> m_backend;
|
|
||||||
|
|
||||||
/** Basic block into which phi nodes are inserted */
|
llvm::IRBuilder<>& m_builder;
|
||||||
llvm::BasicBlock* m_llvmBB;
|
|
||||||
|
/**
|
||||||
|
* 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 current 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;
|
||||||
|
|
||||||
/** Number of items required on the EVM stack at the beginning of the block */
|
|
||||||
size_t m_numRequiredStackItems;
|
|
||||||
};
|
};
|
||||||
|
|
||||||
/// Basic block name prefix. The rest is beging instruction index.
|
/// Basic block name prefix. The rest is beging instruction index.
|
||||||
static const char* NamePrefix;
|
static const char* NamePrefix;
|
||||||
|
|
||||||
explicit BasicBlock(ProgramCounter _beginInstIdx, ProgramCounter _endInstIdx, llvm::Function* _mainFunc);
|
explicit BasicBlock(ProgramCounter _beginInstIdx, ProgramCounter _endInstIdx, llvm::Function* _mainFunc, llvm::IRBuilder<>& _builder);
|
||||||
explicit BasicBlock(std::string _name, llvm::Function* _mainFunc);
|
explicit BasicBlock(std::string _name, llvm::Function* _mainFunc, llvm::IRBuilder<>& _builder);
|
||||||
|
|
||||||
BasicBlock(const BasicBlock&) = delete;
|
BasicBlock(const BasicBlock&) = delete;
|
||||||
void operator=(const BasicBlock&) = delete;
|
void operator=(const BasicBlock&) = delete;
|
||||||
|
|
@ -68,11 +94,15 @@ public:
|
||||||
operator llvm::BasicBlock*() { return m_llvmBB; }
|
operator llvm::BasicBlock*() { return m_llvmBB; }
|
||||||
llvm::BasicBlock* llvm() { return m_llvmBB; }
|
llvm::BasicBlock* llvm() { return m_llvmBB; }
|
||||||
|
|
||||||
LocalStack& getStack() { return m_stack; }
|
|
||||||
|
|
||||||
ProgramCounter begin() { return m_beginInstIdx; }
|
ProgramCounter begin() { return m_beginInstIdx; }
|
||||||
ProgramCounter end() { return m_endInstIdx; }
|
ProgramCounter end() { return m_endInstIdx; }
|
||||||
|
|
||||||
|
LocalStack& localStack() { return m_stack; }
|
||||||
|
|
||||||
|
/// Prints local stack and block instructions to stderr.
|
||||||
|
/// Useful for calling in a debugger session.
|
||||||
|
void dump();
|
||||||
|
|
||||||
private:
|
private:
|
||||||
ProgramCounter const m_beginInstIdx;
|
ProgramCounter const m_beginInstIdx;
|
||||||
ProgramCounter const m_endInstIdx;
|
ProgramCounter const m_endInstIdx;
|
||||||
|
|
|
||||||
|
|
@ -123,12 +123,12 @@ void Compiler::createBasicBlocks(bytesConstRef bytecode)
|
||||||
auto beginInstIdx = *it;
|
auto beginInstIdx = *it;
|
||||||
++it;
|
++it;
|
||||||
auto endInstIdx = it != splitPoints.cend() ? *it : bytecode.size();
|
auto endInstIdx = it != splitPoints.cend() ? *it : bytecode.size();
|
||||||
basicBlocks.emplace(std::piecewise_construct, std::forward_as_tuple(beginInstIdx), std::forward_as_tuple(beginInstIdx, endInstIdx, m_mainFunc));
|
basicBlocks.emplace(std::piecewise_construct, std::forward_as_tuple(beginInstIdx), std::forward_as_tuple(beginInstIdx, endInstIdx, m_mainFunc, m_builder));
|
||||||
}
|
}
|
||||||
|
|
||||||
m_stopBB = llvm::BasicBlock::Create(m_mainFunc->getContext(), "Stop", m_mainFunc);
|
m_stopBB = llvm::BasicBlock::Create(m_mainFunc->getContext(), "Stop", m_mainFunc);
|
||||||
m_badJumpBlock = std::make_unique<BasicBlock>("BadJumpBlock", m_mainFunc);
|
m_badJumpBlock = std::make_unique<BasicBlock>("BadJumpBlock", m_mainFunc, m_builder);
|
||||||
m_jumpTableBlock = std::make_unique<BasicBlock>("JumpTableBlock", m_mainFunc);
|
m_jumpTableBlock = std::make_unique<BasicBlock>("JumpTableBlock", m_mainFunc, m_builder);
|
||||||
|
|
||||||
for (auto it = directJumpTargets.cbegin(); it != directJumpTargets.cend(); ++it)
|
for (auto it = directJumpTargets.cbegin(); it != directJumpTargets.cend(); ++it)
|
||||||
{
|
{
|
||||||
|
|
@ -186,6 +186,9 @@ std::unique_ptr<llvm::Module> Compiler::compile(bytesConstRef bytecode)
|
||||||
++iterCopy;
|
++iterCopy;
|
||||||
auto nextBasicBlock = (iterCopy != basicBlocks.end()) ? iterCopy->second.llvm() : nullptr;
|
auto nextBasicBlock = (iterCopy != basicBlocks.end()) ? iterCopy->second.llvm() : nullptr;
|
||||||
compileBasicBlock(basicBlock, bytecode, memory, ext, gasMeter, nextBasicBlock);
|
compileBasicBlock(basicBlock, bytecode, memory, ext, gasMeter, nextBasicBlock);
|
||||||
|
if (getenv("EVMCC_DEBUG_BLOCKS"))
|
||||||
|
basicBlock.dump();
|
||||||
|
basicBlock.localStack().synchronize(stack);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Code for special blocks:
|
// Code for special blocks:
|
||||||
|
|
@ -201,9 +204,9 @@ std::unique_ptr<llvm::Module> Compiler::compile(bytesConstRef bytecode)
|
||||||
m_builder.SetInsertPoint(m_jumpTableBlock->llvm());
|
m_builder.SetInsertPoint(m_jumpTableBlock->llvm());
|
||||||
if (m_indirectJumpTargets.size() > 0)
|
if (m_indirectJumpTargets.size() > 0)
|
||||||
{
|
{
|
||||||
auto& stack = m_jumpTableBlock->getStack();
|
// auto& stack = m_jumpTableBlock->getStack();
|
||||||
|
|
||||||
auto dest = stack.pop();
|
auto dest = m_jumpTableBlock->localStack().pop(); //m_jumpTableBlock->localGet(0); // stack.pop();
|
||||||
auto switchInstr = m_builder.CreateSwitch(dest, m_badJumpBlock->llvm(),
|
auto switchInstr = m_builder.CreateSwitch(dest, m_badJumpBlock->llvm(),
|
||||||
m_indirectJumpTargets.size());
|
m_indirectJumpTargets.size());
|
||||||
for (auto it = m_indirectJumpTargets.cbegin(); it != m_indirectJumpTargets.cend(); ++it)
|
for (auto it = m_indirectJumpTargets.cbegin(); it != m_indirectJumpTargets.cend(); ++it)
|
||||||
|
|
@ -218,7 +221,8 @@ std::unique_ptr<llvm::Module> Compiler::compile(bytesConstRef bytecode)
|
||||||
m_builder.CreateBr(m_badJumpBlock->llvm());
|
m_builder.CreateBr(m_badJumpBlock->llvm());
|
||||||
}
|
}
|
||||||
|
|
||||||
linkBasicBlocks(stack);
|
m_jumpTableBlock->localStack().synchronize(stack);
|
||||||
|
linkBasicBlocks();
|
||||||
|
|
||||||
return module;
|
return module;
|
||||||
}
|
}
|
||||||
|
|
@ -226,8 +230,8 @@ std::unique_ptr<llvm::Module> Compiler::compile(bytesConstRef bytecode)
|
||||||
|
|
||||||
void Compiler::compileBasicBlock(BasicBlock& basicBlock, bytesConstRef bytecode, Memory& memory, Ext& ext, GasMeter& gasMeter, llvm::BasicBlock* nextBasicBlock)
|
void Compiler::compileBasicBlock(BasicBlock& basicBlock, bytesConstRef bytecode, Memory& memory, Ext& ext, GasMeter& gasMeter, llvm::BasicBlock* nextBasicBlock)
|
||||||
{
|
{
|
||||||
auto& stack = basicBlock.getStack();
|
|
||||||
m_builder.SetInsertPoint(basicBlock.llvm());
|
m_builder.SetInsertPoint(basicBlock.llvm());
|
||||||
|
auto& stack = basicBlock.localStack();
|
||||||
|
|
||||||
for (auto currentPC = basicBlock.begin(); currentPC != basicBlock.end(); ++currentPC)
|
for (auto currentPC = basicBlock.begin(); currentPC != basicBlock.end(); ++currentPC)
|
||||||
{
|
{
|
||||||
|
|
@ -589,8 +593,6 @@ void Compiler::compileBasicBlock(BasicBlock& basicBlock, bytesConstRef bytecode,
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
// FIXME: this get(0) is a temporary workaround to get some of the jump tests running.
|
|
||||||
stack.get(0);
|
|
||||||
m_builder.CreateBr(m_jumpTableBlock->llvm());
|
m_builder.CreateBr(m_jumpTableBlock->llvm());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -873,7 +875,7 @@ void Compiler::compileBasicBlock(BasicBlock& basicBlock, bytesConstRef bytecode,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
void Compiler::linkBasicBlocks(Stack& stack)
|
void Compiler::linkBasicBlocks() // Stack& stack)
|
||||||
{
|
{
|
||||||
// Remove dead basic blocks
|
// Remove dead basic blocks
|
||||||
auto sthErased = false;
|
auto sthErased = false;
|
||||||
|
|
@ -902,13 +904,14 @@ void Compiler::linkBasicBlocks(Stack& stack)
|
||||||
m_jumpTableBlock.reset();
|
m_jumpTableBlock.reset();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
struct BBInfo
|
struct BBInfo
|
||||||
{
|
{
|
||||||
BasicBlock& bblock;
|
BasicBlock& bblock;
|
||||||
std::vector<BBInfo*> predecessors;
|
std::vector<BBInfo*> predecessors;
|
||||||
size_t inputItems;
|
size_t inputItems;
|
||||||
size_t outputItems;
|
size_t outputItems;
|
||||||
|
std::vector<llvm::PHINode*> phisToRewrite;
|
||||||
|
|
||||||
BBInfo(BasicBlock& _bblock)
|
BBInfo(BasicBlock& _bblock)
|
||||||
: bblock(_bblock),
|
: bblock(_bblock),
|
||||||
|
|
@ -978,7 +981,8 @@ void Compiler::linkBasicBlocks(Stack& stack)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
std::map<llvm::Instruction*, llvm::Value*> phiReplacements;
|
// std::map<llvm::Instruction*, llvm::Value*> phiReplacements;
|
||||||
|
// std::vector<llvm::Instruction*> phiNodesToRewrite;
|
||||||
|
|
||||||
// Propagate values between blocks.
|
// Propagate values between blocks.
|
||||||
for (auto& pair : cfg)
|
for (auto& pair : cfg)
|
||||||
|
|
@ -1001,13 +1005,14 @@ void Compiler::linkBasicBlocks(Stack& stack)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Turn the remaining phi nodes into stack.pop's.
|
// Turn the remaining phi nodes into stack.pop's.
|
||||||
m_builder.SetInsertPoint(llbb, llvm::BasicBlock::iterator(llbb->getFirstNonPHI()));
|
// m_builder.SetInsertPoint(llbb, llvm::BasicBlock::iterator(llbb->getFirstNonPHI()));
|
||||||
for (; llvm::isa<llvm::PHINode>(*instrIter); ++instrIter)
|
for (; llvm::isa<llvm::PHINode>(*instrIter); ++instrIter)
|
||||||
{
|
{
|
||||||
auto phi = llvm::cast<llvm::PHINode>(instrIter);
|
auto phi = llvm::cast<llvm::PHINode>(instrIter);
|
||||||
auto value = stack.popWord();
|
// auto value = stack.popWord();
|
||||||
// Don't delete the phi node yet. It may still be stored in a local stack of some block.
|
// Don't delete the phi node yet. It may still be stored in a local stack of some block.
|
||||||
phiReplacements[phi] = value;
|
// phiReplacements[phi] = value;
|
||||||
|
bbInfo.phisToRewrite.push_back(phi);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Emit stack push's at the end of the block, just before the terminator;
|
// Emit stack push's at the end of the block, just before the terminator;
|
||||||
|
|
@ -1018,11 +1023,33 @@ void Compiler::linkBasicBlocks(Stack& stack)
|
||||||
stack.pushWord(bblock.getStack().get(localStackSize - 1 - i));
|
stack.pushWord(bblock.getStack().get(localStackSize - 1 - i));
|
||||||
}
|
}
|
||||||
|
|
||||||
for (auto& entry : phiReplacements)
|
for (auto& entry : cfg)
|
||||||
{
|
{
|
||||||
entry.first->replaceAllUsesWith(entry.second);
|
// Where was the last stack.pop() inserted
|
||||||
entry.first->eraseFromParent();
|
auto lastPopIt = entry.first->begin();
|
||||||
|
|
||||||
|
for (auto phi : entry.second.phisToRewrite)
|
||||||
|
{
|
||||||
|
// Insert stack.pop() before the first use of phi,
|
||||||
|
// then replace all uses of phi with the popped val.
|
||||||
|
|
||||||
|
if (phi->use_begin() == phi->use_end())
|
||||||
|
{
|
||||||
|
// For a phi with no uses, insert pop just after the previous one
|
||||||
|
}
|
||||||
|
std::cout << "*** PHI node " << phi->getName().str() << " has no uses!\n";
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
assert(llvm::isa<llvm::Instruction>(phi->use_begin()->getUser()));
|
||||||
|
|
||||||
|
m_builder.SetInsertPoint(*phi->use_begin());
|
||||||
|
auto popVal = stack.popWord();
|
||||||
|
phi->replaceAllUsesWith(popVal);
|
||||||
|
phi->eraseFromParent();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
*/
|
||||||
}
|
}
|
||||||
|
|
||||||
void Compiler::dumpBasicBlockGraph(std::ostream& out)
|
void Compiler::dumpBasicBlockGraph(std::ostream& out)
|
||||||
|
|
@ -1030,7 +1057,7 @@ void Compiler::dumpBasicBlockGraph(std::ostream& out)
|
||||||
out << "digraph BB {\n"
|
out << "digraph BB {\n"
|
||||||
<< " node [shape=record];\n"
|
<< " node [shape=record];\n"
|
||||||
<< " entry [share=record, label=\"entry block\"];\n";
|
<< " entry [share=record, label=\"entry block\"];\n";
|
||||||
|
/*
|
||||||
std::vector<BasicBlock*> blocks;
|
std::vector<BasicBlock*> blocks;
|
||||||
for (auto& pair : this->basicBlocks)
|
for (auto& pair : this->basicBlocks)
|
||||||
blocks.push_back(&pair.second);
|
blocks.push_back(&pair.second);
|
||||||
|
|
@ -1076,6 +1103,7 @@ void Compiler::dumpBasicBlockGraph(std::ostream& out)
|
||||||
}
|
}
|
||||||
|
|
||||||
out << "}\n";
|
out << "}\n";
|
||||||
|
*/
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -33,7 +33,7 @@ private:
|
||||||
|
|
||||||
void compileBasicBlock(BasicBlock& basicBlock, bytesConstRef bytecode, class Memory& memory, class Ext& ext, class GasMeter& gasMeter, llvm::BasicBlock* nextBasicBlock);
|
void compileBasicBlock(BasicBlock& basicBlock, bytesConstRef bytecode, class Memory& memory, class Ext& ext, class GasMeter& gasMeter, llvm::BasicBlock* nextBasicBlock);
|
||||||
|
|
||||||
void linkBasicBlocks(class Stack& stack);
|
void linkBasicBlocks(); //class Stack& stack);
|
||||||
|
|
||||||
|
|
||||||
llvm::IRBuilder<> m_builder;
|
llvm::IRBuilder<> m_builder;
|
||||||
|
|
|
||||||
|
|
@ -19,6 +19,7 @@
|
||||||
|
|
||||||
#include "Runtime.h"
|
#include "Runtime.h"
|
||||||
#include "Memory.h"
|
#include "Memory.h"
|
||||||
|
#include "Stack.h"
|
||||||
#include "Type.h"
|
#include "Type.h"
|
||||||
|
|
||||||
namespace dev
|
namespace dev
|
||||||
|
|
@ -35,7 +36,6 @@ ExecutionEngine::ExecutionEngine()
|
||||||
|
|
||||||
extern "C" { EXPORT std::jmp_buf* rt_jmpBuf; }
|
extern "C" { EXPORT std::jmp_buf* rt_jmpBuf; }
|
||||||
|
|
||||||
|
|
||||||
int ExecutionEngine::run(std::unique_ptr<llvm::Module> _module, u256& _gas, ExtVMFace* _ext)
|
int ExecutionEngine::run(std::unique_ptr<llvm::Module> _module, u256& _gas, ExtVMFace* _ext)
|
||||||
{
|
{
|
||||||
auto module = _module.get(); // Keep ownership of the module in _module
|
auto module = _module.get(); // Keep ownership of the module in _module
|
||||||
|
|
@ -117,6 +117,8 @@ int ExecutionEngine::run(std::unique_ptr<llvm::Module> _module, u256& _gas, ExtV
|
||||||
// Return remaining gas
|
// Return remaining gas
|
||||||
_gas = returnCode == ReturnCode::OutOfGas ? 0 : Runtime::getGas();
|
_gas = returnCode == ReturnCode::OutOfGas ? 0 : Runtime::getGas();
|
||||||
|
|
||||||
|
std::cout << "Max stack size: " << Stack::maxStackSize << std::endl;
|
||||||
|
|
||||||
if (returnCode == ReturnCode::Return)
|
if (returnCode == ReturnCode::Return)
|
||||||
{
|
{
|
||||||
returnData = Memory::getReturnData().toVector(); // TODO: It might be better to place is in Runtime interface
|
returnData = Memory::getReturnData().toVector(); // TODO: It might be better to place is in Runtime interface
|
||||||
|
|
@ -128,6 +130,7 @@ int ExecutionEngine::run(std::unique_ptr<llvm::Module> _module, u256& _gas, ExtV
|
||||||
}
|
}
|
||||||
|
|
||||||
std::cout << "RETURN CODE: " << (int)returnCode << std::endl;
|
std::cout << "RETURN CODE: " << (int)returnCode << std::endl;
|
||||||
|
|
||||||
return static_cast<int>(returnCode);
|
return static_cast<int>(returnCode);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -17,34 +17,48 @@ namespace jit
|
||||||
Stack::Stack(llvm::IRBuilder<>& _builder)
|
Stack::Stack(llvm::IRBuilder<>& _builder)
|
||||||
: CompilerHelper(_builder)
|
: CompilerHelper(_builder)
|
||||||
{
|
{
|
||||||
auto i256Ty = m_builder.getIntNTy(256);
|
m_arg = m_builder.CreateAlloca(Type::i256, nullptr, "stack.arg");
|
||||||
auto i256PtrTy = i256Ty->getPointerTo();
|
|
||||||
|
|
||||||
m_arg = m_builder.CreateAlloca(i256Ty, nullptr, "stack.retVal");
|
|
||||||
|
|
||||||
using namespace llvm;
|
using namespace llvm;
|
||||||
using Linkage = GlobalValue::LinkageTypes;
|
using Linkage = GlobalValue::LinkageTypes;
|
||||||
|
|
||||||
auto module = getModule();
|
auto module = getModule();
|
||||||
m_push = Function::Create(FunctionType::get(m_builder.getVoidTy(), i256PtrTy, false), Linkage::ExternalLinkage, "stack_push", module);
|
m_push = Function::Create(FunctionType::get(m_builder.getVoidTy(), Type::WordPtr, false), Linkage::ExternalLinkage, "stack_push", module);
|
||||||
m_pop = Function::Create(FunctionType::get(m_builder.getVoidTy(), i256PtrTy, false), Linkage::ExternalLinkage, "stack_pop", module);
|
m_pop = Function::Create(FunctionType::get(m_builder.getVoidTy(), Type::Size, false), Linkage::ExternalLinkage, "stack_pop", module);
|
||||||
|
llvm::Type* getSetArgTypes[] = {Type::Size, Type::WordPtr};
|
||||||
|
m_get = Function::Create(FunctionType::get(m_builder.getVoidTy(), ArrayRef<llvm::Type*>(getSetArgTypes), false), Linkage::ExternalLinkage, "stack_get", module);
|
||||||
|
m_set = Function::Create(FunctionType::get(m_builder.getVoidTy(), ArrayRef<llvm::Type*>(getSetArgTypes), false), Linkage::ExternalLinkage, "stack_set", module);
|
||||||
}
|
}
|
||||||
|
|
||||||
Stack::~Stack()
|
Stack::~Stack()
|
||||||
{}
|
{}
|
||||||
|
|
||||||
llvm::Instruction* Stack::popWord()
|
llvm::Value* Stack::get(size_t _index)
|
||||||
{
|
{
|
||||||
m_builder.CreateCall(m_pop, m_arg);
|
m_builder.CreateCall2(m_get, llvm::ConstantInt::get(Type::Size, _index, false), m_arg);
|
||||||
return m_builder.CreateLoad(m_arg);
|
return m_builder.CreateLoad(m_arg);
|
||||||
}
|
}
|
||||||
|
|
||||||
void Stack::pushWord(llvm::Value* _word)
|
void Stack::set(size_t _index, llvm::Value* _value)
|
||||||
{
|
{
|
||||||
m_builder.CreateStore(_word, m_arg);
|
m_builder.CreateStore(_value, m_arg);
|
||||||
|
m_builder.CreateCall2(m_set, llvm::ConstantInt::get(Type::Size, _index, false), m_arg);
|
||||||
|
}
|
||||||
|
|
||||||
|
void Stack::pop(size_t _count)
|
||||||
|
{
|
||||||
|
m_builder.CreateCall(m_pop, llvm::ConstantInt::get(Type::Size, _count, false));
|
||||||
|
}
|
||||||
|
|
||||||
|
void Stack::push(llvm::Value* _value)
|
||||||
|
{
|
||||||
|
m_builder.CreateStore(_value, m_arg);
|
||||||
m_builder.CreateCall(m_push, m_arg);
|
m_builder.CreateCall(m_push, m_arg);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
size_t Stack::maxStackSize = 0;
|
||||||
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -56,21 +70,42 @@ using namespace dev::eth::jit;
|
||||||
|
|
||||||
extern std::jmp_buf* rt_jmpBuf;
|
extern std::jmp_buf* rt_jmpBuf;
|
||||||
|
|
||||||
EXPORT void stack_pop(i256* _ret)
|
EXPORT void stack_pop(uint64_t _count)
|
||||||
{
|
{
|
||||||
auto& stack = Runtime::getStack();
|
auto& stack = Runtime::getStack();
|
||||||
if (stack.size() == 0)
|
if (stack.size() < _count)
|
||||||
longjmp(*rt_jmpBuf, static_cast<uint64_t>(ReturnCode::StackTooSmall));
|
longjmp(*rt_jmpBuf, static_cast<uint64_t>(ReturnCode::StackTooSmall));
|
||||||
|
|
||||||
assert(stack.size() > 0);
|
stack.erase(stack.end() - _count, stack.end());
|
||||||
*_ret = stack.back();
|
|
||||||
stack.pop_back();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
EXPORT void stack_push(i256* _word)
|
EXPORT void stack_push(i256* _word)
|
||||||
{
|
{
|
||||||
auto& stack = Runtime::getStack();
|
auto& stack = Runtime::getStack();
|
||||||
stack.push_back(*_word);
|
stack.push_back(*_word);
|
||||||
|
|
||||||
|
if (stack.size() > Stack::maxStackSize)
|
||||||
|
Stack::maxStackSize = stack.size();
|
||||||
|
}
|
||||||
|
|
||||||
|
EXPORT void stack_get(uint64_t _index, i256* _ret)
|
||||||
|
{
|
||||||
|
auto& stack = Runtime::getStack();
|
||||||
|
// TODO: encode _index and stack size in the return code
|
||||||
|
if (stack.size() <= _index)
|
||||||
|
longjmp(*rt_jmpBuf, static_cast<uint64_t>(ReturnCode::StackTooSmall));
|
||||||
|
|
||||||
|
*_ret = *(stack.rbegin() + _index);
|
||||||
|
}
|
||||||
|
|
||||||
|
EXPORT void stack_set(uint64_t _index, i256* _word)
|
||||||
|
{
|
||||||
|
auto& stack = Runtime::getStack();
|
||||||
|
// TODO: encode _index and stack size in the return code
|
||||||
|
if (stack.size() <= _index)
|
||||||
|
longjmp(*rt_jmpBuf, static_cast<uint64_t>(ReturnCode::StackTooSmall));
|
||||||
|
|
||||||
|
*(stack.rbegin() + _index) = *_word;
|
||||||
}
|
}
|
||||||
|
|
||||||
} // extern "C"
|
} // extern "C"
|
||||||
|
|
|
||||||
|
|
@ -14,15 +14,23 @@ namespace jit
|
||||||
class Stack : public CompilerHelper
|
class Stack : public CompilerHelper
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
|
|
||||||
Stack(llvm::IRBuilder<>& builder);
|
Stack(llvm::IRBuilder<>& builder);
|
||||||
virtual ~Stack();
|
virtual ~Stack();
|
||||||
|
|
||||||
void pushWord(llvm::Value* _word);
|
llvm::Value* get(size_t _index);
|
||||||
llvm::Instruction* popWord();
|
void set(size_t _index, llvm::Value* _value);
|
||||||
|
void pop(size_t _count);
|
||||||
|
void push(llvm::Value* _value);
|
||||||
|
|
||||||
|
static size_t maxStackSize;
|
||||||
|
|
||||||
private:
|
private:
|
||||||
|
|
||||||
llvm::Function* m_push;
|
llvm::Function* m_push;
|
||||||
llvm::Function* m_pop;
|
llvm::Function* m_pop;
|
||||||
|
llvm::Function* m_get;
|
||||||
|
llvm::Function* m_set;
|
||||||
|
|
||||||
llvm::Value* m_arg;
|
llvm::Value* m_arg;
|
||||||
};
|
};
|
||||||
|
|
|
||||||
|
|
@ -13,6 +13,7 @@ namespace jit
|
||||||
llvm::IntegerType* Type::i256;
|
llvm::IntegerType* Type::i256;
|
||||||
llvm::PointerType* Type::WordPtr;
|
llvm::PointerType* Type::WordPtr;
|
||||||
llvm::IntegerType* Type::lowPrecision;
|
llvm::IntegerType* Type::lowPrecision;
|
||||||
|
llvm::IntegerType* Type::Size;
|
||||||
llvm::IntegerType* Type::Byte;
|
llvm::IntegerType* Type::Byte;
|
||||||
llvm::PointerType* Type::BytePtr;
|
llvm::PointerType* Type::BytePtr;
|
||||||
llvm::Type* Type::Void;
|
llvm::Type* Type::Void;
|
||||||
|
|
@ -23,6 +24,8 @@ void Type::init(llvm::LLVMContext& _context)
|
||||||
i256 = llvm::Type::getIntNTy(_context, 256);
|
i256 = llvm::Type::getIntNTy(_context, 256);
|
||||||
WordPtr = i256->getPointerTo();
|
WordPtr = i256->getPointerTo();
|
||||||
lowPrecision = llvm::Type::getInt64Ty(_context);
|
lowPrecision = llvm::Type::getInt64Ty(_context);
|
||||||
|
// TODO: Size should be architecture-dependent
|
||||||
|
Size = llvm::Type::getInt64Ty(_context);
|
||||||
Byte = llvm::Type::getInt8Ty(_context);
|
Byte = llvm::Type::getInt8Ty(_context);
|
||||||
BytePtr = Byte->getPointerTo();
|
BytePtr = Byte->getPointerTo();
|
||||||
Void = llvm::Type::getVoidTy(_context);
|
Void = llvm::Type::getVoidTy(_context);
|
||||||
|
|
|
||||||
|
|
@ -20,6 +20,8 @@ struct Type
|
||||||
/// @TODO: Use 64-bit for now. In 128-bit compiler-rt library functions are required
|
/// @TODO: Use 64-bit for now. In 128-bit compiler-rt library functions are required
|
||||||
static llvm::IntegerType* lowPrecision;
|
static llvm::IntegerType* lowPrecision;
|
||||||
|
|
||||||
|
static llvm::IntegerType* Size;
|
||||||
|
|
||||||
static llvm::IntegerType* Byte;
|
static llvm::IntegerType* Byte;
|
||||||
static llvm::PointerType* BytePtr;
|
static llvm::PointerType* BytePtr;
|
||||||
|
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue