You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
 
 

352 lines
8.9 KiB

#include "BasicBlock.h"
#include <iostream>
#include "preprocessor/llvm_includes_start.h"
#include <llvm/IR/CFG.h>
#include <llvm/IR/Function.h>
#include <llvm/IR/Instructions.h>
#include <llvm/IR/IRBuilder.h>
#include <llvm/Support/raw_os_ostream.h>
#include "preprocessor/llvm_includes_end.h"
#include "Type.h"
#include "Utils.h"
namespace dev
{
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) :
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_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_builder(_builder),
m_isJumpDest(isJumpDest)
{}
LocalStack::LocalStack(BasicBlock& _owner, Stack& _globalStack) :
m_bblock(_owner),
m_global(_globalStack)
{}
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_currentStack.size());
}
llvm::Value* LocalStack::pop()
{
auto item = get(0);
assert(!m_bblock.m_currentStack.empty() || !m_bblock.m_initialStack.empty());
if (m_bblock.m_currentStack.size() > 0)
m_bblock.m_currentStack.pop_back();
else
++m_bblock.m_globalPops;
m_bblock.m_tosOffset -= 1;
return item;
}
/**
* Pushes a copy of _index-th element (tos is 0-th elem).
*/
void 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 LocalStack::swap(size_t _index)
{
assert(_index > 0);
auto val = get(_index);
auto tos = get(0);
set(_index, tos);
set(0, val);
}
llvm::Value* LocalStack::get(size_t _index)
{
auto& currentStack = m_bblock.m_currentStack;
if (_index < currentStack.size())
return *(currentStack.rbegin() + _index); // count from back
auto& initialStack = m_bblock.m_initialStack;
auto idx = _index - currentStack.size() + m_bblock.m_globalPops;
if (idx >= initialStack.size())
initialStack.resize(idx + 1);
auto& item = initialStack[idx];
if (!item)
item = m_global.get(idx);
return item;
}
void LocalStack::set(size_t _index, llvm::Value* _word)
{
auto& currentStack = m_bblock.m_currentStack;
if (_index < currentStack.size())
{
*(currentStack.rbegin() + _index) = _word;
return;
}
auto& initialStack = m_bblock.m_initialStack;
auto idx = _index - currentStack.size() + m_bblock.m_globalPops;
assert(idx < initialStack.size());
initialStack[idx] = _word;
}
void BasicBlock::synchronizeLocalStack(Stack& _evmStack)
{
auto blockTerminator = m_llvmBB->getTerminator();
assert(blockTerminator);
if (blockTerminator->getOpcode() != llvm::Instruction::Ret)
{
// Not needed in case of ret instruction. Ret invalidates the stack.
m_builder.SetInsertPoint(blockTerminator);
// Update items fetched from global stack ignoring the poped ones
assert(m_globalPops <= m_initialStack.size()); // pop() always does get()
for (auto i = m_globalPops; i < m_initialStack.size(); ++i)
{
if (m_initialStack[i])
_evmStack.set(i, m_initialStack[i]);
}
// Add new items
for (auto& item: m_currentStack)
{
if (m_globalPops) // Override poped global items
_evmStack.set(--m_globalPops, item); // using pops counter as the index
else
_evmStack.push(item);
}
// Pop not overriden items
if (m_globalPops)
_evmStack.pop(m_globalPops);
}
m_tosOffset = 0;
}
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);
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)
{
for (auto& pair : cfg)
{
DLOG(bb) << pair.second.bblock.llvm()->getName().str()
<< ": in " << pair.second.inputItems
<< ", out " << pair.second.outputItems
<< "\n";
}
valuesChanged = false;
for (auto& pair : cfg)
{
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;
}
}
}
}
// 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)
{
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);
}
}
// 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");
}
if (! _dotOutput)
out << " ...\n----------------------------------------\n";
}
}
}
}