diff --git a/.gitignore b/.gitignore index b6abc22a3..8d8974809 100644 --- a/.gitignore +++ b/.gitignore @@ -65,7 +65,6 @@ DerivedData project.pbxproj -evmjit doc/html *.autosave node_modules/ diff --git a/CMakeLists.txt b/CMakeLists.txt index f1246e152..9bab77585 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -124,9 +124,7 @@ include(EthExecutableHelper) createBuildInfo() if (EVMJIT) - # Workaround for Ubuntu broken LLVM package - link_directories(/usr/lib/llvm-3.5/lib) - + set(EVMJIT_CPP TRUE) # include CPP-JIT connector add_subdirectory(evmjit) endif() diff --git a/evmjit/.gitignore b/evmjit/.gitignore new file mode 100644 index 000000000..84c048a73 --- /dev/null +++ b/evmjit/.gitignore @@ -0,0 +1 @@ +/build/ diff --git a/evmjit/CMakeLists.txt b/evmjit/CMakeLists.txt index 18601b921..519346007 100644 --- a/evmjit/CMakeLists.txt +++ b/evmjit/CMakeLists.txt @@ -4,8 +4,18 @@ project(evmjit) set_property(GLOBAL PROPERTY USE_FOLDERS ON) +if(CMAKE_CXX_COMPILER_ID STREQUAL "MSVC") +else() + set(CMAKE_CXX_FLAGS "-std=c++11 -Wall -Wno-unknown-pragmas -Wextra -fPIC") +endif() + +if(CMAKE_SYSTEM_NAME STREQUAL "Linux") + # Do not allow unresovled symbols in shared library (default on linux) + set(CMAKE_SHARED_LINKER_FLAGS "-Wl,--no-undefined") +endif() + # LLVM -if(LLVM_DIR) # local LLVM build +if(LLVM_DIR OR APPLE) # local LLVM build find_package(LLVM REQUIRED CONFIG) message(STATUS "Found LLVM ${LLVM_PACKAGE_VERSION}") message(STATUS "Using LLVMConfig.cmake in: ${LLVM_DIR}") @@ -18,11 +28,19 @@ else() execute_process(COMMAND llvm-config-3.5 --includedir OUTPUT_VARIABLE LLVM_INCLUDE_DIRS) message(STATUS "LLVM include dirs: ${LLVM_INCLUDE_DIRS}") set(LLVM_LIBS "-lLLVMBitWriter -lLLVMX86CodeGen -lLLVMSelectionDAG -lLLVMAsmPrinter -lLLVMCodeGen -lLLVMScalarOpts -lLLVMInstCombine -lLLVMTransformUtils -lLLVMipa -lLLVMAnalysis -lLLVMX86AsmParser -lLLVMX86Desc -lLLVMX86Info -lLLVMX86AsmPrinter -lLLVMX86Utils -lLLVMMCJIT -lLLVMTarget -lLLVMRuntimeDyld -lLLVMObject -lLLVMMCParser -lLLVMBitReader -lLLVMExecutionEngine -lLLVMMC -lLLVMCore -lLLVMSupport -lz -lpthread -lffi -ltinfo -ldl -lm") + add_definitions(-D__STDC_CONSTANT_MACROS -D__STDC_FORMAT_MACROS -D__STDC_LIMIT_MACROS) + link_directories(/usr/lib/llvm-3.5/lib) endif() # Boost find_package(Boost REQUIRED) add_subdirectory(libevmjit) -add_subdirectory(libevmjit-cpp) -add_subdirectory(evmcc) + +if(EVMJIT_CPP) + add_subdirectory(libevmjit-cpp) +endif() + +if(EVMJIT_TOOLS) + add_subdirectory(evmcc) +endif() diff --git a/evmjit/libevmjit-cpp/Env.cpp b/evmjit/libevmjit-cpp/Env.cpp index 6320aa222..90474c7e4 100644 --- a/evmjit/libevmjit-cpp/Env.cpp +++ b/evmjit/libevmjit-cpp/Env.cpp @@ -89,7 +89,7 @@ extern "C" *o_hash = hash; } - EXPORT byte const* env_getExtCode(ExtVMFace* _env, h256* _addr256, uint64_t* o_size) + EXPORT byte const* env_extcode(ExtVMFace* _env, h256* _addr256, uint64_t* o_size) { auto addr = right160(*_addr256); auto& code = _env->codeAt(addr); diff --git a/evmjit/libevmjit/BasicBlock.h b/evmjit/libevmjit/BasicBlock.h index 7a5364a4e..cda4f89bd 100644 --- a/evmjit/libevmjit/BasicBlock.h +++ b/evmjit/libevmjit/BasicBlock.h @@ -57,7 +57,7 @@ public: explicit BasicBlock(std::string _name, llvm::Function* _mainFunc, llvm::IRBuilder<>& _builder, bool isJumpDest); BasicBlock(const BasicBlock&) = delete; - void operator=(const BasicBlock&) = delete; + BasicBlock& operator=(const BasicBlock&) = delete; llvm::BasicBlock* llvm() { return m_llvmBB; } @@ -66,6 +66,9 @@ public: bool isJumpDest() const { return m_isJumpDest; } + llvm::Value* getJumpTarget() const { return m_jumpTarget; } + void setJumpTarget(llvm::Value* _jumpTarget) { m_jumpTarget = _jumpTarget; } + LocalStack& localStack() { return m_stack; } /// Optimization: propagates values between local stacks in basic blocks @@ -112,6 +115,9 @@ private: /// Is the basic block a valid jump destination. /// JUMPDEST is the first instruction of the basic block. bool const m_isJumpDest = false; + + /// If block finishes with dynamic jump target index is stored here + llvm::Value* m_jumpTarget = nullptr; }; } diff --git a/evmjit/libevmjit/CMakeLists.txt b/evmjit/libevmjit/CMakeLists.txt index 7c35169a7..c1acfc409 100644 --- a/evmjit/libevmjit/CMakeLists.txt +++ b/evmjit/libevmjit/CMakeLists.txt @@ -2,23 +2,24 @@ set(TARGET_NAME evmjit) file(GLOB SOURCES "*.cpp") file(GLOB HEADERS "*.h") +set(INTERFACE_HEADERS interface.h) source_group("" FILES ${HEADERS}) source_group("" FILES ${SOURCES}) if (NOT "${CMAKE_CXX_COMPILER_ID}" STREQUAL "MSVC") # Disable rtti for Cache as LLVM has no rtti set_source_files_properties(Cache.cpp PROPERTIES COMPILE_FLAGS -fno-rtti) -endif () +endif() -add_library(${TARGET_NAME} ${SOURCES} ${HEADERS}) +add_library(${TARGET_NAME} SHARED ${SOURCES} ${HEADERS}) set_property(TARGET ${TARGET_NAME} PROPERTY FOLDER "libs") include_directories(${LLVM_INCLUDE_DIRS}) include_directories(${Boost_INCLUDE_DIRS}) -target_link_libraries(${TARGET_NAME} ${LLVM_LIBS}) +target_link_libraries(${TARGET_NAME} PRIVATE ${LLVM_LIBS}) #set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -fno-rtti") -#install( TARGETS ${EXECUTABLE} ARCHIVE DESTINATION lib LIBRARY DESTINATION lib ) -#install( FILES ${HEADERS} DESTINATION include/${EXECUTABLE} ) +install(TARGETS ${TARGET_NAME} LIBRARY DESTINATION lib) +install(FILES ${INTERFACE_HEADERS} DESTINATION include/${TARGET_NAME}) diff --git a/evmjit/libevmjit/Cache.cpp b/evmjit/libevmjit/Cache.cpp index 0b725bc24..0e6fb517a 100644 --- a/evmjit/libevmjit/Cache.cpp +++ b/evmjit/libevmjit/Cache.cpp @@ -36,6 +36,20 @@ std::unique_ptr Cache::getObject(std::string const& id) llvm::sys::path::system_temp_directory(false, cachePath); llvm::sys::path::append(cachePath, "evm_objs", id); +#if defined(__GNUC__) && !defined(NDEBUG) + llvm::sys::fs::file_status st; + auto err = llvm::sys::fs::status(cachePath.str(), st); + if (err) + return nullptr; + auto mtime = st.getLastModificationTime().toEpochTime(); + + std::tm tm; + strptime(__DATE__ __TIME__, " %b %d %Y %H:%M:%S", &tm); + auto btime = (uint64_t)std::mktime(&tm); + if (btime > mtime) + return nullptr; +#endif + if (auto r = llvm::MemoryBuffer::getFile(cachePath.str(), -1, false)) lastObject = llvm::MemoryBuffer::getMemBufferCopy(r.get()->getBuffer()); else if (r.getError() != std::make_error_code(std::errc::no_such_file_or_directory)) diff --git a/evmjit/libevmjit/Compiler.cpp b/evmjit/libevmjit/Compiler.cpp index 26c78b4ca..1d00fd225 100644 --- a/evmjit/libevmjit/Compiler.cpp +++ b/evmjit/libevmjit/Compiler.cpp @@ -100,7 +100,7 @@ llvm::BasicBlock* Compiler::getJumpTableBlock() m_jumpTableBlock.reset(new BasicBlock("JumpTable", m_mainFunc, m_builder, true)); InsertPointGuard g{m_builder}; m_builder.SetInsertPoint(m_jumpTableBlock->llvm()); - auto dest = m_jumpTableBlock->localStack().pop(); + auto dest = m_builder.CreatePHI(Type::Word, 8, "target"); auto switchInstr = m_builder.CreateSwitch(dest, getBadJumpBlock()); for (auto&& p : m_basicBlocks) { @@ -165,6 +165,26 @@ std::unique_ptr Compiler::compile(bytes const& _bytecode, std::str removeDeadBlocks(); + // Link jump table target index + if (m_jumpTableBlock) + { + auto phi = llvm::cast(&m_jumpTableBlock->llvm()->getInstList().front()); + for (auto predIt = llvm::pred_begin(m_jumpTableBlock->llvm()); predIt != llvm::pred_end(m_jumpTableBlock->llvm()); ++predIt) + { + BasicBlock* pred = nullptr; + for (auto&& p : m_basicBlocks) + { + if (p.second.llvm() == *predIt) + { + pred = &p.second; + break; + } + } + + phi->addIncoming(pred->getJumpTarget(), pred->llvm()); + } + } + dumpCFGifRequired("blocks-init.dot"); if (m_options.optimizeStack) @@ -394,7 +414,8 @@ void Compiler::compileBasicBlock(BasicBlock& _basicBlock, bytes const& _bytecode value = Endianness::toBE(m_builder, value); auto bytes = m_builder.CreateBitCast(value, llvm::VectorType::get(Type::Byte, 32), "bytes"); - auto byte = m_builder.CreateExtractElement(bytes, byteNum, "byte"); + auto safeByteNum = m_builder.CreateZExt(m_builder.CreateTrunc(byteNum, m_builder.getIntNTy(5)), Type::lowPrecision); // Trim index, large values can crash + auto byte = m_builder.CreateExtractElement(bytes, safeByteNum, "byte"); value = m_builder.CreateZExt(byte, Type::Word); auto byteNumValid = m_builder.CreateICmpULT(byteNum, Constant::get(32)); @@ -564,7 +585,7 @@ void Compiler::compileBasicBlock(BasicBlock& _basicBlock, bytes const& _bytecode } else { - stack.push(target); + _basicBlock.setJumpTarget(target); m_builder.CreateBr(getJumpTableBlock()); } } @@ -580,7 +601,7 @@ void Compiler::compileBasicBlock(BasicBlock& _basicBlock, bytes const& _bytecode } else { - stack.push(target); + _basicBlock.setJumpTarget(target); m_builder.CreateCondBr(cond, getJumpTableBlock(), _nextBasicBlock); } } diff --git a/evmjit/libevmjit/ExecutionEngine.cpp b/evmjit/libevmjit/ExecutionEngine.cpp index 7e99932c3..b6b140fd5 100644 --- a/evmjit/libevmjit/ExecutionEngine.cpp +++ b/evmjit/libevmjit/ExecutionEngine.cpp @@ -19,7 +19,12 @@ #include "Compiler.h" #include "Cache.h" -extern "C" void env_sha3(dev::eth::jit::byte const* _begin, uint64_t _size, std::array* o_hash); +#if defined(NDEBUG) +#define DEBUG_ENV_OPTION(name) false +#else +#include +#define DEBUG_ENV_OPTION(name) (std::getenv(#name) != nullptr) +#endif namespace dev { @@ -49,14 +54,17 @@ ReturnCode runEntryFunc(EntryFuncPtr _mainFunc, Runtime* _runtime) std::string codeHash(bytes const& _code) { - std::array binHash; - env_sha3(_code.data(), _code.size(), &binHash); - - std::ostringstream os; - for (auto i: binHash) - os << std::hex << std::setfill('0') << std::setw(2) << (int)(std::make_unsigned::type)i; - - return os.str(); + uint32_t hash = 0; + for (auto b : _code) + { + hash += b; + hash += (hash << 10); + hash ^= (hash >> 6); + } + hash += (hash << 3); + hash ^= (hash >> 11); + hash += (hash << 15); + return std::to_string(hash); } } @@ -64,6 +72,8 @@ std::string codeHash(bytes const& _code) ReturnCode ExecutionEngine::run(bytes const& _code, RuntimeData* _data, Env* _env) { static std::unique_ptr ee; // TODO: Use Managed Objects from LLVM? + static auto debugDumpModule = DEBUG_ENV_OPTION(EVMJIT_DUMP_MODULE); + static bool objectCacheEnabled = !DEBUG_ENV_OPTION(EVMJIT_CACHE_OFF); auto mainFuncName = codeHash(_code); EntryFuncPtr entryFuncPtr{}; @@ -74,14 +84,14 @@ ReturnCode ExecutionEngine::run(bytes const& _code, RuntimeData* _data, Env* _en } else { - bool objectCacheEnabled = true; auto objectCache = objectCacheEnabled ? Cache::getObjectCache() : nullptr; std::unique_ptr module; if (objectCache) module = Cache::getObject(mainFuncName); if (!module) module = Compiler({}).compile(_code, mainFuncName); - //module->dump(); + if (debugDumpModule) + module->dump(); if (!ee) { llvm::InitializeNativeTarget(); @@ -92,7 +102,7 @@ ReturnCode ExecutionEngine::run(bytes const& _code, RuntimeData* _data, Env* _en builder.setUseMCJIT(true); std::unique_ptr memoryManager(new llvm::SectionMemoryManager); builder.setMCJITMemoryManager(memoryManager.get()); - builder.setOptLevel(llvm::CodeGenOpt::Default); + builder.setOptLevel(llvm::CodeGenOpt::None); auto triple = llvm::Triple(llvm::sys::getProcessTriple()); if (triple.getOS() == llvm::Triple::OSType::Win32) diff --git a/evmjit/libevmjit/Ext.cpp b/evmjit/libevmjit/Ext.cpp index aae68f43a..a37a6fe99 100644 --- a/evmjit/libevmjit/Ext.cpp +++ b/evmjit/libevmjit/Ext.cpp @@ -5,9 +5,6 @@ #include #include -//#include -//#include - #include "RuntimeManager.h" #include "Memory.h" #include "Type.h" @@ -23,12 +20,9 @@ namespace jit Ext::Ext(RuntimeManager& _runtimeManager, Memory& _memoryMan): RuntimeHelper(_runtimeManager), m_memoryMan(_memoryMan) -#ifdef __MSCVER - , - m_funcs({}), // The only std::array initialization that works in both Visual Studio & GCC - m_argAllocas({}) -#endif { + m_funcs = decltype(m_funcs)(); + m_argAllocas = decltype(m_argAllocas)(); m_size = m_builder.CreateAlloca(Type::Size, nullptr, "env.size"); } @@ -74,7 +68,6 @@ llvm::Value* Ext::getArgAlloca() getBuilder().SetInsertPoint(getMainFunction()->front().getFirstNonPHI()); a = getBuilder().CreateAlloca(Type::Word, nullptr, "arg"); } - return a; } diff --git a/evmjit/libevmjit/Runtime.h b/evmjit/libevmjit/Runtime.h index 8cc5b7968..f97efa128 100644 --- a/evmjit/libevmjit/Runtime.h +++ b/evmjit/libevmjit/Runtime.h @@ -34,7 +34,7 @@ public: Runtime(RuntimeData* _data, Env* _env); Runtime(const Runtime&) = delete; - void operator=(const Runtime&) = delete; + Runtime& operator=(const Runtime&) = delete; StackImpl& getStack() { return m_stack; } MemoryImpl& getMemory() { return m_memory; } diff --git a/evmjit/libevmjit/interface.cpp b/evmjit/libevmjit/interface.cpp new file mode 100644 index 000000000..eef92d00f --- /dev/null +++ b/evmjit/libevmjit/interface.cpp @@ -0,0 +1,34 @@ +#include "interface.h" +#include +#include "ExecutionEngine.h" + +extern "C" +{ + +evmjit_result evmjit_run(void* _data, void* _env) +{ + using namespace dev::eth::jit; + + auto data = static_cast(_data); + + ExecutionEngine engine; + + auto codePtr = data->code; + auto codeSize = data->elems[RuntimeData::CodeSize].a; + bytes bytecode; + bytecode.insert(bytecode.end(), codePtr, codePtr + codeSize); + + auto returnCode = engine.run(bytecode, data, static_cast(_env)); + evmjit_result result = {static_cast(returnCode), 0, nullptr}; + if (returnCode == ReturnCode::Return && !engine.returnData.empty()) + { + // TODO: Optimized returning data. Allocating memory on client side by callback function might be a good idea + result.returnDataSize = engine.returnData.size(); + result.returnData = std::malloc(result.returnDataSize); + std::memcpy(result.returnData, engine.returnData.data(), result.returnDataSize); + } + + return result; +} + +} diff --git a/evmjit/libevmjit/interface.c b/evmjit/libevmjit/interface.h similarity index 76% rename from evmjit/libevmjit/interface.c rename to evmjit/libevmjit/interface.h index 47589578b..5d9307ab2 100644 --- a/evmjit/libevmjit/interface.c +++ b/evmjit/libevmjit/interface.h @@ -1,5 +1,20 @@ +#include #include +#ifdef __cplusplus +extern "C" { +#endif + +typedef struct evmjit_result +{ + int32_t returnCode; + uint64_t returnDataSize; + void* returnData; + +} evmjit_result; + +evmjit_result evmjit_run(void* _data, void* _env); + // JIT object opaque type typedef struct evm_jit evm_jit; @@ -9,6 +24,11 @@ typedef int evm_jit_return_code; // Host-endian 256-bit integer type typedef struct i256 i256; +struct i256 +{ + char b[33]; +}; + // Big-endian right aligned 256-bit hash typedef struct h256 h256; @@ -28,3 +48,7 @@ evm_jit_return_code evm_jit_execute(evm_jit* _jit); void evm_jit_get_return_data(evm_jit* _jit, char* _return_data_offset, size_t* _return_data_size); void evm_jit_destroy(evm_jit* _jit); + +#ifdef __cplusplus +} +#endif