Browse Source

Reimplementation of local stack handling during basic block compilation.

This change removed the intermediate stage when PHI nodes are inserted in place of items fetched from global stack. PHi stage requires information about other basic block therefore must be postponed to the point when all basic blocks has been compiled. In the same time this optimization has not been very effective.
cl-refactor
Paweł Bylica 10 years ago
parent
commit
b604dfefe6
  1. 119
      evmjit/libevmjit/BasicBlock.cpp
  2. 4
      evmjit/libevmjit/BasicBlock.h
  3. 3
      evmjit/libevmjit/Compiler.h

119
evmjit/libevmjit/BasicBlock.cpp

@ -53,13 +53,16 @@ void LocalStack::push(llvm::Value* _value)
llvm::Value* LocalStack::pop()
{
auto result = get(0);
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 result;
return item;
}
/**
@ -84,109 +87,71 @@ void LocalStack::swap(size_t _index)
set(0, val);
}
std::vector<llvm::Value*>::iterator 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;
return *(currentStack.rbegin() + _index); // count from back
// Need to map more elements from the EVM stack
auto nNewItems = 1 + _index - currentStack.size();
currentStack.insert(currentStack.begin(), nNewItems, nullptr);
return currentStack.end() - _index - 1;
}
llvm::Value* LocalStack::get(size_t _index)
{
auto& initialStack = m_bblock.m_initialStack;
auto itemIter = getItemIterator(_index);
auto idx = _index - currentStack.size() + m_bblock.m_globalPops;
if (idx >= initialStack.size())
initialStack.resize(idx + 1);
auto& item = initialStack[idx];
if (*itemIter == nullptr)
{
// 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);
}
if (!item)
item = m_global.get(idx);
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];
}
return *itemIter;
return item;
}
void LocalStack::set(size_t _index, llvm::Value* _word)
{
auto itemIter = getItemIterator(_index);
*itemIter = _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 != nullptr);
assert(blockTerminator);
if (blockTerminator->getOpcode() != llvm::Instruction::Ret)
{
// Not needed in case of ret instruction. Ret also invalidates the stack.
// Not needed in case of ret instruction. Ret 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)
// 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)
{
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_initialStack[i])
_evmStack.set(i, m_initialStack[i]);
}
// Pop values
if (m_tosOffset < 0)
_evmStack.pop(static_cast<size_t>(-m_tosOffset));
// Push new values
for (; currIter < endIter; ++currIter)
// Add new items
for (auto& item: m_currentStack)
{
assert(*currIter != nullptr);
_evmStack.push(*currIter);
if (m_globalPops) // Override poped global items
_evmStack.set(--m_globalPops, item); // using pops counter as the index
else
_evmStack.push(item);
}
}
// 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;
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();
// Pop not overriden items
if (m_globalPops)
_evmStack.pop(m_globalPops);
}
// Reset the stack
m_initialStack.erase(m_initialStack.begin(), m_initialStack.end());
m_currentStack.erase(m_currentStack.begin(), m_currentStack.end());
m_tosOffset = 0;
}
@ -211,8 +176,6 @@ void BasicBlock::linkLocalStacks(std::vector<BasicBlock*> basicBlocks, llvm::IRB
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;

4
evmjit/libevmjit/BasicBlock.h

@ -45,8 +45,6 @@ private:
/// 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:
BasicBlock& m_bblock;
Stack& m_global;
@ -114,6 +112,8 @@ private:
/// May be negative.
int m_tosOffset = 0;
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.
/// JUMPDEST is the first instruction of the basic block.
bool const m_isJumpDest = false;

3
evmjit/libevmjit/Compiler.h

@ -16,7 +16,8 @@ public:
struct Options
{
/// Optimize stack operations between basic blocks
bool optimizeStack = true;
/// TODO: Remove. It must be implemented as a separated pass.
bool optimizeStack = false;
/// Rewrite switch instructions to sequences of branches
bool rewriteSwitchToBranches = true;

Loading…
Cancel
Save