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() 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) if (m_bblock.m_currentStack.size() > 0)
m_bblock.m_currentStack.pop_back(); m_bblock.m_currentStack.pop_back();
else
++m_bblock.m_globalPops;
m_bblock.m_tosOffset -= 1; m_bblock.m_tosOffset -= 1;
return result; return item;
} }
/** /**
@ -84,109 +87,71 @@ void LocalStack::swap(size_t _index)
set(0, val); 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; auto& currentStack = m_bblock.m_currentStack;
if (_index < currentStack.size()) 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& 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) if (!item)
{ item = m_global.get(idx);
// 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); return item;
// 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;
} }
void LocalStack::set(size_t _index, llvm::Value* _word) void LocalStack::set(size_t _index, llvm::Value* _word)
{ {
auto itemIter = getItemIterator(_index); auto& currentStack = m_bblock.m_currentStack;
*itemIter = _word; 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) void BasicBlock::synchronizeLocalStack(Stack& _evmStack)
{ {
auto blockTerminator = m_llvmBB->getTerminator(); auto blockTerminator = m_llvmBB->getTerminator();
assert(blockTerminator != nullptr); assert(blockTerminator);
if (blockTerminator->getOpcode() != llvm::Instruction::Ret) 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); m_builder.SetInsertPoint(blockTerminator);
auto currIter = m_currentStack.begin(); // Update items fetched from global stack ignoring the poped ones
auto endIter = m_currentStack.end(); assert(m_globalPops <= m_initialStack.size()); // pop() always does get()
for (auto i = m_globalPops; i < m_initialStack.size(); ++i)
// 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 (m_initialStack[i])
if (*currIter != m_initialStack[idx]) // value needs update _evmStack.set(i, m_initialStack[i]);
_evmStack.set(static_cast<size_t>(idx), *currIter);
} }
// Pop values // Add new items
if (m_tosOffset < 0) for (auto& item: m_currentStack)
_evmStack.pop(static_cast<size_t>(-m_tosOffset));
// Push new values
for (; currIter < endIter; ++currIter)
{ {
assert(*currIter != nullptr); if (m_globalPops) // Override poped global items
_evmStack.push(*currIter); _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 // Pop not overriden items
for (size_t idx = 0; idx < m_initialStack.size(); ++idx) if (m_globalPops)
{ _evmStack.pop(m_globalPops);
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();
} }
// Reset the stack
m_initialStack.erase(m_initialStack.begin(), m_initialStack.end());
m_currentStack.erase(m_currentStack.begin(), m_currentStack.end());
m_tosOffset = 0; m_tosOffset = 0;
} }
@ -211,8 +176,6 @@ void BasicBlock::linkLocalStacks(std::vector<BasicBlock*> basicBlocks, llvm::IRB
it != initialStack.end() && *it != nullptr; it != initialStack.end() && *it != nullptr;
++it, ++inputItems); ++it, ++inputItems);
//if (bblock.localStack().m_tosOffset > 0)
// outputItems = bblock.localStack().m_tosOffset;
auto& exitStack = bblock.m_currentStack; auto& exitStack = bblock.m_currentStack;
for (auto it = exitStack.rbegin(); for (auto it = exitStack.rbegin();
it != exitStack.rend() && *it != nullptr; it != exitStack.rend() && *it != nullptr;

4
evmjit/libevmjit/BasicBlock.h

@ -45,8 +45,6 @@ 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);
std::vector<llvm::Value*>::iterator getItemIterator(size_t _index);
private: private:
BasicBlock& m_bblock; BasicBlock& m_bblock;
Stack& m_global; Stack& m_global;
@ -114,6 +112,8 @@ private:
/// May be negative. /// May be negative.
int m_tosOffset = 0; 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. /// 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;

3
evmjit/libevmjit/Compiler.h

@ -16,7 +16,8 @@ public:
struct Options struct Options
{ {
/// Optimize stack operations between basic blocks /// 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 /// Rewrite switch instructions to sequences of branches
bool rewriteSwitchToBranches = true; bool rewriteSwitchToBranches = true;

Loading…
Cancel
Save