Browse Source

Propagation of values between basic blocks' local stacks [#80895676]

cl-refactor
artur-zawlocki 10 years ago
parent
commit
547ca3870d
  1. 2
      evmcc/test/stack/swap.evm
  2. 24
      evmcc/test/stack/swap.lll
  3. 2
      evmcc/test/stack/swapswap.evm
  4. 28
      evmcc/test/stack/swapswap.lll
  5. 197
      libevmjit/BasicBlock.cpp
  6. 9
      libevmjit/BasicBlock.h
  7. 246
      libevmjit/Compiler.cpp
  8. 4
      libevmjit/Compiler.h

2
evmcc/test/stack/swap.evm

@ -1 +1 @@
600160026001600a590090600160115900016000546001601ff2
600560026001600c59505000906001601559505000036000546001601ff2

24
evmcc/test/stack/swap.lll

@ -1,20 +1,26 @@
(asm
1 ;; 0
5 ;; 0
2 ;; 2
1 ;; 4
10 ;; 6
12 ;; 6
JUMPI ;; 8
STOP ;; 9
POP ;; 9
POP ;; 10
STOP ;; 11
;; stack = 2,1
SWAP1 ;; 10
1 ;; 11
17 ;; 13
JUMPI ;; 15
STOP ;; 16
SWAP1 ;; 12
1 ;; 13
21 ;; 15
JUMPI ;; 17
POP ;; 18
POP ;; 19
STOP ;; 20
;; stack = 1,2
ADD ;; 17
SUB ;; 21
0
MSTORE
1

2
evmcc/test/stack/swapswap.evm

@ -1 +1 @@
600160026001600a59009090600160125900016000546001601ff2
600260056001600c5950500090906001601659505000036000546001601ff2

28
evmcc/test/stack/swapswap.lll

@ -1,21 +1,27 @@
(asm
1 ;; 0
2 ;; 2
2 ;; 0
5 ;; 2
1 ;; 4
10 ;; 6
12 ;; 6
JUMPI ;; 8
STOP ;; 9
POP ;; 9
POP ;; 10
STOP ;; 11
;; stack = 2,1
SWAP1 ;; 10
SWAP1 ;; 11
1 ;; 12
18 ;; 14
JUMPI ;; 16
STOP ;; 17
SWAP1 ;; 12
SWAP1 ;; 13
1 ;; 14
22 ;; 16
JUMPI ;; 18
POP ;; 19
POP ;; 20
STOP ;; 21
;; stack = 2,1
ADD ;; 18
SUB ;; 22
0
MSTORE
1

197
libevmjit/BasicBlock.cpp

@ -3,9 +3,11 @@
#include <boost/lexical_cast.hpp>
#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 "Type.h"
@ -22,17 +24,18 @@ BasicBlock::BasicBlock(ProgramCounter _beginInstIdx, ProgramCounter _endInstIdx,
m_beginInstIdx(_beginInstIdx),
m_endInstIdx(_endInstIdx),
m_llvmBB(llvm::BasicBlock::Create(_mainFunc->getContext(), {NamePrefix, std::to_string(_beginInstIdx)}, _mainFunc)),
m_stack(_builder)
m_stack(_builder, m_llvmBB)
{}
BasicBlock::BasicBlock(std::string _name, llvm::Function* _mainFunc, llvm::IRBuilder<>& _builder) :
m_beginInstIdx(0),
m_endInstIdx(0),
m_llvmBB(llvm::BasicBlock::Create(_mainFunc->getContext(), _name, _mainFunc)),
m_stack(_builder)
m_stack(_builder, m_llvmBB)
{}
BasicBlock::LocalStack::LocalStack(llvm::IRBuilder<>& _builder) :
BasicBlock::LocalStack::LocalStack(llvm::IRBuilder<>& _builder, llvm::BasicBlock* _llvmBB) :
m_llvmBB(_llvmBB),
m_builder(_builder),
m_initialStack(),
m_currentStack(),
@ -80,7 +83,7 @@ void BasicBlock::LocalStack::swap(size_t _index)
void BasicBlock::LocalStack::synchronize(Stack& _evmStack)
{
auto blockTerminator = m_builder.GetInsertBlock()->getTerminator();
auto blockTerminator = m_llvmBB->getTerminator();
assert(blockTerminator != nullptr);
m_builder.SetInsertPoint(blockTerminator);
@ -180,44 +183,190 @@ void BasicBlock::LocalStack::set(size_t _index, llvm::Value* _word)
}
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.localStack().m_initialStack;
for (auto it = initialStack.begin();
it != initialStack.end() && *it != nullptr;
++it, ++inputItems);
//if (bblock.localStack().m_tosOffset > 0)
// outputItems = bblock.localStack().m_tosOffset;
auto& exitStack = bblock.localStack().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())
info.predecessors.push_back(&predInfoEntry->second);
}
}
// Iteratively compute inputs and outputs of each block, until reaching fixpoint.
bool valuesChanged = true;
while (valuesChanged)
{
if (getenv("EVMCC_DEBUG_BLOCKS"))
{
for (auto& pair: cfg)
std::cerr << 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.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.localStack().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.localStack().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.localStack().m_initialStack;
initialStack.erase(initialStack.begin(), initialStack.begin() + info.inputItems);
// Initial stack shrinks, so the size difference grows:
bblock.localStack().m_tosOffset += 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.localStack().m_currentStack;
exitStack.erase(exitStack.end() - info.outputItems, exitStack.end());
bblock.localStack().m_tosOffset -= info.outputItems;
}
}
void BasicBlock::dump()
{
std::cerr << "Initial stack:\n";
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_stack.m_initialStack)
{
if (val == nullptr)
std::cerr << " ?\n";
out << " ?";
else if (llvm::isa<llvm::Instruction>(val))
val->dump();
out << *val;
else
{
std::cerr << " ";
val->dump();
}
out << " " << *val;
out << (_dotOutput ? "\\l" : "\n");
}
std::cerr << " ...\n";
std::cerr << "Instructions:\n";
out << (_dotOutput ? "| " : "Instructions:\n");
for (auto ins = m_llvmBB->begin(); ins != m_llvmBB->end(); ++ins)
ins->dump();
out << *ins << (_dotOutput ? "\\l" : "\n");
std::cerr << "Current stack (offset = "
<< m_stack.m_tosOffset << "):\n";
if (! _dotOutput)
out << "Current stack (offset = " << m_stack.m_tosOffset << "):\n";
else
out << "|";
for (auto val = m_stack.m_currentStack.rbegin(); val != m_stack.m_currentStack.rend(); ++val)
{
if (*val == nullptr)
std::cerr << " ?\n";
out << " ?";
else if (llvm::isa<llvm::Instruction>(*val))
(*val)->dump();
out << **val;
else
{
std::cerr << " ";
(*val)->dump();
}
out << " " << **val;
out << (_dotOutput ? "\\l" : "\n");
}
std::cerr << " ...\n----------------------------------------\n";
if (! _dotOutput)
out << " ...\n----------------------------------------\n";
}

9
libevmjit/BasicBlock.h

@ -40,7 +40,7 @@ public:
private:
LocalStack(llvm::IRBuilder<>& _builder);
LocalStack(llvm::IRBuilder<>& _builder, llvm::BasicBlock* _llvmBB);
LocalStack(LocalStack const&) = delete;
void operator=(LocalStack const&) = delete;
friend BasicBlock;
@ -55,6 +55,8 @@ public:
private:
llvm::BasicBlock* m_llvmBB;
llvm::IRBuilder<>& m_builder;
/**
@ -99,9 +101,14 @@ public:
LocalStack& localStack() { return m_stack; }
/// Optimization: propagates values between local stacks in basic blocks
/// to avoid excessive pushing/popping on the EVM stack.
static void linkLocalStacks(std::vector<BasicBlock*> _basicBlocks, llvm::IRBuilder<>& _builder);
/// 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:
ProgramCounter const m_beginInstIdx;

246
libevmjit/Compiler.cpp

@ -1,11 +1,13 @@
#include "Compiler.h"
#include <fstream>
#include <boost/dynamic_bitset.hpp>
#include <llvm/IR/Module.h>
#include <llvm/IR/CFG.h>
#include <llvm/ADT/PostOrderIterator.h>
#include <llvm/IR/CFG.h>
#include <llvm/IR/Module.h>
#include <llvm/IR/IntrinsicInst.h>
#include <libevmface/Instruction.h>
@ -186,9 +188,6 @@ std::unique_ptr<llvm::Module> Compiler::compile(bytesConstRef bytecode)
++iterCopy;
auto nextBasicBlock = (iterCopy != basicBlocks.end()) ? iterCopy->second.llvm() : nullptr;
compileBasicBlock(basicBlock, bytecode, memory, ext, gasMeter, nextBasicBlock);
if (getenv("EVMCC_DEBUG_BLOCKS"))
basicBlock.dump();
basicBlock.localStack().synchronize(stack);
}
// Code for special blocks:
@ -204,9 +203,7 @@ std::unique_ptr<llvm::Module> Compiler::compile(bytesConstRef bytecode)
m_builder.SetInsertPoint(m_jumpTableBlock->llvm());
if (m_indirectJumpTargets.size() > 0)
{
// auto& stack = m_jumpTableBlock->getStack();
auto dest = m_jumpTableBlock->localStack().pop(); //m_jumpTableBlock->localGet(0); // stack.pop();
auto dest = m_jumpTableBlock->localStack().pop();
auto switchInstr = m_builder.CreateSwitch(dest, m_badJumpBlock->llvm(),
m_indirectJumpTargets.size());
for (auto it = m_indirectJumpTargets.cbegin(); it != m_indirectJumpTargets.cend(); ++it)
@ -221,8 +218,51 @@ std::unique_ptr<llvm::Module> Compiler::compile(bytesConstRef bytecode)
m_builder.CreateBr(m_badJumpBlock->llvm());
}
m_jumpTableBlock->localStack().synchronize(stack);
linkBasicBlocks();
removeDeadBlocks();
if (getenv("EVMCC_DEBUG_BLOCKS"))
{
std::ofstream ofs("blocks-init.dot");
dumpBasicBlockGraph(ofs);
ofs.close();
std::cerr << "\n\nAfter dead block elimination \n\n";
dump();
}
if (getenv("EVMCC_OPTIMIZE_STACK"))
{
std::vector<BasicBlock*> blockList;
for (auto& entry : basicBlocks)
blockList.push_back(&entry.second);
if (m_jumpTableBlock != nullptr)
blockList.push_back(m_jumpTableBlock.get());
BasicBlock::linkLocalStacks(blockList, m_builder);
if (getenv("EVMCC_DEBUG_BLOCKS"))
{
std::ofstream ofs("blocks-opt.dot");
dumpBasicBlockGraph(ofs);
ofs.close();
std::cerr << "\n\nAfter stack optimization \n\n";
dump();
}
}
for (auto& entry : basicBlocks)
entry.second.localStack().synchronize(stack);
if (m_jumpTableBlock != nullptr)
m_jumpTableBlock->localStack().synchronize(stack);
if (getenv("EVMCC_DEBUG_BLOCKS"))
{
std::ofstream ofs("blocks-sync.dot");
dumpBasicBlockGraph(ofs);
ofs.close();
std::cerr << "\n\nAfter stack synchronization \n\n";
dump();
}
return module;
}
@ -875,7 +915,8 @@ void Compiler::compileBasicBlock(BasicBlock& basicBlock, bytesConstRef bytecode,
}
void Compiler::linkBasicBlocks() // Stack& stack)
void Compiler::removeDeadBlocks()
{
// Remove dead basic blocks
auto sthErased = false;
@ -903,187 +944,33 @@ void Compiler::linkBasicBlocks() // Stack& stack)
m_jumpTableBlock->llvm()->eraseFromParent();
m_jumpTableBlock.reset();
}
/*
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(_bblock.getStack().initialSize()),
outputItems(_bblock.getStack().size())
{}
};
std::map<llvm::BasicBlock*, BBInfo> cfg;
// Create nodes in cfg
for (auto& pair : this->basicBlocks)
{
auto& bb = pair.second;
cfg.emplace(bb.llvm(), bb);
}
// Insert jump table block into cfg
if (m_jumpTableBlock)
cfg.emplace(m_jumpTableBlock->llvm(), *m_jumpTableBlock);
auto& entryBlock = m_mainFunc->getEntryBlock();
// Create edges in cfg
for (auto& pair : cfg)
{
auto bbPtr = pair.first;
auto& bbInfo = pair.second;
for (auto predIt = llvm::pred_begin(bbPtr); predIt != llvm::pred_end(bbPtr); ++predIt)
{
if (*predIt != &entryBlock)
{
auto predInfoEntry = cfg.find(*predIt);
assert(predInfoEntry != cfg.end());
bbInfo.predecessors.push_back(&predInfoEntry->second);
}
}
}
// Iteratively compute inputs and outputs of each block, until reaching fixpoint.
bool valuesChanged = true;
while (valuesChanged)
{
valuesChanged = false;
for (auto& pair : cfg)
{
auto& bbInfo = pair.second;
if (bbInfo.predecessors.empty())
bbInfo.inputItems = 0; // no consequences for other blocks, so leave valuesChanged false
for (auto predInfo : bbInfo.predecessors)
{
if (predInfo->outputItems < bbInfo.inputItems)
{
bbInfo.inputItems = predInfo->outputItems;
valuesChanged = true;
}
else if (predInfo->outputItems > bbInfo.inputItems)
{
predInfo->outputItems = bbInfo.inputItems;
valuesChanged = true;
}
}
}
}
// std::map<llvm::Instruction*, llvm::Value*> phiReplacements;
// std::vector<llvm::Instruction*> phiNodesToRewrite;
// Propagate values between blocks.
for (auto& pair : cfg)
{
auto llbb = pair.first;
auto& bbInfo = pair.second;
auto& bblock = bbInfo.bblock;
// Complete phi nodes for the top bbInfo.inputItems placeholder values
auto instrIter = llbb->begin();
for (size_t valueIdx = 0; valueIdx < bbInfo.inputItems; ++instrIter, ++valueIdx)
{
auto phi = llvm::cast<llvm::PHINode>(instrIter);
for (auto predIt : bbInfo.predecessors)
{
assert(valueIdx < predIt->bblock.getStack().size());
auto value = predIt->bblock.getStack().get(valueIdx);
phi->addIncoming(value, predIt->bblock.llvm());
}
}
// Turn the remaining phi nodes into stack.pop's.
// m_builder.SetInsertPoint(llbb, llvm::BasicBlock::iterator(llbb->getFirstNonPHI()));
for (; llvm::isa<llvm::PHINode>(*instrIter); ++instrIter)
{
auto phi = llvm::cast<llvm::PHINode>(instrIter);
// auto value = stack.popWord();
// Don't delete the phi node yet. It may still be stored in a local stack of some block.
// phiReplacements[phi] = value;
bbInfo.phisToRewrite.push_back(phi);
}
// Emit stack push's at the end of the block, just before the terminator;
m_builder.SetInsertPoint(llbb, -- llbb->end());
auto localStackSize = bblock.getStack().size();
assert(localStackSize >= bbInfo.outputItems);
for (size_t i = 0; i < localStackSize - bbInfo.outputItems; ++i)
stack.pushWord(bblock.getStack().get(localStackSize - 1 - i));
}
for (auto& entry : cfg)
{
// Where was the last stack.pop() inserted
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)
{
out << "digraph BB {\n"
<< " node [shape=record];\n"
<< " node [shape=record, fontname=Courier, fontsize=10];\n"
<< " entry [share=record, label=\"entry block\"];\n";
/*
std::vector<BasicBlock*> blocks;
for (auto& pair : this->basicBlocks)
for (auto& pair : basicBlocks)
blocks.push_back(&pair.second);
if (m_jumpTableBlock)
blocks.push_back(m_jumpTableBlock.get());
if (m_badJumpBlock)
blocks.push_back(m_badJumpBlock.get());
std::map<BasicBlock*,int> phiNodesPerBlock;
// std::map<BasicBlock*,int> phiNodesPerBlock;
// Output nodes
for (auto bb : blocks)
{
std::string blockName = bb->llvm()->getName();
int numOfPhiNodes = 0;
auto firstNonPhiPtr = bb->llvm()->getFirstNonPHI();
for (auto instrIter = bb->llvm()->begin(); &*instrIter != firstNonPhiPtr; ++instrIter, ++numOfPhiNodes);
phiNodesPerBlock[bb] = numOfPhiNodes;
auto initStackSize = bb->getStack().initialSize();
auto endStackSize = bb->getStack().size();
std::ostringstream oss;
bb->dump(oss, true);
out << " \"" << blockName << "\" [shape=record, label=\""
<< initStackSize << "|" << blockName << "|" << endStackSize
<< "\"];\n";
out << " \"" << blockName << "\" [shape=record, label=\" { " << blockName << "|" << oss.str() << "} \"];\n";
}
// Output edges
@ -1096,14 +983,21 @@ void Compiler::dumpBasicBlockGraph(std::ostream& out)
{
out << " \"" << (*it)->getName().str() << "\" -> \"" << blockName << "\" ["
<< ((m_jumpTableBlock.get() && *it == m_jumpTableBlock.get()->llvm()) ? "style = dashed, " : "")
<< "label = \""
<< phiNodesPerBlock[bb]
<< "\"];\n";
//<< "label = \""
//<< phiNodesPerBlock[bb]
<< "];\n";
}
}
out << "}\n";
*/
}
void Compiler::dump()
{
for (auto& entry : basicBlocks)
entry.second.dump();
if (m_jumpTableBlock != nullptr)
m_jumpTableBlock->dump();
}
}

4
libevmjit/Compiler.h

@ -33,8 +33,10 @@ private:
void compileBasicBlock(BasicBlock& basicBlock, bytesConstRef bytecode, class Memory& memory, class Ext& ext, class GasMeter& gasMeter, llvm::BasicBlock* nextBasicBlock);
void linkBasicBlocks(); //class Stack& stack);
void removeDeadBlocks();
/// Dump all basic blocks to stderr. Useful in a debugging session.
void dump();
llvm::IRBuilder<> m_builder;

Loading…
Cancel
Save