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/CMakeLists.txt b/libevmjit/CMakeLists.txt new file mode 100644 index 000000000..f461c16cf --- /dev/null +++ b/libevmjit/CMakeLists.txt @@ -0,0 +1,60 @@ +cmake_policy(SET CMP0015 NEW) + +set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -DSTATICLIB") + +aux_source_directory(. SRC_LIST) + +set(EXECUTABLE evmjit) + +file(GLOB HEADERS "*.h") +if (EVMJIT_STATIC) + add_library(${EXECUTABLE} STATIC ${SRC_LIST} ${HEADERS}) +else() + add_library(${EXECUTABLE} SHARED ${SRC_LIST} ${HEADERS}) +endif() + +include_directories(..) + +target_link_libraries(${EXECUTABLE} devcore) +target_link_libraries(${EXECUTABLE} ethcore) +target_link_libraries(${EXECUTABLE} evm) +target_link_libraries(${EXECUTABLE} evmface) + +if ("${TARGET_PLATFORM}" STREQUAL "w64") + set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -static-libgcc -static-libstdc++") + target_link_libraries(${EXECUTABLE} gcc) + target_link_libraries(${EXECUTABLE} gdi32) + target_link_libraries(${EXECUTABLE} ws2_32) + target_link_libraries(${EXECUTABLE} mswsock) + target_link_libraries(${EXECUTABLE} shlwapi) + target_link_libraries(${EXECUTABLE} iphlpapi) + target_link_libraries(${EXECUTABLE} boost_thread_win32-mt-s) + set(CMAKE_SHARED_LIBRARY_LINK_CXX_FLAGS) +elseif (UNIX) + set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -std=c++14") +else () + find_package(Threads REQUIRED) + target_link_libraries(${EXECUTABLE} ${CMAKE_THREAD_LIBS_INIT}) +endif () + +# LLVM specific commands + +find_package(LLVM REQUIRED CONFIG) + +message(STATUS "Found LLVM ${LLVM_PACKAGE_VERSION}") +message(STATUS "Using LLVMConfig.cmake in: ${LLVM_DIR}") + +include_directories(${LLVM_INCLUDE_DIRS}) +add_definitions(${LLVM_DEFINITIONS}) + +llvm_map_components_to_libnames(llvm_libs core support mcjit x86asmparser x86codegen) +target_link_libraries(evmjit ${llvm_libs}) + +# end of LLVM specific commands + + + +install( TARGETS ${EXECUTABLE} ARCHIVE DESTINATION lib LIBRARY DESTINATION lib ) +install( FILES ${HEADERS} DESTINATION include/${EXECUTABLE} ) + +cmake_policy(SET CMP0015 NEW) diff --git a/libevmjit/Compiler.cpp b/libevmjit/Compiler.cpp index fe720b811..a88797413 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" @@ -174,6 +175,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); @@ -216,7 +218,7 @@ std::unique_ptr Compiler::compile(bytesConstRef bytecode) m_builder.CreateBr(m_badJumpBlock->llvm()); } - linkBasicBlocks(); + linkBasicBlocks(stack); return module; } @@ -871,77 +873,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) { - size_t valueIdx = 0; - auto firstNonPhi = _llbb->getFirstNonPHI(); - for (auto instIt = _llbb->begin(); &*instIt != firstNonPhi; ++instIt, ++valueIdx) + auto bbPtr = pair.first; + auto& bbInfo = pair.second; + + for (auto predIt = llvm::pred_begin(bbPtr); predIt != llvm::pred_end(bbPtr); ++predIt) { - auto phi = llvm::cast(instIt); - for (auto predIt = llvm::pred_begin(_llbb); predIt != llvm::pred_end(_llbb); ++predIt) - { - // TODO: In case entry block is reached - report error - auto predBB = findBasicBlock(*predIt); - if (!predBB) - BOOST_THROW_EXCEPTION(Exception() << errinfo_comment("Unsupported dynamic stack")); - auto value = predBB->getStack().get(valueIdx); - phi->addIncoming(value, predBB->llvm()); - } + if (*predIt != &entryBlock) + bbInfo.predecessors.push_back(&cfg.find(*predIt)->second); } - }; + } - // Remove dead basic blocks - auto sthErased = false; - do + // Iteratively compute inputs and outputs of each block, until reaching fixpoint. + bool valuesChanged = true; + while (valuesChanged) { - sthErased = false; - for (auto it = basicBlocks.begin(); it != basicBlocks.end();) + valuesChanged = false; + for (auto& pair : cfg) { - auto llvmBB = it->second.llvm(); - if (llvm::pred_begin(llvmBB) == llvm::pred_end(llvmBB)) + auto& bbInfo = pair.second; + + for (auto predInfo : bbInfo.predecessors) { - llvmBB->eraseFromParent(); - basicBlocks.erase(it++); - sthErased = true; + if (predInfo->outputItems < bbInfo.inputItems) + { + bbInfo.inputItems = predInfo->outputItems; + valuesChanged = true; + } + else if (predInfo->outputItems > bbInfo.inputItems) + { + predInfo->outputItems = bbInfo.inputItems; + valuesChanged = true; + } } - else - ++it; } } - while (sthErased); - // 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; +}; + + +} +} +} + +