Browse Source

Move fields from BasicBlock to LocalStack. Remove dump() functions.

cl-refactor
Paweł Bylica 10 years ago
parent
commit
1e926fe6bc
  1. 115
      evmjit/libevmjit/BasicBlock.cpp
  2. 55
      evmjit/libevmjit/BasicBlock.h
  3. 82
      evmjit/libevmjit/Compiler.cpp
  4. 9
      evmjit/libevmjit/Compiler.h

115
evmjit/libevmjit/BasicBlock.cpp

@ -23,42 +23,39 @@ namespace jit
static const char* jumpDestName = "JmpDst."; static const char* jumpDestName = "JmpDst.";
static const char* basicBlockName = "Instr."; 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, bool isJumpDest):
m_firstInstrIdx{_firstInstrIdx}, m_firstInstrIdx{_firstInstrIdx},
m_begin(_begin), m_begin(_begin),
m_end(_end), m_end(_end),
m_llvmBB(llvm::BasicBlock::Create(_mainFunc->getContext(), {isJumpDest ? jumpDestName : basicBlockName, std::to_string(_firstInstrIdx)}, _mainFunc)), m_llvmBB(llvm::BasicBlock::Create(_mainFunc->getContext(), {isJumpDest ? jumpDestName : basicBlockName, std::to_string(_firstInstrIdx)}, _mainFunc)),
m_builder(_builder),
m_isJumpDest(isJumpDest) m_isJumpDest(isJumpDest)
{} {}
BasicBlock::BasicBlock(std::string _name, llvm::Function* _mainFunc, llvm::IRBuilder<>& _builder, bool isJumpDest) : BasicBlock::BasicBlock(std::string _name, llvm::Function* _mainFunc, bool isJumpDest):
m_llvmBB(llvm::BasicBlock::Create(_mainFunc->getContext(), _name, _mainFunc)), m_llvmBB(llvm::BasicBlock::Create(_mainFunc->getContext(), _name, _mainFunc)),
m_builder(_builder),
m_isJumpDest(isJumpDest) m_isJumpDest(isJumpDest)
{} {}
LocalStack::LocalStack(BasicBlock& _owner, Stack& _globalStack) : LocalStack::LocalStack(Stack& _globalStack):
m_bblock(_owner),
m_global(_globalStack) m_global(_globalStack)
{} {}
void LocalStack::push(llvm::Value* _value) void LocalStack::push(llvm::Value* _value)
{ {
assert(_value->getType() == Type::Word); assert(_value->getType() == Type::Word);
m_bblock.m_currentStack.push_back(_value); m_currentStack.push_back(_value);
m_maxSize = std::max(m_maxSize, m_bblock.m_currentStack.size()); m_maxSize = std::max(m_maxSize, m_currentStack.size()); // FIXME: This is wrong too. + add min size;
} }
llvm::Value* LocalStack::pop() llvm::Value* LocalStack::pop()
{ {
auto item = get(0); auto item = get(0);
assert(!m_bblock.m_currentStack.empty() || !m_bblock.m_initialStack.empty()); assert(!m_currentStack.empty() || !m_initialStack.empty());
if (m_bblock.m_currentStack.size() > 0) if (m_currentStack.size() > 0)
m_bblock.m_currentStack.pop_back(); m_currentStack.pop_back();
else else
++m_bblock.m_globalPops; ++m_globalPops;
return item; return item;
} }
@ -87,15 +84,13 @@ void LocalStack::swap(size_t _index)
llvm::Value* LocalStack::get(size_t _index) llvm::Value* LocalStack::get(size_t _index)
{ {
auto& currentStack = m_bblock.m_currentStack; if (_index < m_currentStack.size())
if (_index < currentStack.size()) return *(m_currentStack.rbegin() + _index); // count from back
return *(currentStack.rbegin() + _index); // count from back
auto& initialStack = m_bblock.m_initialStack; auto idx = _index - m_currentStack.size() + m_globalPops;
auto idx = _index - currentStack.size() + m_bblock.m_globalPops; if (idx >= m_initialStack.size())
if (idx >= initialStack.size()) m_initialStack.resize(idx + 1);
initialStack.resize(idx + 1); auto& item = m_initialStack[idx];
auto& item = initialStack[idx];
if (!item) if (!item)
item = m_global.get(idx); item = m_global.get(idx);
@ -105,104 +100,50 @@ llvm::Value* LocalStack::get(size_t _index)
void LocalStack::set(size_t _index, llvm::Value* _word) void LocalStack::set(size_t _index, llvm::Value* _word)
{ {
auto& currentStack = m_bblock.m_currentStack; if (_index < m_currentStack.size())
if (_index < currentStack.size())
{ {
*(currentStack.rbegin() + _index) = _word; *(m_currentStack.rbegin() + _index) = _word;
return; return;
} }
auto& initialStack = m_bblock.m_initialStack; auto idx = _index - m_currentStack.size() + m_globalPops;
auto idx = _index - currentStack.size() + m_bblock.m_globalPops; assert(idx < m_initialStack.size());
assert(idx < initialStack.size()); m_initialStack[idx] = _word;
initialStack[idx] = _word;
} }
void BasicBlock::synchronizeLocalStack(Stack& _evmStack) void LocalStack::finalize(llvm::IRBuilder<>& _builder, llvm::BasicBlock& _bb)
{ {
auto blockTerminator = m_llvmBB->getTerminator(); auto blockTerminator = _bb.getTerminator();
assert(blockTerminator); assert(blockTerminator);
if (blockTerminator->getOpcode() != llvm::Instruction::Ret) if (blockTerminator->getOpcode() != llvm::Instruction::Ret)
{ {
// Not needed in case of ret instruction. Ret invalidates the stack. // Not needed in case of ret instruction. Ret invalidates the stack.
m_builder.SetInsertPoint(blockTerminator); _builder.SetInsertPoint(blockTerminator);
// Update items fetched from global stack ignoring the poped ones // Update items fetched from global stack ignoring the poped ones
assert(m_globalPops <= m_initialStack.size()); // pop() always does get() assert(m_globalPops <= m_initialStack.size()); // pop() always does get()
for (auto i = m_globalPops; i < m_initialStack.size(); ++i) for (auto i = m_globalPops; i < m_initialStack.size(); ++i)
{ {
if (m_initialStack[i]) if (m_initialStack[i])
_evmStack.set(i, m_initialStack[i]); m_global.set(i, m_initialStack[i]);
} }
// Add new items // Add new items
for (auto& item: m_currentStack) for (auto& item: m_currentStack)
{ {
if (m_globalPops) // Override poped global items if (m_globalPops) // Override poped global items
_evmStack.set(--m_globalPops, item); // using pops counter as the index m_global.set(--m_globalPops, item); // using pops counter as the index
else else
_evmStack.push(item); m_global.push(item);
} }
// Pop not overriden items // Pop not overriden items
if (m_globalPops) if (m_globalPops)
_evmStack.pop(m_globalPops); m_global.pop(m_globalPops);
} }
} }
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:\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");
}
if (! _dotOutput)
out << " ...\n----------------------------------------\n";
}
} }
} }

55
evmjit/libevmjit/BasicBlock.h

@ -19,7 +19,7 @@ class BasicBlock;
class LocalStack class LocalStack
{ {
public: public:
LocalStack(BasicBlock& _owner, Stack& _globalStack); explicit LocalStack(Stack& _globalStack);
LocalStack(LocalStack const&) = delete; LocalStack(LocalStack const&) = delete;
void operator=(LocalStack const&) = delete; void operator=(LocalStack const&) = delete;
@ -36,8 +36,13 @@ public:
/// @param _index Index of value to be swaped. Must be > 0. /// @param _index Index of value to be swaped. Must be > 0.
void swap(size_t _index); void swap(size_t _index);
ssize_t getDiff() const { return static_cast<ssize_t>(m_currentStack.size()) - static_cast<ssize_t>(m_globalPops); }
size_t getMaxSize() const { return m_maxSize; } size_t getMaxSize() const { return m_maxSize; }
/// TODO: comment
/// TODO: It must be the same builder as in global stack.
void finalize(llvm::IRBuilder<>& _builder, llvm::BasicBlock& _bb);
private: private:
/// Gets _index'th value from top (counting from 0) /// Gets _index'th value from top (counting from 0)
llvm::Value* get(size_t _index); llvm::Value* get(size_t _index);
@ -45,8 +50,20 @@ private:
/// Sets _index'th value from top (counting from 0) /// Sets _index'th value from top (counting from 0)
void set(size_t _index, llvm::Value* _value); void set(size_t _index, llvm::Value* _value);
private: /// This stack contains LLVM values that correspond to items found at
BasicBlock& m_bblock; /// 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;
size_t m_globalPops = 0; ///< Number of items poped from global stack. In other words: global - local stack overlap.
Stack& m_global; Stack& m_global;
size_t m_maxSize = 0; ///< Max size reached by the stack. size_t m_maxSize = 0; ///< Max size reached by the stack.
}; };
@ -54,8 +71,8 @@ private:
class BasicBlock class BasicBlock
{ {
public: public:
explicit BasicBlock(instr_idx _firstInstrIdx, code_iterator _begin, code_iterator _end, llvm::Function* _mainFunc, llvm::IRBuilder<>& _builder, bool isJumpDest); explicit BasicBlock(instr_idx _firstInstrIdx, code_iterator _begin, code_iterator _end, llvm::Function* _mainFunc, bool isJumpDest);
explicit BasicBlock(std::string _name, llvm::Function* _mainFunc, llvm::IRBuilder<>& _builder, bool isJumpDest); explicit BasicBlock(std::string _name, llvm::Function* _mainFunc, bool isJumpDest);
BasicBlock(const BasicBlock&) = delete; BasicBlock(const BasicBlock&) = delete;
BasicBlock& operator=(const BasicBlock&) = delete; BasicBlock& operator=(const BasicBlock&) = delete;
@ -71,16 +88,6 @@ public:
llvm::Value* getJumpTarget() const { return m_jumpTarget; } llvm::Value* getJumpTarget() const { return m_jumpTarget; }
void setJumpTarget(llvm::Value* _jumpTarget) { m_jumpTarget = _jumpTarget; } void setJumpTarget(llvm::Value* _jumpTarget) { m_jumpTarget = _jumpTarget; }
ssize_t getDiff() const { return static_cast<ssize_t>(m_currentStack.size()) - static_cast<ssize_t>(m_globalPops); }
/// 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: private:
instr_idx const m_firstInstrIdx = 0; ///< Code index of first instruction in the block 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_begin = {}; ///< Iterator pointing code beginning of the block
@ -88,24 +95,6 @@ private:
llvm::BasicBlock* const m_llvmBB; llvm::BasicBlock* const m_llvmBB;
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;
friend class LocalStack;
size_t m_globalPops = 0; ///< Number of items poped from global stack. In other words: global - local stack overlap.
/// Is the basic block a valid jump destination. /// Is the basic block a valid jump destination.
/// JUMPDEST is the first instruction of the basic block. /// JUMPDEST is the first instruction of the basic block.
bool const m_isJumpDest = false; bool const m_isJumpDest = false;

82
evmjit/libevmjit/Compiler.cpp

@ -87,7 +87,7 @@ void Compiler::createBasicBlocks(code_iterator _codeBegin, code_iterator _codeEn
{ {
auto beginIdx = begin - _codeBegin; auto beginIdx = begin - _codeBegin;
m_basicBlocks.emplace(std::piecewise_construct, std::forward_as_tuple(beginIdx), m_basicBlocks.emplace(std::piecewise_construct, std::forward_as_tuple(beginIdx),
std::forward_as_tuple(beginIdx, begin, next, m_mainFunc, m_builder, nextJumpDest)); std::forward_as_tuple(beginIdx, begin, next, m_mainFunc, nextJumpDest));
nextJumpDest = false; nextJumpDest = false;
begin = next; begin = next;
} }
@ -98,12 +98,12 @@ llvm::BasicBlock* Compiler::getJumpTableBlock()
{ {
if (!m_jumpTableBlock) if (!m_jumpTableBlock)
{ {
m_jumpTableBlock.reset(new BasicBlock("JumpTable", m_mainFunc, m_builder, true)); m_jumpTableBlock.reset(new BasicBlock("JumpTable", m_mainFunc, true));
InsertPointGuard g{m_builder}; InsertPointGuard g{m_builder};
m_builder.SetInsertPoint(m_jumpTableBlock->llvm()); m_builder.SetInsertPoint(m_jumpTableBlock->llvm());
auto dest = m_builder.CreatePHI(Type::Word, 8, "target"); auto dest = m_builder.CreatePHI(Type::Word, 8, "target");
auto switchInstr = m_builder.CreateSwitch(dest, m_abortBB); auto switchInstr = m_builder.CreateSwitch(dest, m_abortBB);
for (auto&& p : m_basicBlocks) for (auto&& p : m_basicBlocks) // FIXME: It mast be done at the end
{ {
if (p.second.isJumpDest()) if (p.second.isJumpDest())
switchInstr->addCase(Constant::get(p.first), p.second.llvm()); switchInstr->addCase(Constant::get(p.first), p.second.llvm());
@ -196,15 +196,6 @@ std::unique_ptr<llvm::Module> Compiler::compile(code_iterator _begin, code_itera
} }
} }
dumpCFGifRequired("blocks-init.dot");
for (auto& entry : m_basicBlocks)
entry.second.synchronizeLocalStack(stack);
if (m_jumpTableBlock)
m_jumpTableBlock->synchronizeLocalStack(stack);
dumpCFGifRequired("blocks-sync.dot");
return module; return module;
} }
@ -216,7 +207,7 @@ void Compiler::compileBasicBlock(BasicBlock& _basicBlock, RuntimeManager& _runti
_nextBasicBlock = m_stopBB; _nextBasicBlock = m_stopBB;
m_builder.SetInsertPoint(_basicBlock.llvm()); m_builder.SetInsertPoint(_basicBlock.llvm());
LocalStack stack{_basicBlock, _globalStack}; LocalStack stack{_globalStack};
for (auto it = _basicBlock.begin(); it != _basicBlock.end(); ++it) for (auto it = _basicBlock.begin(); it != _basicBlock.end(); ++it)
{ {
@ -842,12 +833,13 @@ void Compiler::compileBasicBlock(BasicBlock& _basicBlock, RuntimeManager& _runti
if (!_basicBlock.llvm()->getTerminator()) if (!_basicBlock.llvm()->getTerminator())
m_builder.CreateBr(_nextBasicBlock); m_builder.CreateBr(_nextBasicBlock);
stack.finalize(m_builder, *_basicBlock.llvm()); // TODO: Use references
m_builder.SetInsertPoint(_basicBlock.llvm()->getFirstNonPHI()); m_builder.SetInsertPoint(_basicBlock.llvm()->getFirstNonPHI());
_runtimeManager.checkStackLimit(stack.getMaxSize(), _basicBlock.getDiff()); _runtimeManager.checkStackLimit(stack.getMaxSize(), stack.getDiff());
} }
void Compiler::removeDeadBlocks() void Compiler::removeDeadBlocks()
{ {
// Remove dead basic blocks // Remove dead basic blocks
@ -877,66 +869,6 @@ void Compiler::removeDeadBlocks()
} }
} }
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();
}
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());
// 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();
}
} }
} }

9
evmjit/libevmjit/Compiler.h

@ -38,15 +38,6 @@ private:
void removeDeadBlocks(); void removeDeadBlocks();
/// 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();
/// Compiler options /// Compiler options
Options const& m_options; Options const& m_options;

Loading…
Cancel
Save