From 71ccd3f353bdc12a3b528c6fd6e917f76f9290e4 Mon Sep 17 00:00:00 2001 From: artur-zawlocki Date: Tue, 21 Oct 2014 14:29:27 +0100 Subject: [PATCH] Propagation of values between basic blocks (and the stack): initial implementation (probably buggy, but simple cases work). [#80895676] --- libevmjit/BasicBlock.cpp | 11 ++-- libevmjit/BasicBlock.h | 22 ++++--- libevmjit/Compiler.cpp | 134 +++++++++++++++++++++++++++------------ libevmjit/Compiler.h | 2 +- libevmjit/Stack.cpp | 69 ++++++++++++++++++++ libevmjit/Stack.h | 35 ++++++++++ 6 files changed, 218 insertions(+), 55 deletions(-) create mode 100644 libevmjit/Stack.cpp create mode 100644 libevmjit/Stack.h diff --git a/libevmjit/BasicBlock.cpp b/libevmjit/BasicBlock.cpp index 736e0a572..927f1180b 100644 --- a/libevmjit/BasicBlock.cpp +++ b/libevmjit/BasicBlock.cpp @@ -30,20 +30,20 @@ BasicBlock::BasicBlock(std::string _name, llvm::Function* _mainFunc) : {} -void BasicBlock::Stack::push(llvm::Value* _value) +void BasicBlock::LocalStack::push(llvm::Value* _value) { assert(_value->getType() == Type::i256); m_backend.push_back(_value); } -llvm::Value* BasicBlock::Stack::pop() +llvm::Value* BasicBlock::LocalStack::pop() { auto top = get(0); m_backend.pop_back(); return top; } -llvm::Value* BasicBlock::Stack::get(size_t _index) +llvm::Value* BasicBlock::LocalStack::get(size_t _index) { if (_index >= m_backend.size()) { @@ -55,18 +55,19 @@ llvm::Value* BasicBlock::Stack::get(size_t _index) m_backend[i] = m_llvmBB->empty() ? llvm::PHINode::Create(Type::i256, 0, {}, m_llvmBB) : llvm::PHINode::Create(Type::i256, 0, {}, m_llvmBB->getFirstNonPHI()); + m_numRequiredStackItems += 1; } } return *(m_backend.rbegin() + _index); } -void BasicBlock::Stack::dup(size_t _index) +void BasicBlock::LocalStack::dup(size_t _index) { m_backend.push_back(get(_index)); } -void BasicBlock::Stack::swap(size_t _index) +void BasicBlock::LocalStack::swap(size_t _index) { assert(_index != 0); get(_index); // Create PHI nodes diff --git a/libevmjit/BasicBlock.h b/libevmjit/BasicBlock.h index 71d8aa355..51a48c6e0 100644 --- a/libevmjit/BasicBlock.h +++ b/libevmjit/BasicBlock.h @@ -15,7 +15,7 @@ using ProgramCounter = uint64_t; // TODO: Rename class BasicBlock { public: - class Stack + class LocalStack { public: /// Pushes value on stack @@ -37,17 +37,23 @@ public: /// Size of the stack size_t size() const { return m_backend.size(); } + size_t initialSize() const { return m_numRequiredStackItems; } + private: - Stack(llvm::BasicBlock* _llvmBB) : m_llvmBB(_llvmBB) {} - Stack(const Stack&) = delete; - void operator=(const Stack&) = delete; + // LocalStack(llvm::BasicBlock* _llvmBB) : m_llvmBB(_llvmBB) {} + LocalStack(llvm::BasicBlock* _llvmBB) : m_llvmBB(_llvmBB), m_numRequiredStackItems(0) {} + LocalStack(LocalStack const&) = delete; + void operator=(LocalStack const&) = delete; friend BasicBlock; private: std::vector m_backend; - /// LLVM Basic Block where phi nodes are inserted - llvm::BasicBlock* const m_llvmBB; + /** Basic block into which phi nodes are inserted */ + llvm::BasicBlock* m_llvmBB; + + /** Number of items required on the EVM stack at the beginning of the block */ + size_t m_numRequiredStackItems; }; /// Basic block name prefix. The rest is beging instruction index. @@ -62,7 +68,7 @@ public: operator llvm::BasicBlock*() { return m_llvmBB; } llvm::BasicBlock* llvm() { return m_llvmBB; } - Stack& getStack() { return m_stack; } + LocalStack& getStack() { return m_stack; } ProgramCounter begin() { return m_beginInstIdx; } ProgramCounter end() { return m_endInstIdx; } @@ -74,7 +80,7 @@ private: /// Basic black state vector (stack) - current/end values and their positions on stack /// @internal Must be AFTER m_llvmBB - Stack m_stack; + LocalStack m_stack; }; } diff --git a/libevmjit/Compiler.cpp b/libevmjit/Compiler.cpp index 34970c602..35e88f045 100644 --- a/libevmjit/Compiler.cpp +++ b/libevmjit/Compiler.cpp @@ -12,6 +12,7 @@ #include "Type.h" #include "Memory.h" +#include "Stack.h" #include "Ext.h" #include "GasMeter.h" #include "Utils.h" @@ -172,6 +173,7 @@ std::unique_ptr Compiler::compile(bytesConstRef bytecode) GasMeter gasMeter(m_builder); Memory memory(m_builder, gasMeter); Ext ext(m_builder); + Stack stack(m_builder); m_builder.CreateBr(basicBlocks.begin()->second); @@ -214,7 +216,7 @@ std::unique_ptr Compiler::compile(bytesConstRef bytecode) m_builder.CreateBr(m_badJumpBlock->llvm()); } - linkBasicBlocks(); + linkBasicBlocks(stack); return module; } @@ -868,61 +870,111 @@ void Compiler::compileBasicBlock(BasicBlock& basicBlock, bytesConstRef bytecode, } -void Compiler::linkBasicBlocks() +void Compiler::linkBasicBlocks(Stack& stack) { - /// Helper function that finds basic block given LLVM basic block pointer - auto findBasicBlock = [this](llvm::BasicBlock* _llbb) -> BasicBlock* + struct BBInfo { - // TODO: Fix for finding jumpTableBlock - if (_llbb == this->m_jumpTableBlock->llvm()) - return this->m_jumpTableBlock.get(); - - for (auto&& bb : this->basicBlocks) - if (_llbb == bb.second.llvm()) - return &bb.second; - return nullptr; - - // Name is used to get basic block index (index of first instruction) - // TODO: If basicBlocs are still a map - multikey map can be used - //auto&& idxStr = _llbb->getName().substr(sizeof(BasicBlock::NamePrefix) - 2); - //auto idx = std::stoul(idxStr); - //return basicBlocks.find(idx)->second; + BasicBlock& bblock; + std::vector predecessors; + size_t inputItems; + size_t outputItems; + + BBInfo(BasicBlock& _bblock) + : bblock(_bblock), + predecessors(), + inputItems(_bblock.getStack().initialSize()), + outputItems(_bblock.getStack().size()) + {} }; - auto completePhiNodes = [findBasicBlock](llvm::BasicBlock* _llbb) -> void + std::map cfg; + + // Create nodes in cfg + for (auto& pair : this->basicBlocks) + { + auto& bb = pair.second; + cfg.emplace(std::piecewise_construct, + std::forward_as_tuple(bb.llvm()), + std::forward_as_tuple(bb)); + } + + 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) + bbInfo.predecessors.push_back(&cfg.find(*predIt)->second); + } + } + + // Iteratively compute inputs and outputs of each block, until reaching fixpoint. + bool valuesChanged = true; + while (valuesChanged) { - size_t valueIdx = 0; - auto firstNonPhi = _llbb->getFirstNonPHI(); - for (auto instIt = _llbb->begin(); &*instIt != firstNonPhi; ++instIt, ++valueIdx) + valuesChanged = false; + for (auto& pair : cfg) { - auto phi = llvm::cast(instIt); - for (auto predIt = llvm::pred_begin(_llbb); predIt != llvm::pred_end(_llbb); ++predIt) + auto& bbInfo = pair.second; + + for (auto predInfo : bbInfo.predecessors) { - // TODO: In case entry block is reached - report error - auto predBB = findBasicBlock(*predIt); - if (!predBB) + if (predInfo->outputItems < bbInfo.inputItems) + { + bbInfo.inputItems = predInfo->outputItems; + valuesChanged = true; + } + else if (predInfo->outputItems > bbInfo.inputItems) { - std::cerr << "Stack too small in " << _llbb->getName().str() << std::endl; - std::exit(1); + predInfo->outputItems = bbInfo.inputItems; + valuesChanged = true; } - auto value = predBB->getStack().get(valueIdx); - phi->addIncoming(value, predBB->llvm()); } } - }; + } - - // TODO: It is crappy visiting of basic blocks. - llvm::SmallPtrSet visitSet; - for (auto&& bb : basicBlocks) // TODO: External loop is to visit unreable blocks that can also have phi nodes + // Propagate values between blocks. + for (auto& pair : cfg) { - for (auto it = llvm::po_ext_begin(bb.second.llvm(), visitSet), - end = llvm::po_ext_end(bb.second.llvm(), visitSet); it != end; ++it) + 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) { - // TODO: Use logger - //std::cerr << it->getName().str() << std::endl; - completePhiNodes(*it); + auto phi = llvm::cast(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(*instrIter); ) + { + auto phi = llvm::cast(instrIter); + auto value = stack.popWord(); + phi->replaceAllUsesWith(value); + ++ instrIter; + phi->eraseFromParent(); } + + // 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)); } // Remove jump table block if not predecessors diff --git a/libevmjit/Compiler.h b/libevmjit/Compiler.h index 8972322ec..ac6277e7b 100644 --- a/libevmjit/Compiler.h +++ b/libevmjit/Compiler.h @@ -33,7 +33,7 @@ private: void compileBasicBlock(BasicBlock& basicBlock, bytesConstRef bytecode, class Memory& memory, class Ext& ext, class GasMeter& gasMeter, llvm::BasicBlock* nextBasicBlock); - void linkBasicBlocks(); + void linkBasicBlocks(class Stack& stack); llvm::IRBuilder<> m_builder; diff --git a/libevmjit/Stack.cpp b/libevmjit/Stack.cpp new file mode 100644 index 000000000..772e6255b --- /dev/null +++ b/libevmjit/Stack.cpp @@ -0,0 +1,69 @@ +#include "Stack.h" +#include "Runtime.h" + +#include +#include + +namespace dev +{ +namespace eth +{ +namespace jit +{ + +Stack::Stack(llvm::IRBuilder<>& _builder) + : CompilerHelper(_builder) +{ + auto i256Ty = m_builder.getIntNTy(256); + auto i256PtrTy = i256Ty->getPointerTo(); + + m_arg = m_builder.CreateAlloca(i256Ty, nullptr, "stack.retVal"); + + using namespace llvm; + using Linkage = GlobalValue::LinkageTypes; + + auto module = getModule(); + m_push = Function::Create(FunctionType::get(m_builder.getVoidTy(), i256PtrTy, false), Linkage::ExternalLinkage, "stack_push", module); + m_pop = Function::Create(FunctionType::get(m_builder.getVoidTy(), i256PtrTy, false), Linkage::ExternalLinkage, "stack_pop", module); +} + +Stack::~Stack() +{} + +llvm::Instruction* Stack::popWord() +{ + m_builder.CreateCall(m_pop, m_arg); + return m_builder.CreateLoad(m_arg); +} + +void Stack::pushWord(llvm::Value* _word) +{ + m_builder.CreateStore(_word, m_arg); + m_builder.CreateCall(m_push, m_arg); +} + +} +} +} + +extern "C" +{ + +using namespace dev::eth::jit; + +EXPORT void stack_pop(i256* _ret) +{ + auto& stack = Runtime::getStack(); + assert(stack.size() > 0); + *_ret = stack.back(); + stack.pop_back(); +} + +EXPORT void stack_push(i256* _word) +{ + auto& stack = Runtime::getStack(); + stack.push_back(*_word); +} + +} // extern "C" + diff --git a/libevmjit/Stack.h b/libevmjit/Stack.h new file mode 100644 index 000000000..5e1ac0f39 --- /dev/null +++ b/libevmjit/Stack.h @@ -0,0 +1,35 @@ +#pragma once + +#include "CompilerHelper.h" + +#include + +namespace dev +{ +namespace eth +{ +namespace jit +{ + +class Stack : public CompilerHelper +{ +public: + Stack(llvm::IRBuilder<>& builder); + virtual ~Stack(); + + void pushWord(llvm::Value* _word); + llvm::Instruction* popWord(); + +private: + llvm::Function* m_push; + llvm::Function* m_pop; + + llvm::Value* m_arg; +}; + + +} +} +} + +